Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions apps/www/public/r/blocks/receipt-standard.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://pdfx.akashpise.dev/schema/registry-item.json",
"name": "receipt-standard",
"type": "registry:block",
"title": "Receipt Standard",
"description": "Compact purchase receipt with payment status badge, line-item table, and totals summary.",
"files": [
{
"path": "templates/pdfx/receipt-standard/receipt-standard.tsx",
"content": "import { PdfxThemeProvider, usePdfxTheme } from '../../lib/pdfx-theme-context';\nimport { Badge } from '../../components/pdfx/badge/pdfx-badge';\nimport { KeyValue } from '../../components/pdfx/key-value/pdfx-key-value';\nimport { Section } from '../../components/pdfx/section/pdfx-section';\nimport { Table, TableBody, TableCell, TableHeader, TableRow } from '../../components/pdfx/table/pdfx-table';\nimport { Text } from '../../components/pdfx/text/pdfx-text';\nimport type { PdfxTheme } from '../../lib/pdfx-theme';\nimport { Document, Page, StyleSheet, View } from '@react-pdf/renderer';\nimport type { ReceiptStandardData } from './receipt-standard.types';\n\nconst sampleData: ReceiptStandardData = {\n receiptNumber: 'RCPT-2026-0410',\n issuedAt: 'April 10, 2026 · 2:45 PM',\n merchantName: 'Northline Supply',\n merchantAddress: '118 Mercer Street, New York, NY 10012',\n merchantEmail: '[email protected]',\n cashier: 'A. Rivera',\n paymentStatus: 'Paid',\n paymentMethod: 'Visa •••• 2048',\n currency: '$',\n customerName: 'Maya Chen',\n items: [\n { name: 'Pocket Notebook Set', quantity: 2, unitPrice: 14.5 },\n { name: 'Archival Ink Pen', quantity: 3, unitPrice: 6.25 },\n { name: 'Desk Organizer Tray', quantity: 1, unitPrice: 28 },\n ],\n summary: {\n subtotal: 75.75,\n tax: 6.44,\n total: 82.19,\n },\n notes: 'Thanks for shopping with Northline Supply.',\n};\n\nfunction formatMoney(amount: number, currency: string) {\n return `${currency}${amount.toFixed(2)}`;\n}\n\nfunction getStatusVariant(status: ReceiptStandardData['paymentStatus']) {\n if (status === 'Paid') return 'success';\n if (status === 'Refunded') return 'outline';\n return 'warning';\n}\n\nexport function ReceiptStandardDocument({\n theme,\n data = sampleData,\n}: {\n theme?: PdfxTheme;\n data?: ReceiptStandardData;\n}) {\n return (\n <PdfxThemeProvider theme={theme}>\n <ReceiptStandardContent data={data} />\n </PdfxThemeProvider>\n );\n}\n\nfunction ReceiptStandardContent({ data }: { data: ReceiptStandardData }) {\n const theme = usePdfxTheme();\n\n const styles = StyleSheet.create({\n page: {\n paddingTop: theme.spacing.page.marginTop,\n paddingLeft: theme.spacing.page.marginLeft,\n paddingRight: theme.spacing.page.marginRight,\n paddingBottom: theme.spacing.page.marginBottom,\n backgroundColor: theme.colors.background,\n },\n shell: {\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: theme.colors.border,\n borderRadius: theme.primitives.borderRadius.lg,\n padding: 18,\n backgroundColor: theme.colors.background,\n },\n headerRow: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n alignItems: 'flex-start',\n marginBottom: 12,\n },\n metaRow: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n gap: 12,\n marginBottom: 8,\n },\n metaBlock: {\n flex: 1,\n },\n label: {\n fontSize: 8,\n letterSpacing: 0.8,\n textTransform: 'uppercase',\n color: theme.colors.mutedForeground,\n marginBottom: 3,\n },\n value: {\n fontSize: 9,\n color: theme.colors.foreground,\n marginBottom: 1,\n },\n totalCard: {\n marginTop: 10,\n marginLeft: 'auto',\n width: 220,\n paddingTop: 8,\n borderTopWidth: 1,\n borderTopStyle: 'solid',\n borderTopColor: theme.colors.border,\n },\n });\n\n return (\n <Document title={`Receipt ${data.receiptNumber}`}>\n <Page size=\"A4\" style={styles.page}>\n <View style={styles.shell}>\n <View style={styles.headerRow}>\n <View>\n <Text variant=\"lg\" style={{ fontWeight: 'bold', marginBottom: 3 }} noMargin>\n {data.merchantName}\n </Text>\n <Text variant=\"xs\" color=\"mutedForeground\" noMargin>\n {data.merchantAddress}\n </Text>\n <Text variant=\"xs\" color=\"mutedForeground\" noMargin>\n {data.merchantEmail}\n </Text>\n </View>\n <Badge\n label={data.paymentStatus}\n variant={getStatusVariant(data.paymentStatus)}\n size=\"sm\"\n />\n </View>\n\n <Section style={{ marginBottom: 10 }}>\n <View style={styles.metaRow}>\n <View style={styles.metaBlock}>\n <Text style={styles.label} noMargin>\n Receipt No.\n </Text>\n <Text style={styles.value} noMargin>\n {data.receiptNumber}\n </Text>\n </View>\n <View style={styles.metaBlock}>\n <Text style={styles.label} noMargin>\n Issued\n </Text>\n <Text style={styles.value} noMargin>\n {data.issuedAt}\n </Text>\n </View>\n <View style={styles.metaBlock}>\n <Text style={styles.label} noMargin>\n Cashier\n </Text>\n <Text style={styles.value} noMargin>\n {data.cashier}\n </Text>\n </View>\n </View>\n\n <View style={styles.metaRow}>\n <View style={styles.metaBlock}>\n <Text style={styles.label} noMargin>\n Customer\n </Text>\n <Text style={styles.value} noMargin>\n {data.customerName}\n </Text>\n </View>\n <View style={styles.metaBlock}>\n <Text style={styles.label} noMargin>\n Payment Method\n </Text>\n <Text style={styles.value} noMargin>\n {data.paymentMethod}\n </Text>\n </View>\n </View>\n </Section>\n\n <Table variant=\"compact\" zebraStripe>\n <TableHeader>\n <TableRow header>\n <TableCell>Item</TableCell>\n <TableCell align=\"center\">Qty</TableCell>\n <TableCell align=\"right\">Price</TableCell>\n <TableCell align=\"right\">Total</TableCell>\n </TableRow>\n </TableHeader>\n <TableBody>\n {data.items.map((item) => (\n <TableRow key={`${item.name}-${item.quantity}-${item.unitPrice}`}>\n <TableCell>{item.name}</TableCell>\n <TableCell align=\"center\">{`${item.quantity}`}</TableCell>\n <TableCell align=\"right\">{formatMoney(item.unitPrice, data.currency)}</TableCell>\n <TableCell align=\"right\">\n {formatMoney(item.quantity * item.unitPrice, data.currency)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n\n <View style={styles.totalCard}>\n <KeyValue\n size=\"sm\"\n divided\n items={[\n { key: 'Subtotal', value: formatMoney(data.summary.subtotal, data.currency) },\n { key: 'Tax', value: formatMoney(data.summary.tax, data.currency) },\n {\n key: 'Total',\n value: formatMoney(data.summary.total, data.currency),\n keyStyle: { fontWeight: 'bold', color: theme.colors.foreground },\n valueStyle: { fontWeight: 'bold', fontSize: 12 },\n },\n ]}\n />\n </View>\n\n {data.notes ? (\n <Text variant=\"xs\" color=\"mutedForeground\" style={{ marginTop: 14 }} noMargin>\n {data.notes}\n </Text>\n ) : null}\n </View>\n </Page>\n </Document>\n );\n}\n",
"type": "registry:file"
},
{
"path": "templates/pdfx/receipt-standard/receipt-standard.types.ts",
"content": "export interface ReceiptStandardData {\n receiptNumber: string;\n issuedAt: string;\n merchantName: string;\n merchantAddress: string;\n merchantEmail: string;\n cashier: string;\n paymentStatus: 'Paid' | 'Pending' | 'Refunded';\n paymentMethod: string;\n currency: string;\n customerName: string;\n items: {\n name: string;\n quantity: number;\n unitPrice: number;\n }[];\n summary: {\n subtotal: number;\n tax: number;\n total: number;\n };\n notes?: string;\n}\n",
"type": "registry:file"
}
],
"dependencies": [
"@react-pdf/renderer"
],
"peerComponents": [
"badge",
"table",
"key-value",
"section",
"text"
]
}
26 changes: 26 additions & 0 deletions apps/www/public/r/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,32 @@
"text"
]
},
{
"name": "receipt-standard",
"type": "registry:block",
"title": "Receipt Standard",
"description": "Compact purchase receipt with payment status badge, line-item table, and totals summary.",
"files": [
{
"path": "blocks/receipt-standard/receipt-standard.tsx",
"type": "registry:file"
},
{
"path": "blocks/receipt-standard/receipt-standard.types.ts",
"type": "registry:file"
}
],
"dependencies": [
"@react-pdf/renderer"
],
"peerComponents": [
"badge",
"table",
"key-value",
"section",
"text"
]
},
{
"name": "report-financial",
"type": "registry:block",
Expand Down
9 changes: 9 additions & 0 deletions apps/www/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const InstallationPage = lazy(() => import('../pages/installation'));
const ServerSidePage = lazy(() => import('../pages/docs/server-side'));
const BlocksIndexPage = lazy(() => import('../pages/blocks/index'));
const InvoicesIndexPage = lazy(() => import('../pages/blocks/invoices/index'));
const ReceiptsIndexPage = lazy(() => import('../pages/blocks/receipts/index'));
const ReportsIndexPage = lazy(() => import('../pages/blocks/reports/index'));
const ThemeBuilderPage = lazy(() => import('../pages/theme-builder'));
const ReleasesPage = lazy(() => import('../pages/releases'));
Expand Down Expand Up @@ -323,6 +324,14 @@ export default function App() {
</Suspense>
}
/>
<Route
path="receipts"
element={
<Suspense fallback={<PageLoader />}>
<ReceiptsIndexPage />
</Suspense>
}
/>
<Route
path="reports"
element={
Expand Down
8 changes: 8 additions & 0 deletions apps/www/src/components/layout/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ export function Header() {
<Receipt className="h-3.5 w-3.5 text-blue-500" />
Invoices
</Link>
<Link
to="/blocks/receipts"
onClick={() => setMobileOpen(false)}
className="text-sm text-muted-foreground hover:text-foreground py-1.5 flex items-center gap-2"
>
<Receipt className="h-3.5 w-3.5 text-amber-500" />
Receipts
</Link>
<Link
to="/blocks/reports"
onClick={() => setMobileOpen(false)}
Expand Down
1 change: 1 addition & 0 deletions apps/www/src/components/layout/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const sections: SidebarSection[] = [
links: [
// { title: 'Overview', href: '/blocks' },
{ title: 'Invoices', href: '/blocks/invoices' },
{ title: 'Receipts', href: '/blocks/receipts' },
{ title: 'Reports', href: '/blocks/reports' },
],
},
Expand Down
7 changes: 7 additions & 0 deletions apps/www/src/constants/command.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ export const commandItems: CommandItem[] = [
group: 'Blocks',
keywords: ['template', 'invoice'],
},
{
label: 'Receipt Blocks',
href: '/blocks/receipts',
icon: FileText,
group: 'Blocks',
keywords: ['template', 'receipt'],
},
{
label: 'Report Blocks',
href: '/blocks/reports',
Expand Down
10 changes: 10 additions & 0 deletions apps/www/src/constants/header.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ export const BLOCKS = [
path: '/blocks/invoices',
count: 6,
},
{
icon: Receipt,
name: 'Receipts',
description: '1 compact receipt layout for checkout, payment status, and totals.',
color: 'text-amber-500',
bg: 'bg-amber-50 dark:bg-amber-950/40',
isDisabled: false,
path: '/blocks/receipts',
count: 1,
},
{
icon: FileSpreadsheet,
name: 'Reports',
Expand Down
8 changes: 8 additions & 0 deletions apps/www/src/pages/blocks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ const CATEGORIES: BlockCategoryCard[] = [
count: 4,
tags: ['Executive', 'Graph', 'DataTable', 'Best Practices'],
},
{
href: '/blocks/receipts',
title: 'Receipts',
description:
'Compact purchase receipt layouts for checkout and retail flows, designed to showcase status and totals clearly.',
count: 1,
tags: ['Compact', 'Badge', 'Table', 'KeyValue'],
},
];

export default function BlocksIndex() {
Expand Down
5 changes: 5 additions & 0 deletions apps/www/src/pages/blocks/receipts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ReceiptsContainerPage from './receipts-container';

export default function ReceiptsIndexPage() {
return <ReceiptsContainerPage />;
}
Loading