restaurant-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +488 -0
  3. package/dist/RestaurantSiteRoutes.js +23 -0
  4. package/dist/admin/AdminActionMenu.js +29 -0
  5. package/dist/admin/AdminAuthPanel.js +53 -0
  6. package/dist/admin/AdminBackupsDialog.js +54 -0
  7. package/dist/admin/AdminDraftProvider.js +10 -0
  8. package/dist/admin/AdminGroupsPanel.js +68 -0
  9. package/dist/admin/AdminItemDrawer.js +264 -0
  10. package/dist/admin/AdminMenuCard.js +63 -0
  11. package/dist/admin/AdminMenuEditor.module.css.js +209 -0
  12. package/dist/admin/AdminMenuGroupTabs.js +43 -0
  13. package/dist/admin/AdminMenuIconPicker.js +39 -0
  14. package/dist/admin/AdminRoutes.js +13 -0
  15. package/dist/admin/AdminSectionEditor.js +104 -0
  16. package/dist/admin/AdminTestimonialsPanel.js +214 -0
  17. package/dist/admin/AdminTestimonialsPanel.module.css.js +29 -0
  18. package/dist/admin/adminDraftContext.js +5 -0
  19. package/dist/admin/adminEditorUtils.js +47 -0
  20. package/dist/admin/adminMenuCommands.js +83 -0
  21. package/dist/admin/useAdminDraft.js +14 -0
  22. package/dist/admin/useAdminMenuDraft.js +226 -0
  23. package/dist/cart/CartDrawer.js +87 -0
  24. package/dist/cart/CartDrawer.module.css.js +47 -0
  25. package/dist/cart/CartProvider.js +31 -0
  26. package/dist/cart/cartContext.js +5 -0
  27. package/dist/cart/cartOrder.js +34 -0
  28. package/dist/cart/cartReducer.js +57 -0
  29. package/dist/cart/useCart.js +10 -0
  30. package/dist/components/Brand.js +16 -0
  31. package/dist/components/Brand.module.css.js +15 -0
  32. package/dist/components/Footer.js +19 -0
  33. package/dist/components/Footer.module.css.js +11 -0
  34. package/dist/components/Header.js +34 -0
  35. package/dist/components/Header.module.css.js +19 -0
  36. package/dist/components/Hero.js +23 -0
  37. package/dist/components/Hero.module.css.js +15 -0
  38. package/dist/components/InstagramIcon.js +27 -0
  39. package/dist/components/LocationsSection.js +34 -0
  40. package/dist/components/LocationsSection.module.css.js +17 -0
  41. package/dist/components/Marquee.js +10 -0
  42. package/dist/components/Marquee.module.css.js +9 -0
  43. package/dist/components/OrderIcon.js +11 -0
  44. package/dist/components/RatingSection.js +32 -0
  45. package/dist/components/RatingSection.module.css.js +19 -0
  46. package/dist/components/TestimonialsSection.js +73 -0
  47. package/dist/components/TestimonialsSection.module.css.js +37 -0
  48. package/dist/components/WhatsAppIcon.js +13 -0
  49. package/dist/config/RestaurantKitProvider.js +26 -0
  50. package/dist/config/siteConfigContext.js +23 -0
  51. package/dist/config/useOrdering.js +12 -0
  52. package/dist/index.js +92 -0
  53. package/dist/menu/localMenuApi.js +43 -0
  54. package/dist/menu/menuApi.js +121 -0
  55. package/dist/menu/menuDraftStorage.js +35 -0
  56. package/dist/menu/menuIcons.js +24 -0
  57. package/dist/menu/menuMutations.js +99 -0
  58. package/dist/menu/menuOrdering.js +94 -0
  59. package/dist/menu/menuSchema.js +4 -0
  60. package/dist/menu/priceFormat.js +39 -0
  61. package/dist/menu/useMenuData.js +38 -0
  62. package/dist/menu/validateMenu.js +63 -0
  63. package/dist/menu-browser/MenuBrowser.js +54 -0
  64. package/dist/menu-browser/MenuBrowser.module.css.js +153 -0
  65. package/dist/menu-browser/MenuCard.js +106 -0
  66. package/dist/menu-browser/MenuCartDrawer.js +18 -0
  67. package/dist/menu-browser/MenuCategoryBlock.js +45 -0
  68. package/dist/menu-browser/MenuItemSheet.js +132 -0
  69. package/dist/menu-browser/MenuLightbox.js +33 -0
  70. package/dist/menu-browser/MenuRow.js +43 -0
  71. package/dist/menu-browser/MenuSection.js +10 -0
  72. package/dist/menu-browser/menuItemDisplay.js +19 -0
  73. package/dist/menu-browser/useMediaQuery.js +19 -0
  74. package/dist/menu-browser/useMenuBrowserState.js +82 -0
  75. package/dist/menu-browser/useParkedSheet.js +32 -0
  76. package/dist/pages/AdminMenuEditor.js +291 -0
  77. package/dist/pages/AdminPreview.js +43 -0
  78. package/dist/pages/AdminPreview.module.css.js +9 -0
  79. package/dist/pages/MenuSite.js +40 -0
  80. package/dist/restaurant-kit.css +1 -0
  81. package/dist/testimonials/downscaleImage.js +14 -0
  82. package/dist/testimonials/testimonialMutations.js +52 -0
  83. package/dist/types/RestaurantSiteRoutes.d.ts +11 -0
  84. package/dist/types/admin/AdminActionMenu.d.ts +20 -0
  85. package/dist/types/admin/AdminAuthPanel.d.ts +13 -0
  86. package/dist/types/admin/AdminBackupsDialog.d.ts +9 -0
  87. package/dist/types/admin/AdminDraftProvider.d.ts +12 -0
  88. package/dist/types/admin/AdminGroupsPanel.d.ts +14 -0
  89. package/dist/types/admin/AdminItemDrawer.d.ts +16 -0
  90. package/dist/types/admin/AdminMenuCard.d.ts +10 -0
  91. package/dist/types/admin/AdminMenuGroupTabs.d.ts +11 -0
  92. package/dist/types/admin/AdminMenuIconPicker.d.ts +8 -0
  93. package/dist/types/admin/AdminRoutes.d.ts +5 -0
  94. package/dist/types/admin/AdminSectionEditor.d.ts +20 -0
  95. package/dist/types/admin/AdminTestimonialsPanel.d.ts +7 -0
  96. package/dist/types/admin/adminDraftContext.d.ts +2 -0
  97. package/dist/types/admin/adminEditorUtils.d.ts +22 -0
  98. package/dist/types/admin/adminMenuCommands.d.ts +73 -0
  99. package/dist/types/admin/useAdminDraft.d.ts +5 -0
  100. package/dist/types/admin/useAdminMenuDraft.d.ts +41 -0
  101. package/dist/types/cart/CartDrawer.d.ts +7 -0
  102. package/dist/types/cart/CartProvider.d.ts +4 -0
  103. package/dist/types/cart/cartContext.d.ts +2 -0
  104. package/dist/types/cart/cartOrder.d.ts +9 -0
  105. package/dist/types/cart/cartReducer.d.ts +36 -0
  106. package/dist/types/cart/cartTypes.d.ts +37 -0
  107. package/dist/types/cart/useCart.d.ts +1 -0
  108. package/dist/types/components/Brand.d.ts +5 -0
  109. package/dist/types/components/Footer.d.ts +1 -0
  110. package/dist/types/components/Header.d.ts +1 -0
  111. package/dist/types/components/Hero.d.ts +1 -0
  112. package/dist/types/components/InstagramIcon.d.ts +6 -0
  113. package/dist/types/components/LocationsSection.d.ts +1 -0
  114. package/dist/types/components/Marquee.d.ts +1 -0
  115. package/dist/types/components/OrderIcon.d.ts +4 -0
  116. package/dist/types/components/RatingSection.d.ts +1 -0
  117. package/dist/types/components/TestimonialsSection.d.ts +2 -0
  118. package/dist/types/components/WhatsAppIcon.d.ts +5 -0
  119. package/dist/types/config/RestaurantKitProvider.d.ts +24 -0
  120. package/dist/types/config/siteConfigContext.d.ts +28 -0
  121. package/dist/types/config/siteTypes.d.ts +50 -0
  122. package/dist/types/config/useOrdering.d.ts +16 -0
  123. package/dist/types/index.d.ts +38 -0
  124. package/dist/types/menu/localMenuApi.d.ts +9 -0
  125. package/dist/types/menu/menuApi.d.ts +68 -0
  126. package/dist/types/menu/menuDraftStorage.d.ts +5 -0
  127. package/dist/types/menu/menuIcons.d.ts +7 -0
  128. package/dist/types/menu/menuMutations.d.ts +13 -0
  129. package/dist/types/menu/menuOrdering.d.ts +38 -0
  130. package/dist/types/menu/menuSchema.d.ts +68 -0
  131. package/dist/types/menu/priceFormat.d.ts +8 -0
  132. package/dist/types/menu/useMenuData.d.ts +12 -0
  133. package/dist/types/menu/validateMenu.d.ts +16 -0
  134. package/dist/types/menu-browser/MenuBrowser.d.ts +6 -0
  135. package/dist/types/menu-browser/MenuCard.d.ts +7 -0
  136. package/dist/types/menu-browser/MenuCartDrawer.d.ts +5 -0
  137. package/dist/types/menu-browser/MenuCategoryBlock.d.ts +11 -0
  138. package/dist/types/menu-browser/MenuItemSheet.d.ts +10 -0
  139. package/dist/types/menu-browser/MenuLightbox.d.ts +9 -0
  140. package/dist/types/menu-browser/MenuRow.d.ts +7 -0
  141. package/dist/types/menu-browser/MenuSection.d.ts +1 -0
  142. package/dist/types/menu-browser/menuItemDisplay.d.ts +5 -0
  143. package/dist/types/menu-browser/useMediaQuery.d.ts +1 -0
  144. package/dist/types/menu-browser/useMenuBrowserState.d.ts +17 -0
  145. package/dist/types/menu-browser/useParkedSheet.d.ts +13 -0
  146. package/dist/types/pages/AdminMenuEditor.d.ts +1 -0
  147. package/dist/types/pages/AdminPreview.d.ts +1 -0
  148. package/dist/types/pages/MenuSite.d.ts +6 -0
  149. package/dist/types/testimonials/downscaleImage.d.ts +6 -0
  150. package/dist/types/testimonials/testimonialMutations.d.ts +9 -0
  151. package/dist/types/utils.d.ts +1 -0
  152. package/dist/types/worker/auth.d.ts +97 -0
  153. package/dist/types/worker/http.d.ts +17 -0
  154. package/dist/types/worker/index.d.ts +16 -0
  155. package/dist/types/worker/menuStorage.d.ts +94 -0
  156. package/dist/utils.js +6 -0
  157. package/dist/worker/auth.js +183 -0
  158. package/dist/worker/http.js +65 -0
  159. package/dist/worker/menuStorage.js +95 -0
  160. package/dist/worker.js +131 -0
  161. package/package.json +105 -0
  162. package/scripts/bootstrap-site.mjs +125 -0
  163. package/scripts/hash-admin-password.mjs +11 -0
  164. package/scripts/lib/password.mjs +72 -0
