@fayz-ai/plugin-menu 0.9.3 → 0.10.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/MenuPage-2LLWRJGN.js +1785 -0
- package/dist/MenuPage-2LLWRJGN.js.map +1 -0
- package/dist/chunk-C6W53TML.js +65 -0
- package/dist/chunk-C6W53TML.js.map +1 -0
- package/dist/components/DraftSectionCard.d.ts +18 -0
- package/dist/components/DraftSectionCard.d.ts.map +1 -0
- package/dist/components/ItemRailPage.d.ts +36 -0
- package/dist/components/ItemRailPage.d.ts.map +1 -0
- package/dist/components/MenuPreview.d.ts +17 -0
- package/dist/components/MenuPreview.d.ts.map +1 -0
- package/dist/components/MenuRailPage.d.ts +17 -0
- package/dist/components/MenuRailPage.d.ts.map +1 -0
- package/dist/components/ScheduleEditor.d.ts +8 -0
- package/dist/components/ScheduleEditor.d.ts.map +1 -0
- package/dist/components/SectionCard.d.ts +32 -0
- package/dist/components/SectionCard.d.ts.map +1 -0
- package/dist/data/legacy-adapter.d.ts +4 -0
- package/dist/data/legacy-adapter.d.ts.map +1 -0
- package/dist/data/mock.d.ts +2 -2
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/registries.d.ts.map +1 -1
- package/dist/data/supabase.d.ts +7 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/tables.d.ts +17 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/data/types.d.ts +36 -2
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2150 -356
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-surface.d.ts +46 -0
- package/dist/lib/agent-surface.d.ts.map +1 -0
- package/dist/lib/availability.d.ts +16 -0
- package/dist/lib/availability.d.ts.map +1 -0
- package/dist/lib/extract-parse.d.ts +16 -0
- package/dist/lib/extract-parse.d.ts.map +1 -0
- package/dist/lib/extract-transport.d.ts +10 -0
- package/dist/lib/extract-transport.d.ts.map +1 -0
- package/dist/lib/images.d.ts +7 -0
- package/dist/lib/images.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +8 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/store.d.ts +46 -10
- package/dist/store.d.ts.map +1 -1
- package/dist/types.d.ts +151 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/views/MenuManagerView.d.ts.map +1 -1
- package/package.json +15 -9
- package/dist/MenuPage-ZNKKQJKD.js +0 -523
- package/dist/MenuPage-ZNKKQJKD.js.map +0 -1
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
import { createPluginContext, formatCurrency, useLimitGuard, invalidateLimit } from '@fayz-ai/admin';
|
|
2
|
-
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
-
import { Loader2, Plus, Search, UtensilsCrossed, GripVertical, ChevronDown, ChevronRight, MoreHorizontal, Star } from 'lucide-react';
|
|
4
|
-
import { Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Input, ModalFooter, Button, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@fayz-ai/ui';
|
|
5
|
-
import { useTranslation } from '@fayz-ai/core';
|
|
6
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
|
|
8
|
-
// src/context.tsx
|
|
9
|
-
var ctx = createPluginContext("MenuPage");
|
|
10
|
-
var MenuContextProvider = ctx.ContextProvider;
|
|
11
|
-
var useMenuConfig = ctx.useConfig;
|
|
12
|
-
ctx.useProvider;
|
|
13
|
-
var useMenuStore = ctx.useStore;
|
|
14
|
-
var UNCATEGORIZED = "__none";
|
|
15
|
-
var fieldLabel = "text-sm font-medium text-foreground";
|
|
16
|
-
function CategoryModal({ open, onClose }) {
|
|
17
|
-
const t = useTranslation();
|
|
18
|
-
const createCategory = useMenuStore((s) => s.createCategory);
|
|
19
|
-
const [name, setName] = useState("");
|
|
20
|
-
const [saving, setSaving] = useState(false);
|
|
21
|
-
const submit = async () => {
|
|
22
|
-
if (!name.trim()) return;
|
|
23
|
-
setSaving(true);
|
|
24
|
-
try {
|
|
25
|
-
await createCategory({ name: name.trim() });
|
|
26
|
-
setName("");
|
|
27
|
-
onClose();
|
|
28
|
-
} catch {
|
|
29
|
-
} finally {
|
|
30
|
-
setSaving(false);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
|
|
34
|
-
if (!o) onClose();
|
|
35
|
-
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
|
|
36
|
-
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: t("menu.manager.addCategory") }) }),
|
|
37
|
-
/* @__PURE__ */ jsx(ModalBody, { className: "space-y-4", children: /* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
38
|
-
/* @__PURE__ */ jsx("span", { className: fieldLabel, children: t("menu.form.categoryName") }),
|
|
39
|
-
/* @__PURE__ */ jsx(
|
|
40
|
-
Input,
|
|
41
|
-
{
|
|
42
|
-
autoFocus: true,
|
|
43
|
-
value: name,
|
|
44
|
-
onChange: (e) => setName(e.target.value),
|
|
45
|
-
onKeyDown: (e) => {
|
|
46
|
-
if (e.key === "Enter") submit();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
] }) }),
|
|
51
|
-
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
52
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: t("menu.form.cancel") }),
|
|
53
|
-
/* @__PURE__ */ jsxs(Button, { onClick: submit, disabled: saving || !name.trim(), children: [
|
|
54
|
-
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }),
|
|
55
|
-
t("menu.form.save")
|
|
56
|
-
] })
|
|
57
|
-
] })
|
|
58
|
-
] }) });
|
|
59
|
-
}
|
|
60
|
-
function ItemModal({
|
|
61
|
-
open,
|
|
62
|
-
categories,
|
|
63
|
-
defaultCategoryId,
|
|
64
|
-
onClose
|
|
65
|
-
}) {
|
|
66
|
-
const t = useTranslation();
|
|
67
|
-
const config = useMenuConfig();
|
|
68
|
-
const createMenuItem = useMenuStore((s) => s.createMenuItem);
|
|
69
|
-
const guardMenuItems = useLimitGuard("menu_items");
|
|
70
|
-
const [name, setName] = useState("");
|
|
71
|
-
const [price, setPrice] = useState("");
|
|
72
|
-
const [categoryId, setCategoryId] = useState(defaultCategoryId ?? UNCATEGORIZED);
|
|
73
|
-
const [description, setDescription] = useState("");
|
|
74
|
-
const [saving, setSaving] = useState(false);
|
|
75
|
-
const submit = async () => {
|
|
76
|
-
if (!name.trim()) return;
|
|
77
|
-
if (await guardMenuItems() === "blocked") return;
|
|
78
|
-
setSaving(true);
|
|
79
|
-
try {
|
|
80
|
-
await createMenuItem({
|
|
81
|
-
name: name.trim(),
|
|
82
|
-
price: Number(price) || 0,
|
|
83
|
-
categoryId: categoryId === UNCATEGORIZED ? void 0 : categoryId,
|
|
84
|
-
description: description.trim() || void 0
|
|
85
|
-
});
|
|
86
|
-
invalidateLimit("menu_items");
|
|
87
|
-
onClose();
|
|
88
|
-
} catch {
|
|
89
|
-
} finally {
|
|
90
|
-
setSaving(false);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
|
|
94
|
-
if (!o) onClose();
|
|
95
|
-
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", children: [
|
|
96
|
-
/* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(ModalTitle, { children: t("menu.manager.addProduct") }) }),
|
|
97
|
-
/* @__PURE__ */ jsxs(ModalBody, { className: "space-y-4", children: [
|
|
98
|
-
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
99
|
-
/* @__PURE__ */ jsx("span", { className: fieldLabel, children: t("menu.form.itemName") }),
|
|
100
|
-
/* @__PURE__ */ jsx(Input, { autoFocus: true, value: name, onChange: (e) => setName(e.target.value) })
|
|
101
|
-
] }),
|
|
102
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
103
|
-
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
104
|
-
/* @__PURE__ */ jsxs("span", { className: fieldLabel, children: [
|
|
105
|
-
t("menu.manager.price"),
|
|
106
|
-
" (",
|
|
107
|
-
config.currency.symbol,
|
|
108
|
-
")"
|
|
109
|
-
] }),
|
|
110
|
-
/* @__PURE__ */ jsx(
|
|
111
|
-
Input,
|
|
112
|
-
{
|
|
113
|
-
type: "number",
|
|
114
|
-
min: 0,
|
|
115
|
-
step: "0.01",
|
|
116
|
-
value: price,
|
|
117
|
-
onChange: (e) => setPrice(e.target.value),
|
|
118
|
-
placeholder: "0"
|
|
119
|
-
}
|
|
120
|
-
)
|
|
121
|
-
] }),
|
|
122
|
-
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
123
|
-
/* @__PURE__ */ jsx("span", { className: fieldLabel, children: t("menu.form.category") }),
|
|
124
|
-
/* @__PURE__ */ jsxs(Select, { value: categoryId, onValueChange: setCategoryId, children: [
|
|
125
|
-
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
126
|
-
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
127
|
-
/* @__PURE__ */ jsx(SelectItem, { value: UNCATEGORIZED, children: t("menu.form.uncategorized") }),
|
|
128
|
-
categories.map((cat) => /* @__PURE__ */ jsx(SelectItem, { value: cat.id, children: cat.name }, cat.id))
|
|
129
|
-
] })
|
|
130
|
-
] })
|
|
131
|
-
] })
|
|
132
|
-
] }),
|
|
133
|
-
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5", children: [
|
|
134
|
-
/* @__PURE__ */ jsxs("span", { className: fieldLabel, children: [
|
|
135
|
-
t("menu.form.description"),
|
|
136
|
-
" ",
|
|
137
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground font-normal", children: [
|
|
138
|
-
"(",
|
|
139
|
-
t("menu.form.optional"),
|
|
140
|
-
")"
|
|
141
|
-
] })
|
|
142
|
-
] }),
|
|
143
|
-
/* @__PURE__ */ jsx(
|
|
144
|
-
"textarea",
|
|
145
|
-
{
|
|
146
|
-
value: description,
|
|
147
|
-
onChange: (e) => setDescription(e.target.value),
|
|
148
|
-
rows: 2,
|
|
149
|
-
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
150
|
-
}
|
|
151
|
-
)
|
|
152
|
-
] })
|
|
153
|
-
] }),
|
|
154
|
-
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
155
|
-
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, children: t("menu.form.cancel") }),
|
|
156
|
-
/* @__PURE__ */ jsxs(Button, { onClick: submit, disabled: saving || !name.trim(), children: [
|
|
157
|
-
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }),
|
|
158
|
-
t("menu.form.save")
|
|
159
|
-
] })
|
|
160
|
-
] })
|
|
161
|
-
] }) });
|
|
162
|
-
}
|
|
163
|
-
function Toggle({
|
|
164
|
-
checked,
|
|
165
|
-
onChange,
|
|
166
|
-
size = "sm"
|
|
167
|
-
}) {
|
|
168
|
-
const h = size === "xs" ? "h-4 w-7" : "h-5 w-9";
|
|
169
|
-
const dot = size === "xs" ? "h-3 w-3" : "h-4 w-4";
|
|
170
|
-
const translate = size === "xs" ? "translate-x-[13px]" : "translate-x-[18px]";
|
|
171
|
-
return /* @__PURE__ */ jsx(
|
|
172
|
-
"button",
|
|
173
|
-
{
|
|
174
|
-
type: "button",
|
|
175
|
-
onClick: () => onChange(!checked),
|
|
176
|
-
className: `relative inline-flex ${h} items-center rounded-full transition-colors ${checked ? "bg-primary" : "bg-muted-foreground/30"}`,
|
|
177
|
-
children: /* @__PURE__ */ jsx(
|
|
178
|
-
"span",
|
|
179
|
-
{
|
|
180
|
-
className: `pointer-events-none block ${dot} rounded-full bg-white shadow-sm transition-transform ${checked ? translate : "translate-x-[2px]"}`
|
|
181
|
-
}
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
function CategorySkeleton() {
|
|
187
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card animate-pulse", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
|
|
188
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded bg-muted" }),
|
|
189
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded bg-muted" }),
|
|
190
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-32 rounded bg-muted flex-1" }),
|
|
191
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-16 rounded bg-muted" })
|
|
192
|
-
] }) });
|
|
193
|
-
}
|
|
194
|
-
function CategorySection({
|
|
195
|
-
category,
|
|
196
|
-
items,
|
|
197
|
-
expanded,
|
|
198
|
-
onToggle,
|
|
199
|
-
onAddProduct
|
|
200
|
-
}) {
|
|
201
|
-
const t = useTranslation();
|
|
202
|
-
const config = useMenuConfig();
|
|
203
|
-
const toggleAvailability = useMenuStore((s) => s.toggleAvailability);
|
|
204
|
-
const handleToggle = async (id, field, current) => {
|
|
205
|
-
await toggleAvailability(id, field, !current);
|
|
206
|
-
};
|
|
207
|
-
const handleFeatureToggle = async (id, current) => {
|
|
208
|
-
await toggleAvailability(id, "isFeatured", !current);
|
|
209
|
-
};
|
|
210
|
-
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card", children: [
|
|
211
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [
|
|
212
|
-
/* @__PURE__ */ jsx(GripVertical, { className: "h-4 w-4 text-muted-foreground/40 cursor-grab" }),
|
|
213
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: onToggle, className: "p-0.5", children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) }),
|
|
214
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold flex-1", children: category.name }),
|
|
215
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 text-xs text-muted-foreground", children: /* @__PURE__ */ jsx("span", { children: t("menu.manager.itemsCount", { count: items.length }) }) }),
|
|
216
|
-
/* @__PURE__ */ jsx("button", { className: "p-1 text-muted-foreground hover:text-foreground rounded", children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4" }) })
|
|
217
|
-
] }),
|
|
218
|
-
expanded && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
219
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsxs(
|
|
220
|
-
"button",
|
|
221
|
-
{
|
|
222
|
-
type: "button",
|
|
223
|
-
onClick: onAddProduct,
|
|
224
|
-
className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground border border-dashed rounded-md px-3 py-1.5 hover:bg-muted/30 transition-colors",
|
|
225
|
-
children: [
|
|
226
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
|
|
227
|
-
t("menu.manager.addProduct")
|
|
228
|
-
]
|
|
229
|
-
}
|
|
230
|
-
) }),
|
|
231
|
-
items.length > 0 ? /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
|
|
232
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-t bg-muted/30 text-[10px] uppercase tracking-wider text-muted-foreground", children: [
|
|
233
|
-
/* @__PURE__ */ jsx("th", { className: "text-left py-2 pl-4 pr-2 font-medium", children: t("menu.manager.item") }),
|
|
234
|
-
/* @__PURE__ */ jsx("th", { className: "py-2 px-2 font-medium text-center w-16", children: t("menu.manager.pos") }),
|
|
235
|
-
/* @__PURE__ */ jsx("th", { className: "py-2 px-2 font-medium text-center w-16", children: t("menu.manager.delivery") }),
|
|
236
|
-
/* @__PURE__ */ jsx("th", { className: "py-2 px-2 font-medium text-center w-28", children: t("menu.manager.price") }),
|
|
237
|
-
config.modules.deliveryPricing && /* @__PURE__ */ jsx("th", { className: "py-2 px-2 font-medium text-center w-28", children: t("menu.manager.deliveryPrice") }),
|
|
238
|
-
/* @__PURE__ */ jsx("th", { className: "py-2 px-2 font-medium text-center w-16", children: t("menu.manager.featured") }),
|
|
239
|
-
/* @__PURE__ */ jsx("th", { className: "py-2 pr-4 w-8" })
|
|
240
|
-
] }) }),
|
|
241
|
-
/* @__PURE__ */ jsx("tbody", { className: "divide-y", children: items.map((item) => /* @__PURE__ */ jsxs(
|
|
242
|
-
"tr",
|
|
243
|
-
{
|
|
244
|
-
className: "hover:bg-muted/20 transition-colors",
|
|
245
|
-
children: [
|
|
246
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 pl-4 pr-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
247
|
-
/* @__PURE__ */ jsx(GripVertical, { className: "h-3.5 w-3.5 text-muted-foreground/30 cursor-grab shrink-0" }),
|
|
248
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted/50 overflow-hidden", children: item.imageUrl ? /* @__PURE__ */ jsx(
|
|
249
|
-
"img",
|
|
250
|
-
{
|
|
251
|
-
src: item.imageUrl,
|
|
252
|
-
alt: "",
|
|
253
|
-
className: "h-full w-full object-cover"
|
|
254
|
-
}
|
|
255
|
-
) : /* @__PURE__ */ jsx(UtensilsCrossed, { className: "h-4 w-4 text-muted-foreground/40" }) }),
|
|
256
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
257
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: item.name }),
|
|
258
|
-
item.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate max-w-[280px]", children: item.description })
|
|
259
|
-
] })
|
|
260
|
-
] }) }),
|
|
261
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 px-2 text-center", children: /* @__PURE__ */ jsx(
|
|
262
|
-
Toggle,
|
|
263
|
-
{
|
|
264
|
-
checked: item.availableForPos,
|
|
265
|
-
onChange: () => handleToggle(
|
|
266
|
-
item.id,
|
|
267
|
-
"availableForPos",
|
|
268
|
-
item.availableForPos
|
|
269
|
-
),
|
|
270
|
-
size: "xs"
|
|
271
|
-
}
|
|
272
|
-
) }),
|
|
273
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 px-2 text-center", children: /* @__PURE__ */ jsx(
|
|
274
|
-
Toggle,
|
|
275
|
-
{
|
|
276
|
-
checked: item.availableForDelivery,
|
|
277
|
-
onChange: () => handleToggle(
|
|
278
|
-
item.id,
|
|
279
|
-
"availableForDelivery",
|
|
280
|
-
item.availableForDelivery
|
|
281
|
-
),
|
|
282
|
-
size: "xs"
|
|
283
|
-
}
|
|
284
|
-
) }),
|
|
285
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 px-2 text-center", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: formatCurrency(item.price, config.currency) }) }),
|
|
286
|
-
config.modules.deliveryPricing && /* @__PURE__ */ jsx("td", { className: "py-2.5 px-2 text-center", children: /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: item.deliveryPrice != null ? formatCurrency(
|
|
287
|
-
item.deliveryPrice,
|
|
288
|
-
config.currency
|
|
289
|
-
) : "\u2014" }) }),
|
|
290
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 px-2 text-center", children: /* @__PURE__ */ jsx(
|
|
291
|
-
"button",
|
|
292
|
-
{
|
|
293
|
-
onClick: () => handleFeatureToggle(item.id, item.isFeatured),
|
|
294
|
-
className: `p-1 rounded ${item.isFeatured ? "text-amber-500" : "text-muted-foreground/30 hover:text-muted-foreground"}`,
|
|
295
|
-
children: /* @__PURE__ */ jsx(
|
|
296
|
-
Star,
|
|
297
|
-
{
|
|
298
|
-
className: `h-4 w-4 ${item.isFeatured ? "fill-current" : ""}`
|
|
299
|
-
}
|
|
300
|
-
)
|
|
301
|
-
}
|
|
302
|
-
) }),
|
|
303
|
-
/* @__PURE__ */ jsx("td", { className: "py-2.5 pr-4", children: /* @__PURE__ */ jsx("button", { className: "p-1 text-muted-foreground hover:text-foreground rounded", children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-3.5 w-3.5" }) }) })
|
|
304
|
-
]
|
|
305
|
-
},
|
|
306
|
-
item.id
|
|
307
|
-
)) })
|
|
308
|
-
] }) }) : /* @__PURE__ */ jsx("div", { className: "border-t px-4 py-6 text-center text-xs text-muted-foreground", children: t("menu.manager.noItems") })
|
|
309
|
-
] })
|
|
310
|
-
] });
|
|
311
|
-
}
|
|
312
|
-
function MenuManagerView() {
|
|
313
|
-
const t = useTranslation();
|
|
314
|
-
useMenuConfig();
|
|
315
|
-
const categories = useMenuStore((s) => s.categories);
|
|
316
|
-
const categoriesLoading = useMenuStore((s) => s.categoriesLoading);
|
|
317
|
-
const menuItems = useMenuStore((s) => s.menuItems);
|
|
318
|
-
const menuItemsLoading = useMenuStore((s) => s.menuItemsLoading);
|
|
319
|
-
const fetchCategories = useMenuStore((s) => s.fetchCategories);
|
|
320
|
-
const fetchMenuItems = useMenuStore((s) => s.fetchMenuItems);
|
|
321
|
-
const [expandedCategories, setExpandedCategories] = useState(
|
|
322
|
-
/* @__PURE__ */ new Set()
|
|
323
|
-
);
|
|
324
|
-
const [search, setSearch] = useState("");
|
|
325
|
-
const [categoryFilter, setCategoryFilter] = useState("all");
|
|
326
|
-
const [showCategoryModal, setShowCategoryModal] = useState(false);
|
|
327
|
-
const [showItemModal, setShowItemModal] = useState(false);
|
|
328
|
-
const [itemModalCategory, setItemModalCategory] = useState(void 0);
|
|
329
|
-
const openItemModal = (categoryId) => {
|
|
330
|
-
setItemModalCategory(categoryId);
|
|
331
|
-
setShowItemModal(true);
|
|
332
|
-
};
|
|
333
|
-
useEffect(() => {
|
|
334
|
-
fetchCategories();
|
|
335
|
-
fetchMenuItems({});
|
|
336
|
-
}, []);
|
|
337
|
-
useEffect(() => {
|
|
338
|
-
if (categories.length > 0 && expandedCategories.size === 0) {
|
|
339
|
-
setExpandedCategories(new Set(categories.map((c) => c.id)));
|
|
340
|
-
}
|
|
341
|
-
}, [categories]);
|
|
342
|
-
const grouped = useMemo(() => {
|
|
343
|
-
const map = /* @__PURE__ */ new Map();
|
|
344
|
-
for (const item of menuItems) {
|
|
345
|
-
const key = item.categoryId ?? "__uncategorized";
|
|
346
|
-
const list = map.get(key) ?? [];
|
|
347
|
-
list.push(item);
|
|
348
|
-
map.set(key, list);
|
|
349
|
-
}
|
|
350
|
-
return map;
|
|
351
|
-
}, [menuItems]);
|
|
352
|
-
const filteredCategories = useMemo(() => {
|
|
353
|
-
const lowerSearch = search.toLowerCase();
|
|
354
|
-
return categories.filter((cat) => {
|
|
355
|
-
if (categoryFilter !== "all" && cat.id !== categoryFilter) return false;
|
|
356
|
-
if (lowerSearch) {
|
|
357
|
-
const items = grouped.get(cat.id) ?? [];
|
|
358
|
-
return items.some(
|
|
359
|
-
(item) => item.name.toLowerCase().includes(lowerSearch) || (item.description?.toLowerCase().includes(lowerSearch) ?? false)
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
return true;
|
|
363
|
-
});
|
|
364
|
-
}, [categories, categoryFilter, search, grouped]);
|
|
365
|
-
const getFilteredItems = (categoryId) => {
|
|
366
|
-
const items = grouped.get(categoryId) ?? [];
|
|
367
|
-
if (!search) return items;
|
|
368
|
-
const lowerSearch = search.toLowerCase();
|
|
369
|
-
return items.filter(
|
|
370
|
-
(item) => item.name.toLowerCase().includes(lowerSearch) || (item.description?.toLowerCase().includes(lowerSearch) ?? false)
|
|
371
|
-
);
|
|
372
|
-
};
|
|
373
|
-
const toggleCategory = (id) => {
|
|
374
|
-
setExpandedCategories((prev) => {
|
|
375
|
-
const next = new Set(prev);
|
|
376
|
-
next.has(id) ? next.delete(id) : next.add(id);
|
|
377
|
-
return next;
|
|
378
|
-
});
|
|
379
|
-
};
|
|
380
|
-
const isLoading = categoriesLoading || menuItemsLoading;
|
|
381
|
-
if (isLoading && categories.length === 0) {
|
|
382
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
383
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
|
|
384
|
-
/* @__PURE__ */ jsx("div", { className: "h-7 w-40 rounded bg-muted animate-pulse" }),
|
|
385
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-64 rounded bg-muted animate-pulse mt-2" })
|
|
386
|
-
] }) }),
|
|
387
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
388
|
-
/* @__PURE__ */ jsx("div", { className: "h-9 w-40 rounded bg-muted animate-pulse" }),
|
|
389
|
-
/* @__PURE__ */ jsx("div", { className: "h-9 flex-1 max-w-sm rounded bg-muted animate-pulse" })
|
|
390
|
-
] }),
|
|
391
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
392
|
-
/* @__PURE__ */ jsx(CategorySkeleton, {}),
|
|
393
|
-
/* @__PURE__ */ jsx(CategorySkeleton, {}),
|
|
394
|
-
/* @__PURE__ */ jsx(CategorySkeleton, {})
|
|
395
|
-
] })
|
|
396
|
-
] });
|
|
397
|
-
}
|
|
398
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
399
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
400
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
401
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: t("menu.manager.title") }),
|
|
402
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("menu.manager.subtitle") })
|
|
403
|
-
] }),
|
|
404
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
405
|
-
isLoading && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }),
|
|
406
|
-
/* @__PURE__ */ jsxs(
|
|
407
|
-
"button",
|
|
408
|
-
{
|
|
409
|
-
type: "button",
|
|
410
|
-
onClick: () => setShowCategoryModal(true),
|
|
411
|
-
className: "inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted/50 transition-colors",
|
|
412
|
-
children: [
|
|
413
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
414
|
-
t("menu.manager.addCategory")
|
|
415
|
-
]
|
|
416
|
-
}
|
|
417
|
-
),
|
|
418
|
-
/* @__PURE__ */ jsxs(
|
|
419
|
-
"button",
|
|
420
|
-
{
|
|
421
|
-
type: "button",
|
|
422
|
-
onClick: () => openItemModal(void 0),
|
|
423
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
424
|
-
children: [
|
|
425
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
426
|
-
t("menu.manager.addProduct")
|
|
427
|
-
]
|
|
428
|
-
}
|
|
429
|
-
)
|
|
430
|
-
] })
|
|
431
|
-
] }),
|
|
432
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
433
|
-
/* @__PURE__ */ jsxs(
|
|
434
|
-
"select",
|
|
435
|
-
{
|
|
436
|
-
value: categoryFilter,
|
|
437
|
-
onChange: (e) => setCategoryFilter(e.target.value),
|
|
438
|
-
className: "h-9 rounded-md border border-input bg-background px-3 text-sm",
|
|
439
|
-
children: [
|
|
440
|
-
/* @__PURE__ */ jsx("option", { value: "all", children: t("menu.manager.allCategories") }),
|
|
441
|
-
categories.map((cat) => /* @__PURE__ */ jsx("option", { value: cat.id, children: cat.name }, cat.id))
|
|
442
|
-
]
|
|
443
|
-
}
|
|
444
|
-
),
|
|
445
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex-1 max-w-sm", children: [
|
|
446
|
-
/* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
|
|
447
|
-
/* @__PURE__ */ jsx(
|
|
448
|
-
"input",
|
|
449
|
-
{
|
|
450
|
-
type: "text",
|
|
451
|
-
value: search,
|
|
452
|
-
onChange: (e) => setSearch(e.target.value),
|
|
453
|
-
placeholder: t("menu.manager.search"),
|
|
454
|
-
className: "h-9 w-full rounded-md border border-input bg-background pl-9 pr-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
455
|
-
}
|
|
456
|
-
)
|
|
457
|
-
] })
|
|
458
|
-
] }),
|
|
459
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: filteredCategories.map((category) => /* @__PURE__ */ jsx(
|
|
460
|
-
CategorySection,
|
|
461
|
-
{
|
|
462
|
-
category,
|
|
463
|
-
items: getFilteredItems(category.id),
|
|
464
|
-
expanded: expandedCategories.has(category.id),
|
|
465
|
-
onToggle: () => toggleCategory(category.id),
|
|
466
|
-
onAddProduct: () => openItemModal(category.id)
|
|
467
|
-
},
|
|
468
|
-
category.id
|
|
469
|
-
)) }),
|
|
470
|
-
filteredCategories.length === 0 && !isLoading && /* @__PURE__ */ jsx("div", { className: "text-center py-12", children: categories.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
471
|
-
/* @__PURE__ */ jsx(UtensilsCrossed, { className: "mx-auto h-10 w-10 text-muted-foreground/40 mb-3" }),
|
|
472
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("menu.manager.noItems") }),
|
|
473
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t("menu.manager.noItemsDesc") }),
|
|
474
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-center gap-2", children: [
|
|
475
|
-
/* @__PURE__ */ jsxs(
|
|
476
|
-
"button",
|
|
477
|
-
{
|
|
478
|
-
type: "button",
|
|
479
|
-
onClick: () => setShowCategoryModal(true),
|
|
480
|
-
className: "inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted/50",
|
|
481
|
-
children: [
|
|
482
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
483
|
-
t("menu.manager.addCategory")
|
|
484
|
-
]
|
|
485
|
-
}
|
|
486
|
-
),
|
|
487
|
-
/* @__PURE__ */ jsxs(
|
|
488
|
-
"button",
|
|
489
|
-
{
|
|
490
|
-
type: "button",
|
|
491
|
-
onClick: () => openItemModal(void 0),
|
|
492
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
493
|
-
children: [
|
|
494
|
-
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
|
|
495
|
-
t("menu.manager.addProduct")
|
|
496
|
-
]
|
|
497
|
-
}
|
|
498
|
-
)
|
|
499
|
-
] })
|
|
500
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
501
|
-
/* @__PURE__ */ jsx(Search, { className: "mx-auto h-10 w-10 text-muted-foreground/40 mb-3" }),
|
|
502
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("menu.manager.noResults") }),
|
|
503
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t("menu.manager.noResultsDesc") })
|
|
504
|
-
] }) }),
|
|
505
|
-
showCategoryModal && /* @__PURE__ */ jsx(CategoryModal, { open: true, onClose: () => setShowCategoryModal(false) }),
|
|
506
|
-
showItemModal && /* @__PURE__ */ jsx(
|
|
507
|
-
ItemModal,
|
|
508
|
-
{
|
|
509
|
-
open: true,
|
|
510
|
-
categories,
|
|
511
|
-
defaultCategoryId: itemModalCategory,
|
|
512
|
-
onClose: () => setShowItemModal(false)
|
|
513
|
-
}
|
|
514
|
-
)
|
|
515
|
-
] });
|
|
516
|
-
}
|
|
517
|
-
function MenuPage({ config, provider, store, registries }) {
|
|
518
|
-
return /* @__PURE__ */ jsx(MenuContextProvider, { config, provider, store, children: /* @__PURE__ */ jsx(MenuManagerView, {}) });
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
export { MenuPage };
|
|
522
|
-
//# sourceMappingURL=MenuPage-ZNKKQJKD.js.map
|
|
523
|
-
//# sourceMappingURL=MenuPage-ZNKKQJKD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/views/MenuManagerView.tsx","../src/views/MenuPage.tsx"],"names":["jsx"],"mappings":"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","file":"MenuPage-ZNKKQJKD.js","sourcesContent":["import { createPluginContext, formatCurrency, type CurrencyConfig } from '@fayz-ai/admin'\nimport type { MenuDataProvider } from './data/types'\nimport type { MenuUIState } from './store'\n\n// ---------------------------------------------------------------------------\n// Labels\n// ---------------------------------------------------------------------------\n\nexport interface MenuPluginLabels {\n pageTitle: string\n menuManager: string\n categories: string\n newItem: string\n}\n\n// ---------------------------------------------------------------------------\n// Resolved config — fully merged, no optionals\n// ---------------------------------------------------------------------------\n\nexport interface MenuModules {\n modifiers: boolean\n deliveryPricing: boolean\n}\n\nexport type MenuCurrency = CurrencyConfig\n\nexport interface ResolvedMenuConfig {\n modules: MenuModules\n labels: MenuPluginLabels\n currency: MenuCurrency\n}\n\nconst ctx = createPluginContext<ResolvedMenuConfig, MenuDataProvider, MenuUIState>('MenuPage')\n\nexport const MenuContextProvider = ctx.ContextProvider\nexport const useMenuConfig = ctx.useConfig\nexport const useMenuProvider = ctx.useProvider\nexport const useMenuStore = ctx.useStore\n\nexport { formatCurrency }\n","import React, { useEffect, useMemo, useState } from 'react'\nimport {\n Plus,\n Search,\n GripVertical,\n ChevronDown,\n ChevronRight,\n MoreHorizontal,\n Star,\n Loader2,\n UtensilsCrossed,\n} from 'lucide-react'\nimport {\n Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter,\n Button, Input,\n Select, SelectTrigger, SelectValue, SelectContent, SelectItem,\n} from '@fayz-ai/ui'\nimport { useMenuConfig, useMenuStore, formatCurrency } from '../context'\nimport { useTranslation } from '@fayz-ai/core'\nimport { useLimitGuard, invalidateLimit } from '@fayz-ai/admin'\nimport type { MenuCategory, MenuItem } from '../types'\n\n// ---------------------------------------------------------------------------\n// Create forms (category / item) — design-system Modal\n// ---------------------------------------------------------------------------\n\nconst UNCATEGORIZED = '__none'\nconst fieldLabel = 'text-sm font-medium text-foreground'\n\nfunction CategoryModal({ open, onClose }: { open: boolean; onClose: () => void }) {\n const t = useTranslation()\n const createCategory = useMenuStore((s) => s.createCategory)\n const [name, setName] = useState('')\n const [saving, setSaving] = useState(false)\n\n const submit = async () => {\n if (!name.trim()) return\n setSaving(true)\n try {\n await createCategory({ name: name.trim() })\n setName('')\n onClose()\n } catch {\n // store surfaces a toast on failure\n } finally {\n setSaving(false)\n }\n }\n\n return (\n <Modal open={open} onOpenChange={(o) => { if (!o) onClose() }}>\n <ModalContent size=\"sm\">\n <ModalHeader>\n <ModalTitle>{t('menu.manager.addCategory')}</ModalTitle>\n </ModalHeader>\n <ModalBody className=\"space-y-4\">\n <label className=\"block space-y-1.5\">\n <span className={fieldLabel}>{t('menu.form.categoryName')}</span>\n <Input\n autoFocus\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') submit() }}\n />\n </label>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"outline\" onClick={onClose}>{t('menu.form.cancel')}</Button>\n <Button onClick={submit} disabled={saving || !name.trim()}>\n {saving && <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" />}\n {t('menu.form.save')}\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n )\n}\n\nfunction ItemModal({\n open,\n categories,\n defaultCategoryId,\n onClose,\n}: {\n open: boolean\n categories: MenuCategory[]\n defaultCategoryId?: string\n onClose: () => void\n}) {\n const t = useTranslation()\n const config = useMenuConfig()\n const createMenuItem = useMenuStore((s) => s.createMenuItem)\n const guardMenuItems = useLimitGuard('menu_items')\n const [name, setName] = useState('')\n const [price, setPrice] = useState('')\n const [categoryId, setCategoryId] = useState(defaultCategoryId ?? UNCATEGORIZED)\n const [description, setDescription] = useState('')\n const [saving, setSaving] = useState(false)\n\n const submit = async () => {\n if (!name.trim()) return\n // Client-side plan guard before the store create. (Note: this dialog also\n // has the known board bug B29 — out of scope here, guard only.)\n if ((await guardMenuItems()) === 'blocked') return\n setSaving(true)\n try {\n await createMenuItem({\n name: name.trim(),\n price: Number(price) || 0,\n categoryId: categoryId === UNCATEGORIZED ? undefined : categoryId,\n description: description.trim() || undefined,\n })\n invalidateLimit('menu_items')\n onClose()\n } catch {\n // store surfaces a toast on failure\n } finally {\n setSaving(false)\n }\n }\n\n return (\n <Modal open={open} onOpenChange={(o) => { if (!o) onClose() }}>\n <ModalContent size=\"md\">\n <ModalHeader>\n <ModalTitle>{t('menu.manager.addProduct')}</ModalTitle>\n </ModalHeader>\n <ModalBody className=\"space-y-4\">\n <label className=\"block space-y-1.5\">\n <span className={fieldLabel}>{t('menu.form.itemName')}</span>\n <Input autoFocus value={name} onChange={(e) => setName(e.target.value)} />\n </label>\n <div className=\"grid grid-cols-2 gap-3\">\n <label className=\"block space-y-1.5\">\n <span className={fieldLabel}>{t('menu.manager.price')} ({config.currency.symbol})</span>\n <Input\n type=\"number\"\n min={0}\n step=\"0.01\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n placeholder=\"0\"\n />\n </label>\n <label className=\"block space-y-1.5\">\n <span className={fieldLabel}>{t('menu.form.category')}</span>\n <Select value={categoryId} onValueChange={setCategoryId}>\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value={UNCATEGORIZED}>{t('menu.form.uncategorized')}</SelectItem>\n {categories.map((cat) => (\n <SelectItem key={cat.id} value={cat.id}>{cat.name}</SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n <label className=\"block space-y-1.5\">\n <span className={fieldLabel}>\n {t('menu.form.description')}{' '}\n <span className=\"text-muted-foreground font-normal\">({t('menu.form.optional')})</span>\n </span>\n <textarea\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className=\"w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n </label>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"outline\" onClick={onClose}>{t('menu.form.cancel')}</Button>\n <Button onClick={submit} disabled={saving || !name.trim()}>\n {saving && <Loader2 className=\"mr-1.5 h-4 w-4 animate-spin\" />}\n {t('menu.form.save')}\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Toggle — inline switch component\n// ---------------------------------------------------------------------------\n\nfunction Toggle({\n checked,\n onChange,\n size = 'sm',\n}: {\n checked: boolean\n onChange: (v: boolean) => void\n size?: 'sm' | 'xs'\n}) {\n const h = size === 'xs' ? 'h-4 w-7' : 'h-5 w-9'\n const dot = size === 'xs' ? 'h-3 w-3' : 'h-4 w-4'\n const translate = size === 'xs' ? 'translate-x-[13px]' : 'translate-x-[18px]'\n return (\n <button\n type=\"button\"\n onClick={() => onChange(!checked)}\n className={`relative inline-flex ${h} items-center rounded-full transition-colors ${\n checked ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`pointer-events-none block ${dot} rounded-full bg-white shadow-sm transition-transform ${\n checked ? translate : 'translate-x-[2px]'\n }`}\n />\n </button>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Skeleton loader\n// ---------------------------------------------------------------------------\n\nfunction CategorySkeleton() {\n return (\n <div className=\"rounded-lg border bg-card animate-pulse\">\n <div className=\"flex items-center gap-3 px-4 py-3\">\n <div className=\"h-4 w-4 rounded bg-muted\" />\n <div className=\"h-4 w-4 rounded bg-muted\" />\n <div className=\"h-4 w-32 rounded bg-muted flex-1\" />\n <div className=\"h-4 w-16 rounded bg-muted\" />\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Category section\n// ---------------------------------------------------------------------------\n\nfunction CategorySection({\n category,\n items,\n expanded,\n onToggle,\n onAddProduct,\n}: {\n category: MenuCategory\n items: MenuItem[]\n expanded: boolean\n onToggle: () => void\n onAddProduct: () => void\n}) {\n const t = useTranslation()\n const config = useMenuConfig()\n const toggleAvailability = useMenuStore((s) => s.toggleAvailability)\n\n const handleToggle = async (\n id: string,\n field: 'availableForPos' | 'availableForDelivery',\n current: boolean,\n ) => {\n await toggleAvailability(id, field, !current)\n }\n\n const handleFeatureToggle = async (id: string, current: boolean) => {\n await toggleAvailability(id, 'isFeatured' as any, !current)\n }\n\n return (\n <div className=\"rounded-lg border bg-card\">\n {/* Category header */}\n <div className=\"flex items-center gap-3 px-4 py-3\">\n <GripVertical className=\"h-4 w-4 text-muted-foreground/40 cursor-grab\" />\n <button type=\"button\" onClick={onToggle} className=\"p-0.5\">\n {expanded ? (\n <ChevronDown className=\"h-4 w-4\" />\n ) : (\n <ChevronRight className=\"h-4 w-4\" />\n )}\n </button>\n <h3 className=\"text-sm font-semibold flex-1\">{category.name}</h3>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>{t('menu.manager.itemsCount', { count: items.length })}</span>\n </div>\n <button className=\"p-1 text-muted-foreground hover:text-foreground rounded\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </div>\n\n {expanded && (\n <>\n {/* Add product button */}\n <div className=\"px-4 pb-2\">\n <button\n type=\"button\"\n onClick={onAddProduct}\n className=\"flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground border border-dashed rounded-md px-3 py-1.5 hover:bg-muted/30 transition-colors\"\n >\n <Plus className=\"h-3 w-3\" />\n {t('menu.manager.addProduct')}\n </button>\n </div>\n\n {/* Items table */}\n {/* drift-allow: raw-table — interactive cells (drag handles, availability toggles, inline price/feature controls) don't fit DataTable */}\n {items.length > 0 ? (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-t bg-muted/30 text-[10px] uppercase tracking-wider text-muted-foreground\">\n <th className=\"text-left py-2 pl-4 pr-2 font-medium\">\n {t('menu.manager.item')}\n </th>\n <th className=\"py-2 px-2 font-medium text-center w-16\">\n {t('menu.manager.pos')}\n </th>\n <th className=\"py-2 px-2 font-medium text-center w-16\">\n {t('menu.manager.delivery')}\n </th>\n <th className=\"py-2 px-2 font-medium text-center w-28\">\n {t('menu.manager.price')}\n </th>\n {config.modules.deliveryPricing && (\n <th className=\"py-2 px-2 font-medium text-center w-28\">\n {t('menu.manager.deliveryPrice')}\n </th>\n )}\n <th className=\"py-2 px-2 font-medium text-center w-16\">\n {t('menu.manager.featured')}\n </th>\n <th className=\"py-2 pr-4 w-8\" />\n </tr>\n </thead>\n <tbody className=\"divide-y\">\n {items.map((item) => (\n <tr\n key={item.id}\n className=\"hover:bg-muted/20 transition-colors\"\n >\n <td className=\"py-2.5 pl-4 pr-2\">\n <div className=\"flex items-center gap-3\">\n <GripVertical className=\"h-3.5 w-3.5 text-muted-foreground/30 cursor-grab shrink-0\" />\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted/50 overflow-hidden\">\n {item.imageUrl ? (\n <img\n src={item.imageUrl}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <UtensilsCrossed className=\"h-4 w-4 text-muted-foreground/40\" />\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">\n {item.name}\n </p>\n {item.description && (\n <p className=\"text-xs text-muted-foreground truncate max-w-[280px]\">\n {item.description}\n </p>\n )}\n </div>\n </div>\n </td>\n <td className=\"py-2.5 px-2 text-center\">\n <Toggle\n checked={item.availableForPos}\n onChange={() =>\n handleToggle(\n item.id,\n 'availableForPos',\n item.availableForPos,\n )\n }\n size=\"xs\"\n />\n </td>\n <td className=\"py-2.5 px-2 text-center\">\n <Toggle\n checked={item.availableForDelivery}\n onChange={() =>\n handleToggle(\n item.id,\n 'availableForDelivery',\n item.availableForDelivery,\n )\n }\n size=\"xs\"\n />\n </td>\n <td className=\"py-2.5 px-2 text-center\">\n <span className=\"text-sm font-medium\">\n {formatCurrency(item.price, config.currency)}\n </span>\n </td>\n {config.modules.deliveryPricing && (\n <td className=\"py-2.5 px-2 text-center\">\n <span className=\"text-sm text-muted-foreground\">\n {item.deliveryPrice != null\n ? formatCurrency(\n item.deliveryPrice,\n config.currency,\n )\n : '\\u2014'}\n </span>\n </td>\n )}\n <td className=\"py-2.5 px-2 text-center\">\n <button\n onClick={() =>\n handleFeatureToggle(item.id, item.isFeatured)\n }\n className={`p-1 rounded ${\n item.isFeatured\n ? 'text-amber-500'\n : 'text-muted-foreground/30 hover:text-muted-foreground'\n }`}\n >\n <Star\n className={`h-4 w-4 ${\n item.isFeatured ? 'fill-current' : ''\n }`}\n />\n </button>\n </td>\n <td className=\"py-2.5 pr-4\">\n <button className=\"p-1 text-muted-foreground hover:text-foreground rounded\">\n <MoreHorizontal className=\"h-3.5 w-3.5\" />\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <div className=\"border-t px-4 py-6 text-center text-xs text-muted-foreground\">\n {t('menu.manager.noItems')}\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Main view\n// ---------------------------------------------------------------------------\n\nexport function MenuManagerView() {\n const t = useTranslation()\n const config = useMenuConfig()\n\n const categories = useMenuStore((s) => s.categories)\n const categoriesLoading = useMenuStore((s) => s.categoriesLoading)\n const menuItems = useMenuStore((s) => s.menuItems)\n const menuItemsLoading = useMenuStore((s) => s.menuItemsLoading)\n const fetchCategories = useMenuStore((s) => s.fetchCategories)\n const fetchMenuItems = useMenuStore((s) => s.fetchMenuItems)\n\n const [expandedCategories, setExpandedCategories] = useState<Set<string>>(\n new Set(),\n )\n const [search, setSearch] = useState('')\n const [categoryFilter, setCategoryFilter] = useState('all')\n const [showCategoryModal, setShowCategoryModal] = useState(false)\n const [showItemModal, setShowItemModal] = useState(false)\n const [itemModalCategory, setItemModalCategory] = useState<string | undefined>(undefined)\n\n const openItemModal = (categoryId?: string) => {\n setItemModalCategory(categoryId)\n setShowItemModal(true)\n }\n\n // ---- Load data on mount ----\n useEffect(() => {\n fetchCategories()\n fetchMenuItems({})\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // ---- Expand all once categories load ----\n useEffect(() => {\n if (categories.length > 0 && expandedCategories.size === 0) {\n setExpandedCategories(new Set(categories.map((c) => c.id)))\n }\n }, [categories]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // ---- Group items by categoryId ----\n const grouped = useMemo(() => {\n const map = new Map<string, MenuItem[]>()\n for (const item of menuItems) {\n const key = item.categoryId ?? '__uncategorized'\n const list = map.get(key) ?? []\n list.push(item)\n map.set(key, list)\n }\n return map\n }, [menuItems])\n\n // ---- Client-side filtering ----\n const filteredCategories = useMemo(() => {\n const lowerSearch = search.toLowerCase()\n\n return categories.filter((cat) => {\n // Category dropdown filter\n if (categoryFilter !== 'all' && cat.id !== categoryFilter) return false\n\n // Search filter — match category items by name or description\n if (lowerSearch) {\n const items = grouped.get(cat.id) ?? []\n return items.some(\n (item) =>\n item.name.toLowerCase().includes(lowerSearch) ||\n (item.description?.toLowerCase().includes(lowerSearch) ?? false),\n )\n }\n return true\n })\n }, [categories, categoryFilter, search, grouped])\n\n // Get filtered items for a category (applying search within it)\n const getFilteredItems = (categoryId: string): MenuItem[] => {\n const items = grouped.get(categoryId) ?? []\n if (!search) return items\n const lowerSearch = search.toLowerCase()\n return items.filter(\n (item) =>\n item.name.toLowerCase().includes(lowerSearch) ||\n (item.description?.toLowerCase().includes(lowerSearch) ?? false),\n )\n }\n\n const toggleCategory = (id: string) => {\n setExpandedCategories((prev) => {\n const next = new Set(prev)\n next.has(id) ? next.delete(id) : next.add(id)\n return next\n })\n }\n\n const isLoading = categoriesLoading || menuItemsLoading\n\n // ---- Loading skeleton ----\n if (isLoading && categories.length === 0) {\n return (\n <div className=\"space-y-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"h-7 w-40 rounded bg-muted animate-pulse\" />\n <div className=\"h-4 w-64 rounded bg-muted animate-pulse mt-2\" />\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n <div className=\"h-9 w-40 rounded bg-muted animate-pulse\" />\n <div className=\"h-9 flex-1 max-w-sm rounded bg-muted animate-pulse\" />\n </div>\n <div className=\"space-y-3\">\n <CategorySkeleton />\n <CategorySkeleton />\n <CategorySkeleton />\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold\">{t('menu.manager.title')}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('menu.manager.subtitle')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {isLoading && (\n <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />\n )}\n <button\n type=\"button\"\n onClick={() => setShowCategoryModal(true)}\n className=\"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted/50 transition-colors\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('menu.manager.addCategory')}\n </button>\n <button\n type=\"button\"\n onClick={() => openItemModal(undefined)}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('menu.manager.addProduct')}\n </button>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"flex items-center gap-3\">\n <select\n value={categoryFilter}\n onChange={(e) => setCategoryFilter(e.target.value)}\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm\"\n >\n <option value=\"all\">{t('menu.manager.allCategories')}</option>\n {categories.map((cat) => (\n <option key={cat.id} value={cat.id}>\n {cat.name}\n </option>\n ))}\n </select>\n <div className=\"relative flex-1 max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <input\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t('menu.manager.search')}\n className=\"h-9 w-full rounded-md border border-input bg-background pl-9 pr-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n />\n </div>\n </div>\n\n {/* Categories */}\n <div className=\"space-y-3\">\n {filteredCategories.map((category) => (\n <CategorySection\n key={category.id}\n category={category}\n items={getFilteredItems(category.id)}\n expanded={expandedCategories.has(category.id)}\n onToggle={() => toggleCategory(category.id)}\n onAddProduct={() => openItemModal(category.id)}\n />\n ))}\n </div>\n\n {/* Empty state — no results after filtering */}\n {filteredCategories.length === 0 && !isLoading && (\n <div className=\"text-center py-12\">\n {categories.length === 0 ? (\n <>\n <UtensilsCrossed className=\"mx-auto h-10 w-10 text-muted-foreground/40 mb-3\" />\n <p className=\"text-sm font-medium\">{t('menu.manager.noItems')}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('menu.manager.noItemsDesc')}\n </p>\n <div className=\"mt-4 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowCategoryModal(true)}\n className=\"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted/50\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('menu.manager.addCategory')}\n </button>\n <button\n type=\"button\"\n onClick={() => openItemModal(undefined)}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('menu.manager.addProduct')}\n </button>\n </div>\n </>\n ) : (\n <>\n <Search className=\"mx-auto h-10 w-10 text-muted-foreground/40 mb-3\" />\n <p className=\"text-sm font-medium\">\n {t('menu.manager.noResults')}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('menu.manager.noResultsDesc')}\n </p>\n </>\n )}\n </div>\n )}\n\n {showCategoryModal && (\n <CategoryModal open onClose={() => setShowCategoryModal(false)} />\n )}\n {showItemModal && (\n <ItemModal\n open\n categories={categories}\n defaultCategoryId={itemModalCategory}\n onClose={() => setShowItemModal(false)}\n />\n )}\n </div>\n )\n}\n","import React from 'react'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { MenuContextProvider, type ResolvedMenuConfig } from '../context'\nimport type { MenuDataProvider } from '../data/types'\nimport type { MenuUIState } from '../store'\nimport type { PluginRegistryDef } from '@fayz-ai/core'\n\nimport { MenuManagerView } from './MenuManagerView'\n\nexport function MenuPage({ config, provider, store, registries }: {\n config: ResolvedMenuConfig\n provider: MenuDataProvider\n store: StoreApi<MenuUIState>\n registries?: PluginRegistryDef[]\n}) {\n return (\n <MenuContextProvider config={config} provider={provider} store={store}>\n <MenuManagerView />\n </MenuContextProvider>\n )\n}\n"]}
|