Added support for discounts in the POS checkout process, allowing operators to apply either a fixed amount or a percentage off the gross total. Updated the state management to include discount details and modified the receipt and cart rendering to display pre- and post-discount values. Enhanced the checkout API to handle discount inputs and ensure proper calculations. Updated relevant tests to cover new discount logic.
195 lines
5.4 KiB
TypeScript
195 lines
5.4 KiB
TypeScript
import ReactMarkdown, { type Components } from "react-markdown";
|
|
import remarkGfm from "remark-gfm";
|
|
|
|
/**
|
|
* Server-rendered markdown for SOPs and the SOP README.
|
|
*
|
|
* The WFDASH app doesn't depend on `@tailwindcss/typography`, so we
|
|
* hand-style the elements that show up in the SOPs (headings,
|
|
* paragraphs, lists, tables, code blocks, blockquotes, hr).
|
|
*
|
|
* The `components` map below is the single source of truth for those
|
|
* styles. If a new markdown shape shows up in an SOP, add a class
|
|
* here rather than introducing the typography plugin.
|
|
*/
|
|
export function Markdown({ children }: { children: string }) {
|
|
return (
|
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={components}>
|
|
{children}
|
|
</ReactMarkdown>
|
|
);
|
|
}
|
|
|
|
const components: Components = {
|
|
h1: ({ children, ...props }) => (
|
|
<h1
|
|
className="text-2xl font-semibold tracking-tight font-heading-theme mt-6 mb-3 first:mt-0"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</h1>
|
|
),
|
|
h2: ({ children, ...props }) => (
|
|
<h2
|
|
className="text-lg font-semibold tracking-tight mt-8 mb-3 pb-2 border-b"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</h2>
|
|
),
|
|
h3: ({ children, ...props }) => (
|
|
<h3 className="text-base font-semibold mt-6 mb-2" {...props}>
|
|
{children}
|
|
</h3>
|
|
),
|
|
h4: ({ children, ...props }) => (
|
|
<h4 className="text-sm font-semibold mt-4 mb-1.5" {...props}>
|
|
{children}
|
|
</h4>
|
|
),
|
|
p: ({ children, ...props }) => (
|
|
<p className="text-sm leading-relaxed my-3" {...props}>
|
|
{children}
|
|
</p>
|
|
),
|
|
ul: ({ children, ...props }) => (
|
|
<ul
|
|
className="text-sm leading-relaxed my-3 ml-6 list-disc space-y-1.5"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</ul>
|
|
),
|
|
ol: ({ children, ...props }) => (
|
|
<ol
|
|
className="text-sm leading-relaxed my-3 ml-6 list-decimal space-y-1.5"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</ol>
|
|
),
|
|
li: ({ children, ...props }) => (
|
|
<li className="pl-0.5" {...props}>
|
|
{children}
|
|
</li>
|
|
),
|
|
a: ({ children, href, ...props }) => (
|
|
<a
|
|
href={href}
|
|
className="text-primary underline underline-offset-2 hover:no-underline"
|
|
target={href?.startsWith("http") ? "_blank" : undefined}
|
|
rel={href?.startsWith("http") ? "noopener noreferrer" : undefined}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</a>
|
|
),
|
|
blockquote: ({ children, ...props }) => (
|
|
<blockquote
|
|
className="my-4 border-l-4 border-muted-foreground/30 bg-muted/40 px-4 py-2 text-sm italic text-muted-foreground"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</blockquote>
|
|
),
|
|
hr: (props) => (
|
|
<hr className="my-6 border-border" {...props} />
|
|
),
|
|
// GFM tables. The Markdown plugin emits these as a `table` element
|
|
// with thead/tbody/tr/th/td children. Wrap in a scrollable container
|
|
// so wide KPI tables don't blow out the layout.
|
|
table: ({ children, ...props }) => (
|
|
<div className="my-4 overflow-x-auto rounded-md border">
|
|
<table className="w-full text-sm" {...props}>
|
|
{children}
|
|
</table>
|
|
</div>
|
|
),
|
|
thead: ({ children, ...props }) => (
|
|
<thead className="bg-muted/50" {...props}>
|
|
{children}
|
|
</thead>
|
|
),
|
|
tbody: ({ children, ...props }) => (
|
|
<tbody className="divide-y" {...props}>
|
|
{children}
|
|
</tbody>
|
|
),
|
|
tr: ({ children, ...props }) => (
|
|
<tr {...props}>{children}</tr>
|
|
),
|
|
th: ({ children, ...props }) => (
|
|
<th
|
|
className="px-3 py-2 text-left font-medium text-foreground text-xs uppercase tracking-wider"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</th>
|
|
),
|
|
td: ({ children, ...props }) => (
|
|
<td
|
|
className="px-3 py-2 align-top text-sm text-foreground/90"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</td>
|
|
),
|
|
code: ({ children, className, ...props }) => {
|
|
// `react-markdown` puts fenced code blocks in a <pre><code> pair
|
|
// and inline code in a bare <code>. The presence of a `className`
|
|
// (set by `react-markdown` for fenced blocks, e.g. "language-ts")
|
|
// is the discriminator. For inline code we just style the chip.
|
|
const isBlock = typeof className === "string" && className.length > 0;
|
|
if (isBlock) {
|
|
return (
|
|
<code className={className} {...props}>
|
|
{children}
|
|
</code>
|
|
);
|
|
}
|
|
return (
|
|
<code
|
|
className="rounded bg-muted px-1.5 py-0.5 font-mono text-[0.85em] text-foreground/90"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</code>
|
|
);
|
|
},
|
|
pre: ({ children, ...props }) => (
|
|
<pre
|
|
className="my-4 overflow-x-auto rounded-md border bg-muted/30 p-3 text-xs leading-relaxed"
|
|
{...props}
|
|
>
|
|
{children}
|
|
</pre>
|
|
),
|
|
strong: ({ children, ...props }) => (
|
|
<strong className="font-semibold text-foreground" {...props}>
|
|
{children}
|
|
</strong>
|
|
),
|
|
em: ({ children, ...props }) => (
|
|
<em className="italic" {...props}>
|
|
{children}
|
|
</em>
|
|
),
|
|
input: ({ type, checked, disabled, ...props }) => {
|
|
// GFM task-list checkboxes render as disabled checkboxes. Just
|
|
// pass them through — we don't need a custom checkbox component,
|
|
// the default browser style is fine and conveys "completed task".
|
|
if (type === "checkbox") {
|
|
return (
|
|
<input
|
|
type="checkbox"
|
|
checked={checked}
|
|
disabled={disabled}
|
|
readOnly
|
|
className="mr-1.5 align-middle"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
return <input type={type} {...props} />;
|
|
},
|
|
};
|