@wix/himalaya-cli 0.1527.0 → 0.1529.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/ecom/src/extensions.d.ts +144 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/restaurants/src/extensions.d.ts +93 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +27 -5
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +19 -3
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +27 -5
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-ecom.mjs +58 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-restaurants.mjs +23 -0
  8. package/dist/cli.mjs +5844 -4606
  9. package/fixtures/wix-canned.json +83 -0
  10. package/guides/hhp.md +39 -0
  11. package/guides/himi-json-output.md +67 -1
  12. package/guides/himi-mcp.md +41 -0
  13. package/guides/iap-products.md +88 -23
  14. package/package.json +2 -2
  15. package/templates/beauty-salon/config.ts +12 -1
  16. package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +12 -5
  17. package/templates/beauty-salon/tier5-src/_shared/restaurant-ordering.ts +201 -0
  18. package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +6 -0
  19. package/templates/branded-lite/config.ts +12 -1
  20. package/templates/branded-lite/dev/index.ts +1278 -180
  21. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +12 -5
  22. package/templates/branded-lite/tier5-src/_shared/restaurant-ordering.ts +201 -0
  23. package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +6 -0
  24. package/templates/branded-lite/tier5-src/himi-branded-lite-menu-item/index.ts +144 -77
  25. package/templates/branded-lite/tier5-src/himi-branded-lite-menus/index.ts +16 -5
  26. package/templates/branded-lite/tier5-src/himi-branded-lite-reservations/index.ts +1 -1
  27. package/templates/branded-lite/tier5-src/wix-spaces-food-order-detail/index.ts +76 -14
  28. package/templates/branded-lite/tier5-src/wix-spaces-food-orders/index.ts +807 -55
  29. package/templates/community-club/config.ts +12 -1
  30. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +12 -5
  31. package/templates/events-meetups/config.ts +12 -1
  32. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +12 -5
  33. package/templates/gym-fitness/config.ts +12 -1
  34. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +12 -5
  35. package/templates/gym-fitness/tier5-src/_shared/restaurant-ordering.ts +201 -0
  36. package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +6 -0
  37. package/templates/nonprofit-cause/config.ts +12 -1
  38. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +12 -5
  39. package/templates/podcast-creator/config.ts +12 -1
  40. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +12 -5
  41. package/templates/portfolio-creative/config.ts +12 -1
  42. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +12 -5
  43. package/templates/portfolio-creative/tier5-src/_shared/restaurant-ordering.ts +201 -0
  44. package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +6 -0
  45. package/templates/restaurant/README.md +1 -1
  46. package/templates/restaurant/config.ts +12 -1
  47. package/templates/restaurant/dev/index.ts +1373 -275
  48. package/templates/restaurant/himalaya.template.json +2 -1
  49. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +12 -5
  50. package/templates/restaurant/tier5-src/_shared/member-orders.ts +8 -0
  51. package/templates/restaurant/tier5-src/_shared/restaurant-ordering.ts +201 -0
  52. package/templates/restaurant/tier5-src/himi-branded-lite-menu-item/index.ts +144 -77
  53. package/templates/restaurant/tier5-src/himi-branded-lite-menus/index.ts +16 -5
  54. package/templates/restaurant/tier5-src/wix-spaces-food-order-detail/index.ts +76 -14
  55. package/templates/restaurant/tier5-src/wix-spaces-food-orders/index.ts +807 -55
  56. package/templates/retail-store/config.ts +12 -1
  57. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +12 -5
  58. package/templates/retail-store/tier5-src/_shared/restaurant-ordering.ts +201 -0
  59. package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +6 -0
  60. package/templates/yoga-studio/config.ts +12 -1
  61. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +12 -5
@@ -17,6 +17,9 @@ import { copyParam, stashCopy } from "../_shared/himi-copy.js";
17
17
  /** One section with its shaped dishes (the source of truth `all` keeps). */
18
18
  interface MenuSection {
19
19
  id: string;
20
+ /** The menu that lists this section ("" for the flat fallback) — the dish detail orders
21
+ * the dish under this menu + section, which is what Wix checkout validates. */
22
+ menuId: string;
20
23
  name: string;
21
24
  items: DishCard[];
22
25
  }
@@ -47,6 +50,9 @@ interface MenuRow {
47
50
  noLabel2: boolean;
48
51
  noLabel3: boolean;
49
52
  detailScreenId: string;
53
+ /** Where the dish is shown — passed to the detail as nav params ("" on header rows). */
54
+ menuId: string;
55
+ sectionId: string;
50
56
  }
51
57
 
52
58
  interface State {
@@ -87,10 +93,12 @@ function headerRow(section: MenuSection): MenuRow {
87
93
  noLabel2: true,
88
94
  noLabel3: true,
89
95
  detailScreenId: "",
96
+ menuId: "",
97
+ sectionId: "",
90
98
  };
91
99
  }
92
100
 
