@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.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 useState6 } from "react";
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] = useState6("");
2443
- const [customers, setCustomers] = useState6([]);
2444
- const [error, setError] = useState6(null);
2445
- const [showForm, setShowForm] = useState6(false);
2446
- const [creating, setCreating] = useState6(false);
2447
- const [values, setValues] = useState6({ firstName: "", lastName: "", email: "", phone: "", address: "" });
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] = useState6(false);
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 useState7 } from "react";
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] = useState7(defaultAmount ?? total);
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] = useState7(decimal2);
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 useState8 } from "react";
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] = useState8(defaultCounts ?? {});
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 useState9 } from "react";
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] = useState9(null);
3149
+ const [enteredAmount, setEnteredAmount] = useState10(null);
3133
3150
  const amount = enteredAmount ?? (expectedFloatMinor != null ? decimal(expectedFloatMinor, digits) : "");
3134
- const [busy, setBusy] = useState9(false);
3135
- const [error, setError] = useState9("");
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 useState10 } from "react";
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] = useState10("");
3284
- const [reason, setReason] = useState10("");
3285
- const [busy, setBusy] = useState10(false);
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] = useState10("");
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 useState11 } from "react";
3345
- import { ScrollView as ScrollView11, useWindowDimensions, View as View47 } from "react-native";
3346
- import { formatMoney as formatMoney7 } from "@tallyui/core";
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 useState13 } from "react";
3531
- import { Platform, Pressable as Pressable22, ScrollView as ScrollView12, useWindowDimensions as useWindowDimensions2, View as View49 } from "react-native";
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 formatMoney8, minorUnitDigits as minorUnitDigits5 } from "@tallyui/core";
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 jsx83, jsxs as jsxs42 } from "react/jsx-runtime";
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__ */ jsxs42(
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__ */ jsx83(Text, { className: "text-sm tabular-nums", numberOfLines: 1, children: label }),
3598
- /* @__PURE__ */ jsx83(Text, { testID: `den-count-${value}`, className: "bg-background min-w-6 rounded-full px-1.5 text-center text-xs tabular-nums", children: count2 })
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 } = useWindowDimensions2();
3443
+ const { width } = useWindowDimensions();
3608
3444
  const columns = width < 480 ? 3 : 4;
