@classytic/pos-ui 0.2.0 → 1.2.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.
Files changed (87) hide show
  1. package/LICENSE +75 -0
  2. package/dist/components/CartSidebar.d.ts +56 -0
  3. package/dist/components/CartSidebar.js +10 -9
  4. package/dist/components/PosTopBar.d.ts +19 -0
  5. package/dist/components/PosTopBar.js +4 -4
  6. package/dist/components/PrinterSettingsDialog.d.ts +8 -0
  7. package/dist/components/PrinterSettingsDialog.js +506 -20
  8. package/dist/components/ReceiptDeliveryDialog.d.ts +11 -0
  9. package/dist/components/ReceiptDeliveryDialog.js +98 -0
  10. package/dist/components/ReceiptReprintDialog.d.ts +10 -0
  11. package/dist/components/ReceiptReprintDialog.js +49 -22
  12. package/dist/components/index.d.ts +6 -0
  13. package/dist/components/index.js +7 -0
  14. package/dist/dashboard/components/CustomerLookupDialog.js +108 -90
  15. package/dist/dashboard/components/CustomerQuickAddDialog.js +3 -1
  16. package/dist/dashboard/components/DeliveryPanel.js +134 -0
  17. package/dist/dashboard/components/ProductCard.js +17 -7
  18. package/dist/dashboard/components/ProductsPanel.js +4 -4
  19. package/dist/dashboard/components/SplitPaymentPanel.js +5 -5
  20. package/dist/dashboard/components/VariantSelectorDialog.js +53 -11
  21. package/dist/dashboard/components/cart/AddChargeDialog.js +3 -2
  22. package/dist/dashboard/components/cart/CartItems.js +5 -5
  23. package/dist/dashboard/components/cart/CartSummary.js +6 -6
  24. package/dist/dashboard/components/cart/CustomerSection.js +3 -3
  25. package/dist/dashboard/components/cart/DiscountSection.js +4 -3
  26. package/dist/dashboard/components/cart/PointsRedemptionSection.js +5 -4
  27. package/dist/dashboard/pos.types.d.ts +32 -0
  28. package/dist/hardware/agent-adapter.js +127 -0
  29. package/dist/hardware/agent-pairing.d.ts +18 -0
  30. package/dist/hardware/agent-pairing.js +63 -0
  31. package/dist/hardware/context.d.ts +1 -5
  32. package/dist/hardware/index.d.ts +4 -3
  33. package/dist/hardware/index.js +3 -2
  34. package/dist/hardware/ports.d.ts +21 -4
  35. package/dist/hardware/tauri-adapter.d.ts +0 -1
  36. package/dist/hardware/tauri-adapter.js +19 -5
  37. package/dist/hardware/web-adapter.d.ts +22 -7
  38. package/dist/hardware/web-adapter.js +56 -14
  39. package/dist/hooks/branch-scoped-key.js +64 -0
  40. package/dist/hooks/index.d.ts +6 -0
  41. package/dist/hooks/index.js +7 -0
  42. package/dist/hooks/sale-attempt.js +62 -0
  43. package/dist/hooks/useManagerAuth.js +9 -1
  44. package/dist/hooks/usePosCart.d.ts +55 -0
  45. package/dist/hooks/usePosCart.js +224 -117
  46. package/dist/hooks/usePosCustomer.d.ts +31 -0
  47. package/dist/hooks/usePosCustomer.js +32 -78
  48. package/dist/hooks/usePosDelivery.d.ts +28 -0
  49. package/dist/hooks/usePosDelivery.js +133 -0
  50. package/dist/hooks/usePosMultiOrder.d.ts +22 -0
  51. package/dist/hooks/usePosMultiOrder.js +14 -4
  52. package/dist/hooks/usePosPayment.d.ts +22 -0
  53. package/dist/lib/delivery-payload.js +83 -0
  54. package/dist/lib/money.js +96 -23
  55. package/dist/node_modules/react-hook-form/dist/index.esm.js +468 -332
  56. package/dist/runtime/auth-port.d.ts +1 -5
  57. package/dist/runtime/branch-port.d.ts +1 -5
  58. package/dist/runtime/config.d.ts +43 -6
  59. package/dist/runtime/config.js +18 -1
  60. package/dist/runtime/index.d.ts +2 -2
  61. package/dist/runtime/index.js +2 -2
  62. package/dist/screens/OrderHistoryDrawer.d.ts +8 -0
  63. package/dist/screens/OrderHistoryDrawer.js +15 -15
  64. package/dist/screens/ParkedOrdersDrawer.d.ts +12 -0
  65. package/dist/screens/ParkedOrdersDrawer.js +3 -3
  66. package/dist/screens/PaymentScreen.d.ts +4 -0
  67. package/dist/screens/PaymentScreen.js +194 -33
  68. package/dist/screens/ProductScreen.d.ts +4 -0
  69. package/dist/screens/ProductScreen.js +13 -10
  70. package/dist/screens/ReceiptScreen.d.ts +4 -0
  71. package/dist/screens/ReceiptScreen.js +96 -72
  72. package/dist/screens/ShiftCloseScreen.d.ts +4 -0
  73. package/dist/screens/ShiftCloseScreen.js +18 -15
  74. package/dist/screens/ShiftOpenScreen.d.ts +4 -0
  75. package/dist/screens/ShiftOpenScreen.js +75 -19
  76. package/dist/screens/index.d.ts +8 -0
  77. package/dist/screens/index.js +9 -0
  78. package/dist/shell/index.d.ts +2 -0
  79. package/dist/shell/pos-shell.d.ts +41 -8
  80. package/dist/shell/pos-shell.js +25 -18
  81. package/dist/state/index.d.ts +3 -0
  82. package/dist/state/index.js +4 -0
  83. package/dist/state/pos-context.d.ts +13 -0
  84. package/dist/state/pos-state.d.ts +49 -0
  85. package/dist/state/pos-state.js +6 -7
  86. package/dist/utils/pos-helpers.js +137 -34
  87. package/package.json +112 -92
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from "react";
2
-
3
2
  //#region src/runtime/auth-port.d.ts