93
- function itemRow(item: DishCard): MenuRow {
101
+ function itemRow(item: DishCard, section: MenuSection): MenuRow {
94
102
  return {
95
103
  id: item.id,
96
104
  isHeader: false,
@@ -112,6 +120,8 @@ function itemRow(item: DishCard): MenuRow {
112
120
  noLabel2: item.noLabel2,
113
121
  noLabel3: item.noLabel3,
114
122
  detailScreenId: item.detailScreenId,
123
+ menuId: section.menuId,
124
+ sectionId: section.id === "all" ? "" : section.id,
115
125
  };
116
126
  }
117
127
 
@@ -128,7 +138,7 @@ function apply(all: MenuSection[], query: string): MenuRow[] {
128
138
  }))
129
139
  .filter((section) => section.items.length > 0)
130
140
  : all;
131
- return filtered.flatMap((section) => [headerRow(section), ...section.items.map(itemRow)]);
141
+ return filtered.flatMap((section) => [headerRow(section), ...section.items.map((item) => itemRow(item, section))]);
132
142
  }
133
143
 
134
144
  function derive(state: State): State {
@@ -163,9 +173,10 @@ export function composeSections(menus: WixMenu[], sections: WixSection[], dishes
163
173
  const sectionById = new Map(sections.filter((section) => section.id).map((section) => [section.id, section]));
164
174
  const orderedIds: string[] = [];
165
175
  const seen = new Set<string>();
176
+ const menuOf = new Map<string, string>();
166
177
  for (const menu of menus.filter((candidate) => candidate.visible !== false)) {
167
178
  for (const sectionId of menu.sectionIds ?? []) {
168
- if (sectionId && !seen.has(sectionId)) { seen.add(sectionId); orderedIds.push(sectionId); }
179
+ if (sectionId && !seen.has(sectionId)) { seen.add(sectionId); orderedIds.push(sectionId); menuOf.set(sectionId, menu.id ?? ""); }
169
180
  }
170
181
  }
171
182
  // Menus query failed / carried no section links → every section, in order.
@@ -182,11 +193,11 @@ export function composeSections(menus: WixMenu[], sections: WixSection[], dishes
182
193
  return dish ? [dish] : [];
183
194
  });
184
195
  if (items.length === 0) return [];
185
- return [{ id: section.id, name: cleanTitle(section.name) || copyParam("menu.sectionFallbackName", "Menu"), items }];
196
+ return [{ id: section.id, menuId: menuOf.get(section.id) ?? "", name: cleanTitle(section.name) || copyParam("menu.sectionFallbackName", "Menu"), items }];
186
197
  });
187
198
  if (composed.length > 0) return composed;
188
199
  // Sections unavailable (or none matched a dish) → flat single-section menu.
189
- return dishes.length ? [{ id: "all", name: copyParam("menu.sectionFallbackName", "Menu"), items: dishes }] : [];
200
+ return dishes.length ? [{ id: "all", menuId: "", name: copyParam("menu.sectionFallbackName", "Menu"), items: dishes }] : [];
190
201
  }
191
202
 
