@wix/himalaya-cli 0.1352.0 → 0.1353.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 (27) hide show
  1. package/package.json +2 -2
  2. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  3. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
  4. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  5. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +17 -3
  6. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
  7. package/templates/community-club/tier5-src/_shared/hosted-checkout.ts +42 -0
  8. package/templates/community-club/tier5-src/himi-events-detail/index.ts +17 -3
  9. package/templates/events-meetups/tier5-src/_shared/hosted-checkout.ts +42 -0
  10. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +17 -3
  11. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  12. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +17 -3
  13. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
  14. package/templates/nonprofit-cause/tier5-src/_shared/hosted-checkout.ts +42 -0
  15. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  16. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +17 -3
  17. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
  18. package/templates/podcast-creator/tier5-src/_shared/hosted-checkout.ts +42 -0
  19. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  20. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +17 -3
  21. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
  22. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +17 -3
  23. package/templates/restaurant/tier5-src/_shared/hosted-checkout.ts +42 -0
  24. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +17 -3
  25. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +17 -0
  26. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +17 -3
  27. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +30 -4
@@ -13,6 +13,7 @@ import {
13
13
  queryAvailableTickets,
14
14
  queryEvents,
15
15
  type WixAvailableTicket,
16
+ type WixEventFieldset,
16
17
  type WixEvent,
17
18
  type WixGuestRsvp,
18
19
  type WixGuestTicket,
@@ -34,6 +35,7 @@ import {
34
35
  type EventRow,
35
36
  } from "../_shared/himi-events";
36
37
  import { initials } from "../_shared/himi-text.js";
38
+ import { isHostedWorker } from "../_shared/hosted-checkout.js";
37
39
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
38
40
  import { openRichLink } from "../_shared/rich-content-link.js";
39
41
  import { richBody } from "../_shared/rich-body.js";
@@ -281,6 +283,10 @@ function calendarFallbackUrl(s: S, end: number): string {
281
283
  return `https://calendar.google.com/calendar/render?${parts.join("&")}`;
282
284
  }
283
285
 
286
+ /** The Events v3 fieldsets the detail reads (see `WixEventFieldset`). DETAILS carries mainImage and
287
+ * shortDescription: REGISTRATION fills eventType, which skips the query enrichment that used to supply them. */
288
+ export const EVENT_DETAIL_FIELDSETS: WixEventFieldset[] = ["DETAILS", "TEXTS", "REGISTRATION", "URLS"];
289
+
284
290
  /** Map the decoded registration/eventType onto the CTA variant. The decoded
285
291
  * `eventType` already folds registration.type / registration.initialType /
286
292
  * the legacy flat `type` (decodeEvent, #478). Unknown ⇒ RSVP (historical). */
@@ -458,8 +464,11 @@ const svc = defineService<S, {
458
464
  const id = s.id || inputId();
459
465
  if (!id) return derive({ ...s, status: "ready", errored: true });
460
466
  try {
461
- // TEXTS adds the Ricos `description` to the base fieldset (a bare read returns none).
462
- const res = await getEvent(id, { fields: ["TEXTS"] });
467
+ // TEXTS adds the Ricos `description` to the base fieldset (a bare read returns none);
468
+ // REGISTRATION adds `registration` (the CTA's type, and an external event's own URL);
469
+ // URLS adds `eventPageUrl` — the registration fallback and the share link. Without URLS a
470
+ // ticket flow that cannot complete in-app had no page to send the buyer to.
471
+ const res = await getEvent(id, { fields: EVENT_DETAIL_FIELDSETS });
463
472
  let e = ((res as unknown as { event?: WixEvent }).event ?? res) as WixEvent;
464
473
  e = await enrichRegistration(id, e);
465
474
  const attendees = await attendeeSummary(id);
@@ -566,7 +575,12 @@ const svc = defineService<S, {
566
575
  // Every failure degrades one rung at a time, never to a dead button:
567
576
  // native chain → hosted eventsCheckout (pinned to the SAME reservation
568
577
  // when we got one, else the slug) → the event page / registration URL.
569
- if (s.registrationKind === "tickets" && s.id) {
578
+ // Hosted inside a host shell (Spaces) neither in-app rung can finish: the classic
579
+ // `checkoutReservation` needs a buyer + guest form this screen does not collect, and the
580
+ // redirect session needs a headless OAuth client a hosted app does not have. Skip both —
581
+ // the native chain would also leave a ticket reservation held for nothing — and open the
582
+ // event's own page, where the member registers on the site.
583
+ if (s.registrationKind === "tickets" && s.id && !isHostedWorker()) {
570
584
  let reservationId = "";
571
585
  try {
572
586
  // Buy what the buyer PICKED. `load` seeds the pick with the cheapest
@@ -1,7 +1,13 @@
1
1
  // Tier-5 bundle: Wix Spaces clone — Pricing plan detail (ON-DEVICE, site-scoped).
2
- // himalaya.input.id carries the plan id; fetched via getPlan.
2
+ // himalaya.input.id carries the plan id.
3
+ //
4
+ // Read from the PUBLIC plans (GET /pricing-plans/v2/plans/public — what the plans list
5
+ // shows), not from getPlan: GET /pricing-plans/v2/plans/{id} is the management read, and a
6
+ // hosted site's member or visitor gets a 403 from it, which rendered "couldn't be loaded"
7
+ // for every plan the list had just shown. getPlan stays as the fallback for a plan that is
8
+ // not public, for a caller that may read it (an owner-scope host).
3
9
  import { withIapPresentation } from "../_shared/iap-presentation.js";
4
- import { getPlan, type WixPlan } from "@himalaya/wix-pricing-plans";
10
+ import { getPlan, listPublicPlans, type WixPlan } from "@himalaya/wix-pricing-plans";
5
11
  import { presence, presenceAfterError, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
6
12
 
7
13
  interface S extends PresenceFlags { status: "loading" | "ready"; name: string; price: string; description: string; perks: string }
@@ -12,6 +18,26 @@ function priceOf(p: WixPlan): string {
12
18
  return m.formattedAmount || [m.amount ?? m.value, m.currency].filter(Boolean).join(" ");
13
19
  }
14
20
 
21
+ /** The plan, or null when it is not public and the caller may not read it otherwise. */
22
+ async function readPlan(id: string): Promise<WixPlan | null> {
23
+ let publicRead = false;
24
+ try {
25
+ const listed = await listPublicPlans();
26
+ publicRead = true;
27
+ const hit = (listed?.plans ?? []).find((plan) => plan?.id === id);
28
+ if (hit) return hit;
29
+ } catch (e) { void e; }
30
+ try {
31
+ const res = await getPlan(id);
32
+ return ((res as unknown as { plan?: WixPlan }).plan) ?? (res as unknown as WixPlan);
33
+ } catch (e) {
34
+ // The public read worked, so the device is online: a refused or empty management
35
+ // read means this plan is not one the viewer can see — not-found, not offline.
36
+ if (publicRead) return null;
37
+ throw e;
38
+ }
39
+ }
40
+
15
41
  const defineService = withIapPresentation<S>({
16
42
  prefix: "plans", transientKeys: ["price"], contentActions: ["load"],
17
43
  items: state => detailInputId() ? [{ reference: { kind: "pricingPlan", id: detailInputId() }, commerceDisplayPrice: state.price }] : [],
@@ -24,8 +50,8 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesPlanDetail",
24
50
  const id = detailInputId();
25
51
  if (!id) return { ...s, status: "ready", ...presence("missing") };
26
52
  try {
27
- const res = await getPlan(id);
28
- const p = ((res as unknown as { plan?: WixPlan }).plan) ?? (res as unknown as WixPlan);
53
+ const p = await readPlan(id);
54
+ if (!p) return { ...s, status: "ready", ...presence("missing") };
29
55
  const perks = (p.perks?.values ?? []).map((v) => `• ${v}`).join("\n");
30
56
  return { status: "ready", name: p.name || "Plan", price: priceOf(p), description: p.description || "", perks, ...presence("found") };
31
57
  } catch (e) { return { ...s, status: "ready", ...presenceAfterError(e) }; }