4
3
  /**
5
4
  * `PosAuthPort` — the auth seam the POS app needs, injected by the host.
@@ -34,10 +33,7 @@ interface PosAuthPort {
34
33
  user: PosSessionUser;
35
34
  }>;
36
35
  }
37
- declare function PosAuthProvider({
38
- auth,
39
- children
40
- }: {
36
+ declare function PosAuthProvider({ auth, children }: {
41
37
  auth: PosAuthPort;
42
38
  children: ReactNode;
43
39
  }): import("react").JSX.Element;
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from "react";
2
-
3
2
  //#region src/runtime/branch-port.d.ts
4
3
  /**
5
4
  * `PosBranchPort` — the active-branch seam, injected by the host. POS is
@@ -23,10 +22,7 @@ interface PosBranchPort {
23
22
  /** Reactive active branch (null before one is selected). */
24
23
  useActiveBranch: () => PosBranch | null;
25
24
  }
26
- declare function PosBranchProvider({
27
- branch,
28
- children
29
- }: {
25
+ declare function PosBranchProvider({ branch, children }: {
30
26
  branch: PosBranchPort;
31
27
  children: ReactNode;
32
28
  }): import("react").JSX.Element;
@@ -1,5 +1,4 @@
1
1
  import { ReactNode } from "react";
2
-
3
2
  //#region src/runtime/config.d.ts
4
3
  /**
5
4
  * `PosRuntime` — host-provided runtime services that are genuinely app/
@@ -12,13 +11,51 @@ interface PosRuntime {
12
11
  /** Navigate to a host route (e.g. back to `/dashboard`, out to `/sign-in`). */
13
12
  onNavigate: (path: string) => void;
14
13
  }
15
- declare function PosRuntimeProvider({
16
- runtime,
17
- children
18
- }: {
14
+ declare function PosRuntimeProvider({ runtime, children }: {
19
15
  runtime: PosRuntime;
20
16
  children: ReactNode;
21
17
  }): import("react").JSX.Element;
22
18
  declare function usePosRuntime(): PosRuntime;
19
+ /**
20
+ * Which SALES CAPABILITIES this deployment has turned on.
21
+ *
22
+ * Deliberately narrow, and every flag defaults to OFF. A capability the host has
23
+ * not declared must not render a control, because the server refuses the request
24
+ * and the cashier is left with an option that never works.
25
+ *
26
+ * ## This decides what RENDERS, never what is ALLOWED
27
+ *
28
+ * The server is the authority: `@spinekit/pos` refuses a collect-on-delivery sale
29
+ * unless the deployment declared `paymentTerms.collectOnDelivery`, and it refuses
30
+ * loudly (`PAYMENT_TERM_NOT_ENABLED`, 400). So a host flag that disagrees produces
31
+ * a visible error rather than a sale that books wrong — which is why a UI-side
32
+ * copy of the flag is acceptable here at all.
33
+ *
34
+ * That asymmetry is the whole design. The failure this avoids is the one AGENTS.md
35
+ * names: a UI rendering an option with no server behind it that silently no-ops.
36
+ * Here the mismatch is impossible to miss.
37
+ */
38
+ interface PosSalesCapabilities {
39
+ /**
40
+ * May the till take a collect-on-delivery order (no money now, courier collects)?
41
+ *
42
+ * OFF unless declared. A COD sale is an order with no money against it, so the
43
+ * deployment needs a sales profile whose money is a receivable, a settlement path
44
+ * for the courier's remittance, and a cancellation path for a refused delivery.
45
+ */
46
+ collectOnDelivery?: boolean | undefined;
47
+ }
48
+ declare function PosSalesCapabilitiesProvider({ sales, children }: {
49
+ sales: PosSalesCapabilities;
50
+ children: ReactNode;
51
+ }): import("react").JSX.Element;
52
+ /**
53
+ * Does NOT throw when unprovided, unlike the other runtime seams.
54
+ *
55
+ * An absent declaration is a complete answer here — "this deployment enabled
56
+ * nothing extra" — and it is the safe one, because every flag is off. Throwing
57
+ * would force every existing host to edit its mount for a feature it does not use.
58
+ */
59
+ declare function usePosSalesCapabilities(): PosSalesCapabilities;
23
60
  //#endregion
24
- export { PosRuntime, PosRuntimeProvider, usePosRuntime };
61
+ export { PosRuntime, PosRuntimeProvider, PosSalesCapabilities, PosSalesCapabilitiesProvider, usePosRuntime, usePosSalesCapabilities };
@@ -16,6 +16,23 @@ function usePosRuntime() {
16
16
  if (!ctx) throw new Error("usePosRuntime() must be used within <PosShell> / <PosRuntimeProvider>. Inject a PosRuntime (onNavigate + formatMoney).");
17
17
  return ctx;
18
18
  }
19
+ const PosSalesCapabilitiesContext = createContext({});
20
+ function PosSalesCapabilitiesProvider({ sales, children }) {
21
+ return /* @__PURE__ */ jsx(PosSalesCapabilitiesContext.Provider, {
22
+ value: sales,
23
+ children
24
+ });
25
+ }
26
+ /**
27
+ * Does NOT throw when unprovided, unlike the other runtime seams.
28
+ *
29
+ * An absent declaration is a complete answer here — "this deployment enabled
30
+ * nothing extra" — and it is the safe one, because every flag is off. Throwing
31
+ * would force every existing host to edit its mount for a feature it does not use.
32
+ */
33
+ function usePosSalesCapabilities() {
34
+ return useContext(PosSalesCapabilitiesContext);
35
+ }
19
36
 
20
37
  //#endregion
21
- export { PosRuntimeProvider, usePosRuntime };
38
+ export { PosRuntimeProvider, PosSalesCapabilitiesProvider, usePosRuntime, usePosSalesCapabilities };
@@ -1,4 +1,4 @@
1
1
  import { PosAuthPort, PosAuthProvider, PosSessionUser, usePosAuth } from "./auth-port.js";
2
- import { PosRuntime, PosRuntimeProvider, usePosRuntime } from "./config.js";
2
+ import { PosRuntime, PosRuntimeProvider, PosSalesCapabilities, PosSalesCapabilitiesProvider, usePosRuntime, usePosSalesCapabilities } from "./config.js";
3
3
  import { PosBranch, PosBranchPort, PosBranchProvider, usePosBranch, usePosBranchPort } from "./branch-port.js";
4
- export { type PosAuthPort, PosAuthProvider, type PosBranch, type PosBranchPort, PosBranchProvider, type PosRuntime, PosRuntimeProvider, type PosSessionUser, usePosAuth, usePosBranch, usePosBranchPort, usePosRuntime };
4
+ export { type PosAuthPort, PosAuthProvider, type PosBranch, type PosBranchPort, PosBranchProvider, type PosRuntime, PosRuntimeProvider, type PosSalesCapabilities, PosSalesCapabilitiesProvider, type PosSessionUser, usePosAuth, usePosBranch, usePosBranchPort, usePosRuntime, usePosSalesCapabilities };
@@ -1,5 +1,5 @@
1
1
  import { PosAuthProvider, usePosAuth } from "./auth-port.js";
2
- import { PosRuntimeProvider, usePosRuntime } from "./config.js";
2
+ import { PosRuntimeProvider, PosSalesCapabilitiesProvider, usePosRuntime, usePosSalesCapabilities } from "./config.js";
3
3
  import { PosBranchProvider, usePosBranch, usePosBranchPort } from "./branch-port.js";
4
4
 
5
- export { PosAuthProvider, PosBranchProvider, PosRuntimeProvider, usePosAuth, usePosBranch, usePosBranchPort, usePosRuntime };
5
+ export { PosAuthProvider, PosBranchProvider, PosRuntimeProvider, PosSalesCapabilitiesProvider, usePosAuth, usePosBranch, usePosBranchPort, usePosRuntime, usePosSalesCapabilities };
@@ -0,0 +1,8 @@
1
+ //#region src/screens/OrderHistoryDrawer.d.ts
2
+ interface OrderHistoryDrawerProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ }
6
+ declare function OrderHistoryDrawer({ open, onOpenChange }: OrderHistoryDrawerProps): import("react").JSX.Element;
7
+ //#endregion
8
+ export { OrderHistoryDrawer };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { formatPaisa } from "../lib/money.js";
3
+ import { formatMinor } from "../lib/money.js";
4
4
  import { ReceiptReprintDialog } from "../components/ReceiptReprintDialog.js";
5
5
  import { useCallback, useState } from "react";
6
6
  import { SheetWrapper } from "@classytic/fluid/client/core";
@@ -8,8 +8,8 @@ import { usePosOrderDetail, usePosOrderHistory, usePosRefundOrder, usePosVoidOrd
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Ban, ChevronRight, Loader2, Package, Printer, RotateCcw, Search } from "lucide-react";
10
10
  import { Button } from "@/components/ui/button";
11
- import { StatusBadge } from "@classytic/fluid";
12
11
  import { Input } from "@/components/ui/input";
12
+ import { StatusBadge } from "@classytic/fluid/server";
13
13
 
14
14
  //#region src/screens/OrderHistoryDrawer.tsx
15
15
  /**
@@ -73,11 +73,11 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
73
73
  className: "px-4 py-2 border-b",
74
74
  children: /* @__PURE__ */ jsxs("div", {
75
75
  className: "relative",
76
- children: [/* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ jsx(Input, {
76
+ children: [/* @__PURE__ */ jsx(Search, { className: "absolute start-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), /* @__PURE__ */ jsx(Input, {
77
77
  placeholder: "Search by order number...",
78
78
  value: search,
79
79
  onChange: (e) => setSearch(e.target.value),
80
- className: "pl-9 h-9"
80
+ className: "ps-9 h-9"
81
81
  })]
82
82
  })
83
83
  }),
