@swift-food-services/catering-widget 0.2.0-beta.11 → 0.2.0-beta.13
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/LICENSE +20 -20
- package/README.md +267 -267
- package/dist/index.cjs +1241 -522
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1242 -523
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +87 -87
package/dist/index.cjs
CHANGED
|
@@ -2765,6 +2765,55 @@ styleInject(`/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
|
|
|
2765
2765
|
translate: 0 -35%;
|
|
2766
2766
|
rotate: 0deg;
|
|
2767
2767
|
}
|
|
2768
|
+
.swift-catering-widget .\\!rating {
|
|
2769
|
+
vertical-align: middle !important;
|
|
2770
|
+
display: inline-flex !important;
|
|
2771
|
+
position: relative !important;
|
|
2772
|
+
}
|
|
2773
|
+
.swift-catering-widget .\\!rating input {
|
|
2774
|
+
appearance: none !important;
|
|
2775
|
+
border: none !important;
|
|
2776
|
+
}
|
|
2777
|
+
.swift-catering-widget .\\!rating :where(*) {
|
|
2778
|
+
background-color: var(--color-base-content) !important;
|
|
2779
|
+
opacity: .2 !important;
|
|
2780
|
+
border-radius: 0 !important;
|
|
2781
|
+
width: 1.5rem !important;
|
|
2782
|
+
height: 1.5rem !important;
|
|
2783
|
+
}
|
|
2784
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2785
|
+
.swift-catering-widget .\\!rating :where(*) {
|
|
2786
|
+
animation: .25s ease-out rating !important;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
.swift-catering-widget .\\!rating :where(*):is(input) {
|
|
2790
|
+
cursor: pointer !important;
|
|
2791
|
+
}
|
|
2792
|
+
.swift-catering-widget .\\!rating .rating-hidden {
|
|
2793
|
+
background-color: #0000 !important;
|
|
2794
|
+
width: .5rem !important;
|
|
2795
|
+
}
|
|
2796
|
+
.swift-catering-widget .\\!rating input[type=radio]:checked {
|
|
2797
|
+
background-image: none !important;
|
|
2798
|
+
}
|
|
2799
|
+
.swift-catering-widget .\\!rating :checked,
|
|
2800
|
+
.swift-catering-widget .\\!rating [aria-checked=true],
|
|
2801
|
+
.swift-catering-widget .\\!rating [aria-current=true],
|
|
2802
|
+
.swift-catering-widget .\\!rating :has(~ :checked, ~ [aria-checked=true], ~ [aria-current=true]) {
|
|
2803
|
+
opacity: 1 !important;
|
|
2804
|
+
}
|
|
2805
|
+
.swift-catering-widget .\\!rating :focus-visible {
|
|
2806
|
+
scale: 1.1 !important;
|
|
2807
|
+
}
|
|
2808
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2809
|
+
.swift-catering-widget .\\!rating :focus-visible {
|
|
2810
|
+
transition: scale .2s ease-out !important;
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
.swift-catering-widget .\\!rating :active:focus {
|
|
2814
|
+
animation: none !important;
|
|
2815
|
+
scale: 1.1 !important;
|
|
2816
|
+
}
|
|
2768
2817
|
.swift-catering-widget .rating {
|
|
2769
2818
|
vertical-align: middle;
|
|
2770
2819
|
display: inline-flex;
|
|
@@ -3422,6 +3471,26 @@ styleInject(`/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
|
|
|
3422
3471
|
cursor: not-allowed;
|
|
3423
3472
|
opacity: .2;
|
|
3424
3473
|
}
|
|
3474
|
+
.swift-catering-widget .\\!rating.rating-xs :where(:not(.rating-hidden)) {
|
|
3475
|
+
width: 1rem !important;
|
|
3476
|
+
height: 1rem !important;
|
|
3477
|
+
}
|
|
3478
|
+
.swift-catering-widget .\\!rating.rating-sm :where(:not(.rating-hidden)) {
|
|
3479
|
+
width: 1.25rem !important;
|
|
3480
|
+
height: 1.25rem !important;
|
|
3481
|
+
}
|
|
3482
|
+
.swift-catering-widget .\\!rating.rating-md :where(:not(.rating-hidden)) {
|
|
3483
|
+
width: 1.5rem !important;
|
|
3484
|
+
height: 1.5rem !important;
|
|
3485
|
+
}
|
|
3486
|
+
.swift-catering-widget .\\!rating.rating-lg :where(:not(.rating-hidden)) {
|
|
3487
|
+
width: 1.75rem !important;
|
|
3488
|
+
height: 1.75rem !important;
|
|
3489
|
+
}
|
|
3490
|
+
.swift-catering-widget .\\!rating.rating-xl :where(:not(.rating-hidden)) {
|
|
3491
|
+
width: 2rem !important;
|
|
3492
|
+
height: 2rem !important;
|
|
3493
|
+
}
|
|
3425
3494
|
.swift-catering-widget .rating.rating-xs :where(:not(.rating-hidden)) {
|
|
3426
3495
|
width: 1rem;
|
|
3427
3496
|
height: 1rem;
|
|
@@ -5347,6 +5416,9 @@ styleInject(`/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
|
|
|
5347
5416
|
.swift-catering-widget .pb-4 {
|
|
5348
5417
|
padding-bottom: calc(var(--spacing) * 4);
|
|
5349
5418
|
}
|
|
5419
|
+
.swift-catering-widget .pb-5 {
|
|
5420
|
+
padding-bottom: calc(var(--spacing) * 5);
|
|
5421
|
+
}
|
|
5350
5422
|
.swift-catering-widget .pb-6 {
|
|
5351
5423
|
padding-bottom: calc(var(--spacing) * 6);
|
|
5352
5424
|
}
|
|
@@ -6349,6 +6421,9 @@ styleInject(`/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
|
|
|
6349
6421
|
.swift-catering-widget .hover\\:text-gray-700:hover {
|
|
6350
6422
|
color: var(--color-gray-700);
|
|
6351
6423
|
}
|
|
6424
|
+
.swift-catering-widget .hover\\:text-gray-800:hover {
|
|
6425
|
+
color: var(--color-gray-800);
|
|
6426
|
+
}
|
|
6352
6427
|
.swift-catering-widget .hover\\:text-green-600:hover {
|
|
6353
6428
|
color: var(--color-green-600);
|
|
6354
6429
|
}
|
|
@@ -6484,6 +6559,15 @@ styleInject(`/*! tailwindcss v4.2.4 | MIT License | https://tailwindcss.com */
|
|
|
6484
6559
|
.swift-catering-widget .disabled\\:opacity-50:disabled {
|
|
6485
6560
|
opacity: .5;
|
|
6486
6561
|
}
|
|
6562
|
+
.swift-catering-widget .disabled\\:shadow-none:disabled {
|
|
6563
|
+
--tw-shadow:0 0 #0000;
|
|
6564
|
+
box-shadow:
|
|
6565
|
+
var(--tw-inset-shadow),
|
|
6566
|
+
var(--tw-inset-ring-shadow),
|
|
6567
|
+
var(--tw-ring-offset-shadow),
|
|
6568
|
+
var(--tw-ring-shadow),
|
|
6569
|
+
var(--tw-shadow);
|
|
6570
|
+
}
|
|
6487
6571
|
@media (hover: hover) {
|
|
6488
6572
|
.swift-catering-widget .disabled\\:hover\\:bg-base-100:disabled:hover {
|
|
6489
6573
|
background-color: var(--color-base-100);
|
|
@@ -7461,6 +7545,19 @@ function createCateringApi(client) {
|
|
|
7461
7545
|
};
|
|
7462
7546
|
}
|
|
7463
7547
|
|
|
7548
|
+
// src/api/endpoints/shared-cart.ts
|
|
7549
|
+
function createSharedCartApi(client) {
|
|
7550
|
+
return {
|
|
7551
|
+
create: (payload) => client.requestJson("/shared-carts", {
|
|
7552
|
+
method: "POST",
|
|
7553
|
+
body: JSON.stringify(payload)
|
|
7554
|
+
}),
|
|
7555
|
+
get: (id) => client.requestJson(
|
|
7556
|
+
`/shared-carts/${id}`
|
|
7557
|
+
)
|
|
7558
|
+
};
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7464
7561
|
// src/api/endpoints/me.ts
|
|
7465
7562
|
async function safeJson(res) {
|
|
7466
7563
|
try {
|
|
@@ -7611,6 +7708,7 @@ function createApiEndpoints(client) {
|
|
|
7611
7708
|
bundles: createBundlesApi(client),
|
|
7612
7709
|
pricing: createPricingApi(client),
|
|
7613
7710
|
catering: createCateringApi(client),
|
|
7711
|
+
sharedCart: createSharedCartApi(client),
|
|
7614
7712
|
me: createMeApi(client),
|
|
7615
7713
|
chat: createChatEndpoints(client)
|
|
7616
7714
|
};
|
|
@@ -7938,6 +8036,12 @@ function CateringStateProvider({ children }) {
|
|
|
7938
8036
|
return updated;
|
|
7939
8037
|
});
|
|
7940
8038
|
};
|
|
8039
|
+
const loadMealSessions = (sessions) => {
|
|
8040
|
+
const next = sessions.length > 0 ? sessions : [createDefaultSession()];
|
|
8041
|
+
setMealSessionsState(next);
|
|
8042
|
+
saveMealSessions(next);
|
|
8043
|
+
setActiveSessionIndex(0);
|
|
8044
|
+
};
|
|
7941
8045
|
const updateMealSession = (index, updates) => {
|
|
7942
8046
|
setMealSessionsState((prev) => {
|
|
7943
8047
|
if (index < 0 || index >= prev.length) return prev;
|
|
@@ -8129,6 +8233,7 @@ function CateringStateProvider({ children }) {
|
|
|
8129
8233
|
updateMealSession,
|
|
8130
8234
|
removeMealSession,
|
|
8131
8235
|
setActiveSessionIndex,
|
|
8236
|
+
loadMealSessions,
|
|
8132
8237
|
addMenuItem,
|
|
8133
8238
|
removeMenuItem,
|
|
8134
8239
|
removeMenuItemByIndex,
|
|
@@ -8159,54 +8264,6 @@ function useCateringState() {
|
|
|
8159
8264
|
}
|
|
8160
8265
|
return context;
|
|
8161
8266
|
}
|
|
8162
|
-
var FilterContext = react.createContext(void 0);
|
|
8163
|
-
var STORAGE_KEY = "catering_filters";
|
|
8164
|
-
var defaultFilters = {
|
|
8165
|
-
dietaryRestrictions: [],
|
|
8166
|
-
allergens: [],
|
|
8167
|
-
pricePerPersonRange: null,
|
|
8168
|
-
restaurantPriceRange: null
|
|
8169
|
-
};
|
|
8170
|
-
function CateringFilterProvider({ children }) {
|
|
8171
|
-
const storage = useStorage();
|
|
8172
|
-
const [filters, setFiltersState] = react.useState(defaultFilters);
|
|
8173
|
-
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
8174
|
-
react.useEffect(() => {
|
|
8175
|
-
try {
|
|
8176
|
-
const stored = storage.getItem(STORAGE_KEY);
|
|
8177
|
-
if (stored) {
|
|
8178
|
-
setFiltersState(JSON.parse(stored));
|
|
8179
|
-
}
|
|
8180
|
-
} catch (error) {
|
|
8181
|
-
console.error("Failed to load filters from storage:", error);
|
|
8182
|
-
}
|
|
8183
|
-
setIsInitialized(true);
|
|
8184
|
-
}, [storage]);
|
|
8185
|
-
react.useEffect(() => {
|
|
8186
|
-
if (isInitialized) {
|
|
8187
|
-
try {
|
|
8188
|
-
storage.setItem(STORAGE_KEY, JSON.stringify(filters));
|
|
8189
|
-
} catch (error) {
|
|
8190
|
-
console.error("Failed to save filters to storage:", error);
|
|
8191
|
-
}
|
|
8192
|
-
}
|
|
8193
|
-
}, [filters, isInitialized, storage]);
|
|
8194
|
-
const setFilters = (newFilters) => setFiltersState(newFilters);
|
|
8195
|
-
const clearFilters = () => setFiltersState(defaultFilters);
|
|
8196
|
-
return /* @__PURE__ */ jsxRuntime.jsx(FilterContext.Provider, { value: { filters, setFilters, clearFilters }, children });
|
|
8197
|
-
}
|
|
8198
|
-
function useCateringFilters() {
|
|
8199
|
-
const context = react.useContext(FilterContext);
|
|
8200
|
-
if (context === void 0) {
|
|
8201
|
-
throw new Error(
|
|
8202
|
-
"useCateringFilters must be used within a CateringFilterProvider"
|
|
8203
|
-
);
|
|
8204
|
-
}
|
|
8205
|
-
return context;
|
|
8206
|
-
}
|
|
8207
|
-
function useSearchParams() {
|
|
8208
|
-
return new URLSearchParams();
|
|
8209
|
-
}
|
|
8210
8267
|
|
|
8211
8268
|
// src/shims/cateringService.ts
|
|
8212
8269
|
var endpoints = null;
|
|
@@ -8225,6 +8282,8 @@ var cateringService = {
|
|
|
8225
8282
|
searchMenuItems: (query, filters) => ep().menu.searchMenuItems(query, filters),
|
|
8226
8283
|
getMenuItems: () => ep().menu.getMenuItems(),
|
|
8227
8284
|
getMenuItemsByRestaurant: (restaurantId) => ep().menu.getMenuItemsByRestaurant(restaurantId),
|
|
8285
|
+
createSharedCart: (snapshot) => ep().sharedCart.create(snapshot),
|
|
8286
|
+
getSharedCart: (id) => ep().sharedCart.get(id),
|
|
8228
8287
|
getBundleById: (bundleId) => ep().bundles.getBundleById(bundleId),
|
|
8229
8288
|
getCateringBundles: () => ep().bundles.getCateringBundles(),
|
|
8230
8289
|
getBundlesByRestaurant: (restaurantId) => ep().bundles.getBundlesByRestaurant(restaurantId),
|
|
@@ -8333,6 +8392,376 @@ var cateringService = {
|
|
|
8333
8392
|
}
|
|
8334
8393
|
};
|
|
8335
8394
|
|
|
8395
|
+
// src/utils/cart-hydration.ts
|
|
8396
|
+
async function hydrateCartFromSnapshot(snapshot, fetchMenuByRestaurant) {
|
|
8397
|
+
const restaurantIds = Array.from(
|
|
8398
|
+
new Set(
|
|
8399
|
+
snapshot.mealSessions.flatMap((s) => s.items.map((i) => i.restaurantId))
|
|
8400
|
+
)
|
|
8401
|
+
).filter(Boolean);
|
|
8402
|
+
const menus = await Promise.all(
|
|
8403
|
+
restaurantIds.map(
|
|
8404
|
+
(rid) => fetchMenuByRestaurant(rid).catch(() => [])
|
|
8405
|
+
)
|
|
8406
|
+
);
|
|
8407
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
8408
|
+
for (const menu of menus) {
|
|
8409
|
+
for (const item of menu) {
|
|
8410
|
+
if (item?.id) lookup.set(item.id, item);
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
const missing = [];
|
|
8414
|
+
const mealSessions = snapshot.mealSessions.map(
|
|
8415
|
+
(session) => {
|
|
8416
|
+
const orderItems = [];
|
|
8417
|
+
for (const snapItem of session.items) {
|
|
8418
|
+
const live = lookup.get(snapItem.menuItemId);
|
|
8419
|
+
if (!live) {
|
|
8420
|
+
missing.push({ menuItemId: snapItem.menuItemId });
|
|
8421
|
+
continue;
|
|
8422
|
+
}
|
|
8423
|
+
const built = applySelectedAddons(live, snapItem.selectedAddons);
|
|
8424
|
+
const liveRestaurant = built.restaurant;
|
|
8425
|
+
const restaurantName = built.restaurantName || liveRestaurant?.name || liveRestaurant?.restaurant_name;
|
|
8426
|
+
orderItems.push({
|
|
8427
|
+
item: restaurantName && !built.restaurantName ? { ...built, restaurantName } : built,
|
|
8428
|
+
quantity: snapItem.quantity,
|
|
8429
|
+
bundleId: snapItem.bundleId,
|
|
8430
|
+
bundleName: snapItem.bundleName
|
|
8431
|
+
});
|
|
8432
|
+
}
|
|
8433
|
+
return {
|
|
8434
|
+
sessionName: session.sessionName ?? "Main Event",
|
|
8435
|
+
sessionDate: session.sessionDate ?? "",
|
|
8436
|
+
eventTime: session.eventTime ?? "",
|
|
8437
|
+
guestCount: session.guestCount,
|
|
8438
|
+
orderItems
|
|
8439
|
+
};
|
|
8440
|
+
}
|
|
8441
|
+
);
|
|
8442
|
+
return { mealSessions, missing };
|
|
8443
|
+
}
|
|
8444
|
+
function serializeCartToSnapshot(mealSessions, eventDetails) {
|
|
8445
|
+
return {
|
|
8446
|
+
mealSessions: mealSessions.map((session) => ({
|
|
8447
|
+
sessionName: session.sessionName,
|
|
8448
|
+
sessionDate: session.sessionDate,
|
|
8449
|
+
eventTime: session.eventTime,
|
|
8450
|
+
guestCount: session.guestCount,
|
|
8451
|
+
items: session.orderItems.map((oi) => {
|
|
8452
|
+
const item = oi.item;
|
|
8453
|
+
return {
|
|
8454
|
+
menuItemId: item.id,
|
|
8455
|
+
restaurantId: item.restaurant?.restaurantId || item.restaurantId || "",
|
|
8456
|
+
quantity: oi.quantity,
|
|
8457
|
+
bundleId: oi.bundleId,
|
|
8458
|
+
bundleName: oi.bundleName,
|
|
8459
|
+
selectedAddons: (item.selectedAddons || []).map((a) => ({
|
|
8460
|
+
name: a.name,
|
|
8461
|
+
quantity: a.quantity,
|
|
8462
|
+
groupTitle: a.groupTitle
|
|
8463
|
+
}))
|
|
8464
|
+
};
|
|
8465
|
+
})
|
|
8466
|
+
})),
|
|
8467
|
+
eventDetails
|
|
8468
|
+
};
|
|
8469
|
+
}
|
|
8470
|
+
function applySelectedAddons(live, snapshotAddons) {
|
|
8471
|
+
if (!snapshotAddons || snapshotAddons.length === 0) return live;
|
|
8472
|
+
const selectedAddons = snapshotAddons.map((sa) => {
|
|
8473
|
+
const group = (live.addons || []).find(
|
|
8474
|
+
(g) => g.groupTitle === sa.groupTitle
|
|
8475
|
+
);
|
|
8476
|
+
const def = group?.items.find((a) => a.name === sa.name);
|
|
8477
|
+
if (!def) return null;
|
|
8478
|
+
return {
|
|
8479
|
+
name: sa.name,
|
|
8480
|
+
price: parseFloat(def.price) || 0,
|
|
8481
|
+
quantity: sa.quantity,
|
|
8482
|
+
groupTitle: sa.groupTitle ?? group?.groupTitle ?? "",
|
|
8483
|
+
allergens: def.allergens,
|
|
8484
|
+
dietaryRestrictions: def.dietaryRestrictions
|
|
8485
|
+
};
|
|
8486
|
+
}).filter((a) => a !== null);
|
|
8487
|
+
const addonPrice = selectedAddons.reduce(
|
|
8488
|
+
(sum, a) => sum + a.price * a.quantity,
|
|
8489
|
+
0
|
|
8490
|
+
);
|
|
8491
|
+
return { ...live, selectedAddons, addonPrice };
|
|
8492
|
+
}
|
|
8493
|
+
|
|
8494
|
+
// src/components/catering-order-helpers.ts
|
|
8495
|
+
var to12h = (totalMins) => {
|
|
8496
|
+
const h = Math.floor(totalMins / 60);
|
|
8497
|
+
const m = totalMins % 60;
|
|
8498
|
+
const period = h >= 12 ? "pm" : "am";
|
|
8499
|
+
const h12 = h % 12 || 12;
|
|
8500
|
+
return `${h12}:${String(m).padStart(2, "0")}${period}`;
|
|
8501
|
+
};
|
|
8502
|
+
var TIME_SLOT_OPTIONS = (() => {
|
|
8503
|
+
const slots = [];
|
|
8504
|
+
for (let startMins = 7 * 60; startMins <= 20 * 60; startMins += 30) {
|
|
8505
|
+
const endMins = startMins + 30;
|
|
8506
|
+
const sH = String(Math.floor(startMins / 60)).padStart(2, "0");
|
|
8507
|
+
const sM = String(startMins % 60).padStart(2, "0");
|
|
8508
|
+
slots.push({ label: `${to12h(startMins)} - ${to12h(endMins)}`, value: `${sH}:${sM}` });
|
|
8509
|
+
}
|
|
8510
|
+
return slots;
|
|
8511
|
+
})();
|
|
8512
|
+
function groupSessionsByDay(sessions, getSessionTotal) {
|
|
8513
|
+
const groups = /* @__PURE__ */ new Map();
|
|
8514
|
+
sessions.forEach((session, index) => {
|
|
8515
|
+
const date = session.sessionDate || "unscheduled";
|
|
8516
|
+
if (!groups.has(date)) {
|
|
8517
|
+
const dateObj = date !== "unscheduled" ? /* @__PURE__ */ new Date(date + "T00:00:00") : null;
|
|
8518
|
+
groups.set(date, {
|
|
8519
|
+
date,
|
|
8520
|
+
displayDate: dateObj?.toLocaleDateString("en-GB", {
|
|
8521
|
+
day: "numeric",
|
|
8522
|
+
month: "short"
|
|
8523
|
+
}) || "No Date",
|
|
8524
|
+
fullDate: dateObj?.toLocaleDateString("en-GB", {
|
|
8525
|
+
day: "numeric",
|
|
8526
|
+
month: "long",
|
|
8527
|
+
year: "numeric"
|
|
8528
|
+
}) || "Unscheduled",
|
|
8529
|
+
dayName: dateObj?.toLocaleDateString("en-GB", { weekday: "short" }) || "",
|
|
8530
|
+
sessions: [],
|
|
8531
|
+
total: 0
|
|
8532
|
+
});
|
|
8533
|
+
}
|
|
8534
|
+
const group = groups.get(date);
|
|
8535
|
+
group.sessions.push({ session, index });
|
|
8536
|
+
group.total += getSessionTotal(index);
|
|
8537
|
+
});
|
|
8538
|
+
groups.forEach((group) => {
|
|
8539
|
+
group.sessions.sort((a, b) => {
|
|
8540
|
+
if (!a.session.eventTime && !b.session.eventTime) return 0;
|
|
8541
|
+
if (!a.session.eventTime) return 1;
|
|
8542
|
+
if (!b.session.eventTime) return -1;
|
|
8543
|
+
return a.session.eventTime.localeCompare(b.session.eventTime);
|
|
8544
|
+
});
|
|
8545
|
+
});
|
|
8546
|
+
return Array.from(groups.values()).sort((a, b) => {
|
|
8547
|
+
if (a.date === "unscheduled") return 1;
|
|
8548
|
+
if (b.date === "unscheduled") return -1;
|
|
8549
|
+
return a.date.localeCompare(b.date);
|
|
8550
|
+
});
|
|
8551
|
+
}
|
|
8552
|
+
function formatTimeDisplay(eventTime) {
|
|
8553
|
+
if (!eventTime) return "Set time";
|
|
8554
|
+
const [hours, minutes] = eventTime.split(":");
|
|
8555
|
+
const hour = parseInt(hours);
|
|
8556
|
+
const minute = parseInt(minutes);
|
|
8557
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
8558
|
+
const hour12 = hour % 12 || 12;
|
|
8559
|
+
const start = `${hour12}:${String(minute).padStart(2, "0")} ${period}`;
|
|
8560
|
+
const totalEnd = hour * 60 + minute + 30;
|
|
8561
|
+
const endHour = Math.floor(totalEnd / 60) % 24;
|
|
8562
|
+
const endMinute = totalEnd % 60;
|
|
8563
|
+
const endPeriod = endHour >= 12 ? "PM" : "AM";
|
|
8564
|
+
const endHour12 = endHour % 12 || 12;
|
|
8565
|
+
return `${start} \u2013 ${endHour12}:${String(endMinute).padStart(2, "0")} ${endPeriod}`;
|
|
8566
|
+
}
|
|
8567
|
+
function getMinDate() {
|
|
8568
|
+
const date = /* @__PURE__ */ new Date();
|
|
8569
|
+
return date.toISOString().split("T")[0];
|
|
8570
|
+
}
|
|
8571
|
+
function getMaxDate() {
|
|
8572
|
+
const date = /* @__PURE__ */ new Date();
|
|
8573
|
+
date.setMonth(date.getMonth() + 3);
|
|
8574
|
+
return date.toISOString().split("T")[0];
|
|
8575
|
+
}
|
|
8576
|
+
function mapToMenuItem(item) {
|
|
8577
|
+
return {
|
|
8578
|
+
id: item.id,
|
|
8579
|
+
menuItemName: item.name,
|
|
8580
|
+
description: item.description,
|
|
8581
|
+
price: item.price?.toString() || "0",
|
|
8582
|
+
discountPrice: item.discountPrice?.toString(),
|
|
8583
|
+
allergens: item.allergens,
|
|
8584
|
+
isDiscount: item.isDiscount || false,
|
|
8585
|
+
images: item.images ?? (item.image ? [item.image] : []),
|
|
8586
|
+
averageRating: item.averageRating,
|
|
8587
|
+
restaurantId: item.restaurantId,
|
|
8588
|
+
restaurantName: item.restaurant?.restaurant_name,
|
|
8589
|
+
restaurant: item.restaurant ? {
|
|
8590
|
+
id: item.restaurant.id || item.restaurantId,
|
|
8591
|
+
name: item.restaurant.restaurant_name || item.restaurant.name,
|
|
8592
|
+
restaurantId: item.restaurantId,
|
|
8593
|
+
menuGroupSettings: item.restaurant.menuGroupSettings,
|
|
8594
|
+
images: item.restaurant.images || item.restaurant.image
|
|
8595
|
+
} : void 0,
|
|
8596
|
+
groupTitle: item.groupTitle,
|
|
8597
|
+
status: item.status,
|
|
8598
|
+
itemDisplayOrder: item.itemDisplayOrder || 0,
|
|
8599
|
+
cateringQuantityUnit: item.cateringQuantityUnit,
|
|
8600
|
+
feedsPerUnit: item.feedsPerUnit,
|
|
8601
|
+
minOrderQuantity: item.minOrderQuantity,
|
|
8602
|
+
dietaryFilters: item.dietaryFilters,
|
|
8603
|
+
categoryId: item.categories?.[0]?.id || item.categoryId,
|
|
8604
|
+
categoryName: item.categories?.[0]?.name || item.categoryName,
|
|
8605
|
+
// Include subcategory info from API response
|
|
8606
|
+
subcategoryId: item.subcategories?.[0]?.id,
|
|
8607
|
+
subcategoryName: item.subcategories?.[0]?.name,
|
|
8608
|
+
addons: (item.addons || []).map((group) => ({
|
|
8609
|
+
groupTitle: group.groupTitle || "",
|
|
8610
|
+
selectionType: group.selectionType === "multiple" ? "multiple_no_repeat" : group.selectionType || "multiple_no_repeat",
|
|
8611
|
+
isRequired: group.isRequired || false,
|
|
8612
|
+
minSelections: group.minSelections,
|
|
8613
|
+
maxSelections: group.maxSelections,
|
|
8614
|
+
items: (group.items || []).map((addon) => ({
|
|
8615
|
+
name: addon.name,
|
|
8616
|
+
price: addon.price?.toString() || "0",
|
|
8617
|
+
allergens: addon.allergens?.join?.(", ") || "",
|
|
8618
|
+
dietaryRestrictions: addon.dietaryRestrictions,
|
|
8619
|
+
isDefault: addon.isDefault,
|
|
8620
|
+
displayOrder: addon.displayOrder
|
|
8621
|
+
}))
|
|
8622
|
+
}))
|
|
8623
|
+
};
|
|
8624
|
+
}
|
|
8625
|
+
function formatTime(hour, minute) {
|
|
8626
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
8627
|
+
const hour12 = hour % 12 || 12;
|
|
8628
|
+
return `${hour12}:${minute.toString().padStart(2, "0")} ${period}`;
|
|
8629
|
+
}
|
|
8630
|
+
|
|
8631
|
+
// src/hooks/useImportCart.ts
|
|
8632
|
+
function useImportCart() {
|
|
8633
|
+
const { loadMealSessions } = useCateringState();
|
|
8634
|
+
const [status, setStatus] = react.useState("idle");
|
|
8635
|
+
const loadById = async (id) => {
|
|
8636
|
+
const { cart } = await cateringService.getSharedCart(id);
|
|
8637
|
+
const { mealSessions, missing } = await hydrateCartFromSnapshot(
|
|
8638
|
+
cart,
|
|
8639
|
+
// Map raw API items → widget MenuItem shape, same as every other
|
|
8640
|
+
// getMenuItemsByRestaurant caller (RestaurantMenuBrowser, BundleBrowser).
|
|
8641
|
+
// Without this the cart reads menuItemName/dietaryFilters off the raw
|
|
8642
|
+
// entity (name/dietaryFilters) → blank item names + dietary on load.
|
|
8643
|
+
(rid) => cateringService.getMenuItemsByRestaurant(rid).then((items) => items.map(mapToMenuItem))
|
|
8644
|
+
);
|
|
8645
|
+
const empty = mealSessions.every((s) => s.orderItems.length === 0);
|
|
8646
|
+
if (!empty) loadMealSessions(mealSessions);
|
|
8647
|
+
return { ok: !empty, empty, missing: missing.length };
|
|
8648
|
+
};
|
|
8649
|
+
const handleImport = async () => {
|
|
8650
|
+
if (status === "loading") return;
|
|
8651
|
+
const id = window.prompt("Enter a cart code to import:")?.trim();
|
|
8652
|
+
if (!id) return;
|
|
8653
|
+
setStatus("loading");
|
|
8654
|
+
try {
|
|
8655
|
+
const { empty, missing } = await loadById(id);
|
|
8656
|
+
if (empty) {
|
|
8657
|
+
window.alert("The items in this shared cart are no longer available.");
|
|
8658
|
+
setStatus("idle");
|
|
8659
|
+
return;
|
|
8660
|
+
}
|
|
8661
|
+
if (missing > 0) {
|
|
8662
|
+
window.alert(
|
|
8663
|
+
`${missing} item${missing === 1 ? "" : "s"} from this shared cart ${missing === 1 ? "is" : "are"} no longer available and ${missing === 1 ? "was" : "were"} removed.`
|
|
8664
|
+
);
|
|
8665
|
+
}
|
|
8666
|
+
setStatus("done");
|
|
8667
|
+
setTimeout(() => setStatus("idle"), 2500);
|
|
8668
|
+
} catch (err) {
|
|
8669
|
+
console.error("[catering] failed to import shared cart", err);
|
|
8670
|
+
window.alert("We couldn't load that cart \u2014 check the code and try again.");
|
|
8671
|
+
setStatus("error");
|
|
8672
|
+
setTimeout(() => setStatus("idle"), 2500);
|
|
8673
|
+
}
|
|
8674
|
+
};
|
|
8675
|
+
const label = status === "loading" ? "Importing\u2026" : status === "done" ? "Cart imported" : status === "error" ? "Try again" : "Import cart";
|
|
8676
|
+
return { status, handleImport, label, loadById };
|
|
8677
|
+
}
|
|
8678
|
+
|
|
8679
|
+
// src/components/SharedCartLoader.tsx
|
|
8680
|
+
function SharedCartLoader() {
|
|
8681
|
+
const { loadById } = useImportCart();
|
|
8682
|
+
react.useEffect(() => {
|
|
8683
|
+
if (typeof window === "undefined") return;
|
|
8684
|
+
const id = new URLSearchParams(window.location.search).get("sharedCart");
|
|
8685
|
+
if (!id) return;
|
|
8686
|
+
let cancelled = false;
|
|
8687
|
+
void (async () => {
|
|
8688
|
+
try {
|
|
8689
|
+
const { empty, missing } = await loadById(id);
|
|
8690
|
+
if (cancelled) return;
|
|
8691
|
+
if (empty) {
|
|
8692
|
+
window.alert(
|
|
8693
|
+
"The items in this shared cart are no longer available."
|
|
8694
|
+
);
|
|
8695
|
+
} else if (missing > 0) {
|
|
8696
|
+
window.alert(
|
|
8697
|
+
`${missing} item${missing === 1 ? "" : "s"} from this shared cart ${missing === 1 ? "is" : "are"} no longer available and ${missing === 1 ? "was" : "were"} removed.`
|
|
8698
|
+
);
|
|
8699
|
+
}
|
|
8700
|
+
const url = new URL(window.location.href);
|
|
8701
|
+
url.searchParams.delete("sharedCart");
|
|
8702
|
+
window.history.replaceState({}, "", url.toString());
|
|
8703
|
+
} catch (err) {
|
|
8704
|
+
console.error("[catering] failed to load shared cart", err);
|
|
8705
|
+
window.alert(
|
|
8706
|
+
"We couldn't load this shared cart \u2014 the link may have expired."
|
|
8707
|
+
);
|
|
8708
|
+
}
|
|
8709
|
+
})();
|
|
8710
|
+
return () => {
|
|
8711
|
+
cancelled = true;
|
|
8712
|
+
};
|
|
8713
|
+
}, []);
|
|
8714
|
+
return null;
|
|
8715
|
+
}
|
|
8716
|
+
var FilterContext = react.createContext(void 0);
|
|
8717
|
+
var STORAGE_KEY = "catering_filters";
|
|
8718
|
+
var defaultFilters = {
|
|
8719
|
+
dietaryRestrictions: [],
|
|
8720
|
+
allergens: [],
|
|
8721
|
+
pricePerPersonRange: null,
|
|
8722
|
+
restaurantPriceRange: null
|
|
8723
|
+
};
|
|
8724
|
+
function CateringFilterProvider({ children }) {
|
|
8725
|
+
const storage = useStorage();
|
|
8726
|
+
const [filters, setFiltersState] = react.useState(defaultFilters);
|
|
8727
|
+
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
8728
|
+
react.useEffect(() => {
|
|
8729
|
+
try {
|
|
8730
|
+
const stored = storage.getItem(STORAGE_KEY);
|
|
8731
|
+
if (stored) {
|
|
8732
|
+
setFiltersState(JSON.parse(stored));
|
|
8733
|
+
}
|
|
8734
|
+
} catch (error) {
|
|
8735
|
+
console.error("Failed to load filters from storage:", error);
|
|
8736
|
+
}
|
|
8737
|
+
setIsInitialized(true);
|
|
8738
|
+
}, [storage]);
|
|
8739
|
+
react.useEffect(() => {
|
|
8740
|
+
if (isInitialized) {
|
|
8741
|
+
try {
|
|
8742
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(filters));
|
|
8743
|
+
} catch (error) {
|
|
8744
|
+
console.error("Failed to save filters to storage:", error);
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
}, [filters, isInitialized, storage]);
|
|
8748
|
+
const setFilters = (newFilters) => setFiltersState(newFilters);
|
|
8749
|
+
const clearFilters = () => setFiltersState(defaultFilters);
|
|
8750
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FilterContext.Provider, { value: { filters, setFilters, clearFilters }, children });
|
|
8751
|
+
}
|
|
8752
|
+
function useCateringFilters() {
|
|
8753
|
+
const context = react.useContext(FilterContext);
|
|
8754
|
+
if (context === void 0) {
|
|
8755
|
+
throw new Error(
|
|
8756
|
+
"useCateringFilters must be used within a CateringFilterProvider"
|
|
8757
|
+
);
|
|
8758
|
+
}
|
|
8759
|
+
return context;
|
|
8760
|
+
}
|
|
8761
|
+
function useSearchParams() {
|
|
8762
|
+
return new URLSearchParams();
|
|
8763
|
+
}
|
|
8764
|
+
|
|
8336
8765
|
// src/constants/allergens.ts
|
|
8337
8766
|
var ALLERGENS = [
|
|
8338
8767
|
{ label: "Celery", value: "celery" },
|
|
@@ -10947,179 +11376,42 @@ function validateRestaurantMinOrders(session, restaurant) {
|
|
|
10947
11376
|
currentQuantity,
|
|
10948
11377
|
minQuantity: rule.minQuantity,
|
|
10949
11378
|
isMet,
|
|
10950
|
-
isRequired: false
|
|
10951
|
-
});
|
|
10952
|
-
if (!isMet) {
|
|
10953
|
-
isValid = false;
|
|
10954
|
-
}
|
|
10955
|
-
}
|
|
10956
|
-
});
|
|
10957
|
-
});
|
|
10958
|
-
}
|
|
10959
|
-
return {
|
|
10960
|
-
restaurantId,
|
|
10961
|
-
restaurantName,
|
|
10962
|
-
isValid,
|
|
10963
|
-
sections
|
|
10964
|
-
};
|
|
10965
|
-
}
|
|
10966
|
-
function getRequiredMinForSection(restaurant, sectionName) {
|
|
10967
|
-
const required = restaurant?.cateringMinOrderSettings?.required;
|
|
10968
|
-
if (!required?.length) return null;
|
|
10969
|
-
let max = null;
|
|
10970
|
-
for (const rule of required) {
|
|
10971
|
-
if (!rule.applicableSections?.includes(sectionName)) continue;
|
|
10972
|
-
if (max === null || rule.minQuantity > max) max = rule.minQuantity;
|
|
10973
|
-
}
|
|
10974
|
-
return max;
|
|
10975
|
-
}
|
|
10976
|
-
function validateSessionMinOrders(session, restaurants) {
|
|
10977
|
-
const restaurantIdsInSession = new Set(
|
|
10978
|
-
session.orderItems.map((item) => item.item.restaurantId)
|
|
10979
|
-
);
|
|
10980
|
-
const relevantRestaurants = restaurants.filter(
|
|
10981
|
-
(r) => restaurantIdsInSession.has(r.id)
|
|
10982
|
-
);
|
|
10983
|
-
return relevantRestaurants.map(
|
|
10984
|
-
(restaurant) => validateRestaurantMinOrders(session, restaurant)
|
|
10985
|
-
);
|
|
10986
|
-
}
|
|
10987
|
-
|
|
10988
|
-
// src/components/catering-order-helpers.ts
|
|
10989
|
-
var to12h = (totalMins) => {
|
|
10990
|
-
const h = Math.floor(totalMins / 60);
|
|
10991
|
-
const m = totalMins % 60;
|
|
10992
|
-
const period = h >= 12 ? "pm" : "am";
|
|
10993
|
-
const h12 = h % 12 || 12;
|
|
10994
|
-
return `${h12}:${String(m).padStart(2, "0")}${period}`;
|
|
10995
|
-
};
|
|
10996
|
-
var TIME_SLOT_OPTIONS = (() => {
|
|
10997
|
-
const slots = [];
|
|
10998
|
-
for (let startMins = 7 * 60; startMins <= 20 * 60; startMins += 30) {
|
|
10999
|
-
const endMins = startMins + 30;
|
|
11000
|
-
const sH = String(Math.floor(startMins / 60)).padStart(2, "0");
|
|
11001
|
-
const sM = String(startMins % 60).padStart(2, "0");
|
|
11002
|
-
slots.push({ label: `${to12h(startMins)} - ${to12h(endMins)}`, value: `${sH}:${sM}` });
|
|
11003
|
-
}
|
|
11004
|
-
return slots;
|
|
11005
|
-
})();
|
|
11006
|
-
function groupSessionsByDay(sessions, getSessionTotal) {
|
|
11007
|
-
const groups = /* @__PURE__ */ new Map();
|
|
11008
|
-
sessions.forEach((session, index) => {
|
|
11009
|
-
const date = session.sessionDate || "unscheduled";
|
|
11010
|
-
if (!groups.has(date)) {
|
|
11011
|
-
const dateObj = date !== "unscheduled" ? /* @__PURE__ */ new Date(date + "T00:00:00") : null;
|
|
11012
|
-
groups.set(date, {
|
|
11013
|
-
date,
|
|
11014
|
-
displayDate: dateObj?.toLocaleDateString("en-GB", {
|
|
11015
|
-
day: "numeric",
|
|
11016
|
-
month: "short"
|
|
11017
|
-
}) || "No Date",
|
|
11018
|
-
fullDate: dateObj?.toLocaleDateString("en-GB", {
|
|
11019
|
-
day: "numeric",
|
|
11020
|
-
month: "long",
|
|
11021
|
-
year: "numeric"
|
|
11022
|
-
}) || "Unscheduled",
|
|
11023
|
-
dayName: dateObj?.toLocaleDateString("en-GB", { weekday: "short" }) || "",
|
|
11024
|
-
sessions: [],
|
|
11025
|
-
total: 0
|
|
11026
|
-
});
|
|
11027
|
-
}
|
|
11028
|
-
const group = groups.get(date);
|
|
11029
|
-
group.sessions.push({ session, index });
|
|
11030
|
-
group.total += getSessionTotal(index);
|
|
11031
|
-
});
|
|
11032
|
-
groups.forEach((group) => {
|
|
11033
|
-
group.sessions.sort((a, b) => {
|
|
11034
|
-
if (!a.session.eventTime && !b.session.eventTime) return 0;
|
|
11035
|
-
if (!a.session.eventTime) return 1;
|
|
11036
|
-
if (!b.session.eventTime) return -1;
|
|
11037
|
-
return a.session.eventTime.localeCompare(b.session.eventTime);
|
|
11038
|
-
});
|
|
11039
|
-
});
|
|
11040
|
-
return Array.from(groups.values()).sort((a, b) => {
|
|
11041
|
-
if (a.date === "unscheduled") return 1;
|
|
11042
|
-
if (b.date === "unscheduled") return -1;
|
|
11043
|
-
return a.date.localeCompare(b.date);
|
|
11044
|
-
});
|
|
11045
|
-
}
|
|
11046
|
-
function formatTimeDisplay(eventTime) {
|
|
11047
|
-
if (!eventTime) return "Set time";
|
|
11048
|
-
const [hours, minutes] = eventTime.split(":");
|
|
11049
|
-
const hour = parseInt(hours);
|
|
11050
|
-
const minute = parseInt(minutes);
|
|
11051
|
-
const period = hour >= 12 ? "PM" : "AM";
|
|
11052
|
-
const hour12 = hour % 12 || 12;
|
|
11053
|
-
const start = `${hour12}:${String(minute).padStart(2, "0")} ${period}`;
|
|
11054
|
-
const totalEnd = hour * 60 + minute + 30;
|
|
11055
|
-
const endHour = Math.floor(totalEnd / 60) % 24;
|
|
11056
|
-
const endMinute = totalEnd % 60;
|
|
11057
|
-
const endPeriod = endHour >= 12 ? "PM" : "AM";
|
|
11058
|
-
const endHour12 = endHour % 12 || 12;
|
|
11059
|
-
return `${start} \u2013 ${endHour12}:${String(endMinute).padStart(2, "0")} ${endPeriod}`;
|
|
11060
|
-
}
|
|
11061
|
-
function getMinDate() {
|
|
11062
|
-
const date = /* @__PURE__ */ new Date();
|
|
11063
|
-
return date.toISOString().split("T")[0];
|
|
11064
|
-
}
|
|
11065
|
-
function getMaxDate() {
|
|
11066
|
-
const date = /* @__PURE__ */ new Date();
|
|
11067
|
-
date.setMonth(date.getMonth() + 3);
|
|
11068
|
-
return date.toISOString().split("T")[0];
|
|
11069
|
-
}
|
|
11070
|
-
function mapToMenuItem(item) {
|
|
11071
|
-
return {
|
|
11072
|
-
id: item.id,
|
|
11073
|
-
menuItemName: item.name,
|
|
11074
|
-
description: item.description,
|
|
11075
|
-
price: item.price?.toString() || "0",
|
|
11076
|
-
discountPrice: item.discountPrice?.toString(),
|
|
11077
|
-
allergens: item.allergens,
|
|
11078
|
-
isDiscount: item.isDiscount || false,
|
|
11079
|
-
images: item.images ?? (item.image ? [item.image] : []),
|
|
11080
|
-
averageRating: item.averageRating,
|
|
11081
|
-
restaurantId: item.restaurantId,
|
|
11082
|
-
restaurantName: item.restaurant?.restaurant_name,
|
|
11083
|
-
restaurant: item.restaurant ? {
|
|
11084
|
-
id: item.restaurant.id || item.restaurantId,
|
|
11085
|
-
name: item.restaurant.restaurant_name || item.restaurant.name,
|
|
11086
|
-
restaurantId: item.restaurantId,
|
|
11087
|
-
menuGroupSettings: item.restaurant.menuGroupSettings,
|
|
11088
|
-
images: item.restaurant.images || item.restaurant.image
|
|
11089
|
-
} : void 0,
|
|
11090
|
-
groupTitle: item.groupTitle,
|
|
11091
|
-
status: item.status,
|
|
11092
|
-
itemDisplayOrder: item.itemDisplayOrder || 0,
|
|
11093
|
-
cateringQuantityUnit: item.cateringQuantityUnit,
|
|
11094
|
-
feedsPerUnit: item.feedsPerUnit,
|
|
11095
|
-
minOrderQuantity: item.minOrderQuantity,
|
|
11096
|
-
dietaryFilters: item.dietaryFilters,
|
|
11097
|
-
categoryId: item.categories?.[0]?.id || item.categoryId,
|
|
11098
|
-
categoryName: item.categories?.[0]?.name || item.categoryName,
|
|
11099
|
-
// Include subcategory info from API response
|
|
11100
|
-
subcategoryId: item.subcategories?.[0]?.id,
|
|
11101
|
-
subcategoryName: item.subcategories?.[0]?.name,
|
|
11102
|
-
addons: (item.addons || []).map((group) => ({
|
|
11103
|
-
groupTitle: group.groupTitle || "",
|
|
11104
|
-
selectionType: group.selectionType === "multiple" ? "multiple_no_repeat" : group.selectionType || "multiple_no_repeat",
|
|
11105
|
-
isRequired: group.isRequired || false,
|
|
11106
|
-
minSelections: group.minSelections,
|
|
11107
|
-
maxSelections: group.maxSelections,
|
|
11108
|
-
items: (group.items || []).map((addon) => ({
|
|
11109
|
-
name: addon.name,
|
|
11110
|
-
price: addon.price?.toString() || "0",
|
|
11111
|
-
allergens: addon.allergens?.join?.(", ") || "",
|
|
11112
|
-
dietaryRestrictions: addon.dietaryRestrictions,
|
|
11113
|
-
isDefault: addon.isDefault,
|
|
11114
|
-
displayOrder: addon.displayOrder
|
|
11115
|
-
}))
|
|
11116
|
-
}))
|
|
11379
|
+
isRequired: false
|
|
11380
|
+
});
|
|
11381
|
+
if (!isMet) {
|
|
11382
|
+
isValid = false;
|
|
11383
|
+
}
|
|
11384
|
+
}
|
|
11385
|
+
});
|
|
11386
|
+
});
|
|
11387
|
+
}
|
|
11388
|
+
return {
|
|
11389
|
+
restaurantId,
|
|
11390
|
+
restaurantName,
|
|
11391
|
+
isValid,
|
|
11392
|
+
sections
|
|
11117
11393
|
};
|
|
11118
11394
|
}
|
|
11119
|
-
function
|
|
11120
|
-
const
|
|
11121
|
-
|
|
11122
|
-
|
|
11395
|
+
function getRequiredMinForSection(restaurant, sectionName) {
|
|
11396
|
+
const required = restaurant?.cateringMinOrderSettings?.required;
|
|
11397
|
+
if (!required?.length) return null;
|
|
11398
|
+
let max = null;
|
|
11399
|
+
for (const rule of required) {
|
|
11400
|
+
if (!rule.applicableSections?.includes(sectionName)) continue;
|
|
11401
|
+
if (max === null || rule.minQuantity > max) max = rule.minQuantity;
|
|
11402
|
+
}
|
|
11403
|
+
return max;
|
|
11404
|
+
}
|
|
11405
|
+
function validateSessionMinOrders(session, restaurants) {
|
|
11406
|
+
const restaurantIdsInSession = new Set(
|
|
11407
|
+
session.orderItems.map((item) => item.item.restaurantId)
|
|
11408
|
+
);
|
|
11409
|
+
const relevantRestaurants = restaurants.filter(
|
|
11410
|
+
(r) => restaurantIdsInSession.has(r.id)
|
|
11411
|
+
);
|
|
11412
|
+
return relevantRestaurants.map(
|
|
11413
|
+
(restaurant) => validateRestaurantMinOrders(session, restaurant)
|
|
11414
|
+
);
|
|
11123
11415
|
}
|
|
11124
11416
|
function readPrimaryColor(from) {
|
|
11125
11417
|
if (!from) return void 0;
|
|
@@ -21613,118 +21905,120 @@ function PricingSummary({
|
|
|
21613
21905
|
if (calculatingPricing && !pricing) {
|
|
21614
21906
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `text-center text-base-content/60 ${compact ? "py-2 text-xs" : "py-4 text-sm"}`, children: "Calculating pricing..." });
|
|
21615
21907
|
}
|
|
21616
|
-
if (pricing) {
|
|
21617
|
-
|
|
21618
|
-
|
|
21619
|
-
|
|
21620
|
-
|
|
21621
|
-
|
|
21622
|
-
|
|
21623
|
-
|
|
21624
|
-
|
|
21625
|
-
|
|
21626
|
-
|
|
21908
|
+
if (!pricing && estimatedTotal !== void 0) {
|
|
21909
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between pt-4 border-t border-base-300", children: [
|
|
21910
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-base-content", children: "Estimated Total:" }),
|
|
21911
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-xl text-base-content", children: [
|
|
21912
|
+
"\xA3",
|
|
21913
|
+
estimatedTotal.toFixed(2)
|
|
21914
|
+
] })
|
|
21915
|
+
] });
|
|
21916
|
+
}
|
|
21917
|
+
const quote = pricing ?? {
|
|
21918
|
+
subtotal: 0,
|
|
21919
|
+
deliveryFee: 0,
|
|
21920
|
+
total: 0
|
|
21921
|
+
};
|
|
21922
|
+
const deliveryBreakdown = quote.deliveryFeeBreakdown || quote.mealSessions?.[0]?.deliveryFeeBreakdown;
|
|
21923
|
+
const distanceInMiles = quote.distanceInMiles || quote.mealSessions?.[0]?.distanceInMiles;
|
|
21924
|
+
const backendPromos = quote.appliedPromotions?.filter((p) => p.discount > 0) ?? [];
|
|
21925
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `transition-opacity duration-200 ${calculatingPricing ? "opacity-50" : "opacity-100"} ${compact ? "space-y-1.5" : "border-t border-base-300 space-y-2 pt-4"}`, children: [
|
|
21926
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-base-content/70 ${compact ? "text-xs" : "text-sm"}`, children: [
|
|
21927
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Subtotal" }),
|
|
21928
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21929
|
+
"\xA3",
|
|
21930
|
+
quote.subtotal.toFixed(2)
|
|
21931
|
+
] })
|
|
21932
|
+
] }),
|
|
21933
|
+
(quote.promotionDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-green-600 font-semibold ${compact ? "text-xs" : "text-sm"}`, children: [
|
|
21934
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21935
|
+
"Restaurant Promotion",
|
|
21936
|
+
backendPromos.length > 1 ? "s" : ""
|
|
21627
21937
|
] }),
|
|
21628
|
-
|
|
21629
|
-
|
|
21630
|
-
|
|
21631
|
-
|
|
21938
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21939
|
+
"-\xA3",
|
|
21940
|
+
quote.promotionDiscount.toFixed(2)
|
|
21941
|
+
] })
|
|
21942
|
+
] }),
|
|
21943
|
+
(quote.promoDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-success font-medium ${compact ? "text-xs" : "text-sm"}`, children: [
|
|
21944
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Promo Code Discount" }),
|
|
21945
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21946
|
+
"-\xA3",
|
|
21947
|
+
quote.promoDiscount.toFixed(2)
|
|
21948
|
+
] })
|
|
21949
|
+
] }),
|
|
21950
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
21951
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between items-center text-base-content/70 gap-2 ${compact ? "text-xs" : "text-sm"}`, children: [
|
|
21952
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
21953
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap", children: "Delivery Cost" }),
|
|
21954
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
|
|
21955
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "text-base-content/30 hover:text-base-content/60 leading-none", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }) }),
|
|
21956
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-full left-0 mb-1.5 w-52 hidden group-hover:block z-50", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-base-content text-base-100 text-[10px] leading-relaxed rounded-lg px-2.5 py-2 shadow-lg", children: "Based on number of restaurants, distance to your delivery address, and catering order size." }) })
|
|
21957
|
+
] })
|
|
21632
21958
|
] }),
|
|
21633
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
21634
|
-
|
|
21635
|
-
|
|
21959
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
21960
|
+
deliveryBreakdown && !compact && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21961
|
+
"button",
|
|
21962
|
+
{
|
|
21963
|
+
onClick: () => setShowDeliveryBreakdown(!showDeliveryBreakdown),
|
|
21964
|
+
className: "text-base-content/40 hover:text-base-content/70",
|
|
21965
|
+
type: "button",
|
|
21966
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
21967
|
+
"svg",
|
|
21968
|
+
{
|
|
21969
|
+
className: `w-4 h-4 transition-transform ${showDeliveryBreakdown ? "rotate-180" : ""}`,
|
|
21970
|
+
fill: "none",
|
|
21971
|
+
stroke: "currentColor",
|
|
21972
|
+
viewBox: "0 0 24 24",
|
|
21973
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
|
|
21974
|
+
}
|
|
21975
|
+
)
|
|
21976
|
+
}
|
|
21977
|
+
),
|
|
21978
|
+
distanceInMiles && onClearAddress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21979
|
+
"button",
|
|
21980
|
+
{
|
|
21981
|
+
type: "button",
|
|
21982
|
+
onClick: onClearAddress,
|
|
21983
|
+
className: "text-red-400 hover:text-red-600 transition-colors flex-shrink-0",
|
|
21984
|
+
title: "Change address",
|
|
21985
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: 2.5, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
|
|
21986
|
+
}
|
|
21987
|
+
),
|
|
21988
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21989
|
+
"\xA3",
|
|
21990
|
+
quote.deliveryFee.toFixed(2)
|
|
21991
|
+
] }),
|
|
21992
|
+
!distanceInMiles && onPlaceSelect && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base-content/40 text-xs italic", children: "(min)" })
|
|
21636
21993
|
] })
|
|
21637
21994
|
] }),
|
|
21638
|
-
|
|
21639
|
-
|
|
21995
|
+
!distanceInMiles && onPlaceSelect && /* @__PURE__ */ jsxRuntime.jsx(InlineAddressInput, { onPlaceSelect }),
|
|
21996
|
+
showDeliveryBreakdown && quote.mealSessions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-4 space-y-1 text-xs text-base-content/60", children: quote.mealSessions.map((session, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
|
|
21997
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21998
|
+
session.sessionName,
|
|
21999
|
+
":"
|
|
22000
|
+
] }),
|
|
21640
22001
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21641
|
-
"
|
|
21642
|
-
|
|
22002
|
+
"\xA3",
|
|
22003
|
+
session.deliveryFee.toFixed(2)
|
|
21643
22004
|
] })
|
|
21644
|
-
] }),
|
|
21645
|
-
/* @__PURE__ */ jsxRuntime.
|
|
21646
|
-
|
|
21647
|
-
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
] }),
|
|
21654
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
21655
|
-
deliveryBreakdown && !compact && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21656
|
-
"button",
|
|
21657
|
-
{
|
|
21658
|
-
onClick: () => setShowDeliveryBreakdown(!showDeliveryBreakdown),
|
|
21659
|
-
className: "text-base-content/40 hover:text-base-content/70",
|
|
21660
|
-
type: "button",
|
|
21661
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
21662
|
-
"svg",
|
|
21663
|
-
{
|
|
21664
|
-
className: `w-4 h-4 transition-transform ${showDeliveryBreakdown ? "rotate-180" : ""}`,
|
|
21665
|
-
fill: "none",
|
|
21666
|
-
stroke: "currentColor",
|
|
21667
|
-
viewBox: "0 0 24 24",
|
|
21668
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" })
|
|
21669
|
-
}
|
|
21670
|
-
)
|
|
21671
|
-
}
|
|
21672
|
-
),
|
|
21673
|
-
distanceInMiles && onClearAddress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21674
|
-
"button",
|
|
21675
|
-
{
|
|
21676
|
-
type: "button",
|
|
21677
|
-
onClick: onClearAddress,
|
|
21678
|
-
className: "text-red-400 hover:text-red-600 transition-colors flex-shrink-0",
|
|
21679
|
-
title: "Change address",
|
|
21680
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", strokeWidth: 2.5, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
|
|
21681
|
-
}
|
|
21682
|
-
),
|
|
21683
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21684
|
-
"\xA3",
|
|
21685
|
-
pricing.deliveryFee.toFixed(2)
|
|
21686
|
-
] }),
|
|
21687
|
-
!distanceInMiles && onPlaceSelect && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base-content/40 text-xs italic", children: "(min)" })
|
|
21688
|
-
] })
|
|
22005
|
+
] }, index)) }),
|
|
22006
|
+
deliveryBreakdown?.requiresCustomQuote && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 p-2 bg-warning/10 border border-warning/30 rounded text-warning-content", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: "\u26A0\uFE0F Delivery exceeds 6 miles. Final fee subject to review." }) })
|
|
22007
|
+
] }),
|
|
22008
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between font-bold text-base-content border-t border-base-300 ${compact ? "text-sm pt-2" : "text-lg pt-3"}`, children: [
|
|
22009
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Total" }),
|
|
22010
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
|
|
22011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
22012
|
+
"\xA3",
|
|
22013
|
+
quote.total.toFixed(2)
|
|
21689
22014
|
] }),
|
|
21690
|
-
|
|
21691
|
-
|
|
21692
|
-
|
|
21693
|
-
session.sessionName,
|
|
21694
|
-
":"
|
|
21695
|
-
] }),
|
|
21696
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
21697
|
-
"\xA3",
|
|
21698
|
-
session.deliveryFee.toFixed(2)
|
|
21699
|
-
] })
|
|
21700
|
-
] }, index)) }),
|
|
21701
|
-
deliveryBreakdown?.requiresCustomQuote && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 p-2 bg-warning/10 border border-warning/30 rounded text-warning-content", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: "\u26A0\uFE0F Delivery exceeds 6 miles. Final fee subject to review." }) })
|
|
21702
|
-
] }),
|
|
21703
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between font-bold text-base-content border-t border-base-300 ${compact ? "text-sm pt-2" : "text-lg pt-3"}`, children: [
|
|
21704
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Total" }),
|
|
21705
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
|
|
21706
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
21707
|
-
"\xA3",
|
|
21708
|
-
pricing.total.toFixed(2)
|
|
21709
|
-
] }),
|
|
21710
|
-
(pricing.totalDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs line-through font-normal text-base-content/50", children: [
|
|
21711
|
-
"\xA3",
|
|
21712
|
-
(pricing.subtotal + pricing.deliveryFee).toFixed(2)
|
|
21713
|
-
] })
|
|
22015
|
+
(quote.totalDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs line-through font-normal text-base-content/50", children: [
|
|
22016
|
+
"\xA3",
|
|
22017
|
+
(quote.subtotal + quote.deliveryFee).toFixed(2)
|
|
21714
22018
|
] })
|
|
21715
22019
|
] })
|
|
21716
|
-
] })
|
|
21717
|
-
}
|
|
21718
|
-
if (estimatedTotal !== void 0) {
|
|
21719
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between pt-4 border-t border-base-300", children: [
|
|
21720
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-base-content", children: "Estimated Total:" }),
|
|
21721
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-bold text-xl text-base-content", children: [
|
|
21722
|
-
"\xA3",
|
|
21723
|
-
estimatedTotal.toFixed(2)
|
|
21724
|
-
] })
|
|
21725
|
-
] });
|
|
21726
|
-
}
|
|
21727
|
-
return null;
|
|
22020
|
+
] })
|
|
22021
|
+
] });
|
|
21728
22022
|
}
|
|
21729
22023
|
function ViewOrderModal({
|
|
21730
22024
|
isOpen,
|
|
@@ -22882,7 +23176,9 @@ function RestaurantMenuBrowser({
|
|
|
22882
23176
|
const [isDescriptionExpanded, setIsDescriptionExpanded] = react.useState(false);
|
|
22883
23177
|
const [isDescriptionOverflowing, setIsDescriptionOverflowing] = react.useState(false);
|
|
22884
23178
|
const descriptionRef = react.useRef(null);
|
|
22885
|
-
const [restaurantMenuItems, setRestaurantMenuItems] = react.useState(
|
|
23179
|
+
const [restaurantMenuItems, setRestaurantMenuItems] = react.useState(
|
|
23180
|
+
[]
|
|
23181
|
+
);
|
|
22886
23182
|
const [restaurantMenuItemsLoading, setRestaurantMenuItemsLoading] = react.useState(false);
|
|
22887
23183
|
const restaurantItemsCache = react.useRef(/* @__PURE__ */ new Map());
|
|
22888
23184
|
react.useEffect(() => {
|
|
@@ -22956,12 +23252,19 @@ function RestaurantMenuBrowser({
|
|
|
22956
23252
|
closeRestaurantSearch();
|
|
22957
23253
|
};
|
|
22958
23254
|
const scrollTimer = setTimeout(() => {
|
|
22959
|
-
document.addEventListener("scroll", handleScroll, {
|
|
23255
|
+
document.addEventListener("scroll", handleScroll, {
|
|
23256
|
+
capture: true,
|
|
23257
|
+
passive: true
|
|
23258
|
+
});
|
|
22960
23259
|
}, 100);
|
|
22961
|
-
document.addEventListener("pointerdown", handlePointerDown, {
|
|
23260
|
+
document.addEventListener("pointerdown", handlePointerDown, {
|
|
23261
|
+
capture: true
|
|
23262
|
+
});
|
|
22962
23263
|
return () => {
|
|
22963
23264
|
clearTimeout(scrollTimer);
|
|
22964
|
-
document.removeEventListener("pointerdown", handlePointerDown, {
|
|
23265
|
+
document.removeEventListener("pointerdown", handlePointerDown, {
|
|
23266
|
+
capture: true
|
|
23267
|
+
});
|
|
22965
23268
|
document.removeEventListener("scroll", handleScroll, { capture: true });
|
|
22966
23269
|
};
|
|
22967
23270
|
}, [isRestaurantSearchOpen, restaurantSearchQuery]);
|
|
@@ -23244,7 +23547,10 @@ function RestaurantMenuBrowser({
|
|
|
23244
23547
|
() => availableRestaurants.find((r) => r.id === selectedRestaurantId) || null,
|
|
23245
23548
|
[availableRestaurants, selectedRestaurantId]
|
|
23246
23549
|
);
|
|
23247
|
-
const restaurantItems = react.useMemo(
|
|
23550
|
+
const restaurantItems = react.useMemo(
|
|
23551
|
+
() => dietaryFilteredItems,
|
|
23552
|
+
[dietaryFilteredItems]
|
|
23553
|
+
);
|
|
23248
23554
|
const filteredRestaurantItems = react.useMemo(() => {
|
|
23249
23555
|
if (!isRestaurantSearchActive) return restaurantItems;
|
|
23250
23556
|
const query = restaurantSearchQuery.toLowerCase();
|
|
@@ -23357,17 +23663,31 @@ function RestaurantMenuBrowser({
|
|
|
23357
23663
|
isRestaurantSearchActive
|
|
23358
23664
|
]);
|
|
23359
23665
|
const firstMenuGroupName = groupedItems[0]?.name ?? null;
|
|
23360
|
-
const ensureMenuItemsForBundle = react.useCallback(
|
|
23361
|
-
|
|
23362
|
-
|
|
23363
|
-
|
|
23364
|
-
|
|
23365
|
-
missing.
|
|
23366
|
-
restaurantItemsCache.current.
|
|
23367
|
-
|
|
23368
|
-
|
|
23369
|
-
|
|
23370
|
-
|
|
23666
|
+
const ensureMenuItemsForBundle = react.useCallback(
|
|
23667
|
+
async (bundle) => {
|
|
23668
|
+
const restaurantIds = [
|
|
23669
|
+
...new Set(bundle.items.map((i) => i.restaurantId).filter(Boolean))
|
|
23670
|
+
];
|
|
23671
|
+
const missing = restaurantIds.filter(
|
|
23672
|
+
(id) => !restaurantItemsCache.current.has(id)
|
|
23673
|
+
);
|
|
23674
|
+
if (missing.length > 0) {
|
|
23675
|
+
const fetched = await Promise.all(
|
|
23676
|
+
missing.map((id) => cateringService.getMenuItemsByRestaurant(id))
|
|
23677
|
+
);
|
|
23678
|
+
missing.forEach((id, idx) => {
|
|
23679
|
+
restaurantItemsCache.current.set(
|
|
23680
|
+
id,
|
|
23681
|
+
(fetched[idx] || []).map(mapToMenuItem)
|
|
23682
|
+
);
|
|
23683
|
+
});
|
|
23684
|
+
}
|
|
23685
|
+
return restaurantIds.flatMap(
|
|
23686
|
+
(id) => restaurantItemsCache.current.get(id) ?? []
|
|
23687
|
+
);
|
|
23688
|
+
},
|
|
23689
|
+
[]
|
|
23690
|
+
);
|
|
23371
23691
|
const handleAddBundle = react.useCallback(
|
|
23372
23692
|
async (bundle, guestQuantity) => {
|
|
23373
23693
|
setAddingBundleId(bundle.id);
|
|
@@ -23549,7 +23869,7 @@ function RestaurantMenuBrowser({
|
|
|
23549
23869
|
}
|
|
23550
23870
|
),
|
|
23551
23871
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 p-3 flex flex-col relative", children: [
|
|
23552
|
-
restaurant.logoImageUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-1/2 right-2 -translate-y-1/2 w-12 h-12 rounded-full overflow-hidden border-2 border-white
|
|
23872
|
+
restaurant.logoImageUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-1/2 right-2 -translate-y-1/2 w-12 h-12 rounded-full overflow-hidden border-2 border-white", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23553
23873
|
"img",
|
|
23554
23874
|
{
|
|
23555
23875
|
src: restaurant.logoImageUrl,
|
|
@@ -23563,7 +23883,7 @@ function RestaurantMenuBrowser({
|
|
|
23563
23883
|
restaurant.minCateringOrderQuantity,
|
|
23564
23884
|
" items"
|
|
23565
23885
|
] }) }) : null,
|
|
23566
|
-
isUnavailableForSession && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 min-h-8 space-y-
|
|
23886
|
+
isUnavailableForSession && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 min-h-8 space-y-1.5", children: [
|
|
23567
23887
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23568
23888
|
"div",
|
|
23569
23889
|
{
|
|
@@ -23579,68 +23899,35 @@ function RestaurantMenuBrowser({
|
|
|
23579
23899
|
]
|
|
23580
23900
|
}
|
|
23581
23901
|
),
|
|
23582
|
-
/* @__PURE__ */ jsxRuntime.
|
|
23583
|
-
|
|
23584
|
-
|
|
23585
|
-
{
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
{
|
|
23591
|
-
className: `h-3.5 w-3.5 flex-shrink-0 ${cateringWindowInfo.isAvailable ? "text-gray-400" : "text-red-500"}`
|
|
23592
|
-
}
|
|
23593
|
-
),
|
|
23594
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-1", title: hoursTooltipText, children: cateringWindowInfo.text })
|
|
23595
|
-
]
|
|
23596
|
-
}
|
|
23597
|
-
),
|
|
23598
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23599
|
-
"button",
|
|
23600
|
-
{
|
|
23601
|
-
ref: (element) => {
|
|
23602
|
-
if (element) {
|
|
23603
|
-
hoursInfoButtonRefs.current.set(restaurant.id, element);
|
|
23604
|
-
} else {
|
|
23605
|
-
hoursInfoButtonRefs.current.delete(restaurant.id);
|
|
23606
|
-
}
|
|
23607
|
-
},
|
|
23608
|
-
type: "button",
|
|
23609
|
-
"aria-label": `View weekly catering hours for ${restaurant.restaurant_name}`,
|
|
23610
|
-
onClick: (event) => {
|
|
23611
|
-
event.stopPropagation();
|
|
23612
|
-
if (!isMobileViewport) {
|
|
23613
|
-
updateHoursInfoPosition(restaurant.id);
|
|
23614
|
-
}
|
|
23615
|
-
setOpenHoursInfoRestaurantId(
|
|
23616
|
-
(current) => current === restaurant.id ? null : restaurant.id
|
|
23617
|
-
);
|
|
23618
|
-
},
|
|
23619
|
-
onMouseEnter: () => {
|
|
23620
|
-
if (!isMobileViewport) {
|
|
23621
|
-
updateHoursInfoPosition(restaurant.id);
|
|
23622
|
-
setHoveredHoursInfoRestaurantId(restaurant.id);
|
|
23623
|
-
}
|
|
23624
|
-
},
|
|
23625
|
-
onMouseLeave: () => {
|
|
23626
|
-
if (!isMobileViewport) {
|
|
23627
|
-
setHoveredHoursInfoRestaurantId(
|
|
23628
|
-
(current) => current === restaurant.id ? null : current
|
|
23629
|
-
);
|
|
23902
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
23903
|
+
"div",
|
|
23904
|
+
{
|
|
23905
|
+
className: `flex min-w-0 items-center gap-1.5 text-[11px] leading-4 ${cateringWindowInfo.isAvailable ? "text-gray-500" : "text-red-500 font-semibold"}`,
|
|
23906
|
+
children: [
|
|
23907
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23908
|
+
lucideReact.Clock3,
|
|
23909
|
+
{
|
|
23910
|
+
className: `h-3.5 w-3.5 flex-shrink-0 ${cateringWindowInfo.isAvailable ? "text-gray-400" : "text-red-500"}`
|
|
23630
23911
|
}
|
|
23631
|
-
|
|
23632
|
-
|
|
23633
|
-
|
|
23634
|
-
|
|
23635
|
-
|
|
23636
|
-
) })
|
|
23637
|
-
] })
|
|
23912
|
+
),
|
|
23913
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-1", title: hoursTooltipText, children: cateringWindowInfo.text })
|
|
23914
|
+
]
|
|
23915
|
+
}
|
|
23916
|
+
) })
|
|
23638
23917
|
] }),
|
|
23639
23918
|
(() => {
|
|
23640
23919
|
if (isUnavailableForSession) return null;
|
|
23641
23920
|
const restaurantLoc = restaurant.address?.location;
|
|
23642
|
-
const userLoc = contactInfo?.latitude && contactInfo?.longitude ? {
|
|
23643
|
-
|
|
23921
|
+
const userLoc = contactInfo?.latitude && contactInfo?.longitude ? {
|
|
23922
|
+
latitude: contactInfo.latitude,
|
|
23923
|
+
longitude: contactInfo.longitude
|
|
23924
|
+
} : null;
|
|
23925
|
+
const distance = userLoc && restaurantLoc ? haversineDistanceMiles(
|
|
23926
|
+
userLoc.latitude,
|
|
23927
|
+
userLoc.longitude,
|
|
23928
|
+
restaurantLoc.latitude,
|
|
23929
|
+
restaurantLoc.longitude
|
|
23930
|
+
) : null;
|
|
23644
23931
|
const rating = restaurant.averageRating && parseFloat(restaurant.averageRating) > 0 ? parseFloat(restaurant.averageRating) : null;
|
|
23645
23932
|
const priceRange = restaurant.priceRange;
|
|
23646
23933
|
const tags = restaurant.tags?.filter(Boolean) ?? [];
|
|
@@ -23763,27 +24050,34 @@ function RestaurantMenuBrowser({
|
|
|
23763
24050
|
] }),
|
|
23764
24051
|
document.body
|
|
23765
24052
|
) : null;
|
|
23766
|
-
const renderCategoryFilters = () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
23767
|
-
"
|
|
24053
|
+
const renderCategoryFilters = () => /* @__PURE__ */ jsxRuntime.jsx(
|
|
24054
|
+
"div",
|
|
23768
24055
|
{
|
|
23769
|
-
|
|
23770
|
-
|
|
23771
|
-
),
|
|
23772
|
-
|
|
23773
|
-
|
|
23774
|
-
|
|
23775
|
-
|
|
23776
|
-
|
|
23777
|
-
|
|
23778
|
-
|
|
23779
|
-
|
|
23780
|
-
|
|
23781
|
-
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
|
|
24056
|
+
ref: expandedSessionIndex === sessionIndex ? categoriesRowRef : void 0,
|
|
24057
|
+
style: { contain: "inline-size" },
|
|
24058
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto pb-2 pt-1 scrollbar-hide", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 md:gap-3", children: categoriesLoading ? [...Array(8)].map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(CategoryButtonSkeleton, {}, index)) : categories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
24059
|
+
"button",
|
|
24060
|
+
{
|
|
24061
|
+
onClick: () => setSelectedCategoryId(
|
|
24062
|
+
selectedCategoryId === category.id ? null : category.id
|
|
24063
|
+
),
|
|
24064
|
+
className: `w-20 flex-shrink-0 py-1.5 rounded-xl text-sm font-medium transition-all border flex flex-col items-center justify-center gap-0.5 leading-none ${category.images || category.icon ? "min-h-16" : ""} ${selectedCategoryId === category.id ? "border-primary text-primary" : "border-transparent text-gray-700 hover:text-primary"}`,
|
|
24065
|
+
children: [
|
|
24066
|
+
category.images ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
24067
|
+
"img",
|
|
24068
|
+
{
|
|
24069
|
+
src: category.images,
|
|
24070
|
+
alt: category.name,
|
|
24071
|
+
className: "h-8 w-8 md:h-9 md:w-9 object-cover rounded-full border border-base-300"
|
|
24072
|
+
}
|
|
24073
|
+
) : category.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 md:h-7 items-center justify-center text-xl md:text-2xl leading-none", children: category.icon }) : null,
|
|
24074
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-xs md:text-sm", children: category.name })
|
|
24075
|
+
]
|
|
24076
|
+
},
|
|
24077
|
+
category.id
|
|
24078
|
+
)) }) })
|
|
24079
|
+
}
|
|
24080
|
+
);
|
|
23787
24081
|
if (selectedRestaurantId && selectedRestaurant) {
|
|
23788
24082
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { contain: "inline-size" }, children: [
|
|
23789
24083
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -23816,14 +24110,27 @@ function RestaurantMenuBrowser({
|
|
|
23816
24110
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900", children: selectedRestaurant.restaurant_name }),
|
|
23817
24111
|
(() => {
|
|
23818
24112
|
const restaurantLoc = selectedRestaurant.address?.location;
|
|
23819
|
-
const userLoc = contactInfo?.latitude && contactInfo?.longitude ? {
|
|
23820
|
-
|
|
24113
|
+
const userLoc = contactInfo?.latitude && contactInfo?.longitude ? {
|
|
24114
|
+
latitude: contactInfo.latitude,
|
|
24115
|
+
longitude: contactInfo.longitude
|
|
24116
|
+
} : null;
|
|
24117
|
+
const distance = userLoc && restaurantLoc ? haversineDistanceMiles(
|
|
24118
|
+
userLoc.latitude,
|
|
24119
|
+
userLoc.longitude,
|
|
24120
|
+
restaurantLoc.latitude,
|
|
24121
|
+
restaurantLoc.longitude
|
|
24122
|
+
) : null;
|
|
23821
24123
|
const rating = selectedRestaurant.averageRating && parseFloat(selectedRestaurant.averageRating) > 0 ? parseFloat(selectedRestaurant.averageRating) : null;
|
|
23822
24124
|
const advanceNoticeText = getRestaurantAdvanceNoticeText(selectedRestaurant);
|
|
23823
|
-
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24125
|
+
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24126
|
+
selectedRestaurant,
|
|
24127
|
+
activeSession?.sessionDate,
|
|
24128
|
+
activeSession?.eventTime
|
|
24129
|
+
);
|
|
23824
24130
|
const priceRange = selectedRestaurant.priceRange;
|
|
23825
24131
|
const tags = selectedRestaurant.tags?.filter(Boolean).slice(0, 3) ?? [];
|
|
23826
|
-
if (!rating && !distance && !advanceNoticeText && !priceRange && tags.length === 0)
|
|
24132
|
+
if (!rating && !distance && !advanceNoticeText && !priceRange && tags.length === 0)
|
|
24133
|
+
return null;
|
|
23827
24134
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 mb-1 flex flex-col gap-1", children: [
|
|
23828
24135
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [
|
|
23829
24136
|
rating !== null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-sm text-gray-500 whitespace-nowrap", children: [
|
|
@@ -23839,10 +24146,21 @@ function RestaurantMenuBrowser({
|
|
|
23839
24146
|
priceRange && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500 whitespace-nowrap", children: priceRange }),
|
|
23840
24147
|
advanceNoticeText && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
23841
24148
|
(rating !== null || distance !== null || priceRange) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "\xB7" }),
|
|
23842
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
24149
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
24150
|
+
"span",
|
|
24151
|
+
{
|
|
24152
|
+
className: `flex items-center gap-1 text-sm whitespace-nowrap ${noticeMet ? "text-gray-500" : "text-gray-500"}`,
|
|
24153
|
+
children: [
|
|
24154
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
24155
|
+
lucideReact.Clock3,
|
|
24156
|
+
{
|
|
24157
|
+
className: `h-3.5 w-3.5 flex-shrink-0 ${noticeMet ? "text-gray-400" : "text-red-500"}`
|
|
24158
|
+
}
|
|
24159
|
+
),
|
|
24160
|
+
advanceNoticeText
|
|
24161
|
+
]
|
|
24162
|
+
}
|
|
24163
|
+
)
|
|
23846
24164
|
] })
|
|
23847
24165
|
] }),
|
|
23848
24166
|
tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: tags.join(" - ") })
|
|
@@ -23877,42 +24195,79 @@ function RestaurantMenuBrowser({
|
|
|
23877
24195
|
const promos = selectedRestaurantId ? restaurantPromotions[selectedRestaurantId] ?? [] : [];
|
|
23878
24196
|
if (promos.length === 0) return null;
|
|
23879
24197
|
const discountLabel = (promo) => promo.promotionType === "BUY_MORE_SAVE_MORE" && promo.discountTiers?.length ? `Up to ${Math.max(...promo.discountTiers.map((t) => Number(t.discountPercentage)))}% OFF` : `${Number(promo.discountPercentage)}% OFF`;
|
|
23880
|
-
const endDateLabel = (promo) => new Date(promo.endDate).toLocaleDateString("en-GB", {
|
|
23881
|
-
|
|
24198
|
+
const endDateLabel = (promo) => new Date(promo.endDate).toLocaleDateString("en-GB", {
|
|
24199
|
+
day: "numeric",
|
|
24200
|
+
month: "short"
|
|
24201
|
+
});
|
|
24202
|
+
const sortedTiers = (promo) => [...promo.discountTiers ?? []].sort(
|
|
24203
|
+
(a, b) => a.minQuantity - b.minQuantity
|
|
24204
|
+
);
|
|
23882
24205
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3", children: [
|
|
23883
24206
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
23884
24207
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900", children: "Active Offers" }),
|
|
23885
24208
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-primary text-white text-xs font-bold px-2 py-0.5 rounded-full", children: promos.length })
|
|
23886
24209
|
] }),
|
|
23887
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3 overflow-x-auto pb-1 scrollbar-hide snap-x snap-mandatory", children: promos.map((promo) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
23901
|
-
|
|
23902
|
-
|
|
23903
|
-
|
|
23904
|
-
|
|
23905
|
-
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
|
|
23911
|
-
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
|
|
23915
|
-
|
|
24210
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3 overflow-x-auto pb-1 scrollbar-hide snap-x snap-mandatory", children: promos.map((promo) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
24211
|
+
"div",
|
|
24212
|
+
{
|
|
24213
|
+
className: "flex-shrink-0 snap-start bg-white border border-base-200 rounded-2xl p-4 min-w-[220px] max-w-[280px] shadow-sm",
|
|
24214
|
+
children: [
|
|
24215
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2 mb-2", children: [
|
|
24216
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold text-gray-400 uppercase tracking-wide truncate", children: promo.name }),
|
|
24217
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
24218
|
+
"svg",
|
|
24219
|
+
{
|
|
24220
|
+
className: "w-4 h-4 flex-shrink-0 text-primary",
|
|
24221
|
+
fill: "none",
|
|
24222
|
+
stroke: "currentColor",
|
|
24223
|
+
viewBox: "0 0 24 24",
|
|
24224
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
24225
|
+
"path",
|
|
24226
|
+
{
|
|
24227
|
+
strokeLinecap: "round",
|
|
24228
|
+
strokeLinejoin: "round",
|
|
24229
|
+
strokeWidth: 2,
|
|
24230
|
+
d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
|
|
24231
|
+
}
|
|
24232
|
+
)
|
|
24233
|
+
}
|
|
24234
|
+
)
|
|
24235
|
+
] }),
|
|
24236
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-black text-gray-900 leading-none", children: discountLabel(promo) }),
|
|
24237
|
+
promo.promotionType === "BUY_MORE_SAVE_MORE" && sortedTiers(promo).length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 mt-2.5", children: sortedTiers(promo).map((tier, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
24238
|
+
"span",
|
|
24239
|
+
{
|
|
24240
|
+
className: "text-[11px] font-medium bg-primary/10 text-primary px-2 py-0.5 rounded-full",
|
|
24241
|
+
children: [
|
|
24242
|
+
tier.minQuantity,
|
|
24243
|
+
"+ \u2192",
|
|
24244
|
+
" ",
|
|
24245
|
+
Number(tier.discountPercentage),
|
|
24246
|
+
"%"
|
|
24247
|
+
]
|
|
24248
|
+
},
|
|
24249
|
+
idx
|
|
24250
|
+
)) }),
|
|
24251
|
+
promo.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 mt-1.5 line-clamp-2", children: promo.description }),
|
|
24252
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-x-2 gap-y-0.5 mt-2.5 text-xs text-gray-400", children: [
|
|
24253
|
+
promo.minOrderAmount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
24254
|
+
"Min. \xA3",
|
|
24255
|
+
promo.minOrderAmount
|
|
24256
|
+
] }),
|
|
24257
|
+
promo.maxDiscountAmount && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
24258
|
+
"\xB7 Max. \xA3",
|
|
24259
|
+
promo.maxDiscountAmount,
|
|
24260
|
+
" off"
|
|
24261
|
+
] }),
|
|
24262
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
24263
|
+
"\xB7 Until ",
|
|
24264
|
+
endDateLabel(promo)
|
|
24265
|
+
] })
|
|
24266
|
+
] })
|
|
24267
|
+
]
|
|
24268
|
+
},
|
|
24269
|
+
promo.id
|
|
24270
|
+
)) })
|
|
23916
24271
|
] });
|
|
23917
24272
|
})(),
|
|
23918
24273
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: renderDietaryFilters() }),
|
|
@@ -23925,58 +24280,64 @@ function RestaurantMenuBrowser({
|
|
|
23925
24280
|
top: stickyTopOffset + 8
|
|
23926
24281
|
},
|
|
23927
24282
|
children: [
|
|
23928
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23929
|
-
|
|
23930
|
-
|
|
23931
|
-
{
|
|
23932
|
-
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
23938
|
-
"
|
|
23939
|
-
{
|
|
23940
|
-
|
|
23941
|
-
|
|
23942
|
-
|
|
23943
|
-
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
|
|
24283
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
24284
|
+
"div",
|
|
24285
|
+
{
|
|
24286
|
+
className: `relative flex-1 min-w-0 ${isRestaurantSearchOpen ? "hidden md:block" : ""}`,
|
|
24287
|
+
children: [
|
|
24288
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
24289
|
+
"div",
|
|
24290
|
+
{
|
|
24291
|
+
ref: setPillRowEl,
|
|
24292
|
+
"data-pill-row": true,
|
|
24293
|
+
className: "overflow-x-auto scrollbar-hide rounded-full border border-base-300 bg-white/50 px-2 shadow-sm backdrop-blur-md flex items-center h-11",
|
|
24294
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 md:gap-5", children: pillRowGroupNames.map((name) => {
|
|
24295
|
+
const isActive = activeGroupName === name;
|
|
24296
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24297
|
+
"button",
|
|
24298
|
+
{
|
|
24299
|
+
ref: (el) => {
|
|
24300
|
+
if (el) groupButtonRefs.current.set(name, el);
|
|
24301
|
+
else groupButtonRefs.current.delete(name);
|
|
24302
|
+
},
|
|
24303
|
+
onClick: () => handlePillClick(name),
|
|
24304
|
+
className: `flex-shrink-0 whitespace-nowrap rounded-full px-3 py-1.5 text-sm font-semibold transition-colors ${isActive ? "bg-primary text-white" : "text-gray-500 hover:bg-black/5 hover:text-gray-700"}`,
|
|
24305
|
+
children: name
|
|
24306
|
+
},
|
|
24307
|
+
name
|
|
24308
|
+
);
|
|
24309
|
+
}) })
|
|
24310
|
+
}
|
|
24311
|
+
),
|
|
24312
|
+
canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
|
|
24313
|
+
"button",
|
|
24314
|
+
{
|
|
24315
|
+
onClick: () => scrollPillRow("left"),
|
|
24316
|
+
className: "group absolute left-0 top-0 bottom-0 z-10 flex w-14 items-center justify-start rounded-l-full pl-3",
|
|
24317
|
+
style: {
|
|
24318
|
+
background: "linear-gradient(to right, rgba(255,255,255,0.95) 50%, transparent)"
|
|
23947
24319
|
},
|
|
23948
|
-
|
|
23949
|
-
|
|
23950
|
-
|
|
23951
|
-
|
|
23952
|
-
|
|
23953
|
-
|
|
23954
|
-
|
|
23955
|
-
|
|
23956
|
-
|
|
23957
|
-
|
|
23958
|
-
|
|
23959
|
-
|
|
23960
|
-
|
|
23961
|
-
|
|
23962
|
-
|
|
23963
|
-
|
|
23964
|
-
|
|
23965
|
-
|
|
23966
|
-
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
onClick: () => scrollPillRow("right"),
|
|
23970
|
-
className: "group absolute right-0 top-0 bottom-0 z-10 flex w-14 items-center justify-end rounded-r-full pr-3",
|
|
23971
|
-
style: {
|
|
23972
|
-
background: "linear-gradient(to left, rgba(255,255,255,0.95) 50%, transparent)"
|
|
23973
|
-
},
|
|
23974
|
-
"aria-label": "Scroll right",
|
|
23975
|
-
type: "button",
|
|
23976
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center rounded-full p-1 transition-colors group-hover:bg-gray-300/50", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5 text-gray-600" }) })
|
|
23977
|
-
}
|
|
23978
|
-
)
|
|
23979
|
-
] }),
|
|
24320
|
+
"aria-label": "Scroll left",
|
|
24321
|
+
type: "button",
|
|
24322
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center rounded-full p-1 transition-colors group-hover:bg-gray-300/50", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-5 w-5 text-gray-600" }) })
|
|
24323
|
+
}
|
|
24324
|
+
),
|
|
24325
|
+
canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
|
|
24326
|
+
"button",
|
|
24327
|
+
{
|
|
24328
|
+
onClick: () => scrollPillRow("right"),
|
|
24329
|
+
className: "group absolute right-0 top-0 bottom-0 z-10 flex w-14 items-center justify-end rounded-r-full pr-3",
|
|
24330
|
+
style: {
|
|
24331
|
+
background: "linear-gradient(to left, rgba(255,255,255,0.95) 50%, transparent)"
|
|
24332
|
+
},
|
|
24333
|
+
"aria-label": "Scroll right",
|
|
24334
|
+
type: "button",
|
|
24335
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center rounded-full p-1 transition-colors group-hover:bg-gray-300/50", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5 text-gray-600" }) })
|
|
24336
|
+
}
|
|
24337
|
+
)
|
|
24338
|
+
]
|
|
24339
|
+
}
|
|
24340
|
+
),
|
|
23980
24341
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23981
24342
|
"div",
|
|
23982
24343
|
{
|
|
@@ -24224,15 +24585,33 @@ function RestaurantMenuBrowser({
|
|
|
24224
24585
|
] })
|
|
24225
24586
|
] }),
|
|
24226
24587
|
(() => {
|
|
24227
|
-
const matched = apiSearchResults.restaurants.map(
|
|
24588
|
+
const matched = apiSearchResults.restaurants.map(
|
|
24589
|
+
(r) => availableRestaurants.find((ar) => ar.id === r.id)
|
|
24590
|
+
).filter((r) => r !== void 0);
|
|
24228
24591
|
const available = matched.filter((r) => {
|
|
24229
|
-
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24230
|
-
|
|
24592
|
+
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24593
|
+
r,
|
|
24594
|
+
activeSession?.sessionDate,
|
|
24595
|
+
activeSession?.eventTime
|
|
24596
|
+
);
|
|
24597
|
+
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24598
|
+
r,
|
|
24599
|
+
activeSession?.sessionDate,
|
|
24600
|
+
activeSession?.eventTime
|
|
24601
|
+
);
|
|
24231
24602
|
return windowInfo.isAvailable && noticeMet;
|
|
24232
24603
|
});
|
|
24233
24604
|
const unavailable = matched.filter((r) => {
|
|
24234
|
-
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24235
|
-
|
|
24605
|
+
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24606
|
+
r,
|
|
24607
|
+
activeSession?.sessionDate,
|
|
24608
|
+
activeSession?.eventTime
|
|
24609
|
+
);
|
|
24610
|
+
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24611
|
+
r,
|
|
24612
|
+
activeSession?.sessionDate,
|
|
24613
|
+
activeSession?.eventTime
|
|
24614
|
+
);
|
|
24236
24615
|
return !windowInfo.isAvailable || !noticeMet;
|
|
24237
24616
|
});
|
|
24238
24617
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
@@ -24286,20 +24665,42 @@ function RestaurantMenuBrowser({
|
|
|
24286
24665
|
] })
|
|
24287
24666
|
] }) : null : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: restaurantsLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3 mt-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(RestaurantMenuBrowserCardSkeleton, {}, i)) }) : filteredRestaurants.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-6 mt-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500 text-sm", children: "No restaurants found." }) }) : (() => {
|
|
24288
24667
|
const available = filteredRestaurants.filter((r) => {
|
|
24289
|
-
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24290
|
-
|
|
24668
|
+
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24669
|
+
r,
|
|
24670
|
+
activeSession?.sessionDate,
|
|
24671
|
+
activeSession?.eventTime
|
|
24672
|
+
);
|
|
24673
|
+
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24674
|
+
r,
|
|
24675
|
+
activeSession?.sessionDate,
|
|
24676
|
+
activeSession?.eventTime
|
|
24677
|
+
);
|
|
24291
24678
|
return windowInfo.isAvailable && noticeMet;
|
|
24292
24679
|
});
|
|
24293
24680
|
const unavailable = filteredRestaurants.filter((r) => {
|
|
24294
|
-
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24295
|
-
|
|
24681
|
+
const windowInfo = getRestaurantCateringWindowInfo(
|
|
24682
|
+
r,
|
|
24683
|
+
activeSession?.sessionDate,
|
|
24684
|
+
activeSession?.eventTime
|
|
24685
|
+
);
|
|
24686
|
+
const noticeMet = isRestaurantAdvanceNoticeMet(
|
|
24687
|
+
r,
|
|
24688
|
+
activeSession?.sessionDate,
|
|
24689
|
+
activeSession?.eventTime
|
|
24690
|
+
);
|
|
24296
24691
|
return !windowInfo.isAvailable || !noticeMet;
|
|
24297
24692
|
});
|
|
24298
24693
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: restaurantListRef, className: "mt-3 space-y-6", children: [
|
|
24299
|
-
available.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3", children: available.map((restaurant) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", children: renderRestaurantCard(
|
|
24694
|
+
available.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3", children: available.map((restaurant) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", children: renderRestaurantCard(
|
|
24695
|
+
restaurant,
|
|
24696
|
+
() => handleSelectRestaurant(restaurant.id)
|
|
24697
|
+
) }, restaurant.id)) }),
|
|
24300
24698
|
unavailable.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
24301
24699
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold text-gray-400 uppercase tracking-widest mb-3", children: "Unavailable today" }),
|
|
24302
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3", children: unavailable.map((restaurant) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", children: renderRestaurantCard(
|
|
24700
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3", children: unavailable.map((restaurant) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full", children: renderRestaurantCard(
|
|
24701
|
+
restaurant,
|
|
24702
|
+
() => handleSelectRestaurant(restaurant.id)
|
|
24703
|
+
) }, restaurant.id)) })
|
|
24303
24704
|
] })
|
|
24304
24705
|
] });
|
|
24305
24706
|
})() })
|
|
@@ -25173,7 +25574,8 @@ function AddressAutocomplete({
|
|
|
25173
25574
|
onPlaceSelect,
|
|
25174
25575
|
onClearAddress,
|
|
25175
25576
|
error,
|
|
25176
|
-
hasValidAddress
|
|
25577
|
+
hasValidAddress,
|
|
25578
|
+
hideChangeButton
|
|
25177
25579
|
}) {
|
|
25178
25580
|
const {
|
|
25179
25581
|
inputRef,
|
|
@@ -25213,7 +25615,7 @@ function AddressAutocomplete({
|
|
|
25213
25615
|
className: `address-search-input w-full bg-gray-50 border rounded-lg px-4 py-2.5 text-base text-base-content placeholder:text-base-content/50 focus:outline-none focus:ring-2 focus:ring-dark-pink/20 focus:border-dark-pink transition-all ${shownError ? "border-error" : hasValidAddress ? "border-success" : "border-base-300"}`
|
|
25214
25616
|
}
|
|
25215
25617
|
),
|
|
25216
|
-
hasValidAddress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
25618
|
+
hasValidAddress && !hideChangeButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
25217
25619
|
"button",
|
|
25218
25620
|
{
|
|
25219
25621
|
type: "button",
|
|
@@ -27314,6 +27716,40 @@ function CheckoutScreen() {
|
|
|
27314
27716
|
] })
|
|
27315
27717
|
] });
|
|
27316
27718
|
}
|
|
27719
|
+
function useShareCart() {
|
|
27720
|
+
const { mealSessions, eventDetails } = useCateringState();
|
|
27721
|
+
const [status, setStatus] = react.useState("idle");
|
|
27722
|
+
const hasItems = mealSessions.some((s) => s.orderItems.length > 0);
|
|
27723
|
+
const handleShare = async () => {
|
|
27724
|
+
if (status === "loading") return;
|
|
27725
|
+
setStatus("loading");
|
|
27726
|
+
try {
|
|
27727
|
+
const snapshot = serializeCartToSnapshot(
|
|
27728
|
+
mealSessions,
|
|
27729
|
+
eventDetails ? {
|
|
27730
|
+
eventType: eventDetails.eventType,
|
|
27731
|
+
eventDate: eventDetails.eventDate,
|
|
27732
|
+
eventTime: eventDetails.eventTime,
|
|
27733
|
+
guestCount: eventDetails.guestCount
|
|
27734
|
+
} : void 0
|
|
27735
|
+
);
|
|
27736
|
+
const { id } = await cateringService.createSharedCart(snapshot);
|
|
27737
|
+
try {
|
|
27738
|
+
await navigator.clipboard.writeText(id);
|
|
27739
|
+
} catch {
|
|
27740
|
+
window.prompt("Copy your cart code:", id);
|
|
27741
|
+
}
|
|
27742
|
+
setStatus("done");
|
|
27743
|
+
setTimeout(() => setStatus("idle"), 2500);
|
|
27744
|
+
} catch (err) {
|
|
27745
|
+
console.error("[catering] failed to create share link", err);
|
|
27746
|
+
setStatus("error");
|
|
27747
|
+
setTimeout(() => setStatus("idle"), 2500);
|
|
27748
|
+
}
|
|
27749
|
+
};
|
|
27750
|
+
const label = status === "loading" ? "Creating code\u2026" : status === "done" ? "Code copied" : status === "error" ? "Try again" : "Share cart";
|
|
27751
|
+
return { status, hasItems, handleShare, label };
|
|
27752
|
+
}
|
|
27317
27753
|
function EmptySessionWarningModal({
|
|
27318
27754
|
sessionName,
|
|
27319
27755
|
onRemove,
|
|
@@ -27680,6 +28116,182 @@ function ValidationAlertModal({
|
|
|
27680
28116
|
)
|
|
27681
28117
|
] }) });
|
|
27682
28118
|
}
|
|
28119
|
+
function parseHHMM2(value) {
|
|
28120
|
+
if (!value) return null;
|
|
28121
|
+
const parts = value.split(":");
|
|
28122
|
+
if (parts.length !== 2) return null;
|
|
28123
|
+
const h = Number(parts[0]);
|
|
28124
|
+
const m = Number(parts[1]);
|
|
28125
|
+
if (!Number.isFinite(h) || !Number.isFinite(m)) return null;
|
|
28126
|
+
return h * 60 + m;
|
|
28127
|
+
}
|
|
28128
|
+
function WelcomeModal({ onComplete }) {
|
|
28129
|
+
const { contactInfo, setContactInfo, updateMealSession, mealSessions } = useCateringState();
|
|
28130
|
+
const { initialData, allowedCateringTimes } = useCateringConfig();
|
|
28131
|
+
const [addressUpdate, setAddressUpdate] = react.useState(null);
|
|
28132
|
+
const [sessionDate, setSessionDate] = react.useState("");
|
|
28133
|
+
const [selectedTimeSlot, setSelectedTimeSlot] = react.useState("");
|
|
28134
|
+
react.useEffect(() => {
|
|
28135
|
+
const first = mealSessions[0];
|
|
28136
|
+
if (!first) return;
|
|
28137
|
+
if (first.sessionDate) setSessionDate((prev) => prev || first.sessionDate);
|
|
28138
|
+
if (first.eventTime) setSelectedTimeSlot((prev) => prev || first.eventTime);
|
|
28139
|
+
}, [mealSessions]);
|
|
28140
|
+
const allowedStartMin = parseHHMM2(allowedCateringTimes?.start);
|
|
28141
|
+
const allowedEndMin = parseHHMM2(allowedCateringTimes?.end);
|
|
28142
|
+
const eventStartDate = initialData?.eventStartDate ?? "";
|
|
28143
|
+
const eventEndDate = initialData?.eventEndDate ?? "";
|
|
28144
|
+
const eventStartMin = parseHHMM2(initialData?.eventStartTime);
|
|
28145
|
+
const eventEndMin = parseHHMM2(initialData?.eventEndTime);
|
|
28146
|
+
const lowerBoundMin = (() => {
|
|
28147
|
+
let lo = allowedStartMin ?? null;
|
|
28148
|
+
if (sessionDate && eventStartDate && sessionDate === eventStartDate && eventStartMin != null) {
|
|
28149
|
+
lo = lo == null ? eventStartMin : Math.max(lo, eventStartMin);
|
|
28150
|
+
}
|
|
28151
|
+
return lo;
|
|
28152
|
+
})();
|
|
28153
|
+
const upperBoundMin = (() => {
|
|
28154
|
+
let hi = allowedEndMin ?? null;
|
|
28155
|
+
if (sessionDate && eventEndDate && sessionDate === eventEndDate && eventEndMin != null) {
|
|
28156
|
+
hi = hi == null ? eventEndMin : Math.min(hi, eventEndMin);
|
|
28157
|
+
}
|
|
28158
|
+
return hi;
|
|
28159
|
+
})();
|
|
28160
|
+
const SLOT_DURATION_MIN = 30;
|
|
28161
|
+
const availableSlots = TIME_SLOT_OPTIONS.filter((o) => {
|
|
28162
|
+
const slotStart = parseHHMM2(o.value);
|
|
28163
|
+
if (slotStart == null) return false;
|
|
28164
|
+
const slotEnd = slotStart + SLOT_DURATION_MIN;
|
|
28165
|
+
if (lowerBoundMin != null && slotStart < lowerBoundMin) return false;
|
|
28166
|
+
if (upperBoundMin != null && slotEnd > upperBoundMin) return false;
|
|
28167
|
+
return true;
|
|
28168
|
+
});
|
|
28169
|
+
const dateMin = eventStartDate || getMinDate();
|
|
28170
|
+
const dateMax = eventEndDate || getMaxDate();
|
|
28171
|
+
const handlePlaceSelect = (place) => {
|
|
28172
|
+
const parsed = parsePlaceResult(place);
|
|
28173
|
+
if (!parsed) return;
|
|
28174
|
+
setAddressUpdate(parsed.contactInfo);
|
|
28175
|
+
};
|
|
28176
|
+
const handleClearAddress = () => {
|
|
28177
|
+
setAddressUpdate(null);
|
|
28178
|
+
};
|
|
28179
|
+
const applyAndClose = () => {
|
|
28180
|
+
if (addressUpdate) {
|
|
28181
|
+
setContactInfo({
|
|
28182
|
+
...contactInfo ?? {},
|
|
28183
|
+
...addressUpdate
|
|
28184
|
+
});
|
|
28185
|
+
}
|
|
28186
|
+
if (sessionDate || selectedTimeSlot) {
|
|
28187
|
+
updateMealSession(0, {
|
|
28188
|
+
...sessionDate && { sessionDate },
|
|
28189
|
+
...selectedTimeSlot && { eventTime: selectedTimeSlot }
|
|
28190
|
+
});
|
|
28191
|
+
}
|
|
28192
|
+
onComplete();
|
|
28193
|
+
};
|
|
28194
|
+
const hasAnyDetail = !!addressUpdate || !!sessionDate || !!selectedTimeSlot;
|
|
28195
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-[100] p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl max-w-md w-full max-h-[90vh] overflow-y-auto shadow-xl animate-in zoom-in-95 duration-200", children: [
|
|
28196
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 pt-6 pb-5", children: [
|
|
28197
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-2xl bg-primary flex items-center justify-center shadow-md mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "h-6 w-6 text-white" }) }),
|
|
28198
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold uppercase tracking-widest text-primary", children: "Welcome" }),
|
|
28199
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mt-1 text-xl font-bold text-gray-900 leading-tight", children: "Let's tailor your order" })
|
|
28200
|
+
] }),
|
|
28201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 pb-6", children: [
|
|
28202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 mb-6 space-y-3", children: [
|
|
28203
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
28204
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-7 h-7 flex-shrink-0 rounded-lg bg-primary/10 flex items-center justify-center mt-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { className: "h-4 w-4 text-primary" }) }),
|
|
28205
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600 leading-snug", children: [
|
|
28206
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-800", children: "Delivery address" }),
|
|
28207
|
+
" ",
|
|
28208
|
+
"\u2014 so we surface the restaurants nearest to you."
|
|
28209
|
+
] })
|
|
28210
|
+
] }),
|
|
28211
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
|
|
28212
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-7 h-7 flex-shrink-0 rounded-lg bg-primary/10 flex items-center justify-center mt-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4 text-primary" }) }),
|
|
28213
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-600 leading-snug", children: [
|
|
28214
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-800", children: "Event date & time" }),
|
|
28215
|
+
" ",
|
|
28216
|
+
"\u2014 so we respect each restaurant's opening hours and minimum order notice."
|
|
28217
|
+
] })
|
|
28218
|
+
] })
|
|
28219
|
+
] }),
|
|
28220
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
28221
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28222
|
+
AddressAutocomplete,
|
|
28223
|
+
{
|
|
28224
|
+
onPlaceSelect: handlePlaceSelect,
|
|
28225
|
+
onClearAddress: handleClearAddress,
|
|
28226
|
+
hasValidAddress: !!addressUpdate,
|
|
28227
|
+
hideChangeButton: true
|
|
28228
|
+
}
|
|
28229
|
+
) }),
|
|
28230
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28231
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] font-bold uppercase tracking-widest text-base-content/60 mb-1.5", children: "Event date" }),
|
|
28232
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28233
|
+
DatePickerField,
|
|
28234
|
+
{
|
|
28235
|
+
value: sessionDate,
|
|
28236
|
+
onChange: setSessionDate,
|
|
28237
|
+
min: dateMin,
|
|
28238
|
+
max: dateMax,
|
|
28239
|
+
ariaLabel: "Event date"
|
|
28240
|
+
}
|
|
28241
|
+
)
|
|
28242
|
+
] }),
|
|
28243
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
28244
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] font-bold uppercase tracking-widest text-base-content/60 mb-1.5", children: "Event time" }),
|
|
28245
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28246
|
+
TimeSlotDropdown,
|
|
28247
|
+
{
|
|
28248
|
+
value: selectedTimeSlot,
|
|
28249
|
+
onChange: setSelectedTimeSlot,
|
|
28250
|
+
slots: availableSlots
|
|
28251
|
+
}
|
|
28252
|
+
),
|
|
28253
|
+
lowerBoundMin != null && upperBoundMin != null && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1.5 text-xs text-base-content/60", children: [
|
|
28254
|
+
"Available between",
|
|
28255
|
+
" ",
|
|
28256
|
+
formatTime(
|
|
28257
|
+
Math.floor(lowerBoundMin / 60),
|
|
28258
|
+
lowerBoundMin % 60
|
|
28259
|
+
),
|
|
28260
|
+
" ",
|
|
28261
|
+
"and",
|
|
28262
|
+
" ",
|
|
28263
|
+
formatTime(
|
|
28264
|
+
Math.floor(upperBoundMin / 60),
|
|
28265
|
+
upperBoundMin % 60
|
|
28266
|
+
),
|
|
28267
|
+
"."
|
|
28268
|
+
] })
|
|
28269
|
+
] })
|
|
28270
|
+
] }),
|
|
28271
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mt-6", children: [
|
|
28272
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28273
|
+
"button",
|
|
28274
|
+
{
|
|
28275
|
+
type: "button",
|
|
28276
|
+
onClick: onComplete,
|
|
28277
|
+
className: "px-4 py-3 text-sm font-medium text-gray-500 hover:text-gray-800 transition-colors",
|
|
28278
|
+
children: "Skip for now"
|
|
28279
|
+
}
|
|
28280
|
+
),
|
|
28281
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28282
|
+
"button",
|
|
28283
|
+
{
|
|
28284
|
+
type: "button",
|
|
28285
|
+
onClick: applyAndClose,
|
|
28286
|
+
disabled: !hasAnyDetail,
|
|
28287
|
+
className: "flex-1 px-4 py-3 bg-primary text-white rounded-xl shadow-md hover:bg-primary/90 transition-colors font-semibold disabled:opacity-40 disabled:cursor-not-allowed disabled:shadow-none",
|
|
28288
|
+
children: "Continue"
|
|
28289
|
+
}
|
|
28290
|
+
)
|
|
28291
|
+
] })
|
|
28292
|
+
] })
|
|
28293
|
+
] }) });
|
|
28294
|
+
}
|
|
27683
28295
|
var TUTORIAL_STORAGE_KEY = "catering_tutorial_completed";
|
|
27684
28296
|
function useCateringTutorial({
|
|
27685
28297
|
mealSessions,
|
|
@@ -28113,6 +28725,8 @@ function CateringOrderBuilder() {
|
|
|
28113
28725
|
const [isMobileCartMenuOpen, setIsMobileCartMenuOpen] = react.useState(false);
|
|
28114
28726
|
const [isMobileChatMenuOpen, setIsMobileChatMenuOpen] = react.useState(false);
|
|
28115
28727
|
const [isDesktopCartMenuOpen, setIsDesktopCartMenuOpen] = react.useState(false);
|
|
28728
|
+
const shareCart = useShareCart();
|
|
28729
|
+
const importCart = useImportCart();
|
|
28116
28730
|
const [isAIMobileUnavailableOpen, setIsAIMobileUnavailableOpen] = react.useState(false);
|
|
28117
28731
|
const storage = useStorage();
|
|
28118
28732
|
const [rightPanelTab, setRightPanelTabState] = react.useState(() => {
|
|
@@ -28126,6 +28740,17 @@ function CateringOrderBuilder() {
|
|
|
28126
28740
|
},
|
|
28127
28741
|
[storage]
|
|
28128
28742
|
);
|
|
28743
|
+
const WELCOME_SEEN_KEY = "catering_welcome_seen";
|
|
28744
|
+
const [showWelcomeModal, setShowWelcomeModal] = react.useState(() => {
|
|
28745
|
+
if (storage.getItem(WELCOME_SEEN_KEY) === "true") return false;
|
|
28746
|
+
if (contactInfo?.addressLine1) return false;
|
|
28747
|
+
if (mealSessions[0]?.sessionDate) return false;
|
|
28748
|
+
return true;
|
|
28749
|
+
});
|
|
28750
|
+
const handleWelcomeComplete = react.useCallback(() => {
|
|
28751
|
+
storage.setItem(WELCOME_SEEN_KEY, "true");
|
|
28752
|
+
setShowWelcomeModal(false);
|
|
28753
|
+
}, [storage]);
|
|
28129
28754
|
const [isMobileAIChatOpen, setIsMobileAIChatOpen] = react.useState(false);
|
|
28130
28755
|
const [mobileChatView, setMobileChatView] = react.useState(
|
|
28131
28756
|
"chat"
|
|
@@ -29085,6 +29710,8 @@ function CateringOrderBuilder() {
|
|
|
29085
29710
|
setActiveSessionIndex(0);
|
|
29086
29711
|
setIsClearAllConfirmOpen(false);
|
|
29087
29712
|
setIsMobileCartMenuOpen(false);
|
|
29713
|
+
storage.removeItem(WELCOME_SEEN_KEY);
|
|
29714
|
+
setShowWelcomeModal(true);
|
|
29088
29715
|
};
|
|
29089
29716
|
const handlePdfDownload = async (withPrices) => {
|
|
29090
29717
|
if (generatingPdf) return;
|
|
@@ -29243,6 +29870,7 @@ function CateringOrderBuilder() {
|
|
|
29243
29870
|
},
|
|
29244
29871
|
children: [
|
|
29245
29872
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-base-100", children: [
|
|
29873
|
+
showWelcomeModal && /* @__PURE__ */ jsxRuntime.jsx(WelcomeModal, { onComplete: handleWelcomeComplete }),
|
|
29246
29874
|
editingSessionIndex !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
29247
29875
|
SessionEditor,
|
|
29248
29876
|
{
|
|
@@ -29383,7 +30011,7 @@ function CateringOrderBuilder() {
|
|
|
29383
30011
|
}
|
|
29384
30012
|
) : /* @__PURE__ */ jsxRuntime.jsx(AIChat, {}),
|
|
29385
30013
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex-shrink-0 flex flex-col gap-1.5", children: effectiveRightPanelTab === "ai" ? /* @__PURE__ */ jsxRuntime.jsx(AISuggestionsBottomButton, {}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
29386
|
-
|
|
30014
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-t border-base-300 pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
29387
30015
|
PricingSummary,
|
|
29388
30016
|
{
|
|
29389
30017
|
pricing,
|
|
@@ -29464,6 +30092,50 @@ function CateringOrderBuilder() {
|
|
|
29464
30092
|
]
|
|
29465
30093
|
}
|
|
29466
30094
|
),
|
|
30095
|
+
shareCart.hasItems && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30096
|
+
"button",
|
|
30097
|
+
{
|
|
30098
|
+
onClick: shareCart.handleShare,
|
|
30099
|
+
disabled: shareCart.status === "loading",
|
|
30100
|
+
title: "Copy a code to share this cart",
|
|
30101
|
+
className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm text-gray-700 transition-colors hover:bg-base-100 disabled:cursor-not-allowed disabled:opacity-50",
|
|
30102
|
+
children: [
|
|
30103
|
+
shareCart.status === "loading" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-xs" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
30104
|
+
"svg",
|
|
30105
|
+
{
|
|
30106
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
30107
|
+
className: "h-4 w-4",
|
|
30108
|
+
fill: "none",
|
|
30109
|
+
viewBox: "0 0 24 24",
|
|
30110
|
+
stroke: "currentColor",
|
|
30111
|
+
strokeWidth: 2,
|
|
30112
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
30113
|
+
"path",
|
|
30114
|
+
{
|
|
30115
|
+
strokeLinecap: "round",
|
|
30116
|
+
strokeLinejoin: "round",
|
|
30117
|
+
d: shareCart.status === "done" ? "M5 13l4 4L19 7" : "M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
|
|
30118
|
+
}
|
|
30119
|
+
)
|
|
30120
|
+
}
|
|
30121
|
+
),
|
|
30122
|
+
shareCart.label
|
|
30123
|
+
]
|
|
30124
|
+
}
|
|
30125
|
+
),
|
|
30126
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
30127
|
+
"button",
|
|
30128
|
+
{
|
|
30129
|
+
onClick: importCart.handleImport,
|
|
30130
|
+
disabled: importCart.status === "loading",
|
|
30131
|
+
title: "Load a cart from a shared code",
|
|
30132
|
+
className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm text-gray-700 transition-colors hover:bg-base-100 disabled:cursor-not-allowed disabled:opacity-50",
|
|
30133
|
+
children: [
|
|
30134
|
+
importCart.status === "loading" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-xs" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardPaste, { className: "h-4 w-4" }),
|
|
30135
|
+
importCart.label
|
|
30136
|
+
]
|
|
30137
|
+
}
|
|
30138
|
+
),
|
|
29467
30139
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
29468
30140
|
"button",
|
|
29469
30141
|
{
|
|
@@ -29680,6 +30352,50 @@ function CateringOrderBuilder() {
|
|
|
29680
30352
|
]
|
|
29681
30353
|
}
|
|
29682
30354
|
),
|
|
30355
|
+
shareCart.hasItems && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30356
|
+
"button",
|
|
30357
|
+
{
|
|
30358
|
+
onClick: shareCart.handleShare,
|
|
30359
|
+
disabled: shareCart.status === "loading",
|
|
30360
|
+
title: "Copy a code to share this cart",
|
|
30361
|
+
className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm text-gray-700 transition-colors hover:bg-base-100 disabled:cursor-not-allowed disabled:opacity-50",
|
|
30362
|
+
children: [
|
|
30363
|
+
shareCart.status === "loading" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-xs" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
30364
|
+
"svg",
|
|
30365
|
+
{
|
|
30366
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
30367
|
+
className: "h-4 w-4",
|
|
30368
|
+
fill: "none",
|
|
30369
|
+
viewBox: "0 0 24 24",
|
|
30370
|
+
stroke: "currentColor",
|
|
30371
|
+
strokeWidth: 2,
|
|
30372
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
30373
|
+
"path",
|
|
30374
|
+
{
|
|
30375
|
+
strokeLinecap: "round",
|
|
30376
|
+
strokeLinejoin: "round",
|
|
30377
|
+
d: shareCart.status === "done" ? "M5 13l4 4L19 7" : "M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
|
|
30378
|
+
}
|
|
30379
|
+
)
|
|
30380
|
+
}
|
|
30381
|
+
),
|
|
30382
|
+
shareCart.label
|
|
30383
|
+
]
|
|
30384
|
+
}
|
|
30385
|
+
),
|
|
30386
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
30387
|
+
"button",
|
|
30388
|
+
{
|
|
30389
|
+
onClick: importCart.handleImport,
|
|
30390
|
+
disabled: importCart.status === "loading",
|
|
30391
|
+
title: "Load a cart from a shared code",
|
|
30392
|
+
className: "flex w-full items-center gap-2 px-3 py-2.5 text-left text-sm text-gray-700 transition-colors hover:bg-base-100 disabled:cursor-not-allowed disabled:opacity-50",
|
|
30393
|
+
children: [
|
|
30394
|
+
importCart.status === "loading" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-xs" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ClipboardPaste, { className: "h-4 w-4" }),
|
|
30395
|
+
importCart.label
|
|
30396
|
+
]
|
|
30397
|
+
}
|
|
30398
|
+
),
|
|
29683
30399
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-3 border-t border-base-200" }),
|
|
29684
30400
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
29685
30401
|
"button",
|
|
@@ -30516,7 +31232,10 @@ function CateringWidget(props) {
|
|
|
30516
31232
|
);
|
|
30517
31233
|
}
|
|
30518
31234
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "swift-catering-widget", style, children: [
|
|
30519
|
-
/* @__PURE__ */ jsxRuntime.jsx(CateringConfigProvider, { value: config, children: /* @__PURE__ */ jsxRuntime.
|
|
31235
|
+
/* @__PURE__ */ jsxRuntime.jsx(CateringConfigProvider, { value: config, children: /* @__PURE__ */ jsxRuntime.jsxs(CateringStateProvider, { children: [
|
|
31236
|
+
/* @__PURE__ */ jsxRuntime.jsx(SharedCartLoader, {}),
|
|
31237
|
+
/* @__PURE__ */ jsxRuntime.jsx(CateringFilterProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(CateringOrderBuilder, {}) })
|
|
31238
|
+
] }) }),
|
|
30520
31239
|
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-swift-overlay-root": true, className: "swift-chat-design" })
|
|
30521
31240
|
] });
|
|
30522
31241
|
}
|