@swift-food-services/catering-widget 0.2.0-beta.7 → 0.2.0-beta.9

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
@@ -7536,12 +7536,15 @@ function createChatEndpoints(client) {
7536
7536
  const res = await client.request(`/catering-chat/${sid}`);
7537
7537
  return handleChatResponse(res);
7538
7538
  },
7539
- sendMessage: (sid, message, address, selectedMealSessionIndex) => {
7539
+ sendMessage: (sid, message, address, selectedMealSessionIndex, selectedIntentId) => {
7540
7540
  const body = { message };
7541
7541
  if (address) body.address = address;
7542
7542
  if (typeof selectedMealSessionIndex === "number") {
7543
7543
  body.selectedMealSessionIndex = selectedMealSessionIndex;
7544
7544
  }
7545
+ if (typeof selectedIntentId === "string" && selectedIntentId.length > 0) {
7546
+ body.selectedIntentId = selectedIntentId;
7547
+ }
7545
7548
  return post(`/catering-chat/${sid}/message`, body);
7546
7549
  },
7547
7550
  editField: (sid, field, value, mealSessionIndex) => {
@@ -12555,21 +12558,10 @@ function ActiveSessionPanel({
12555
12558
  }
12556
12559
 
12557
12560
  // src/components/ai/edit/translateEditField.ts
12558
- function translateEditField(field, value, meals, aiMealSessions, mealSessionIndex) {
12559
- if (field === "guestCount") {
12560
- return { field: "headcount", value };
12561
- }
12562
- const idx = mealSessionIndex ?? 0;
12563
- const meal = meals.find((m) => m.mealSessionIndex === idx) ?? meals[0] ?? null;
12564
- const meta = meal ? aiMealSessions[meal.mealSessionIndex] : void 0;
12565
- if (field === "sessionDate") {
12566
- const time = meta?.eventTime || "12:00";
12567
- return { field: "event_datetime", value: `${value}T${time}` };
12568
- }
12569
- if (field === "eventTime") {
12570
- const date = meta?.sessionDate || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
12571
- return { field: "event_datetime", value: `${date}T${value}` };
12572
- }
12561
+ function translateEditField(field, value) {
12562
+ if (field === "guestCount") return { field: "headcount", value };
12563
+ if (field === "sessionDate") return { field: "event_date", value };
12564
+ if (field === "eventTime") return { field: "event_time", value };
12573
12565
  return { field, value };
12574
12566
  }
12575
12567
 
@@ -12577,6 +12569,16 @@ function translateEditField(field, value, meals, aiMealSessions, mealSessionInde
12577
12569
  var STORAGE_KEY2 = "swift-food-chat-session";
12578
12570
  var messagesKey = (sid) => `swift-food-chat-messages-${sid}`;
12579
12571
  var errorKey = (sid) => `swift-food-chat-error-${sid}`;
12572
+ function clearChatSessionStorage() {
12573
+ if (typeof window === "undefined") return null;
12574
+ const sid = window.localStorage.getItem(STORAGE_KEY2);
12575
+ window.localStorage.removeItem(STORAGE_KEY2);
12576
+ if (sid) {
12577
+ window.localStorage.removeItem(messagesKey(sid));
12578
+ window.localStorage.removeItem(errorKey(sid));
12579
+ }
12580
+ return sid;
12581
+ }
12580
12582
  function readCachedError(sid) {
12581
12583
  if (typeof window === "undefined") return null;
12582
12584
  const raw = window.localStorage.getItem(errorKey(sid));
@@ -12653,10 +12655,14 @@ function useChatSession(options = {}) {
12653
12655
  const sessionIdRef = react.useRef(null);
12654
12656
  const lastTaxonomyRef = react.useRef({});
12655
12657
  const selectedMealSessionIndexRef = react.useRef(null);
12658
+ const selectedIntentIdRef = react.useRef(null);
12656
12659
  const applySideState = react.useCallback((data) => {
12657
12660
  setStatus(data.status ?? null);
12658
- setLatestMealSessions(data.mealSessions ?? []);
12659
- setLatestActiveMealSessionIndex(data.activeMealSessionIndex ?? 0);
12661
+ const meals = data.mealSessions ?? [];
12662
+ setLatestMealSessions(meals);
12663
+ setLatestActiveMealSessionIndex(
12664
+ (prev) => meals.length === 0 ? 0 : Math.max(0, Math.min(prev, meals.length - 1))
12665
+ );
12660
12666
  if (data.summary) {
12661
12667
  setLatestSummary(data.summary);
12662
12668
  lastTaxonomyRef.current = data.summary.taxonomy;
@@ -12928,11 +12934,13 @@ function useChatSession(options = {}) {
12928
12934
  setRetryParams(null);
12929
12935
  try {
12930
12936
  const selectedIdx = selectedMealSessionIndexRef.current;
12937
+ const selectedIntentId = selectedIntentIdRef.current;
12931
12938
  const data = await api.chat.sendMessage(
12932
12939
  sid,
12933
12940
  text,
12934
12941
  address,
12935
- selectedIdx
12942
+ selectedIdx,
12943
+ selectedIntentId
12936
12944
  );
12937
12945
  applyResponse(data);
12938
12946
  } catch (e) {
@@ -12965,19 +12973,11 @@ function useChatSession(options = {}) {
12965
12973
  if (!params || !sid) return;
12966
12974
  await dispatchSend(sid, params.text, params.address);
12967
12975
  }, [retryParams, dispatchSend]);
12968
- const latestMealSessionPartsRef = react.useRef([]);
12969
- const latestMealSessionsRef = react.useRef([]);
12970
12976
  const applyEditField = react.useCallback(
12971
12977
  async (field, value, mealSessionIndex) => {
12972
12978
  const sid = sessionIdRef.current;
12973
12979
  if (!sid) return;
12974
- const translated = translateEditField(
12975
- field,
12976
- value,
12977
- latestMealSessionPartsRef.current,
12978
- latestMealSessionsRef.current,
12979
- mealSessionIndex
12980
- );
12980
+ const translated = translateEditField(field, value);
12981
12981
  await callApiAndApply(
12982
12982
  () => api.chat.editField(
12983
12983
  sid,
@@ -13034,17 +13034,22 @@ function useChatSession(options = {}) {
13034
13034
  [api, callApiAndApply]
13035
13035
  );
13036
13036
  const pickMealSession = react.useCallback(
13037
+ // Local-only navigation — no network round-trip. The old
13038
+ // pick_meal_session call blocked the UI on a server response just to
13039
+ // move the active tab, which made switching meals feel like it hung.
13040
+ // We update the viewed index immediately and stash it as the
13041
+ // "currently viewing" signal so the next /message carries it
13042
+ // (selectedMealSessionIndex) for BE disambiguation. The viewed intent
13043
+ // is cleared here and repopulated by IntentStepper's focus effect once
13044
+ // the new meal renders. Kept async (returns a resolved promise) so the
13045
+ // hook's contract and existing `void pickMealSession(idx)` callers are
13046
+ // unchanged.
13037
13047
  async (mealSessionIndex) => {
13038
- const sid = sessionIdRef.current;
13039
- if (!sid) return;
13040
- await callApiAndApply(
13041
- () => api.chat.menuAction(sid, {
13042
- action: "pick_meal_session",
13043
- mealSessionIndex
13044
- })
13045
- );
13048
+ setLatestActiveMealSessionIndex(mealSessionIndex);
13049
+ selectedMealSessionIndexRef.current = mealSessionIndex;
13050
+ selectedIntentIdRef.current = null;
13046
13051
  },
13047
- [api, callApiAndApply]
13052
+ []
13048
13053
  );
13049
13054
  const confirmInheritance = react.useCallback(
13050
13055
  async (mealSessionIndex, accept) => {
@@ -13146,8 +13151,6 @@ function useChatSession(options = {}) {
13146
13151
  if (latestMealSessions.length === 0) return parts;
13147
13152
  return parts.filter((p) => p.mealSessionIndex < latestMealSessions.length);
13148
13153
  }, [messages, latestMealSessions]);
13149
- latestMealSessionPartsRef.current = latestMealSessionParts;
13150
- latestMealSessionsRef.current = latestMealSessions;
13151
13154
  return {
13152
13155
  messages,
13153
13156
  sending,
@@ -13181,6 +13184,10 @@ function useChatSession(options = {}) {
13181
13184
  getTaxonomyValueFor,
13182
13185
  setSelectedMealSessionIndex: (idx) => {
13183
13186
  selectedMealSessionIndexRef.current = idx;
13187
+ selectedIntentIdRef.current = null;
13188
+ },
13189
+ setSelectedIntentId: (id) => {
13190
+ selectedIntentIdRef.current = id;
13184
13191
  },
13185
13192
  submitFeedback
13186
13193
  };
@@ -13200,7 +13207,6 @@ function mergeAdaptedMealParts(data) {
13200
13207
  mealSessionIndex: i,
13201
13208
  intentBlocks: blocks.map((b) => ({
13202
13209
  type: "intent_block",
13203
- intentId: b.intentId,
13204
13210
  mealSessionIndex: i,
13205
13211
  intent: b.intent,
13206
13212
  restaurantPicks: b.restaurantPicks
@@ -13268,7 +13274,11 @@ function findLatestMealSessionParts(messages) {
13268
13274
  (a, b) => a.mealSessionIndex - b.mealSessionIndex
13269
13275
  );
13270
13276
  }
13271
- var STORAGE_KEY_PREFIX = "swift-food-cart-v3-";
13277
+ var STORAGE_KEY_PREFIX = "swift-food-cart-v4-";
13278
+ function clearCartStorage(sessionId) {
13279
+ if (!sessionId || typeof window === "undefined") return;
13280
+ window.localStorage.removeItem(STORAGE_KEY_PREFIX + sessionId);
13281
+ }
13272
13282
  var emptyIntent = () => ({
13273
13283
  selectedRestaurantId: null,
13274
13284
  qtyOverrides: {},
@@ -13487,12 +13497,13 @@ function ChatSessionProvider({
13487
13497
  const [suggestionsOverlayDismissed, setSuggestionsOverlayDismissed] = react.useState(false);
13488
13498
  const [feedbackTarget, setFeedbackTarget] = react.useState(null);
13489
13499
  const [feedbackRating, setFeedbackRating] = react.useState(0);
13500
+ const [feedbackState, setFeedbackState] = react.useState("idle");
13490
13501
  react.useEffect(() => {
13491
13502
  if (!chat.sessionId) setActiveViewedPreview(null);
13492
13503
  }, [chat.sessionId]);
13493
13504
  const hasResults = react.useMemo(
13494
- () => chat.latestMealSessionParts.some((p) => p.intentBlocks.length > 0),
13495
- [chat.latestMealSessionParts]
13505
+ () => chat.latestMealSessions.length > 0,
13506
+ [chat.latestMealSessions]
13496
13507
  );
13497
13508
  const prevPartsRef = react.useRef(chat.latestMealSessionParts);
13498
13509
  const onViewResultsRef = react.useRef(onViewResults);
@@ -13528,17 +13539,22 @@ function ChatSessionProvider({
13528
13539
  feedbackTarget,
13529
13540
  feedbackRating,
13530
13541
  setFeedbackRating,
13542
+ feedbackState,
13543
+ setFeedbackState,
13531
13544
  startFeedback: (messageId, up, eventId) => {
13532
13545
  setFeedbackTarget({ messageId, up, eventId });
13533
13546
  setFeedbackRating(0);
13547
+ setFeedbackState("idle");
13534
13548
  },
13535
13549
  startGeneralFeedback: () => {
13536
13550
  setFeedbackTarget({ messageId: "__general__", up: false });
13537
13551
  setFeedbackRating(0);
13552
+ setFeedbackState("idle");
13538
13553
  },
13539
13554
  cancelFeedback: () => {
13540
13555
  setFeedbackTarget(null);
13541
13556
  setFeedbackRating(0);
13557
+ setFeedbackState("idle");
13542
13558
  },
13543
13559
  hasResults,
13544
13560
  suggestionsOverlayDismissed,
@@ -15066,7 +15082,7 @@ function IntentBlockCard({
15066
15082
  items: selected.items,
15067
15083
  onAddItem
15068
15084
  },
15069
- `${part.intentId}-${selected.restaurant.id}-${section.title ?? "_null"}`
15085
+ `${part.intent.id}-${selected.restaurant.id}-${section.title ?? "_null"}`
15070
15086
  )) }),
15071
15087
  alts.length >= 3 && /* @__PURE__ */ jsxRuntime.jsx(
15072
15088
  AltRestaurantChips,
@@ -15251,10 +15267,10 @@ function resolveSelections(blocks, explicit) {
15251
15267
  const resolved = /* @__PURE__ */ new Map();
15252
15268
  const priorIds = /* @__PURE__ */ new Set();
15253
15269
  for (const block of blocks) {
15254
- const explicitId = explicit.get(block.intentId);
15270
+ const explicitId = explicit.get(block.intent.id);
15255
15271
  const chosenId = explicitId ?? block.restaurantPicks[pickDefaultIndex(block.restaurantPicks, priorIds)]?.restaurant.id;
15256
15272
  if (chosenId !== void 0) {
15257
- resolved.set(block.intentId, chosenId);
15273
+ resolved.set(block.intent.id, chosenId);
15258
15274
  priorIds.add(chosenId);
15259
15275
  }
15260
15276
  }
@@ -15340,11 +15356,11 @@ function MealSessionStepper({
15340
15356
  IntentBlockCard,
15341
15357
  {
15342
15358
  part: block,
15343
- selectedRestaurantId: resolved.get(block.intentId) ?? block.restaurantPicks[0]?.restaurant.id ?? "",
15344
- onSelectRestaurant: handleSelect(block.intentId),
15359
+ selectedRestaurantId: resolved.get(block.intent.id) ?? block.restaurantPicks[0]?.restaurant.id ?? "",
15360
+ onSelectRestaurant: handleSelect(block.intent.id),
15345
15361
  onAddItem
15346
15362
  },
15347
- block.intentId
15363
+ block.intent.id
15348
15364
  ))
15349
15365
  },
15350
15366
  stepMode ? `step-${safeStepIdx}` : "all"
@@ -17113,14 +17129,14 @@ function AIChatBody() {
17113
17129
  const el = textareaRef.current;
17114
17130
  if (!el) return;
17115
17131
  el.style.height = "auto";
17116
- const gateMinHeight = gateActive ? 72 : 0;
17132
+ const gateMinHeight = gateActive && feedbackTarget === null ? 72 : 0;
17117
17133
  const fullHeight = Math.max(el.scrollHeight, gateMinHeight);
17118
17134
  el.style.height = `${Math.min(fullHeight, MAX_INPUT_HEIGHT)}px`;
17119
17135
  el.style.overflowY = fullHeight > MAX_INPUT_HEIGHT ? "auto" : "hidden";
17120
17136
  };
17121
17137
  react.useEffect(() => {
17122
17138
  handleInput();
17123
- }, [input, feedbackNote, gateActive]);
17139
+ }, [input, feedbackNote, gateActive, feedbackTarget]);
17124
17140
  react.useEffect(() => {
17125
17141
  if (!pendingInputFocus) return;
17126
17142
  if (gateActive || pillEditing) return;
@@ -17255,8 +17271,8 @@ function AIChatBody() {
17255
17271
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 px-3 py-2.5 flex items-center gap-3 border-b border-base-200", children: [
17256
17272
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-full bg-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-white" }) }),
17257
17273
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
17258
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800", children: "AI Assistant" }),
17259
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: "Get menu suggestions" })
17274
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Nibbles" }),
17275
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: "Our AI Assistant" })
17260
17276
  ] }),
17261
17277
  /* @__PURE__ */ jsxRuntime.jsxs(
17262
17278
  "button",
@@ -17486,7 +17502,7 @@ function AIChatBody() {
17486
17502
  animate: { opacity: 1, y: 0 },
17487
17503
  exit: { opacity: 0, y: -8 },
17488
17504
  transition: { duration: 0.18, ease: "easeOut" },
17489
- children: gateActive ? /* @__PURE__ */ jsxRuntime.jsxs(
17505
+ children: gateActive && !inFeedbackMode ? /* @__PURE__ */ jsxRuntime.jsxs(
17490
17506
  react$1.motion.form,
17491
17507
  {
17492
17508
  onSubmit: handleSubmit,
@@ -18049,13 +18065,13 @@ function buildCategoryView(category, mealSession, cart, resolveSelectedRestauran
18049
18065
  const view = [];
18050
18066
  for (const block of mealSession.intentBlocks) {
18051
18067
  if (block.intent.category !== category) continue;
18052
- const selectedRestaurantId = resolveSelectedRestaurantId(block.intentId) ?? block.restaurantPicks[0]?.restaurant.id ?? null;
18068
+ const selectedRestaurantId = resolveSelectedRestaurantId(block.intent.id) ?? block.restaurantPicks[0]?.restaurant.id ?? null;
18053
18069
  if (!selectedRestaurantId) continue;
18054
18070
  const pick = block.restaurantPicks.find(
18055
18071
  (rp) => rp.restaurant.id === selectedRestaurantId
18056
18072
  );
18057
18073
  if (!pick) continue;
18058
- const cartIntent = cart[block.intentId];
18074
+ const cartIntent = cart[block.intent.id];
18059
18075
  const removedSet = new Set(cartIntent?.removedItemIds ?? []);
18060
18076
  const pickedSet = new Set(cartIntent?.pickedItemIds ?? []);
18061
18077
  const swappedInItems = cartIntent ? Object.values(cartIntent.swappedIn) : [];
@@ -18070,7 +18086,7 @@ function buildCategoryView(category, mealSession, cart, resolveSelectedRestauran
18070
18086
  for (const item of liveItems) {
18071
18087
  view.push({
18072
18088
  item,
18073
- intent: { intentId: block.intentId, quantity: block.intent.quantity },
18089
+ intent: { intentId: block.intent.id, quantity: block.intent.quantity },
18074
18090
  itemsInPick,
18075
18091
  overrideQty: cartIntent?.qtyOverrides[item.id],
18076
18092
  intentQuantityPacks: packsByItemId?.get(item.id) ?? null
@@ -18226,7 +18242,7 @@ function checkMinOrders(mealSessionParts, aiMealSessions, cart) {
18226
18242
  return out;
18227
18243
  }
18228
18244
  function findRestaurantForEntry(intentId, ms, cart) {
18229
- const block = ms.intentBlocks.find((b) => b.intentId === intentId);
18245
+ const block = ms.intentBlocks.find((b) => b.intent.id === intentId);
18230
18246
  if (!block) return null;
18231
18247
  const selectedId = cart.getSelectedRestaurantId(intentId) ?? block.restaurantPicks[0]?.restaurant.id ?? null;
18232
18248
  if (!selectedId) return null;
@@ -18305,13 +18321,13 @@ function aiPlanToPricingInput(mealSessionParts, aiMealSessions, cart) {
18305
18321
  }
18306
18322
  }
18307
18323
  for (const block of ms.intentBlocks) {
18308
- const selectedRestaurantId = cart.getSelectedRestaurantId(block.intentId) ?? block.restaurantPicks[0]?.restaurant.id ?? null;
18324
+ const selectedRestaurantId = cart.getSelectedRestaurantId(block.intent.id) ?? block.restaurantPicks[0]?.restaurant.id ?? null;
18309
18325
  if (!selectedRestaurantId) continue;
18310
18326
  const pick = block.restaurantPicks.find(
18311
18327
  (rp) => rp.restaurant.id === selectedRestaurantId
18312
18328
  );
18313
18329
  if (!pick) continue;
18314
- const cartIntent = cart.cart[block.intentId];
18330
+ const cartIntent = cart.cart[block.intent.id];
18315
18331
  const removedSet = new Set(cartIntent?.removedItemIds ?? []);
18316
18332
  const pickedSet = new Set(cartIntent?.pickedItemIds ?? []);
18317
18333
  const liveItems = [
@@ -18323,7 +18339,7 @@ function aiPlanToPricingInput(mealSessionParts, aiMealSessions, cart) {
18323
18339
  )
18324
18340
  ];
18325
18341
  for (const it of liveItems) {
18326
- const qty = qtyByItemKey.get(`${block.intentId}::${it.id}`) ?? 0;
18342
+ const qty = qtyByItemKey.get(`${block.intent.id}::${it.id}`) ?? 0;
18327
18343
  if (qty <= 0) continue;
18328
18344
  orderItems.push({
18329
18345
  item: toPricingItem(it, selectedRestaurantId),
@@ -18733,7 +18749,13 @@ function IntentStepper({
18733
18749
  const safeIntentIdx = Math.min(intentIndex, blocks.length);
18734
18750
  const isReview = blocks.length > 0 && safeIntentIdx === blocks.length;
18735
18751
  const block = isReview || blocks.length === 0 ? null : blocks[safeIntentIdx] ?? null;
18736
- const selectedRestaurantId = block ? cart.getSelectedRestaurantId(block.intentId) ?? block.restaurantPicks[0]?.restaurant.id ?? null : null;
18752
+ const focusedIntentId = block?.intent.id ?? null;
18753
+ const { chat: chatForFocus } = useChatSessionContext();
18754
+ react.useEffect(() => {
18755
+ chatForFocus.setSelectedMealSessionIndex(activeMealSessionIndex);
18756
+ chatForFocus.setSelectedIntentId(focusedIntentId);
18757
+ }, [activeMealSessionIndex, focusedIntentId]);
18758
+ const selectedRestaurantId = block ? cart.getSelectedRestaurantId(block.intent.id) ?? block.restaurantPicks[0]?.restaurant.id ?? null : null;
18737
18759
  const pickIdx = block ? Math.max(
18738
18760
  0,
18739
18761
  block.restaurantPicks.findIndex(
@@ -18741,7 +18763,7 @@ function IntentStepper({
18741
18763
  )
18742
18764
  ) : 0;
18743
18765
  const selected = block ? block.restaurantPicks[pickIdx] : void 0;
18744
- const cartIntent = block ? cart.cart[block.intentId] : void 0;
18766
+ const cartIntent = block ? cart.cart[block.intent.id] : void 0;
18745
18767
  const removedSet = new Set(cartIntent?.removedItemIds ?? []);
18746
18768
  const intentLiveSlots = react.useMemo(() => {
18747
18769
  if (!block || !selected) return [];
@@ -18757,13 +18779,13 @@ function IntentStepper({
18757
18779
  const headcount = activeMealMeta?.guestCount ?? 1;
18758
18780
  const result = [];
18759
18781
  for (const b of blocks) {
18760
- const selRestId = cart.getSelectedRestaurantId(b.intentId) ?? b.restaurantPicks[0]?.restaurant.id ?? null;
18782
+ const selRestId = cart.getSelectedRestaurantId(b.intent.id) ?? b.restaurantPicks[0]?.restaurant.id ?? null;
18761
18783
  if (!selRestId) continue;
18762
18784
  const pick = b.restaurantPicks.find(
18763
18785
  (rp) => rp.restaurant.id === selRestId
18764
18786
  );
18765
18787
  if (!pick) continue;
18766
- const ci = cart.cart[b.intentId];
18788
+ const ci = cart.cart[b.intent.id];
18767
18789
  const rem = new Set(ci?.removedItemIds ?? []);
18768
18790
  const picked = new Set(ci?.pickedItemIds ?? []);
18769
18791
  const swappedIn = ci?.swappedIn ?? {};
@@ -18785,7 +18807,7 @@ function IntentStepper({
18785
18807
  const items = liveSlots.map(({ item: it, slotId }) => {
18786
18808
  const qty = effectiveQty({
18787
18809
  targetItem: it,
18788
- targetIntent: { intentId: b.intentId, quantity: b.intent.quantity },
18810
+ targetIntent: { intentId: b.intent.id, quantity: b.intent.quantity },
18789
18811
  itemsInTargetPick: liveItems.length,
18790
18812
  categoryView,
18791
18813
  headcount
@@ -18900,7 +18922,7 @@ function IntentStepper({
18900
18922
  const nextLabel = isReview ? !isLastMeal ? aiMealSessions[orderedMeals[myPos + 1].mealSessionIndex]?.sessionName ?? null : null : !isLastIntent ? capitalize(blocks[safeIntentIdx + 1].intent.phrase) : "Review";
18901
18923
  const handlePickAlt = (restaurantId) => {
18902
18924
  if (!block) return;
18903
- cart.setRestaurant(block.intentId, restaurantId);
18925
+ cart.setRestaurant(block.intent.id, restaurantId);
18904
18926
  };
18905
18927
  function computeAddonChip(intentId, item, restaurantId, restaurantName) {
18906
18928
  const groups = item.addons ?? [];
@@ -19094,27 +19116,27 @@ function IntentStepper({
19094
19116
  intentPhrase: b.intent.phrase,
19095
19117
  restaurantName,
19096
19118
  items,
19097
- intentId: b.intentId,
19119
+ intentId: b.intent.id,
19098
19120
  restaurantId,
19099
19121
  intentExcludes: b.intent.excludes,
19100
19122
  onSwap,
19101
- onRemove: (itemId) => cart.removeItem(b.intentId, itemId),
19102
- onTogglePicked: (itemId) => cart.togglePicked(b.intentId, itemId),
19103
- onQtyChange: (itemId, q) => cart.setQty(b.intentId, itemId, q),
19123
+ onRemove: (itemId) => cart.removeItem(b.intent.id, itemId),
19124
+ onTogglePicked: (itemId) => cart.togglePicked(b.intent.id, itemId),
19125
+ onQtyChange: (itemId, q) => cart.setQty(b.intent.id, itemId, q),
19104
19126
  onViewItem: (item) => setViewItem({
19105
19127
  item,
19106
- intentId: b.intentId,
19128
+ intentId: b.intent.id,
19107
19129
  restaurantId,
19108
19130
  restaurantName
19109
19131
  }),
19110
19132
  getAddonChip: (item) => computeAddonChip(
19111
- b.intentId,
19133
+ b.intent.id,
19112
19134
  item,
19113
19135
  restaurantId,
19114
19136
  restaurantName
19115
19137
  )
19116
19138
  },
19117
- b.intentId
19139
+ b.intent.id
19118
19140
  )) }) : /* @__PURE__ */ jsxRuntime.jsx(EmptyIntentState, { message: "No items picked yet \u2014 head back and check the items you want." })
19119
19141
  },
19120
19142
  `${activeMealSessionIndex}-review`
@@ -19140,9 +19162,9 @@ function IntentStepper({
19140
19162
  }
19141
19163
  ),
19142
19164
  intentLiveSlots.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: "0", padding: 0, listStyle: "none" }, children: intentLiveSlots.map(({ item, slotId }) => {
19143
- const checked = cart.isPicked(block.intentId, item.id);
19165
+ const checked = cart.isPicked(block.intent.id, item.id);
19144
19166
  const chip = computeAddonChip(
19145
- block.intentId,
19167
+ block.intent.id,
19146
19168
  item,
19147
19169
  selected.restaurant.id,
19148
19170
  selected.restaurant.name
@@ -19166,7 +19188,7 @@ function IntentStepper({
19166
19188
  addonState: chip.state,
19167
19189
  addonSummary: chip.summary,
19168
19190
  onSwap: () => onSwap({
19169
- intentId: block.intentId,
19191
+ intentId: block.intent.id,
19170
19192
  slotId,
19171
19193
  itemId: item.id,
19172
19194
  itemName: item.name,
@@ -19176,10 +19198,10 @@ function IntentStepper({
19176
19198
  intentExcludes: block.intent.excludes,
19177
19199
  visibleItemIds: intentLiveSlots.map((s) => s.item.id)
19178
19200
  }),
19179
- onRemove: () => cart.removeItem(block.intentId, item.id),
19201
+ onRemove: () => cart.removeItem(block.intent.id, item.id),
19180
19202
  onView: () => setViewItem({
19181
19203
  item,
19182
- intentId: block.intentId,
19204
+ intentId: block.intent.id,
19183
19205
  restaurantId: selected.restaurant.id,
19184
19206
  restaurantName: selected.restaurant.name
19185
19207
  })
@@ -19189,7 +19211,7 @@ function IntentStepper({
19189
19211
  }) }) : /* @__PURE__ */ jsxRuntime.jsx(EmptyIntentState, { message: "No items in this intent yet \u2014 keep chatting on the right." })
19190
19212
  ]
19191
19213
  },
19192
- `${activeMealSessionIndex}-${block.intentId}-${pickedId}`
19214
+ `${activeMealSessionIndex}-${block.intent.id}-${pickedId}`
19193
19215
  ) }),
19194
19216
  atFinal && minOrderShortfalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
19195
19217
  "div",
@@ -19742,7 +19764,7 @@ function TimelineList({
19742
19764
  children: [
19743
19765
  ...meal.intentBlocks.map((b, i) => ({
19744
19766
  kind: "intent",
19745
- key: b.intentId,
19767
+ key: b.intent.id,
19746
19768
  phrase: b.intent.phrase,
19747
19769
  idx: i
19748
19770
  })),
@@ -20812,13 +20834,13 @@ function resolveDraftItems(mealSessionParts, aiMealSessions, activeMealSessionIn
20812
20834
  }
20813
20835
  const out = [];
20814
20836
  for (const block of ms.intentBlocks) {
20815
- const selectedRestaurantId = cart.getSelectedRestaurantId(block.intentId) ?? block.restaurantPicks[0]?.restaurant.id;
20837
+ const selectedRestaurantId = cart.getSelectedRestaurantId(block.intent.id) ?? block.restaurantPicks[0]?.restaurant.id;
20816
20838
  if (!selectedRestaurantId) continue;
20817
20839
  const pick = block.restaurantPicks.find(
20818
20840
  (rp) => rp.restaurant.id === selectedRestaurantId
20819
20841
  );
20820
20842
  if (!pick) continue;
20821
- const cartIntent = cart.cart[block.intentId];
20843
+ const cartIntent = cart.cart[block.intent.id];
20822
20844
  const removedSet = new Set(cartIntent?.removedItemIds ?? []);
20823
20845
  const pickedSet = new Set(cartIntent?.pickedItemIds ?? []);
20824
20846
  const liveItems = [
@@ -20830,7 +20852,7 @@ function resolveDraftItems(mealSessionParts, aiMealSessions, activeMealSessionIn
20830
20852
  )
20831
20853
  ];
20832
20854
  for (const it of liveItems) {
20833
- const qty = qtyByItemKey.get(`${block.intentId}::${it.id}`) ?? 0;
20855
+ const qty = qtyByItemKey.get(`${block.intent.id}::${it.id}`) ?? 0;
20834
20856
  if (qty <= 0) continue;
20835
20857
  out.push({
20836
20858
  id: it.id,
@@ -20850,7 +20872,7 @@ function resolveDraftItems(mealSessionParts, aiMealSessions, activeMealSessionIn
20850
20872
  reason: it.reason ?? "",
20851
20873
  restaurantId: selectedRestaurantId,
20852
20874
  intentPhrase: block.intent.phrase,
20853
- addonSelections: cart.getAddonSelections(block.intentId, it.id)
20875
+ addonSelections: cart.getAddonSelections(block.intent.id, it.id)
20854
20876
  });
20855
20877
  }
20856
20878
  }
@@ -23135,7 +23157,7 @@ function RestaurantMenuBrowser({
23135
23157
  ] }),
23136
23158
  (() => {
23137
23159
  if (isUnavailableForSession) return null;
23138
- const restaurantLoc = restaurant.pickupAddresses?.[0]?.location;
23160
+ const restaurantLoc = restaurant.address?.location;
23139
23161
  const userLoc = contactInfo?.latitude && contactInfo?.longitude ? { latitude: contactInfo.latitude, longitude: contactInfo.longitude } : null;
23140
23162
  const distance = userLoc && restaurantLoc ? haversineDistanceMiles(userLoc.latitude, userLoc.longitude, restaurantLoc.latitude, restaurantLoc.longitude) : null;
23141
23163
  const rating = restaurant.averageRating && parseFloat(restaurant.averageRating) > 0 ? parseFloat(restaurant.averageRating) : null;
@@ -23312,7 +23334,7 @@ function RestaurantMenuBrowser({
23312
23334
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
23313
23335
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-2xl font-bold text-gray-900", children: selectedRestaurant.restaurant_name }),
23314
23336
  (() => {
23315
- const restaurantLoc = selectedRestaurant.pickupAddresses?.[0]?.location;
23337
+ const restaurantLoc = selectedRestaurant.address?.location;
23316
23338
  const userLoc = contactInfo?.latitude && contactInfo?.longitude ? { latitude: contactInfo.latitude, longitude: contactInfo.longitude } : null;
23317
23339
  const distance = userLoc && restaurantLoc ? haversineDistanceMiles(userLoc.latitude, userLoc.longitude, restaurantLoc.latitude, restaurantLoc.longitude) : null;
23318
23340
  const rating = selectedRestaurant.averageRating && parseFloat(selectedRestaurant.averageRating) > 0 ? parseFloat(selectedRestaurant.averageRating) : null;
@@ -25703,6 +25725,8 @@ function CheckoutScreen() {
25703
25725
  markOrderAsSubmitted();
25704
25726
  setShowPaymentModal(false);
25705
25727
  setSubmittedOrder(createCateringOrderResponse);
25728
+ const clearedSid = clearChatSessionStorage();
25729
+ clearCartStorage(clearedSid);
25706
25730
  const orderId = createCateringOrderResponse?.id ?? "";
25707
25731
  const accessToken = createCateringOrderResponse?.sharedAccessUsers?.[0]?.accessToken ?? "";
25708
25732
  setPendingOrderResult({
@@ -26373,7 +26397,7 @@ function CheckoutScreen() {
26373
26397
  deliveryFeeAmt.toFixed(2)
26374
26398
  ] })
26375
26399
  ] }),
26376
- promoDiscountAmt > 2 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-sm text-success font-medium", children: [
26400
+ promoDiscountAmt > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-sm text-success font-medium", children: [
26377
26401
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Promo Discount" }),
26378
26402
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
26379
26403
  "-\xA3",
@@ -29061,7 +29085,17 @@ function CateringOrderBuilder() {
29061
29085
  )
29062
29086
  }
29063
29087
  ),
29064
- mobileChatView === "chat" && /* @__PURE__ */ jsxRuntime.jsx(MobileChatFloatingChips, { bottomOffset: 64 + 8, inputRef: mobileAIInputRef })
29088
+ mobileChatView === "chat" && /* @__PURE__ */ jsxRuntime.jsx(
29089
+ MobileChatFloatingChips,
29090
+ {
29091
+ bottomOffset: mobileAIInputHeight + 28,
29092
+ inputRef: mobileAIInputRef,
29093
+ onCancelFeedback: () => {
29094
+ setMobileAIInput("");
29095
+ resetMobileAIInputHeight();
29096
+ }
29097
+ }
29098
+ )
29065
29099
  ]
29066
29100
  },
29067
29101
  "chat-surface"
@@ -29630,15 +29664,24 @@ function MobileChatThread({
29630
29664
  const paddingTop = hasAddressTopPill ? 68 : 56;
29631
29665
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4", style: { paddingTop, paddingBottom }, children: /* @__PURE__ */ jsxRuntime.jsx(ChatMessagesView, {}) });
29632
29666
  }
29633
- function MobileChatFloatingChips({ bottomOffset, inputRef }) {
29667
+ function MobileChatFloatingChips({
29668
+ bottomOffset,
29669
+ inputRef,
29670
+ onCancelFeedback
29671
+ }) {
29634
29672
  const {
29635
29673
  hasResults,
29636
29674
  onViewResults,
29637
29675
  feedbackTarget,
29638
29676
  feedbackRating,
29639
29677
  setFeedbackRating,
29678
+ feedbackState,
29640
29679
  cancelFeedback
29641
29680
  } = useChatSessionContext();
29681
+ const handleCancel = () => {
29682
+ cancelFeedback();
29683
+ onCancelFeedback?.();
29684
+ };
29642
29685
  const [feedbackHover, setFeedbackHover] = react.useState(0);
29643
29686
  const inFeedbackMode = feedbackTarget !== null;
29644
29687
  react.useEffect(() => {
@@ -29665,7 +29708,7 @@ function MobileChatFloatingChips({ bottomOffset, inputRef }) {
29665
29708
  "button",
29666
29709
  {
29667
29710
  type: "button",
29668
- onClick: cancelFeedback,
29711
+ onClick: handleCancel,
29669
29712
  className: "p-0.5 rounded text-white/60 hover:text-white transition-colors",
29670
29713
  "aria-label": "Cancel feedback",
29671
29714
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3.5 h-3.5" })
@@ -29693,7 +29736,9 @@ function MobileChatFloatingChips({ bottomOffset, inputRef }) {
29693
29736
  },
29694
29737
  n
29695
29738
  )) }),
29696
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-white/80 min-w-[3.5rem]", children: activeStar > 0 ? RATING_LABELS[activeStar - 1] : "" })
29739
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-white/80 min-w-[3.5rem]", children: feedbackState === "done" ? "" : activeStar > 0 ? RATING_LABELS[activeStar - 1] : "" }),
29740
+ feedbackState === "error" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-red-300", children: "Failed" }),
29741
+ feedbackState === "done" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-green-300 font-medium", children: "Thanks!" })
29697
29742
  ]
29698
29743
  },
29699
29744
  "feedback-pill"
@@ -29731,13 +29776,11 @@ function MobileAIInput({
29731
29776
  chat: { sending, bootstrapping, sessionId, sendText, submitFeedback },
29732
29777
  feedbackTarget,
29733
29778
  feedbackRating,
29779
+ feedbackState,
29780
+ setFeedbackState,
29734
29781
  cancelFeedback
29735
29782
  } = useChatSessionContext();
29736
29783
  const inFeedbackMode = feedbackTarget !== null;
29737
- const [feedbackState, setFeedbackState] = react.useState("idle");
29738
- react.useEffect(() => {
29739
- if (!feedbackTarget) setFeedbackState("idle");
29740
- }, [feedbackTarget]);
29741
29784
  const chatDisabled = sending || bootstrapping || !sessionId;
29742
29785
  const inputDisabled = inFeedbackMode ? feedbackState === "sending" : bootstrapping || !sessionId;
29743
29786
  const sendDisabled = inFeedbackMode ? feedbackState === "sending" : submitBlocked || (chatDisabled || !value.trim());