@tallyui/components 3.0.3 → 3.1.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 +42 -5
- package/dist/index.js +676 -473
- package/package.json +9 -9
- package/src/customer/customer-picker.tsx +1 -1
- package/src/customer/customer-select.tsx +16 -0
- package/src/index.ts +2 -0
- package/src/register/register-count.tsx +5 -2
- package/src/register/register-panel.tsx +15 -2
- package/src/sale/cart.tsx +24 -7
- package/src/sale/catalogue.tsx +6 -3
- package/src/sale/index.ts +2 -0
- package/src/sale/parked-sales.tsx +103 -0
- package/src/sale/price-form.tsx +51 -0
package/dist/index.js
CHANGED
|
@@ -2308,6 +2308,7 @@ function CustomerCard({ doc, traits, className, ...props }) {
|
|
|
2308
2308
|
}
|
|
2309
2309
|
|
|
2310
2310
|
// src/customer/customer-select.tsx
|
|
2311
|
+
import { useState as useState6 } from "react";
|
|
2311
2312
|
import { Pressable as Pressable14, ScrollView as ScrollView6, View as View29 } from "react-native";
|
|
2312
2313
|
import { Select as Select2 } from "@tallyui/primitives";
|
|
2313
2314
|
import { useCustomerTraits as useCustomerTraits2 } from "@tallyui/core";
|
|
@@ -2325,10 +2326,12 @@ function CustomerSelectView({
|
|
|
2325
2326
|
onSelect,
|
|
2326
2327
|
onSearch,
|
|
2327
2328
|
placeholder = "Search customers...",
|
|
2329
|
+
showSearch = true,
|
|
2328
2330
|
className,
|
|
2329
2331
|
traits,
|
|
2330
2332
|
...props
|
|
2331
2333
|
}) {
|
|
2334
|
+
const [search, setSearch] = useState6("");
|
|
2332
2335
|
const selectedOption = selected && traits ? { value: traits.getId(selected), label: traits.getName(selected) } : void 0;
|
|
2333
2336
|
const customerById = /* @__PURE__ */ new Map();
|
|
2334
2337
|
if (traits) {
|
|
@@ -2347,6 +2350,19 @@ function CustomerSelectView({
|
|
|
2347
2350
|
onValueChange: handleValueChange,
|
|
2348
2351
|
defaultOpen: true,
|
|
2349
2352
|
children: /* @__PURE__ */ jsxs19(VStack, { space: "sm", className: cn56(className), ...props, children: [
|
|
2353
|
+
showSearch && /* @__PURE__ */ jsx59(
|
|
2354
|
+
SearchInput,
|
|
2355
|
+
{
|
|
2356
|
+
value: search,
|
|
2357
|
+
onChangeText: (text) => {
|
|
2358
|
+
setSearch(text);
|
|
2359
|
+
onSearch(text);
|
|
2360
|
+
},
|
|
2361
|
+
placeholder,
|
|
2362
|
+
testID: "customer-select-search",
|
|
2363
|
+
accessibilityLabel: "Search customers"
|
|
2364
|
+
}
|
|
2365
|
+
),
|
|
2350
2366
|
selected && traits && /* @__PURE__ */ jsx59(Select2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx59(View29, { className: "rounded-lg border border-primary bg-primary/5 px-3 py-2", children: /* @__PURE__ */ jsx59(CustomerCard, { doc: selected, traits }) }) }),
|
|
2351
2367
|
customers.length > 0 && /* @__PURE__ */ jsx59(Select2.Content, { asChild: true, forceMount: true, disablePositioningStyle: true, children: /* @__PURE__ */ jsx59(ScrollView6, { className: "max-h-60", children: customers.map((customer, index) => {
|
|
2352
2368
|
const id = traits?.getId(customer) ?? String(index);
|
|
@@ -2431,7 +2447,7 @@ function CustomerOrderHistory({
|
|
|
2431
2447
|
}
|
|
2432
2448
|
|
|
2433
2449
|
// src/customer/customer-picker.tsx
|
|
2434
|
-
import { useEffect, useRef, useState as
|
|
2450
|
+
import { useEffect, useRef, useState as useState7 } from "react";
|
|
2435
2451
|
import { Pressable as Pressable16, Text as Text19, TextInput as TextInput7, View as View32 } from "react-native";
|
|
2436
2452
|
import { customerTraits, CustomerServiceError, ConnectorUnauthorizedError } from "@tallyui/core";
|
|
2437
2453
|
import { cn as cn59 } from "@tallyui/theme";
|
|
@@ -2439,16 +2455,16 @@ import { jsx as jsx62, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
|
2439
2455
|
var EMAIL_MAX = 254;
|
|
2440
2456
|
var EMAIL_SHAPE = /^[^\s@\u0000]+@[^\s@\u0000]+\.[^\s@\u0000]+$/u;
|
|
2441
2457
|
function CustomerPicker({ search, create, selected, onSelect, onError, online, debounceMs = 250, className }) {
|
|
2442
|
-
const [query, setQuery] =
|
|
2443
|
-
const [customers, setCustomers] =
|
|
2444
|
-
const [error, setError] =
|
|
2445
|
-
const [showForm, setShowForm] =
|
|
2446
|
-
const [creating, setCreating] =
|
|
2447
|
-
const [values, setValues] =
|
|
2458
|
+
const [query, setQuery] = useState7("");
|
|
2459
|
+
const [customers, setCustomers] = useState7([]);
|
|
2460
|
+
const [error, setError] = useState7(null);
|
|
2461
|
+
const [showForm, setShowForm] = useState7(false);
|
|
2462
|
+
const [creating, setCreating] = useState7(false);
|
|
2463
|
+
const [values, setValues] = useState7({ firstName: "", lastName: "", email: "", phone: "", address: "" });
|
|
2448
2464
|
const sequence = useRef(0);
|
|
2449
2465
|
const searchRef = useRef(search);
|
|
2450
2466
|
searchRef.current = search;
|
|
2451
|
-
const [noResults, setNoResults] =
|
|
2467
|
+
const [noResults, setNoResults] = useState7(false);
|
|
2452
2468
|
function handleError(error2) {
|
|
2453
2469
|
if (error2 instanceof CustomerServiceError) {
|
|
2454
2470
|
setError(error2.code === "network" ? "Couldn't reach the store. Try again." : error2.code === "server" ? "The store couldn't do that. Try again." : error2.message);
|
|
@@ -2530,6 +2546,7 @@ function CustomerPicker({ search, create, selected, onSelect, onError, online, d
|
|
|
2530
2546
|
customers,
|
|
2531
2547
|
traits: customerTraits,
|
|
2532
2548
|
onSearch: setQuery,
|
|
2549
|
+
showSearch: false,
|
|
2533
2550
|
onSelect: (customer) => {
|
|
2534
2551
|
onSelect(customer);
|
|
2535
2552
|
setQuery("");
|
|
@@ -2675,7 +2692,7 @@ function ChangeDisplay({ change, locale, className, ...viewProps }) {
|
|
|
2675
2692
|
}
|
|
2676
2693
|
|
|
2677
2694
|
// src/checkout/cash-tendered.tsx
|
|
2678
|
-
import { useEffect as useEffect2, useRef as useRef2, useState as
|
|
2695
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState8 } from "react";
|
|
2679
2696
|
import { Pressable as Pressable18, Text as Text21, TextInput as TextInput8, View as View36 } from "react-native";
|
|
2680
2697
|
import { formatMoney as formatMoney5, minorUnitDigits as minorUnitDigits2, moneyFromDecimalString } from "@tallyui/core";
|
|
2681
2698
|
import { cn as cn65 } from "@tallyui/theme";
|
|
@@ -2700,13 +2717,13 @@ function CashTendered({
|
|
|
2700
2717
|
className,
|
|
2701
2718
|
...viewProps
|
|
2702
2719
|
}) {
|
|
2703
|
-
const [internalAmount, setInternalAmount] =
|
|
2720
|
+
const [internalAmount, setInternalAmount] = useState8(defaultAmount ?? total);
|
|
2704
2721
|
const amount = controlledAmount ?? internalAmount;
|
|
2705
2722
|
const buttons = quickAmounts ?? defaultQuickAmounts(total);
|
|
2706
2723
|
const digits = minorUnitDigits2(amount.currency);
|
|
2707
2724
|
const amountText = String(Math.abs(amount.amount)).padStart(digits + 1, "0");
|
|
2708
2725
|
const decimal2 = (amount.amount < 0 ? "-" : "") + (digits ? `${amountText.slice(0, -digits)}.${amountText.slice(-digits)}` : amountText);
|
|
2709
|
-
const [text, setText] =
|
|
2726
|
+
const [text, setText] = useState8(decimal2);
|
|
2710
2727
|
const lastTextAmount = useRef2(amount);
|
|
2711
2728
|
useEffect2(() => {
|
|
2712
2729
|
if (amount.amount !== lastTextAmount.current.amount || amount.currency !== lastTextAmount.current.currency) {
|
|
@@ -2946,7 +2963,7 @@ function OrderDetail({
|
|
|
2946
2963
|
}
|
|
2947
2964
|
|
|
2948
2965
|
// src/register/cash-count-input.tsx
|
|
2949
|
-
import { useState as
|
|
2966
|
+
import { useState as useState9 } from "react";
|
|
2950
2967
|
import { Text as Text26, View as View42 } from "react-native";
|
|
2951
2968
|
import { cn as cn71 } from "@tallyui/theme";
|
|
2952
2969
|
import { jsx as jsx74, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
@@ -2971,7 +2988,7 @@ function CashCountInput({
|
|
|
2971
2988
|
className,
|
|
2972
2989
|
...viewProps
|
|
2973
2990
|
}) {
|
|
2974
|
-
const [internalCounts, setInternalCounts] =
|
|
2991
|
+
const [internalCounts, setInternalCounts] = useState9(defaultCounts ?? {});
|
|
2975
2992
|
const counts = controlledCounts ?? internalCounts;
|
|
2976
2993
|
const total = denominations2.reduce(
|
|
2977
2994
|
(sum, d) => sum + d.value * (counts[d.value] ?? 0),
|
|
@@ -3118,7 +3135,7 @@ function RegisterPicker({ registers, onPick, className }) {
|
|
|
3118
3135
|
}
|
|
3119
3136
|
|
|
3120
3137
|
// src/register/open-register-card.tsx
|
|
3121
|
-
import { useState as
|
|
3138
|
+
import { useState as useState10 } from "react";
|
|
3122
3139
|
import { View as View45 } from "react-native";
|
|
3123
3140
|
import { parseMinor, validAmount } from "@tallyui/pos";
|
|
3124
3141
|
import { currencySymbol, formatMoney as formatMoney6, minorUnitDigits as minorUnitDigits3 } from "@tallyui/core";
|
|
@@ -3129,10 +3146,10 @@ function OpenRegisterCard({ register, currency, configuredFloatMinor, className
|
|
|
3129
3146
|
const digits = minorUnitDigits3(currency);
|
|
3130
3147
|
const lastCountedMinor = register.lastClosure?.counted?.cash ?? null;
|
|
3131
3148
|
const expectedFloatMinor = configuredFloatMinor ?? lastCountedMinor ?? null;
|
|
3132
|
-
const [enteredAmount, setEnteredAmount] =
|
|
3149
|
+
const [enteredAmount, setEnteredAmount] = useState10(null);
|
|
3133
3150
|
const amount = enteredAmount ?? (expectedFloatMinor != null ? decimal(expectedFloatMinor, digits) : "");
|
|
3134
|
-
const [busy, setBusy] =
|
|
3135
|
-
const [error, setError] =
|
|
3151
|
+
const [busy, setBusy] = useState10(false);
|
|
3152
|
+
const [error, setError] = useState10("");
|
|
3136
3153
|
const valid = validAmount(amount, digits);
|
|
3137
3154
|
const countedMinor = valid ? parseMinor(amount, digits) : NaN;
|
|
3138
3155
|
const open = async () => {
|
|
@@ -3267,7 +3284,7 @@ function RegisterBar({
|
|
|
3267
3284
|
}
|
|
3268
3285
|
|
|
3269
3286
|
// src/register/movement-sheet.tsx
|
|
3270
|
-
import { useRef as useRef3, useState as
|
|
3287
|
+
import { useRef as useRef3, useState as useState11 } from "react";
|
|
3271
3288
|
import { movementFieldError, normalizeAmount, parseMinor as parseMinor2 } from "@tallyui/pos";
|
|
3272
3289
|
import { currencySymbol as currencySymbol2, minorUnitDigits as minorUnitDigits4 } from "@tallyui/core";
|
|
3273
3290
|
import { Fragment as Fragment3, jsx as jsx80, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
@@ -3280,11 +3297,11 @@ var AMOUNT_LABEL_ID2 = "movement-amount-label";
|
|
|
3280
3297
|
var REASON_LABEL_ID = "movement-reason-label";
|
|
3281
3298
|
function MovementSheet({ register, type, currency, onDone, onOpenChange, onOpenDrawer }) {
|
|
3282
3299
|
const digits = minorUnitDigits4(currency);
|
|
3283
|
-
const [amount, setAmount] =
|
|
3284
|
-
const [reason, setReason] =
|
|
3285
|
-
const [busy, setBusy] =
|
|
3300
|
+
const [amount, setAmount] = useState11("");
|
|
3301
|
+
const [reason, setReason] = useState11("");
|
|
3302
|
+
const [busy, setBusy] = useState11(false);
|
|
3286
3303
|
const busyRef = useRef3(false);
|
|
3287
|
-
const [error, setError] =
|
|
3304
|
+
const [error, setError] = useState11("");
|
|
3288
3305
|
const invalid = movementFieldError({ type, amount, reason });
|
|
3289
3306
|
const confirm = async () => {
|
|
3290
3307
|
if (invalid || busyRef.current) return;
|
|
@@ -3341,194 +3358,13 @@ function MovementSheet({ register, type, currency, onDone, onOpenChange, onOpenD
|
|
|
3341
3358
|
}
|
|
3342
3359
|
|
|
3343
3360
|
// src/register/register-panel.tsx
|
|
3344
|
-
import { useState as
|
|
3345
|
-
import { ScrollView as
|
|
3346
|
-
import { formatMoney as
|
|
3347
|
-
import { jsx as jsx81, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3348
|
-
var DIALOG_VERTICAL_MARGIN = 64;
|
|
3349
|
-
function RegisterPanel({ register, currency, registerName, open, onOpenChange, className }) {
|
|
3350
|
-
const { session, expected, salesCount, blind, movements, lastClosure, actions } = register;
|
|
3351
|
-
const { height: windowHeight } = useWindowDimensions();
|
|
3352
|
-
const maxHeight = Math.max(280, windowHeight - DIALOG_VERTICAL_MARGIN);
|
|
3353
|
-
const [movementType, setMovementType] = useState11(null);
|
|
3354
|
-
const [expanded, setExpanded] = useState11(false);
|
|
3355
|
-
const [error, setError] = useState11("");
|
|
3356
|
-
const attempt = async (action) => {
|
|
3357
|
-
try {
|
|
3358
|
-
await action();
|
|
3359
|
-
} catch (e) {
|
|
3360
|
-
setError(String(e));
|
|
3361
|
-
}
|
|
3362
|
-
};
|
|
3363
|
-
if (!session && !lastClosure) return null;
|
|
3364
|
-
const activeMovements = movements.filter(
|
|
3365
|
-
(row) => row.type !== "void" && !row.voided_by && !movements.some((entry) => entry.voids === row.id)
|
|
3366
|
-
);
|
|
3367
|
-
return /* @__PURE__ */ jsx81(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs40(
|
|
3368
|
-
DialogContent,
|
|
3369
|
-
{
|
|
3370
|
-
testID: "register-panel",
|
|
3371
|
-
className: `flex-col overflow-hidden ${className ?? ""}`,
|
|
3372
|
-
style: { maxHeight },
|
|
3373
|
-
children: [
|
|
3374
|
-
/* @__PURE__ */ jsxs40(View47, { className: "flex-row items-center justify-between gap-2", children: [
|
|
3375
|
-
/* @__PURE__ */ jsx81(DialogTitle, { testID: "register-panel-amount", children: registerName ?? "Register" }),
|
|
3376
|
-
/* @__PURE__ */ jsx81(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx81(IconButton, { variant: "ghost", size: "sm", testID: "register-panel-dismiss", accessibilityLabel: "Close panel", children: /* @__PURE__ */ jsx81(XIcon, { size: 16 }) }) })
|
|
3377
|
-
] }),
|
|
3378
|
-
/* @__PURE__ */ jsxs40(Text, { testID: "register-panel-sales-count", children: [
|
|
3379
|
-
salesCount,
|
|
3380
|
-
" sale",
|
|
3381
|
-
salesCount === 1 ? "" : "s",
|
|
3382
|
-
" this session"
|
|
3383
|
-
] }),
|
|
3384
|
-
/* @__PURE__ */ jsxs40(ScrollView11, { testID: "register-panel-body", className: "flex-1", contentContainerClassName: "gap-3", children: [
|
|
3385
|
-
/* @__PURE__ */ jsx81(View47, { className: "flex-row gap-2", children: ["paid_in", "paid_out", "no_sale"].map((type) => /* @__PURE__ */ jsx81(
|
|
3386
|
-
Button,
|
|
3387
|
-
{
|
|
3388
|
-
testID: `register-panel-${type.replace("_", "-")}`,
|
|
3389
|
-
className: "min-h-14 flex-1",
|
|
3390
|
-
variant: "outline",
|
|
3391
|
-
disabled: session?.status !== "open",
|
|
3392
|
-
onPress: () => setMovementType(type),
|
|
3393
|
-
children: /* @__PURE__ */ jsx81(Text, { children: type === "paid_in" ? "Paid in" : type === "paid_out" ? "Paid out" : "No sale" })
|
|
3394
|
-
},
|
|
3395
|
-
type
|
|
3396
|
-
)) }),
|
|
3397
|
-
!blind && /* @__PURE__ */ jsxs40(View47, { testID: "register-panel-expected", className: "gap-1", children: [
|
|
3398
|
-
/* @__PURE__ */ jsx81(Text, { className: "text-muted-foreground text-sm font-semibold", children: "Expected in the drawer" }),
|
|
3399
|
-
Object.entries(expected).map(([method, amount]) => /* @__PURE__ */ jsxs40(View47, { className: "min-h-11 flex-row items-center justify-between", children: [
|
|
3400
|
-
/* @__PURE__ */ jsx81(Text, { className: "capitalize", children: method === "cash" ? "Cash" : method }),
|
|
3401
|
-
/* @__PURE__ */ jsx81(Text, { className: "tabular-nums", children: formatMoney7({ amount, currency }) })
|
|
3402
|
-
] }, method))
|
|
3403
|
-
] }),
|
|
3404
|
-
/* @__PURE__ */ jsx81(
|
|
3405
|
-
Button,
|
|
3406
|
-
{
|
|
3407
|
-
testID: "register-panel-movements",
|
|
3408
|
-
variant: "ghost",
|
|
3409
|
-
className: "min-h-11",
|
|
3410
|
-
onPress: () => setExpanded(!expanded),
|
|
3411
|
-
children: /* @__PURE__ */ jsx81(Text, { children: "Paid in / out" })
|
|
3412
|
-
}
|
|
3413
|
-
),
|
|
3414
|
-
expanded && activeMovements.map((row) => /* @__PURE__ */ jsxs40(View47, { className: "min-h-11 flex-row items-center gap-2", children: [
|
|
3415
|
-
/* @__PURE__ */ jsxs40(Text, { testID: `movement-row-${row.id}`, className: "flex-1", children: [
|
|
3416
|
-
row.type === "paid_in" ? "Paid in" : row.type === "paid_out" ? "Paid out" : "No sale",
|
|
3417
|
-
!blind ? ` \xB7 ${formatMoney7({ amount: row.amountMinor, currency })} \xB7 ${row.reason}` : ""
|
|
3418
|
-
] }),
|
|
3419
|
-
/* @__PURE__ */ jsx81(
|
|
3420
|
-
Button,
|
|
3421
|
-
{
|
|
3422
|
-
variant: "ghost",
|
|
3423
|
-
className: "min-h-11",
|
|
3424
|
-
testID: `movement-void-${row.id}`,
|
|
3425
|
-
disabled: session?.status !== "open",
|
|
3426
|
-
onPress: () => attempt(() => actions.voidMovement(row.id)),
|
|
3427
|
-
children: /* @__PURE__ */ jsx81(Text, { children: "Undo" })
|
|
3428
|
-
}
|
|
3429
|
-
)
|
|
3430
|
-
] }, row.id)),
|
|
3431
|
-
!!error && /* @__PURE__ */ jsx81(Text, { className: "text-destructive", children: error }),
|
|
3432
|
-
/* @__PURE__ */ jsx81(
|
|
3433
|
-
Button,
|
|
3434
|
-
{
|
|
3435
|
-
testID: "register-panel-close",
|
|
3436
|
-
disabled: !session,
|
|
3437
|
-
variant: "outline",
|
|
3438
|
-
className: "min-h-14",
|
|
3439
|
-
onPress: () => attempt(async () => {
|
|
3440
|
-
await actions.startCounting();
|
|
3441
|
-
onOpenChange(false);
|
|
3442
|
-
}),
|
|
3443
|
-
children: /* @__PURE__ */ jsx81(Text, { children: "Close register" })
|
|
3444
|
-
}
|
|
3445
|
-
)
|
|
3446
|
-
] }),
|
|
3447
|
-
movementType && /* @__PURE__ */ jsx81(
|
|
3448
|
-
MovementSheet,
|
|
3449
|
-
{
|
|
3450
|
-
register,
|
|
3451
|
-
type: movementType,
|
|
3452
|
-
currency,
|
|
3453
|
-
onOpenChange: (value) => {
|
|
3454
|
-
if (!value) setMovementType(null);
|
|
3455
|
-
},
|
|
3456
|
-
onDone: () => setMovementType(null)
|
|
3457
|
-
}
|
|
3458
|
-
)
|
|
3459
|
-
]
|
|
3460
|
-
}
|
|
3461
|
-
) });
|
|
3462
|
-
}
|
|
3463
|
-
|
|
3464
|
-
// src/register/register-column.tsx
|
|
3465
|
-
import { useState as useState12 } from "react";
|
|
3466
|
-
import { View as View48 } from "react-native";
|
|
3467
|
-
import { Fragment as Fragment4, jsx as jsx82, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
3468
|
-
function RegisterColumn({
|
|
3469
|
-
register,
|
|
3470
|
-
registerId,
|
|
3471
|
-
registers,
|
|
3472
|
-
onPick,
|
|
3473
|
-
currency,
|
|
3474
|
-
configuredFloatMinor,
|
|
3475
|
-
countSlot,
|
|
3476
|
-
cartEmpty,
|
|
3477
|
-
children
|
|
3478
|
-
}) {
|
|
3479
|
-
const [finishing, setFinishing] = useState12(false);
|
|
3480
|
-
if (registerId === null) {
|
|
3481
|
-
return /* @__PURE__ */ jsx82(RegisterPicker, { registers, onPick, className: "flex-1" });
|
|
3482
|
-
}
|
|
3483
|
-
if (register.session?.status === "closed") {
|
|
3484
|
-
return register.closing && !finishing ? /* @__PURE__ */ jsx82(Fragment4, { children: countSlot }) : /* @__PURE__ */ jsx82(FinishClose, { register, busy: finishing, setBusy: setFinishing });
|
|
3485
|
-
}
|
|
3486
|
-
if (!register.session) {
|
|
3487
|
-
return /* @__PURE__ */ jsx82(OpenRegisterCard, { register, currency, configuredFloatMinor, className: "flex-1" });
|
|
3488
|
-
}
|
|
3489
|
-
if (register.session.status === "counting") {
|
|
3490
|
-
return /* @__PURE__ */ jsx82(Fragment4, { children: countSlot });
|
|
3491
|
-
}
|
|
3492
|
-
return /* @__PURE__ */ jsxs41(View48, { testID: "register-column", className: "flex-1", children: [
|
|
3493
|
-
register.overdue && cartEmpty && /* @__PURE__ */ jsxs41(View48, { testID: "register-column-overdue", className: "bg-warning/10 flex-row items-center justify-between gap-2 px-3 py-2", children: [
|
|
3494
|
-
/* @__PURE__ */ jsx82(Text, { className: "flex-1", children: "This register is overdue for closing." }),
|
|
3495
|
-
/* @__PURE__ */ jsx82(
|
|
3496
|
-
Button,
|
|
3497
|
-
{
|
|
3498
|
-
testID: "register-column-close-overdue",
|
|
3499
|
-
variant: "outline",
|
|
3500
|
-
className: "min-h-11",
|
|
3501
|
-
onPress: () => register.actions.startCounting(),
|
|
3502
|
-
children: /* @__PURE__ */ jsx82(Text, { children: "Close register" })
|
|
3503
|
-
}
|
|
3504
|
-
)
|
|
3505
|
-
] }),
|
|
3506
|
-
children
|
|
3507
|
-
] });
|
|
3508
|
-
}
|
|
3509
|
-
function FinishClose({ register, busy, setBusy }) {
|
|
3510
|
-
const [error, setError] = useState12("");
|
|
3511
|
-
const finish = async () => {
|
|
3512
|
-
setBusy(true);
|
|
3513
|
-
setError("");
|
|
3514
|
-
try {
|
|
3515
|
-
await register.actions.closeSession({ counted: register.session?.counted ?? {} });
|
|
3516
|
-
} catch (e) {
|
|
3517
|
-
setError(e instanceof Error ? e.message : String(e));
|
|
3518
|
-
} finally {
|
|
3519
|
-
setBusy(false);
|
|
3520
|
-
}
|
|
3521
|
-
};
|
|
3522
|
-
return /* @__PURE__ */ jsxs41(View48, { testID: "register-column-finish-close", className: "flex-1 gap-3 p-4", children: [
|
|
3523
|
-
/* @__PURE__ */ jsx82(Text, { children: "The last close didn't finish." }),
|
|
3524
|
-
/* @__PURE__ */ jsx82(Button, { testID: "register-column-finish-close-button", nativeID: "register-column-finish-close-button", disabled: busy, onPress: finish, children: /* @__PURE__ */ jsx82(Text, { children: "Finish closing" }) }),
|
|
3525
|
-
!!error && /* @__PURE__ */ jsx82(Text, { testID: "register-column-finish-close-error", className: "text-destructive", children: error })
|
|
3526
|
-
] });
|
|
3527
|
-
}
|
|
3361
|
+
import { useState as useState13 } from "react";
|
|
3362
|
+
import { ScrollView as ScrollView12, useWindowDimensions as useWindowDimensions2, View as View48 } from "react-native";
|
|
3363
|
+
import { formatMoney as formatMoney8 } from "@tallyui/core";
|
|
3528
3364
|
|
|
3529
3365
|
// src/register/register-count.tsx
|
|
3530
|
-
import { useEffect as useEffect3, useRef as useRef4, useState as
|
|
3531
|
-
import { Platform, Pressable as Pressable22, ScrollView as
|
|
3366
|
+
import { useEffect as useEffect3, useRef as useRef4, useState as useState12 } from "react";
|
|
3367
|
+
import { Platform, Pressable as Pressable22, ScrollView as ScrollView11, useWindowDimensions, View as View47 } from "react-native";
|
|
3532
3368
|
import {
|
|
3533
3369
|
closeNeedsApproval,
|
|
3534
3370
|
countVariance,
|
|
@@ -3539,9 +3375,9 @@ import {
|
|
|
3539
3375
|
validAmount as validAmount2,
|
|
3540
3376
|
varianceText
|
|
3541
3377
|
} from "@tallyui/pos";
|
|
3542
|
-
import { currencySymbol as currencySymbol3, formatMoney as
|
|
3378
|
+
import { currencySymbol as currencySymbol3, formatMoney as formatMoney7, minorUnitDigits as minorUnitDigits5 } from "@tallyui/core";
|
|
3543
3379
|
import { cn as cn76 } from "@tallyui/theme";
|
|
3544
|
-
import { jsx as
|
|
3380
|
+
import { jsx as jsx81, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3545
3381
|
var AMOUNT_LABEL_ID3 = "count-amount-label";
|
|
3546
3382
|
var APPROVAL_REQUIRED_TEXT = "Manager approval needed. Ask a manager to approve, or count again.";
|
|
3547
3383
|
function varianceWord(key) {
|
|
@@ -3562,7 +3398,7 @@ function tileLabel(value, currency) {
|
|
|
3562
3398
|
}
|
|
3563
3399
|
return formatter.format(value / 10 ** minorUnitDigits5(currency));
|
|
3564
3400
|
}
|
|
3565
|
-
var tenderLabel = (method) => method.charAt(0).toUpperCase() + method.slice(1);
|
|
3401
|
+
var tenderLabel = (method) => (/* @__PURE__ */ new Map([["cash", "Cash"], ["external", "Card"]])).get(method) ?? method.charAt(0).toUpperCase() + method.slice(1);
|
|
3566
3402
|
function DenominationTile({ value, count: count2, currency, onAdd }) {
|
|
3567
3403
|
const label = tileLabel(value, currency);
|
|
3568
3404
|
const held = useRef4(false);
|
|
@@ -3572,7 +3408,7 @@ function DenominationTile({ value, count: count2, currency, onAdd }) {
|
|
|
3572
3408
|
timer.current = void 0;
|
|
3573
3409
|
};
|
|
3574
3410
|
useEffect3(() => stop, []);
|
|
3575
|
-
return /* @__PURE__ */
|
|
3411
|
+
return /* @__PURE__ */ jsxs40(
|
|
3576
3412
|
Pressable22,
|
|
3577
3413
|
{
|
|
3578
3414
|
testID: `den-tile-${value}`,
|
|
@@ -3594,8 +3430,8 @@ function DenominationTile({ value, count: count2, currency, onAdd }) {
|
|
|
3594
3430
|
if (!held.current) onAdd(1);
|
|
3595
3431
|
},
|
|
3596
3432
|
children: [
|
|
3597
|
-
/* @__PURE__ */
|
|
3598
|
-
/* @__PURE__ */
|
|
3433
|
+
/* @__PURE__ */ jsx81(Text, { className: "text-sm tabular-nums", numberOfLines: 1, children: label }),
|
|
3434
|
+
/* @__PURE__ */ jsx81(Text, { testID: `den-count-${value}`, className: "bg-background min-w-6 rounded-full px-1.5 text-center text-xs tabular-nums", children: count2 })
|
|
3599
3435
|
]
|
|
3600
3436
|
}
|
|
3601
3437
|
);
|
|
@@ -3604,20 +3440,20 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3604
3440
|
const { session, expected, blind, varianceThreshold, actions } = register;
|
|
3605
3441
|
const digits = minorUnitDigits5(currency);
|
|
3606
3442
|
const faces = denominations[currency] ?? denominations.default;
|
|
3607
|
-
const { width } =
|
|
3443
|
+
const { width } = useWindowDimensions();
|
|
3608
3444
|
const columns = width < 480 ? 3 : 4;
|
|
3609
|
-
const [pieces, setPieces] =
|
|
3610
|
-
const [cashText, setCashText] =
|
|
3445
|
+
const [pieces, setPieces] = useState12({});
|
|
3446
|
+
const [cashText, setCashText] = useState12(
|
|
3611
3447
|
() => session?.counted?.cash != null ? minorToDecimal(session.counted.cash, digits) : ""
|
|
3612
3448
|
);
|
|
3613
|
-
const [others, setOthers] =
|
|
3449
|
+
const [others, setOthers] = useState12(
|
|
3614
3450
|
() => Object.fromEntries(
|
|
3615
3451
|
Object.entries(session?.counted ?? {}).filter(([method]) => method !== "cash").map(([method, minor]) => [method, minorToDecimal(minor, digits)])
|
|
3616
3452
|
)
|
|
3617
3453
|
);
|
|
3618
|
-
const [busy, setBusy] =
|
|
3619
|
-
const [error, setError] =
|
|
3620
|
-
const [refusal, setRefusal] =
|
|
3454
|
+
const [busy, setBusy] = useState12(false);
|
|
3455
|
+
const [error, setError] = useState12("");
|
|
3456
|
+
const [refusal, setRefusal] = useState12("");
|
|
3621
3457
|
const usingTiles = Object.keys(pieces).length > 0;
|
|
3622
3458
|
const tilesTotal = denominationTotal(pieces);
|
|
3623
3459
|
const displayAmount = usingTiles ? minorToDecimal(tilesTotal, digits) : cashText;
|
|
@@ -3665,11 +3501,11 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3665
3501
|
void attempt(() => actions.closeSession({ counted }));
|
|
3666
3502
|
};
|
|
3667
3503
|
if (!session) return null;
|
|
3668
|
-
return /* @__PURE__ */
|
|
3669
|
-
/* @__PURE__ */
|
|
3670
|
-
/* @__PURE__ */
|
|
3671
|
-
/* @__PURE__ */
|
|
3672
|
-
/* @__PURE__ */
|
|
3504
|
+
return /* @__PURE__ */ jsxs40(View47, { testID: "register-count", className: cn76("bg-card flex-1 rounded-md", className), children: [
|
|
3505
|
+
/* @__PURE__ */ jsxs40(ScrollView11, { className: "flex-1", contentContainerClassName: "gap-3 p-4", children: [
|
|
3506
|
+
/* @__PURE__ */ jsx81(Text, { className: "min-h-11 text-lg font-semibold", children: "Count the drawer" }),
|
|
3507
|
+
/* @__PURE__ */ jsx81(Label, { testID: "count-amount-label", nativeID: AMOUNT_LABEL_ID3, children: `Cash counted (${currencySymbol3(currency)})` }),
|
|
3508
|
+
/* @__PURE__ */ jsx81(Input, { children: /* @__PURE__ */ jsx81(
|
|
3673
3509
|
Input.Field,
|
|
3674
3510
|
{
|
|
3675
3511
|
testID: "count-amount",
|
|
@@ -3683,16 +3519,16 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3683
3519
|
accessibilityLabelledBy: AMOUNT_LABEL_ID3
|
|
3684
3520
|
}
|
|
3685
3521
|
) }),
|
|
3686
|
-
!blind && valid && /* @__PURE__ */
|
|
3522
|
+
!blind && valid && /* @__PURE__ */ jsx81(Text, { testID: "count-variance", className: "min-h-11 tabular-nums", children: `Expected ${formatMoney7({ amount: expected.cash ?? 0, currency })} \xB7 ${varianceText(
|
|
3687
3523
|
variance,
|
|
3688
3524
|
digits,
|
|
3689
|
-
(major) =>
|
|
3525
|
+
(major) => formatMoney7({ amount: Math.round(major * 10 ** digits), currency }) ?? "",
|
|
3690
3526
|
varianceWord
|
|
3691
3527
|
)}` }),
|
|
3692
|
-
/* @__PURE__ */
|
|
3693
|
-
Array.from({ length: Math.ceil(faces.length / columns) }, (_, row) => /* @__PURE__ */
|
|
3528
|
+
/* @__PURE__ */ jsxs40(View47, { testID: "count-denominations", className: "gap-2", children: [
|
|
3529
|
+
Array.from({ length: Math.ceil(faces.length / columns) }, (_, row) => /* @__PURE__ */ jsx81(View47, { className: "flex-row gap-2", children: Array.from({ length: columns }, (_2, column) => {
|
|
3694
3530
|
const value = faces[row * columns + column];
|
|
3695
|
-
return value ? /* @__PURE__ */
|
|
3531
|
+
return value ? /* @__PURE__ */ jsx81(
|
|
3696
3532
|
DenominationTile,
|
|
3697
3533
|
{
|
|
3698
3534
|
value,
|
|
@@ -3701,9 +3537,9 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3701
3537
|
onAdd: (n) => addPieces(value, n)
|
|
3702
3538
|
},
|
|
3703
3539
|
value
|
|
3704
|
-
) : /* @__PURE__ */
|
|
3540
|
+
) : /* @__PURE__ */ jsx81(View47, { className: "flex-1" }, column);
|
|
3705
3541
|
}) }, row)),
|
|
3706
|
-
/* @__PURE__ */
|
|
3542
|
+
/* @__PURE__ */ jsx81(
|
|
3707
3543
|
Button,
|
|
3708
3544
|
{
|
|
3709
3545
|
testID: "count-clear",
|
|
@@ -3713,16 +3549,16 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3713
3549
|
setPieces({});
|
|
3714
3550
|
setCashText(minorToDecimal(0, digits));
|
|
3715
3551
|
},
|
|
3716
|
-
children: /* @__PURE__ */
|
|
3552
|
+
children: /* @__PURE__ */ jsx81(Text, { children: "Clear" })
|
|
3717
3553
|
}
|
|
3718
3554
|
)
|
|
3719
3555
|
] }),
|
|
3720
3556
|
otherMethods.map((method) => {
|
|
3721
3557
|
const labelId = `count-tender-${method}-label`;
|
|
3722
3558
|
const label = `${tenderLabel(method)} counted (${currencySymbol3(currency)})`;
|
|
3723
|
-
return /* @__PURE__ */
|
|
3724
|
-
/* @__PURE__ */
|
|
3725
|
-
/* @__PURE__ */
|
|
3559
|
+
return /* @__PURE__ */ jsxs40(View47, { className: "gap-2", children: [
|
|
3560
|
+
/* @__PURE__ */ jsx81(Label, { testID: labelId, nativeID: labelId, children: label }),
|
|
3561
|
+
/* @__PURE__ */ jsx81(Input, { children: /* @__PURE__ */ jsx81(
|
|
3726
3562
|
Input.Field,
|
|
3727
3563
|
{
|
|
3728
3564
|
testID: `count-tender-${method}`,
|
|
@@ -3735,11 +3571,11 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3735
3571
|
) })
|
|
3736
3572
|
] }, method);
|
|
3737
3573
|
}),
|
|
3738
|
-
!!refusal && /* @__PURE__ */
|
|
3739
|
-
!!error && /* @__PURE__ */
|
|
3574
|
+
!!refusal && /* @__PURE__ */ jsx81(Text, { testID: "count-manager-line", className: "text-warning min-h-11", children: refusal }),
|
|
3575
|
+
!!error && /* @__PURE__ */ jsx81(Text, { testID: "count-error", className: "text-destructive min-h-11", children: error })
|
|
3740
3576
|
] }),
|
|
3741
|
-
/* @__PURE__ */
|
|
3742
|
-
/* @__PURE__ */
|
|
3577
|
+
/* @__PURE__ */ jsxs40(View47, { className: "gap-2 border-t border-border p-4", children: [
|
|
3578
|
+
/* @__PURE__ */ jsx81(
|
|
3743
3579
|
Button,
|
|
3744
3580
|
{
|
|
3745
3581
|
testID: "count-back",
|
|
@@ -3747,25 +3583,216 @@ function RegisterCount({ register, currency, approve, className }) {
|
|
|
3747
3583
|
className: "min-h-11",
|
|
3748
3584
|
disabled: busy,
|
|
3749
3585
|
onPress: () => attempt(actions.backToSelling),
|
|
3750
|
-
children: /* @__PURE__ */
|
|
3586
|
+
children: /* @__PURE__ */ jsx81(Text, { children: "Back to selling" })
|
|
3751
3587
|
}
|
|
3752
3588
|
),
|
|
3753
|
-
/* @__PURE__ */
|
|
3589
|
+
/* @__PURE__ */ jsx81(
|
|
3754
3590
|
Button,
|
|
3755
3591
|
{
|
|
3756
3592
|
testID: "count-close",
|
|
3757
3593
|
className: "min-h-14",
|
|
3758
3594
|
disabled: !valid || otherTendersInvalid || busy,
|
|
3759
3595
|
onPress: onClose,
|
|
3760
|
-
children: /* @__PURE__ */
|
|
3596
|
+
children: /* @__PURE__ */ jsx81(Text, { children: busy ? "Closing\u2026" : "Close register" })
|
|
3761
3597
|
}
|
|
3762
3598
|
)
|
|
3763
3599
|
] })
|
|
3764
3600
|
] });
|
|
3765
3601
|
}
|
|
3766
3602
|
|
|
3603
|
+
// src/register/register-panel.tsx
|
|
3604
|
+
import { jsx as jsx82, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
3605
|
+
var DIALOG_VERTICAL_MARGIN = 64;
|
|
3606
|
+
function RegisterPanel({ register, currency, registerName, open, onOpenChange, className }) {
|
|
3607
|
+
const { session, expected, salesCount, blind, movements, lastClosure, actions } = register;
|
|
3608
|
+
const otherTenders = Object.entries(expected).filter(([method, amount]) => method !== "cash" && amount !== 0);
|
|
3609
|
+
const { height: windowHeight } = useWindowDimensions2();
|
|
3610
|
+
const maxHeight = Math.max(280, windowHeight - DIALOG_VERTICAL_MARGIN);
|
|
3611
|
+
const [movementType, setMovementType] = useState13(null);
|
|
3612
|
+
const [expanded, setExpanded] = useState13(false);
|
|
3613
|
+
const [error, setError] = useState13("");
|
|
3614
|
+
const attempt = async (action) => {
|
|
3615
|
+
try {
|
|
3616
|
+
await action();
|
|
3617
|
+
} catch (e) {
|
|
3618
|
+
setError(String(e));
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
if (!session && !lastClosure) return null;
|
|
3622
|
+
const activeMovements = movements.filter(
|
|
3623
|
+
(row) => row.type !== "void" && !row.voided_by && !movements.some((entry) => entry.voids === row.id)
|
|
3624
|
+
);
|
|
3625
|
+
return /* @__PURE__ */ jsx82(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs41(
|
|
3626
|
+
DialogContent,
|
|
3627
|
+
{
|
|
3628
|
+
testID: "register-panel",
|
|
3629
|
+
className: `flex-col overflow-hidden ${className ?? ""}`,
|
|
3630
|
+
style: { maxHeight },
|
|
3631
|
+
children: [
|
|
3632
|
+
/* @__PURE__ */ jsxs41(View48, { className: "flex-row items-center justify-between gap-2", children: [
|
|
3633
|
+
/* @__PURE__ */ jsx82(DialogTitle, { testID: "register-panel-amount", children: registerName ?? "Register" }),
|
|
3634
|
+
/* @__PURE__ */ jsx82(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx82(IconButton, { variant: "ghost", size: "sm", testID: "register-panel-dismiss", accessibilityLabel: "Close panel", children: /* @__PURE__ */ jsx82(XIcon, { size: 16 }) }) })
|
|
3635
|
+
] }),
|
|
3636
|
+
/* @__PURE__ */ jsxs41(Text, { testID: "register-panel-sales-count", children: [
|
|
3637
|
+
salesCount,
|
|
3638
|
+
" sale",
|
|
3639
|
+
salesCount === 1 ? "" : "s",
|
|
3640
|
+
" this session"
|
|
3641
|
+
] }),
|
|
3642
|
+
/* @__PURE__ */ jsxs41(ScrollView12, { testID: "register-panel-body", className: "flex-1", contentContainerClassName: "gap-3", children: [
|
|
3643
|
+
/* @__PURE__ */ jsx82(View48, { className: "flex-row gap-2", children: ["paid_in", "paid_out", "no_sale"].map((type) => /* @__PURE__ */ jsx82(
|
|
3644
|
+
Button,
|
|
3645
|
+
{
|
|
3646
|
+
testID: `register-panel-${type.replace("_", "-")}`,
|
|
3647
|
+
className: "min-h-14 flex-1",
|
|
3648
|
+
variant: "outline",
|
|
3649
|
+
disabled: session?.status !== "open",
|
|
3650
|
+
onPress: () => setMovementType(type),
|
|
3651
|
+
children: /* @__PURE__ */ jsx82(Text, { children: type === "paid_in" ? "Paid in" : type === "paid_out" ? "Paid out" : "No sale" })
|
|
3652
|
+
},
|
|
3653
|
+
type
|
|
3654
|
+
)) }),
|
|
3655
|
+
!blind && /* @__PURE__ */ jsxs41(View48, { testID: "register-panel-expected", className: "gap-1", children: [
|
|
3656
|
+
/* @__PURE__ */ jsx82(Text, { className: "text-muted-foreground text-sm font-semibold", children: "Expected in the drawer" }),
|
|
3657
|
+
Object.entries(expected).filter(([method]) => method === "cash").map(([method, amount]) => /* @__PURE__ */ jsxs41(View48, { className: "min-h-11 flex-row items-center justify-between", children: [
|
|
3658
|
+
/* @__PURE__ */ jsx82(Text, { children: tenderLabel("cash") }),
|
|
3659
|
+
/* @__PURE__ */ jsx82(Text, { className: "tabular-nums", children: formatMoney8({ amount, currency }) })
|
|
3660
|
+
] }, method))
|
|
3661
|
+
] }),
|
|
3662
|
+
!blind && otherTenders.length > 0 && /* @__PURE__ */ jsxs41(View48, { testID: "register-panel-other-tenders", className: "gap-1", children: [
|
|
3663
|
+
/* @__PURE__ */ jsx82(Text, { className: "text-muted-foreground text-sm font-semibold", children: "Other tenders" }),
|
|
3664
|
+
otherTenders.map(([method, amount]) => /* @__PURE__ */ jsxs41(View48, { className: "min-h-11 flex-row items-center justify-between", children: [
|
|
3665
|
+
/* @__PURE__ */ jsx82(Text, { children: tenderLabel(method) }),
|
|
3666
|
+
/* @__PURE__ */ jsx82(Text, { className: "tabular-nums", children: formatMoney8({ amount, currency }) })
|
|
3667
|
+
] }, method))
|
|
3668
|
+
] }),
|
|
3669
|
+
/* @__PURE__ */ jsx82(
|
|
3670
|
+
Button,
|
|
3671
|
+
{
|
|
3672
|
+
testID: "register-panel-movements",
|
|
3673
|
+
variant: "ghost",
|
|
3674
|
+
className: "min-h-11",
|
|
3675
|
+
onPress: () => setExpanded(!expanded),
|
|
3676
|
+
children: /* @__PURE__ */ jsx82(Text, { children: "Paid in / out" })
|
|
3677
|
+
}
|
|
3678
|
+
),
|
|
3679
|
+
expanded && activeMovements.map((row) => /* @__PURE__ */ jsxs41(View48, { className: "min-h-11 flex-row items-center gap-2", children: [
|
|
3680
|
+
/* @__PURE__ */ jsxs41(Text, { testID: `movement-row-${row.id}`, className: "flex-1", children: [
|
|
3681
|
+
row.type === "paid_in" ? "Paid in" : row.type === "paid_out" ? "Paid out" : "No sale",
|
|
3682
|
+
!blind ? ` \xB7 ${formatMoney8({ amount: row.amountMinor, currency })} \xB7 ${row.reason}` : ""
|
|
3683
|
+
] }),
|
|
3684
|
+
/* @__PURE__ */ jsx82(
|
|
3685
|
+
Button,
|
|
3686
|
+
{
|
|
3687
|
+
variant: "ghost",
|
|
3688
|
+
className: "min-h-11",
|
|
3689
|
+
testID: `movement-void-${row.id}`,
|
|
3690
|
+
disabled: session?.status !== "open",
|
|
3691
|
+
onPress: () => attempt(() => actions.voidMovement(row.id)),
|
|
3692
|
+
children: /* @__PURE__ */ jsx82(Text, { children: "Undo" })
|
|
3693
|
+
}
|
|
3694
|
+
)
|
|
3695
|
+
] }, row.id)),
|
|
3696
|
+
!!error && /* @__PURE__ */ jsx82(Text, { className: "text-destructive", children: error }),
|
|
3697
|
+
/* @__PURE__ */ jsx82(
|
|
3698
|
+
Button,
|
|
3699
|
+
{
|
|
3700
|
+
testID: "register-panel-close",
|
|
3701
|
+
disabled: !session,
|
|
3702
|
+
variant: "outline",
|
|
3703
|
+
className: "min-h-14",
|
|
3704
|
+
onPress: () => attempt(async () => {
|
|
3705
|
+
await actions.startCounting();
|
|
3706
|
+
onOpenChange(false);
|
|
3707
|
+
}),
|
|
3708
|
+
children: /* @__PURE__ */ jsx82(Text, { children: "Close register" })
|
|
3709
|
+
}
|
|
3710
|
+
)
|
|
3711
|
+
] }),
|
|
3712
|
+
movementType && /* @__PURE__ */ jsx82(
|
|
3713
|
+
MovementSheet,
|
|
3714
|
+
{
|
|
3715
|
+
register,
|
|
3716
|
+
type: movementType,
|
|
3717
|
+
currency,
|
|
3718
|
+
onOpenChange: (value) => {
|
|
3719
|
+
if (!value) setMovementType(null);
|
|
3720
|
+
},
|
|
3721
|
+
onDone: () => setMovementType(null)
|
|
3722
|
+
}
|
|
3723
|
+
)
|
|
3724
|
+
]
|
|
3725
|
+
}
|
|
3726
|
+
) });
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3729
|
+
// src/register/register-column.tsx
|
|
3730
|
+
import { useState as useState14 } from "react";
|
|
3731
|
+
import { View as View49 } from "react-native";
|
|
3732
|
+
import { Fragment as Fragment4, jsx as jsx83, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
3733
|
+
function RegisterColumn({
|
|
3734
|
+
register,
|
|
3735
|
+
registerId,
|
|
3736
|
+
registers,
|
|
3737
|
+
onPick,
|
|
3738
|
+
currency,
|
|
3739
|
+
configuredFloatMinor,
|
|
3740
|
+
countSlot,
|
|
3741
|
+
cartEmpty,
|
|
3742
|
+
children
|
|
3743
|
+
}) {
|
|
3744
|
+
const [finishing, setFinishing] = useState14(false);
|
|
3745
|
+
if (registerId === null) {
|
|
3746
|
+
return /* @__PURE__ */ jsx83(RegisterPicker, { registers, onPick, className: "flex-1" });
|
|
3747
|
+
}
|
|
3748
|
+
if (register.session?.status === "closed") {
|
|
3749
|
+
return register.closing && !finishing ? /* @__PURE__ */ jsx83(Fragment4, { children: countSlot }) : /* @__PURE__ */ jsx83(FinishClose, { register, busy: finishing, setBusy: setFinishing });
|
|
3750
|
+
}
|
|
3751
|
+
if (!register.session) {
|
|
3752
|
+
return /* @__PURE__ */ jsx83(OpenRegisterCard, { register, currency, configuredFloatMinor, className: "flex-1" });
|
|
3753
|
+
}
|
|
3754
|
+
if (register.session.status === "counting") {
|
|
3755
|
+
return /* @__PURE__ */ jsx83(Fragment4, { children: countSlot });
|
|
3756
|
+
}
|
|
3757
|
+
return /* @__PURE__ */ jsxs42(View49, { testID: "register-column", className: "flex-1", children: [
|
|
3758
|
+
register.overdue && cartEmpty && /* @__PURE__ */ jsxs42(View49, { testID: "register-column-overdue", className: "bg-warning/10 flex-row items-center justify-between gap-2 px-3 py-2", children: [
|
|
3759
|
+
/* @__PURE__ */ jsx83(Text, { className: "flex-1", children: "This register is overdue for closing." }),
|
|
3760
|
+
/* @__PURE__ */ jsx83(
|
|
3761
|
+
Button,
|
|
3762
|
+
{
|
|
3763
|
+
testID: "register-column-close-overdue",
|
|
3764
|
+
variant: "outline",
|
|
3765
|
+
className: "min-h-11",
|
|
3766
|
+
onPress: () => register.actions.startCounting(),
|
|
3767
|
+
children: /* @__PURE__ */ jsx83(Text, { children: "Close register" })
|
|
3768
|
+
}
|
|
3769
|
+
)
|
|
3770
|
+
] }),
|
|
3771
|
+
children
|
|
3772
|
+
] });
|
|
3773
|
+
}
|
|
3774
|
+
function FinishClose({ register, busy, setBusy }) {
|
|
3775
|
+
const [error, setError] = useState14("");
|
|
3776
|
+
const finish = async () => {
|
|
3777
|
+
setBusy(true);
|
|
3778
|
+
setError("");
|
|
3779
|
+
try {
|
|
3780
|
+
await register.actions.closeSession({ counted: register.session?.counted ?? {} });
|
|
3781
|
+
} catch (e) {
|
|
3782
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
3783
|
+
} finally {
|
|
3784
|
+
setBusy(false);
|
|
3785
|
+
}
|
|
3786
|
+
};
|
|
3787
|
+
return /* @__PURE__ */ jsxs42(View49, { testID: "register-column-finish-close", className: "flex-1 gap-3 p-4", children: [
|
|
3788
|
+
/* @__PURE__ */ jsx83(Text, { children: "The last close didn't finish." }),
|
|
3789
|
+
/* @__PURE__ */ jsx83(Button, { testID: "register-column-finish-close-button", nativeID: "register-column-finish-close-button", disabled: busy, onPress: finish, children: /* @__PURE__ */ jsx83(Text, { children: "Finish closing" }) }),
|
|
3790
|
+
!!error && /* @__PURE__ */ jsx83(Text, { testID: "register-column-finish-close-error", className: "text-destructive", children: error })
|
|
3791
|
+
] });
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3767
3794
|
// src/register/closure-sheet.tsx
|
|
3768
|
-
import { useEffect as useEffect4, useRef as useRef5, useState as
|
|
3795
|
+
import { useEffect as useEffect4, useRef as useRef5, useState as useState15 } from "react";
|
|
3769
3796
|
import { AccessibilityInfo, Animated, View as View50 } from "react-native";
|
|
3770
3797
|
import { countVariance as countVariance2, TAX_ROUNDING_MIXED_NOTE, varianceText as varianceText2 } from "@tallyui/pos";
|
|
3771
3798
|
import { formatMoney as formatMoney9, minorUnitDigits as minorUnitDigits6 } from "@tallyui/core";
|
|
@@ -3797,8 +3824,8 @@ function ClosureSheet({ register, currency, onPrint, onDone }) {
|
|
|
3797
3824
|
const { lastClosure: closure, blind } = register;
|
|
3798
3825
|
const digits = minorUnitDigits6(currency);
|
|
3799
3826
|
const { opacity, scale } = useCompletionMotion();
|
|
3800
|
-
const [busy, setBusy] =
|
|
3801
|
-
const [error, setError] =
|
|
3827
|
+
const [busy, setBusy] = useState15(false);
|
|
3828
|
+
const [error, setError] = useState15("");
|
|
3802
3829
|
if (!closure) return null;
|
|
3803
3830
|
const methods = Object.keys(closure.counted);
|
|
3804
3831
|
const approvedByName = closure.breakdowns.approved_by_name;
|
|
@@ -3935,12 +3962,12 @@ function ConnectorStatus({
|
|
|
3935
3962
|
}
|
|
3936
3963
|
|
|
3937
3964
|
// src/sale/cart.tsx
|
|
3938
|
-
import { useState as
|
|
3939
|
-
import { Pressable as
|
|
3965
|
+
import { useState as useState18 } from "react";
|
|
3966
|
+
import { Pressable as Pressable25, Text as Text34, View as View56 } from "react-native";
|
|
3940
3967
|
import { buildReceiptData } from "@tallyui/pos";
|
|
3941
3968
|
|
|
3942
3969
|
// src/sale/discount-form.tsx
|
|
3943
|
-
import { useState as
|
|
3970
|
+
import { useState as useState16 } from "react";
|
|
3944
3971
|
import { Pressable as Pressable23, Text as Text32, TextInput as TextInput9, View as View54 } from "react-native";
|
|
3945
3972
|
import { formatMoney as formatMoney10, minorUnitDigits as minorUnitDigits7, moneyFromDecimalString as moneyFromDecimalString2 } from "@tallyui/core";
|
|
3946
3973
|
import { jsx as jsx88, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
@@ -3960,9 +3987,9 @@ function parseDiscount(type, text, currency) {
|
|
|
3960
3987
|
var discountLabel = (discount, currency) => discount.type === "percentage" ? `${discount.value}%` : formatMoney10({ amount: discount.amountMinor, currency }) ?? String(discount.amountMinor);
|
|
3961
3988
|
var button = "rounded-md border border-border px-3 py-2 min-h-11 items-center justify-center";
|
|
3962
3989
|
function DiscountForm({ title, currency, onApply, onClose }) {
|
|
3963
|
-
const [type, setType] =
|
|
3964
|
-
const [text, setText] =
|
|
3965
|
-
const [error, setError] =
|
|
3990
|
+
const [type, setType] = useState16("percentage");
|
|
3991
|
+
const [text, setText] = useState16("");
|
|
3992
|
+
const [error, setError] = useState16(null);
|
|
3966
3993
|
function apply() {
|
|
3967
3994
|
const parsed = parseDiscount(type, text, currency);
|
|
3968
3995
|
const refused = typeof parsed === "string" ? parsed : onApply(parsed);
|
|
@@ -4030,17 +4057,79 @@ function DiscountChips({ discounts, currency, onRemove, prefix, amounts = [] })
|
|
|
4030
4057
|
}) });
|
|
4031
4058
|
}
|
|
4032
4059
|
|
|
4060
|
+
// src/sale/price-form.tsx
|
|
4061
|
+
import { useState as useState17 } from "react";
|
|
4062
|
+
import { Pressable as Pressable24, Text as Text33, TextInput as TextInput10, View as View55 } from "react-native";
|
|
4063
|
+
import { formatMoney as formatMoney11, minorUnitDigits as minorUnitDigits8, moneyFromDecimalString as moneyFromDecimalString3 } from "@tallyui/core";
|
|
4064
|
+
import { jsx as jsx89, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
4065
|
+
function parsePrice(text, currency) {
|
|
4066
|
+
const raw = text.trim();
|
|
4067
|
+
const trimmed = raw.split(",").length === 2 ? raw.replace(",", ".") : raw;
|
|
4068
|
+
if (!trimmed) return "Enter a price.";
|
|
4069
|
+
if (!/^-?\d+(\.\d+)?$/.test(trimmed)) return "Enter a number.";
|
|
4070
|
+
if (Number(trimmed) < 0) return "Enter a price of 0 or more.";
|
|
4071
|
+
const digits = Math.min(2, minorUnitDigits8(currency));
|
|
4072
|
+
if ((trimmed.split(".")[1]?.length ?? 0) > digits) return digits ? `Use at most ${digits} decimal places.` : "Use a whole amount.";
|
|
4073
|
+
return moneyFromDecimalString3(trimmed, currency)?.amount ?? "Enter a number.";
|
|
4074
|
+
}
|
|
4075
|
+
var button2 = "rounded-md border border-border px-3 py-2 min-h-11 items-center justify-center";
|
|
4076
|
+
function PriceForm({ lineName, currency, currentMinor, onApply, onClose }) {
|
|
4077
|
+
const [text, setText] = useState17("");
|
|
4078
|
+
const [reason, setReason] = useState17("");
|
|
4079
|
+
const [error, setError] = useState17(null);
|
|
4080
|
+
const title = `Price for ${lineName}`;
|
|
4081
|
+
function apply() {
|
|
4082
|
+
const parsed = parsePrice(text, currency);
|
|
4083
|
+
const refused = typeof parsed === "string" ? parsed : onApply(parsed, reason.trim() || void 0);
|
|
4084
|
+
setError(refused);
|
|
4085
|
+
if (refused === null) onClose();
|
|
4086
|
+
}
|
|
4087
|
+
return /* @__PURE__ */ jsxs48(View55, { testID: "price-form", role: "group", accessibilityLabel: title, className: "gap-2 px-3 py-2", children: [
|
|
4088
|
+
/* @__PURE__ */ jsx89(Text33, { className: "font-semibold text-foreground", children: title }),
|
|
4089
|
+
/* @__PURE__ */ jsx89(Text33, { className: "text-muted-foreground", children: formatMoney11({ amount: currentMinor, currency }) }),
|
|
4090
|
+
/* @__PURE__ */ jsx89(
|
|
4091
|
+
TextInput10,
|
|
4092
|
+
{
|
|
4093
|
+
testID: "price-input",
|
|
4094
|
+
accessibilityLabel: "Price value",
|
|
4095
|
+
value: text,
|
|
4096
|
+
onChangeText: setText,
|
|
4097
|
+
onSubmitEditing: apply,
|
|
4098
|
+
inputMode: "decimal",
|
|
4099
|
+
placeholder: currency,
|
|
4100
|
+
className: "min-w-0 rounded-md border border-border bg-background px-3 py-2 text-foreground"
|
|
4101
|
+
}
|
|
4102
|
+
),
|
|
4103
|
+
/* @__PURE__ */ jsx89(
|
|
4104
|
+
TextInput10,
|
|
4105
|
+
{
|
|
4106
|
+
testID: "price-reason",
|
|
4107
|
+
accessibilityLabel: "Reason (optional)",
|
|
4108
|
+
placeholder: "Reason (optional)",
|
|
4109
|
+
value: reason,
|
|
4110
|
+
onChangeText: setReason,
|
|
4111
|
+
className: "min-w-0 rounded-md border border-border bg-background px-3 py-2 text-foreground"
|
|
4112
|
+
}
|
|
4113
|
+
),
|
|
4114
|
+
error ? /* @__PURE__ */ jsx89(Text33, { testID: "price-error", accessibilityRole: "alert", className: "text-destructive", children: error }) : null,
|
|
4115
|
+
/* @__PURE__ */ jsxs48(View55, { className: "flex-row gap-2", children: [
|
|
4116
|
+
/* @__PURE__ */ jsx89(Pressable24, { testID: "price-apply", accessibilityRole: "button", onPress: apply, className: `${button2} bg-primary`, children: /* @__PURE__ */ jsx89(Text33, { className: "font-semibold text-primary-foreground", children: "Apply" }) }),
|
|
4117
|
+
/* @__PURE__ */ jsx89(Pressable24, { testID: "price-cancel", accessibilityRole: "button", onPress: onClose, className: `${button2} bg-card`, children: /* @__PURE__ */ jsx89(Text33, { className: "text-foreground", children: "Cancel" }) })
|
|
4118
|
+
] })
|
|
4119
|
+
] });
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4033
4122
|
// src/sale/cart.tsx
|
|
4034
|
-
import { Fragment as Fragment5, jsx as
|
|
4035
|
-
function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}
|
|
4123
|
+
import { Fragment as Fragment5, jsx as jsx90, jsxs as jsxs49 } from "react/jsx-runtime";
|
|
4124
|
+
function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%`, canEditPrice = false, onPriceChange }) {
|
|
4036
4125
|
const { order } = sale;
|
|
4037
4126
|
const money = (amount) => ({ amount, currency: order.currency });
|
|
4038
4127
|
const { totals } = buildReceiptData(order, { storeName: "" });
|
|
4039
4128
|
const taxRows = totals.taxLines.map((line) => ({ label: taxLabel(line.ratePpm), amount: money(line.amountMinor) }));
|
|
4040
4129
|
const display = (lineId) => order.display.lines.find((line) => line.lineId === lineId);
|
|
4041
4130
|
const orderAmounts = order.discounts.length === 1 ? [{ discountId: order.discounts[0].id, amountMinor: order.display.orderDiscountMinor }] : [];
|
|
4042
|
-
const [form, setForm] =
|
|
4043
|
-
const discountForm = (lineId, title) => form?.lineId === lineId ? /* @__PURE__ */
|
|
4131
|
+
const [form, setForm] = useState18(null);
|
|
4132
|
+
const discountForm = (lineId, title) => form?.type === "discount" && form.lineId === lineId ? /* @__PURE__ */ jsx90(
|
|
4044
4133
|
DiscountForm,
|
|
4045
4134
|
{
|
|
4046
4135
|
title,
|
|
@@ -4051,8 +4140,11 @@ function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%` }) {
|
|
|
4051
4140
|
lineId ?? "order"
|
|
4052
4141
|
) : null;
|
|
4053
4142
|
const pay = `min-h-12 flex-1 justify-center rounded-md bg-primary px-4 py-3 ${!order.lineItems.length ? "opacity-50" : ""}`;
|
|
4054
|
-
const renderItem = (line) => /* @__PURE__ */
|
|
4055
|
-
/* @__PURE__ */
|
|
4143
|
+
const renderItem = (line) => /* @__PURE__ */ jsxs49(Fragment5, { children: [
|
|
4144
|
+
/* @__PURE__ */ jsx90(CartLineActions, { actions: [
|
|
4145
|
+
{ id: "discount", label: "Discount", color: "text-primary", onPress: () => setForm({ type: "discount", lineId: line.id }) },
|
|
4146
|
+
...canEditPrice ? [{ id: "price", label: "Price", color: "text-primary", onPress: () => setForm({ type: "price", lineId: line.id }) }] : []
|
|
4147
|
+
], children: /* @__PURE__ */ jsx90(
|
|
4056
4148
|
CartLine,
|
|
4057
4149
|
{
|
|
4058
4150
|
name: line.name,
|
|
@@ -4061,50 +4153,66 @@ function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%` }) {
|
|
|
4061
4153
|
lineTotal: money(display(line.id).amountMinor)
|
|
4062
4154
|
}
|
|
4063
4155
|
) }),
|
|
4064
|
-
/* @__PURE__ */
|
|
4065
|
-
/* @__PURE__ */
|
|
4066
|
-
/* @__PURE__ */
|
|
4067
|
-
|
|
4156
|
+
/* @__PURE__ */ jsx90(DiscountChips, { discounts: line.discounts, amounts: display(line.id).discounts, currency: order.currency, onRemove: sale.removeDiscount }),
|
|
4157
|
+
/* @__PURE__ */ jsxs49(View56, { className: "flex-row gap-4 px-3 py-2", children: [
|
|
4158
|
+
/* @__PURE__ */ jsx90(
|
|
4159
|
+
Pressable25,
|
|
4068
4160
|
{
|
|
4069
4161
|
accessibilityRole: "button",
|
|
4070
4162
|
accessibilityLabel: `Decrease ${line.name}`,
|
|
4071
4163
|
onPress: () => sale.setQuantity(line.id, line.quantity - 1),
|
|
4072
4164
|
className: "rounded-md border border-border bg-card px-3 py-2 min-h-11 min-w-11 items-center justify-center",
|
|
4073
|
-
children: /* @__PURE__ */
|
|
4165
|
+
children: /* @__PURE__ */ jsx90(Text34, { className: "text-center text-foreground", children: "\u2212" })
|
|
4074
4166
|
}
|
|
4075
4167
|
),
|
|
4076
|
-
/* @__PURE__ */
|
|
4077
|
-
|
|
4168
|
+
/* @__PURE__ */ jsx90(
|
|
4169
|
+
Pressable25,
|
|
4078
4170
|
{
|
|
4079
4171
|
accessibilityRole: "button",
|
|
4080
4172
|
accessibilityLabel: `Increase ${line.name}`,
|
|
4081
4173
|
onPress: () => sale.setQuantity(line.id, line.quantity + 1),
|
|
4082
4174
|
className: "rounded-md border border-border bg-card px-3 py-2 min-h-11 min-w-11 items-center justify-center",
|
|
4083
|
-
children: /* @__PURE__ */
|
|
4175
|
+
children: /* @__PURE__ */ jsx90(Text34, { className: "text-center text-foreground", children: "+" })
|
|
4084
4176
|
}
|
|
4085
4177
|
),
|
|
4086
|
-
/* @__PURE__ */
|
|
4087
|
-
|
|
4178
|
+
/* @__PURE__ */ jsx90(
|
|
4179
|
+
Pressable25,
|
|
4088
4180
|
{
|
|
4089
4181
|
accessibilityRole: "button",
|
|
4090
4182
|
accessibilityLabel: `Remove ${line.name}`,
|
|
4091
4183
|
onPress: () => sale.remove(line.id),
|
|
4092
4184
|
className: "rounded-md border border-border bg-card px-3 py-2 min-h-11 min-w-11 items-center justify-center",
|
|
4093
|
-
children: /* @__PURE__ */
|
|
4185
|
+
children: /* @__PURE__ */ jsx90(Text34, { className: "text-center text-foreground", children: "Remove" })
|
|
4094
4186
|
}
|
|
4095
4187
|
)
|
|
4096
4188
|
] }),
|
|
4097
|
-
discountForm(line.id, `Discount on ${line.name}`)
|
|
4189
|
+
discountForm(line.id, `Discount on ${line.name}`),
|
|
4190
|
+
canEditPrice && form?.type === "price" && form.lineId === line.id ? /* @__PURE__ */ jsx90(
|
|
4191
|
+
PriceForm,
|
|
4192
|
+
{
|
|
4193
|
+
lineName: line.name,
|
|
4194
|
+
currency: order.currency,
|
|
4195
|
+
currentMinor: line.unitPriceMinor,
|
|
4196
|
+
onClose: () => setForm(null),
|
|
4197
|
+
onApply: (amount, reason) => {
|
|
4198
|
+
const fromMinor = line.unitPriceMinor;
|
|
4199
|
+
const refused = sale.setUnitPrice(line.id, amount);
|
|
4200
|
+
if (refused === null) onPriceChange?.({ lineId: line.id, fromMinor, toMinor: amount, reason });
|
|
4201
|
+
return refused;
|
|
4202
|
+
}
|
|
4203
|
+
},
|
|
4204
|
+
line.id
|
|
4205
|
+
) : null
|
|
4098
4206
|
] });
|
|
4099
|
-
return /* @__PURE__ */
|
|
4207
|
+
return /* @__PURE__ */ jsx90(
|
|
4100
4208
|
CartPanel,
|
|
4101
4209
|
{
|
|
4102
4210
|
dataSet: { print: "hide" },
|
|
4103
4211
|
items: order.lineItems,
|
|
4104
4212
|
renderItem,
|
|
4105
|
-
emptyState: /* @__PURE__ */
|
|
4106
|
-
afterItems: order.lineItems.length ? /* @__PURE__ */
|
|
4107
|
-
/* @__PURE__ */
|
|
4213
|
+
emptyState: /* @__PURE__ */ jsx90(Text34, { testID: "cart-empty", className: "px-3 py-2 text-muted-foreground", children: "Scan or tap a product to start a sale." }),
|
|
4214
|
+
afterItems: order.lineItems.length ? /* @__PURE__ */ jsxs49(View56, { className: "gap-3 px-3 py-2", children: [
|
|
4215
|
+
/* @__PURE__ */ jsx90(
|
|
4108
4216
|
DiscountChips,
|
|
4109
4217
|
{
|
|
4110
4218
|
discounts: order.discounts,
|
|
@@ -4114,18 +4222,18 @@ function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%` }) {
|
|
|
4114
4222
|
prefix: "Order discount"
|
|
4115
4223
|
}
|
|
4116
4224
|
),
|
|
4117
|
-
form?.lineId === null ? discountForm(null, "Order discount") : /* @__PURE__ */
|
|
4118
|
-
|
|
4225
|
+
form?.lineId === null ? discountForm(null, "Order discount") : /* @__PURE__ */ jsx90(
|
|
4226
|
+
Pressable25,
|
|
4119
4227
|
{
|
|
4120
4228
|
accessibilityRole: "button",
|
|
4121
|
-
onPress: () => setForm({ lineId: null }),
|
|
4229
|
+
onPress: () => setForm({ type: "discount", lineId: null }),
|
|
4122
4230
|
className: "self-start rounded-md border border-border bg-card px-4 py-2 min-h-11 justify-center",
|
|
4123
|
-
children: /* @__PURE__ */
|
|
4231
|
+
children: /* @__PURE__ */ jsx90(Text34, { className: "text-foreground", children: "Order discount" })
|
|
4124
4232
|
}
|
|
4125
4233
|
)
|
|
4126
4234
|
] }) : void 0,
|
|
4127
|
-
footer: /* @__PURE__ */
|
|
4128
|
-
/* @__PURE__ */
|
|
4235
|
+
footer: /* @__PURE__ */ jsxs49(View56, { testID: "cart-footer", className: "gap-3 pb-4", children: [
|
|
4236
|
+
/* @__PURE__ */ jsx90(
|
|
4129
4237
|
CartTotal,
|
|
4130
4238
|
{
|
|
4131
4239
|
subtotal: money(totals.subtotalMinor),
|
|
@@ -4135,10 +4243,10 @@ function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%` }) {
|
|
|
4135
4243
|
taxInclusive: totals.taxInclusive
|
|
4136
4244
|
}
|
|
4137
4245
|
),
|
|
4138
|
-
sale.error ? /* @__PURE__ */
|
|
4139
|
-
/* @__PURE__ */
|
|
4140
|
-
/* @__PURE__ */
|
|
4141
|
-
/* @__PURE__ */
|
|
4246
|
+
sale.error ? /* @__PURE__ */ jsx90(Text34, { accessibilityRole: "alert", className: "text-destructive", children: sale.error }) : null,
|
|
4247
|
+
/* @__PURE__ */ jsxs49(View56, { className: "flex-row gap-3", children: [
|
|
4248
|
+
/* @__PURE__ */ jsx90(Pressable25, { accessibilityRole: "button", disabled: !order.lineItems.length, onPress: () => sale.startTender("cash"), className: pay, children: /* @__PURE__ */ jsx90(Text34, { className: "text-center font-semibold text-primary-foreground", children: "Cash" }) }),
|
|
4249
|
+
/* @__PURE__ */ jsx90(Pressable25, { accessibilityRole: "button", disabled: !order.lineItems.length, onPress: () => sale.startTender("external"), className: pay, children: /* @__PURE__ */ jsx90(Text34, { className: "text-center font-semibold text-primary-foreground", children: "Card terminal" }) })
|
|
4142
4250
|
] })
|
|
4143
4251
|
] })
|
|
4144
4252
|
}
|
|
@@ -4146,17 +4254,17 @@ function Cart({ sale, taxLabel = (ratePpm) => `Tax ${ratePpm / 1e4}%` }) {
|
|
|
4146
4254
|
}
|
|
4147
4255
|
|
|
4148
4256
|
// src/sale/cart-bar.tsx
|
|
4149
|
-
import { Pressable as
|
|
4150
|
-
import { formatMoney as
|
|
4257
|
+
import { Pressable as Pressable26, Text as Text35 } from "react-native";
|
|
4258
|
+
import { formatMoney as formatMoney12 } from "@tallyui/core";
|
|
4151
4259
|
import { buildReceiptData as buildReceiptData2 } from "@tallyui/pos";
|
|
4152
|
-
import { jsx as
|
|
4260
|
+
import { jsx as jsx91, jsxs as jsxs50 } from "react/jsx-runtime";
|
|
4153
4261
|
function CartBar({ sale, onOpen }) {
|
|
4154
4262
|
const { order } = sale;
|
|
4155
4263
|
const count2 = order.lineItems.reduce((sum, line) => sum + line.quantity, 0);
|
|
4156
|
-
const total =
|
|
4264
|
+
const total = formatMoney12({ amount: buildReceiptData2(order, { storeName: "" }).totals.totalMinor, currency: order.currency });
|
|
4157
4265
|
const items = `${count2} ${count2 === 1 ? "item" : "items"}`;
|
|
4158
|
-
return /* @__PURE__ */
|
|
4159
|
-
|
|
4266
|
+
return /* @__PURE__ */ jsxs50(
|
|
4267
|
+
Pressable26,
|
|
4160
4268
|
{
|
|
4161
4269
|
accessibilityRole: "button",
|
|
4162
4270
|
accessibilityLabel: count2 ? `Open cart, ${items}, ${total}` : "Cart is empty",
|
|
@@ -4164,8 +4272,8 @@ function CartBar({ sale, onOpen }) {
|
|
|
4164
4272
|
onPress: onOpen,
|
|
4165
4273
|
className: `min-h-14 mx-3 mb-3 flex-row items-center justify-between rounded-md bg-primary px-4 py-3 ${count2 ? "" : "opacity-50"}`,
|
|
4166
4274
|
children: [
|
|
4167
|
-
/* @__PURE__ */
|
|
4168
|
-
count2 ? /* @__PURE__ */
|
|
4275
|
+
/* @__PURE__ */ jsx91(Text35, { className: "font-bold text-primary-foreground", children: count2 ? `Cart \xB7 ${items}` : "Cart is empty" }),
|
|
4276
|
+
count2 ? /* @__PURE__ */ jsxs50(Text35, { className: "font-bold text-primary-foreground", children: [
|
|
4169
4277
|
total,
|
|
4170
4278
|
" \u203A"
|
|
4171
4279
|
] }) : null
|
|
@@ -4175,17 +4283,17 @@ function CartBar({ sale, onOpen }) {
|
|
|
4175
4283
|
}
|
|
4176
4284
|
|
|
4177
4285
|
// src/sale/tender.tsx
|
|
4178
|
-
import { Pressable as
|
|
4179
|
-
import { formatMoney as
|
|
4180
|
-
import { Fragment as Fragment6, jsx as
|
|
4286
|
+
import { Pressable as Pressable27, Text as Text36, TextInput as TextInput11, View as View57 } from "react-native";
|
|
4287
|
+
import { formatMoney as formatMoney13 } from "@tallyui/core";
|
|
4288
|
+
import { Fragment as Fragment6, jsx as jsx92, jsxs as jsxs51 } from "react/jsx-runtime";
|
|
4181
4289
|
var REFERENCE_MAX = 255;
|
|
4182
4290
|
function Tender({ sale }) {
|
|
4183
4291
|
const { order, stage } = sale;
|
|
4184
4292
|
if (stage.kind !== "tender") return null;
|
|
4185
4293
|
const money = (amount) => ({ amount, currency: order.currency });
|
|
4186
|
-
return /* @__PURE__ */
|
|
4187
|
-
stage.method === "cash" ? /* @__PURE__ */
|
|
4188
|
-
/* @__PURE__ */
|
|
4294
|
+
return /* @__PURE__ */ jsxs51(View57, { dataSet: { print: "hide" }, className: "gap-4 p-4", children: [
|
|
4295
|
+
stage.method === "cash" ? /* @__PURE__ */ jsxs51(Fragment6, { children: [
|
|
4296
|
+
/* @__PURE__ */ jsx92(
|
|
4189
4297
|
CashTendered,
|
|
4190
4298
|
{
|
|
4191
4299
|
total: money(order.totalMinor),
|
|
@@ -4193,19 +4301,19 @@ function Tender({ sale }) {
|
|
|
4193
4301
|
onChangeAmount: (amount) => sale.setTender({ method: "cash", amountMinor: amount.amount })
|
|
4194
4302
|
}
|
|
4195
4303
|
),
|
|
4196
|
-
/* @__PURE__ */
|
|
4197
|
-
order.balanceDueMinor > 0 ? /* @__PURE__ */
|
|
4304
|
+
/* @__PURE__ */ jsx92(ChangeDisplay, { change: money(order.changeDueMinor) }),
|
|
4305
|
+
order.balanceDueMinor > 0 ? /* @__PURE__ */ jsxs51(Text36, { className: "text-foreground", children: [
|
|
4198
4306
|
"Balance due: ",
|
|
4199
|
-
|
|
4307
|
+
formatMoney13(money(order.balanceDueMinor))
|
|
4200
4308
|
] }) : null,
|
|
4201
|
-
/* @__PURE__ */
|
|
4202
|
-
] }) : /* @__PURE__ */
|
|
4203
|
-
/* @__PURE__ */
|
|
4309
|
+
/* @__PURE__ */ jsx92(Pressable27, { accessibilityRole: "button", disabled: order.balanceDueMinor > 0, onPress: sale.complete, className: `rounded-md bg-primary px-4 py-3 ${order.balanceDueMinor > 0 ? "opacity-50" : ""}`, children: /* @__PURE__ */ jsx92(Text36, { className: "text-center font-semibold text-primary-foreground", children: "Complete sale" }) })
|
|
4310
|
+
] }) : /* @__PURE__ */ jsxs51(Fragment6, { children: [
|
|
4311
|
+
/* @__PURE__ */ jsxs51(Text36, { className: "text-foreground", children: [
|
|
4204
4312
|
"Card terminal: ",
|
|
4205
|
-
|
|
4313
|
+
formatMoney13(money(order.totalMinor))
|
|
4206
4314
|
] }),
|
|
4207
|
-
/* @__PURE__ */
|
|
4208
|
-
|
|
4315
|
+
/* @__PURE__ */ jsx92(
|
|
4316
|
+
TextInput11,
|
|
4209
4317
|
{
|
|
4210
4318
|
accessibilityLabel: "Terminal reference",
|
|
4211
4319
|
placeholder: "Terminal reference",
|
|
@@ -4215,23 +4323,23 @@ function Tender({ sale }) {
|
|
|
4215
4323
|
className: "rounded-md border border-border px-3 py-2 text-foreground"
|
|
4216
4324
|
}
|
|
4217
4325
|
),
|
|
4218
|
-
/* @__PURE__ */
|
|
4326
|
+
/* @__PURE__ */ jsx92(Pressable27, { accessibilityRole: "button", onPress: sale.complete, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx92(Text36, { className: "text-center font-semibold text-primary-foreground", children: "Payment approved on terminal" }) })
|
|
4219
4327
|
] }),
|
|
4220
|
-
sale.error ? /* @__PURE__ */
|
|
4221
|
-
sale.canContinue ? /* @__PURE__ */
|
|
4222
|
-
/* @__PURE__ */
|
|
4223
|
-
/* @__PURE__ */
|
|
4328
|
+
sale.error ? /* @__PURE__ */ jsx92(Text36, { accessibilityRole: "alert", className: "text-destructive", children: sale.error }) : null,
|
|
4329
|
+
sale.canContinue ? /* @__PURE__ */ jsxs51(Fragment6, { children: [
|
|
4330
|
+
/* @__PURE__ */ jsx92(Text36, { className: "text-foreground", children: "This sale is stored and will be sent. Continue to the next sale." }),
|
|
4331
|
+
/* @__PURE__ */ jsx92(Pressable27, { accessibilityRole: "button", onPress: () => sale.continueSale(), className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx92(Text36, { className: "text-center font-semibold text-primary-foreground", children: "Continue" }) })
|
|
4224
4332
|
] }) : null,
|
|
4225
|
-
/* @__PURE__ */
|
|
4333
|
+
/* @__PURE__ */ jsx92(Pressable27, { accessibilityRole: "button", onPress: sale.cancelTender, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx92(Text36, { className: "text-center text-foreground", children: "Back" }) })
|
|
4226
4334
|
] });
|
|
4227
4335
|
}
|
|
4228
4336
|
|
|
4229
4337
|
// src/sale/catalogue.tsx
|
|
4230
|
-
import { useEffect as useEffect5, useMemo as useMemo2, useState as
|
|
4231
|
-
import { Pressable as
|
|
4338
|
+
import { useEffect as useEffect5, useMemo as useMemo2, useState as useState19 } from "react";
|
|
4339
|
+
import { Pressable as Pressable28, Text as Text37, View as View58 } from "react-native";
|
|
4232
4340
|
import { useStockOverlaid, useStockOverlayAsOf } from "@tallyui/core";
|
|
4233
4341
|
import { catalogueEntries, findEntryByCode, searchProducts, variantPriceLabel } from "@tallyui/pos";
|
|
4234
|
-
import { jsx as
|
|
4342
|
+
import { jsx as jsx93, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
4235
4343
|
var MIN_TILE_WIDTH = 160;
|
|
4236
4344
|
var STOCK_LABEL = {
|
|
4237
4345
|
in_stock: "In Stock",
|
|
@@ -4250,16 +4358,16 @@ function laterOf(a, b) {
|
|
|
4250
4358
|
if (!b) return a;
|
|
4251
4359
|
return a.getTime() >= b.getTime() ? a : b;
|
|
4252
4360
|
}
|
|
4253
|
-
function Catalogue({ products, traits, currency, onSelect, statusText, statusAccessory, lastSyncedAt, loading, lastStockCheckAt, hour12, minCodeLength }) {
|
|
4254
|
-
const isLoading = loading ?? lastSyncedAt === null;
|
|
4361
|
+
function Catalogue({ products, traits, currency, onSelect, statusText, statusAccessory, lastSyncedAt, loading, pullError, lastStockCheckAt, hour12, minCodeLength }) {
|
|
4362
|
+
const isLoading = loading ?? (lastSyncedAt === null && pullError == null);
|
|
4255
4363
|
const shown = useStockOverlaid(products);
|
|
4256
4364
|
const overlayAsOf = useStockOverlayAsOf();
|
|
4257
4365
|
const parsedOverlayAsOf = overlayAsOf ? new Date(overlayAsOf) : void 0;
|
|
4258
4366
|
const validOverlayAsOf = parsedOverlayAsOf && !isNaN(parsedOverlayAsOf.getTime()) ? parsedOverlayAsOf : void 0;
|
|
4259
4367
|
const stockAsOf = laterOf(laterOf(lastStockCheckAt, validOverlayAsOf), lastSyncedAt);
|
|
4260
|
-
const [query, setQuery] =
|
|
4261
|
-
const [chooserId, setChooserId] =
|
|
4262
|
-
const [width, setWidth] =
|
|
4368
|
+
const [query, setQuery] = useState19("");
|
|
4369
|
+
const [chooserId, setChooserId] = useState19(null);
|
|
4370
|
+
const [width, setWidth] = useState19(0);
|
|
4263
4371
|
const columns = Math.max(2, Math.min(6, Math.floor((width - 8) / (MIN_TILE_WIDTH + 8))));
|
|
4264
4372
|
const entries = useMemo2(() => catalogueEntries(shown, traits, { currency }), [shown, traits, currency]);
|
|
4265
4373
|
const results = useMemo2(() => searchProducts(shown, query, traits), [shown, query, traits]);
|
|
@@ -4271,9 +4379,9 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4271
4379
|
onSelect(entry);
|
|
4272
4380
|
setChooserId(null);
|
|
4273
4381
|
}
|
|
4274
|
-
return /* @__PURE__ */
|
|
4275
|
-
/* @__PURE__ */
|
|
4276
|
-
/* @__PURE__ */
|
|
4382
|
+
return /* @__PURE__ */ jsxs52(View58, { className: "flex-1", onLayout: (event) => setWidth(event.nativeEvent.layout.width), children: [
|
|
4383
|
+
/* @__PURE__ */ jsxs52(View58, { className: "gap-2 border-b border-border bg-card px-4 pb-3 pt-3", children: [
|
|
4384
|
+
/* @__PURE__ */ jsx93(
|
|
4277
4385
|
SearchInput,
|
|
4278
4386
|
{
|
|
4279
4387
|
value: query,
|
|
@@ -4290,25 +4398,25 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4290
4398
|
}
|
|
4291
4399
|
}
|
|
4292
4400
|
),
|
|
4293
|
-
statusText || statusAccessory ? /* @__PURE__ */
|
|
4294
|
-
statusText ? /* @__PURE__ */
|
|
4401
|
+
statusText || statusAccessory ? /* @__PURE__ */ jsxs52(View58, { testID: "catalogue-status-row", className: "flex-row items-center gap-2", children: [
|
|
4402
|
+
statusText ? /* @__PURE__ */ jsxs52(Text37, { className: "flex-1 text-xs text-muted-foreground", numberOfLines: statusAccessory ? 1 : void 0, children: [
|
|
4295
4403
|
statusText,
|
|
4296
4404
|
query.trim() ? ` \xB7 ${results.length.toLocaleString()} matching` : ""
|
|
4297
4405
|
] }) : null,
|
|
4298
4406
|
statusAccessory
|
|
4299
4407
|
] }) : null,
|
|
4300
|
-
choices.length > 0 ? /* @__PURE__ */
|
|
4301
|
-
choices.map((entry) => /* @__PURE__ */
|
|
4302
|
-
|
|
4408
|
+
choices.length > 0 ? /* @__PURE__ */ jsxs52(View58, { accessibilityLabel: "Choose variant", className: "gap-2", children: [
|
|
4409
|
+
choices.map((entry) => /* @__PURE__ */ jsxs52(
|
|
4410
|
+
Pressable28,
|
|
4303
4411
|
{
|
|
4304
4412
|
accessibilityRole: "button",
|
|
4305
4413
|
onPress: () => select(entry),
|
|
4306
4414
|
className: "gap-1 rounded-md border border-border p-3",
|
|
4307
4415
|
children: [
|
|
4308
|
-
/* @__PURE__ */
|
|
4309
|
-
/* @__PURE__ */
|
|
4310
|
-
/* @__PURE__ */
|
|
4311
|
-
/* @__PURE__ */
|
|
4416
|
+
/* @__PURE__ */ jsx93(Text37, { className: "font-semibold text-foreground", children: entry.variant.title }),
|
|
4417
|
+
/* @__PURE__ */ jsx93(Text37, { className: "text-muted-foreground", children: entry.variant.sku }),
|
|
4418
|
+
/* @__PURE__ */ jsx93(Text37, { className: "text-foreground", children: variantPriceLabel(entry.variant, currency) }),
|
|
4419
|
+
/* @__PURE__ */ jsxs52(Text37, { className: "text-muted-foreground", children: [
|
|
4312
4420
|
STOCK_LABEL[entry.variant.stock.status],
|
|
4313
4421
|
" \xB7 ",
|
|
4314
4422
|
stockAsOf ? `as of ${formatStockSyncTime(stockAsOf, void 0, hour12)}` : "not yet synced"
|
|
@@ -4317,10 +4425,10 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4317
4425
|
},
|
|
4318
4426
|
entry.variant.id
|
|
4319
4427
|
)),
|
|
4320
|
-
/* @__PURE__ */
|
|
4428
|
+
/* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", onPress: () => setChooserId(null), className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx93(Text37, { className: "text-center text-foreground", children: "Cancel" }) })
|
|
4321
4429
|
] }) : null
|
|
4322
4430
|
] }),
|
|
4323
|
-
/* @__PURE__ */
|
|
4431
|
+
/* @__PURE__ */ jsx93(
|
|
4324
4432
|
ProductGrid,
|
|
4325
4433
|
{
|
|
4326
4434
|
items: results,
|
|
@@ -4329,18 +4437,18 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4329
4437
|
// TallyUI's ProductCard has no children slot, so the tile is composed directly here
|
|
4330
4438
|
// (same structure/classes as ProductCard) to add the stock badge as a fourth child,
|
|
4331
4439
|
// inside the card and centred with the rest, rather than adding a slot to ProductCard (out of scope for TV6b).
|
|
4332
|
-
/* @__PURE__ */
|
|
4440
|
+
/* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", testID: `product-tile-${traits.getName(product)}`, onPress: () => {
|
|
4333
4441
|
const variants = entries.filter((entry) => entry.product === product);
|
|
4334
4442
|
if (variants.length === 1) select(variants[0]);
|
|
4335
4443
|
else setChooserId(traits.getId(product));
|
|
4336
|
-
}, children: /* @__PURE__ */
|
|
4337
|
-
/* @__PURE__ */
|
|
4338
|
-
/* @__PURE__ */
|
|
4339
|
-
/* @__PURE__ */
|
|
4340
|
-
/* @__PURE__ */
|
|
4444
|
+
}, children: /* @__PURE__ */ jsxs52(VStack, { space: "sm", className: "items-center rounded-lg border border-border bg-card p-3", children: [
|
|
4445
|
+
/* @__PURE__ */ jsx93(ProductImage, { doc: product, size: 80, className: "rounded-md" }),
|
|
4446
|
+
/* @__PURE__ */ jsx93(ProductTitle, { doc: product, className: "text-sm", numberOfLines: 2 }),
|
|
4447
|
+
/* @__PURE__ */ jsx93(ProductPrice, { doc: product }),
|
|
4448
|
+
/* @__PURE__ */ jsx93(ProductStockBadge, { doc: product, showAsOf: false, className: "self-center" })
|
|
4341
4449
|
] }) })
|
|
4342
4450
|
),
|
|
4343
|
-
emptyState: /* @__PURE__ */
|
|
4451
|
+
emptyState: /* @__PURE__ */ jsx93(Text37, { className: "mt-10 text-center text-sm text-muted-foreground", children: query.trim() ? `No products match "${query.trim()}".` : isLoading ? "Loading products\u2026" : "No products yet." })
|
|
4344
4452
|
}
|
|
4345
4453
|
)
|
|
4346
4454
|
] });
|
|
@@ -4348,8 +4456,8 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4348
4456
|
|
|
4349
4457
|
// src/sale/receipt.tsx
|
|
4350
4458
|
import { useEffect as useEffect6 } from "react";
|
|
4351
|
-
import { Pressable as
|
|
4352
|
-
import { formatMoney as
|
|
4459
|
+
import { Pressable as Pressable29, Text as Text38, View as View59 } from "react-native";
|
|
4460
|
+
import { formatMoney as formatMoney14 } from "@tallyui/core";
|
|
4353
4461
|
import { buildReceiptData as buildReceiptData3 } from "@tallyui/pos";
|
|
4354
4462
|
|
|
4355
4463
|
// src/sale/print-style.ts
|
|
@@ -4369,7 +4477,7 @@ function orderReference(order) {
|
|
|
4369
4477
|
}
|
|
4370
4478
|
|
|
4371
4479
|
// src/sale/receipt.tsx
|
|
4372
|
-
import { Fragment as Fragment7, jsx as
|
|
4480
|
+
import { Fragment as Fragment7, jsx as jsx94, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
4373
4481
|
var defaultFormatDate = (iso) => new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" }).format(new Date(iso));
|
|
4374
4482
|
function Receipt({
|
|
4375
4483
|
order,
|
|
@@ -4389,33 +4497,33 @@ function Receipt({
|
|
|
4389
4497
|
cashier,
|
|
4390
4498
|
register: registerId
|
|
4391
4499
|
});
|
|
4392
|
-
const money = (amount) =>
|
|
4393
|
-
const row = (label, amount, bold = false, key) => /* @__PURE__ */
|
|
4394
|
-
/* @__PURE__ */
|
|
4395
|
-
/* @__PURE__ */
|
|
4500
|
+
const money = (amount) => formatMoney14({ amount, currency: receipt.currency }) ?? "";
|
|
4501
|
+
const row = (label, amount, bold = false, key) => /* @__PURE__ */ jsxs53(View59, { accessibilityLabel: `${label}: ${amount}`, className: "flex-row justify-between gap-4", children: [
|
|
4502
|
+
/* @__PURE__ */ jsx94(Text38, { className: `text-foreground ${bold ? "font-semibold" : ""}`, children: label }),
|
|
4503
|
+
/* @__PURE__ */ jsx94(Text38, { className: `text-right text-foreground ${bold ? "font-semibold" : ""}`, children: amount })
|
|
4396
4504
|
] }, key);
|
|
4397
4505
|
const { totals } = receipt;
|
|
4398
4506
|
const lineDiscountMinor = totals.discountMinor - receipt.orderDiscountMinor;
|
|
4399
4507
|
const taxRows = totals.taxLines.map((line, index) => row(`${totals.taxInclusive ? "incl. " : ""}${taxLabel(line.ratePpm)}`, money(line.amountMinor), false, index));
|
|
4400
|
-
return /* @__PURE__ */
|
|
4401
|
-
topInset > 0 ? /* @__PURE__ */
|
|
4402
|
-
/* @__PURE__ */
|
|
4403
|
-
/* @__PURE__ */
|
|
4404
|
-
receipt.header.storeAddress ? /* @__PURE__ */
|
|
4405
|
-
/* @__PURE__ */
|
|
4406
|
-
/* @__PURE__ */
|
|
4407
|
-
/* @__PURE__ */
|
|
4508
|
+
return /* @__PURE__ */ jsxs53(Fragment7, { children: [
|
|
4509
|
+
topInset > 0 ? /* @__PURE__ */ jsx94(View59, { dataSet: { print: "hide" }, style: { height: topInset, flexShrink: 0 } }) : null,
|
|
4510
|
+
/* @__PURE__ */ jsxs53(View59, { className: "w-full max-w-md self-center gap-3 p-4 bg-card", children: [
|
|
4511
|
+
/* @__PURE__ */ jsx94(Text38, { className: "text-lg font-semibold text-foreground", children: receipt.header.storeName }),
|
|
4512
|
+
receipt.header.storeAddress ? /* @__PURE__ */ jsx94(Text38, { className: "text-muted-foreground", children: receipt.header.storeAddress }) : null,
|
|
4513
|
+
/* @__PURE__ */ jsx94(Text38, { testID: "receipt-order", className: "text-foreground", children: posOrder ? `Order ${orderReference(posOrder)}` : `Order ${receipt.header.orderNumber.slice(-8)} (draft)` }),
|
|
4514
|
+
/* @__PURE__ */ jsx94(Text38, { className: "text-muted-foreground", children: formatDate(posOrder?.createdAt ?? receipt.header.date) }),
|
|
4515
|
+
/* @__PURE__ */ jsxs53(Text38, { className: "text-muted-foreground", children: [
|
|
4408
4516
|
"Cashier: ",
|
|
4409
4517
|
receipt.header.cashier
|
|
4410
4518
|
] }),
|
|
4411
|
-
receipt.header.customer && /* @__PURE__ */
|
|
4519
|
+
receipt.header.customer && /* @__PURE__ */ jsxs53(Text38, { className: "text-muted-foreground", children: [
|
|
4412
4520
|
"Customer: ",
|
|
4413
4521
|
receipt.header.customer
|
|
4414
4522
|
] }),
|
|
4415
|
-
receipt.lineItems.map((line, index) => /* @__PURE__ */
|
|
4416
|
-
/* @__PURE__ */
|
|
4523
|
+
receipt.lineItems.map((line, index) => /* @__PURE__ */ jsxs53(View59, { children: [
|
|
4524
|
+
/* @__PURE__ */ jsx94(Text38, { className: "text-foreground", children: line.name }),
|
|
4417
4525
|
row(`${line.quantity} \xD7 ${money(line.unitPriceMinor)}`, money(line.displayAmountMinor)),
|
|
4418
|
-
line.displayDiscounts.map((discount, i) => /* @__PURE__ */
|
|
4526
|
+
line.displayDiscounts.map((discount, i) => /* @__PURE__ */ jsx94(View59, { className: "pl-4", children: row(`${discountLabel(
|
|
4419
4527
|
order.lineItems[index].discounts[i],
|
|
4420
4528
|
receipt.currency
|
|
4421
4529
|
)} off`, `\u2212${money(discount.amountMinor)}`) }, i))
|
|
@@ -4432,19 +4540,19 @@ function Receipt({
|
|
|
4432
4540
|
index
|
|
4433
4541
|
)),
|
|
4434
4542
|
row("Change", money(receipt.changeDueMinor)),
|
|
4435
|
-
/* @__PURE__ */
|
|
4436
|
-
/* @__PURE__ */
|
|
4543
|
+
/* @__PURE__ */ jsxs53(View59, { dataSet: { print: "hide" }, className: "flex-row gap-4", children: [
|
|
4544
|
+
/* @__PURE__ */ jsx94(Pressable29, { accessibilityRole: "button", onPress: () => {
|
|
4437
4545
|
if (typeof window !== "undefined" && typeof window.print === "function") window.print();
|
|
4438
|
-
}, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */
|
|
4439
|
-
/* @__PURE__ */
|
|
4546
|
+
}, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx94(Text38, { className: "text-center text-foreground", children: "Print receipt" }) }),
|
|
4547
|
+
/* @__PURE__ */ jsx94(Pressable29, { accessibilityRole: "button", onPress: newSale, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx94(Text38, { className: "text-center font-semibold text-primary-foreground", children: "New sale" }) })
|
|
4440
4548
|
] })
|
|
4441
4549
|
] })
|
|
4442
4550
|
] });
|
|
4443
4551
|
}
|
|
4444
4552
|
|
|
4445
4553
|
// src/sale/sync-status.tsx
|
|
4446
|
-
import { useEffect as useEffect7, useRef as useRef6, useState as
|
|
4447
|
-
import { AccessibilityInfo as AccessibilityInfo2, Platform as Platform3, Text as
|
|
4554
|
+
import { useEffect as useEffect7, useRef as useRef6, useState as useState20 } from "react";
|
|
4555
|
+
import { AccessibilityInfo as AccessibilityInfo2, Platform as Platform3, Text as Text39, View as View60 } from "react-native";
|
|
4448
4556
|
|
|
4449
4557
|
// src/sale/since-text.ts
|
|
4450
4558
|
function sinceText(clock) {
|
|
@@ -4453,7 +4561,7 @@ function sinceText(clock) {
|
|
|
4453
4561
|
}
|
|
4454
4562
|
|
|
4455
4563
|
// src/sale/sync-status.tsx
|
|
4456
|
-
import { Fragment as Fragment8, jsx as
|
|
4564
|
+
import { Fragment as Fragment8, jsx as jsx95, jsxs as jsxs54 } from "react/jsx-runtime";
|
|
4457
4565
|
var PULL_NOTICE_TEXT = {
|
|
4458
4566
|
unauthorized: {
|
|
4459
4567
|
line: () => "Products aren't updating: this till needs to sign in to the online store again.",
|
|
@@ -4505,7 +4613,7 @@ function useAnnounceOnIos(...texts) {
|
|
|
4505
4613
|
function SyncStatus({ state, registerState, pluginName = "the POS plugin", pullNotice }) {
|
|
4506
4614
|
const updates = registerState?.pending ?? 0;
|
|
4507
4615
|
const outbox = state.pending === 0 && registerState?.pending ? registerState : state;
|
|
4508
|
-
const [now, setNow] =
|
|
4616
|
+
const [now, setNow] = useState20(Date.now);
|
|
4509
4617
|
useEffect7(() => {
|
|
4510
4618
|
setNow(Date.now());
|
|
4511
4619
|
if (!outbox.nextAttemptAt) return;
|
|
@@ -4529,24 +4637,24 @@ function SyncStatus({ state, registerState, pluginName = "the POS plugin", pullN
|
|
|
4529
4637
|
const notice = pullNotice && text && { line: text.line(pluginName), detail: text.detail(pullNotice, pluginName) };
|
|
4530
4638
|
useAnnounceOnIos(notice?.line, spoken);
|
|
4531
4639
|
const oneDetail = pullNotice?.code === "missing_plugin" && !!backendMissing;
|
|
4532
|
-
return /* @__PURE__ */
|
|
4533
|
-
notice ? /* @__PURE__ */
|
|
4534
|
-
/* @__PURE__ */
|
|
4535
|
-
oneDetail ? null : /* @__PURE__ */
|
|
4640
|
+
return /* @__PURE__ */ jsxs54(View60, { children: [
|
|
4641
|
+
notice ? /* @__PURE__ */ jsxs54(Fragment8, { children: [
|
|
4642
|
+
/* @__PURE__ */ jsx95(Text39, { accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: notice.line }),
|
|
4643
|
+
oneDetail ? null : /* @__PURE__ */ jsx95(Text39, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail })
|
|
4536
4644
|
] }) : null,
|
|
4537
|
-
/* @__PURE__ */
|
|
4538
|
-
doing ? /* @__PURE__ */
|
|
4539
|
-
refused ? /* @__PURE__ */
|
|
4540
|
-
oneDetail && notice ? /* @__PURE__ */
|
|
4645
|
+
/* @__PURE__ */ jsx95(Text39, { accessibilityLabel: spoken, accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: spoken }),
|
|
4646
|
+
doing ? /* @__PURE__ */ jsx95(Text39, { className: "px-4 pb-2 text-xs text-muted-foreground", children: doing }) : null,
|
|
4647
|
+
refused ? /* @__PURE__ */ jsx95(Text39, { className: "px-4 pb-2 text-xs text-muted-foreground", children: REFUSED_DETAIL }) : null,
|
|
4648
|
+
oneDetail && notice ? /* @__PURE__ */ jsx95(Text39, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail }) : backendMissing ? /* @__PURE__ */ jsx95(Text39, { className: "px-4 pb-2 text-xs text-muted-foreground", children: BACKEND_MISSING_DETAIL.replace("{pluginName}", () => pluginName).replace("{lastTime}", () => upToDate ? " the last time it checked" : "") }) : null
|
|
4541
4649
|
] });
|
|
4542
4650
|
}
|
|
4543
4651
|
|
|
4544
4652
|
// src/sale/orders-list.tsx
|
|
4545
|
-
import { useEffect as useEffect8, useRef as useRef7, useState as
|
|
4546
|
-
import { Pressable as
|
|
4547
|
-
import { formatMoney as
|
|
4653
|
+
import { useEffect as useEffect8, useRef as useRef7, useState as useState21 } from "react";
|
|
4654
|
+
import { Pressable as Pressable30, ScrollView as ScrollView13, Text as Text40, View as View61 } from "react-native";
|
|
4655
|
+
import { formatMoney as formatMoney15, knownWarnings } from "@tallyui/core";
|
|
4548
4656
|
import { needsAttention } from "@tallyui/pos";
|
|
4549
|
-
import { jsx as
|
|
4657
|
+
import { jsx as jsx96, jsxs as jsxs55 } from "react/jsx-runtime";
|
|
4550
4658
|
var STATUS_LABEL = { pending: "Waiting to sync", applied: "Synced", rejected: "Not accepted" };
|
|
4551
4659
|
var FIGURE_LABELS = /* @__PURE__ */ new Map([["subtotalMinor", "Subtotal"], ["taxMinor", "Tax"], ["discountMinor", "Discount"]]);
|
|
4552
4660
|
var REFUSAL_SENTENCES = /* @__PURE__ */ new Map([
|
|
@@ -4570,26 +4678,26 @@ function defaultFormatDate2(iso) {
|
|
|
4570
4678
|
}
|
|
4571
4679
|
function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer, stuck }) {
|
|
4572
4680
|
const retrying = useRef7(/* @__PURE__ */ new Set());
|
|
4573
|
-
const [retryingIds, setRetryingIds] =
|
|
4574
|
-
const [expanded, setExpanded] =
|
|
4681
|
+
const [retryingIds, setRetryingIds] = useState21(/* @__PURE__ */ new Set());
|
|
4682
|
+
const [expanded, setExpanded] = useState21(/* @__PURE__ */ new Set());
|
|
4575
4683
|
useEffect8(() => {
|
|
4576
4684
|
for (const id of retrying.current) {
|
|
4577
4685
|
if (!orders.some((order) => order.id === id && order.syncStatus === "rejected")) retrying.current.delete(id);
|
|
4578
4686
|
}
|
|
4579
4687
|
setRetryingIds(new Set(retrying.current));
|
|
4580
4688
|
}, [orders]);
|
|
4581
|
-
return /* @__PURE__ */
|
|
4582
|
-
[{ title: "Needs attention", orders: needsAttention(orders, { stuckCommandIds: stuck?.commandIds }) }, { title: "Recent", orders }].filter((section) => section.title === "Recent" || section.orders.length > 0).map((section) => /* @__PURE__ */
|
|
4583
|
-
/* @__PURE__ */
|
|
4584
|
-
section.title === "Recent" && orders.length === 0 ? /* @__PURE__ */
|
|
4689
|
+
return /* @__PURE__ */ jsxs55(ScrollView13, { className: "flex-1 bg-background p-4", children: [
|
|
4690
|
+
[{ title: "Needs attention", orders: needsAttention(orders, { stuckCommandIds: stuck?.commandIds }) }, { title: "Recent", orders }].filter((section) => section.title === "Recent" || section.orders.length > 0).map((section) => /* @__PURE__ */ jsxs55(View61, { className: "mb-6 gap-3", children: [
|
|
4691
|
+
/* @__PURE__ */ jsx96(Text40, { accessibilityRole: "header", className: "text-xl font-semibold text-foreground", children: section.title }),
|
|
4692
|
+
section.title === "Recent" && orders.length === 0 ? /* @__PURE__ */ jsx96(Text40, { testID: "orders-empty", className: "text-muted-foreground", children: "No sales yet. Completed sales appear here." }) : null,
|
|
4585
4693
|
section.orders.map((order) => {
|
|
4586
4694
|
const count2 = order.lines.reduce((sum, line) => sum + line.quantity, 0);
|
|
4587
4695
|
const key = `${section.title}:${order.id}`;
|
|
4588
4696
|
const isExpanded = expanded.has(key);
|
|
4589
4697
|
const stuckEntry = order.syncStatus === "pending" ? stuck?.orders.find((entry) => entry.commandId === order.commandId) : void 0;
|
|
4590
|
-
return /* @__PURE__ */
|
|
4591
|
-
/* @__PURE__ */
|
|
4592
|
-
|
|
4698
|
+
return /* @__PURE__ */ jsxs55(View61, { className: "gap-1 rounded-md border border-border bg-card p-3", children: [
|
|
4699
|
+
/* @__PURE__ */ jsxs55(
|
|
4700
|
+
Pressable30,
|
|
4593
4701
|
{
|
|
4594
4702
|
accessibilityRole: "button",
|
|
4595
4703
|
"aria-expanded": isExpanded,
|
|
@@ -4603,26 +4711,26 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4603
4711
|
return next;
|
|
4604
4712
|
}),
|
|
4605
4713
|
children: [
|
|
4606
|
-
/* @__PURE__ */
|
|
4607
|
-
/* @__PURE__ */
|
|
4714
|
+
/* @__PURE__ */ jsx96(Text40, { className: "text-foreground", children: `Order ${orderReference(order)} \xB7 ${count2} ${count2 === 1 ? "item" : "items"}` }),
|
|
4715
|
+
/* @__PURE__ */ jsxs55(Text40, { className: "text-muted-foreground", children: [
|
|
4608
4716
|
formatDate(order.createdAt),
|
|
4609
4717
|
" \xB7 ",
|
|
4610
|
-
|
|
4718
|
+
formatMoney15({ amount: order.totalMinor, currency: order.currency }),
|
|
4611
4719
|
" \xB7 ",
|
|
4612
4720
|
STATUS_LABEL[order.syncStatus]
|
|
4613
4721
|
] })
|
|
4614
4722
|
]
|
|
4615
4723
|
}
|
|
4616
4724
|
),
|
|
4617
|
-
isExpanded ? /* @__PURE__ */
|
|
4618
|
-
order.lines.map((line) => /* @__PURE__ */
|
|
4619
|
-
order.payments.map((payment) => /* @__PURE__ */
|
|
4725
|
+
isExpanded ? /* @__PURE__ */ jsxs55(View61, { testID: `order-detail-${order.id}`, className: "mt-1 gap-1 border-t border-border pt-2", children: [
|
|
4726
|
+
order.lines.map((line) => /* @__PURE__ */ jsx96(Text40, { className: "text-foreground", children: `${line.quantity} \xD7 ${line.name}` }, line.id)),
|
|
4727
|
+
order.payments.map((payment) => /* @__PURE__ */ jsx96(Text40, { className: "text-muted-foreground", children: `${payment.method === "cash" ? "Cash" : "Card terminal"} ${formatMoney15({ amount: payment.amountMinor, currency: order.currency })}${payment.reference ? ` \xB7 ${payment.reference}` : ""}` }, payment.id))
|
|
4620
4728
|
] }) : null,
|
|
4621
|
-
stuckEntry ? /* @__PURE__ */
|
|
4622
|
-
order.syncStatus === "rejected" ? /* @__PURE__ */
|
|
4623
|
-
order.lateSessionId !== void 0 ? /* @__PURE__ */
|
|
4624
|
-
order.localWarnings?.map((warning, index) => /* @__PURE__ */
|
|
4625
|
-
section.title === "Needs attention" && order.syncStatus === "rejected" && order.error?.code !== "idempotency_mismatch" ? /* @__PURE__ */
|
|
4729
|
+
stuckEntry ? /* @__PURE__ */ jsx96(Text40, { className: "text-destructive", children: `Hasn't reached the online store since ${sinceText(stuckEntry)}.` }) : null,
|
|
4730
|
+
order.syncStatus === "rejected" ? /* @__PURE__ */ jsx96(Text40, { className: "text-destructive", children: refusalSentence(order.error?.code) }) : null,
|
|
4731
|
+
order.lateSessionId !== void 0 ? /* @__PURE__ */ jsx96(Text40, { className: "text-foreground", children: "Taken after the register closed. It is not in that register's closure." }) : null,
|
|
4732
|
+
order.localWarnings?.map((warning, index) => /* @__PURE__ */ jsx96(Text40, { className: "text-foreground", children: warning.code === "customer_omitted" ? `The customer's ${warning.field} couldn't be sent to the store, so the order isn't linked to them.` : "The terminal's payment reference couldn't be kept; the payment is recorded without it." }, index)),
|
|
4733
|
+
section.title === "Needs attention" && order.syncStatus === "rejected" && order.error?.code !== "idempotency_mismatch" ? /* @__PURE__ */ jsx96(Pressable30, { accessibilityRole: "button", disabled: retryingIds.has(order.id), onPress: async () => {
|
|
4626
4734
|
if (retrying.current.has(order.id)) return;
|
|
4627
4735
|
retrying.current.add(order.id);
|
|
4628
4736
|
setRetryingIds(new Set(retrying.current));
|
|
@@ -4630,8 +4738,8 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4630
4738
|
retrying.current.delete(order.id);
|
|
4631
4739
|
setRetryingIds(new Set(retrying.current));
|
|
4632
4740
|
}
|
|
4633
|
-
}, className: "rounded-md bg-primary px-4 py-2", children: /* @__PURE__ */
|
|
4634
|
-
knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */
|
|
4741
|
+
}, className: "rounded-md bg-primary px-4 py-2", children: /* @__PURE__ */ jsx96(Text40, { className: "text-center font-semibold text-primary-foreground", children: "Retry" }) }) : null,
|
|
4742
|
+
knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */ jsx96(View61, { className: "border-l-4 border-warning pl-2", children: /* @__PURE__ */ jsx96(Text40, { className: "text-foreground", children: warning.code === "insufficient_stock" ? `Stock short by ${warning.quantity} for ${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 ${formatMoney15({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney15({ 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 ${formatMoney15({ amount: tillMinor, currency: order.currency })}, store ${formatMoney15({ amount: serverMinor, currency: order.currency })}.`).join(" ")}` : warning.bridgeMinor !== void 0 ? `Store calculated ${formatMoney15({ amount: warning.serverMinor, currency: order.currency })}; a rounding line of ${warning.bridgeMinor > 0 ? "+" : ""}${formatMoney15({ amount: warning.bridgeMinor, currency: order.currency })} brought it to ${formatMoney15({ amount: warning.expectedMinor, currency: order.currency })}` : `Store total ${formatMoney15({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney15({ amount: warning.expectedMinor, currency: order.currency })}` }) }, index))
|
|
4635
4743
|
] }, order.id);
|
|
4636
4744
|
})
|
|
4637
4745
|
] }, section.title)),
|
|
@@ -4639,11 +4747,103 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4639
4747
|
] });
|
|
4640
4748
|
}
|
|
4641
4749
|
|
|
4750
|
+
// src/sale/parked-sales.tsx
|
|
4751
|
+
import { useState as useState22 } from "react";
|
|
4752
|
+
import { ScrollView as ScrollView14, useWindowDimensions as useWindowDimensions3, View as View62 } from "react-native";
|
|
4753
|
+
import { formatMoney as formatMoney16 } from "@tallyui/core";
|
|
4754
|
+
import { Fragment as Fragment9, jsx as jsx97, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
4755
|
+
function ParkedSales({ sale, parked, onDiscard, currency, open, onOpenChange, hour12 }) {
|
|
4756
|
+
const { height: windowHeight } = useWindowDimensions3();
|
|
4757
|
+
const maxHeight = Math.max(280, windowHeight - 64);
|
|
4758
|
+
const [confirming, setConfirming] = useState22(null);
|
|
4759
|
+
const [error, setError] = useState22("");
|
|
4760
|
+
const attempt = async (action) => {
|
|
4761
|
+
setError("");
|
|
4762
|
+
try {
|
|
4763
|
+
await action();
|
|
4764
|
+
} catch (e) {
|
|
4765
|
+
setError(String(e));
|
|
4766
|
+
}
|
|
4767
|
+
};
|
|
4768
|
+
return /* @__PURE__ */ jsx97(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs56(DialogContent, { testID: "parked-sales", className: "flex-col overflow-hidden", style: { maxHeight }, children: [
|
|
4769
|
+
/* @__PURE__ */ jsxs56(View62, { className: "flex-row items-center justify-between gap-2", children: [
|
|
4770
|
+
/* @__PURE__ */ jsx97(DialogTitle, { children: "Parked sales" }),
|
|
4771
|
+
/* @__PURE__ */ jsx97(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx97(IconButton, { variant: "ghost", size: "sm", testID: "parked-sales-dismiss", accessibilityLabel: "Close panel", children: /* @__PURE__ */ jsx97(XIcon, { size: 16 }) }) })
|
|
4772
|
+
] }),
|
|
4773
|
+
/* @__PURE__ */ jsxs56(ScrollView14, { className: "flex-1", contentContainerClassName: "gap-3", children: [
|
|
4774
|
+
/* @__PURE__ */ jsx97(
|
|
4775
|
+
Button,
|
|
4776
|
+
{
|
|
4777
|
+
testID: "parked-sales-park",
|
|
4778
|
+
accessibilityLabel: "Park this sale",
|
|
4779
|
+
disabled: sale.order.lineItems.length === 0,
|
|
4780
|
+
onPress: () => attempt(async () => setError(await sale.park() ?? "")),
|
|
4781
|
+
children: /* @__PURE__ */ jsx97(Text, { children: "Park this sale" })
|
|
4782
|
+
}
|
|
4783
|
+
),
|
|
4784
|
+
!!error && /* @__PURE__ */ jsx97(Text, { testID: "parked-sales-error", className: "text-destructive", children: error }),
|
|
4785
|
+
parked.length === 0 && /* @__PURE__ */ jsx97(Text, { testID: "parked-sales-empty", children: "No parked sales." }),
|
|
4786
|
+
parked.map((row) => /* @__PURE__ */ jsx97(View62, { testID: `parked-row-${row.id}`, className: "gap-2 rounded-md border border-border p-3", children: confirming === row.id ? /* @__PURE__ */ jsxs56(Fragment9, { children: [
|
|
4787
|
+
/* @__PURE__ */ jsx97(Text, { children: "Discard this parked sale?" }),
|
|
4788
|
+
/* @__PURE__ */ jsxs56(View62, { className: "flex-row gap-2", children: [
|
|
4789
|
+
/* @__PURE__ */ jsx97(
|
|
4790
|
+
Button,
|
|
4791
|
+
{
|
|
4792
|
+
testID: `parked-discard-confirm-${row.id}`,
|
|
4793
|
+
variant: "destructive",
|
|
4794
|
+
onPress: () => attempt(async () => {
|
|
4795
|
+
await onDiscard(row.id);
|
|
4796
|
+
setConfirming(null);
|
|
4797
|
+
}),
|
|
4798
|
+
children: /* @__PURE__ */ jsx97(Text, { children: "Discard" })
|
|
4799
|
+
}
|
|
4800
|
+
),
|
|
4801
|
+
/* @__PURE__ */ jsx97(Button, { testID: `parked-discard-cancel-${row.id}`, variant: "outline", onPress: () => setConfirming(null), children: /* @__PURE__ */ jsx97(Text, { children: "Cancel" }) })
|
|
4802
|
+
] })
|
|
4803
|
+
] }) : /* @__PURE__ */ jsxs56(Fragment9, { children: [
|
|
4804
|
+
/* @__PURE__ */ jsx97(Text, { children: formatStockSyncTime(new Date(row.parkedAt), void 0, hour12) }),
|
|
4805
|
+
/* @__PURE__ */ jsxs56(Text, { children: [
|
|
4806
|
+
row.itemCount,
|
|
4807
|
+
" ",
|
|
4808
|
+
row.itemCount === 1 ? "item" : "items"
|
|
4809
|
+
] }),
|
|
4810
|
+
/* @__PURE__ */ jsx97(Text, { children: formatMoney16({ amount: row.totalMinor, currency }) }),
|
|
4811
|
+
!!row.customerName && /* @__PURE__ */ jsx97(Text, { children: row.customerName }),
|
|
4812
|
+
/* @__PURE__ */ jsxs56(View62, { className: "flex-row gap-2", children: [
|
|
4813
|
+
/* @__PURE__ */ jsx97(
|
|
4814
|
+
Button,
|
|
4815
|
+
{
|
|
4816
|
+
testID: `parked-resume-${row.id}`,
|
|
4817
|
+
accessibilityLabel: "Resume parked sale",
|
|
4818
|
+
onPress: () => attempt(async () => {
|
|
4819
|
+
const refusal = await sale.resume(row.id);
|
|
4820
|
+
if (refusal === null) onOpenChange(false);
|
|
4821
|
+
else setError(refusal);
|
|
4822
|
+
}),
|
|
4823
|
+
children: /* @__PURE__ */ jsx97(Text, { children: "Resume" })
|
|
4824
|
+
}
|
|
4825
|
+
),
|
|
4826
|
+
/* @__PURE__ */ jsx97(
|
|
4827
|
+
Button,
|
|
4828
|
+
{
|
|
4829
|
+
testID: `parked-discard-${row.id}`,
|
|
4830
|
+
accessibilityLabel: "Discard parked sale",
|
|
4831
|
+
variant: "outline",
|
|
4832
|
+
onPress: () => setConfirming(row.id),
|
|
4833
|
+
children: /* @__PURE__ */ jsx97(Text, { children: "Discard" })
|
|
4834
|
+
}
|
|
4835
|
+
)
|
|
4836
|
+
] })
|
|
4837
|
+
] }) }, row.id))
|
|
4838
|
+
] })
|
|
4839
|
+
] }) });
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4642
4842
|
// src/layout/pos-layout.tsx
|
|
4643
|
-
import { useState as
|
|
4644
|
-
import { Pressable as
|
|
4843
|
+
import { useState as useState23 } from "react";
|
|
4844
|
+
import { Pressable as Pressable31, Text as Text41, View as View63, useWindowDimensions as useWindowDimensions4 } from "react-native";
|
|
4645
4845
|
import { cn as cn80 } from "@tallyui/theme";
|
|
4646
|
-
import { Fragment as
|
|
4846
|
+
import { Fragment as Fragment10, jsx as jsx98, jsxs as jsxs57 } from "react/jsx-runtime";
|
|
4647
4847
|
function POSLayout({
|
|
4648
4848
|
browseSlot,
|
|
4649
4849
|
cartSlot,
|
|
@@ -4653,42 +4853,42 @@ function POSLayout({
|
|
|
4653
4853
|
className,
|
|
4654
4854
|
...viewProps
|
|
4655
4855
|
}) {
|
|
4656
|
-
const { width } =
|
|
4856
|
+
const { width } = useWindowDimensions4();
|
|
4657
4857
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4658
|
-
const [activeTab, setActiveTab] =
|
|
4659
|
-
return /* @__PURE__ */
|
|
4660
|
-
headerSlot && /* @__PURE__ */
|
|
4661
|
-
layout === "split" ? /* @__PURE__ */
|
|
4662
|
-
/* @__PURE__ */
|
|
4663
|
-
/* @__PURE__ */
|
|
4664
|
-
] }) : /* @__PURE__ */
|
|
4665
|
-
/* @__PURE__ */
|
|
4666
|
-
/* @__PURE__ */
|
|
4667
|
-
|
|
4858
|
+
const [activeTab, setActiveTab] = useState23("browse");
|
|
4859
|
+
return /* @__PURE__ */ jsxs57(View63, { className: cn80("flex-1 bg-bg", className), ...viewProps, children: [
|
|
4860
|
+
headerSlot && /* @__PURE__ */ jsx98(View63, { className: "border-b border-border", children: headerSlot }),
|
|
4861
|
+
layout === "split" ? /* @__PURE__ */ jsxs57(View63, { className: "flex-1 flex-row", children: [
|
|
4862
|
+
/* @__PURE__ */ jsx98(View63, { className: "flex-[3]", children: browseSlot }),
|
|
4863
|
+
/* @__PURE__ */ jsx98(View63, { className: "flex-[2] border-l border-border", children: cartSlot })
|
|
4864
|
+
] }) : /* @__PURE__ */ jsxs57(Fragment10, { children: [
|
|
4865
|
+
/* @__PURE__ */ jsxs57(View63, { className: "flex-row border-b border-border", children: [
|
|
4866
|
+
/* @__PURE__ */ jsx98(
|
|
4867
|
+
Pressable31,
|
|
4668
4868
|
{
|
|
4669
4869
|
onPress: () => setActiveTab("browse"),
|
|
4670
4870
|
className: cn80("flex-1 py-3", activeTab === "browse" && "border-b-2 border-primary"),
|
|
4671
|
-
children: /* @__PURE__ */
|
|
4871
|
+
children: /* @__PURE__ */ jsx98(Text41, { className: cn80("text-center text-sm font-semibold", activeTab === "browse" ? "text-primary" : "text-muted-foreground"), children: "Browse" })
|
|
4672
4872
|
}
|
|
4673
4873
|
),
|
|
4674
|
-
/* @__PURE__ */
|
|
4675
|
-
|
|
4874
|
+
/* @__PURE__ */ jsx98(
|
|
4875
|
+
Pressable31,
|
|
4676
4876
|
{
|
|
4677
4877
|
onPress: () => setActiveTab("cart"),
|
|
4678
4878
|
className: cn80("flex-1 py-3", activeTab === "cart" && "border-b-2 border-primary"),
|
|
4679
|
-
children: /* @__PURE__ */
|
|
4879
|
+
children: /* @__PURE__ */ jsx98(Text41, { className: cn80("text-center text-sm font-semibold", activeTab === "cart" ? "text-primary" : "text-muted-foreground"), children: "Cart" })
|
|
4680
4880
|
}
|
|
4681
4881
|
)
|
|
4682
4882
|
] }),
|
|
4683
|
-
/* @__PURE__ */
|
|
4883
|
+
/* @__PURE__ */ jsx98(View63, { className: "flex-1", children: activeTab === "browse" ? browseSlot : cartSlot })
|
|
4684
4884
|
] })
|
|
4685
4885
|
] });
|
|
4686
4886
|
}
|
|
4687
4887
|
|
|
4688
4888
|
// src/layout/checkout-layout.tsx
|
|
4689
|
-
import { ScrollView as
|
|
4889
|
+
import { ScrollView as ScrollView15, View as View64, useWindowDimensions as useWindowDimensions5 } from "react-native";
|
|
4690
4890
|
import { cn as cn81 } from "@tallyui/theme";
|
|
4691
|
-
import { jsx as
|
|
4891
|
+
import { jsx as jsx99, jsxs as jsxs58 } from "react/jsx-runtime";
|
|
4692
4892
|
function CheckoutLayout({
|
|
4693
4893
|
summarySlot,
|
|
4694
4894
|
paymentSlot,
|
|
@@ -4698,14 +4898,14 @@ function CheckoutLayout({
|
|
|
4698
4898
|
className,
|
|
4699
4899
|
...viewProps
|
|
4700
4900
|
}) {
|
|
4701
|
-
const { width } =
|
|
4901
|
+
const { width } = useWindowDimensions5();
|
|
4702
4902
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4703
|
-
return /* @__PURE__ */
|
|
4704
|
-
headerSlot && /* @__PURE__ */
|
|
4705
|
-
layout === "split" ? /* @__PURE__ */
|
|
4706
|
-
/* @__PURE__ */
|
|
4707
|
-
/* @__PURE__ */
|
|
4708
|
-
] }) : /* @__PURE__ */
|
|
4903
|
+
return /* @__PURE__ */ jsxs58(View64, { className: cn81("flex-1 bg-bg", className), ...viewProps, children: [
|
|
4904
|
+
headerSlot && /* @__PURE__ */ jsx99(View64, { className: "border-b border-border", children: headerSlot }),
|
|
4905
|
+
layout === "split" ? /* @__PURE__ */ jsxs58(View64, { className: "flex-1 flex-row", children: [
|
|
4906
|
+
/* @__PURE__ */ jsx99(View64, { className: "flex-1 border-r border-border", children: summarySlot }),
|
|
4907
|
+
/* @__PURE__ */ jsx99(View64, { className: "flex-1", children: paymentSlot })
|
|
4908
|
+
] }) : /* @__PURE__ */ jsx99(ScrollView15, { className: "flex-1", children: /* @__PURE__ */ jsxs58(View64, { className: "gap-4 p-4", children: [
|
|
4709
4909
|
summarySlot,
|
|
4710
4910
|
paymentSlot
|
|
4711
4911
|
] }) })
|
|
@@ -4713,10 +4913,10 @@ function CheckoutLayout({
|
|
|
4713
4913
|
}
|
|
4714
4914
|
|
|
4715
4915
|
// src/layout/settings-layout.tsx
|
|
4716
|
-
import { useState as
|
|
4717
|
-
import { Pressable as
|
|
4916
|
+
import { useState as useState24 } from "react";
|
|
4917
|
+
import { Pressable as Pressable32, Text as Text42, View as View65, useWindowDimensions as useWindowDimensions6 } from "react-native";
|
|
4718
4918
|
import { cn as cn82 } from "@tallyui/theme";
|
|
4719
|
-
import { jsx as
|
|
4919
|
+
import { jsx as jsx100, jsxs as jsxs59 } from "react/jsx-runtime";
|
|
4720
4920
|
function SettingsLayout({
|
|
4721
4921
|
navSlot,
|
|
4722
4922
|
contentSlot,
|
|
@@ -4726,28 +4926,28 @@ function SettingsLayout({
|
|
|
4726
4926
|
className,
|
|
4727
4927
|
...viewProps
|
|
4728
4928
|
}) {
|
|
4729
|
-
const { width } =
|
|
4929
|
+
const { width } = useWindowDimensions6();
|
|
4730
4930
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4731
|
-
const [showNav, setShowNav] =
|
|
4732
|
-
return /* @__PURE__ */
|
|
4733
|
-
headerSlot && /* @__PURE__ */
|
|
4734
|
-
layout === "split" ? /* @__PURE__ */
|
|
4735
|
-
/* @__PURE__ */
|
|
4736
|
-
/* @__PURE__ */
|
|
4737
|
-
] }) : /* @__PURE__ */
|
|
4931
|
+
const [showNav, setShowNav] = useState24(false);
|
|
4932
|
+
return /* @__PURE__ */ jsxs59(View65, { className: cn82("flex-1 bg-bg", className), ...viewProps, children: [
|
|
4933
|
+
headerSlot && /* @__PURE__ */ jsx100(View65, { className: "border-b border-border", children: headerSlot }),
|
|
4934
|
+
layout === "split" ? /* @__PURE__ */ jsxs59(View65, { className: "flex-1 flex-row", children: [
|
|
4935
|
+
/* @__PURE__ */ jsx100(View65, { className: "w-64 border-r border-border", children: navSlot }),
|
|
4936
|
+
/* @__PURE__ */ jsx100(View65, { className: "flex-1", children: contentSlot })
|
|
4937
|
+
] }) : /* @__PURE__ */ jsx100(View65, { className: "flex-1", children: showNav ? /* @__PURE__ */ jsxs59(View65, { className: "flex-1", children: [
|
|
4738
4938
|
navSlot,
|
|
4739
|
-
/* @__PURE__ */
|
|
4740
|
-
] }) : /* @__PURE__ */
|
|
4741
|
-
/* @__PURE__ */
|
|
4939
|
+
/* @__PURE__ */ jsx100(Pressable32, { onPress: () => setShowNav(false), className: "border-t border-border p-3", children: /* @__PURE__ */ jsx100(Text42, { className: "text-center text-sm font-medium text-primary", children: "Done" }) })
|
|
4940
|
+
] }) : /* @__PURE__ */ jsxs59(View65, { className: "flex-1", children: [
|
|
4941
|
+
/* @__PURE__ */ jsx100(Pressable32, { onPress: () => setShowNav(true), className: "border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx100(Text42, { className: "text-sm font-medium text-primary", children: "Menu" }) }),
|
|
4742
4942
|
contentSlot
|
|
4743
4943
|
] }) })
|
|
4744
4944
|
] });
|
|
4745
4945
|
}
|
|
4746
4946
|
|
|
4747
4947
|
// src/layout/live-tab-screen.tsx
|
|
4748
|
-
import { Pressable as
|
|
4948
|
+
import { Pressable as Pressable33, Text as Text43, View as View66 } from "react-native";
|
|
4749
4949
|
import { cn as cn83 } from "@tallyui/theme";
|
|
4750
|
-
import { jsx as
|
|
4950
|
+
import { jsx as jsx101, jsxs as jsxs60 } from "react/jsx-runtime";
|
|
4751
4951
|
function LiveTabScreen({
|
|
4752
4952
|
state,
|
|
4753
4953
|
onUseHere,
|
|
@@ -4764,21 +4964,21 @@ function LiveTabScreen({
|
|
|
4764
4964
|
const body = state === "parked" ? parkedBody : blockedBody;
|
|
4765
4965
|
const buttonLabel = state === "parked" ? useHereLabel : reloadLabel;
|
|
4766
4966
|
const onPress = state === "parked" ? onUseHere : onReload;
|
|
4767
|
-
return /* @__PURE__ */
|
|
4768
|
-
/* @__PURE__ */
|
|
4769
|
-
/* @__PURE__ */
|
|
4770
|
-
/* @__PURE__ */
|
|
4967
|
+
return /* @__PURE__ */ jsxs60(View66, { className: cn83("flex-1 items-center justify-center gap-4 bg-bg p-6", className), ...viewProps, children: [
|
|
4968
|
+
/* @__PURE__ */ jsx101(Text43, { className: "text-center text-lg font-bold text-foreground", children: parkedTitle }),
|
|
4969
|
+
/* @__PURE__ */ jsx101(Text43, { className: "text-center text-sm text-muted-foreground", children: body }),
|
|
4970
|
+
/* @__PURE__ */ jsx101(Pressable33, { onPress, className: "items-center rounded-lg bg-primary px-4 py-3", children: /* @__PURE__ */ jsx101(Text43, { className: "text-sm font-semibold text-primary-foreground", children: buttonLabel }) })
|
|
4771
4971
|
] });
|
|
4772
4972
|
}
|
|
4773
4973
|
|
|
4774
4974
|
// src/layout/store-settings-choice-screen.tsx
|
|
4775
|
-
import { useState as
|
|
4776
|
-
import { Pressable as
|
|
4975
|
+
import { useState as useState25 } from "react";
|
|
4976
|
+
import { Pressable as Pressable34, Text as Text44, View as View67 } from "react-native";
|
|
4777
4977
|
import { cn as cn84 } from "@tallyui/theme";
|
|
4778
|
-
import { jsx as
|
|
4978
|
+
import { jsx as jsx102, jsxs as jsxs61 } from "react/jsx-runtime";
|
|
4779
4979
|
function useSectionSelection(options, initialValue) {
|
|
4780
4980
|
const preset = options.length === 1 ? options[0].value : options.find((o) => o.value === initialValue)?.value;
|
|
4781
|
-
return
|
|
4981
|
+
return useState25(preset);
|
|
4782
4982
|
}
|
|
4783
4983
|
function defaultCountryName(code, locale) {
|
|
4784
4984
|
try {
|
|
@@ -4824,32 +5024,32 @@ function StoreSettingsChoiceScreen({
|
|
|
4824
5024
|
else if (initial?.channel !== void 0) choice.channel = initial.channel;
|
|
4825
5025
|
onSubmit(choice);
|
|
4826
5026
|
};
|
|
4827
|
-
return /* @__PURE__ */
|
|
4828
|
-
/* @__PURE__ */
|
|
4829
|
-
/* @__PURE__ */
|
|
4830
|
-
/* @__PURE__ */
|
|
5027
|
+
return /* @__PURE__ */ jsxs61(View67, { className: cn84("w-full max-w-[560px] flex-1 self-center gap-6 bg-bg p-6", className), ...viewProps, children: [
|
|
5028
|
+
/* @__PURE__ */ jsxs61(View67, { className: "gap-2", children: [
|
|
5029
|
+
/* @__PURE__ */ jsx102(Text44, { className: "text-lg font-bold text-foreground", children: title }),
|
|
5030
|
+
/* @__PURE__ */ jsx102(Text44, { className: "text-sm text-muted-foreground", children: body })
|
|
4831
5031
|
] }),
|
|
4832
|
-
showRegions && /* @__PURE__ */
|
|
4833
|
-
showCountries && /* @__PURE__ */
|
|
4834
|
-
showChannels && /* @__PURE__ */
|
|
4835
|
-
/* @__PURE__ */
|
|
4836
|
-
|
|
5032
|
+
showRegions && /* @__PURE__ */ jsx102(ChoiceSection, { label: regionLabel, options: regionOptions, selected: region, onSelect: setRegion }),
|
|
5033
|
+
showCountries && /* @__PURE__ */ jsx102(ChoiceSection, { label: countryLabel, options: countryOptions, selected: country, onSelect: setCountry }),
|
|
5034
|
+
showChannels && /* @__PURE__ */ jsx102(ChoiceSection, { label: channelLabel, options: channelOptions, selected: channel, onSelect: setChannel }),
|
|
5035
|
+
/* @__PURE__ */ jsx102(
|
|
5036
|
+
Pressable34,
|
|
4837
5037
|
{
|
|
4838
5038
|
onPress: handleSubmit,
|
|
4839
5039
|
disabled: !complete,
|
|
4840
5040
|
className: cn84("items-center rounded-lg bg-primary px-4 py-3", !complete && "opacity-50"),
|
|
4841
|
-
children: /* @__PURE__ */
|
|
5041
|
+
children: /* @__PURE__ */ jsx102(Text44, { className: "text-sm font-semibold text-primary-foreground", children: submitLabel })
|
|
4842
5042
|
}
|
|
4843
5043
|
)
|
|
4844
5044
|
] });
|
|
4845
5045
|
}
|
|
4846
5046
|
function ChoiceSection({ label, options, selected, onSelect }) {
|
|
4847
|
-
return /* @__PURE__ */
|
|
4848
|
-
/* @__PURE__ */
|
|
4849
|
-
/* @__PURE__ */
|
|
5047
|
+
return /* @__PURE__ */ jsxs61(View67, { className: "gap-1.5", children: [
|
|
5048
|
+
/* @__PURE__ */ jsx102(Text44, { className: "text-xs font-semibold text-muted-foreground", children: label }),
|
|
5049
|
+
/* @__PURE__ */ jsx102(View67, { className: "rounded-lg border border-border", children: options.map((option, index) => {
|
|
4850
5050
|
const checked = option.value === selected;
|
|
4851
|
-
return /* @__PURE__ */
|
|
4852
|
-
|
|
5051
|
+
return /* @__PURE__ */ jsxs61(
|
|
5052
|
+
Pressable34,
|
|
4853
5053
|
{
|
|
4854
5054
|
onPress: () => onSelect(option.value),
|
|
4855
5055
|
accessibilityRole: "radio",
|
|
@@ -4857,8 +5057,8 @@ function ChoiceSection({ label, options, selected, onSelect }) {
|
|
|
4857
5057
|
testID: `choice-option-${option.value}`,
|
|
4858
5058
|
className: cn84("flex-row items-center gap-3 px-4 py-3", index > 0 && "border-t border-border", checked && "bg-primary/10"),
|
|
4859
5059
|
children: [
|
|
4860
|
-
/* @__PURE__ */
|
|
4861
|
-
/* @__PURE__ */
|
|
5060
|
+
/* @__PURE__ */ jsx102(View67, { className: "h-4 w-4 items-center justify-center rounded-full border border-primary", children: checked && /* @__PURE__ */ jsx102(View67, { testID: `choice-option-${option.value}-dot`, className: "h-2.5 w-2.5 rounded-full bg-primary" }) }),
|
|
5061
|
+
/* @__PURE__ */ jsx102(Text44, { className: cn84("text-sm", checked ? "font-semibold text-primary" : "text-foreground"), children: option.label })
|
|
4862
5062
|
]
|
|
4863
5063
|
},
|
|
4864
5064
|
option.value
|
|
@@ -4988,11 +5188,13 @@ export {
|
|
|
4988
5188
|
OrderSummaryLine,
|
|
4989
5189
|
OrdersList,
|
|
4990
5190
|
POSLayout,
|
|
5191
|
+
ParkedSales,
|
|
4991
5192
|
PaymentMethodCard,
|
|
4992
5193
|
PaymentSelector,
|
|
4993
5194
|
Popover,
|
|
4994
5195
|
PopoverContent,
|
|
4995
5196
|
PopoverTrigger,
|
|
5197
|
+
PriceForm,
|
|
4996
5198
|
ProductCard,
|
|
4997
5199
|
ProductGrid,
|
|
4998
5200
|
ProductImage,
|
|
@@ -5076,6 +5278,7 @@ export {
|
|
|
5076
5278
|
loaderVariants,
|
|
5077
5279
|
orderReference,
|
|
5078
5280
|
parseDiscount,
|
|
5281
|
+
parsePrice,
|
|
5079
5282
|
textVariants,
|
|
5080
5283
|
toastVariants,
|
|
5081
5284
|
toggleTextVariants,
|