@tallyui/components 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -1
- package/dist/index.js +318 -185
- package/package.json +9 -9
- package/src/index.ts +1 -1
- package/src/sale/index.ts +1 -0
- package/src/sale/split-tender.tsx +81 -0
package/dist/index.d.ts
CHANGED
|
@@ -1240,6 +1240,10 @@ declare function Tender({ sale }: {
|
|
|
1240
1240
|
sale: ReturnType<typeof useSale>;
|
|
1241
1241
|
}): react_jsx_runtime.JSX.Element | null;
|
|
1242
1242
|
|
|
1243
|
+
declare function SplitTender({ sale }: {
|
|
1244
|
+
sale: ReturnType<typeof useSale>;
|
|
1245
|
+
}): JSX.Element | null;
|
|
1246
|
+
|
|
1243
1247
|
/** The cashier's entry as a TallyUI Discount (a percent, or integer minor units), or the inline error to show. */
|
|
1244
1248
|
declare function parseDiscount(type: Discount['type'], text: string, currency: string): Discount | string;
|
|
1245
1249
|
/** "10%", or a fixed discount's amount as it actually comes off (amountMinor), never the amount requested. */
|
|
@@ -1450,4 +1454,4 @@ interface StoreSettingsChoiceScreenProps extends Omit<ViewProps, 'children'> {
|
|
|
1450
1454
|
*/
|
|
1451
1455
|
declare function StoreSettingsChoiceScreen({ choices, initial, onSubmit, countryName, locale, title, body, regionLabel, countryLabel, channelLabel, submitLabel, className, ...viewProps }: StoreSettingsChoiceScreenProps): react_jsx_runtime.JSX.Element;
|
|
1452
1456
|
|
|
1453
|
-
export { APPROVAL_REQUIRED_TEXT, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cart, type CartAction, CartBar, CartLine, CartLineActions, type CartLineActionsProps, type CartLineProps, CartNoteInput, type CartNoteInputProps, CartPanel, type CartPanelProps, CartTotal, type CartTotalProps, CashCountInput, type CashCountInputProps, CashTendered, type CashTenderedProps, Catalogue, type CategoryItem, CategoryNav, type CategoryNavProps, ChangeDisplay, type ChangeDisplayProps, Checkbox, CheckoutLayout, type CheckoutLayoutProps, type ChipItem, ClosureSheet, type ClosureSheetProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxTrigger, type ConnectionStatus, ConnectorStatus, type ConnectorStatusProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CustomerCard, type CustomerCardProps, CustomerForm, type CustomerFormProps, type CustomerFormValues, CustomerOrderHistory, type CustomerOrderHistoryProps, CustomerPicker, type CustomerPickerProps, CustomerSelect, type CustomerSelectProps, type Denomination, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiscountBadge, type DiscountBadgeProps, DiscountChips, DiscountForm, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FilterChip, FilterChipGroup, type FilterChipGroupProps, type FilterChipProps, HStack, type HStackProps, HoverCard, HoverCardContent, HoverCardTrigger, Icon, IconButton, type IconButtonProps, type IconProps, Input, InputField, InputRoot, type InputRootProps, InputSlot, Label, type LabelProps, LiveTabScreen, type LiveTabScreenProps, type LiveTabScreenState, Loader, type LoaderProps, MovementSheet, type MovementSheetProps, OpenRegisterCard, type OpenRegisterCardProps, OrderCard, type OrderCardProps, OrderDetail, type OrderDetailLineItem, type OrderDetailProps, OrderList, type OrderListProps, type OrderStatus, OrderStatusBadge, type OrderStatusBadgeProps, OrderSummary, OrderSummaryLine, type OrderSummaryLineProps, type OrderSummaryPayment, type OrderSummaryProps, OrdersList, POSLayout, type POSLayoutProps, ParkedSales, type ParkedSalesProps, type PaymentMethod, PaymentMethodCard, type PaymentMethodCardProps, PaymentSelector, type PaymentSelectorProps, Popover, PopoverContent, PopoverTrigger, PriceForm, ProductCard, type ProductCardProps, ProductGrid, type ProductGridProps, ProductImage, type ProductImageProps, ProductList, type ProductListProps, ProductPrice, type ProductPriceProps, ProductSku, type ProductSkuProps, ProductStockBadge, type ProductStockBadgeProps, ProductTitle, type ProductTitleProps, ProductVariantPicker, type ProductVariantPickerProps, Progress, type ProgressProps, QuantityStepper, type QuantityStepperProps, RadioGroup, RadioGroupItem, Receipt, type ReceiptItem, ReceiptPreview, type ReceiptPreviewProps, RegisterBar, type RegisterBarPill, type RegisterBarProps, RegisterColumn, type RegisterColumnProps, RegisterCount, type RegisterCountProps, RegisterOpenClose, type RegisterOpenCloseProps, RegisterPanel, type RegisterPanelProps, RegisterPicker, type RegisterPickerProps, type RegisterPickerRegister, RegisterSummary, type RegisterSummaryProps, SearchIcon, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, SettingsGroup, type SettingsGroupProps, SettingsLayout, type SettingsLayoutProps, SettingsRow, type SettingsRowProps, Slider, type SliderProps, StoreSettingsChoiceScreen, type StoreSettingsChoiceScreenProps, type SvgIconProps, Switch, SyncStatus, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tender, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastClose, ToastDescription, type ToastProps, ToastTitle, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, type TransactionSummary, VStack, type VStackProps, type VariantOption, XIcon, avatarVariants, badgeTextVariants, badgeVariants, buttonTextVariants, buttonVariants, createSvgIcon, describeRegisterBarPill, discountLabel, formatStockSyncTime, hstackVariants, iconButtonVariants, iconVariants, injectPrintStyle, loaderVariants, orderReference, parseDiscount, parsePrice, textVariants, toastVariants, toggleTextVariants, toggleVariants, useInputContext, vstackVariants };
|
|
1457
|
+
export { APPROVAL_REQUIRED_TEXT, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, type AvatarProps, Badge, type BadgeProps, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cart, type CartAction, CartBar, CartLine, CartLineActions, type CartLineActionsProps, type CartLineProps, CartNoteInput, type CartNoteInputProps, CartPanel, type CartPanelProps, CartTotal, type CartTotalProps, CashCountInput, type CashCountInputProps, CashTendered, type CashTenderedProps, Catalogue, type CategoryItem, CategoryNav, type CategoryNavProps, ChangeDisplay, type ChangeDisplayProps, Checkbox, CheckoutLayout, type CheckoutLayoutProps, type ChipItem, ClosureSheet, type ClosureSheetProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxTrigger, type ConnectionStatus, ConnectorStatus, type ConnectorStatusProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CustomerCard, type CustomerCardProps, CustomerForm, type CustomerFormProps, type CustomerFormValues, CustomerOrderHistory, type CustomerOrderHistoryProps, CustomerPicker, type CustomerPickerProps, CustomerSelect, type CustomerSelectProps, type Denomination, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiscountBadge, type DiscountBadgeProps, DiscountChips, DiscountForm, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FilterChip, FilterChipGroup, type FilterChipGroupProps, type FilterChipProps, HStack, type HStackProps, HoverCard, HoverCardContent, HoverCardTrigger, Icon, IconButton, type IconButtonProps, type IconProps, Input, InputField, InputRoot, type InputRootProps, InputSlot, Label, type LabelProps, LiveTabScreen, type LiveTabScreenProps, type LiveTabScreenState, Loader, type LoaderProps, MovementSheet, type MovementSheetProps, OpenRegisterCard, type OpenRegisterCardProps, OrderCard, type OrderCardProps, OrderDetail, type OrderDetailLineItem, type OrderDetailProps, OrderList, type OrderListProps, type OrderStatus, OrderStatusBadge, type OrderStatusBadgeProps, OrderSummary, OrderSummaryLine, type OrderSummaryLineProps, type OrderSummaryPayment, type OrderSummaryProps, OrdersList, POSLayout, type POSLayoutProps, ParkedSales, type ParkedSalesProps, type PaymentMethod, PaymentMethodCard, type PaymentMethodCardProps, PaymentSelector, type PaymentSelectorProps, Popover, PopoverContent, PopoverTrigger, PriceForm, ProductCard, type ProductCardProps, ProductGrid, type ProductGridProps, ProductImage, type ProductImageProps, ProductList, type ProductListProps, ProductPrice, type ProductPriceProps, ProductSku, type ProductSkuProps, ProductStockBadge, type ProductStockBadgeProps, ProductTitle, type ProductTitleProps, ProductVariantPicker, type ProductVariantPickerProps, Progress, type ProgressProps, QuantityStepper, type QuantityStepperProps, RadioGroup, RadioGroupItem, Receipt, type ReceiptItem, ReceiptPreview, type ReceiptPreviewProps, RegisterBar, type RegisterBarPill, type RegisterBarProps, RegisterColumn, type RegisterColumnProps, RegisterCount, type RegisterCountProps, RegisterOpenClose, type RegisterOpenCloseProps, RegisterPanel, type RegisterPanelProps, RegisterPicker, type RegisterPickerProps, type RegisterPickerRegister, RegisterSummary, type RegisterSummaryProps, SearchIcon, SearchInput, type SearchInputProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, SettingsGroup, type SettingsGroupProps, SettingsLayout, type SettingsLayoutProps, SettingsRow, type SettingsRowProps, Slider, type SliderProps, SplitTender, StoreSettingsChoiceScreen, type StoreSettingsChoiceScreenProps, type SvgIconProps, Switch, SyncStatus, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tender, Text, type TextProps, Textarea, type TextareaProps, Toast, ToastClose, ToastDescription, type ToastProps, ToastTitle, Toggle, ToggleGroup, ToggleGroupItem, type ToggleGroupItemProps, type ToggleGroupProps, type ToggleProps, Tooltip, TooltipContent, TooltipTrigger, type TransactionSummary, VStack, type VStackProps, type VariantOption, XIcon, avatarVariants, badgeTextVariants, badgeVariants, buttonTextVariants, buttonVariants, createSvgIcon, describeRegisterBarPill, discountLabel, formatStockSyncTime, hstackVariants, iconButtonVariants, iconVariants, injectPrintStyle, loaderVariants, orderReference, parseDiscount, parsePrice, textVariants, toastVariants, toggleTextVariants, toggleVariants, useInputContext, vstackVariants };
|
package/dist/index.js
CHANGED
|
@@ -4334,12 +4334,144 @@ function Tender({ sale }) {
|
|
|
4334
4334
|
] });
|
|
4335
4335
|
}
|
|
4336
4336
|
|
|
4337
|
+
// src/sale/split-tender.tsx
|
|
4338
|
+
import { useState as useState19 } from "react";
|
|
4339
|
+
import { Pressable as Pressable28, Text as Text37, TextInput as TextInput12, View as View58 } from "react-native";
|
|
4340
|
+
import { formatMoney as formatMoney14, minorUnitDigits as minorUnitDigits9 } from "@tallyui/core";
|
|
4341
|
+
import { Fragment as Fragment7, jsx as jsx93, jsxs as jsxs52 } from "react/jsx-runtime";
|
|
4342
|
+
function SplitTender({ sale }) {
|
|
4343
|
+
const { order, stage } = sale;
|
|
4344
|
+
const [method, setMethod] = useState19("cash");
|
|
4345
|
+
const [amount, setAmount] = useState19(null);
|
|
4346
|
+
const [reference, setReference] = useState19("");
|
|
4347
|
+
const [error, setError] = useState19(null);
|
|
4348
|
+
if (stage.kind !== "tender") return null;
|
|
4349
|
+
const money = (value) => formatMoney14({ amount: value, currency: order.currency });
|
|
4350
|
+
const digits = minorUnitDigits9(order.currency);
|
|
4351
|
+
const amountText = amount ?? (order.balanceDueMinor / 10 ** digits).toFixed(digits);
|
|
4352
|
+
const incomplete = order.balanceDueMinor > 0 || order.payments.length === 0;
|
|
4353
|
+
function add() {
|
|
4354
|
+
const parsed = parsePrice(amountText, order.currency);
|
|
4355
|
+
if (typeof parsed === "string") {
|
|
4356
|
+
setError(parsed);
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
const id = sale.addTender({
|
|
4360
|
+
method,
|
|
4361
|
+
amountMinor: parsed,
|
|
4362
|
+
...method === "external" && reference ? { reference } : {}
|
|
4363
|
+
});
|
|
4364
|
+
if (id === null) {
|
|
4365
|
+
setError("That tender wasn't added: nothing is due, or the amount isn't valid.");
|
|
4366
|
+
return;
|
|
4367
|
+
}
|
|
4368
|
+
setError(null);
|
|
4369
|
+
setReference("");
|
|
4370
|
+
setAmount(null);
|
|
4371
|
+
}
|
|
4372
|
+
return /* @__PURE__ */ jsxs52(View58, { dataSet: { print: "hide" }, className: "gap-4 p-4", children: [
|
|
4373
|
+
/* @__PURE__ */ jsxs52(View58, { testID: "split-tender-summary", className: "gap-2", children: [
|
|
4374
|
+
/* @__PURE__ */ jsxs52(Text37, { className: "text-foreground", children: [
|
|
4375
|
+
"Total: ",
|
|
4376
|
+
money(order.totalMinor)
|
|
4377
|
+
] }),
|
|
4378
|
+
/* @__PURE__ */ jsxs52(Text37, { className: "text-foreground", children: [
|
|
4379
|
+
"Paid: ",
|
|
4380
|
+
money(order.paidMinor)
|
|
4381
|
+
] }),
|
|
4382
|
+
/* @__PURE__ */ jsxs52(Text37, { className: "text-foreground", children: [
|
|
4383
|
+
"Remaining: ",
|
|
4384
|
+
money(order.balanceDueMinor)
|
|
4385
|
+
] }),
|
|
4386
|
+
order.changeDueMinor > 0 ? /* @__PURE__ */ jsxs52(Text37, { className: "text-foreground", children: [
|
|
4387
|
+
"Change: ",
|
|
4388
|
+
money(order.changeDueMinor)
|
|
4389
|
+
] }) : null
|
|
4390
|
+
] }),
|
|
4391
|
+
/* @__PURE__ */ jsx93(View58, { testID: "split-tender-list", className: "gap-2", children: order.payments.map((payment) => /* @__PURE__ */ jsxs52(View58, { testID: `split-tender-row-${payment.id}`, className: "gap-2", children: [
|
|
4392
|
+
/* @__PURE__ */ jsxs52(Text37, { className: "text-foreground", children: [
|
|
4393
|
+
tenderLabel(payment.method),
|
|
4394
|
+
" ",
|
|
4395
|
+
money(payment.amountMinor)
|
|
4396
|
+
] }),
|
|
4397
|
+
payment.reference ? /* @__PURE__ */ jsx93(Text37, { className: "text-foreground", children: payment.reference }) : null,
|
|
4398
|
+
/* @__PURE__ */ jsx93(
|
|
4399
|
+
Pressable28,
|
|
4400
|
+
{
|
|
4401
|
+
testID: `split-tender-remove-${payment.id}`,
|
|
4402
|
+
accessibilityRole: "button",
|
|
4403
|
+
accessibilityLabel: "Remove tender",
|
|
4404
|
+
onPress: () => sale.removeTender(payment.id),
|
|
4405
|
+
className: "rounded-md border border-border bg-card px-4 py-3",
|
|
4406
|
+
children: /* @__PURE__ */ jsx93(Text37, { className: "text-center text-foreground", children: "Remove" })
|
|
4407
|
+
}
|
|
4408
|
+
)
|
|
4409
|
+
] }, payment.id)) }),
|
|
4410
|
+
/* @__PURE__ */ jsxs52(View58, { testID: "split-tender-add", className: "gap-2", children: [
|
|
4411
|
+
/* @__PURE__ */ jsx93(View58, { className: "flex-row gap-2", children: ["cash", "external"].map((value) => /* @__PURE__ */ jsx93(
|
|
4412
|
+
Pressable28,
|
|
4413
|
+
{
|
|
4414
|
+
testID: `split-tender-method-${value === "cash" ? "cash" : "card"}`,
|
|
4415
|
+
accessibilityRole: "button",
|
|
4416
|
+
accessibilityState: { selected: method === value },
|
|
4417
|
+
onPress: () => setMethod(value),
|
|
4418
|
+
className: `rounded-md border border-border px-4 py-3 ${method === value ? "bg-primary" : "bg-card"}`,
|
|
4419
|
+
children: /* @__PURE__ */ jsx93(Text37, { className: `text-center ${method === value ? "text-primary-foreground" : "text-foreground"}`, children: tenderLabel(value) })
|
|
4420
|
+
},
|
|
4421
|
+
value
|
|
4422
|
+
)) }),
|
|
4423
|
+
/* @__PURE__ */ jsx93(
|
|
4424
|
+
TextInput12,
|
|
4425
|
+
{
|
|
4426
|
+
testID: "split-tender-amount",
|
|
4427
|
+
accessibilityLabel: "Tender amount",
|
|
4428
|
+
inputMode: "decimal",
|
|
4429
|
+
value: amountText,
|
|
4430
|
+
onChangeText: setAmount,
|
|
4431
|
+
className: "rounded-md border border-border px-3 py-2 text-foreground"
|
|
4432
|
+
}
|
|
4433
|
+
),
|
|
4434
|
+
method === "external" ? /* @__PURE__ */ jsx93(
|
|
4435
|
+
TextInput12,
|
|
4436
|
+
{
|
|
4437
|
+
testID: "split-tender-reference",
|
|
4438
|
+
accessibilityLabel: "Terminal reference",
|
|
4439
|
+
placeholder: "Terminal reference",
|
|
4440
|
+
maxLength: 255,
|
|
4441
|
+
value: reference,
|
|
4442
|
+
onChangeText: setReference,
|
|
4443
|
+
className: "rounded-md border border-border px-3 py-2 text-foreground"
|
|
4444
|
+
}
|
|
4445
|
+
) : null,
|
|
4446
|
+
error ? /* @__PURE__ */ jsx93(Text37, { testID: "split-tender-error", accessibilityRole: "alert", className: "text-destructive", children: error }) : null,
|
|
4447
|
+
/* @__PURE__ */ jsx93(Pressable28, { testID: "split-tender-add-button", accessibilityRole: "button", onPress: add, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx93(Text37, { className: "text-center font-semibold text-primary-foreground", children: "Add" }) })
|
|
4448
|
+
] }),
|
|
4449
|
+
/* @__PURE__ */ jsx93(
|
|
4450
|
+
Pressable28,
|
|
4451
|
+
{
|
|
4452
|
+
testID: "split-tender-complete",
|
|
4453
|
+
accessibilityRole: "button",
|
|
4454
|
+
disabled: incomplete,
|
|
4455
|
+
onPress: sale.complete,
|
|
4456
|
+
className: `rounded-md bg-primary px-4 py-3 ${incomplete ? "opacity-50" : ""}`,
|
|
4457
|
+
children: /* @__PURE__ */ jsx93(Text37, { className: "text-center font-semibold text-primary-foreground", children: "Complete sale" })
|
|
4458
|
+
}
|
|
4459
|
+
),
|
|
4460
|
+
sale.error ? /* @__PURE__ */ jsx93(Text37, { accessibilityRole: "alert", className: "text-destructive", children: sale.error }) : null,
|
|
4461
|
+
sale.canContinue ? /* @__PURE__ */ jsxs52(Fragment7, { children: [
|
|
4462
|
+
/* @__PURE__ */ jsx93(Text37, { className: "text-foreground", children: "This sale is stored and will be sent. Continue to the next sale." }),
|
|
4463
|
+
/* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", onPress: () => sale.continueSale(), className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx93(Text37, { className: "text-center font-semibold text-primary-foreground", children: "Continue" }) })
|
|
4464
|
+
] }) : null,
|
|
4465
|
+
/* @__PURE__ */ jsx93(Pressable28, { accessibilityRole: "button", onPress: sale.cancelTender, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx93(Text37, { className: "text-center text-foreground", children: "Back" }) })
|
|
4466
|
+
] });
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4337
4469
|
// src/sale/catalogue.tsx
|
|
4338
|
-
import { useEffect as useEffect5, useMemo as useMemo2, useState as
|
|
4339
|
-
import { Pressable as
|
|
4470
|
+
import { useEffect as useEffect5, useMemo as useMemo2, useState as useState20 } from "react";
|
|
4471
|
+
import { Pressable as Pressable29, Text as Text38, View as View59 } from "react-native";
|
|
4340
4472
|
import { useStockOverlaid, useStockOverlayAsOf } from "@tallyui/core";
|
|
4341
4473
|
import { catalogueEntries, findEntryByCode, searchProducts, variantPriceLabel } from "@tallyui/pos";
|
|
4342
|
-
import { jsx as
|
|
4474
|
+
import { jsx as jsx94, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
4343
4475
|
var MIN_TILE_WIDTH = 160;
|
|
4344
4476
|
var STOCK_LABEL = {
|
|
4345
4477
|
in_stock: "In Stock",
|
|
@@ -4365,9 +4497,9 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4365
4497
|
const parsedOverlayAsOf = overlayAsOf ? new Date(overlayAsOf) : void 0;
|
|
4366
4498
|
const validOverlayAsOf = parsedOverlayAsOf && !isNaN(parsedOverlayAsOf.getTime()) ? parsedOverlayAsOf : void 0;
|
|
4367
4499
|
const stockAsOf = laterOf(laterOf(lastStockCheckAt, validOverlayAsOf), lastSyncedAt);
|
|
4368
|
-
const [query, setQuery] =
|
|
4369
|
-
const [chooserId, setChooserId] =
|
|
4370
|
-
const [width, setWidth] =
|
|
4500
|
+
const [query, setQuery] = useState20("");
|
|
4501
|
+
const [chooserId, setChooserId] = useState20(null);
|
|
4502
|
+
const [width, setWidth] = useState20(0);
|
|
4371
4503
|
const columns = Math.max(2, Math.min(6, Math.floor((width - 8) / (MIN_TILE_WIDTH + 8))));
|
|
4372
4504
|
const entries = useMemo2(() => catalogueEntries(shown, traits, { currency }), [shown, traits, currency]);
|
|
4373
4505
|
const results = useMemo2(() => searchProducts(shown, query, traits), [shown, query, traits]);
|
|
@@ -4379,9 +4511,9 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4379
4511
|
onSelect(entry);
|
|
4380
4512
|
setChooserId(null);
|
|
4381
4513
|
}
|
|
4382
|
-
return /* @__PURE__ */
|
|
4383
|
-
/* @__PURE__ */
|
|
4384
|
-
/* @__PURE__ */
|
|
4514
|
+
return /* @__PURE__ */ jsxs53(View59, { className: "flex-1", onLayout: (event) => setWidth(event.nativeEvent.layout.width), children: [
|
|
4515
|
+
/* @__PURE__ */ jsxs53(View59, { className: "gap-2 border-b border-border bg-card px-4 pb-3 pt-3", children: [
|
|
4516
|
+
/* @__PURE__ */ jsx94(
|
|
4385
4517
|
SearchInput,
|
|
4386
4518
|
{
|
|
4387
4519
|
value: query,
|
|
@@ -4398,25 +4530,25 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4398
4530
|
}
|
|
4399
4531
|
}
|
|
4400
4532
|
),
|
|
4401
|
-
statusText || statusAccessory ? /* @__PURE__ */
|
|
4402
|
-
statusText ? /* @__PURE__ */
|
|
4533
|
+
statusText || statusAccessory ? /* @__PURE__ */ jsxs53(View59, { testID: "catalogue-status-row", className: "flex-row items-center gap-2", children: [
|
|
4534
|
+
statusText ? /* @__PURE__ */ jsxs53(Text38, { className: "flex-1 text-xs text-muted-foreground", numberOfLines: statusAccessory ? 1 : void 0, children: [
|
|
4403
4535
|
statusText,
|
|
4404
4536
|
query.trim() ? ` \xB7 ${results.length.toLocaleString()} matching` : ""
|
|
4405
4537
|
] }) : null,
|
|
4406
4538
|
statusAccessory
|
|
4407
4539
|
] }) : null,
|
|
4408
|
-
choices.length > 0 ? /* @__PURE__ */
|
|
4409
|
-
choices.map((entry) => /* @__PURE__ */
|
|
4410
|
-
|
|
4540
|
+
choices.length > 0 ? /* @__PURE__ */ jsxs53(View59, { accessibilityLabel: "Choose variant", className: "gap-2", children: [
|
|
4541
|
+
choices.map((entry) => /* @__PURE__ */ jsxs53(
|
|
4542
|
+
Pressable29,
|
|
4411
4543
|
{
|
|
4412
4544
|
accessibilityRole: "button",
|
|
4413
4545
|
onPress: () => select(entry),
|
|
4414
4546
|
className: "gap-1 rounded-md border border-border p-3",
|
|
4415
4547
|
children: [
|
|
4416
|
-
/* @__PURE__ */
|
|
4417
|
-
/* @__PURE__ */
|
|
4418
|
-
/* @__PURE__ */
|
|
4419
|
-
/* @__PURE__ */
|
|
4548
|
+
/* @__PURE__ */ jsx94(Text38, { className: "font-semibold text-foreground", children: entry.variant.title }),
|
|
4549
|
+
/* @__PURE__ */ jsx94(Text38, { className: "text-muted-foreground", children: entry.variant.sku }),
|
|
4550
|
+
/* @__PURE__ */ jsx94(Text38, { className: "text-foreground", children: variantPriceLabel(entry.variant, currency) }),
|
|
4551
|
+
/* @__PURE__ */ jsxs53(Text38, { className: "text-muted-foreground", children: [
|
|
4420
4552
|
STOCK_LABEL[entry.variant.stock.status],
|
|
4421
4553
|
" \xB7 ",
|
|
4422
4554
|
stockAsOf ? `as of ${formatStockSyncTime(stockAsOf, void 0, hour12)}` : "not yet synced"
|
|
@@ -4425,10 +4557,10 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4425
4557
|
},
|
|
4426
4558
|
entry.variant.id
|
|
4427
4559
|
)),
|
|
4428
|
-
/* @__PURE__ */
|
|
4560
|
+
/* @__PURE__ */ jsx94(Pressable29, { accessibilityRole: "button", onPress: () => setChooserId(null), className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx94(Text38, { className: "text-center text-foreground", children: "Cancel" }) })
|
|
4429
4561
|
] }) : null
|
|
4430
4562
|
] }),
|
|
4431
|
-
/* @__PURE__ */
|
|
4563
|
+
/* @__PURE__ */ jsx94(
|
|
4432
4564
|
ProductGrid,
|
|
4433
4565
|
{
|
|
4434
4566
|
items: results,
|
|
@@ -4437,18 +4569,18 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4437
4569
|
// TallyUI's ProductCard has no children slot, so the tile is composed directly here
|
|
4438
4570
|
// (same structure/classes as ProductCard) to add the stock badge as a fourth child,
|
|
4439
4571
|
// inside the card and centred with the rest, rather than adding a slot to ProductCard (out of scope for TV6b).
|
|
4440
|
-
/* @__PURE__ */
|
|
4572
|
+
/* @__PURE__ */ jsx94(Pressable29, { accessibilityRole: "button", testID: `product-tile-${traits.getName(product)}`, onPress: () => {
|
|
4441
4573
|
const variants = entries.filter((entry) => entry.product === product);
|
|
4442
4574
|
if (variants.length === 1) select(variants[0]);
|
|
4443
4575
|
else setChooserId(traits.getId(product));
|
|
4444
|
-
}, children: /* @__PURE__ */
|
|
4445
|
-
/* @__PURE__ */
|
|
4446
|
-
/* @__PURE__ */
|
|
4447
|
-
/* @__PURE__ */
|
|
4448
|
-
/* @__PURE__ */
|
|
4576
|
+
}, children: /* @__PURE__ */ jsxs53(VStack, { space: "sm", className: "items-center rounded-lg border border-border bg-card p-3", children: [
|
|
4577
|
+
/* @__PURE__ */ jsx94(ProductImage, { doc: product, size: 80, className: "rounded-md" }),
|
|
4578
|
+
/* @__PURE__ */ jsx94(ProductTitle, { doc: product, className: "text-sm", numberOfLines: 2 }),
|
|
4579
|
+
/* @__PURE__ */ jsx94(ProductPrice, { doc: product }),
|
|
4580
|
+
/* @__PURE__ */ jsx94(ProductStockBadge, { doc: product, showAsOf: false, className: "self-center" })
|
|
4449
4581
|
] }) })
|
|
4450
4582
|
),
|
|
4451
|
-
emptyState: /* @__PURE__ */
|
|
4583
|
+
emptyState: /* @__PURE__ */ jsx94(Text38, { 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." })
|
|
4452
4584
|
}
|
|
4453
4585
|
)
|
|
4454
4586
|
] });
|
|
@@ -4456,8 +4588,8 @@ function Catalogue({ products, traits, currency, onSelect, statusText, statusAcc
|
|
|
4456
4588
|
|
|
4457
4589
|
// src/sale/receipt.tsx
|
|
4458
4590
|
import { useEffect as useEffect6 } from "react";
|
|
4459
|
-
import { Pressable as
|
|
4460
|
-
import { formatMoney as
|
|
4591
|
+
import { Pressable as Pressable30, Text as Text39, View as View60 } from "react-native";
|
|
4592
|
+
import { formatMoney as formatMoney15 } from "@tallyui/core";
|
|
4461
4593
|
import { buildReceiptData as buildReceiptData3 } from "@tallyui/pos";
|
|
4462
4594
|
|
|
4463
4595
|
// src/sale/print-style.ts
|
|
@@ -4477,7 +4609,7 @@ function orderReference(order) {
|
|
|
4477
4609
|
}
|
|
4478
4610
|
|
|
4479
4611
|
// src/sale/receipt.tsx
|
|
4480
|
-
import { Fragment as
|
|
4612
|
+
import { Fragment as Fragment8, jsx as jsx95, jsxs as jsxs54 } from "react/jsx-runtime";
|
|
4481
4613
|
var defaultFormatDate = (iso) => new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" }).format(new Date(iso));
|
|
4482
4614
|
function Receipt({
|
|
4483
4615
|
order,
|
|
@@ -4497,33 +4629,33 @@ function Receipt({
|
|
|
4497
4629
|
cashier,
|
|
4498
4630
|
register: registerId
|
|
4499
4631
|
});
|
|
4500
|
-
const money = (amount) =>
|
|
4501
|
-
const row = (label, amount, bold = false, key) => /* @__PURE__ */
|
|
4502
|
-
/* @__PURE__ */
|
|
4503
|
-
/* @__PURE__ */
|
|
4632
|
+
const money = (amount) => formatMoney15({ amount, currency: receipt.currency }) ?? "";
|
|
4633
|
+
const row = (label, amount, bold = false, key) => /* @__PURE__ */ jsxs54(View60, { accessibilityLabel: `${label}: ${amount}`, className: "flex-row justify-between gap-4", children: [
|
|
4634
|
+
/* @__PURE__ */ jsx95(Text39, { className: `text-foreground ${bold ? "font-semibold" : ""}`, children: label }),
|
|
4635
|
+
/* @__PURE__ */ jsx95(Text39, { className: `text-right text-foreground ${bold ? "font-semibold" : ""}`, children: amount })
|
|
4504
4636
|
] }, key);
|
|
4505
4637
|
const { totals } = receipt;
|
|
4506
4638
|
const lineDiscountMinor = totals.discountMinor - receipt.orderDiscountMinor;
|
|
4507
4639
|
const taxRows = totals.taxLines.map((line, index) => row(`${totals.taxInclusive ? "incl. " : ""}${taxLabel(line.ratePpm)}`, money(line.amountMinor), false, index));
|
|
4508
|
-
return /* @__PURE__ */
|
|
4509
|
-
topInset > 0 ? /* @__PURE__ */
|
|
4510
|
-
/* @__PURE__ */
|
|
4511
|
-
/* @__PURE__ */
|
|
4512
|
-
receipt.header.storeAddress ? /* @__PURE__ */
|
|
4513
|
-
/* @__PURE__ */
|
|
4514
|
-
/* @__PURE__ */
|
|
4515
|
-
/* @__PURE__ */
|
|
4640
|
+
return /* @__PURE__ */ jsxs54(Fragment8, { children: [
|
|
4641
|
+
topInset > 0 ? /* @__PURE__ */ jsx95(View60, { dataSet: { print: "hide" }, style: { height: topInset, flexShrink: 0 } }) : null,
|
|
4642
|
+
/* @__PURE__ */ jsxs54(View60, { className: "w-full max-w-md self-center gap-3 p-4 bg-card", children: [
|
|
4643
|
+
/* @__PURE__ */ jsx95(Text39, { className: "text-lg font-semibold text-foreground", children: receipt.header.storeName }),
|
|
4644
|
+
receipt.header.storeAddress ? /* @__PURE__ */ jsx95(Text39, { className: "text-muted-foreground", children: receipt.header.storeAddress }) : null,
|
|
4645
|
+
/* @__PURE__ */ jsx95(Text39, { testID: "receipt-order", className: "text-foreground", children: posOrder ? `Order ${orderReference(posOrder)}` : `Order ${receipt.header.orderNumber.slice(-8)} (draft)` }),
|
|
4646
|
+
/* @__PURE__ */ jsx95(Text39, { className: "text-muted-foreground", children: formatDate(posOrder?.createdAt ?? receipt.header.date) }),
|
|
4647
|
+
/* @__PURE__ */ jsxs54(Text39, { className: "text-muted-foreground", children: [
|
|
4516
4648
|
"Cashier: ",
|
|
4517
4649
|
receipt.header.cashier
|
|
4518
4650
|
] }),
|
|
4519
|
-
receipt.header.customer && /* @__PURE__ */
|
|
4651
|
+
receipt.header.customer && /* @__PURE__ */ jsxs54(Text39, { className: "text-muted-foreground", children: [
|
|
4520
4652
|
"Customer: ",
|
|
4521
4653
|
receipt.header.customer
|
|
4522
4654
|
] }),
|
|
4523
|
-
receipt.lineItems.map((line, index) => /* @__PURE__ */
|
|
4524
|
-
/* @__PURE__ */
|
|
4655
|
+
receipt.lineItems.map((line, index) => /* @__PURE__ */ jsxs54(View60, { children: [
|
|
4656
|
+
/* @__PURE__ */ jsx95(Text39, { className: "text-foreground", children: line.name }),
|
|
4525
4657
|
row(`${line.quantity} \xD7 ${money(line.unitPriceMinor)}`, money(line.displayAmountMinor)),
|
|
4526
|
-
line.displayDiscounts.map((discount, i) => /* @__PURE__ */
|
|
4658
|
+
line.displayDiscounts.map((discount, i) => /* @__PURE__ */ jsx95(View60, { className: "pl-4", children: row(`${discountLabel(
|
|
4527
4659
|
order.lineItems[index].discounts[i],
|
|
4528
4660
|
receipt.currency
|
|
4529
4661
|
)} off`, `\u2212${money(discount.amountMinor)}`) }, i))
|
|
@@ -4540,19 +4672,19 @@ function Receipt({
|
|
|
4540
4672
|
index
|
|
4541
4673
|
)),
|
|
4542
4674
|
row("Change", money(receipt.changeDueMinor)),
|
|
4543
|
-
/* @__PURE__ */
|
|
4544
|
-
/* @__PURE__ */
|
|
4675
|
+
/* @__PURE__ */ jsxs54(View60, { dataSet: { print: "hide" }, className: "flex-row gap-4", children: [
|
|
4676
|
+
/* @__PURE__ */ jsx95(Pressable30, { accessibilityRole: "button", onPress: () => {
|
|
4545
4677
|
if (typeof window !== "undefined" && typeof window.print === "function") window.print();
|
|
4546
|
-
}, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */
|
|
4547
|
-
/* @__PURE__ */
|
|
4678
|
+
}, className: "rounded-md border border-border bg-card px-4 py-3", children: /* @__PURE__ */ jsx95(Text39, { className: "text-center text-foreground", children: "Print receipt" }) }),
|
|
4679
|
+
/* @__PURE__ */ jsx95(Pressable30, { accessibilityRole: "button", onPress: newSale, className: "rounded-md bg-primary px-4 py-3", children: /* @__PURE__ */ jsx95(Text39, { className: "text-center font-semibold text-primary-foreground", children: "New sale" }) })
|
|
4548
4680
|
] })
|
|
4549
4681
|
] })
|
|
4550
4682
|
] });
|
|
4551
4683
|
}
|
|
4552
4684
|
|
|
4553
4685
|
// src/sale/sync-status.tsx
|
|
4554
|
-
import { useEffect as useEffect7, useRef as useRef6, useState as
|
|
4555
|
-
import { AccessibilityInfo as AccessibilityInfo2, Platform as Platform3, Text as
|
|
4686
|
+
import { useEffect as useEffect7, useRef as useRef6, useState as useState21 } from "react";
|
|
4687
|
+
import { AccessibilityInfo as AccessibilityInfo2, Platform as Platform3, Text as Text40, View as View61 } from "react-native";
|
|
4556
4688
|
|
|
4557
4689
|
// src/sale/since-text.ts
|
|
4558
4690
|
function sinceText(clock) {
|
|
@@ -4561,7 +4693,7 @@ function sinceText(clock) {
|
|
|
4561
4693
|
}
|
|
4562
4694
|
|
|
4563
4695
|
// src/sale/sync-status.tsx
|
|
4564
|
-
import { Fragment as
|
|
4696
|
+
import { Fragment as Fragment9, jsx as jsx96, jsxs as jsxs55 } from "react/jsx-runtime";
|
|
4565
4697
|
var PULL_NOTICE_TEXT = {
|
|
4566
4698
|
unauthorized: {
|
|
4567
4699
|
line: () => "Products aren't updating: this till needs to sign in to the online store again.",
|
|
@@ -4613,7 +4745,7 @@ function useAnnounceOnIos(...texts) {
|
|
|
4613
4745
|
function SyncStatus({ state, registerState, pluginName = "the POS plugin", pullNotice }) {
|
|
4614
4746
|
const updates = registerState?.pending ?? 0;
|
|
4615
4747
|
const outbox = state.pending === 0 && registerState?.pending ? registerState : state;
|
|
4616
|
-
const [now, setNow] =
|
|
4748
|
+
const [now, setNow] = useState21(Date.now);
|
|
4617
4749
|
useEffect7(() => {
|
|
4618
4750
|
setNow(Date.now());
|
|
4619
4751
|
if (!outbox.nextAttemptAt) return;
|
|
@@ -4637,24 +4769,24 @@ function SyncStatus({ state, registerState, pluginName = "the POS plugin", pullN
|
|
|
4637
4769
|
const notice = pullNotice && text && { line: text.line(pluginName), detail: text.detail(pullNotice, pluginName) };
|
|
4638
4770
|
useAnnounceOnIos(notice?.line, spoken);
|
|
4639
4771
|
const oneDetail = pullNotice?.code === "missing_plugin" && !!backendMissing;
|
|
4640
|
-
return /* @__PURE__ */
|
|
4641
|
-
notice ? /* @__PURE__ */
|
|
4642
|
-
/* @__PURE__ */
|
|
4643
|
-
oneDetail ? null : /* @__PURE__ */
|
|
4772
|
+
return /* @__PURE__ */ jsxs55(View61, { children: [
|
|
4773
|
+
notice ? /* @__PURE__ */ jsxs55(Fragment9, { children: [
|
|
4774
|
+
/* @__PURE__ */ jsx96(Text40, { accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: notice.line }),
|
|
4775
|
+
oneDetail ? null : /* @__PURE__ */ jsx96(Text40, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail })
|
|
4644
4776
|
] }) : null,
|
|
4645
|
-
/* @__PURE__ */
|
|
4646
|
-
doing ? /* @__PURE__ */
|
|
4647
|
-
refused ? /* @__PURE__ */
|
|
4648
|
-
oneDetail && notice ? /* @__PURE__ */
|
|
4777
|
+
/* @__PURE__ */ jsx96(Text40, { accessibilityLabel: spoken, accessibilityLiveRegion: "polite", className: "px-4 py-2 text-xs text-muted-foreground", children: spoken }),
|
|
4778
|
+
doing ? /* @__PURE__ */ jsx96(Text40, { className: "px-4 pb-2 text-xs text-muted-foreground", children: doing }) : null,
|
|
4779
|
+
refused ? /* @__PURE__ */ jsx96(Text40, { className: "px-4 pb-2 text-xs text-muted-foreground", children: REFUSED_DETAIL }) : null,
|
|
4780
|
+
oneDetail && notice ? /* @__PURE__ */ jsx96(Text40, { className: "px-4 pb-2 text-xs text-muted-foreground", children: notice.detail }) : backendMissing ? /* @__PURE__ */ jsx96(Text40, { 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
|
|
4649
4781
|
] });
|
|
4650
4782
|
}
|
|
4651
4783
|
|
|
4652
4784
|
// src/sale/orders-list.tsx
|
|
4653
|
-
import { useEffect as useEffect8, useRef as useRef7, useState as
|
|
4654
|
-
import { Pressable as
|
|
4655
|
-
import { formatMoney as
|
|
4785
|
+
import { useEffect as useEffect8, useRef as useRef7, useState as useState22 } from "react";
|
|
4786
|
+
import { Pressable as Pressable31, ScrollView as ScrollView13, Text as Text41, View as View62 } from "react-native";
|
|
4787
|
+
import { formatMoney as formatMoney16, knownWarnings } from "@tallyui/core";
|
|
4656
4788
|
import { needsAttention } from "@tallyui/pos";
|
|
4657
|
-
import { jsx as
|
|
4789
|
+
import { jsx as jsx97, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
4658
4790
|
var STATUS_LABEL = { pending: "Waiting to sync", applied: "Synced", rejected: "Not accepted" };
|
|
4659
4791
|
var FIGURE_LABELS = /* @__PURE__ */ new Map([["subtotalMinor", "Subtotal"], ["taxMinor", "Tax"], ["discountMinor", "Discount"]]);
|
|
4660
4792
|
var REFUSAL_SENTENCES = /* @__PURE__ */ new Map([
|
|
@@ -4678,26 +4810,26 @@ function defaultFormatDate2(iso) {
|
|
|
4678
4810
|
}
|
|
4679
4811
|
function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer, stuck }) {
|
|
4680
4812
|
const retrying = useRef7(/* @__PURE__ */ new Set());
|
|
4681
|
-
const [retryingIds, setRetryingIds] =
|
|
4682
|
-
const [expanded, setExpanded] =
|
|
4813
|
+
const [retryingIds, setRetryingIds] = useState22(/* @__PURE__ */ new Set());
|
|
4814
|
+
const [expanded, setExpanded] = useState22(/* @__PURE__ */ new Set());
|
|
4683
4815
|
useEffect8(() => {
|
|
4684
4816
|
for (const id of retrying.current) {
|
|
4685
4817
|
if (!orders.some((order) => order.id === id && order.syncStatus === "rejected")) retrying.current.delete(id);
|
|
4686
4818
|
}
|
|
4687
4819
|
setRetryingIds(new Set(retrying.current));
|
|
4688
4820
|
}, [orders]);
|
|
4689
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
4691
|
-
/* @__PURE__ */
|
|
4692
|
-
section.title === "Recent" && orders.length === 0 ? /* @__PURE__ */
|
|
4821
|
+
return /* @__PURE__ */ jsxs56(ScrollView13, { className: "flex-1 bg-background p-4", children: [
|
|
4822
|
+
[{ title: "Needs attention", orders: needsAttention(orders, { stuckCommandIds: stuck?.commandIds }) }, { title: "Recent", orders }].filter((section) => section.title === "Recent" || section.orders.length > 0).map((section) => /* @__PURE__ */ jsxs56(View62, { className: "mb-6 gap-3", children: [
|
|
4823
|
+
/* @__PURE__ */ jsx97(Text41, { accessibilityRole: "header", className: "text-xl font-semibold text-foreground", children: section.title }),
|
|
4824
|
+
section.title === "Recent" && orders.length === 0 ? /* @__PURE__ */ jsx97(Text41, { testID: "orders-empty", className: "text-muted-foreground", children: "No sales yet. Completed sales appear here." }) : null,
|
|
4693
4825
|
section.orders.map((order) => {
|
|
4694
4826
|
const count2 = order.lines.reduce((sum, line) => sum + line.quantity, 0);
|
|
4695
4827
|
const key = `${section.title}:${order.id}`;
|
|
4696
4828
|
const isExpanded = expanded.has(key);
|
|
4697
4829
|
const stuckEntry = order.syncStatus === "pending" ? stuck?.orders.find((entry) => entry.commandId === order.commandId) : void 0;
|
|
4698
|
-
return /* @__PURE__ */
|
|
4699
|
-
/* @__PURE__ */
|
|
4700
|
-
|
|
4830
|
+
return /* @__PURE__ */ jsxs56(View62, { className: "gap-1 rounded-md border border-border bg-card p-3", children: [
|
|
4831
|
+
/* @__PURE__ */ jsxs56(
|
|
4832
|
+
Pressable31,
|
|
4701
4833
|
{
|
|
4702
4834
|
accessibilityRole: "button",
|
|
4703
4835
|
"aria-expanded": isExpanded,
|
|
@@ -4711,26 +4843,26 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4711
4843
|
return next;
|
|
4712
4844
|
}),
|
|
4713
4845
|
children: [
|
|
4714
|
-
/* @__PURE__ */
|
|
4715
|
-
/* @__PURE__ */
|
|
4846
|
+
/* @__PURE__ */ jsx97(Text41, { className: "text-foreground", children: `Order ${orderReference(order)} \xB7 ${count2} ${count2 === 1 ? "item" : "items"}` }),
|
|
4847
|
+
/* @__PURE__ */ jsxs56(Text41, { className: "text-muted-foreground", children: [
|
|
4716
4848
|
formatDate(order.createdAt),
|
|
4717
4849
|
" \xB7 ",
|
|
4718
|
-
|
|
4850
|
+
formatMoney16({ amount: order.totalMinor, currency: order.currency }),
|
|
4719
4851
|
" \xB7 ",
|
|
4720
4852
|
STATUS_LABEL[order.syncStatus]
|
|
4721
4853
|
] })
|
|
4722
4854
|
]
|
|
4723
4855
|
}
|
|
4724
4856
|
),
|
|
4725
|
-
isExpanded ? /* @__PURE__ */
|
|
4726
|
-
order.lines.map((line) => /* @__PURE__ */
|
|
4727
|
-
order.payments.map((payment) => /* @__PURE__ */
|
|
4857
|
+
isExpanded ? /* @__PURE__ */ jsxs56(View62, { testID: `order-detail-${order.id}`, className: "mt-1 gap-1 border-t border-border pt-2", children: [
|
|
4858
|
+
order.lines.map((line) => /* @__PURE__ */ jsx97(Text41, { className: "text-foreground", children: `${line.quantity} \xD7 ${line.name}` }, line.id)),
|
|
4859
|
+
order.payments.map((payment) => /* @__PURE__ */ jsx97(Text41, { className: "text-muted-foreground", children: `${payment.method === "cash" ? "Cash" : "Card terminal"} ${formatMoney16({ amount: payment.amountMinor, currency: order.currency })}${payment.reference ? ` \xB7 ${payment.reference}` : ""}` }, payment.id))
|
|
4728
4860
|
] }) : null,
|
|
4729
|
-
stuckEntry ? /* @__PURE__ */
|
|
4730
|
-
order.syncStatus === "rejected" ? /* @__PURE__ */
|
|
4731
|
-
order.lateSessionId !== void 0 ? /* @__PURE__ */
|
|
4732
|
-
order.localWarnings?.map((warning, index) => /* @__PURE__ */
|
|
4733
|
-
section.title === "Needs attention" && order.syncStatus === "rejected" && order.error?.code !== "idempotency_mismatch" ? /* @__PURE__ */
|
|
4861
|
+
stuckEntry ? /* @__PURE__ */ jsx97(Text41, { className: "text-destructive", children: `Hasn't reached the online store since ${sinceText(stuckEntry)}.` }) : null,
|
|
4862
|
+
order.syncStatus === "rejected" ? /* @__PURE__ */ jsx97(Text41, { className: "text-destructive", children: refusalSentence(order.error?.code) }) : null,
|
|
4863
|
+
order.lateSessionId !== void 0 ? /* @__PURE__ */ jsx97(Text41, { className: "text-foreground", children: "Taken after the register closed. It is not in that register's closure." }) : null,
|
|
4864
|
+
order.localWarnings?.map((warning, index) => /* @__PURE__ */ jsx97(Text41, { 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)),
|
|
4865
|
+
section.title === "Needs attention" && order.syncStatus === "rejected" && order.error?.code !== "idempotency_mismatch" ? /* @__PURE__ */ jsx97(Pressable31, { accessibilityRole: "button", disabled: retryingIds.has(order.id), onPress: async () => {
|
|
4734
4866
|
if (retrying.current.has(order.id)) return;
|
|
4735
4867
|
retrying.current.add(order.id);
|
|
4736
4868
|
setRetryingIds(new Set(retrying.current));
|
|
@@ -4738,8 +4870,8 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4738
4870
|
retrying.current.delete(order.id);
|
|
4739
4871
|
setRetryingIds(new Set(retrying.current));
|
|
4740
4872
|
}
|
|
4741
|
-
}, className: "rounded-md bg-primary px-4 py-2", children: /* @__PURE__ */
|
|
4742
|
-
knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */
|
|
4873
|
+
}, className: "rounded-md bg-primary px-4 py-2", children: /* @__PURE__ */ jsx97(Text41, { className: "text-center font-semibold text-primary-foreground", children: "Retry" }) }) : null,
|
|
4874
|
+
knownWarnings(order.warnings).map((warning, index) => /* @__PURE__ */ jsx97(View62, { className: "border-l-4 border-warning pl-2", children: /* @__PURE__ */ jsx97(Text41, { 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 ${formatMoney16({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney16({ 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 ${formatMoney16({ amount: tillMinor, currency: order.currency })}, store ${formatMoney16({ amount: serverMinor, currency: order.currency })}.`).join(" ")}` : warning.bridgeMinor !== void 0 ? `Store calculated ${formatMoney16({ amount: warning.serverMinor, currency: order.currency })}; a rounding line of ${warning.bridgeMinor > 0 ? "+" : ""}${formatMoney16({ amount: warning.bridgeMinor, currency: order.currency })} brought it to ${formatMoney16({ amount: warning.expectedMinor, currency: order.currency })}` : `Store total ${formatMoney16({ amount: warning.serverMinor, currency: order.currency })} vs POS ${formatMoney16({ amount: warning.expectedMinor, currency: order.currency })}` }) }, index))
|
|
4743
4875
|
] }, order.id);
|
|
4744
4876
|
})
|
|
4745
4877
|
] }, section.title)),
|
|
@@ -4748,15 +4880,15 @@ function OrdersList({ orders, onRetry, formatDate = defaultFormatDate2, footer,
|
|
|
4748
4880
|
}
|
|
4749
4881
|
|
|
4750
4882
|
// src/sale/parked-sales.tsx
|
|
4751
|
-
import { useState as
|
|
4752
|
-
import { ScrollView as ScrollView14, useWindowDimensions as useWindowDimensions3, View as
|
|
4753
|
-
import { formatMoney as
|
|
4754
|
-
import { Fragment as
|
|
4883
|
+
import { useState as useState23 } from "react";
|
|
4884
|
+
import { ScrollView as ScrollView14, useWindowDimensions as useWindowDimensions3, View as View63 } from "react-native";
|
|
4885
|
+
import { formatMoney as formatMoney17 } from "@tallyui/core";
|
|
4886
|
+
import { Fragment as Fragment10, jsx as jsx98, jsxs as jsxs57 } from "react/jsx-runtime";
|
|
4755
4887
|
function ParkedSales({ sale, parked, onDiscard, onPark, onResume, currency, open, onOpenChange, hour12 }) {
|
|
4756
4888
|
const { height: windowHeight } = useWindowDimensions3();
|
|
4757
4889
|
const maxHeight = Math.max(280, windowHeight - 64);
|
|
4758
|
-
const [confirming, setConfirming] =
|
|
4759
|
-
const [error, setError] =
|
|
4890
|
+
const [confirming, setConfirming] = useState23(null);
|
|
4891
|
+
const [error, setError] = useState23("");
|
|
4760
4892
|
const attempt = async (action) => {
|
|
4761
4893
|
setError("");
|
|
4762
4894
|
try {
|
|
@@ -4765,28 +4897,28 @@ function ParkedSales({ sale, parked, onDiscard, onPark, onResume, currency, open
|
|
|
4765
4897
|
setError(String(e));
|
|
4766
4898
|
}
|
|
4767
4899
|
};
|
|
4768
|
-
return /* @__PURE__ */
|
|
4769
|
-
/* @__PURE__ */
|
|
4770
|
-
/* @__PURE__ */
|
|
4771
|
-
/* @__PURE__ */
|
|
4900
|
+
return /* @__PURE__ */ jsx98(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs57(DialogContent, { testID: "parked-sales", className: "flex-col overflow-hidden", style: { maxHeight }, children: [
|
|
4901
|
+
/* @__PURE__ */ jsxs57(View63, { className: "flex-row items-center justify-between gap-2", children: [
|
|
4902
|
+
/* @__PURE__ */ jsx98(DialogTitle, { children: "Parked sales" }),
|
|
4903
|
+
/* @__PURE__ */ jsx98(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx98(IconButton, { variant: "ghost", size: "sm", testID: "parked-sales-dismiss", accessibilityLabel: "Close panel", children: /* @__PURE__ */ jsx98(XIcon, { size: 16 }) }) })
|
|
4772
4904
|
] }),
|
|
4773
|
-
/* @__PURE__ */
|
|
4774
|
-
/* @__PURE__ */
|
|
4905
|
+
/* @__PURE__ */ jsxs57(ScrollView14, { className: "flex-1", contentContainerClassName: "gap-3", children: [
|
|
4906
|
+
/* @__PURE__ */ jsx98(
|
|
4775
4907
|
Button,
|
|
4776
4908
|
{
|
|
4777
4909
|
testID: "parked-sales-park",
|
|
4778
4910
|
accessibilityLabel: "Park this sale",
|
|
4779
4911
|
disabled: sale.order.lineItems.length === 0,
|
|
4780
4912
|
onPress: () => attempt(async () => setError(await (onPark ?? sale.park)() ?? "")),
|
|
4781
|
-
children: /* @__PURE__ */
|
|
4913
|
+
children: /* @__PURE__ */ jsx98(Text, { children: "Park this sale" })
|
|
4782
4914
|
}
|
|
4783
4915
|
),
|
|
4784
|
-
!!error && /* @__PURE__ */
|
|
4785
|
-
parked.length === 0 && /* @__PURE__ */
|
|
4786
|
-
parked.map((row) => /* @__PURE__ */
|
|
4787
|
-
/* @__PURE__ */
|
|
4788
|
-
/* @__PURE__ */
|
|
4789
|
-
/* @__PURE__ */
|
|
4916
|
+
!!error && /* @__PURE__ */ jsx98(Text, { testID: "parked-sales-error", className: "text-destructive", children: error }),
|
|
4917
|
+
parked.length === 0 && /* @__PURE__ */ jsx98(Text, { testID: "parked-sales-empty", children: "No parked sales." }),
|
|
4918
|
+
parked.map((row) => /* @__PURE__ */ jsx98(View63, { testID: `parked-row-${row.id}`, className: "gap-2 rounded-md border border-border p-3", children: confirming === row.id ? /* @__PURE__ */ jsxs57(Fragment10, { children: [
|
|
4919
|
+
/* @__PURE__ */ jsx98(Text, { children: "Discard this parked sale?" }),
|
|
4920
|
+
/* @__PURE__ */ jsxs57(View63, { className: "flex-row gap-2", children: [
|
|
4921
|
+
/* @__PURE__ */ jsx98(
|
|
4790
4922
|
Button,
|
|
4791
4923
|
{
|
|
4792
4924
|
testID: `parked-discard-confirm-${row.id}`,
|
|
@@ -4795,22 +4927,22 @@ function ParkedSales({ sale, parked, onDiscard, onPark, onResume, currency, open
|
|
|
4795
4927
|
await onDiscard(row.id);
|
|
4796
4928
|
setConfirming(null);
|
|
4797
4929
|
}),
|
|
4798
|
-
children: /* @__PURE__ */
|
|
4930
|
+
children: /* @__PURE__ */ jsx98(Text, { children: "Discard" })
|
|
4799
4931
|
}
|
|
4800
4932
|
),
|
|
4801
|
-
/* @__PURE__ */
|
|
4933
|
+
/* @__PURE__ */ jsx98(Button, { testID: `parked-discard-cancel-${row.id}`, variant: "outline", onPress: () => setConfirming(null), children: /* @__PURE__ */ jsx98(Text, { children: "Cancel" }) })
|
|
4802
4934
|
] })
|
|
4803
|
-
] }) : /* @__PURE__ */
|
|
4804
|
-
/* @__PURE__ */
|
|
4805
|
-
/* @__PURE__ */
|
|
4935
|
+
] }) : /* @__PURE__ */ jsxs57(Fragment10, { children: [
|
|
4936
|
+
/* @__PURE__ */ jsx98(Text, { children: formatStockSyncTime(new Date(row.parkedAt), void 0, hour12) }),
|
|
4937
|
+
/* @__PURE__ */ jsxs57(Text, { children: [
|
|
4806
4938
|
row.itemCount,
|
|
4807
4939
|
" ",
|
|
4808
4940
|
row.itemCount === 1 ? "item" : "items"
|
|
4809
4941
|
] }),
|
|
4810
|
-
/* @__PURE__ */
|
|
4811
|
-
!!row.customerName && /* @__PURE__ */
|
|
4812
|
-
/* @__PURE__ */
|
|
4813
|
-
/* @__PURE__ */
|
|
4942
|
+
/* @__PURE__ */ jsx98(Text, { children: formatMoney17({ amount: row.totalMinor, currency }) }),
|
|
4943
|
+
!!row.customerName && /* @__PURE__ */ jsx98(Text, { children: row.customerName }),
|
|
4944
|
+
/* @__PURE__ */ jsxs57(View63, { className: "flex-row gap-2", children: [
|
|
4945
|
+
/* @__PURE__ */ jsx98(
|
|
4814
4946
|
Button,
|
|
4815
4947
|
{
|
|
4816
4948
|
testID: `parked-resume-${row.id}`,
|
|
@@ -4820,17 +4952,17 @@ function ParkedSales({ sale, parked, onDiscard, onPark, onResume, currency, open
|
|
|
4820
4952
|
if (refusal === null) onOpenChange(false);
|
|
4821
4953
|
else setError(refusal);
|
|
4822
4954
|
}),
|
|
4823
|
-
children: /* @__PURE__ */
|
|
4955
|
+
children: /* @__PURE__ */ jsx98(Text, { children: "Resume" })
|
|
4824
4956
|
}
|
|
4825
4957
|
),
|
|
4826
|
-
/* @__PURE__ */
|
|
4958
|
+
/* @__PURE__ */ jsx98(
|
|
4827
4959
|
Button,
|
|
4828
4960
|
{
|
|
4829
4961
|
testID: `parked-discard-${row.id}`,
|
|
4830
4962
|
accessibilityLabel: "Discard parked sale",
|
|
4831
4963
|
variant: "outline",
|
|
4832
4964
|
onPress: () => setConfirming(row.id),
|
|
4833
|
-
children: /* @__PURE__ */
|
|
4965
|
+
children: /* @__PURE__ */ jsx98(Text, { children: "Discard" })
|
|
4834
4966
|
}
|
|
4835
4967
|
)
|
|
4836
4968
|
] })
|
|
@@ -4840,10 +4972,10 @@ function ParkedSales({ sale, parked, onDiscard, onPark, onResume, currency, open
|
|
|
4840
4972
|
}
|
|
4841
4973
|
|
|
4842
4974
|
// src/layout/pos-layout.tsx
|
|
4843
|
-
import { useState as
|
|
4844
|
-
import { Pressable as
|
|
4975
|
+
import { useState as useState24 } from "react";
|
|
4976
|
+
import { Pressable as Pressable32, Text as Text42, View as View64, useWindowDimensions as useWindowDimensions4 } from "react-native";
|
|
4845
4977
|
import { cn as cn80 } from "@tallyui/theme";
|
|
4846
|
-
import { Fragment as
|
|
4978
|
+
import { Fragment as Fragment11, jsx as jsx99, jsxs as jsxs58 } from "react/jsx-runtime";
|
|
4847
4979
|
function POSLayout({
|
|
4848
4980
|
browseSlot,
|
|
4849
4981
|
cartSlot,
|
|
@@ -4855,40 +4987,40 @@ function POSLayout({
|
|
|
4855
4987
|
}) {
|
|
4856
4988
|
const { width } = useWindowDimensions4();
|
|
4857
4989
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4858
|
-
const [activeTab, setActiveTab] =
|
|
4859
|
-
return /* @__PURE__ */
|
|
4860
|
-
headerSlot && /* @__PURE__ */
|
|
4861
|
-
layout === "split" ? /* @__PURE__ */
|
|
4862
|
-
/* @__PURE__ */
|
|
4863
|
-
/* @__PURE__ */
|
|
4864
|
-
] }) : /* @__PURE__ */
|
|
4865
|
-
/* @__PURE__ */
|
|
4866
|
-
/* @__PURE__ */
|
|
4867
|
-
|
|
4990
|
+
const [activeTab, setActiveTab] = useState24("browse");
|
|
4991
|
+
return /* @__PURE__ */ jsxs58(View64, { className: cn80("flex-1 bg-bg", className), ...viewProps, children: [
|
|
4992
|
+
headerSlot && /* @__PURE__ */ jsx99(View64, { className: "border-b border-border", children: headerSlot }),
|
|
4993
|
+
layout === "split" ? /* @__PURE__ */ jsxs58(View64, { className: "flex-1 flex-row", children: [
|
|
4994
|
+
/* @__PURE__ */ jsx99(View64, { className: "flex-[3]", children: browseSlot }),
|
|
4995
|
+
/* @__PURE__ */ jsx99(View64, { className: "flex-[2] border-l border-border", children: cartSlot })
|
|
4996
|
+
] }) : /* @__PURE__ */ jsxs58(Fragment11, { children: [
|
|
4997
|
+
/* @__PURE__ */ jsxs58(View64, { className: "flex-row border-b border-border", children: [
|
|
4998
|
+
/* @__PURE__ */ jsx99(
|
|
4999
|
+
Pressable32,
|
|
4868
5000
|
{
|
|
4869
5001
|
onPress: () => setActiveTab("browse"),
|
|
4870
5002
|
className: cn80("flex-1 py-3", activeTab === "browse" && "border-b-2 border-primary"),
|
|
4871
|
-
children: /* @__PURE__ */
|
|
5003
|
+
children: /* @__PURE__ */ jsx99(Text42, { className: cn80("text-center text-sm font-semibold", activeTab === "browse" ? "text-primary" : "text-muted-foreground"), children: "Browse" })
|
|
4872
5004
|
}
|
|
4873
5005
|
),
|
|
4874
|
-
/* @__PURE__ */
|
|
4875
|
-
|
|
5006
|
+
/* @__PURE__ */ jsx99(
|
|
5007
|
+
Pressable32,
|
|
4876
5008
|
{
|
|
4877
5009
|
onPress: () => setActiveTab("cart"),
|
|
4878
5010
|
className: cn80("flex-1 py-3", activeTab === "cart" && "border-b-2 border-primary"),
|
|
4879
|
-
children: /* @__PURE__ */
|
|
5011
|
+
children: /* @__PURE__ */ jsx99(Text42, { className: cn80("text-center text-sm font-semibold", activeTab === "cart" ? "text-primary" : "text-muted-foreground"), children: "Cart" })
|
|
4880
5012
|
}
|
|
4881
5013
|
)
|
|
4882
5014
|
] }),
|
|
4883
|
-
/* @__PURE__ */
|
|
5015
|
+
/* @__PURE__ */ jsx99(View64, { className: "flex-1", children: activeTab === "browse" ? browseSlot : cartSlot })
|
|
4884
5016
|
] })
|
|
4885
5017
|
] });
|
|
4886
5018
|
}
|
|
4887
5019
|
|
|
4888
5020
|
// src/layout/checkout-layout.tsx
|
|
4889
|
-
import { ScrollView as ScrollView15, View as
|
|
5021
|
+
import { ScrollView as ScrollView15, View as View65, useWindowDimensions as useWindowDimensions5 } from "react-native";
|
|
4890
5022
|
import { cn as cn81 } from "@tallyui/theme";
|
|
4891
|
-
import { jsx as
|
|
5023
|
+
import { jsx as jsx100, jsxs as jsxs59 } from "react/jsx-runtime";
|
|
4892
5024
|
function CheckoutLayout({
|
|
4893
5025
|
summarySlot,
|
|
4894
5026
|
paymentSlot,
|
|
@@ -4900,12 +5032,12 @@ function CheckoutLayout({
|
|
|
4900
5032
|
}) {
|
|
4901
5033
|
const { width } = useWindowDimensions5();
|
|
4902
5034
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4903
|
-
return /* @__PURE__ */
|
|
4904
|
-
headerSlot && /* @__PURE__ */
|
|
4905
|
-
layout === "split" ? /* @__PURE__ */
|
|
4906
|
-
/* @__PURE__ */
|
|
4907
|
-
/* @__PURE__ */
|
|
4908
|
-
] }) : /* @__PURE__ */
|
|
5035
|
+
return /* @__PURE__ */ jsxs59(View65, { className: cn81("flex-1 bg-bg", className), ...viewProps, children: [
|
|
5036
|
+
headerSlot && /* @__PURE__ */ jsx100(View65, { className: "border-b border-border", children: headerSlot }),
|
|
5037
|
+
layout === "split" ? /* @__PURE__ */ jsxs59(View65, { className: "flex-1 flex-row", children: [
|
|
5038
|
+
/* @__PURE__ */ jsx100(View65, { className: "flex-1 border-r border-border", children: summarySlot }),
|
|
5039
|
+
/* @__PURE__ */ jsx100(View65, { className: "flex-1", children: paymentSlot })
|
|
5040
|
+
] }) : /* @__PURE__ */ jsx100(ScrollView15, { className: "flex-1", children: /* @__PURE__ */ jsxs59(View65, { className: "gap-4 p-4", children: [
|
|
4909
5041
|
summarySlot,
|
|
4910
5042
|
paymentSlot
|
|
4911
5043
|
] }) })
|
|
@@ -4913,10 +5045,10 @@ function CheckoutLayout({
|
|
|
4913
5045
|
}
|
|
4914
5046
|
|
|
4915
5047
|
// src/layout/settings-layout.tsx
|
|
4916
|
-
import { useState as
|
|
4917
|
-
import { Pressable as
|
|
5048
|
+
import { useState as useState25 } from "react";
|
|
5049
|
+
import { Pressable as Pressable33, Text as Text43, View as View66, useWindowDimensions as useWindowDimensions6 } from "react-native";
|
|
4918
5050
|
import { cn as cn82 } from "@tallyui/theme";
|
|
4919
|
-
import { jsx as
|
|
5051
|
+
import { jsx as jsx101, jsxs as jsxs60 } from "react/jsx-runtime";
|
|
4920
5052
|
function SettingsLayout({
|
|
4921
5053
|
navSlot,
|
|
4922
5054
|
contentSlot,
|
|
@@ -4928,26 +5060,26 @@ function SettingsLayout({
|
|
|
4928
5060
|
}) {
|
|
4929
5061
|
const { width } = useWindowDimensions6();
|
|
4930
5062
|
const layout = layoutProp ?? (width >= breakpoint ? "split" : "stacked");
|
|
4931
|
-
const [showNav, setShowNav] =
|
|
4932
|
-
return /* @__PURE__ */
|
|
4933
|
-
headerSlot && /* @__PURE__ */
|
|
4934
|
-
layout === "split" ? /* @__PURE__ */
|
|
4935
|
-
/* @__PURE__ */
|
|
4936
|
-
/* @__PURE__ */
|
|
4937
|
-
] }) : /* @__PURE__ */
|
|
5063
|
+
const [showNav, setShowNav] = useState25(false);
|
|
5064
|
+
return /* @__PURE__ */ jsxs60(View66, { className: cn82("flex-1 bg-bg", className), ...viewProps, children: [
|
|
5065
|
+
headerSlot && /* @__PURE__ */ jsx101(View66, { className: "border-b border-border", children: headerSlot }),
|
|
5066
|
+
layout === "split" ? /* @__PURE__ */ jsxs60(View66, { className: "flex-1 flex-row", children: [
|
|
5067
|
+
/* @__PURE__ */ jsx101(View66, { className: "w-64 border-r border-border", children: navSlot }),
|
|
5068
|
+
/* @__PURE__ */ jsx101(View66, { className: "flex-1", children: contentSlot })
|
|
5069
|
+
] }) : /* @__PURE__ */ jsx101(View66, { className: "flex-1", children: showNav ? /* @__PURE__ */ jsxs60(View66, { className: "flex-1", children: [
|
|
4938
5070
|
navSlot,
|
|
4939
|
-
/* @__PURE__ */
|
|
4940
|
-
] }) : /* @__PURE__ */
|
|
4941
|
-
/* @__PURE__ */
|
|
5071
|
+
/* @__PURE__ */ jsx101(Pressable33, { onPress: () => setShowNav(false), className: "border-t border-border p-3", children: /* @__PURE__ */ jsx101(Text43, { className: "text-center text-sm font-medium text-primary", children: "Done" }) })
|
|
5072
|
+
] }) : /* @__PURE__ */ jsxs60(View66, { className: "flex-1", children: [
|
|
5073
|
+
/* @__PURE__ */ jsx101(Pressable33, { onPress: () => setShowNav(true), className: "border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx101(Text43, { className: "text-sm font-medium text-primary", children: "Menu" }) }),
|
|
4942
5074
|
contentSlot
|
|
4943
5075
|
] }) })
|
|
4944
5076
|
] });
|
|
4945
5077
|
}
|
|
4946
5078
|
|
|
4947
5079
|
// src/layout/live-tab-screen.tsx
|
|
4948
|
-
import { Pressable as
|
|
5080
|
+
import { Pressable as Pressable34, Text as Text44, View as View67 } from "react-native";
|
|
4949
5081
|
import { cn as cn83 } from "@tallyui/theme";
|
|
4950
|
-
import { jsx as
|
|
5082
|
+
import { jsx as jsx102, jsxs as jsxs61 } from "react/jsx-runtime";
|
|
4951
5083
|
function LiveTabScreen({
|
|
4952
5084
|
state,
|
|
4953
5085
|
onUseHere,
|
|
@@ -4964,21 +5096,21 @@ function LiveTabScreen({
|
|
|
4964
5096
|
const body = state === "parked" ? parkedBody : blockedBody;
|
|
4965
5097
|
const buttonLabel = state === "parked" ? useHereLabel : reloadLabel;
|
|
4966
5098
|
const onPress = state === "parked" ? onUseHere : onReload;
|
|
4967
|
-
return /* @__PURE__ */
|
|
4968
|
-
/* @__PURE__ */
|
|
4969
|
-
/* @__PURE__ */
|
|
4970
|
-
/* @__PURE__ */
|
|
5099
|
+
return /* @__PURE__ */ jsxs61(View67, { className: cn83("flex-1 items-center justify-center gap-4 bg-bg p-6", className), ...viewProps, children: [
|
|
5100
|
+
/* @__PURE__ */ jsx102(Text44, { className: "text-center text-lg font-bold text-foreground", children: parkedTitle }),
|
|
5101
|
+
/* @__PURE__ */ jsx102(Text44, { className: "text-center text-sm text-muted-foreground", children: body }),
|
|
5102
|
+
/* @__PURE__ */ jsx102(Pressable34, { onPress, className: "items-center rounded-lg bg-primary px-4 py-3", children: /* @__PURE__ */ jsx102(Text44, { className: "text-sm font-semibold text-primary-foreground", children: buttonLabel }) })
|
|
4971
5103
|
] });
|
|
4972
5104
|
}
|
|
4973
5105
|
|
|
4974
5106
|
// src/layout/store-settings-choice-screen.tsx
|
|
4975
|
-
import { useState as
|
|
4976
|
-
import { Pressable as
|
|
5107
|
+
import { useState as useState26 } from "react";
|
|
5108
|
+
import { Pressable as Pressable35, Text as Text45, View as View68 } from "react-native";
|
|
4977
5109
|
import { cn as cn84 } from "@tallyui/theme";
|
|
4978
|
-
import { jsx as
|
|
5110
|
+
import { jsx as jsx103, jsxs as jsxs62 } from "react/jsx-runtime";
|
|
4979
5111
|
function useSectionSelection(options, initialValue) {
|
|
4980
5112
|
const preset = options.length === 1 ? options[0].value : options.find((o) => o.value === initialValue)?.value;
|
|
4981
|
-
return
|
|
5113
|
+
return useState26(preset);
|
|
4982
5114
|
}
|
|
4983
5115
|
function defaultCountryName(code, locale) {
|
|
4984
5116
|
try {
|
|
@@ -5024,32 +5156,32 @@ function StoreSettingsChoiceScreen({
|
|
|
5024
5156
|
else if (initial?.channel !== void 0) choice.channel = initial.channel;
|
|
5025
5157
|
onSubmit(choice);
|
|
5026
5158
|
};
|
|
5027
|
-
return /* @__PURE__ */
|
|
5028
|
-
/* @__PURE__ */
|
|
5029
|
-
/* @__PURE__ */
|
|
5030
|
-
/* @__PURE__ */
|
|
5159
|
+
return /* @__PURE__ */ jsxs62(View68, { className: cn84("w-full max-w-[560px] flex-1 self-center gap-6 bg-bg p-6", className), ...viewProps, children: [
|
|
5160
|
+
/* @__PURE__ */ jsxs62(View68, { className: "gap-2", children: [
|
|
5161
|
+
/* @__PURE__ */ jsx103(Text45, { className: "text-lg font-bold text-foreground", children: title }),
|
|
5162
|
+
/* @__PURE__ */ jsx103(Text45, { className: "text-sm text-muted-foreground", children: body })
|
|
5031
5163
|
] }),
|
|
5032
|
-
showRegions && /* @__PURE__ */
|
|
5033
|
-
showCountries && /* @__PURE__ */
|
|
5034
|
-
showChannels && /* @__PURE__ */
|
|
5035
|
-
/* @__PURE__ */
|
|
5036
|
-
|
|
5164
|
+
showRegions && /* @__PURE__ */ jsx103(ChoiceSection, { label: regionLabel, options: regionOptions, selected: region, onSelect: setRegion }),
|
|
5165
|
+
showCountries && /* @__PURE__ */ jsx103(ChoiceSection, { label: countryLabel, options: countryOptions, selected: country, onSelect: setCountry }),
|
|
5166
|
+
showChannels && /* @__PURE__ */ jsx103(ChoiceSection, { label: channelLabel, options: channelOptions, selected: channel, onSelect: setChannel }),
|
|
5167
|
+
/* @__PURE__ */ jsx103(
|
|
5168
|
+
Pressable35,
|
|
5037
5169
|
{
|
|
5038
5170
|
onPress: handleSubmit,
|
|
5039
5171
|
disabled: !complete,
|
|
5040
5172
|
className: cn84("items-center rounded-lg bg-primary px-4 py-3", !complete && "opacity-50"),
|
|
5041
|
-
children: /* @__PURE__ */
|
|
5173
|
+
children: /* @__PURE__ */ jsx103(Text45, { className: "text-sm font-semibold text-primary-foreground", children: submitLabel })
|
|
5042
5174
|
}
|
|
5043
5175
|
)
|
|
5044
5176
|
] });
|
|
5045
5177
|
}
|
|
5046
5178
|
function ChoiceSection({ label, options, selected, onSelect }) {
|
|
5047
|
-
return /* @__PURE__ */
|
|
5048
|
-
/* @__PURE__ */
|
|
5049
|
-
/* @__PURE__ */
|
|
5179
|
+
return /* @__PURE__ */ jsxs62(View68, { className: "gap-1.5", children: [
|
|
5180
|
+
/* @__PURE__ */ jsx103(Text45, { className: "text-xs font-semibold text-muted-foreground", children: label }),
|
|
5181
|
+
/* @__PURE__ */ jsx103(View68, { className: "rounded-lg border border-border", children: options.map((option, index) => {
|
|
5050
5182
|
const checked = option.value === selected;
|
|
5051
|
-
return /* @__PURE__ */
|
|
5052
|
-
|
|
5183
|
+
return /* @__PURE__ */ jsxs62(
|
|
5184
|
+
Pressable35,
|
|
5053
5185
|
{
|
|
5054
5186
|
onPress: () => onSelect(option.value),
|
|
5055
5187
|
accessibilityRole: "radio",
|
|
@@ -5057,8 +5189,8 @@ function ChoiceSection({ label, options, selected, onSelect }) {
|
|
|
5057
5189
|
testID: `choice-option-${option.value}`,
|
|
5058
5190
|
className: cn84("flex-row items-center gap-3 px-4 py-3", index > 0 && "border-t border-border", checked && "bg-primary/10"),
|
|
5059
5191
|
children: [
|
|
5060
|
-
/* @__PURE__ */
|
|
5061
|
-
/* @__PURE__ */
|
|
5192
|
+
/* @__PURE__ */ jsx103(View68, { className: "h-4 w-4 items-center justify-center rounded-full border border-primary", children: checked && /* @__PURE__ */ jsx103(View68, { testID: `choice-option-${option.value}-dot`, className: "h-2.5 w-2.5 rounded-full bg-primary" }) }),
|
|
5193
|
+
/* @__PURE__ */ jsx103(Text45, { className: cn84("text-sm", checked ? "font-semibold text-primary" : "text-foreground"), children: option.label })
|
|
5062
5194
|
]
|
|
5063
5195
|
},
|
|
5064
5196
|
option.value
|
|
@@ -5232,6 +5364,7 @@ export {
|
|
|
5232
5364
|
SettingsLayout,
|
|
5233
5365
|
SettingsRow,
|
|
5234
5366
|
Slider,
|
|
5367
|
+
SplitTender,
|
|
5235
5368
|
StoreSettingsChoiceScreen,
|
|
5236
5369
|
Switch,
|
|
5237
5370
|
SyncStatus,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tallyui/components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Universal POS UI components for Tally UI",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,22 +27,22 @@
|
|
|
27
27
|
"directory": "packages/components"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@tallyui/core": "^3.
|
|
31
|
-
"@tallyui/primitives": "^3.
|
|
32
|
-
"@tallyui/theme": "^3.
|
|
30
|
+
"@tallyui/core": "^3.3.0",
|
|
31
|
+
"@tallyui/primitives": "^3.3.0",
|
|
32
|
+
"@tallyui/theme": "^3.3.0",
|
|
33
33
|
"react": ">=18",
|
|
34
34
|
"react-native": ">=0.74",
|
|
35
35
|
"react-native-svg": ">=15"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"class-variance-authority": "^0.7.1",
|
|
39
|
-
"@tallyui/pos": "3.
|
|
39
|
+
"@tallyui/pos": "3.3.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@tallyui/connector-medusa": "3.
|
|
43
|
-
"@tallyui/connector-vendure": "3.
|
|
44
|
-
"@tallyui/connector-woocommerce": "3.
|
|
45
|
-
"@tallyui/mock-api": "3.
|
|
42
|
+
"@tallyui/connector-medusa": "3.3.0",
|
|
43
|
+
"@tallyui/connector-vendure": "3.3.0",
|
|
44
|
+
"@tallyui/connector-woocommerce": "3.3.0",
|
|
45
|
+
"@tallyui/mock-api": "3.3.0",
|
|
46
46
|
"@types/react": ">=18",
|
|
47
47
|
"react": "^19.2.4",
|
|
48
48
|
"react-native-svg": "15.12.1",
|
package/src/index.ts
CHANGED
|
@@ -154,7 +154,7 @@ export {
|
|
|
154
154
|
|
|
155
155
|
// Sale components
|
|
156
156
|
export {
|
|
157
|
-
Cart, CartBar, Tender, DiscountForm, DiscountChips, parseDiscount, discountLabel,
|
|
157
|
+
Cart, CartBar, Tender, SplitTender, DiscountForm, DiscountChips, parseDiscount, discountLabel,
|
|
158
158
|
PriceForm, parsePrice,
|
|
159
159
|
Catalogue, formatStockSyncTime, Receipt, injectPrintStyle, SyncStatus, OrdersList, orderReference,
|
|
160
160
|
ParkedSales, type ParkedSalesProps,
|
package/src/sale/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Cart } from './cart';
|
|
2
2
|
export { CartBar } from './cart-bar';
|
|
3
3
|
export { Tender } from './tender';
|
|
4
|
+
export { SplitTender } from './split-tender';
|
|
4
5
|
export { DiscountForm, DiscountChips, parseDiscount, discountLabel } from './discount-form';
|
|
5
6
|
export { PriceForm, parsePrice } from './price-form';
|
|
6
7
|
export { Catalogue, formatStockSyncTime } from './catalogue';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useState, type JSX } from 'react';
|
|
2
|
+
import { Pressable, Text, TextInput, View } from 'react-native';
|
|
3
|
+
import { formatMoney, minorUnitDigits } from '@tallyui/core';
|
|
4
|
+
import type { useSale } from '@tallyui/pos';
|
|
5
|
+
import { tenderLabel } from '../register/register-count';
|
|
6
|
+
import { parsePrice } from './price-form';
|
|
7
|
+
|
|
8
|
+
export function SplitTender({ sale }: { sale: ReturnType<typeof useSale> }): JSX.Element | null {
|
|
9
|
+
const { order, stage } = sale;
|
|
10
|
+
const [method, setMethod] = useState<'cash' | 'external'>('cash');
|
|
11
|
+
const [amount, setAmount] = useState<string | null>(null);
|
|
12
|
+
const [reference, setReference] = useState('');
|
|
13
|
+
const [error, setError] = useState<string | null>(null);
|
|
14
|
+
if (stage.kind !== 'tender') return null;
|
|
15
|
+
const money = (value: number) => formatMoney({ amount: value, currency: order.currency });
|
|
16
|
+
const digits = minorUnitDigits(order.currency);
|
|
17
|
+
const amountText = amount ?? (order.balanceDueMinor / 10 ** digits).toFixed(digits);
|
|
18
|
+
const incomplete = order.balanceDueMinor > 0 || order.payments.length === 0;
|
|
19
|
+
function add() {
|
|
20
|
+
const parsed = parsePrice(amountText, order.currency);
|
|
21
|
+
if (typeof parsed === 'string') { setError(parsed); return; }
|
|
22
|
+
const id = sale.addTender({ method, amountMinor: parsed,
|
|
23
|
+
...(method === 'external' && reference ? { reference } : {}) });
|
|
24
|
+
if (id === null) {
|
|
25
|
+
setError("That tender wasn't added: nothing is due, or the amount isn't valid.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
setError(null);
|
|
29
|
+
setReference('');
|
|
30
|
+
setAmount(null);
|
|
31
|
+
}
|
|
32
|
+
return <View dataSet={{ print: 'hide' }} className="gap-4 p-4">
|
|
33
|
+
<View testID="split-tender-summary" className="gap-2">
|
|
34
|
+
<Text className="text-foreground">Total: {money(order.totalMinor)}</Text>
|
|
35
|
+
<Text className="text-foreground">Paid: {money(order.paidMinor)}</Text>
|
|
36
|
+
<Text className="text-foreground">Remaining: {money(order.balanceDueMinor)}</Text>
|
|
37
|
+
{order.changeDueMinor > 0 ? <Text className="text-foreground">Change: {money(order.changeDueMinor)}</Text> : null}
|
|
38
|
+
</View>
|
|
39
|
+
<View testID="split-tender-list" className="gap-2">
|
|
40
|
+
{order.payments.map((payment) => <View key={payment.id} testID={`split-tender-row-${payment.id}`} className="gap-2">
|
|
41
|
+
<Text className="text-foreground">{tenderLabel(payment.method)} {money(payment.amountMinor)}</Text>
|
|
42
|
+
{payment.reference ? <Text className="text-foreground">{payment.reference}</Text> : null}
|
|
43
|
+
<Pressable testID={`split-tender-remove-${payment.id}`} accessibilityRole="button" accessibilityLabel="Remove tender"
|
|
44
|
+
onPress={() => sale.removeTender(payment.id)} className="rounded-md border border-border bg-card px-4 py-3">
|
|
45
|
+
<Text className="text-center text-foreground">Remove</Text>
|
|
46
|
+
</Pressable>
|
|
47
|
+
</View>)}
|
|
48
|
+
</View>
|
|
49
|
+
<View testID="split-tender-add" className="gap-2">
|
|
50
|
+
<View className="flex-row gap-2">
|
|
51
|
+
{(['cash', 'external'] as const).map((value) => <Pressable key={value}
|
|
52
|
+
testID={`split-tender-method-${value === 'cash' ? 'cash' : 'card'}`} accessibilityRole="button"
|
|
53
|
+
accessibilityState={{ selected: method === value }} onPress={() => setMethod(value)}
|
|
54
|
+
className={`rounded-md border border-border px-4 py-3 ${method === value ? 'bg-primary' : 'bg-card'}`}>
|
|
55
|
+
<Text className={`text-center ${method === value ? 'text-primary-foreground' : 'text-foreground'}`}>{tenderLabel(value)}</Text>
|
|
56
|
+
</Pressable>)}
|
|
57
|
+
</View>
|
|
58
|
+
<TextInput testID="split-tender-amount" accessibilityLabel="Tender amount" inputMode="decimal"
|
|
59
|
+
value={amountText} onChangeText={setAmount} className="rounded-md border border-border px-3 py-2 text-foreground" />
|
|
60
|
+
{method === 'external' ? <TextInput testID="split-tender-reference" accessibilityLabel="Terminal reference"
|
|
61
|
+
placeholder="Terminal reference" maxLength={255} value={reference} onChangeText={setReference}
|
|
62
|
+
className="rounded-md border border-border px-3 py-2 text-foreground" /> : null}
|
|
63
|
+
{error ? <Text testID="split-tender-error" accessibilityRole="alert" className="text-destructive">{error}</Text> : null}
|
|
64
|
+
<Pressable testID="split-tender-add-button" accessibilityRole="button" onPress={add} className="rounded-md bg-primary px-4 py-3">
|
|
65
|
+
<Text className="text-center font-semibold text-primary-foreground">Add</Text>
|
|
66
|
+
</Pressable>
|
|
67
|
+
</View>
|
|
68
|
+
<Pressable testID="split-tender-complete" accessibilityRole="button" disabled={incomplete} onPress={sale.complete}
|
|
69
|
+
className={`rounded-md bg-primary px-4 py-3 ${incomplete ? 'opacity-50' : ''}`}>
|
|
70
|
+
<Text className="text-center font-semibold text-primary-foreground">Complete sale</Text>
|
|
71
|
+
</Pressable>
|
|
72
|
+
{sale.error ? <Text accessibilityRole="alert" className="text-destructive">{sale.error}</Text> : null}
|
|
73
|
+
{sale.canContinue ? <>
|
|
74
|
+
<Text className="text-foreground">This sale is stored and will be sent. Continue to the next sale.</Text>
|
|
75
|
+
<Pressable accessibilityRole="button" onPress={() => sale.continueSale()} className="rounded-md bg-primary px-4 py-3">
|
|
76
|
+
<Text className="text-center font-semibold text-primary-foreground">Continue</Text>
|
|
77
|
+
</Pressable>
|
|
78
|
+
</> : null}
|
|
79
|
+
<Pressable accessibilityRole="button" onPress={sale.cancelTender} className="rounded-md border border-border bg-card px-4 py-3"><Text className="text-center text-foreground">Back</Text></Pressable>
|
|
80
|
+
</View>;
|
|
81
|
+
}
|