@tallyui/components 3.5.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1411,7 +1411,7 @@ declare function Catalogue<Doc>({ products, traits, currency, onSelect, statusTe
1411
1411
 
1412
1412
  /** `store`/`taxLabel`/`topInset`/`formatDate` replace the app's Medusa settings, hard-coded VAT (default matches
1413
1413
  * TV6a's `Cart`), outbox strip and date util, so `Receipt` stays platform-neutral (ADR-052). */
1414
- declare function Receipt({ order, posOrder, store, cashier, registerId, newSale, taxLabel, topInset, formatDate }: {
1414
+ declare function Receipt({ order, posOrder, store, cashier, registerId, registerName, newSale, taxLabel, topInset, formatDate, footerSlot }: {
1415
1415
  order: SentOrder;
1416
1416
  store: {
1417
1417
  name: string;
@@ -1420,12 +1420,16 @@ declare function Receipt({ order, posOrder, store, cashier, registerId, newSale,
1420
1420
  cashier: string;
1421
1421
  registerId: string;
1422
1422
  newSale: () => void;
1423
+ /** The register's name, printed as `Register: <name>`; the id is never printed. */
1424
+ registerName?: string;
1423
1425
  /** The finalized order (useSale's receipt stage has it). With it the receipt prints its reference and time, the
1424
1426
  * same as Orders; without it (a preview before finalize) the sale id is marked as a draft. */
1425
1427
  posOrder?: PosOrder;
1426
1428
  taxLabel?: (ratePpm: number) => string;
1427
1429
  topInset?: number;
1428
1430
  formatDate?: (iso: string) => string;
1431
+ /** Prints after the Change row and above the on-screen buttons (e.g. an app's register/software block). */
1432
+ footerSlot?: ReactNode;
1429
1433
  }): react_jsx_runtime.JSX.Element;
1430
1434
 
1431
1435
  /** The order reference a visitor sees on the receipt and in Orders (front desk ruling, 2026-10-06): the finalized
package/dist/index.js CHANGED
@@ -5021,17 +5021,20 @@ function Receipt({
5021
5021
  store,
5022
5022
  cashier,
5023
5023
  registerId,
5024
+ registerName,
5024
5025
  newSale,
5025
5026
  taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%`,
5026
5027
  topInset = 0,
5027
- formatDate = defaultFormatDate
5028
+ formatDate = defaultFormatDate,
5029
+ footerSlot
5028
5030
  }) {
5029
5031
  useEffect6(injectPrintStyle, []);
5030
5032
  const receipt = buildReceiptData3(order, {
5031
5033
  storeName: store.name,
5032
5034
  storeAddress: store.address,
5033
5035
  cashier,
5034
- register: registerId
5036
+ register: registerId,
5037
+ registerName
5035
5038
  });
5036
5039
  const money = (amount) => formatMoney16({ amount, currency: receipt.currency }) ?? "";
5037
5040
  const row = (label, amount, bold = false, key) => /* @__PURE__ */ jsxs56(View63, { accessibilityLabel: `${label}: ${amount}`, className: "flex-row justify-between gap-4", children: [
@@ -5052,6 +5055,10 @@ function Receipt({
5052
5055
  "Cashier: ",
5053
5056
  receipt.header.cashier
5054
5057
  ] }),
5058
+ receipt.register.name ? /* @__PURE__ */ jsxs56(Text41, { testID: "receipt-register", className: "text-muted-foreground", children: [
5059
+ "Register: ",
5060
+ receipt.register.name
5061
+ ] }) : null,
5055
5062
  receipt.header.customer && /* @__PURE__ */ jsxs56(Text41, { className: "text-muted-foreground", children: [
5056
5063
  "Customer: ",
5057
5064
  receipt.header.customer
@@ -5078,6 +5085,7 @@ function Receipt({
5078
5085
  index
5079
5086
  )),
5080
5087
  row("Change", money(receipt.changeDueMinor)),
5088
+ footerSlot ? /* @__PURE__ */ jsx98(View63, { testID: "receipt-footer", className: "mt-3 items-center border-t border-dashed border-border pt-3", children: footerSlot }) : null,
5081
5089
  /* @__PURE__ */ jsxs56(View63, { dataSet: { print: "hide" }, className: "flex-row gap-4", children: [
5082
5090
  /* @__PURE__ */ jsx98(Pressable33, { accessibilityRole: "button", onPress: () => {
5083
5091
  if (typeof window !== "undefined" && typeof window.print === "function") window.print();
@@ -5277,7 +5285,7 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
5277
5285
  setRetryingIds(new Set(retrying.current));
5278
5286
  }
5279
5287
  }, className: "rounded-md bg-primary px-4 py-2", children: /* @__PURE__ */ jsx100(Text43, { className: "text-center font-semibold text-primary-foreground", children: "Retry" }) }) : null,
5280
- knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */ jsx100(View65, { className: "border-l-4 border-warning pl-2", children: /* @__PURE__ */ jsx100(Text43, { className: "text-foreground", children: warning.code === "insufficient_stock" ? `${warning.quantity} sold without stock: ${order.lines.find((line) => line.variantId === warning.variantId)?.name}` : warning.code === "tax_rate_mismatch" ? `Tax at ${(warning.ratePpm / 1e4).toLocaleString(void 0, { maximumFractionDigits: 4 })}%: store ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` : warning.code === "customer_ignored" ? `The online store didn't recognise the customer on this sale, so it was saved as a guest sale. Customer id: ${warning.customerId}.` : warning.code === "figures_mismatch" ? `The online store worked out different figures for this sale. ${warning.fields.map(({ field, tillMinor, serverMinor }) => `${FIGURE_LABELS.get(field) ?? field}: till ${formatMoney17({ amount: tillMinor, currency: order.currency })}, store ${formatMoney17({ amount: serverMinor, currency: order.currency })}.`).join(" ")}` : warning.bridgeMinor !== void 0 ? `Store calculated ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })}; a rounding line of ${warning.bridgeMinor > 0 ? "+" : ""}${formatMoney17({ amount: warning.bridgeMinor, currency: order.currency })} brought it to ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` : `Store total ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` }) }, index))
5288
+ knownWarnings(order.warnings).filter((warning) => warning.code !== "register_session_unknown").map((warning, index) => /* @__PURE__ */ jsx100(View65, { className: "border-l-4 border-warning pl-2", children: /* @__PURE__ */ jsx100(Text43, { className: "text-foreground", children: warning.code === "insufficient_stock" ? `${warning.quantity} sold without stock: ${order.lines.find((line) => line.variantId === warning.variantId)?.name}` : warning.code === "tax_rate_mismatch" ? `Tax at ${(warning.ratePpm / 1e4).toLocaleString(void 0, { maximumFractionDigits: 4 })}%: store ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` : warning.code === "customer_ignored" ? `The online store didn't recognise the customer on this sale, so it was saved as a guest sale. Customer id: ${warning.customerId}.` : warning.code === "figures_mismatch" ? `The online store worked out different figures for this sale. ${warning.fields.map(({ field, tillMinor, serverMinor }) => `${FIGURE_LABELS.get(field) ?? field}: till ${formatMoney17({ amount: tillMinor, currency: order.currency })}, store ${formatMoney17({ amount: serverMinor, currency: order.currency })}.`).join(" ")}` : warning.bridgeMinor !== void 0 ? `Store calculated ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })}; a rounding line of ${warning.bridgeMinor > 0 ? "+" : ""}${formatMoney17({ amount: warning.bridgeMinor, currency: order.currency })} brought it to ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` : `Store total ${formatMoney17({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney17({ amount: warning.expectedMinor, currency: order.currency })}` }) }, index))
5281
5289
  ] }, order.id);
5282
5290
  })
5283
5291
  ] }, section.title)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tallyui/components",
3
- "version": "3.5.2",
3
+ "version": "3.6.0",
4
4
  "type": "module",
5
5
  "description": "Universal POS UI components for Tally UI",
6
6
  "main": "./dist/index.js",
@@ -27,22 +27,22 @@
27
27
  "directory": "packages/components"
28
28
  },
29
29
  "peerDependencies": {
30
- "@tallyui/core": "^3.5.2",
31
- "@tallyui/primitives": "^3.5.2",
32
- "@tallyui/theme": "^3.5.2",
30
+ "@tallyui/core": "^3.6.0",
31
+ "@tallyui/primitives": "^3.6.0",
32
+ "@tallyui/theme": "^3.6.0",
33
33
  "react": ">=18",
34
34
  "react-native": ">=0.74",
35
35
  "react-native-svg": ">=15"
36
36
  },
37
37
  "dependencies": {
38
38
  "class-variance-authority": "^0.7.1",
39
- "@tallyui/pos": "3.5.2"
39
+ "@tallyui/pos": "3.6.0"
40
40
  },
41
41
  "devDependencies": {
42
- "@tallyui/connector-medusa": "3.5.2",
43
- "@tallyui/connector-vendure": "3.5.2",
44
- "@tallyui/connector-woocommerce": "3.5.2",
45
- "@tallyui/mock-api": "3.5.2",
42
+ "@tallyui/connector-medusa": "3.6.0",
43
+ "@tallyui/connector-vendure": "3.6.0",
44
+ "@tallyui/connector-woocommerce": "3.6.0",
45
+ "@tallyui/mock-api": "3.6.0",
46
46
  "@types/react": ">=18",
47
47
  "react": "^19.2.4",
48
48
  "react-native-svg": "15.12.1",
@@ -105,7 +105,7 @@ export function OrdersList({ orders, onRetry, formatDate = defaultFormatDate, fo
105
105
  }} className="rounded-md bg-primary px-4 py-2">
106
106
  <Text className="text-center font-semibold text-primary-foreground">Retry</Text>
107
107
  </Pressable> : null}
108
- {knownWarnings(order.warnings).map((warning, index) => <View key={index} className="border-l-4 border-warning pl-2"><Text className="text-foreground">
108
+ {knownWarnings(order.warnings).filter((warning) => warning.code !== 'register_session_unknown').map((warning, index) => <View key={index} className="border-l-4 border-warning pl-2"><Text className="text-foreground">
109
109
  {warning.code === 'insufficient_stock'
110
110
  ? `${warning.quantity} sold without stock: ${order.lines.find((line) => line.variantId === warning.variantId)?.name}`
111
111
  : warning.code === 'tax_rate_mismatch'
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import { useEffect, type ReactNode } from 'react';
2
2
  import { Pressable, Text, View } from 'react-native';
3
3
  import { formatMoney } from '@tallyui/core';
4
4
  import { buildReceiptData, type SentOrder } from '@tallyui/pos';
@@ -11,17 +11,21 @@ const defaultFormatDate = (iso: string) => new Intl.DateTimeFormat(undefined, {
11
11
 
12
12
  /** `store`/`taxLabel`/`topInset`/`formatDate` replace the app's Medusa settings, hard-coded VAT (default matches
13
13
  * TV6a's `Cart`), outbox strip and date util, so `Receipt` stays platform-neutral (ADR-052). */
14
- export function Receipt({ order, posOrder, store, cashier, registerId, newSale, taxLabel = (ratePpm: number) => `Tax ${ratePpm / 10000}%`,
15
- topInset = 0, formatDate = defaultFormatDate }: {
14
+ export function Receipt({ order, posOrder, store, cashier, registerId, registerName, newSale, taxLabel = (ratePpm: number) => `Tax ${ratePpm / 10000}%`,
15
+ topInset = 0, formatDate = defaultFormatDate, footerSlot }: {
16
16
  order: SentOrder; store: { name: string; address?: string }; cashier: string; registerId: string; newSale: () => void;
17
+ /** The register's name, printed as `Register: <name>`; the id is never printed. */
18
+ registerName?: string;
17
19
  /** The finalized order (useSale's receipt stage has it). With it the receipt prints its reference and time, the
18
20
  * same as Orders; without it (a preview before finalize) the sale id is marked as a draft. */
19
21
  posOrder?: PosOrder;
20
22
  taxLabel?: (ratePpm: number) => string; topInset?: number; formatDate?: (iso: string) => string;
23
+ /** Prints after the Change row and above the on-screen buttons (e.g. an app's register/software block). */
24
+ footerSlot?: ReactNode;
21
25
  }) {
22
26
  useEffect(injectPrintStyle, []);
23
27
  const receipt = buildReceiptData(order, {
24
- storeName: store.name, storeAddress: store.address, cashier, register: registerId,
28
+ storeName: store.name, storeAddress: store.address, cashier, register: registerId, registerName,
25
29
  });
26
30
  const money = (amount: number) => formatMoney({ amount, currency: receipt.currency }) ?? '';
27
31
  const row = (label: string, amount: string, bold = false, key?: number) => <View key={key} accessibilityLabel={`${label}: ${amount}`} className="flex-row justify-between gap-4">
@@ -40,6 +44,7 @@ export function Receipt({ order, posOrder, store, cashier, registerId, newSale,
40
44
  <Text testID="receipt-order" className="text-foreground">{posOrder ? `Order ${orderReference(posOrder)}` : `Order ${receipt.header.orderNumber.slice(-8)} (draft)`}</Text>
41
45
  <Text className="text-muted-foreground">{formatDate(posOrder?.createdAt ?? receipt.header.date)}</Text>
42
46
  <Text className="text-muted-foreground">Cashier: {receipt.header.cashier}</Text>
47
+ {receipt.register.name ? <Text testID="receipt-register" className="text-muted-foreground">Register: {receipt.register.name}</Text> : null}
43
48
  {receipt.header.customer && <Text className="text-muted-foreground">Customer: {receipt.header.customer}</Text>}
44
49
  {receipt.lineItems.map((line, index) => <View key={index}>
45
50
  <Text className="text-foreground">{line.name}</Text>
@@ -61,6 +66,7 @@ export function Receipt({ order, posOrder, store, cashier, registerId, newSale,
61
66
  {receipt.payments.map((payment, index) => row(payment.method === 'cash' ? 'Cash tendered' : 'Card terminal',
62
67
  money(payment.amountMinor) + (payment.reference ? ` · ${payment.reference}` : ''), false, index))}
63
68
  {row('Change', money(receipt.changeDueMinor))}
69
+ {footerSlot ? <View testID="receipt-footer" className="mt-3 items-center border-t border-dashed border-border pt-3">{footerSlot}</View> : null}
64
70
  <View dataSet={{ print: 'hide' }} className="flex-row gap-4">
65
71
  <Pressable accessibilityRole="button" onPress={() => { if (typeof window !== 'undefined' && typeof window.print === 'function') window.print(); }} className="rounded-md border border-border bg-card px-4 py-3">
66
72
  <Text className="text-center text-foreground">Print receipt</Text>