diff --git a/packages/ui/src/components.tsx b/packages/ui/src/components.tsx
index 4d9f133..1e3b6fc 100644
--- a/packages/ui/src/components.tsx
+++ b/packages/ui/src/components.tsx
@@ -2,9 +2,16 @@ import type { ReactNode } from 'react'
/** Wireframe kit: structure-first components shared by POS and back office. */
-export function PageHeader(props: { title: string; desc?: string; badge?: string; actions?: ReactNode }) {
+export function PageHeader(props: {
+ title: string
+ desc?: string
+ badge?: string
+ breadcrumb?: ReactNode
+ actions?: ReactNode
+}) {
return (
<>
+ {props.breadcrumb !== undefined &&
{props.breadcrumb}
}
{props.title}
{props.badge !== undefined &&
{props.badge}}
@@ -43,13 +50,19 @@ export interface Column {
key: string
label: string
numeric?: boolean
+ mono?: boolean
}
export function DataTable(props: {
columns: Column[]
rows: Record
[]
onRowClick?: (row: Record, index: number) => void
+ rowTone?: (index: number) => 'ok' | 'warn' | 'err' | undefined
}) {
+ const cellClass = (c: Column): string | undefined => {
+ const cls = [c.numeric === true ? 'num' : '', c.mono === true ? 'mono' : ''].filter((x) => x !== '').join(' ')
+ return cls !== '' ? cls : undefined
+ }
return (
@@ -60,13 +73,21 @@ export function DataTable(props: {
- {props.rows.map((row, i) => (
- props.onRowClick?.(row, i)} style={props.onRowClick !== undefined ? { cursor: 'pointer' } : undefined}>
- {props.columns.map((c) => (
- | {row[c.key]} |
- ))}
-
- ))}
+ {props.rows.map((row, i) => {
+ const tone = props.rowTone?.(i)
+ return (
+ props.onRowClick?.(row, i)}
+ style={props.onRowClick !== undefined ? { cursor: 'pointer' } : undefined}
+ >
+ {props.columns.map((c) => (
+ | {row[c.key]} |
+ ))}
+
+ )
+ })}
)
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 1d8139e..2eaaf82 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -5,3 +5,4 @@ export * from './ThemeSwitcher'
export * from './states'
export * from './chips'
export * from './tabs'
+export * from './record'
diff --git a/packages/ui/src/record.tsx b/packages/ui/src/record.tsx
new file mode 100644
index 0000000..50f7033
--- /dev/null
+++ b/packages/ui/src/record.tsx
@@ -0,0 +1,27 @@
+import type { ReactNode } from 'react'
+import { avatarColors, initials } from './avatar'
+
+/** Entity header: avatar + name + status pill + one meta line + action cluster. */
+export function RecordHeader(props: {
+ name: string
+ avatarSeed?: string
+ status?: ReactNode
+ meta?: ReactNode
+ actions?: ReactNode
+}) {
+ const seed = props.avatarSeed ?? props.name
+ const { bg, fg } = avatarColors(seed)
+ return (
+
+
{initials(seed)}
+
+
+
{props.name}
+ {props.status}
+
+ {props.meta !== undefined &&
{props.meta}
}
+
+ {props.actions !== undefined &&
{props.actions}
}
+
+ )
+}