@tallyui/components 3.5.3 → 3.7.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 +9 -3
- package/dist/index.js +11 -3
- package/package.json +9 -9
- package/src/register/register-bar.helpers.ts +4 -1
- package/src/sale/orders-list.tsx +1 -1
- package/src/sale/receipt.tsx +10 -4
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
8
8
|
import { SvgProps } from 'react-native-svg';
|
|
9
9
|
import { Separator as Separator$1, Label as Label$1, Tabs as Tabs$1, Dialog as Dialog$1, AlertDialog as AlertDialog$1, Select as Select$1, Combobox as Combobox$1, DropdownMenu as DropdownMenu$1, ContextMenu as ContextMenu$1, Popover as Popover$1, Tooltip as Tooltip$1, HoverCard as HoverCard$1, Accordion as Accordion$1, Collapsible as Collapsible$1, Checkbox as Checkbox$1, Switch as Switch$1, RadioGroup as RadioGroup$1, Toggle as Toggle$1, ToggleGroup as ToggleGroup$1, Progress as Progress$1, Slider as Slider$1, Image } from '@tallyui/primitives';
|
|
10
10
|
export { TextClassContext } from '@tallyui/primitives';
|
|
11
|
-
import { CatalogueView, ProductSortValue, ProductSort, useRegisterSession, MovementType, useSale, AppliedDiscount, Discount, CatalogueEntry, CatalogueViewState, SentOrder, PosOrder, OutboxState, ParkedOrderSummary } from '@tallyui/pos';
|
|
11
|
+
import { CatalogueView, ProductSortValue, ProductSort, useRegisterSession, RegisterSession, MovementType, useSale, AppliedDiscount, Discount, CatalogueEntry, CatalogueViewState, SentOrder, PosOrder, OutboxState, ParkedOrderSummary } from '@tallyui/pos';
|
|
12
12
|
import { ProductTraits, TraitContext, Money, CustomerTraits, Customer, CustomerInput, ProductCategory, SyncNotice, StoreSettingsChoices, StoreSettingsChoice } from '@tallyui/core';
|
|
13
13
|
|
|
14
14
|
declare const textVariants: (props?: ({
|
|
@@ -1093,6 +1093,7 @@ declare function RegisterBar({ register, registerId, online, registerName, multi
|
|
|
1093
1093
|
* register name (`multiRegister` decides whether that's shown at all) and the status pill.
|
|
1094
1094
|
* Copy is plain English (no i18n keys, ADR-064's neutral-copy rule).
|
|
1095
1095
|
*/
|
|
1096
|
+
|
|
1096
1097
|
type RegisterBarPill = 'Choose a register' | 'Close not finished' | 'Offline' | 'Approval needed' | 'Counting' | 'Overdue' | 'Register closed';
|
|
1097
1098
|
/**
|
|
1098
1099
|
* Priority, matching WCPOS's ordering: choosing a register outranks everything (LEDGER 48);
|
|
@@ -1104,7 +1105,8 @@ declare function describeRegisterBarPill({ registerId, online, sessionStatus, ov
|
|
|
1104
1105
|
/** The register this till is bound to, or `null` when it isn't bound yet. */
|
|
1105
1106
|
registerId: string | null;
|
|
1106
1107
|
online: boolean;
|
|
1107
|
-
|
|
1108
|
+
/** `conflict` and `superseded` (ADR-078 decision 9) get no pill of their own yet; step 3b decides theirs. */
|
|
1109
|
+
sessionStatus?: RegisterSession['status'] | null;
|
|
1108
1110
|
overdue?: boolean;
|
|
1109
1111
|
approvalRequired?: boolean;
|
|
1110
1112
|
/** The store uses register sessions at all (`useRegisterSession`'s `enabled`). */
|
|
@@ -1411,7 +1413,7 @@ declare function Catalogue<Doc>({ products, traits, currency, onSelect, statusTe
|
|
|
1411
1413
|
|
|
1412
1414
|
/** `store`/`taxLabel`/`topInset`/`formatDate` replace the app's Medusa settings, hard-coded VAT (default matches
|
|
1413
1415
|
* 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 }: {
|
|
1416
|
+
declare function Receipt({ order, posOrder, store, cashier, registerId, registerName, newSale, taxLabel, topInset, formatDate, footerSlot }: {
|
|
1415
1417
|
order: SentOrder;
|
|
1416
1418
|
store: {
|
|
1417
1419
|
name: string;
|
|
@@ -1420,12 +1422,16 @@ declare function Receipt({ order, posOrder, store, cashier, registerId, newSale,
|
|
|
1420
1422
|
cashier: string;
|
|
1421
1423
|
registerId: string;
|
|
1422
1424
|
newSale: () => void;
|
|
1425
|
+
/** The register's name, printed as `Register: <name>`; the id is never printed. */
|
|
1426
|
+
registerName?: string;
|
|
1423
1427
|
/** The finalized order (useSale's receipt stage has it). With it the receipt prints its reference and time, the
|
|
1424
1428
|
* same as Orders; without it (a preview before finalize) the sale id is marked as a draft. */
|
|
1425
1429
|
posOrder?: PosOrder;
|
|
1426
1430
|
taxLabel?: (ratePpm: number) => string;
|
|
1427
1431
|
topInset?: number;
|
|
1428
1432
|
formatDate?: (iso: string) => string;
|
|
1433
|
+
/** Prints after the Change row and above the on-screen buttons (e.g. an app's register/software block). */
|
|
1434
|
+
footerSlot?: ReactNode;
|
|
1429
1435
|
}): react_jsx_runtime.JSX.Element;
|
|
1430
1436
|
|
|
1431
1437
|
/** 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.
|
|
3
|
+
"version": "3.7.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.
|
|
31
|
-
"@tallyui/primitives": "^3.
|
|
32
|
-
"@tallyui/theme": "^3.
|
|
30
|
+
"@tallyui/core": "^3.7.0",
|
|
31
|
+
"@tallyui/primitives": "^3.7.0",
|
|
32
|
+
"@tallyui/theme": "^3.7.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.
|
|
39
|
+
"@tallyui/pos": "3.7.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@tallyui/connector-medusa": "3.
|
|
43
|
-
"@tallyui/connector-vendure": "3.
|
|
44
|
-
"@tallyui/connector-woocommerce": "3.
|
|
45
|
-
"@tallyui/mock-api": "3.
|
|
42
|
+
"@tallyui/connector-medusa": "3.7.0",
|
|
43
|
+
"@tallyui/connector-vendure": "3.7.0",
|
|
44
|
+
"@tallyui/connector-woocommerce": "3.7.0",
|
|
45
|
+
"@tallyui/mock-api": "3.7.0",
|
|
46
46
|
"@types/react": ">=18",
|
|
47
47
|
"react": "^19.2.4",
|
|
48
48
|
"react-native-svg": "15.12.1",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* Copy is plain English (no i18n keys, ADR-064's neutral-copy rule).
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { RegisterSession } from '@tallyui/pos';
|
|
15
|
+
|
|
14
16
|
export type RegisterBarPill =
|
|
15
17
|
| 'Choose a register'
|
|
16
18
|
| 'Close not finished'
|
|
@@ -38,7 +40,8 @@ export function describeRegisterBarPill({
|
|
|
38
40
|
/** The register this till is bound to, or `null` when it isn't bound yet. */
|
|
39
41
|
registerId: string | null;
|
|
40
42
|
online: boolean;
|
|
41
|
-
|
|
43
|
+
/** `conflict` and `superseded` (ADR-078 decision 9) get no pill of their own yet; step 3b decides theirs. */
|
|
44
|
+
sessionStatus?: RegisterSession['status'] | null;
|
|
42
45
|
overdue?: boolean;
|
|
43
46
|
approvalRequired?: boolean;
|
|
44
47
|
/** The store uses register sessions at all (`useRegisterSession`'s `enabled`). */
|
package/src/sale/orders-list.tsx
CHANGED
|
@@ -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'
|
package/src/sale/receipt.tsx
CHANGED
|
@@ -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>
|