3609
- const [pieces, setPieces] = useState13({});
3610
- const [cashText, setCashText] = useState13(
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] = useState13(
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] = useState13(false);
3619
- const [error, setError] = useState13("");
3620
- const [refusal, setRefusal] = useState13("");
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__ */ jsxs42(View49, { testID: "register-count", className: cn76("bg-card flex-1 rounded-md", className), children: [
3669
- /* @__PURE__ */ jsxs42(ScrollView12, { className: "flex-1", contentContainerClassName: "gap-3 p-4", children: [
3670
- /* @__PURE__ */ jsx83(Text, { className: "min-h-11 text-lg font-semibold", children: "Count the drawer" }),
3671
- /* @__PURE__ */ jsx83(Label, { testID: "count-amount-label", nativeID: AMOUNT_LABEL_ID3, children: `Cash counted (${currencySymbol3(currency)})` }),
3672
- /* @__PURE__ */ jsx83(Input, { children: /* @__PURE__ */ jsx83(
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__ */ jsx83(Text, { testID: "count-variance", className: "min-h-11 tabular-nums", children: `Expected ${formatMoney8({ amount: expected.cash ?? 0, currency })} \xB7 ${varianceText(
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) => formatMoney8({ amount: Math.round(major * 10 ** digits), currency }) ?? "",
3525
+ (major) => formatMoney7({ amount: Math.round(major * 10 ** digits), currency }) ?? "",
3690
3526
  varianceWord
3691
3527
  )}` }),
3692
- /* @__PURE__ */ jsxs42(View49, { testID: "count-denominations", className: "gap-2", children: [
3693
- Array.from({ length: Math.ceil(faces.length / columns) }, (_, row) => /* @__PURE__ */ jsx83(View49, { className: "flex-row gap-2", children: Array.from({ length: columns }, (_2, column) => {
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__ */ jsx83(
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__ */ jsx83(View49, { className: "flex-1" }, column);
3540
+ ) : /* @__PURE__ */ jsx81(View47, { className: "flex-1" }, column);
3705
3541
  }) }, row)),
3706
- /* @__PURE__ */ jsx83(
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__ */ jsx83(Text, { children: "Clear" })
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__ */ jsxs42(View49, { className: "gap-2", children: [
3724
- /* @__PURE__ */ jsx83(Label, { testID: labelId, nativeID: labelId, children: label }),
3725
- /* @__PURE__ */ jsx83(Input, { children: /* @__PURE__ */ jsx83(
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__ */ jsx83(Text, { testID: "count-manager-line", className: "text-warning min-h-11", children: refusal }),
3739
- !!error && /* @__PURE__ */ jsx83(Text, { testID: "count-error", className: "text-destructive min-h-11", children: error })
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__ */ jsxs42(View49, { className: "gap-2 border-t border-border p-4", children: [
3742
- /* @__PURE__ */ jsx83(
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__ */ jsx83(Text, { children: "Back to selling" })
3586
+ children: /* @__PURE__ */ jsx81(Text, { children: "Back to selling" })
3751
3587
  }
3752
3588
  ),
3753
- /* @__PURE__ */ jsx83(
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__ */ jsx83(Text, { children: busy ? "Closing\u2026" : "Close register" })
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 useState14 } from "react";
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] = useState14(false);
3801
- const [error, setError] = useState14("");
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 useState16 } from "react";
3939
- import { Pressable as Pressable24, Text as Text33, View as View55 } from "react-native";
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 useState15 } from "react";
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] = useState15("percentage");
3964
- const [text, setText] = useState15("");
3965
- const [error, setError] = useState15(null);
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 jsx89, jsxs as jsxs48 } from "react/jsx-runtime";
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] = useState16(null);
4043
- const discountForm = (lineId, title) => form?.lineId === lineId ? /* @__PURE__ */ jsx89(
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__ */ jsxs48(Fragment5, { children: [
4055
- /* @__PURE__ */ jsx89(CartLineActions, { actions: [{ id: "discount", label: "Discount", color: "text-primary", onPress: () => setForm({ lineId: line.id }) }], children: /* @__PURE__ */ jsx89(
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__ */ jsx89(DiscountChips, { discounts: line.discounts, amounts: display(line.id).discounts, currency: order.currency, onRemove: sale.removeDiscount }),
4065
- /* @__PURE__ */ jsxs48(View55, { className: "flex-row gap-4 px-3 py-2", children: [
4066
- /* @__PURE__ */ jsx89(
4067
- Pressable24,
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__ */ jsx89(Text33, { className: "text-center text-foreground", children: "\u2212" })
4165
+ children: /* @__PURE__ */ jsx90(Text34, { className: "text-center text-foreground", children: "\u2212" })
4074
4166
  }
4075
4167
  ),
4076
- /* @__PURE__ */ jsx89(
4077
- Pressable24,
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__ */ jsx89(Text33, { className: "text-center text-foreground", children: "+" })
4175
+ children: /* @__PURE__ */ jsx90(Text34, { className: "text-center text-foreground", children: "+" })
4084
4176
  }
4085
4177
  ),
4086
- /* @__PURE__ */ jsx89(
4087
- Pressable24,
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__ */ jsx89(Text33, { className: "text-center text-foreground", children: "Remove" })
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__ */ jsx89(
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__ */ jsx89(Text33, { testID: "cart-empty", className: "px-3 py-2 text-muted-foreground", children: "Scan or tap a product to start a sale." }),
4106
- afterItems: order.lineItems.length ? /* @__PURE__ */ jsxs48(View55, { className: "gap-3 px-3 py-2", children: [
4107
- /* @__PURE__ */ jsx89(
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__ */ jsx89(
4118
- Pressable24,
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__ */ jsx89(Text33, { className: "text-foreground", children: "Order discount" })
4231
+ children: /* @__PURE__ */ jsx90(Text34, { className: "text-foreground", children: "Order discount" })
4124
4232
  }
4125
4233
  )
4126
4234
  ] }) : void 0,
4127
- footer: /* @__PURE__ */ jsxs48(View55, { testID: "cart-footer", className: "gap-3 pb-4", children: [
4128
- /* @__PURE__ */ jsx89(
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__ */ jsx89(Text33, { accessibilityRole: "alert", className: "text-destructive", children: sale.error }) : null,
4139
- /* @__PURE__ */ jsxs48(View55, { className: "flex-row gap-3", children: [
4140
- /* @__PURE__ */ jsx89(Pressable24, { accessibilityRole: "button", disabled: !order.lineItems.length, onPress: () => sale.startTender("cash"), className: pay, children: /* @__PURE__ */ jsx89(Text33, { className: "text-center font-semibold text-primary-foreground", children: "Cash" }) }),
4141
- /* @__PURE__ */ jsx89(Pressable24, { accessibilityRole: "button", disabled: !order.lineItems.length, onPress: () => sale.startTender("external"), className: pay, children: /* @__PURE__ */ jsx89(Text33, { className: "text-center font-semibold text-primary-foreground", children: "Card terminal" }) })
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 Pressable25, Text as Text34 } from "react-native";
4150
- import { formatMoney as formatMoney11 } from "@tallyui/core";
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 jsx90, jsxs as jsxs49 } from "react/jsx-runtime";
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 = formatMoney11({ amount: buildReceiptData2(order, { storeName: "" }).totals.totalMinor, currency: order.currency });
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__ */ jsxs49(
4159
- Pressable25,
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__ */ jsx90(Text34, { className: "font-bold text-primary-foreground", children: count2 ? `Cart \xB7 ${items}` : "Cart is empty" }),
4168
- count2 ? /* @__PURE__ */ jsxs49(Text34, { className: "font-bold text-primary-foreground", children: [
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 Pressable26, Text as Text35, TextInput as TextInput10, View as View56 } from "react-native";
4179
- import { formatMoney as formatMoney12 } from "@tallyui/core";
4180
- import { Fragment as Fragment6, jsx as jsx91, jsxs as jsxs50 } from "react/jsx-runtime";
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__ */ jsxs50(View56, { dataSet: { print: "hide" }, className: "gap-4 p-4", children: [
4187
- stage.method === "cash" ? /* @__PURE__ */ jsxs50(Fragment6, { children: [
4188
- /* @__PURE__ */ jsx91(
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__ */ jsx91(ChangeDisplay, { change: money(order.changeDueMinor) }),
4197
- order.balanceDueMinor > 0 ? /* @__PURE__ */ jsxs50(Text35, { className: "text-foreground", children: [
4304
+ /* @__PURE__ */ jsx92(ChangeDisplay, { change: money(order.changeDueMinor) }),
4305
+ order.balanceDueMinor > 0 ? /* @__PURE__ */ jsxs51(Text36, { className: "text-foreground", children: [
4198
4306
  "Balance due: ",
4199
- formatMoney12(money(order.balanceDueMinor))
4307
+ formatMoney13(money(order.balanceDueMinor))
4200
4308
  ] }) : null,
4201
- /* @__PURE__ */ jsx91(Pressable26, { 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__ */ jsx91(Text35, { className: "text-center font-semibold text-primary-foreground", children: "Complete sale" }) })
4202
- ] }) : /* @__PURE__ */ jsxs50(Fragment6, { children: [
4203
- /* @__PURE__ */ jsxs50(Text35, { className: "text-foreground", children: [
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
- formatMoney12(money(order.totalMinor))
4313
+ formatMoney13(money(order.totalMinor))
4206
4314
  ] }),
4207
- /* @__PURE__ */ jsx91(
4208
- TextInput10,
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__ */ jsx91(Pressable26, { accessibilityRole: "button", onPress: sale.complete, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx91(Text35, { className: "text-center font-semibold text-primary-foreground", children: "Payment approved on terminal" }) })
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__ */ jsx91(Text35, { accessibilityRole: "alert", className: "text-destructive", children: sale.error }) : null,
4221
- sale.canContinue ? /* @__PURE__ */ jsxs50(Fragment6, { children: [
4222
- /* @__PURE__ */ jsx91(Text35, { className: "text-foreground", children: "This sale is stored and will be sent. Continue to the next sale." }),
4223
- /* @__PURE__ */ jsx91(Pressable26, { accessibilityRole: "button", onPress: () => sale.continueSale(), className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx91(Text35, { className: "text-center font-semibold text-primary-foreground", children: "Continue" }) })
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__ */ jsx91(Pressable26, { accessibilityRole: "button", onPress: sale.cancelTender, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx91(Text35, { className: "text-center text-foreground", children: "Back" }) })
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 useState17 } from "react";
4231
- import { Pressable as Pressable27, Text as Text36, View as View57 } from "react-native";
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 jsx92, jsxs as jsxs51 } from "react/jsx-runtime";
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] = useState17("");
4261
- const [chooserId, setChooserId] = useState17(null);
4262
- const [width, setWidth] = useState17(0);
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__ */ jsxs51(View57, { className: "flex-1", onLayout: (event) => setWidth(event.nativeEvent.layout.width), children: [
4275
- /* @__PURE__ */ jsxs51(View57, { className: "gap-2 border-b border-border bg-card px-4 pb-3 pt-3", children: [
4276
- /* @__PURE__ */ jsx92(
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__ */ jsxs51(View57, { testID: "catalogue-status-row", className: "flex-row items-center gap-2", children: [
4294
- statusText ? /* @__PURE__ */ jsxs51(Text36, { className: "flex-1 text-xs text-muted-foreground", numberOfLines: statusAccessory ? 1 : void 0, children: [
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__ */ jsxs51(View57, { accessibilityLabel: "Choose variant", className: "gap-2", children: [
4301
- choices.map((entry) => /* @__PURE__ */ jsxs51(
4302
- Pressable27,
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__ */ jsx92(Text36, { className: "font-semibold text-foreground", children: entry.variant.title }),
4309
- /* @__PURE__ */ jsx92(Text36, { className: "text-muted-foreground", children: entry.variant.sku }),
4310
- /* @__PURE__ */ jsx92(Text36, { className: "text-foreground", children: variantPriceLabel(entry.variant, currency) }),
4311
- /* @__PURE__ */ jsxs51(Text36, { className: "text-muted-foreground", children: [
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__ */ jsx92(Pressable27, { accessibilityRole: "button", onPress: () => setChooserId(null), className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx92(Text36, { className: "text-center text-foreground", children: "Cancel" }) })
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__ */ jsx92(
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__ */ jsx92(Pressable27, { accessibilityRole: "button", testID: `product-tile-${traits.getName(product)}`, onPress: () => {
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__ */ jsxs51(VStack, { space: "sm", className: "items-center rounded-lg border border-border bg-card p-3", children: [
4337
- /* @__PURE__ */ jsx92(ProductImage, { doc: product, size: 80, className: "rounded-md" }),
4338
- /* @__PURE__ */ jsx92(ProductTitle, { doc: product, className: "text-sm", numberOfLines: 2 }),
4339
- /* @__PURE__ */ jsx92(ProductPrice, { doc: product }),
4340
- /* @__PURE__ */ jsx92(ProductStockBadge, { doc: product, showAsOf: false, className: "self-center" })
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__ */ jsx92(Text36, { 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." })
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 Pressable28, Text as Text37, View as View58 } from "react-native";
4352
- import { formatMoney as formatMoney13 } from "@tallyui/core";
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 jsx93, jsxs as jsxs52 } from "react/jsx-runtime";
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) => formatMoney13({ amount, currency: receipt.currency }) ?? "";
4393
- const row = (label, amount, bold = false, key) => /* @__PURE__ */ jsxs52(View58, { accessibilityLabel: `${label}: ${amount}`, className: "flex-row justify-between gap-4", children: [
4394
- /* @__PURE__ */ jsx93(Text37, { className: `text-foreground ${bold ? "font-semibold" : ""}`, children: label }),
4395
- /* @__PURE__ */ jsx93(Text37, { className: `text-right text-foreground ${bold ? "font-semibold" : ""}`, children: amount })
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__ */ jsxs52(Fragment7, { children: [
4401
- topInset > 0 ? /* @__PURE__ */ jsx93(View58, { dataSet: { print: "hide" }, style: { height: topInset, flexShrink: 0 } }) : null,
4402
- /* @__PURE__ */ jsxs52(View58, { className: "w-full max-w-md self-center gap-3 p-4 bg-card", children: [
4403
- /* @__PURE__ */ jsx93(Text37, { className: "text-lg font-semibold text-foreground", children: receipt.header.storeName }),
4404
- receipt.header.storeAddress ? /* @__PURE__ */ jsx93(Text37, { className: "text-muted-foreground", children: receipt.header.storeAddress }) : null,
4405
- /* @__PURE__ */ jsx93(Text37, { testID: "receipt-order", className: "text-foreground", children: posOrder ? `Order ${orderReference(posOrder)}` : `Order ${receipt.header.orderNumber.slice(-8)} (draft)` }),
4406
- /* @__PURE__ */ jsx93(Text37, { className: "text-muted-foreground", children: formatDate(posOrder?.createdAt ?? receipt.header.date) }),
4407
- /* @__PURE__ */ jsxs52(Text37, { className: "text-muted-foreground", children: [
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__ */ jsxs52(Text37, { className: "text-muted-foreground", children: [
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__ */ jsxs52(View58, { children: [
4416
- /* @__PURE__ */ jsx93(Text37, { className: "text-foreground", children: line.name }),
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__ */ jsx93(View58, { className: "pl-4", children: row(`${discountLabel(
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__ */ jsxs52(View58, { dataSet: { print: "hide" }, className: "flex-row gap-4", children: [
4436
- /* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", onPress: () => {
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__ */ jsx93(Text37, { className: "text-center text-foreground", children: "Print receipt" }) }),
4439
- /* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", onPress: newSale, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx93(Text37, { className: "text-center font-semibold text-primary-foreground", children: "New sale" }) })
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 useState18 } from "react";
4447
- import { AccessibilityInfo as AccessibilityInfo2, Platform as Platform3, Text as Text38, View as View59 } from "react-native";
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 jsx94, jsxs as jsxs53 } from "react/jsx-runtime";
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] = useState18(Date.now);
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__ */ jsxs53(View59, { children: [
4533
- notice ? /* @__PURE__ */ jsxs53(Fragment8, { children: [
4534
- /* @__PURE__ */ jsx94(Text38, { accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: notice.line }),
4535
- oneDetail ? null : /* @__PURE__ */ jsx94(Text38, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail })
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__ */ jsx94(Text38, { accessibilityLabel: spoken, accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: spoken }),
4538
- doing ? /* @__PURE__ */ jsx94(Text38, { className: "px-4 pb-2 text-xs text-muted-foreground", children: doing }) : null,
4539
- refused ? /* @__PURE__ */ jsx94(Text38, { className: "px-4 pb-2 text-xs text-muted-foreground", children: REFUSED_DETAIL }) : null,
4540
- oneDetail && notice ? /* @__PURE__ */ jsx94(Text38, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail }) : backendMissing ? /* @__PURE__ */ jsx94(Text38, { 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
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 useState19 } from "react";
4546
- import { Pressable as Pressable29, ScrollView as ScrollView13, Text as Text39, View as View60 } from "react-native";
4547
- import { formatMoney as formatMoney14, knownWarnings } from "@tallyui/core";
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 jsx95, jsxs as jsxs54 } from "react/jsx-runtime";
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] = useState19(/* @__PURE__ */ new Set());
4574
- const [expanded, setExpanded] = useState19(/* @__PURE__ */ new Set());
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__ */ jsxs54(ScrollView13, { className: "flex-1 bg-background p-4", children: [
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__ */ jsxs54(View60, { className: "mb-6 gap-3", children: [
4583
- /* @__PURE__ */ jsx95(Text39, { accessibilityRole: "header", className: "text-xl font-semibold text-foreground", children: section.title }),
4584
- section.title === "Recent" && orders.length === 0 ? /* @__PURE__ */ jsx95(Text39, { testID: "orders-empty", className: "text-muted-foreground", children: "No sales yet. Completed sales appear here." }) : null,
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__ */ jsxs54(View60, { className: "gap-1 rounded-md border border-border bg-card p-3", children: [
4591
- /* @__PURE__ */ jsxs54(
4592
- Pressable29,
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__ */ jsx95(Text39, { className: "text-foreground", children: `Order ${orderReference(order)} \xB7 ${count2} ${count2 === 1 ? "item" : "items"}` }),
4607
- /* @__PURE__ */ jsxs54(Text39, { className: "text-muted-foreground", children: [
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
- formatMoney14({ amount: order.totalMinor, currency: order.currency }),
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__ */ jsxs54(View60, { testID: `order-detail-${order.id}`, className: "mt-1 gap-1 border-t border-border pt-2", children: [
4618
- order.lines.map((line) => /* @__PURE__ */ jsx95(Text39, { className: "text-foreground", children: `${line.quantity} \xD7 ${line.name}` }, line.id)),
4619
- order.payments.map((payment) => /* @__PURE__ */ jsx95(Text39, { className: "text-muted-foreground", children: `${payment.method === "cash" ? "Cash" : "Card terminal"} ${formatMoney14({ amount: payment.amountMinor, currency: order.currency })}${payment.reference ? ` \xB7 ${payment.reference}` : ""}` }, payment.id))
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__ */ jsx95(Text39, { className: "text-destructive", children: `Hasn't reached the online store since ${sinceText(stuckEntry)}.` }) : null,
4622
- order.syncStatus === "rejected" ? /* @__PURE__ */ jsx95(Text39, { className: "text-destructive", children: refusalSentence(order.error?.code) }) : null,
4623
- order.lateSessionId !== void 0 ? /* @__PURE__ */ jsx95(Text39, { className: "text-foreground", children: "Taken after the register closed. It is not in that register's closure." }) : null,
4624
- order.localWarnings?.map((warning, index) => /* @__PURE__ */ jsx95(Text39, { 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)),
4625
- section.title === "Needs attention" && order.syncStatus === "rejected" && order.error?.code !== "idempotency_mismatch" ? /* @__PURE__ */ jsx95(Pressable29, { accessibilityRole: "button", disabled: retryingIds.has(order.id), onPress: async () => {
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__ */ jsx95(Text39, { className: "text-center font-semibold text-primary-foreground", children: "Retry" }) }) : null,
4634
- knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */ jsx95(View60, { className: "border-l-4 border-warning pl-2", children: /* @__PURE__ */ jsx95(Text39, { 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 ${formatMoney14({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney14({ 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 ${formatMoney14({ amount: tillMinor, currency: order.currency })}, store ${formatMoney14({ amount: serverMinor, currency: order.currency })}.`).join(" ")}` : warning.bridgeMinor !== void 0 ? `Store calculated ${formatMoney14({ amount: warning.serverMinor, currency: order.currency })}; a rounding line of ${warning.bridgeMinor > 0 ? "+" : ""}${formatMoney14({ amount: warning.bridgeMinor, currency: order.currency })} brought it to ${formatMoney14({ amount: warning.expectedMinor, currency: order.currency })}` : `Store total ${formatMoney14({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney14({ amount: warning.expectedMinor, currency: order.currency })}` }) }, index))
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 useState20 } from "react";
4644
- import { Pressable as Pressable30, Text as Text40, View as View61, useWindowDimensions as useWindowDimensions3 } from "react-native";
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 Fragment9, jsx as jsx96, jsxs as jsxs55 } from "react/jsx-runtime";
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 } = useWindowDimensions3();
4856
+ const { width } = useWindowDimensions4();
4657
4857
  const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
4658
- const [activeTab, setActiveTab] = useState20("browse");
4659
- return /* @__PURE__ */ jsxs55(View61, { className: cn80("flex-1 bg-bg", className), ...viewProps, children: [
4660
- headerSlot && /* @__PURE__ */ jsx96(View61, { className: "border-b border-border", children: headerSlot }),
4661
- layout === "split" ? /* @__PURE__ */ jsxs55(View61, { className: "flex-1 flex-row", children: [
4662
- /* @__PURE__ */ jsx96(View61, { className: "flex-[3]", children: browseSlot }),
4663
- /* @__PURE__ */ jsx96(View61, { className: "flex-[2] border-l border-border", children: cartSlot })
4664
- ] }) : /* @__PURE__ */ jsxs55(Fragment9, { children: [
4665
- /* @__PURE__ */ jsxs55(View61, { className: "flex-row border-b border-border", children: [
4666
- /* @__PURE__ */ jsx96(
4667
- Pressable30,
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__ */ jsx96(Text40, { className: cn80("text-center text-sm font-semibold", activeTab === "browse" ? "text-primary" : "text-muted-foreground"), children: "Browse" })
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__ */ jsx96(
4675
- Pressable30,
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__ */ jsx96(Text40, { className: cn80("text-center text-sm font-semibold", activeTab === "cart" ? "text-primary" : "text-muted-foreground"), children: "Cart" })
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__ */ jsx96(View61, { className: "flex-1", children: activeTab === "browse" ? browseSlot : cartSlot })
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 ScrollView14, View as View62, useWindowDimensions as useWindowDimensions4 } from "react-native";
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 jsx97, jsxs as jsxs56 } from "react/jsx-runtime";
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 } = useWindowDimensions4();
4901
+ const { width } = useWindowDimensions5();
4702
4902
  const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
4703
- return /* @__PURE__ */ jsxs56(View62, { className: cn81("flex-1 bg-bg", className), ...viewProps, children: [
4704
- headerSlot && /* @__PURE__ */ jsx97(View62, { className: "border-b border-border", children: headerSlot }),
4705
- layout === "split" ? /* @__PURE__ */ jsxs56(View62, { className: "flex-1 flex-row", children: [
4706
- /* @__PURE__ */ jsx97(View62, { className: "flex-1 border-r border-border", children: summarySlot }),
4707
- /* @__PURE__ */ jsx97(View62, { className: "flex-1", children: paymentSlot })
4708
- ] }) : /* @__PURE__ */ jsx97(ScrollView14, { className: "flex-1", children: /* @__PURE__ */ jsxs56(View62, { className: "gap-4 p-4", children: [
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 useState21 } from "react";
4717
- import { Pressable as Pressable31, Text as Text41, View as View63, useWindowDimensions as useWindowDimensions5 } from "react-native";
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 jsx98, jsxs as jsxs57 } from "react/jsx-runtime";
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 } = useWindowDimensions5();
4929
+ const { width } = useWindowDimensions6();
4730
4930
  const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
4731
- const [showNav, setShowNav] = useState21(false);
4732
- return /* @__PURE__ */ jsxs57(View63, { className: cn82("flex-1 bg-bg", className), ...viewProps, children: [
4733
- headerSlot && /* @__PURE__ */ jsx98(View63, { className: "border-b border-border", children: headerSlot }),
4734
- layout === "split" ? /* @__PURE__ */ jsxs57(View63, { className: "flex-1 flex-row", children: [
4735
- /* @__PURE__ */ jsx98(View63, { className: "w-64 border-r border-border", children: navSlot }),
4736
- /* @__PURE__ */ jsx98(View63, { className: "flex-1", children: contentSlot })
4737
- ] }) : /* @__PURE__ */ jsx98(View63, { className: "flex-1", children: showNav ? /* @__PURE__ */ jsxs57(View63, { className: "flex-1", children: [
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__ */ jsx98(Pressable31, { onPress: () => setShowNav(false), className: "border-t border-border p-3", children: /* @__PURE__ */ jsx98(Text41, { className: "text-center text-sm font-medium text-primary", children: "Done" }) })
4740
- ] }) : /* @__PURE__ */ jsxs57(View63, { className: "flex-1", children: [
4741
- /* @__PURE__ */ jsx98(Pressable31, { onPress: () => setShowNav(true), className: "border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx98(Text41, { className: "text-sm font-medium text-primary", children: "Menu" }) }),
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 Pressable32, Text as Text42, View as View64 } from "react-native";
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 jsx99, jsxs as jsxs58 } from "react/jsx-runtime";
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__ */ jsxs58(View64, { className: cn83("flex-1 items-center justify-center gap-4 bg-bg p-6", className), ...viewProps, children: [
4768
- /* @__PURE__ */ jsx99(Text42, { className: "text-center text-lg font-bold text-foreground", children: parkedTitle }),
4769
- /* @__PURE__ */ jsx99(Text42, { className: "text-center text-sm text-muted-foreground", children: body }),
4770
- /* @__PURE__ */ jsx99(Pressable32, { onPress, className: "items-center rounded-lg bg-primary px-4 py-3", children: /* @__PURE__ */ jsx99(Text42, { className: "text-sm font-semibold text-primary-foreground", children: buttonLabel }) })
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 useState22 } from "react";
4776
- import { Pressable as Pressable33, Text as Text43, View as View65 } from "react-native";
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 jsx100, jsxs as jsxs59 } from "react/jsx-runtime";
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 useState22(preset);
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__ */ jsxs59(View65, { className: cn84("w-full max-w-[560px] flex-1 self-center gap-6 bg-bg p-6", className), ...viewProps, children: [
4828
- /* @__PURE__ */ jsxs59(View65, { className: "gap-2", children: [
4829
- /* @__PURE__ */ jsx100(Text43, { className: "text-lg font-bold text-foreground", children: title }),
4830
- /* @__PURE__ */ jsx100(Text43, { className: "text-sm text-muted-foreground", children: body })
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__ */ jsx100(ChoiceSection, { label: regionLabel, options: regionOptions, selected: region, onSelect: setRegion }),
4833
- showCountries && /* @__PURE__ */ jsx100(ChoiceSection, { label: countryLabel, options: countryOptions, selected: country, onSelect: setCountry }),
4834
- showChannels && /* @__PURE__ */ jsx100(ChoiceSection, { label: channelLabel, options: channelOptions, selected: channel, onSelect: setChannel }),
4835
- /* @__PURE__ */ jsx100(
4836
- Pressable33,
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__ */ jsx100(Text43, { className: "text-sm font-semibold text-primary-foreground", children: submitLabel })
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__ */ jsxs59(View65, { className: "gap-1.5", children: [
4848
- /* @__PURE__ */ jsx100(Text43, { className: "text-xs font-semibold text-muted-foreground", children: label }),
4849
- /* @__PURE__ */ jsx100(View65, { className: "rounded-lg border border-border", children: options.map((option, index) => {
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__ */ jsxs59(
4852
- Pressable33,
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__ */ jsx100(View65, { className: "h-4 w-4 items-center justify-center rounded-full border border-primary", children: checked && /* @__PURE__ */ jsx100(View65, { testID: `choice-option-${option.value}-dot`, className: "h-2.5 w-2.5 rounded-full bg-primary" }) }),
4861
- /* @__PURE__ */ jsx100(Text43, { className: cn84("text-sm", checked ? "font-semibold text-primary" : "text-foreground"), children: option.label })
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,