@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/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 formatTime(hour, minute) {
11120
- const period = hour >= 12 ? "PM" : "AM";
11121
- const hour12 = hour % 12 || 12;
11122
- return `${hour12}:${minute.toString().padStart(2, "0")} ${period}`;
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
- const deliveryBreakdown = pricing.deliveryFeeBreakdown || pricing.mealSessions?.[0]?.deliveryFeeBreakdown;
21618
- const distanceInMiles = pricing.distanceInMiles || pricing.mealSessions?.[0]?.distanceInMiles;
21619
- const backendPromos = pricing.appliedPromotions?.filter((p) => p.discount > 0) ?? [];
21620
- 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: [
21621
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-base-content/70 ${compact ? "text-xs" : "text-sm"}`, children: [
21622
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Subtotal" }),
21623
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
21624
- "\xA3",
21625
- pricing.subtotal.toFixed(2)
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
- (pricing.promotionDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-green-600 font-semibold ${compact ? "text-xs" : "text-sm"}`, children: [
21629
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
21630
- "Restaurant Promotion",
21631
- backendPromos.length > 1 ? "s" : ""
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("span", { children: [
21634
- "-\xA3",
21635
- pricing.promotionDiscount.toFixed(2)
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
- (pricing.promoDiscount ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between text-success font-medium ${compact ? "text-xs" : "text-sm"}`, children: [
21639
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Promo Code Discount" }),
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
- "-\xA3",
21642
- pricing.promoDiscount.toFixed(2)
22002
+ "\xA3",
22003
+ session.deliveryFee.toFixed(2)
21643
22004
  ] })
21644
- ] }),
21645
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
21646
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex justify-between items-center text-base-content/70 gap-2 ${compact ? "text-xs" : "text-sm"}`, children: [
21647
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
21648
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-nowrap", children: "Delivery Cost" }),
21649
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
21650
- /* @__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" }) }) }),
21651
- /* @__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." }) })
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
- !distanceInMiles && onPlaceSelect && /* @__PURE__ */ jsxRuntime.jsx(InlineAddressInput, { onPlaceSelect }),
21691
- showDeliveryBreakdown && pricing.mealSessions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-4 space-y-1 text-xs text-base-content/60", children: pricing.mealSessions.map((session, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
21692
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
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, { capture: true, passive: true });
23255
+ document.addEventListener("scroll", handleScroll, {
23256
+ capture: true,
23257
+ passive: true
23258
+ });
22960
23259
  }, 100);
22961
- document.addEventListener("pointerdown", handlePointerDown, { capture: true });
23260
+ document.addEventListener("pointerdown", handlePointerDown, {
23261
+ capture: true
23262
+ });
22962
23263
  return () => {
22963
23264
  clearTimeout(scrollTimer);
22964
- document.removeEventListener("pointerdown", handlePointerDown, { capture: true });
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(() => dietaryFilteredItems, [dietaryFilteredItems]);
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(async (bundle) => {
23361
- const restaurantIds = [...new Set(bundle.items.map((i) => i.restaurantId).filter(Boolean))];
23362
- const missing = restaurantIds.filter((id) => !restaurantItemsCache.current.has(id));
23363
- if (missing.length > 0) {
23364
- const fetched = await Promise.all(missing.map((id) => cateringService.getMenuItemsByRestaurant(id)));
23365
- missing.forEach((id, idx) => {
23366
- restaurantItemsCache.current.set(id, (fetched[idx] || []).map(mapToMenuItem));
23367
- });
23368
- }
23369
- return restaurantIds.flatMap((id) => restaurantItemsCache.current.get(id) ?? []);
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 shadow-md", children: /* @__PURE__ */ jsxRuntime.jsx(
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-0.5", children: [
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.jsxs("div", { className: "relative flex items-center justify-between gap-2", children: [
23583
- /* @__PURE__ */ jsxRuntime.jsxs(
23584
- "div",
23585
- {
23586
- className: `flex min-w-0 items-center gap-1.5 text-[11px] leading-4 ${cateringWindowInfo.isAvailable ? "text-gray-500" : "text-red-500 font-semibold"}`,
23587
- children: [
23588
- /* @__PURE__ */ jsxRuntime.jsx(
23589
- lucideReact.Clock3,
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
- className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full border border-base-300 bg-white text-gray-500 shadow-sm transition-colors hover:text-primary focus:outline-none",
23633
- title: "View weekly catering hours",
23634
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-4 w-4" })
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 ? { latitude: contactInfo.latitude, longitude: contactInfo.longitude } : null;
23643
- const distance = userLoc && restaurantLoc ? haversineDistanceMiles(userLoc.latitude, userLoc.longitude, restaurantLoc.latitude, restaurantLoc.longitude) : null;
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("div", { ref: expandedSessionIndex === sessionIndex ? categoriesRowRef : void 0, style: { contain: "inline-size" }, 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(
23767
- "button",
24053
+ const renderCategoryFilters = () => /* @__PURE__ */ jsxRuntime.jsx(
24054
+ "div",
23768
24055
  {
23769
- onClick: () => setSelectedCategoryId(
23770
- selectedCategoryId === category.id ? null : category.id
23771
- ),
23772
- 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"}`,
23773
- children: [
23774
- category.images ? /* @__PURE__ */ jsxRuntime.jsx(
23775
- "img",
23776
- {
23777
- src: category.images,
23778
- alt: category.name,
23779
- className: "h-8 w-8 md:h-9 md:w-9 object-cover rounded-full border border-base-300"
23780
- }
23781
- ) : 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,
23782
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-xs md:text-sm", children: category.name })
23783
- ]
23784
- },
23785
- category.id
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 ? { latitude: contactInfo.latitude, longitude: contactInfo.longitude } : null;
23820
- const distance = userLoc && restaurantLoc ? haversineDistanceMiles(userLoc.latitude, userLoc.longitude, restaurantLoc.latitude, restaurantLoc.longitude) : null;
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(selectedRestaurant, activeSession?.sessionDate, activeSession?.eventTime);
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) return null;
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("span", { className: `flex items-center gap-1 text-sm whitespace-nowrap ${noticeMet ? "text-gray-500" : "text-gray-500"}`, children: [
23843
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock3, { className: `h-3.5 w-3.5 flex-shrink-0 ${noticeMet ? "text-gray-400" : "text-red-500"}` }),
23844
- advanceNoticeText
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", { day: "numeric", month: "short" });
23881
- const sortedTiers = (promo) => [...promo.discountTiers ?? []].sort((a, b) => a.minQuantity - b.minQuantity);
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("div", { className: "flex-shrink-0 snap-start bg-white border border-base-200 rounded-2xl p-4 min-w-[220px] max-w-[280px] shadow-sm", children: [
23888
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2 mb-2", children: [
23889
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold text-gray-400 uppercase tracking-wide truncate", children: promo.name }),
23890
- /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 flex-shrink-0 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, 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" }) })
23891
- ] }),
23892
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg font-black text-gray-900 leading-none", children: discountLabel(promo) }),
23893
- 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("span", { className: "text-[11px] font-medium bg-primary/10 text-primary px-2 py-0.5 rounded-full", children: [
23894
- tier.minQuantity,
23895
- "+ \u2192 ",
23896
- Number(tier.discountPercentage),
23897
- "%"
23898
- ] }, idx)) }),
23899
- promo.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 mt-1.5 line-clamp-2", children: promo.description }),
23900
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-x-2 gap-y-0.5 mt-2.5 text-xs text-gray-400", children: [
23901
- promo.minOrderAmount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
23902
- "Min. \xA3",
23903
- promo.minOrderAmount
23904
- ] }),
23905
- promo.maxDiscountAmount && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
23906
- "\xB7 Max. \xA3",
23907
- promo.maxDiscountAmount,
23908
- " off"
23909
- ] }),
23910
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
23911
- "\xB7 Until ",
23912
- endDateLabel(promo)
23913
- ] })
23914
- ] })
23915
- ] }, promo.id)) })
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("div", { className: `relative flex-1 min-w-0 ${isRestaurantSearchOpen ? "hidden md:block" : ""}`, children: [
23929
- /* @__PURE__ */ jsxRuntime.jsx(
23930
- "div",
23931
- {
23932
- ref: setPillRowEl,
23933
- "data-pill-row": true,
23934
- 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",
23935
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 md:gap-5", children: pillRowGroupNames.map((name) => {
23936
- const isActive = activeGroupName === name;
23937
- return /* @__PURE__ */ jsxRuntime.jsx(
23938
- "button",
23939
- {
23940
- ref: (el) => {
23941
- if (el) groupButtonRefs.current.set(name, el);
23942
- else groupButtonRefs.current.delete(name);
23943
- },
23944
- onClick: () => handlePillClick(name),
23945
- 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"}`,
23946
- children: name
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
- name
23949
- );
23950
- }) })
23951
- }
23952
- ),
23953
- canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
23954
- "button",
23955
- {
23956
- onClick: () => scrollPillRow("left"),
23957
- className: "group absolute left-0 top-0 bottom-0 z-10 flex w-14 items-center justify-start rounded-l-full pl-3",
23958
- style: {
23959
- background: "linear-gradient(to right, rgba(255,255,255,0.95) 50%, transparent)"
23960
- },
23961
- "aria-label": "Scroll left",
23962
- type: "button",
23963
- 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" }) })
23964
- }
23965
- ),
23966
- canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
23967
- "button",
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((r) => availableRestaurants.find((ar) => ar.id === r.id)).filter((r) => r !== void 0);
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(r, activeSession?.sessionDate, activeSession?.eventTime);
24230
- const noticeMet = isRestaurantAdvanceNoticeMet(r, activeSession?.sessionDate, activeSession?.eventTime);
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(r, activeSession?.sessionDate, activeSession?.eventTime);
24235
- const noticeMet = isRestaurantAdvanceNoticeMet(r, activeSession?.sessionDate, activeSession?.eventTime);
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(r, activeSession?.sessionDate, activeSession?.eventTime);
24290
- const noticeMet = isRestaurantAdvanceNoticeMet(r, activeSession?.sessionDate, activeSession?.eventTime);
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(r, activeSession?.sessionDate, activeSession?.eventTime);
24295
- const noticeMet = isRestaurantAdvanceNoticeMet(r, activeSession?.sessionDate, activeSession?.eventTime);
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(restaurant, () => handleSelectRestaurant(restaurant.id)) }, restaurant.id)) }),
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(restaurant, () => handleSelectRestaurant(restaurant.id)) }, restaurant.id)) })
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
- totalItems > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 border-t border-base-300 pt-3", children: /* @__PURE__ */ jsxRuntime.jsx(
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.jsx(CateringStateProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(CateringFilterProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(CateringOrderBuilder, {}) }) }) }),
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
  }