@fayz-ai/pos 0.22.4 → 0.23.1

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, 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,6 +5004,7 @@ function DispatchRail({
4997
5004
  totals,
4998
5005
  onAdvance,
4999
5006
  onCancel,
5007
+ onCloseDetails,
5000
5008
  now,
5001
5009
  open,
5002
5010
  containerRef,
@@ -5016,7 +5024,18 @@ function DispatchRail({
5016
5024
  /* @__PURE__ */ jsxs("header", { className: "pos-tkt", children: [
5017
5025
  /* @__PURE__ */ jsxs("div", { className: "pos-tkt__row", children: [
5018
5026
  /* @__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] })
5027
+ /* @__PURE__ */ jsx("span", { className: "pos-badge", "data-status": status === "out" || status === "dispatched" ? "bill_requested" : "occupied", children: DISPATCH_LABEL[status] }),
5028
+ onCloseDetails && /* @__PURE__ */ jsx(
5029
+ PosButton,
5030
+ {
5031
+ className: "pos-icon-btn",
5032
+ style: { marginLeft: "auto" },
5033
+ onClick: onCloseDetails,
5034
+ "aria-label": "Fechar detalhes",
5035
+ title: "Fechar detalhes \xB7 Esc",
5036
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
5037
+ }
5038
+ )
5020
5039
  ] }),
5021
5040
  /* @__PURE__ */ jsx("div", { className: "pos-tkt__sub", children: [order.channel, order.customerName].filter(Boolean).join(" \xB7 ") })
5022
5041
  ] }),
@@ -7490,6 +7509,8 @@ function PosScreen({
7490
7509
  onWaitlistLookupPhone,
7491
7510
  onReservationArrived,
7492
7511
  connection = "online",
7512
+ initialMenuId,
7513
+ onMenuChange,
7493
7514
  operatorId,
7494
7515
  onAction,
7495
7516
  onOpenPosition,
@@ -7565,7 +7586,12 @@ function PosScreen({
7565
7586
  }
7566
7587
  return seeded;
7567
7588
  });
7568
- const [menuId, setMenuId] = useState(config.menus[0]?.id);
7589
+ const [menuId, setMenuId] = useState(() => config.menus.some((m) => m.id === initialMenuId && !m.hidden) ? initialMenuId : config.menus[0]?.id);
7590
+ const onMenuChangeRef = useRef(onMenuChange);
7591
+ onMenuChangeRef.current = onMenuChange;
7592
+ useEffect(() => {
7593
+ if (menuId) onMenuChangeRef.current?.(menuId);
7594
+ }, [menuId]);
7569
7595
  useEffect(() => {
7570
7596
  if (!initialTickets) return;
7571
7597
  dispatchBook({ type: "hydrate", tickets: initialTickets, at: new Date(now).toISOString() });
@@ -7723,6 +7749,10 @@ function PosScreen({
7723
7749
  return;
7724
7750
  }
7725
7751
  if (event.key === "Escape") {
7752
+ if (activeKindRef.current === "delivery") {
7753
+ closeDetailsRef.current();
7754
+ return;
7755
+ }
7726
7756
  commandRef.current?.focus();
7727
7757
  return;
7728
7758
  }
@@ -7775,6 +7805,10 @@ function PosScreen({
7775
7805
  return { ...p, itemCount: counts[p.id] ?? p.itemCount, totalCents: totalCents || p.totalCents };
7776
7806
  }), [positions, opened, dispatched, settled, book, counts, config.serviceChargePercent, minhas]);
7777
7807
  const activePosition = livePositions.find((p) => p.id === activePositionId);
7808
+ const activeIdRef = useRef(activePositionId);
7809
+ activeIdRef.current = activePositionId;
7810
+ const activeKindRef = useRef(activePosition?.kind);
7811
+ activeKindRef.current = activePosition?.kind;
7778
7812
  const trocarCanal = useCallback((id) => {
7779
7813
  if (channelSwitchDropsPosition(
7780
7814
  { menuId, activeKind: activePosition?.kind },
@@ -7798,8 +7832,20 @@ function PosScreen({
7798
7832
  ),
7799
7833
  [closedTickets, canalMarcado, menu?.view]
7800
7834
  );
7835
+ const [deliveryHistory, setDeliveryHistory] = useState(false);
7836
+ const closeDetailsRef = useRef(() => {
7837
+ });
7838
+ closeDetailsRef.current = () => {
7839
+ setActivePositionId(void 0);
7840
+ setSelectedLineId(void 0);
7841
+ setRailOpen(false);
7842
+ };
7801
7843
  const escapeRef = useRef(() => false);
7802
7844
  escapeRef.current = () => {
7845
+ if (activePosition?.kind === "delivery") {
7846
+ closeDetailsRef.current();
7847
+ return true;
7848
+ }
7803
7849
  if (railOpen) {
7804
7850
  setRailOpen(false);
7805
7851
  return true;
@@ -7898,6 +7944,10 @@ function PosScreen({
7898
7944
  return;
7899
7945
  }
7900
7946
  if (seatingRef.current) return;
7947
+ if (p.kind === "delivery" && p.id === activeIdRef.current) {
7948
+ closeDetailsRef.current();
7949
+ return;
7950
+ }
7901
7951
  setActivePositionId(p.id);
7902
7952
  onActivatePosition?.(p.id);
7903
7953
  setRailOpen(true);
@@ -8334,7 +8384,7 @@ function PosScreen({
8334
8384
  style,
8335
8385
  "data-cart-open": railOpen || void 0,
8336
8386
  children: [
8337
- /* @__PURE__ */ jsxs("div", { className: "pos-body", children: [
8387
+ /* @__PURE__ */ jsxs("div", { className: "pos-body", "data-details": menu?.view === "list" && !activePosition && !deliveryHistory ? "closed" : void 0, children: [
8338
8388
  /* @__PURE__ */ jsxs("nav", { className: "pos-rail", "aria-label": "Navega\xE7\xE3o principal", children: [
8339
8389
  (brandMark || brandName || !railTop) && /* @__PURE__ */ jsxs("div", { className: "pos-brand", children: [
8340
8390
  /* @__PURE__ */ jsx("span", { className: "pos-brand__mark", children: brandMark ?? config.name.slice(0, 1) }),
@@ -8642,7 +8692,9 @@ function PosScreen({
8642
8692
  orders: livePositions.filter((p) => p.kind === "delivery"),
8643
8693
  activeId: activePositionId,
8644
8694
  onSelect: selectPosition,
8645
- now
8695
+ now,
8696
+ historyOpen: deliveryHistory && !activePosition,
8697
+ onToggleHistory: activePosition ? void 0 : () => setDeliveryHistory((v) => !v)
8646
8698
  }
8647
8699
  ),
8648
8700
  menu?.view === "reservations" && /* @__PURE__ */ jsx(
@@ -8968,6 +9020,7 @@ function PosScreen({
8968
9020
  void onDispatch?.(activePosition.id, to);
8969
9021
  },
8970
9022
  onCancel: () => handleAction("cancel_order"),
9023
+ onCloseDetails: () => closeDetailsRef.current(),
8971
9024
  extra: renderDispatchExtra?.(activePosition, ticket)
8972
9025
  }
8973
9026
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -10204,8 +10257,11 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10204
10257
  const [failed, setFailed] = useState(false);
10205
10258
  const [chosen, setChosen] = useState(null);
10206
10259
  const [sending, setSending] = useState(false);
10260
+ const [attempt, setAttempt] = useState(0);
10207
10261
  useEffect(() => {
10208
10262
  let alive = true;
10263
+ setFailed(false);
10264
+ setReasons(null);
10209
10265
  loadReasons().then((r) => {
10210
10266
  if (alive) setReasons(r);
10211
10267
  }).catch(() => {
@@ -10214,7 +10270,7 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10214
10270
  return () => {
10215
10271
  alive = false;
10216
10272
  };
10217
- }, [loadReasons]);
10273
+ }, [loadReasons, attempt]);
10218
10274
  const reason = reasons?.find((r) => r.code === chosen);
10219
10275
  return /* @__PURE__ */ jsxs(PosSheet, { label: "Cancelar pedido", onDismiss: onCancel, children: [
10220
10276
  /* @__PURE__ */ jsxs("div", { className: "pos-sheet__head", children: [
@@ -10230,37 +10286,32 @@ function MarketplaceCancelSheet({ provider, orderLabel, loadReasons, onConfirm,
10230
10286
  provider,
10231
10287
  " decide o cancelamento. Escolha um dos motivos que ele aceita para este pedido."
10232
10288
  ] }),
10233
- /* @__PURE__ */ jsx("div", { className: "pos-sheet__body", role: "radiogroup", "aria-label": `Motivos do ${provider}`, children: failed ? /* @__PURE__ */ jsxs("p", { className: "pos-empty__t", role: "alert", children: [
10234
- "N\xE3o foi poss\xEDvel buscar os motivos no ",
10235
- provider,
10236
- ". Tente de novo."
10237
- ] }) : reasons === null ? /* @__PURE__ */ jsxs("p", { className: "pos-empty__h", children: [
10238
- "Buscando os motivos no ",
10239
- provider,
10240
- "\u2026"
10241
- ] }) : reasons.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "pos-empty__t", children: [
10242
- "O ",
10243
- provider,
10244
- " n\xE3o aceita cancelar este pedido agora."
10245
- ] }) : reasons.map((r) => /* @__PURE__ */ jsx(
10246
- PosButton,
10289
+ /* @__PURE__ */ jsx("div", { className: "pos-sheet__body", children: failed ? /* @__PURE__ */ jsx(
10290
+ EmptyState,
10247
10291
  {
10248
- role: "radio",
10249
- "aria-checked": chosen === r.code,
10250
- className: `pos-btn pos-btn--ghost${chosen === r.code ? " is-active" : ""}`,
10251
- style: { width: "100%", justifyContent: "flex-start", marginBottom: "var(--pos-1x)" },
10252
- onClick: () => setChosen(r.code),
10253
- children: r.description
10254
- },
10255
- r.code
10256
- )) }),
10292
+ title: `N\xE3o foi poss\xEDvel buscar os motivos no ${provider}.`,
10293
+ Icon: Ban,
10294
+ action: { label: "Tentar de novo", onClick: () => setAttempt((n) => n + 1) }
10295
+ }
10296
+ ) : reasons !== null && reasons.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: `O ${provider} n\xE3o aceita cancelar este pedido agora.`, Icon: Ban }) : (
10297
+ // The same option rows the modifier and open-table sheets use: a box
10298
+ // that checks and an accent edge on the one chosen.
10299
+ /* @__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) => {
10300
+ const on = chosen === r.code;
10301
+ return /* @__PURE__ */ jsxs(PosButton, { className: "pos-mopt", "aria-pressed": on, onClick: () => setChosen(r.code), children: [
10302
+ /* @__PURE__ */ jsx("span", { className: "pos-mopt__check", children: on && /* @__PURE__ */ jsx(Check, { size: 14, strokeWidth: 3 }) }),
10303
+ /* @__PURE__ */ jsx("span", { className: "pos-mopt__label", children: r.description })
10304
+ ] }, r.code);
10305
+ }) })
10306
+ ) }),
10257
10307
  /* @__PURE__ */ jsxs("div", { className: "pos-sheet__foot", children: [
10258
- /* @__PURE__ */ jsx(PosButton, { className: "pos-btn pos-btn--ghost", onClick: onCancel, children: "Voltar" }),
10308
+ /* @__PURE__ */ jsx(PosButton, { className: "pos-btn pos-btn--ghost", onClick: onCancel, disabled: sending, children: "Voltar" }),
10259
10309
  /* @__PURE__ */ jsx(
10260
10310
  PosButton,
10261
10311
  {
10262
- className: "pos-btn",
10312
+ className: "pos-btn pos-btn--danger",
10263
10313
  disabled: !reason || sending,
10314
+ "aria-busy": sending,
10264
10315
  onClick: async () => {
10265
10316
  if (!reason) return;
10266
10317
  setSending(true);