@@ -0,0 +1,24 @@
1
+ import { GlassWater as i, CakeSlice as e, Soup as l, Drumstick as r, Sandwich as a, Beef as t, Utensils as n } from "lucide-react";
2
+ const d = {
3
+ plate: n,
4
+ burger: t,
5
+ wrap: a,
6
+ chicken: r,
7
+ platter: l,
8
+ fries: e,
9
+ kids: e,
10
+ drink: i
11
+ }, c = [
12
+ { id: "plate", label: "Plate" },
13
+ { id: "burger", label: "Burger" },
14
+ { id: "wrap", label: "Wrap" },
15
+ { id: "chicken", label: "Chicken" },
16
+ { id: "platter", label: "Platter" },
17
+ { id: "fries", label: "Fries" },
18
+ { id: "kids", label: "Kids" },
19
+ { id: "drink", label: "Drink" }
20
+ ];
21
+ export {
22
+ c as menuIconOptions,
23
+ d as menuIcons
24
+ };
@@ -0,0 +1,99 @@
1
+ import { normalizeOrder as f, sortByOrder as u, renumberSections as g } from "./menuOrdering.js";
2
+ function p(r, e) {
3
+ return {
4
+ ...r,
5
+ groups: [...r.groups, { ...e, order: r.groups.length }]
6
+ };
7
+ }
8
+ function l(r, e, i) {
9
+ return r.groups.some((t) => t.id === e) ? {
10
+ ...r,
11
+ groups: r.groups.map((t) => t.id === e ? { ...t, ...i } : t)
12
+ } : r;
13
+ }
14
+ function M(r, e) {
15
+ return e === "all" || r.categories.some((i) => i.groupId === e) ? r : {
16
+ ...r,
17
+ groups: f(u(r.groups.filter((i) => i.id !== e)))
18
+ };
19
+ }
20
+ function z(r, e) {
21
+ const i = r.categories.filter((t) => t.groupId === e.groupId).length;
22
+ return {
23
+ ...r,
24
+ categories: [...r.categories, { ...e, order: i }]
25
+ };
26
+ }
27
+ function C(r, e, i) {
28
+ const t = r.categories.find((d) => d.id === e);
29
+ if (!t) return r;
30
+ const o = i.groupId ?? t.groupId, n = o !== t.groupId, s = Math.max(
31
+ -1,
32
+ ...r.categories.filter((d) => d.id !== e && d.groupId === o).map((d) => d.order)
33
+ ) + 1;
34
+ return g({
35
+ ...r,
36
+ categories: r.categories.map((d) => d.id === e ? { ...d, ...i, order: n ? s : i.order ?? d.order } : d)
37
+ });
38
+ }
39
+ function G(r, e) {
40
+ return r.items.some((i) => i.categoryId === e) || !r.categories.some((i) => i.id === e) ? r : g({
41
+ ...r,
42
+ categories: r.categories.filter((i) => i.id !== e)
43
+ });
44
+ }
45
+ function b(r, e) {
46
+ const i = r.items.filter((t) => t.categoryId === e.categoryId).length;
47
+ return {
48
+ ...r,
49
+ items: [...r.items, { ...e, order: i }]
50
+ };
51
+ }
52
+ function c(r, e, i) {
53
+ return r.items.some((t) => t.id === e) ? {
54
+ ...r,
55
+ items: r.items.map((t) => t.id === e ? { ...t, ...i } : t)
56
+ } : r;
57
+ }
58
+ function S(r, e) {
59
+ const i = r.items.find((s) => s.id === e);
60
+ if (!i) return r;
61
+ const t = r.items.filter((s) => s.id !== e), o = f(u(t.filter((s) => s.categoryId === i.categoryId))), n = new Map(o.map((s) => [s.id, s]));
62
+ return {
63
+ ...r,
64
+ items: t.map((s) => n.get(s.id) ?? s)
65
+ };
66
+ }
67
+ function h(r, e, i) {
68
+ const t = r.items.find((o) => o.id === e);
69
+ return t ? c(r, e, {
70
+ pricingMode: "sizes",
71
+ sizes: [...t.sizes, { ...i, order: t.sizes.length }]
72
+ }) : r;
73
+ }
74
+ function O(r, e, i, t) {
75
+ const o = r.items.find((n) => n.id === e);
76
+ return !o || !o.sizes.some((n) => n.id === i) ? r : c(r, e, {
77
+ sizes: o.sizes.map((n) => n.id === i ? { ...n, ...t } : n)
78
+ });
79
+ }
80
+ function x(r, e, i) {
81
+ const t = r.items.find((o) => o.id === e);
82
+ return !t || !t.sizes.some((o) => o.id === i) ? r : c(r, e, {
83
+ sizes: f(u(t.sizes.filter((o) => o.id !== i)))
84
+ });
85
+ }
86
+ export {
87
+ z as addMenuCategory,
88
+ p as addMenuGroup,
89
+ b as addMenuItem,
90
+ h as addMenuSize,
91
+ G as deleteMenuCategory,
92
+ M as deleteMenuGroup,
93
+ S as deleteMenuItem,
94
+ x as deleteMenuSize,
95
+ C as updateMenuCategory,
96
+ l as updateMenuGroup,
97
+ c as updateMenuItem,
98
+ O as updateMenuSize
99
+ };
@@ -0,0 +1,94 @@
1
+ function l(t) {
2
+ return [...t].sort((r, o) => r.order - o.order);
3
+ }
4
+ function u(t) {
5
+ return t.map((r, o) => ({ ...r, order: o }));
6
+ }
7
+ function g(t) {
8
+ const r = u(M(t).sections.map(({ section: i }) => i)), o = new Set(r.map((i) => i.id)), e = t.categories.filter((i) => !o.has(i.id));
9
+ return { ...t, categories: [...r, ...e] };
10
+ }
11
+ function M(t, r = {}) {
12
+ const o = r.includeHidden ?? !0, e = r.includeEmptySections ?? !0, i = l(o ? t.groups : t.groups.filter((s) => s.isVisible)), c = o ? t.categories : t.categories.filter((s) => s.isVisible), a = l(o ? t.items : t.items.filter((s) => s.isVisible)), n = /* @__PURE__ */ new Map();
13
+ for (const s of a) {
14
+ const f = n.get(s.categoryId) ?? [];
15
+ f.push(s), n.set(s.categoryId, f);
16
+ }
17
+ const p = /* @__PURE__ */ new Map();
18
+ for (const s of c) {
19
+ const f = n.get(s.id) ?? [];
20
+ if (!e && f.length === 0) continue;
21
+ const m = p.get(s.groupId) ?? [];
22
+ m.push({ section: s, items: f }), p.set(s.groupId, m);
23
+ }
24
+ const d = i.map((s) => ({
25
+ group: s,
26
+ sections: v(p.get(s.id) ?? [])
27
+ }));
28
+ return {
29
+ groups: d,
30
+ sections: d.flatMap((s) => s.sections)
31
+ };
32
+ }
33
+ function v(t) {
34
+ return [...t].sort((r, o) => r.section.order - o.section.order);
35
+ }
36
+ function h(t, r, o) {
37
+ if (r === "all") return t;
38
+ const e = t.groups.filter((p) => p.id === "all"), i = l(t.groups.filter((p) => p.id !== "all")), c = i.findIndex((p) => p.id === r), a = c + o;
39
+ if (!G(i, c, a)) return t;
40
+ const n = y([...e, ...I(i, c, a)]);
41
+ return g({ ...t, groups: n });
42
+ }
43
+ function x(t, r) {
44
+ const o = t.groups.filter((n) => n.id === "all"), e = l(t.groups.filter((n) => n.id !== "all")), i = new Map(e.map((n) => [n.id, n])), c = r.map((n) => i.get(n)).filter((n) => !!n);
45
+ if (c.length !== e.length || c.every((n, p) => n.id === e[p].id)) return t;
46
+ const a = y([...o, ...c]);
47
+ return g({ ...t, groups: a });
48
+ }
49
+ function S(t, r, o) {
50
+ const e = t.categories.find((n) => n.id === r);
51
+ if (!e) return t;
52
+ const i = l(t.categories.filter((n) => n.groupId === e.groupId)), c = i.findIndex((n) => n.id === r), a = c + o;
53
+ return G(i, c, a) ? b(t, I(i, c, a)) : t;
54
+ }
55
+ function B(t, r, o) {
56
+ if (r === o) return t;
57
+ const e = t.items.find((d) => d.id === r), i = t.items.find((d) => d.id === o);
58
+ if (!e || !i || e.categoryId !== i.categoryId) return t;
59
+ const c = l(t.items.filter((d) => d.categoryId === e.categoryId)), a = c.findIndex((d) => d.id === r), n = c.findIndex((d) => d.id === o);
60
+ if (!G(c, a, n)) return t;
61
+ const p = new Map(u(I(c, a, n)).map((d) => [d.id, d]));
62
+ return {
63
+ ...t,
64
+ items: t.items.map((d) => p.get(d.id) ?? d)
65
+ };
66
+ }
67
+ function y(t) {
68
+ const r = t.filter((e) => e.id === "all").map((e) => ({ ...e, order: 0 })), o = t.filter((e) => e.id !== "all");
69
+ return [...r, ...o.map((e, i) => ({ ...e, order: i + 1 }))];
70
+ }
71
+ function b(t, r) {
72
+ const o = new Map(u(r).map((e) => [e.id, e]));
73
+ return g({
74
+ ...t,
75
+ categories: t.categories.map((e) => o.get(e.id) ?? e)
76
+ });
77
+ }
78
+ function I(t, r, o) {
79
+ const e = [...t], [i] = e.splice(r, 1);
80
+ return e.splice(o, 0, i), e;
81
+ }
82
+ function G(t, r, o) {
83
+ return r >= 0 && o >= 0 && r < t.length && o < t.length && r !== o;
84
+ }
85
+ export {
86
+ M as getOrderedMenu,
87
+ h as moveGroup,
88
+ B as moveItemBefore,
89
+ S as moveSection,
90
+ u as normalizeOrder,
91
+ g as renumberSections,
92
+ x as setGroupOrder,
93
+ l as sortByOrder
94
+ };
@@ -0,0 +1,4 @@
1
+ const e = ["plate", "burger", "wrap", "chicken", "platter", "fries", "kids", "drink"];
2
+ export {
3
+ e as menuIconIds
4
+ };
@@ -0,0 +1,39 @@
1
+ const m = /^\s*([\d,.]+)\s*(\$|usd|lbp|l\.l\.|ل\.ل)\s*$/i, a = /^\s*(\$|usd|lbp|l\.l\.|ل\.ل)\s*([\d,.]+)\s*$/i;
2
+ function s(t, r) {
3
+ return new Intl.NumberFormat("en-US", {
4
+ useGrouping: r,
5
+ minimumFractionDigits: Number.isInteger(t) ? 0 : 2,
6
+ maximumFractionDigits: 2
7
+ }).format(t);
8
+ }
9
+ function f(t) {
10
+ return t === "$" || t.toUpperCase() === "USD" ? "USD" : "LBP";
11
+ }
12
+ function u(t) {
13
+ const r = t.trim(), n = r.match(m), e = r.match(a), c = n?.[1] ?? e?.[2] ?? r, o = f(n?.[2] ?? e?.[1] ?? "$"), i = Number.parseFloat(c.replaceAll(",", ""));
14
+ return !c || Number.isNaN(i) ? null : { amount: c, currency: o, numericAmount: i };
15
+ }
16
+ function l(t, r = "USD") {
17
+ const n = u(t);
18
+ return n ? { amount: n.amount, currency: n.currency } : { amount: "", currency: r };
19
+ }
20
+ function p(t, r) {
21
+ const n = t.trim();
22
+ return n ? r === "USD" ? n : `${n} LBP` : "";
23
+ }
24
+ function $(t) {
25
+ const r = u(t);
26
+ return r ? r.currency === "USD" ? `$${r.amount}` : `${r.amount} ${r.currency}` : "";
27
+ }
28
+ function P(t, r) {
29
+ const n = u(t);
30
+ if (!n) return "";
31
+ const e = s(n.numericAmount * r, n.amount.includes(","));
32
+ return n.currency === "USD" ? `$${e}` : `${e} ${n.currency}`;
33
+ }
34
+ export {
35
+ $ as formatPrice,
36
+ P as formatPriceTotal,
37
+ p as priceFromInput,
38
+ l as priceInputValue
39
+ };
@@ -0,0 +1,38 @@
1
+ import { createElement as M, useContext as d, createContext as D, useState as c, useEffect as h } from "react";
2
+ import { useRestaurantKit as p } from "../config/siteConfigContext.js";
3
+ import { readMenuDraft as x } from "./menuDraftStorage.js";
4
+ const f = D(null);
5
+ function g({ children: e, value: r }) {
6
+ return M(f.Provider, { value: r }, e);
7
+ }
8
+ function v(e) {
9
+ const { seedMenu: r, draftStorageKey: i, isLocalApi: s, publicMenuUrl: u } = p(), [l, o] = c(r), [m, n] = c("local");
10
+ return h(() => {
11
+ if (!e) return;
12
+ if (s) {
13
+ const t = x(i);
14
+ t && (o(t), n("draft"));
15
+ return;
16
+ }
17
+ if (!u) return;
18
+ let a = !0;
19
+ return n("loading"), fetch(u, { cache: "no-store" }).then((t) => {
20
+ if (!t.ok) throw new Error(`Menu request failed: ${t.status}`);
21
+ return t.json();
22
+ }).then((t) => {
23
+ a && (o(t), n("remote"));
24
+ }).catch(() => {
25
+ a && (o(r), n("fallback"));
26
+ }), () => {
27
+ a = !1;
28
+ };
29
+ }, [i, e, s, u, r]), { menu: l, status: m };
30
+ }
31
+ function k() {
32
+ const e = d(f), r = v(!e);
33
+ return e ?? r;
34
+ }
35
+ export {
36
+ g as MenuDataProvider,
37
+ k as useMenuData
38
+ };
@@ -0,0 +1,63 @@
1
+ import { menuIconIds as m } from "./menuSchema.js";
2
+ function z(t) {
3
+ const r = [], e = (s, a) => r.push({ path: s, message: a });
4
+ if (!$(t))
5
+ return e("", "Menu must be an object."), r;
6
+ if (t.schemaVersion !== 1 && e("schemaVersion", "Must be 1."), typeof t.updatedAt != "string" && e("updatedAt", "Must be an ISO date string."), !$(t.restaurant))
7
+ e("restaurant", "Must be an object.");
8
+ else
9
+ for (const s of ["id", "name", "tagline", "phone", "whatsapp", "address"])
10
+ typeof t.restaurant[s] != "string" && e(`restaurant.${s}`, "Must be a string.");
11
+ const n = u(t.groups, "groups", e), o = u(t.categories, "categories", e), g = u(t.items, "items", e), l = t.testimonials === void 0 ? [] : u(t.testimonials, "testimonials", e), M = d(n, "groups", e), y = d(o, "categories", e);
12
+ return d(g, "items", e), d(l, "testimonials", e), n.forEach((s, a) => {
13
+ const i = `groups[${a}]`;
14
+ f(s, i, ["label"], e), p(s, i, e), m.includes(s.icon) || e(`${i}.icon`, `Must be one of: ${m.join(", ")}.`);
15
+ }), o.forEach((s, a) => {
16
+ const i = `categories[${a}]`;
17
+ f(s, i, ["title"], e), p(s, i, e), M.has(s.groupId) || e(`${i}.groupId`, `No group with id "${String(s.groupId)}".`);
18
+ }), g.forEach((s, a) => {
19
+ const i = `items[${a}]`;
20
+ f(s, i, ["title", "description", "image", "price"], e), p(s, i, e), y.has(s.categoryId) || e(`${i}.categoryId`, `No category with id "${String(s.categoryId)}".`), s.pricingMode !== "single" && s.pricingMode !== "sizes" && e(`${i}.pricingMode`, 'Must be "single" or "sizes".');
21
+ const c = u(s.sizes, `${i}.sizes`, e);
22
+ s.pricingMode === "sizes" && c.length === 0 && e(`${i}.sizes`, 'Needs at least one size when pricingMode is "sizes".'), d(c, `${i}.sizes`, e), c.forEach((b, I) => {
23
+ const h = `${i}.sizes[${I}]`;
24
+ f(b, h, ["id", "label", "price"], e), typeof b.order != "number" && e(`${h}.order`, "Must be a number.");
25
+ }), s.removableIngredients !== void 0 && !(Array.isArray(s.removableIngredients) && s.removableIngredients.every((b) => typeof b == "string")) && e(`${i}.removableIngredients`, "Must be an array of strings.");
26
+ }), l.forEach((s, a) => {
27
+ const i = `testimonials[${a}]`;
28
+ f(s, i, ["author", "text"], e), p(s, i, e);
29
+ const c = s.rating;
30
+ (typeof c != "number" || !Number.isInteger(c) || c < 1 || c > 5) && e(`${i}.rating`, "Must be a whole number from 1 to 5.");
31
+ }), r;
32
+ }
33
+ function A(t) {
34
+ const r = z(t);
35
+ if (r.length === 0) return;
36
+ const e = r.map((n) => ` - ${n.path || "(root)"}: ${n.message}`);
37
+ throw new Error(`Invalid menu (${r.length} issue${r.length === 1 ? "" : "s"}):
38
+ ${e.join(`
39
+ `)}`);
40
+ }
41
+ function $(t) {
42
+ return typeof t == "object" && t !== null && !Array.isArray(t);
43
+ }
44
+ function u(t, r, e) {
45
+ return Array.isArray(t) ? t.filter((n, o) => $(n) ? !0 : (e(`${r}[${o}]`, "Must be an object."), !1)) : (e(r, "Must be an array."), []);
46
+ }
47
+ function d(t, r, e) {
48
+ const n = /* @__PURE__ */ new Set();
49
+ return t.forEach((o, g) => {
50
+ n.has(o.id) && e(`${r}[${g}].id`, `Duplicate id "${String(o.id)}".`), n.add(o.id);
51
+ }), n;
52
+ }
53
+ function p(t, r, e) {
54
+ (typeof t.id != "string" || t.id === "") && e(`${r}.id`, "Must be a non-empty string."), typeof t.order != "number" && e(`${r}.order`, "Must be a number."), typeof t.isVisible != "boolean" && e(`${r}.isVisible`, "Must be a boolean.");
55
+ }
56
+ function f(t, r, e, n) {
57
+ for (const o of e)
58
+ typeof t[o] != "string" && n(`${r}.${o}`, "Must be a string.");
59
+ }
60
+ export {
61
+ A as assertValidMenu,
62
+ z as validateMenu
63
+ };
@@ -0,0 +1,54 @@
1
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
2
+ import { menuIcons as g } from "../menu/menuIcons.js";
3
+ import { cx as o } from "../utils.js";
4
+ import l from "./MenuBrowser.module.css.js";
5
+ import { MenuCategoryBlock as C } from "./MenuCategoryBlock.js";
6
+ import { useMenuBrowserState as k } from "./useMenuBrowserState.js";
7
+ function G({ menu: t }) {
8
+ const { activeGroup: c, catalogRef: d, controlStripRef: m, groupLabels: u, handleGroupClick: p, isTabsStuck: b, openCategories: h, tabsRef: v, toggleCategory: f, visibleCategories: s, visibleGroups: N } = k(t);
9
+ return /* @__PURE__ */ i("section", { className: `section ${l.section}`, id: "menu", "aria-labelledby": "menu-title", children: /* @__PURE__ */ a("div", { className: "section-inner", children: [
10
+ /* @__PURE__ */ i("div", { className: l.topline, children: /* @__PURE__ */ a("div", { children: [
11
+ /* @__PURE__ */ i("p", { className: "micro-label", children: "Full menu" }),
12
+ /* @__PURE__ */ i("h2", { id: "menu-title", children: "Choose your craving." })
13
+ ] }) }),
14
+ /* @__PURE__ */ a("div", { className: l.browser, children: [
15
+ /* @__PURE__ */ i("div", { className: o(l.controlStrip, b && l.stuckControlStrip), ref: m, children: /* @__PURE__ */ i("div", { className: l.tabs, role: "tablist", "aria-label": "Menu cravings", ref: v, children: N.map((e) => {
16
+ const r = g[e.icon], n = e.id === c;
17
+ return /* @__PURE__ */ a(
18
+ "button",
19
+ {
20
+ className: o(l.tab, n && l.activeTab),
21
+ type: "button",
22
+ role: "tab",
23
+ "aria-selected": n,
24
+ "data-group": e.id,
25
+ onClick: () => p(e.id),
26
+ children: [
27
+ /* @__PURE__ */ i("span", { className: l.tabIcon, children: /* @__PURE__ */ i(r, { "aria-hidden": "true" }) }),
28
+ /* @__PURE__ */ i("span", { className: l.tabLabel, children: e.label })
29
+ ]
30
+ },
31
+ e.id
32
+ );
33
+ }) }) }),
34
+ /* @__PURE__ */ i("div", { className: l.catalog, ref: d, children: s.length ? s.map(({ category: e, items: r }) => /* @__PURE__ */ i(
35
+ C,
36
+ {
37
+ category: e,
38
+ groupId: e.groupId,
39
+ groupLabel: u.get(e.groupId) ?? e.groupId,
40
+ isOpen: h.includes(e.id),
41
+ items: r,
42
+ onToggle: () => f(e.id)
43
+ },
44
+ e.id
45
+ )) : /* @__PURE__ */ a("div", { className: l.empty, children: [
46
+ /* @__PURE__ */ i("h3", { children: "No menu items." }),
47
+ /* @__PURE__ */ i("p", { children: "Check back soon." })
48
+ ] }) })
49
+ ] })
50
+ ] }) });
51
+ }
52
+ export {
53
+ G as MenuBrowser
54
+ };
@@ -0,0 +1,153 @@
1
+ const t = "_section_jjdh9_1", e = "_topline_jjdh9_17", o = "_browser_jjdh9_26", _ = "_controlStrip_jjdh9_30", s = "_tabs_jjdh9_44", n = "_tab_jjdh9_44", h = "_tabIcon_jjdh9_77", c = "_tabLabel_jjdh9_92", d = "_activeTab_jjdh9_99", j = "_catalog_jjdh9_109", r = "_categoryBlock_jjdh9_114", a = "_categoryHeading_jjdh9_120", i = "_toggleMeta_jjdh9_165", l = "_itemCount_jjdh9_172", b = "_chevron_jjdh9_179", g = "_openCategory_jjdh9_186", w = "_categoryContent_jjdh9_190", m = "_grid_jjdh9_203", p = "_rowList_jjdh9_204", C = "_card_jjdh9_220", y = "_clickableCard_jjdh9_228", T = "_noImageCard_jjdh9_256", H = "_media_jjdh9_261", S = "_zoomHint_jjdh9_275", u = "_cardBody_jjdh9_308", v = "_price_jjdh9_336", A = "_variationTabs_jjdh9_355", I = "_variationTab_jjdh9_355", x = "_selectedVariationTab_jjdh9_403", B = "_cardActions_jjdh9_413", L = "_customizeHint_jjdh9_417", k = "_customizeHintChip_jjdh9_428", z = "_bottomCardActions_jjdh9_452", M = "_addButton_jjdh9_456", D = "_itemStepper_jjdh9_487", N = "_empty_jjdh9_528", O = "_stuckControlStrip_jjdh9_571", R = "_lightbox_jjdh9_678", V = "_lightboxImage_jjdh9_691", F = "_lightboxClose_jjdh9_699", P = "_lightboxAdd_jjdh9_719", Q = "_row_jjdh9_204", f = "_rowMain_jjdh9_763", q = "_rowText_jjdh9_781", E = "_rowTitle_jjdh9_788", G = "_rowDesc_jjdh9_797", J = "_rowPrice_jjdh9_807", K = "_rowSizeHint_jjdh9_815", U = "_rowMediaCell_jjdh9_823", W = "_rowThumb_jjdh9_830", X = "_rowAdd_jjdh9_849", Y = "_rowAddInline_jjdh9_866", Z = "_rowQty_jjdh9_889", $ = "_sheetLayer_jjdh9_905", tt = "_sheetVeil_jjdh9_911", et = "_sheet_jjdh9_905", ot = "_sheetScroll_jjdh9_945", _t = "_sheetHero_jjdh9_997", st = "_sheetClose_jjdh9_1026", nt = "_sheetBody_jjdh9_1052", ht = "_plainSheetBody_jjdh9_1056", ct = "_sheetHeader_jjdh9_1060", dt = "_sheetDesc_jjdh9_1078", jt = "_sheetInCart_jjdh9_1084", rt = "_sheetNoteLabel_jjdh9_1112", at = "_sheetRemovables_jjdh9_1125", it = "_sheetRemovable_jjdh9_1125", lt = "_sheetNote_jjdh9_1112", bt = "_sheetFooter_jjdh9_1189", gt = "_sheetStepper_jjdh9_1197", wt = "_sheetAdd_jjdh9_1239", mt = "_sheetOrderLink_jjdh9_1259", pt = "_sheetOrderIcon_jjdh9_1271", Ct = "_sheetTotal_jjdh9_1287", yt = {
2
+ section: t,
3
+ topline: e,
4
+ browser: o,
5
+ controlStrip: _,
6
+ tabs: s,
7
+ tab: n,
8
+ tabIcon: h,
9
+ tabLabel: c,
10
+ activeTab: d,
11
+ catalog: j,
12
+ categoryBlock: r,
13
+ categoryHeading: a,
14
+ toggleMeta: i,
15
+ itemCount: l,
16
+ chevron: b,
17
+ openCategory: g,
18
+ categoryContent: w,
19
+ grid: m,
20
+ rowList: p,
21
+ card: C,
22
+ clickableCard: y,
23
+ noImageCard: T,
24
+ media: H,
25
+ zoomHint: S,
26
+ cardBody: u,
27
+ price: v,
28
+ variationTabs: A,
29
+ variationTab: I,
30
+ selectedVariationTab: x,
31
+ cardActions: B,
32
+ customizeHint: L,
33
+ customizeHintChip: k,
34
+ bottomCardActions: z,
35
+ addButton: M,
36
+ itemStepper: D,
37
+ empty: N,
38
+ stuckControlStrip: O,
39
+ lightbox: R,
40
+ lightboxImage: V,
41
+ lightboxClose: F,
42
+ lightboxAdd: P,
43
+ row: Q,
44
+ rowMain: f,
45
+ rowText: q,
46
+ rowTitle: E,
47
+ rowDesc: G,
48
+ rowPrice: J,
49
+ rowSizeHint: K,
50
+ rowMediaCell: U,
51
+ rowThumb: W,
52
+ rowAdd: X,
53
+ rowAddInline: Y,
54
+ rowQty: Z,
55
+ sheetLayer: $,
56
+ sheetVeil: tt,
57
+ sheet: et,
58
+ sheetScroll: ot,
59
+ sheetHero: _t,
60
+ sheetClose: st,
61
+ sheetBody: nt,
62
+ plainSheetBody: ht,
63
+ sheetHeader: ct,
64
+ sheetDesc: dt,
65
+ sheetInCart: jt,
66
+ sheetNoteLabel: rt,
67
+ sheetRemovables: at,
68
+ sheetRemovable: it,
69
+ sheetNote: lt,
70
+ sheetFooter: bt,
71
+ sheetStepper: gt,
72
+ sheetAdd: wt,
73
+ sheetOrderLink: mt,
74
+ sheetOrderIcon: pt,
75
+ sheetTotal: Ct
76
+ };
77
+ export {
78
+ d as activeTab,
79
+ M as addButton,
80
+ z as bottomCardActions,
81
+ o as browser,
82
+ C as card,
83
+ B as cardActions,
84
+ u as cardBody,
85
+ j as catalog,
86
+ r as categoryBlock,
87
+ w as categoryContent,
88
+ a as categoryHeading,
89
+ b as chevron,
90
+ y as clickableCard,
91
+ _ as controlStrip,
92
+ L as customizeHint,
93
+ k as customizeHintChip,
94
+ yt as default,
95
+ N as empty,
96
+ m as grid,
97
+ l as itemCount,
98
+ D as itemStepper,
99
+ R as lightbox,
100
+ P as lightboxAdd,
101
+ F as lightboxClose,
102
+ V as lightboxImage,
103
+ H as media,
104
+ T as noImageCard,
105
+ g as openCategory,
106
+ ht as plainSheetBody,
107
+ v as price,
108
+ Q as row,
109
+ X as rowAdd,
110
+ Y as rowAddInline,
111
+ G as rowDesc,
112
+ p as rowList,
113
+ f as rowMain,
114
+ U as rowMediaCell,
115
+ J as rowPrice,
116
+ Z as rowQty,
117
+ K as rowSizeHint,
118
+ q as rowText,
119
+ W as rowThumb,
120
+ E as rowTitle,
121
+ t as section,
122
+ x as selectedVariationTab,
123
+ et as sheet,
124
+ wt as sheetAdd,
125
+ nt as sheetBody,
126
+ st as sheetClose,
127
+ dt as sheetDesc,
128
+ bt as sheetFooter,
129
+ ct as sheetHeader,
130
+ _t as sheetHero,
131
+ jt as sheetInCart,
132
+ $ as sheetLayer,
133
+ lt as sheetNote,
134
+ rt as sheetNoteLabel,
135
+ pt as sheetOrderIcon,
136
+ mt as sheetOrderLink,
137
+ it as sheetRemovable,
138
+ at as sheetRemovables,
139
+ ot as sheetScroll,
140
+ gt as sheetStepper,
141
+ Ct as sheetTotal,
142
+ tt as sheetVeil,
143
+ O as stuckControlStrip,
144
+ n as tab,
145
+ h as tabIcon,
146
+ c as tabLabel,
147
+ s as tabs,
148
+ i as toggleMeta,
149
+ e as topline,
150
+ I as variationTab,
151
+ A as variationTabs,
152
+ S as zoomHint
153
+ };
@@ -0,0 +1,106 @@
1
+ import { jsxs as r, jsx as t } from "react/jsx-runtime";
2
+ import { SlidersHorizontal as z, Info as $, Minus as x, Plus as P } from "lucide-react";
3
+ import { useState as A } from "react";
4
+ import { cartSelectionKey as M } from "../cart/cartOrder.js";
5
+ import { useCart as T } from "../cart/useCart.js";
6
+ import { useOrdering as B } from "../config/useOrdering.js";
7
+ import { cx as u } from "../utils.js";
8
+ import i from "./MenuBrowser.module.css.js";
9
+ import { sortedSizes as D, imageSrc as H, variationLabel as K } from "./menuItemDisplay.js";
10
+ import { formatPrice as g } from "../menu/priceFormat.js";
11
+ function J({ item: a, onOpen: l }) {
12
+ const p = !!a.image, n = D(a), [h, C] = A(n[0]?.id ?? ""), d = n.find((e) => e.id === h) ?? n[0], N = d?.price ?? a.price, m = n.length > 1, { addItem: b, decreaseItem: S, getQuantity: I } = T(), { cartEnabled: o } = B(), c = {
13
+ itemId: a.id,
14
+ itemName: a.title,
15
+ variationId: d?.id,
16
+ variationName: d?.label
17
+ }, k = M(c), f = I(c), s = (e) => e.stopPropagation();
18
+ return /* @__PURE__ */ r(
19
+ "article",
20
+ {
21
+ className: u(i.card, !p && i.noImageCard, l && i.clickableCard),
22
+ ...l ? {
23
+ role: "button",
24
+ tabIndex: 0,
25
+ "aria-label": `${o ? "Customize" : "View"} ${a.title}`,
26
+ onClick: l,
27
+ onKeyDown: (e) => {
28
+ e.target === e.currentTarget && (e.key === "Enter" || e.key === " ") && (e.preventDefault(), l());
29
+ }
30
+ } : {},
31
+ children: [
32
+ p ? /* @__PURE__ */ r("div", { className: i.media, children: [
33
+ /* @__PURE__ */ t("img", { src: H(a.image), alt: a.title, loading: "lazy" }),
34
+ l ? /* @__PURE__ */ t("span", { className: i.customizeHint, "aria-hidden": "true", children: /* @__PURE__ */ r("span", { className: i.customizeHintChip, children: [
35
+ o ? /* @__PURE__ */ t(z, {}) : /* @__PURE__ */ t($, {}),
36
+ o ? "Customize" : "Details"
37
+ ] }) }) : null
38
+ ] }) : null,
39
+ /* @__PURE__ */ r("div", { className: i.cardBody, children: [
40
+ /* @__PURE__ */ r("header", { children: [
41
+ /* @__PURE__ */ t("h3", { children: a.title }),
42
+ m ? null : /* @__PURE__ */ t("span", { className: i.price, children: g(N) || "M.P." })
43
+ ] }),
44
+ a.description ? /* @__PURE__ */ t("p", { children: a.description }) : null,
45
+ m ? /* @__PURE__ */ t("div", { className: i.variationTabs, "aria-label": `${a.title} size`, children: n.map((e) => {
46
+ const y = e.id === h;
47
+ return /* @__PURE__ */ r(
48
+ "button",
49
+ {
50
+ className: u(i.variationTab, y && i.selectedVariationTab),
51
+ type: "button",
52
+ "aria-pressed": y,
53
+ onClick: (v) => {
54
+ s(v), C(e.id);
55
+ },
56
+ children: [
57
+ /* @__PURE__ */ t("span", { children: K(e) }),
58
+ /* @__PURE__ */ t("strong", { children: g(e.price) })
59
+ ]
60
+ },
61
+ e.id
62
+ );
63
+ }) }) : null,
64
+ o ? /* @__PURE__ */ t("div", { className: u(i.cardActions, !m && i.bottomCardActions), children: f ? /* @__PURE__ */ r("div", { className: i.itemStepper, "aria-label": `${a.title} quantity`, children: [
65
+ /* @__PURE__ */ t(
66
+ "button",
67
+ {
68
+ type: "button",
69
+ onClick: (e) => {
70
+ s(e), S(k);
71
+ },
72
+ "aria-label": `Remove one ${a.title}`,
73
+ children: /* @__PURE__ */ t(x, { "aria-hidden": "true" })
74
+ }
75
+ ),
76
+ /* @__PURE__ */ t("span", { children: f }),
77
+ /* @__PURE__ */ t(
78
+ "button",
79
+ {
80
+ type: "button",
81
+ onClick: (e) => {
82
+ s(e), b(c);
83
+ },
84
+ "aria-label": `Add one ${a.title}`,
85
+ children: /* @__PURE__ */ t(P, { "aria-hidden": "true" })
86
+ }
87
+ )
88
+ ] }) : /* @__PURE__ */ t(
89
+ "button",
90
+ {
91
+ className: i.addButton,
92
+ type: "button",
93
+ onClick: (e) => {
94
+ s(e), b(c);
95
+ },
96
+ children: "Add to cart"
97
+ }
98
+ ) }) : null
99
+ ] })
100
+ ]
101
+ }
102
+ );
103
+ }
104
+ export {
105
+ J as MenuCard
106
+ };