@@ -89,7 +89,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
89
89
  variant: "ghost",
90
90
  size: "sm",
91
91
  onClick: () => setSelectedOrderId(null),
92
- className: "h-7 text-xs -ml-2",
92
+ className: "h-7 text-xs -ms-2",
93
93
  children: "Back to list"
94
94
  })
95
95
  }), detailLoading ? /* @__PURE__ */ jsx("div", {
@@ -128,16 +128,16 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
128
128
  const snapshot = line.snapshot;
129
129
  const unitPriceMoney = snapshot?.unitPrice;
130
130
  const qty = Number(line.quantity ?? 0);
131
- const unitPaisa = typeof unitPriceMoney === "number" ? unitPriceMoney : unitPriceMoney?.amount ?? 0;
132
- const lineTotalPaisa = typeof lineTotalMoney?.amount === "number" ? lineTotalMoney.amount : unitPaisa * qty;
131
+ const unitMinor = typeof unitPriceMoney === "number" ? unitPriceMoney : unitPriceMoney?.amount ?? 0;
132
+ const lineTotalMinor = typeof lineTotalMoney?.amount === "number" ? lineTotalMoney.amount : unitMinor * qty;
133
133
  return /* @__PURE__ */ jsxs("div", {
134
134
  className: "flex justify-between text-sm",
135
135
  children: [/* @__PURE__ */ jsxs("span", { children: [String(snapshot?.name ?? "Item"), /* @__PURE__ */ jsxs("span", {
136
- className: "text-muted-foreground ml-1",
136
+ className: "text-muted-foreground ms-1",
137
137
  children: ["x", qty]
138
138
  })] }), /* @__PURE__ */ jsx("span", {
139
139
  className: "tabular-nums",
140
- children: formatPaisa(lineTotalPaisa)
140
+ children: formatMinor(lineTotalMinor)
141
141
  })]
142
142
  }, i);
143
143
  })]