192
203
  function strOf(arg: unknown): string {
@@ -7,7 +7,7 @@ import { withOnlineWrites } from "../_shared/online-actions.js";
7
7
  // INTERACTIVE: tapping an upcoming reservation cancels it — optimistic + best-effort
8
8
  // cancelReservation (no-op on sample rows).
9
9
  //
10
- // RFC 0052 consumer: the list is a `himalaya.Agenda` — chronological, grouped
10
+ // The list is a `himalaya.Agenda` — chronological, grouped
11
11
  // by LOCAL day under sticky headers. A reservation list is a diary, and the
12
12
  // flat grid it replaced repeated "Fri, Aug 8 · 7:30 PM" on every card because
13
13
  // no row knew what day the row above it was on. `Agenda` owns the day headers
@@ -1,24 +1,86 @@
1
- // Tier-5 bundle: Wix Spaces clone — explicit secure-site handoff.
2
- // The owner-capable Restaurant Orders API must never be queried from a member device.
1
+ // Tier-5 bundle: one placed restaurant order for the signed-in member.
2
+ // Read through the member eCom orders search (filtered to the member's email and the order id),
3
+ // the same path as the order list — never the owner-capable Restaurants Orders API. An order that
4
+ // is not this member's, or has no Restaurants line, is "not found".
3
5
  import { defineService } from "@himalaya/state";
6
+ import { searchOrders, type WixOrder } from "@himalaya/wix-ecom";
4
7
  import { presence, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
8
+ import { assertOrderMember, orderAccessDenied, orderMember } from "../_shared/member-orders.js";
9
+ import { RESTAURANTS_ORDERS_APP_ID } from "../_shared/restaurant-ordering.js";
10
+ import { formatDateMedium } from "../_shared/engine-intl.js";
11
+ import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
5
12
 
6
13
  interface S extends PresenceFlags { status: "loading" | "ready"; name: string; status_: string; total: string; items: string; message: string }
7
14
 
8
- const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesFoodOrderDetail", {
9
- state: { status: "loading", name: "", status_: "", total: "", items: "", message: "", ...presence("loading") },
10
- actions: {
11
- // No order id → the shared not-found state; an id → the secure-site handoff.
12
- load: async () => (!detailInputId() ? { status: "ready", name: "", status_: "", total: "", items: "", message: "", ...presence("missing") } : {
13
- ...presence("found"),
15
+ const EMPTY = { name: "", status_: "", total: "", items: "", message: "" };
16
+
17
+ function statusLine(order: WixOrder): string {
18
+ const raw = order as WixOrder & { status?: string };
19
+ const status = raw.status === "CANCELED" ? copyParam("foodOrders.statusCanceled", "Canceled")
20
+ : order.fulfillmentStatus === "FULFILLED" ? copyParam("foodOrders.statusCompleted", "Completed")
21
+ : order.paymentStatus === "NOT_PAID" || order.paymentStatus === "PENDING" ? copyParam("foodOrders.statusAwaitingPayment", "Awaiting payment")
22
+ : copyParam("foodOrders.statusReceived", "Received");
23
+ const created = order.createdDate ? new Date(order.createdDate) : undefined;
24
+ return [status, created && !Number.isNaN(created.getTime()) ? formatDateMedium(created) : ""].filter(Boolean).join(" · ");
25
+ }
26
+
27
+ function fulfillmentLine(order: WixOrder): string {
28
+ const title = (order as WixOrder & { shippingInfo?: { title?: string } }).shippingInfo?.title ?? "";
29
+ return title ? copyTemplate("foodOrderDetail.fulfillment", "Fulfillment: {method}", { method: title }) : "";
30
+ }
31
+
32
+ /** Bumped by every load. The module-scope load and the screen's on-appear load overlap: only the
33
+ * newest commits, so an older, slower answer never replaces a newer one. */
34
+ let loadSeq = 0;
35
+ /** The service's live state (set once the service exists). */
36
+ let latestState: () => S = () => ({ status: "loading", ...EMPTY, ...presence("loading") });
37
+
38
+ async function read(id: string): Promise<S> {
39
+ const member = await orderMember();
40
+ if (!member.email) {
41
+ return { status: "ready", ...EMPTY, ...presence("found"),
42
+ message: copyParam("foodOrderDetail.signIn", "Sign in to see this order.") };
43
+ }
44
+ try {
45
+ const response = await searchOrders({ search: {
46
+ filter: { "buyerInfo.email": { $eq: member.email }, id: { $eq: id } },
47
+ cursorPaging: { limit: 1 },
48
+ } });
49
+ await assertOrderMember(member);
50
+ const order = (response.orders ?? []).find((candidate) => candidate.id === id
51
+ && (candidate.buyerInfo?.email ?? "").trim().toLowerCase() === member.email
52
+ && (candidate.lineItems ?? []).some((line) =>
53
+ (line as { catalogReference?: { appId?: string } }).catalogReference?.appId === RESTAURANTS_ORDERS_APP_ID));
54
+ if (!order) return { status: "ready", ...EMPTY, ...presence("missing") };
55
+ return {
14
56
  status: "ready",
15
- name: "",
16
- status_: "",
17
- total: "",
18
- items: "",
19
- message: "Live order details require a secure connection configured by this business.",
20
- }),
57
+ ...presence("found"),
58
+ name: copyTemplate("foodOrders.orderTitle", "Order #{number}", { number: order.number || order.id }),
59
+ status_: statusLine(order),
60
+ total: order.priceSummary?.total?.formattedAmount ?? "",
61
+ items: (order.lineItems ?? [])
62
+ .map((line) => `${line.quantity || 1} × ${line.productName?.translated || line.productName?.original || ""}`.trim())
63
+ .join("\n"),
64
+ message: fulfillmentLine(order),
65
+ };
66
+ } catch (error) {
67
+ return { status: "ready", ...EMPTY, ...presence(orderAccessDenied(error) ? "missing" : "failed") };
68
+ }
69
+ }
70
+
71
+ const svc = defineService<S, { load: (s: S, arg?: unknown) => Promise<S> }>("spacesFoodOrderDetail", {
72
+ state: { status: "loading", ...EMPTY, ...presence("loading") },
73
+ actions: {
74
+ load: async (_state, arg) => {
75
+ stashCopy(arg);
76
+ const id = detailInputId();
77
+ if (!id) return { status: "ready", ...EMPTY, ...presence("missing") };
78
+ const seq = ++loadSeq;
79
+ const next = await read(id);
80
+ return seq === loadSeq ? next : latestState();
81
+ },
21
82
  },
22
83
  });
84
+ latestState = () => svc.getState() as S;
23
85
  svc.load();
24
86
  export { svc };