@fayz-ai/pos 0.27.6 → 0.27.8
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 +320 -162
- package/dist/index.js.map +1 -1
- package/dist/screen/PosScreen.d.ts.map +1 -1
- package/dist/screen/catalogWindow.d.ts +24 -0
- package/dist/screen/catalogWindow.d.ts.map +1 -0
- package/dist/screen/money.d.ts.map +1 -1
- package/dist/screen/views.d.ts +8 -2
- package/dist/screen/views.d.ts.map +1 -1
- package/dist/styles.css +5 -0
- package/package.json +2 -2
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
|
-
import { forwardRef, memo,
|
|
4
|
-
import { UserPlus, X, Check, Phone, User, Receipt, RotateCcw, FileCheck, FileWarning, ChevronDown, Pencil, ChevronLeft, ChevronRight,
|
|
3
|
+
import { forwardRef, memo, useRef, useState, useCallback, useLayoutEffect, useEffect, useMemo, useReducer, useDeferredValue, Fragment as Fragment$1 } from 'react';
|
|
4
|
+
import { Minus, Plus, Search, Pizza, Wheat, Sandwich, IceCreamCone, CakeSlice, Coffee, Croissant, CupSoda, Salad, Beef, Utensils, Wine, GlassWater, Soup, Fish, UserPlus, X, Check, Phone, User, Receipt, RotateCcw, FileCheck, FileWarning, ChevronDown, Pencil, ChevronLeft, ChevronRight, ChevronUp, ChefHat, AlertTriangle, Printer, Percent, QrCode, CreditCard, Banknote, Trash2, Clock, Bike, History, PackageCheck, CheckCheck, Package, Loader2, MapPin, ShieldCheck, Keyboard, CalendarClock, Users, UserRound, 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, RefreshCw, MapPinOff, Eye, EyeOff, Undo2, TriangleAlert, ArrowRight, 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';
|
|
@@ -981,8 +981,15 @@ function asksInstallments(transactionType, accountKind, typeName) {
|
|
|
981
981
|
}
|
|
982
982
|
|
|
983
983
|
// src/screen/money.ts
|
|
984
|
+
var formatters = /* @__PURE__ */ new Map();
|
|
984
985
|
function formatCents(cents, currency = "BRL", locale = "pt-BR") {
|
|
985
|
-
|
|
986
|
+
const key = `${locale}|${currency}`;
|
|
987
|
+
let f = formatters.get(key);
|
|
988
|
+
if (!f) {
|
|
989
|
+
f = new Intl.NumberFormat(locale, { style: "currency", currency });
|
|
990
|
+
formatters.set(key, f);
|
|
991
|
+
}
|
|
992
|
+
return f.format(cents / 100);
|
|
986
993
|
}
|
|
987
994
|
function splitEvenly(totalCents, ways) {
|
|
988
995
|
if (ways <= 0) throw new Error("splitEvenly: ways must be positive");
|
|
@@ -2770,6 +2777,39 @@ function categoryIcon(label) {
|
|
|
2770
2777
|
for (const [palavra, Icone] of POR_PALAVRA) if (nome.includes(palavra)) return Icone;
|
|
2771
2778
|
return Utensils;
|
|
2772
2779
|
}
|
|
2780
|
+
|
|
2781
|
+
// src/screen/catalogWindow.ts
|
|
2782
|
+
var CATALOG_OVERSCAN_PX = 480;
|
|
2783
|
+
var CATALOG_FIRST_PASS = 36;
|
|
2784
|
+
function visibleRows(sections, cols, stride, viewTop, viewBottom) {
|
|
2785
|
+
const c = Math.max(1, cols);
|
|
2786
|
+
return sections.map(({ top, count }) => {
|
|
2787
|
+
const rows = Math.ceil(count / c);
|
|
2788
|
+
if (rows === 0 || stride <= 0) return [0, rows];
|
|
2789
|
+
const first = clamp(Math.floor((viewTop - top) / stride), 0, rows);
|
|
2790
|
+
const end = clamp(Math.ceil((viewBottom - top) / stride), first, rows);
|
|
2791
|
+
return [first, end];
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
function firstPassRows(counts, cols, budget = CATALOG_FIRST_PASS) {
|
|
2795
|
+
const c = Math.max(1, cols);
|
|
2796
|
+
let left = budget;
|
|
2797
|
+
return counts.map((count) => {
|
|
2798
|
+
const rows = Math.ceil(count / c);
|
|
2799
|
+
const take = clamp(Math.ceil(left / c), 0, rows);
|
|
2800
|
+
left -= take * c;
|
|
2801
|
+
return [0, take];
|
|
2802
|
+
});
|
|
2803
|
+
}
|
|
2804
|
+
function spacerHeight(rows, stride, gap) {
|
|
2805
|
+
return rows > 0 ? rows * stride - gap : 0;
|
|
2806
|
+
}
|
|
2807
|
+
function sameRows(a, b) {
|
|
2808
|
+
return a.length === b.length && a.every((r, i) => r[0] === b[i][0] && r[1] === b[i][1]);
|
|
2809
|
+
}
|
|
2810
|
+
function clamp(n, lo, hi) {
|
|
2811
|
+
return Math.min(hi, Math.max(lo, n));
|
|
2812
|
+
}
|
|
2773
2813
|
var STATUS = {
|
|
2774
2814
|
free: { label: "Livre", Icon: CheckCircle2 },
|
|
2775
2815
|
occupied: { label: "Ocupada", Icon: Utensils },
|
|
@@ -3180,7 +3220,8 @@ function seatOffsets(seats, w, h) {
|
|
|
3180
3220
|
place(perSide, (i) => ({ x: w + 4, y: step(perSide, h, i) }));
|
|
3181
3221
|
return out;
|
|
3182
3222
|
}
|
|
3183
|
-
|
|
3223
|
+
var CatalogView = memo(CatalogViewImpl, (a, b) => !!a.hidden && !!b.hidden);
|
|
3224
|
+
function CatalogViewImpl({
|
|
3184
3225
|
categories,
|
|
3185
3226
|
items,
|
|
3186
3227
|
activeCategoryId,
|
|
@@ -3192,7 +3233,8 @@ function CatalogView({
|
|
|
3192
3233
|
query,
|
|
3193
3234
|
menuLabel,
|
|
3194
3235
|
onRefreshMenu,
|
|
3195
|
-
menuRefreshing
|
|
3236
|
+
menuRefreshing,
|
|
3237
|
+
hidden
|
|
3196
3238
|
}) {
|
|
3197
3239
|
const searching = menuSearchActive(query);
|
|
3198
3240
|
const shown = items.filter(
|
|
@@ -3205,155 +3247,251 @@ function CatalogView({
|
|
|
3205
3247
|
}
|
|
3206
3248
|
const orfaos = shown.filter((i) => !categories.some((c) => c.id === i.categoryId));
|
|
3207
3249
|
if (orfaos.length > 0) secoes.push([void 0, orfaos]);
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
))
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3250
|
+
const scrollRef = useRef(null);
|
|
3251
|
+
const gridRefs = useRef([]);
|
|
3252
|
+
gridRefs.current.length = secoes.length;
|
|
3253
|
+
const countsRef = useRef([]);
|
|
3254
|
+
countsRef.current = secoes.map(([, itens]) => itens.length);
|
|
3255
|
+
const [win, setWin] = useState({ kind: "first" });
|
|
3256
|
+
const measure = useCallback(() => {
|
|
3257
|
+
const el = scrollRef.current;
|
|
3258
|
+
if (!el || el.style.display === "none") return;
|
|
3259
|
+
if (el.clientHeight === 0) {
|
|
3260
|
+
setWin((w) => w.kind === "all" ? w : { kind: "all" });
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
const grids = gridRefs.current;
|
|
3264
|
+
const g0 = grids.find(Boolean);
|
|
3265
|
+
if (!g0) return;
|
|
3266
|
+
const cs = getComputedStyle(g0);
|
|
3267
|
+
const cols2 = Math.max(1, cs.gridTemplateColumns.split(" ").filter(Boolean).length);
|
|
3268
|
+
const gap2 = parseFloat(cs.rowGap) || 0;
|
|
3269
|
+
const padTop = parseFloat(cs.paddingTop) || 0;
|
|
3270
|
+
const card = el.querySelector(".pos-items > .pos-item");
|
|
3271
|
+
const stride2 = (card?.offsetHeight || 128) + gap2;
|
|
3272
|
+
const base = el.getBoundingClientRect().top - el.scrollTop;
|
|
3273
|
+
const boxes = countsRef.current.map((count, i) => ({
|
|
3274
|
+
top: (grids[i]?.getBoundingClientRect().top ?? 0) - base + padTop,
|
|
3275
|
+
count
|
|
3276
|
+
}));
|
|
3277
|
+
const rows = visibleRows(
|
|
3278
|
+
boxes,
|
|
3279
|
+
cols2,
|
|
3280
|
+
stride2,
|
|
3281
|
+
el.scrollTop - CATALOG_OVERSCAN_PX,
|
|
3282
|
+
el.scrollTop + el.clientHeight + CATALOG_OVERSCAN_PX
|
|
3283
|
+
);
|
|
3284
|
+
setWin((w) => w.kind === "rows" && w.cols === cols2 && w.stride === stride2 && w.gap === gap2 && sameRows(w.rows, rows) ? w : { kind: "rows", cols: cols2, stride: stride2, gap: gap2, rows });
|
|
3285
|
+
}, []);
|
|
3286
|
+
const shape = countsRef.current.join(",");
|
|
3287
|
+
const wasHidden = useRef(hidden);
|
|
3288
|
+
useLayoutEffect(() => {
|
|
3289
|
+
if (wasHidden.current && !hidden && scrollRef.current) scrollRef.current.scrollTop = 0;
|
|
3290
|
+
wasHidden.current = hidden;
|
|
3291
|
+
}, [hidden]);
|
|
3292
|
+
const geometry = win.kind === "rows" ? `${win.cols}|${win.stride}|${win.gap}` : win.kind;
|
|
3293
|
+
useLayoutEffect(() => {
|
|
3294
|
+
if (!hidden) measure();
|
|
3295
|
+
}, [measure, hidden, shape, activeCategoryId, query, geometry]);
|
|
3296
|
+
useEffect(() => {
|
|
3297
|
+
const el = scrollRef.current;
|
|
3298
|
+
if (!el) return;
|
|
3299
|
+
let frame = 0;
|
|
3300
|
+
const onScroll = () => {
|
|
3301
|
+
if (frame) return;
|
|
3302
|
+
frame = requestAnimationFrame(() => {
|
|
3303
|
+
frame = 0;
|
|
3304
|
+
measure();
|
|
3305
|
+
});
|
|
3306
|
+
};
|
|
3307
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
3308
|
+
const ro = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(onScroll);
|
|
3309
|
+
ro?.observe(el);
|
|
3310
|
+
return () => {
|
|
3311
|
+
el.removeEventListener("scroll", onScroll);
|
|
3312
|
+
ro?.disconnect();
|
|
3313
|
+
if (frame) cancelAnimationFrame(frame);
|
|
3314
|
+
};
|
|
3315
|
+
}, [measure]);
|
|
3316
|
+
const dealRef = useRef({ key: "", at: 0 });
|
|
3317
|
+
const dealKey = `${hidden ? 1 : 0}|${activeCategoryId ?? ""}|${query}`;
|
|
3318
|
+
if (dealRef.current.key !== dealKey) dealRef.current = { key: dealKey, at: Date.now() };
|
|
3319
|
+
const late = win.kind === "rows" && Date.now() - dealRef.current.at > 400;
|
|
3320
|
+
const ranges = win.kind === "rows" ? win.rows.length === secoes.length ? win.rows : firstPassRows(countsRef.current, win.cols) : win.kind === "first" ? firstPassRows(countsRef.current, 4) : void 0;
|
|
3321
|
+
const cols = win.kind === "rows" ? win.cols : 4;
|
|
3322
|
+
const stride = win.kind === "rows" ? win.stride : 128 + 12;
|
|
3323
|
+
const gap = win.kind === "rows" ? win.gap : 12;
|
|
3324
|
+
return /* @__PURE__ */ jsxs(
|
|
3325
|
+
"div",
|
|
3326
|
+
{
|
|
3327
|
+
ref: scrollRef,
|
|
3328
|
+
className: "pos-scroll",
|
|
3329
|
+
style: { flex: 1, minHeight: 0, display: hidden ? "none" : "flex", flexDirection: "column" },
|
|
3330
|
+
children: [
|
|
3331
|
+
!searching && /* @__PURE__ */ jsx("div", { className: "pos-cats", children: categories.map((c, index) => /* @__PURE__ */ jsxs(
|
|
3332
|
+
PosButton,
|
|
3333
|
+
{
|
|
3334
|
+
className: "pos-cat",
|
|
3335
|
+
style: {
|
|
3336
|
+
background: `var(--pos-tone-${c.tone})`,
|
|
3337
|
+
["--pos-stagger"]: `${Math.min(index, 11) * 20}ms`
|
|
3338
|
+
},
|
|
3339
|
+
"aria-pressed": c.id === activeCategoryId,
|
|
3340
|
+
onClick: () => {
|
|
3341
|
+
haptic("tap");
|
|
3342
|
+
onCategoryChange(c.id);
|
|
3343
|
+
},
|
|
3344
|
+
children: [
|
|
3345
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
3346
|
+
/* @__PURE__ */ jsx("span", { className: "pos-cat__name", children: c.label }),
|
|
3347
|
+
c.itemCount != null && /* @__PURE__ */ jsxs("span", { className: "pos-cat__n pos-num", style: { display: "block" }, children: [
|
|
3348
|
+
c.itemCount,
|
|
3349
|
+
" itens"
|
|
3350
|
+
] })
|
|
3351
|
+
] }),
|
|
3352
|
+
/* @__PURE__ */ jsx("span", { className: "pos-cat__icon", children: c.icon ?? /* @__PURE__ */ jsx(CategoryIcon, { label: c.label }) })
|
|
3353
|
+
]
|
|
3264
3354
|
},
|
|
3265
|
-
|
|
3266
|
-
}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
categorias numa grade plana são duzentos e cinquenta cartões sem
|
|
3281
|
-
nenhuma divisa, e achar "Coca Cola" vira rolagem cega.
|
|
3282
|
-
|
|
3283
|
-
A ORDEM É A DO CARDÁPIO, não a alfabética — o V1 é explícito: é o
|
|
3284
|
-
`menu_order` das categorias, a mesma ordem das fichas coloridas do
|
|
3285
|
-
topo. Ordenar por nome faria a faixa de cima e a lista de baixo
|
|
3286
|
-
discordarem, e é a faixa que o operador usa para se orientar.
|
|
3287
|
-
|
|
3288
|
-
Com uma categoria escolhida sobra uma seção só, e o rótulo continua
|
|
3289
|
-
pagando o espaço: ele confirma onde se está depois de rolar. */
|
|
3290
|
-
secoes.map(([cat, itens]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3291
|
-
/* @__PURE__ */ jsx("h3", { className: "pos-sect", children: cat?.label ?? "Sem categoria" }),
|
|
3292
|
-
/* @__PURE__ */ jsx("div", { className: "pos-items", children: itens.map((item, index) => {
|
|
3293
|
-
const quantity = quantities[item.id] ?? 0;
|
|
3294
|
-
const category = categories.find((c) => c.id === item.categoryId);
|
|
3295
|
-
const filled = quantity > 0;
|
|
3296
|
-
return /* @__PURE__ */ jsxs(
|
|
3297
|
-
ItemCard,
|
|
3355
|
+
c.id
|
|
3356
|
+
)) }),
|
|
3357
|
+
!searching && /* @__PURE__ */ jsx("hr", { className: "pos-cats-rule" }),
|
|
3358
|
+
onRefreshMenu && /* @__PURE__ */ jsxs("div", { style: {
|
|
3359
|
+
display: "flex",
|
|
3360
|
+
alignItems: "center",
|
|
3361
|
+
justifyContent: "flex-end",
|
|
3362
|
+
gap: "0.5rem",
|
|
3363
|
+
padding: "0 0.75rem 0.375rem",
|
|
3364
|
+
fontSize: 11,
|
|
3365
|
+
opacity: 0.55
|
|
3366
|
+
}, children: [
|
|
3367
|
+
/* @__PURE__ */ jsx("span", { children: menuLabel }),
|
|
3368
|
+
/* @__PURE__ */ jsx(
|
|
3369
|
+
PosButton,
|
|
3298
3370
|
{
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3371
|
+
type: "button",
|
|
3372
|
+
className: "fz-tap",
|
|
3373
|
+
onClick: () => {
|
|
3302
3374
|
haptic("tap");
|
|
3303
|
-
|
|
3375
|
+
onRefreshMenu();
|
|
3304
3376
|
},
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3377
|
+
disabled: menuRefreshing,
|
|
3378
|
+
style: {
|
|
3379
|
+
background: "transparent",
|
|
3380
|
+
border: 0,
|
|
3381
|
+
padding: "0.125rem 0.25rem",
|
|
3382
|
+
color: "inherit",
|
|
3383
|
+
font: "inherit",
|
|
3384
|
+
textDecoration: "underline",
|
|
3385
|
+
cursor: menuRefreshing ? "default" : "pointer"
|
|
3386
|
+
},
|
|
3387
|
+
children: "Atualizar"
|
|
3388
|
+
}
|
|
3389
|
+
)
|
|
3390
|
+
] }),
|
|
3391
|
+
shown.length === 0 ? (
|
|
3392
|
+
/* A FRASE SEGUE A CAUSA. Sem busca, `query` é vazio e o título saía
|
|
3393
|
+
«Nada encontrado para “”» — aspas vazias sobre uma busca que
|
|
3394
|
+
ninguém fez. Ver `catalogEmpty.ts`. */
|
|
3395
|
+
(() => {
|
|
3396
|
+
const v = catalogEmpty(query);
|
|
3397
|
+
return /* @__PURE__ */ jsx(EmptyState, { title: v.title, hint: v.hint });
|
|
3398
|
+
})()
|
|
3399
|
+
) : (
|
|
3400
|
+
/* UMA SEÇÃO POR CATEGORIA, com o nome acima da grade — é assim que o
|
|
3401
|
+
V1 desenha o cardápio, e a razão aparece com "Todas": dezenove
|
|
3402
|
+
categorias numa grade plana são duzentos e cinquenta cartões sem
|
|
3403
|
+
nenhuma divisa, e achar "Coca Cola" vira rolagem cega.
|
|
3404
|
+
|
|
3405
|
+
A ORDEM É A DO CARDÁPIO, não a alfabética — o V1 é explícito: é o
|
|
3406
|
+
`menu_order` das categorias, a mesma ordem das fichas coloridas do
|
|
3407
|
+
topo. Ordenar por nome faria a faixa de cima e a lista de baixo
|
|
3408
|
+
discordarem, e é a faixa que o operador usa para se orientar.
|
|
3409
|
+
|
|
3410
|
+
Com uma categoria escolhida sobra uma seção só, e o rótulo continua
|
|
3411
|
+
pagando o espaço: ele confirma onde se está depois de rolar. */
|
|
3412
|
+
secoes.map(([cat, itens], sectionIndex) => {
|
|
3413
|
+
const rows = Math.ceil(itens.length / cols);
|
|
3414
|
+
const [first, end] = ranges?.[sectionIndex] ?? [0, rows];
|
|
3415
|
+
const windowed = ranges ? itens.slice(first * cols, end * cols) : itens;
|
|
3416
|
+
const before = ranges ? first : 0;
|
|
3417
|
+
const after = ranges ? rows - Math.max(end, first) : 0;
|
|
3418
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3419
|
+
/* @__PURE__ */ jsx("h3", { className: "pos-sect", children: cat?.label ?? "Sem categoria" }),
|
|
3420
|
+
/* @__PURE__ */ jsxs("div", { className: "pos-items", ref: (n) => {
|
|
3421
|
+
gridRefs.current[sectionIndex] = n;
|
|
3422
|
+
}, children: [
|
|
3423
|
+
windowed.length === 0 ? /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "pos-items__gap", style: { height: spacerHeight(rows, stride, gap) } }) : before > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "pos-items__gap", style: { height: spacerHeight(before, stride, gap) } }),
|
|
3424
|
+
windowed.map((item, index) => {
|
|
3425
|
+
const quantity = quantities[item.id] ?? 0;
|
|
3426
|
+
const category = categories.find((c) => c.id === item.categoryId);
|
|
3427
|
+
const filled = quantity > 0;
|
|
3428
|
+
return /* @__PURE__ */ jsxs(
|
|
3429
|
+
ItemCard,
|
|
3338
3430
|
{
|
|
3339
|
-
|
|
3340
|
-
|
|
3431
|
+
quantity,
|
|
3432
|
+
filled,
|
|
3433
|
+
onQuickAdd: () => {
|
|
3341
3434
|
haptic("tap");
|
|
3342
3435
|
onAdd(item);
|
|
3343
3436
|
},
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3437
|
+
label: item.name,
|
|
3438
|
+
delayMs: Math.min(index, 11) * 24,
|
|
3439
|
+
late,
|
|
3440
|
+
tone: category?.tone,
|
|
3441
|
+
children: [
|
|
3442
|
+
/* @__PURE__ */ jsxs("span", { className: "pos-item__top", children: [
|
|
3443
|
+
item.imageUrl ? /* @__PURE__ */ jsx("span", { className: "pos-item__img", children: /* @__PURE__ */ jsx("img", { src: item.imageUrl, alt: "", loading: "lazy", decoding: "async" }) }) : null,
|
|
3444
|
+
/* @__PURE__ */ jsxs("span", { className: "pos-item__id", children: [
|
|
3445
|
+
item.route && /* @__PURE__ */ jsx("span", { className: "pos-item__route", children: item.route }),
|
|
3446
|
+
/* @__PURE__ */ jsx("span", { className: "pos-item__name", children: item.name }),
|
|
3447
|
+
/* @__PURE__ */ jsxs("span", { className: "pos-item__price pos-num", children: [
|
|
3448
|
+
formatCents(item.priceCents),
|
|
3449
|
+
item.code && ` \xB7 ${item.code}`
|
|
3450
|
+
] })
|
|
3451
|
+
] })
|
|
3452
|
+
] }),
|
|
3453
|
+
/* @__PURE__ */ jsx("span", { className: "pos-item__foot", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs("span", { className: "pos-step", children: [
|
|
3454
|
+
/* @__PURE__ */ jsx(
|
|
3455
|
+
PosButton,
|
|
3456
|
+
{
|
|
3457
|
+
className: "pos-step__b",
|
|
3458
|
+
onClick: () => {
|
|
3459
|
+
haptic("tap");
|
|
3460
|
+
onRemove(item);
|
|
3461
|
+
},
|
|
3462
|
+
disabled: (pending?.[item.id] ?? 0) === 0,
|
|
3463
|
+
title: quantity > 0 && (pending?.[item.id] ?? 0) === 0 ? "J\xE1 foi para a cozinha \u2014 use Cancelamento" : void 0,
|
|
3464
|
+
"aria-label": `Tirar um ${item.name}`,
|
|
3465
|
+
children: /* @__PURE__ */ jsx(Minus, { size: 16, strokeWidth: 2.6, "aria-hidden": true })
|
|
3466
|
+
}
|
|
3467
|
+
),
|
|
3468
|
+
/* @__PURE__ */ jsx("span", { className: "pos-step__n", "data-zero": quantity === 0 ? "" : void 0, children: quantity }),
|
|
3469
|
+
/* @__PURE__ */ jsx(
|
|
3470
|
+
PosButton,
|
|
3471
|
+
{
|
|
3472
|
+
className: "pos-step__b",
|
|
3473
|
+
onClick: () => {
|
|
3474
|
+
haptic("tap");
|
|
3475
|
+
onAdd(item);
|
|
3476
|
+
},
|
|
3477
|
+
"aria-label": `Mais um ${item.name}`,
|
|
3478
|
+
children: /* @__PURE__ */ jsx(Plus, { size: 16, strokeWidth: 2.6, "aria-hidden": true })
|
|
3479
|
+
}
|
|
3480
|
+
)
|
|
3481
|
+
] }) })
|
|
3482
|
+
]
|
|
3483
|
+
},
|
|
3484
|
+
item.id
|
|
3485
|
+
);
|
|
3486
|
+
}),
|
|
3487
|
+
windowed.length > 0 && after > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "pos-items__gap", style: { height: spacerHeight(after, stride, gap) } })
|
|
3488
|
+
] })
|
|
3489
|
+
] }, cat?.id ?? "__sem");
|
|
3490
|
+
})
|
|
3491
|
+
)
|
|
3492
|
+
]
|
|
3493
|
+
}
|
|
3494
|
+
);
|
|
3357
3495
|
}
|
|
3358
3496
|
function ShiftView({ positions, staff }) {
|
|
3359
3497
|
const open = positions.filter((p) => p.status !== "free");
|
|
@@ -3378,7 +3516,7 @@ function Stat({ label, value }) {
|
|
|
3378
3516
|
/* @__PURE__ */ jsx("div", { className: "pos-t-display pos-num", children: value })
|
|
3379
3517
|
] });
|
|
3380
3518
|
}
|
|
3381
|
-
function ItemCard({ quantity, filled, delayMs, tone, onQuickAdd, label, children }) {
|
|
3519
|
+
function ItemCard({ quantity, filled, delayMs, late, tone, onQuickAdd, label, children }) {
|
|
3382
3520
|
return /* @__PURE__ */ jsx(
|
|
3383
3521
|
"div",
|
|
3384
3522
|
{
|
|
@@ -3387,6 +3525,7 @@ function ItemCard({ quantity, filled, delayMs, tone, onQuickAdd, label, children
|
|
|
3387
3525
|
tabIndex: 0,
|
|
3388
3526
|
"aria-label": `${label}, adicionar \xE0 comanda`,
|
|
3389
3527
|
"data-filled": filled,
|
|
3528
|
+
"data-late": late || void 0,
|
|
3390
3529
|
onClick: onQuickAdd,
|
|
3391
3530
|
onKeyDown: (e) => {
|
|
3392
3531
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -8760,6 +8899,10 @@ function PosScreen({
|
|
|
8760
8899
|
const positionsDoSalao = query.trim() && !plantaNaTela ? positionsDoFiltro.filter((p) => tableMatches({ label: p.label, name: p.customerName }, query)) : positionsDoFiltro;
|
|
8761
8900
|
const wideRail = useWideRail();
|
|
8762
8901
|
const headHasContent = menu?.view === "floor" || !wideRail;
|
|
8902
|
+
const onCatalog = menu?.view === "catalog";
|
|
8903
|
+
const catalogShown = useDeferredValue(onCatalog);
|
|
8904
|
+
const [catalogKept, setCatalogKept] = useState(false);
|
|
8905
|
+
if (catalogShown && !catalogKept) setCatalogKept(true);
|
|
8763
8906
|
const visibleMenus = config.menus.filter((m) => !m.hidden);
|
|
8764
8907
|
const phoneOrder = [...visibleMenus].sort((a, b) => {
|
|
8765
8908
|
const peso = (m) => m.id === "delivery" ? 1 : m.id === "retirada" ? 2 : 0;
|
|
@@ -9095,10 +9238,11 @@ function PosScreen({
|
|
|
9095
9238
|
onToggleTableInZone: pracaNaFicha && onTogglaMesaNaPraca ? (tableId, how) => onTogglaMesaNaPraca(pracaNaFicha, tableId, how) : void 0
|
|
9096
9239
|
}
|
|
9097
9240
|
),
|
|
9098
|
-
|
|
9099
|
-
|
|
9241
|
+
onCatalog && (loading || !catalogShown) && /* @__PURE__ */ jsx(CatalogSkeleton, {}),
|
|
9242
|
+
!loading && catalogKept && /* @__PURE__ */ jsx(
|
|
9100
9243
|
CatalogView,
|
|
9101
9244
|
{
|
|
9245
|
+
hidden: !(onCatalog && catalogShown),
|
|
9102
9246
|
categories,
|
|
9103
9247
|
items,
|
|
9104
9248
|
activeCategoryId,
|
|
@@ -9690,12 +9834,26 @@ function Dock({ positions, activeId, onSelect, now, onDiscard, paidByPosition })
|
|
|
9690
9834
|
useEffect(() => {
|
|
9691
9835
|
const el = activeRef.current;
|
|
9692
9836
|
if (!el) return;
|
|
9693
|
-
const
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9837
|
+
const go = () => {
|
|
9838
|
+
const still = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
9839
|
+
el.scrollIntoView?.({
|
|
9840
|
+
behavior: still ? "auto" : "smooth",
|
|
9841
|
+
block: "nearest",
|
|
9842
|
+
inline: "nearest"
|
|
9843
|
+
});
|
|
9844
|
+
};
|
|
9845
|
+
if (document.visibilityState !== "visible") {
|
|
9846
|
+
go();
|
|
9847
|
+
return;
|
|
9848
|
+
}
|
|
9849
|
+
let timer = 0;
|
|
9850
|
+
const frame = requestAnimationFrame(() => {
|
|
9851
|
+
timer = window.setTimeout(go, 0);
|
|
9698
9852
|
});
|
|
9853
|
+
return () => {
|
|
9854
|
+
cancelAnimationFrame(frame);
|
|
9855
|
+
clearTimeout(timer);
|
|
9856
|
+
};
|
|
9699
9857
|
}, [activeId]);
|
|
9700
9858
|
if (positions.length === 0) return null;
|
|
9701
9859
|
const rotulo = `${positions.length} comanda${positions.length === 1 ? "" : "s"} aberta${positions.length === 1 ? "" : "s"}`;
|
|
@@ -9874,7 +10032,7 @@ function DiscountSheet({
|
|
|
9874
10032
|
const [raw, setRaw] = useState("");
|
|
9875
10033
|
const [reasonId, setReasonId] = useState("");
|
|
9876
10034
|
const [authorizerId, setAuthorizerId] = useState("");
|
|
9877
|
-
const [
|
|
10035
|
+
const [memo3, setMemo] = useState("");
|
|
9878
10036
|
const comAbatimento = useMemo(
|
|
9879
10037
|
() => ticket.lines.filter((l) => (l.discountCents ?? 0) > 0),
|
|
9880
10038
|
[ticket.lines]
|
|
@@ -9888,7 +10046,7 @@ function DiscountSheet({
|
|
|
9888
10046
|
const reason = reasons.find((r) => r.id === reasonId);
|
|
9889
10047
|
const authorizer = authorizers.find((a) => a.id === authorizerId);
|
|
9890
10048
|
const percentEquivalente = base > 0 ? cents / base * 100 : 0;
|
|
9891
|
-
const impedimento = picked.size === 0 ? "Escolha ao menos um item." : value <= 0 ? "Informe o desconto." : mode === "percent" && value > 100 ? "Percentual n\xE3o pode ser maior que 100%." : cents > base ? "O desconto \xE9 maior que o valor dos itens escolhidos." : !authorizerId ? "Selecione o usu\xE1rio autorizador." : !reasonId && !
|
|
10049
|
+
const impedimento = picked.size === 0 ? "Escolha ao menos um item." : value <= 0 ? "Informe o desconto." : mode === "percent" && value > 100 ? "Percentual n\xE3o pode ser maior que 100%." : cents > base ? "O desconto \xE9 maior que o valor dos itens escolhidos." : !authorizerId ? "Selecione o usu\xE1rio autorizador." : !reasonId && !memo3.trim() ? "Selecione um motivo ou informe no memorando." : capRefusal(percentEquivalente, {
|
|
9892
10050
|
reasonMax: reason?.maxPercent,
|
|
9893
10051
|
authorizerMax: authorizer?.maxPercent,
|
|
9894
10052
|
authorizerName: authorizer?.name
|
|
@@ -10074,7 +10232,7 @@ function DiscountSheet({
|
|
|
10074
10232
|
"textarea",
|
|
10075
10233
|
{
|
|
10076
10234
|
className: "pos-dsc__memo",
|
|
10077
|
-
value:
|
|
10235
|
+
value: memo3,
|
|
10078
10236
|
onChange: (e) => setMemo(e.target.value),
|
|
10079
10237
|
placeholder: "Memorando",
|
|
10080
10238
|
rows: 2,
|
|
@@ -10096,7 +10254,7 @@ function DiscountSheet({
|
|
|
10096
10254
|
value,
|
|
10097
10255
|
authorizerId: authorizerId || void 0,
|
|
10098
10256
|
reasonId: reasonId || void 0,
|
|
10099
|
-
memo:
|
|
10257
|
+
memo: memo3.trim() || void 0
|
|
10100
10258
|
}),
|
|
10101
10259
|
children: [
|
|
10102
10260
|
/* @__PURE__ */ jsx(Tag, { size: 17, strokeWidth: 2.2, "aria-hidden": true }),
|
|
@@ -10566,7 +10724,7 @@ function CancelSheet({ ticket, party = [], reasons, error, onCancel, onConfirm }
|
|
|
10566
10724
|
const [qty, setQty] = useState({});
|
|
10567
10725
|
const [tudo, setTudo] = useState(false);
|
|
10568
10726
|
const [reasonId, setReasonId] = useState("");
|
|
10569
|
-
const [
|
|
10727
|
+
const [memo3, setMemo] = useState("");
|
|
10570
10728
|
const escolhidas = tudo ? enviados.map((l) => ({ lineId: l.id, quantity: l.quantity })) : enviados.map((l) => ({ lineId: l.id, quantity: qty[l.id] ?? 0 })).filter((x) => x.quantity > 0);
|
|
10571
10729
|
const totalCents = escolhidas.reduce((sum, x) => {
|
|
10572
10730
|
const linha = enviados.find((l) => l.id === x.lineId);
|
|
@@ -10577,7 +10735,7 @@ function CancelSheet({ ticket, party = [], reasons, error, onCancel, onConfirm }
|
|
|
10577
10735
|
[enviados, party]
|
|
10578
10736
|
);
|
|
10579
10737
|
const temGente = grupos.length > 1 || grupos.length === 1 && grupos[0].guestId !== null;
|
|
10580
|
-
const impedimento = enviados.length === 0 ? "Nenhum item enviado para cancelar." : escolhidas.length === 0 ? "Escolha quantas unidades cancelar." : !reasonId && !
|
|
10738
|
+
const impedimento = enviados.length === 0 ? "Nenhum item enviado para cancelar." : escolhidas.length === 0 ? "Escolha quantas unidades cancelar." : !reasonId && !memo3.trim() ? "Selecione um motivo ou informe no memorando." : void 0;
|
|
10581
10739
|
const linhaDoItem = (l) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "pos-svc__guest", children: [
|
|
10582
10740
|
/* @__PURE__ */ jsxs("span", { className: "pos-dsc__name", children: [
|
|
10583
10741
|
/* @__PURE__ */ jsxs("span", { className: "pos-dsc__n", children: [
|
|
@@ -10654,7 +10812,7 @@ function CancelSheet({ ticket, party = [], reasons, error, onCancel, onConfirm }
|
|
|
10654
10812
|
"textarea",
|
|
10655
10813
|
{
|
|
10656
10814
|
className: "pos-dsc__memo",
|
|
10657
|
-
value:
|
|
10815
|
+
value: memo3,
|
|
10658
10816
|
onChange: (e) => setMemo(e.target.value),
|
|
10659
10817
|
placeholder: "Memorando",
|
|
10660
10818
|
rows: 2,
|
|
@@ -10680,7 +10838,7 @@ function CancelSheet({ ticket, party = [], reasons, error, onCancel, onConfirm }
|
|
|
10680
10838
|
onClick: () => onConfirm({
|
|
10681
10839
|
quantities: escolhidas,
|
|
10682
10840
|
reasonId: reasonId || void 0,
|
|
10683
|
-
memo:
|
|
10841
|
+
memo: memo3.trim() || void 0
|
|
10684
10842
|
}),
|
|
10685
10843
|
children: "Cancelar itens"
|
|
10686
10844
|
}
|