@@ -146,7 +146,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
146
146
  className: "flex justify-between font-semibold text-base pt-2 border-t",
147
147
  children: [/* @__PURE__ */ jsx("span", { children: "Total" }), /* @__PURE__ */ jsx("span", {
148
148
  className: "tabular-nums",
149
- children: formatPaisa(Number((selectedOrder.totals?.grandTotal)?.amount ?? 0))
149
+ children: formatMinor(Number((selectedOrder.totals?.grandTotal)?.amount ?? 0))
150
150
  })]
151
151
  }),
152
152
  /* @__PURE__ */ jsxs("div", {
@@ -156,7 +156,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
156
156
  variant: "outline",
157
157
  size: "sm",
158
158
  onClick: () => selectedOrderId && setReprintOrderId(selectedOrderId),
159
- children: [/* @__PURE__ */ jsx(Printer, { className: "h-4 w-4 mr-2" }), "Reprint Receipt"]
159
+ children: [/* @__PURE__ */ jsx(Printer, { className: "h-4 w-4 me-2" }), "Reprint Receipt"]
160
160
  }),
161
161
  (selectedOrder.status === "pending" || selectedOrder.status === "confirmed") && /* @__PURE__ */ jsxs(Button, {
162
162
  variant: "outline",
@@ -164,7 +164,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
164
164
  className: "text-destructive",
165
165
  onClick: handleVoid,
166
166
  disabled: isVoiding,
167
- children: [/* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 mr-2" }), isVoiding ? "Voiding..." : "Void Order"]
167
+ children: [/* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 me-2" }), isVoiding ? "Voiding..." : "Void Order"]
168
168
  }),
169
169
  selectedOrder.status === "completed" && /* @__PURE__ */ jsxs(Button, {
170
170
  variant: "outline",
@@ -172,7 +172,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
172
172
  className: "text-orange-600",
173
173
  onClick: handleRefund,
174
174
  disabled: isRefunding,
175
- children: [/* @__PURE__ */ jsx(RotateCcw, { className: "h-4 w-4 mr-2" }), isRefunding ? "Processing..." : "Full Refund"]
175
+ children: [/* @__PURE__ */ jsx(RotateCcw, { className: "h-4 w-4 me-2" }), isRefunding ? "Processing..." : "Full Refund"]
176
176
  })
177
177
  ]
178
178
  })
