@fayz-ai/pos 0.23.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { haptic, createKeymap, directionOf, moveFocus } from '@fayz-ai/core/interaction';
2
2
  export { comboOf, createKeymap, haptic, setHapticsEnabled } from '@fayz-ai/core/interaction';
3
3
  import { forwardRef, memo, useState, useRef, useCallback, useEffect, useMemo, useReducer, Fragment as Fragment$1 } from 'react';
4
- import { UserPlus, X, Check, Phone, User, Receipt, RotateCcw, FileCheck, FileWarning, ChevronDown, Pencil, ChevronLeft, ChevronRight, Plus, ChevronUp, ChefHat, AlertTriangle, Printer, Percent, QrCode, CreditCard, Banknote, Trash2, Clock, Minus, Bike, PackageCheck, CheckCheck, Package, MapPin, ShieldCheck, Keyboard, CalendarClock, Users, UserRound, Search, Utensils, BellRing, UserX, UserCheck, Delete, UtensilsCrossed, Info, ClipboardList, LayoutGrid, ShoppingBag, ConciergeBell, Map as Map$1, Settings2, Square, Circle, PenLine, MoreHorizontal, Store, Tag, Star, Ticket, Ban, Loader2, ArrowLeftRight, Gift, UserMinus, CheckCircle2, Crosshair, Unlink, MousePointerClick, PencilRuler, MapPinOff, Eye, EyeOff, Undo2, TriangleAlert, ArrowRight, Pizza, Wheat, Sandwich, IceCreamCone, CakeSlice, Coffee, Croissant, CupSoda, Salad, Beef, Wine, GlassWater, Soup, Fish, Wrench, CalendarCheck } from 'lucide-react';
4
+ import { UserPlus, X, Check, Phone, User, Receipt, RotateCcw, FileCheck, FileWarning, ChevronDown, Pencil, ChevronLeft, ChevronRight, Plus, ChevronUp, ChefHat, AlertTriangle, Printer, Percent, QrCode, CreditCard, Banknote, Trash2, Clock, Minus, Bike, History, PackageCheck, CheckCheck, Package, MapPin, ShieldCheck, Keyboard, CalendarClock, Users, UserRound, Search, Utensils, BellRing, UserX, UserCheck, Delete, UtensilsCrossed, Info, ClipboardList, LayoutGrid, ShoppingBag, ConciergeBell, Map as Map$1, Settings2, Square, Circle, PenLine, MoreHorizontal, Store, Tag, Star, Ticket, Ban, ArrowLeftRight, Gift, UserMinus, CheckCircle2, Crosshair, Unlink, MousePointerClick, PencilRuler, MapPinOff, Eye, EyeOff, Undo2, TriangleAlert, ArrowRight, Pizza, Wheat, Sandwich, IceCreamCone, CakeSlice, Coffee, Croissant, CupSoda, Salad, Beef, Wine, GlassWater, Soup, Fish, Wrench, CalendarCheck } from 'lucide-react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { describeError, channelOfOrder } from '@fayz-ai/core';
7
7
  import { createPortal } from 'react-dom';
@@ -4869,7 +4869,7 @@ function lateness(p, now) {
4869
4869
  return Math.floor((now - new Date(p.dueAt).getTime()) / 6e4);
4870
4870
  }
4871
4871
  var GHOST_MS = 300;