@@ -193,7 +193,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
193
193
  className: "divide-y",
194
194
  children: orders.map((order) => /* @__PURE__ */ jsxs("button", {
195
195
  type: "button",
196
- className: "w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors flex items-center gap-3",
196
+ className: "w-full px-4 py-3 text-start hover:bg-muted/50 transition-colors flex items-center gap-3",
197
197
  onClick: () => setSelectedOrderId(String(order.orderNumber ?? order._id)),
198
198
  children: [
199
199
  /* @__PURE__ */ jsxs("div", {
@@ -223,7 +223,7 @@ function OrderHistoryDrawer({ open, onOpenChange }) {
223
223
  }),
224
224
  /* @__PURE__ */ jsx("span", {
225
225
  className: "font-semibold text-sm tabular-nums shrink-0",
226
- children: formatPaisa(Number((order.totals?.grandTotal)?.amount ?? 0))
226
+ children: formatMinor(Number((order.totals?.grandTotal)?.amount ?? 0))
227
227
  }),
228
228
  /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground shrink-0" })
229
229
  ]
@@ -0,0 +1,12 @@
1
+ import { ParkedOrder } from "../hooks/usePosMultiOrder.js";
2
+ //#region src/screens/ParkedOrdersDrawer.d.ts
3
+ interface ParkedOrdersDrawerProps {
4
+ open: boolean;
5
+ onOpenChange: (open: boolean) => void;
6
+ orders: ParkedOrder[];
7
+ onResume: (id: string) => void;
8
+ onDelete: (id: string) => void;
9
+ }
10
+ declare function ParkedOrdersDrawer({ open, onOpenChange, orders, onResume, onDelete }: ParkedOrdersDrawerProps): import("react").JSX.Element;
11
+ //#endregion
12
+ export { ParkedOrdersDrawer };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { formatBdt } from "../lib/money.js";
3
+ import { formatMajor } from "../lib/money.js";
4
4
  import { calculateCartTotals } from "../utils/pos-helpers.js";
5
5
  import { useCallback } from "react";
6
6
  import { SheetWrapper } from "@classytic/fluid/client/core";
@@ -99,7 +99,7 @@ function ParkedOrdersDrawer({ open, onOpenChange, orders, onResume, onDelete })
99
99
  })]
100
100
  }), /* @__PURE__ */ jsx("span", {
101
101
  className: "text-sm font-semibold tabular-nums shrink-0",
102
- children: formatBdt(totals.total)
102
+ children: formatMajor(totals.total)
103
103
  })]
104
104
  }), /* @__PURE__ */ jsxs("div", {
105
105
  className: "flex gap-2",
@@ -107,7 +107,7 @@ function ParkedOrdersDrawer({ open, onOpenChange, orders, onResume, onDelete })
107
107
  size: "sm",
108
108
  className: "h-8 flex-1",
109
109
  onClick: () => onResume(order.id),
110
- children: [/* @__PURE__ */ jsx(PlayCircle, { className: "h-3.5 w-3.5 mr-1.5" }), "Resume"]
110
+ children: [/* @__PURE__ */ jsx(PlayCircle, { className: "h-3.5 w-3.5 me-1.5" }), "Resume"]
111
111
  }), /* @__PURE__ */ jsx(Button, {
112
112
  variant: "outline",
113
113
  size: "sm",
@@ -0,0 +1,4 @@
1
+ //#region src/screens/PaymentScreen.d.ts
2
+ declare function PaymentScreen(): import("react").JSX.Element;
3
+ //#endregion
4
+ export { PaymentScreen };