4872
- function DeliveryBoard({ orders, activeId, onSelect, now, channelsConnected }) {
4872
+ function DeliveryBoard({ orders, activeId, onSelect, now, channelsConnected, historyOpen, onToggleHistory }) {
4873
4873
  const seen = useRef({});
4874
4874
  const [ghosts, setGhosts] = useState([]);
4875
4875
  useEffect(() => {
@@ -4897,7 +4897,14 @@ function DeliveryBoard({ orders, activeId, onSelect, now, channelsConnected }) {
4897
4897
  Icon: Bike
4898
4898
  }
4899
4899
  ),
4900
- /* @__PURE__ */ jsx("p", { className: "pos-board__note", children: DELIVERED_LINGER_NOTE }),
4900
+ /* @__PURE__ */ jsxs("div", { className: "pos-board__bar", children: [
4901
+ /* @__PURE__ */ jsx("p", { className: "pos-board__note", children: DELIVERED_LINGER_NOTE }),
4902
+ onToggleHistory && /* @__PURE__ */ jsxs(PosButton, { className: "pos-pill", "aria-pressed": !!historyOpen, onClick: onToggleHistory, children: [
4903
+ /* @__PURE__ */ jsx(History, { size: 14 }),
4904
+ " ",
4905
+ historyOpen ? "Ocultar hist\xF3rico" : "Hist\xF3rico do dia"
4906
+ ] })
4907
+ ] }),
4901
4908
  /* @__PURE__ */ jsx("div", { className: "pos-board", children: DELIVERY_COLUMNS.map(({ name, states, tone }) => {
4902
4909
  const Icon = ICONE[name] ?? Clock;
4903
4910
  const column = orders.filter((o) => stillOnBoard(o, now)).filter((o) => states.includes(deliveryStatus(o.dispatch)));
@@ -4997,13 +5004,16 @@ function DispatchRail({
4997
5004
  totals,
4998
5005
  onAdvance,
4999
5006
  onCancel,
5007
+ onCloseDetails,
5000
5008
  now,
5001
5009
  open,
5002
5010
  containerRef,
5003
- extra
5011
+ extra,
5012
+ carrier
5004
5013
  }) {
5005
5014
  const status = order.dispatch ?? "received";
5006
5015
  const next = nextDispatch(status);
5016
+ const held = !!carrier && (next === "dispatched" || next === "out" || next === "delivered");
5007
5017
  const late = order.dueAt ? Math.floor((now - new Date(order.dueAt).getTime()) / 6e4) : void 0;
5008
5018
  return /* @__PURE__ */ jsxs(
5009
5019
  "aside",
@@ -5016,7 +5026,18 @@ function DispatchRail({
5016
5026
  /* @__PURE__ */ jsxs("header", { className: "pos-tkt", children: [
5017
5027
  /* @__PURE__ */ jsxs("div", { className: "pos-tkt__row", children: [
5018
5028
  /* @__PURE__ */ jsx("span", { className: "pos-t-xl", children: order.label }),
5019
- /* @__PURE__ */ jsx("span", { className: "pos-badge", "data-status": status === "out" || status === "dispatched" ? "bill_requested" : "occupied", children: DISPATCH_LABEL[status] })
5029
+ /* @__PURE__ */ jsx("span", { className: "pos-badge", "data-status": status === "out" || status === "dispatched" ? "bill_requested" : "occupied", children: DISPATCH_LABEL[status] }),
5030
+ onCloseDetails && /* @__PURE__ */ jsx(
5031
+ PosButton,
5032
+ {
5033
+ className: "pos-icon-btn",
5034
+ style: { marginLeft: "auto" },
5035
+ onClick: onCloseDetails,
5036
+ "aria-label": "Fechar detalhes",
5037
+ title: "Fechar detalhes \xB7 Esc",
5038
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
5039
+ }
5040
+ )
5020
5041
  ] }),
5021
5042
  /* @__PURE__ */ jsx("div", { className: "pos-tkt__sub", children: [order.channel, order.customerName].filter(Boolean).join(" \xB7 ") })
5022
5043
  ] }),
@@ -5099,7 +5120,10 @@ function DispatchRail({
5099
5120
  ] }),
5100
5121
  extra,
5101
5122
  /* @__PURE__ */ jsxs("div", { className: "pos-cta", style: { display: "grid", gap: "var(--pos-2x)" }, children: [
5102
- next ? /* @__PURE__ */ jsx(PosButton, { className: "pos-btn", onClick: () => onAdvance(next), children: ADVANCE_LABEL[next] }) : /* @__PURE__ */ jsx(PosButton, { className: "pos-btn", disabled: true, children: "Entregue" }),
5123
+ held ? /* @__PURE__ */ jsxs(Fragment, { children: [
5124
+ /* @__PURE__ */ jsx(PosButton, { className: "pos-btn", disabled: true, children: carrier.label }),
5125
+ /* @__PURE__ */ jsx("p", { className: "pos-t-sm", style: { margin: 0, textAlign: "center", color: "var(--pos-fg-3)" }, children: carrier.note })
5126
+ ] }) : next ? /* @__PURE__ */ jsx(PosButton, { className: "pos-btn", onClick: () => onAdvance(next), children: ADVANCE_LABEL[next] }) : /* @__PURE__ */ jsx(PosButton, { className: "pos-btn", disabled: true, children: "Entregue" }),
5103
5127
  /* @__PURE__ */ jsx(PosButton, { className: "pos-btn pos-btn--ghost", style: { width: "100%" }, onClick: onCancel, children: "Cancelar pedido" })
5104
5128
  ] })
5105
5129
  ]
@@ -7499,6 +7523,7 @@ function PosScreen({
7499
7523
  onCloseBill,
7500
7524
  onDispatch,
7501
7525
  renderDispatchExtra,
7526
+ dispatchCarrier,
7502
7527
  verifyPin,
7503
7528
  onAuthorised,
7504
7529
  actionGate,
@@ -7730,6 +7755,10 @@ function PosScreen({
7730
7755
  return;
7731
7756
  }
7732
7757
  if (event.key === "Escape") {
7758
+ if (activeKindRef.current === "delivery") {
7759
+ closeDetailsRef.current();
7760
+ return;
7761
+ }
7733
7762
  commandRef.current?.focus();
7734
7763
  return;
7735
7764
  }
@@ -7782,6 +7811,10 @@ function PosScreen({
7782
7811
  return { ...p, itemCount: counts[p.id] ?? p.itemCount, totalCents: totalCents || p.totalCents };
7783
7812
  }), [positions, opened, dispatched, settled, book, counts, config.serviceChargePercent, minhas]);
7784
7813
  const activePosition = livePositions.find((p) => p.id === activePositionId);
7814
+ const activeIdRef = useRef(activePositionId);
7815
+ activeIdRef.current = activePositionId;
7816
+ const activeKindRef = useRef(activePosition?.kind);
7817
+ activeKindRef.current = activePosition?.kind;
7785
7818
  const trocarCanal = useCallback((id) => {
7786
7819
  if (channelSwitchDropsPosition(
7787
7820
  { menuId, activeKind: activePosition?.kind },
@@ -7805,8 +7838,20 @@ function PosScreen({
7805
7838
  ),
7806
7839
  [closedTickets, canalMarcado, menu?.view]
7807
7840
  );
7841
+ const [deliveryHistory, setDeliveryHistory] = useState(false);
7842
+ const closeDetailsRef = useRef(() => {
7843
+ });
7844
+ closeDetailsRef.current = () => {
7845
+ setActivePositionId(void 0);
7846
+ setSelectedLineId(void 0);
7847
+ setRailOpen(false);
7848
+ };
7808
7849
  const escapeRef = useRef(() => false);
7809
7850
  escapeRef.current = () => {
7851
+ if (activePosition?.kind === "delivery") {
7852
+ closeDetailsRef.current();
7853
+ return true;
7854
+ }
7810
7855
  if (railOpen) {
7811
7856
  setRailOpen(false);
7812
7857
  return true;
@@ -7905,6 +7950,10 @@ function PosScreen({
7905
7950
  return;
7906
7951
  }
7907
7952
  if (seatingRef.current) return;
7953
+ if (p.kind === "delivery" && p.id === activeIdRef.current) {
7954
+ closeDetailsRef.current();
7955
+ return;
7956
+ }
7908
7957
  setActivePositionId(p.id);
7909
7958
  onActivatePosition?.(p.id);
7910
7959
  setRailOpen(true);
@@ -8341,7 +8390,7 @@ function PosScreen({
8341
8390
  style,
8342
8391
  "data-cart-open": railOpen || void 0,
8343
8392
  children: [
8344
- /* @__PURE__ */ jsxs("div", { className: "pos-body", children: [
8393
+ /* @__PURE__ */ jsxs("div", { className: "pos-body", "data-details": menu?.view === "list" && !activePosition && !deliveryHistory ? "closed" : void 0, children: [
8345
8394
  /* @__PURE__ */ jsxs("nav", { className: "pos-rail", "aria-label": "Navega\xE7\xE3o principal", children: [
8346
8395
  (brandMark || brandName || !railTop) && /* @__PURE__ */ jsxs("div", { className: "pos-brand", children: [
8347
8396
  /* @__PURE__ */ jsx("span", { className: "pos-brand__mark", children: brandMark ?? config.name.slice(0, 1) }),
@@ -8649,7 +8698,9 @@ function PosScreen({
8649
8698
  orders: livePositions.filter((p) => p.kind === "delivery"),
8650
8699
  activeId: activePositionId,
8651
8700
  onSelect: selectPosition,
8652
- now
8701
+ now,
8702
+ historyOpen: deliveryHistory && !activePosition,
8703
+ onToggleHistory: activePosition ? void 0 : () => setDeliveryHistory((v) => !v)
8653
8704
  }
8654
8705
  ),
8655
8706
  menu?.view === "reservations" && /* @__PURE__ */ jsx(
@@ -8975,7 +9026,9 @@ function PosScreen({
8975
9026
  void onDispatch?.(activePosition.id, to);
8976
9027
  },
8977
9028
  onCancel: () => handleAction("cancel_order"),
8978
- extra: renderDispatchExtra?.(activePosition, ticket)
9029
+ onCloseDetails: () => closeDetailsRef.current(),
9030
+ extra: renderDispatchExtra?.(activePosition, ticket),
9031
+ carrier: dispatchCarrier?.(activePosition) ?? null
8979
9032
  }
8980
9033
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
8981
9034
  railOpen && /* @__PURE__ */ jsx(
@@ -10212,7 +10265,6 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10212
10265
  const [chosen, setChosen] = useState(null);
10213
10266
  const [sending, setSending] = useState(false);
10214
10267
  const [attempt, setAttempt] = useState(0);
10215
- const list = useRef(null);
10216
10268
  useEffect(() => {
10217
10269
  let alive = true;
10218
10270
  setFailed(false);
@@ -10227,72 +10279,44 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10227
10279
  };
10228
10280
  }, [loadReasons, attempt]);
10229
10281
  const reason = reasons?.find((r) => r.code === chosen);
10230
- const onKey = (e) => {
10231
- if (!reasons?.length || !["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft"].includes(e.key)) return;
10232
- e.preventDefault();
10233
- const at = Math.max(0, reasons.findIndex((r) => r.code === chosen));
10234
- const step = e.key === "ArrowDown" || e.key === "ArrowRight" ? 1 : -1;
10235
- const next = reasons[(at + step + reasons.length) % reasons.length];
10236
- setChosen(next.code);
10237
- list.current?.querySelector(`[data-code="${CSS.escape(next.code)}"]`)?.focus();
10238
- };
10239
10282
  return /* @__PURE__ */ jsxs(PosSheet, { label: "Cancelar pedido", onDismiss: onCancel, children: [
10240
10283
  /* @__PURE__ */ jsxs("div", { className: "pos-sheet__head", children: [
10241
- /* @__PURE__ */ jsxs("div", { className: "pos-mcs__headtext", children: [
10242
- /* @__PURE__ */ jsxs("h2", { className: "pos-sheet__title", children: [
10243
- /* @__PURE__ */ jsx(Ban, { size: 18, strokeWidth: 2.2, "aria-hidden": true }),
10244
- " Cancelar ",
10245
- orderLabel
10246
- ] }),
10247
- /* @__PURE__ */ jsxs("p", { className: "pos-sheet__sub", children: [
10248
- "O ",
10249
- provider,
10250
- " decide o cancelamento. Escolha um dos motivos que ele aceita para este pedido."
10251
- ] })
10284
+ /* @__PURE__ */ jsxs("h2", { className: "pos-sheet__title", children: [
10285
+ /* @__PURE__ */ jsx(Ban, { size: 18, strokeWidth: 2.2, "aria-hidden": true }),
10286
+ " Cancelar ",
10287
+ orderLabel
10252
10288
  ] }),
10253
10289
  /* @__PURE__ */ jsx(PosButton, { className: "pos-sheet__x", onClick: onCancel, "aria-label": "Fechar", children: /* @__PURE__ */ jsx(X, { size: 20 }) })
10254
10290
  ] }),
10255
- /* @__PURE__ */ jsx("div", { className: "pos-sheet__body pos-mcs__body", children: failed ? /* @__PURE__ */ jsxs("div", { className: "pos-mcs__state", role: "alert", children: [
10256
- /* @__PURE__ */ jsxs("p", { children: [
10257
- "N\xE3o foi poss\xEDvel buscar os motivos no ",
10258
- provider,
10259
- "."
10260
- ] }),
10261
- /* @__PURE__ */ jsx(PosButton, { className: "pos-mcs__btn", onClick: () => setAttempt((n) => n + 1), children: "Tentar de novo" })
10262
- ] }) : reasons === null ? /* @__PURE__ */ jsxs("div", { className: "pos-mcs__state", "aria-live": "polite", children: [
10263
- /* @__PURE__ */ jsx(Loader2, { size: 18, className: "pos-mcs__spin", "aria-hidden": true }),
10264
- " Buscando os motivos no ",
10265
- provider,
10266
- "\u2026"
10267
- ] }) : reasons.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "pos-mcs__state", children: [
10291
+ /* @__PURE__ */ jsxs("p", { className: "pos-sheet__sub", children: [
10268
10292
  "O ",
10269
10293
  provider,
10270
- " n\xE3o aceita cancelar este pedido agora."
10271
- ] }) : /* @__PURE__ */ jsx("div", { ref: list, className: "pos-mcs__list", role: "radiogroup", "aria-label": `Motivos do ${provider}`, onKeyDown: onKey, children: reasons.map((r, i) => {
10272
- const on = chosen === r.code;
10273
- return /* @__PURE__ */ jsxs(
10274
- PosButton,
10275
- {
10276
- "data-code": r.code,
10277
- role: "radio",
10278
- "aria-checked": on,
10279
- tabIndex: on || !chosen && i === 0 ? 0 : -1,
10280
- className: `pos-mcs__opt${on ? " is-on" : ""}`,
10281
- onClick: () => setChosen(r.code),
10282
- children: [
10283
- /* @__PURE__ */ jsx("span", { className: "pos-mcs__dot", "aria-hidden": true }),
10284
- /* @__PURE__ */ jsx("span", { children: r.description })
10285
- ]
10286
- },
10287
- r.code
10288
- );
10289
- }) }) }),
10290
- /* @__PURE__ */ jsxs("div", { className: "pos-mcs__foot", children: [
10291
- /* @__PURE__ */ jsx(PosButton, { className: "pos-mcs__btn", onClick: onCancel, disabled: sending, children: "Voltar" }),
10292
- /* @__PURE__ */ jsxs(
10294
+ " decide o cancelamento. Escolha um dos motivos que ele aceita para este pedido."
10295
+ ] }),
10296
+ /* @__PURE__ */ jsx("div", { className: "pos-sheet__body", children: failed ? /* @__PURE__ */ jsx(
10297
+ EmptyState,
10298
+ {
10299
+ title: `N\xE3o foi poss\xEDvel buscar os motivos no ${provider}.`,
10300
+ Icon: Ban,
10301
+ action: { label: "Tentar de novo", onClick: () => setAttempt((n) => n + 1) }
10302
+ }
10303
+ ) : reasons !== null && reasons.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: `O ${provider} n\xE3o aceita cancelar este pedido agora.`, Icon: Ban }) : (
10304
+ // The same option rows the modifier and open-table sheets use: a box
10305
+ // that checks and an accent edge on the one chosen.
10306
+ /* @__PURE__ */ jsx("div", { className: "pos-mgroup__opts pos-mgroup__opts--stack", role: "group", "aria-label": `Motivos do ${provider}`, "aria-busy": reasons === null, children: reasons === null ? [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx("div", { className: "pos-mopt pos-skel", "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: "pos-skel__b", style: { width: `${55 + i * 9}%`, height: 14 } }) }, i)) : reasons.map((r) => {
10307
+ const on = chosen === r.code;
10308
+ return /* @__PURE__ */ jsxs(PosButton, { className: "pos-mopt", "aria-pressed": on, onClick: () => setChosen(r.code), children: [
10309
+ /* @__PURE__ */ jsx("span", { className: "pos-mopt__check", children: on && /* @__PURE__ */ jsx(Check, { size: 14, strokeWidth: 3 }) }),
10310
+ /* @__PURE__ */ jsx("span", { className: "pos-mopt__label", children: r.description })
10311
+ ] }, r.code);
10312
+ }) })
10313
+ ) }),
10314
+ /* @__PURE__ */ jsxs("div", { className: "pos-sheet__foot", children: [
10315
+ /* @__PURE__ */ jsx(PosButton, { className: "pos-btn pos-btn--ghost", onClick: onCancel, disabled: sending, children: "Voltar" }),
10316
+ /* @__PURE__ */ jsx(
10293
10317
  PosButton,
10294
10318
  {
10295
- className: "pos-mcs__btn pos-mcs__btn--danger",
10319
+ className: "pos-btn pos-btn--danger",
10296
10320
  disabled: !reason || sending,
10297
10321
  "aria-busy": sending,
10298
10322
  onClick: async () => {
@@ -10304,10 +10328,7 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10304
10328
  setSending(false);
10305
10329
  }
10306
10330
  },
10307
- children: [
10308
- sending && /* @__PURE__ */ jsx(Loader2, { size: 16, className: "pos-mcs__spin", "aria-hidden": true }),
10309
- sending ? "Enviando\u2026" : `Pedir cancelamento ao ${provider}`
10310
- ]
10331
+ children: sending ? "Enviando\u2026" : `Pedir cancelamento ao ${provider}`
10311
10332
  }
10312
10333
  )
10313
10334
  ] })