@wix/himalaya-cli 0.1478.0 → 0.1480.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 (45) hide show
  1. package/dist/cli.mjs +454 -263
  2. package/dist/preview-assets.generated.bin +0 -0
  3. package/guides/component-reference.json +2 -25
  4. package/guides/component-reference.md +3 -9
  5. package/package.json +2 -2
  6. package/templates/beauty-salon/dev/index.ts +29 -229
  7. package/templates/beauty-salon/tier5-src/himi-booking-time/index.ts +69 -9
  8. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +6 -1
  9. package/templates/beauty-salon/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
  10. package/templates/branded-lite/dev/index.ts +58 -364
  11. package/templates/branded-lite/tier5-src/himi-booking-time/index.ts +69 -9
  12. package/templates/branded-lite/tier5-src/himi-events-rsvp/index.ts +5 -0
  13. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +6 -1
  14. package/templates/branded-lite/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
  15. package/templates/community-club/dev/index.ts +26 -134
  16. package/templates/community-club/tier5-src/himi-events-rsvp/index.ts +5 -0
  17. package/templates/community-club/tier5-src/himi-store-account/index.ts +6 -1
  18. package/templates/events-meetups/dev/index.ts +26 -134
  19. package/templates/events-meetups/tier5-src/himi-events-rsvp/index.ts +5 -0
  20. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +6 -1
  21. package/templates/gym-fitness/dev/index.ts +55 -363
  22. package/templates/gym-fitness/tier5-src/himi-booking-time/index.ts +69 -9
  23. package/templates/gym-fitness/tier5-src/himi-events-rsvp/index.ts +5 -0
  24. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +6 -1
  25. package/templates/gym-fitness/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
  26. package/templates/nonprofit-cause/dev/index.ts +29 -136
  27. package/templates/nonprofit-cause/tier5-src/himi-events-rsvp/index.ts +5 -0
  28. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +6 -1
  29. package/templates/nonprofit-cause/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
  30. package/templates/podcast-creator/dev/index.ts +29 -136
  31. package/templates/podcast-creator/tier5-src/himi-events-rsvp/index.ts +5 -0
  32. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +6 -1
  33. package/templates/podcast-creator/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
  34. package/templates/portfolio-creative/dev/index.ts +26 -134
  35. package/templates/portfolio-creative/tier5-src/himi-events-rsvp/index.ts +5 -0
  36. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +6 -1
  37. package/templates/restaurant/dev/index.ts +26 -134
  38. package/templates/restaurant/tier5-src/himi-events-rsvp/index.ts +5 -0
  39. package/templates/restaurant/tier5-src/himi-store-account/index.ts +6 -1
  40. package/templates/retail-store/tier5-src/himi-store-account/index.ts +6 -1
  41. package/templates/yoga-studio/dev/index.ts +55 -363
  42. package/templates/yoga-studio/tier5-src/himi-booking-time/index.ts +69 -9
  43. package/templates/yoga-studio/tier5-src/himi-events-rsvp/index.ts +5 -0
  44. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +6 -1
  45. package/templates/yoga-studio/tier5-src/wix-spaces-my-subscriptions/index.ts +2 -2
@@ -26086,14 +26086,15 @@ const SCREENS = runtimeSetting({
26086
26086
  "package": "himalaya",
26087
26087
  "type": "Button",
26088
26088
  "props": {
26089
- "title": "Go to Account",
26089
+ "title": "Sign in",
26090
26090
  "style": "secondary",
26091
26091
  "fullWidth": true,
26092
26092
  "leadingIcon": "person",
26093
26093
  "action": {
26094
26094
  "actionId": "himi.open",
26095
26095
  "params": {
26096
- "screenId": "himi_account"
26096
+ "screenId": "himi_sign_in",
26097
+ "mode": "signin"
26097
26098
  }
26098
26099
  }
26099
26100
  }
@@ -37196,233 +37197,6 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
37196
37197
  "props": {
37197
37198
  "spacing": 12,
37198
37199
  "children": [
37199
- {
37200
- "id": "himi_time.signin_card",
37201
- "body": {
37202
- "package": "himalaya",
37203
- "type": "VStack",
37204
- "props": {
37205
- "spacing": 12,
37206
- "alignment": "leading",
37207
- "children": [
37208
- {
37209
- "id": "",
37210
- "body": {
37211
- "package": "himalaya",
37212
- "type": "HStack",
37213
- "props": {
37214
- "spacing": 10,
37215
- "children": [
37216
- {
37217
- "id": "",
37218
- "body": {
37219
- "package": "himalaya",
37220
- "type": "Icon",
37221
- "props": {
37222
- "name": "person",
37223
- "size": 18,
37224
- "tint": "accentPrimary"
37225
- }
37226
- },
37227
- "modifiers": {
37228
- "accessibilityRole": "decorative"
37229
- }
37230
- },
37231
- {
37232
- "id": "",
37233
- "body": {
37234
- "package": "himalaya",
37235
- "type": "Text",
37236
- "props": {
37237
- "text": "$service:himiBookingTime.authTitle",
37238
- "style": "bodyEmphasis",
37239
- "collapseWhenEmpty": true
37240
- }
37241
- }
37242
- }
37243
- ]
37244
- }
37245
- }
37246
- },
37247
- {
37248
- "id": "",
37249
- "body": {
37250
- "package": "himalaya",
37251
- "type": "Text",
37252
- "props": {
37253
- "text": "$service:himiBookingTime.authBody",
37254
- "style": "body",
37255
- "color": "textSecondary",
37256
- "collapseWhenEmpty": true
37257
- }
37258
- }
37259
- },
37260
- {
37261
- "id": "himi_time.signin.first_name",
37262
- "body": {
37263
- "package": "himalaya",
37264
- "type": "TextField",
37265
- "props": {
37266
- "placeholder": "First name",
37267
- "autoCapitalization": "words",
37268
- "onChange": {
37269
- "actionId": "tier5.himiBookingTime.setFirstName"
37270
- }
37271
- }
37272
- },
37273
- "modifiers": {
37274
- "hiddenPath": "$service:himiBookingTime.notRegistering"
37275
- }
37276
- },
37277
- {
37278
- "id": "himi_time.signin.last_name",
37279
- "body": {
37280
- "package": "himalaya",
37281
- "type": "TextField",
37282
- "props": {
37283
- "placeholder": "Last name",
37284
- "autoCapitalization": "words",
37285
- "onChange": {
37286
- "actionId": "tier5.himiBookingTime.setLastName"
37287
- }
37288
- }
37289
- },
37290
- "modifiers": {
37291
- "hiddenPath": "$service:himiBookingTime.notRegistering"
37292
- }
37293
- },
37294
- {
37295
- "id": "himi_time.signin.email",
37296
- "body": {
37297
- "package": "himalaya",
37298
- "type": "TextField",
37299
- "props": {
37300
- "placeholder": "Email",
37301
- "keyboard": "email",
37302
- "autoCapitalization": "none",
37303
- "onChange": {
37304
- "actionId": "tier5.himiBookingTime.setEmail"
37305
- }
37306
- }
37307
- }
37308
- },
37309
- {
37310
- "id": "himi_time.signin.password",
37311
- "body": {
37312
- "package": "himalaya",
37313
- "type": "TextField",
37314
- "props": {
37315
- "placeholder": "Password",
37316
- "valuePath": "$service:himiBookingTime.password",
37317
- "secure": true,
37318
- "autoCapitalization": "none",
37319
- "onChange": {
37320
- "actionId": "tier5.himiBookingTime.setPassword"
37321
- }
37322
- }
37323
- }
37324
- },
37325
- {
37326
- "id": "himi_time.signin",
37327
- "body": {
37328
- "package": "himalaya",
37329
- "type": "Button",
37330
- "props": {
37331
- "title": "Sign in",
37332
- "style": "primary",
37333
- "fullWidth": true,
37334
- "action": {
37335
- "actionId": "tier5.himiBookingTime.signIn"
37336
- }
37337
- }
37338
- },
37339
- "modifiers": {
37340
- "cornerRadius": 8,
37341
- "pressFeedback": "scale",
37342
- "haptic": "medium",
37343
- "hiddenPath": "$service:himiBookingTime.isRegistering"
37344
- }
37345
- },
37346
- {
37347
- "id": "himi_time.show_register",
37348
- "body": {
37349
- "package": "himalaya",
37350
- "type": "Button",
37351
- "props": {
37352
- "title": "Create account",
37353
- "style": "secondary",
37354
- "fullWidth": true,
37355
- "action": {
37356
- "actionId": "tier5.himiBookingTime.showRegister"
37357
- }
37358
- }
37359
- },
37360
- "modifiers": {
37361
- "hiddenPath": "$service:himiBookingTime.isRegistering"
37362
- }
37363
- },
37364
- {
37365
- "id": "himi_time.register",
37366
- "body": {
37367
- "package": "himalaya",
37368
- "type": "Button",
37369
- "props": {
37370
- "title": "Create account",
37371
- "style": "primary",
37372
- "fullWidth": true,
37373
- "action": {
37374
- "actionId": "tier5.himiBookingTime.register"
37375
- }
37376
- }
37377
- },
37378
- "modifiers": {
37379
- "cornerRadius": 8,
37380
- "pressFeedback": "scale",
37381
- "haptic": "medium",
37382
- "hiddenPath": "$service:himiBookingTime.notRegistering"
37383
- }
37384
- },
37385
- {
37386
- "id": "himi_time.show_signin",
37387
- "body": {
37388
- "package": "himalaya",
37389
- "type": "Button",
37390
- "props": {
37391
- "title": "I already have an account",
37392
- "style": "secondary",
37393
- "fullWidth": true,
37394
- "action": {
37395
- "actionId": "tier5.himiBookingTime.showSignIn"
37396
- }
37397
- }
37398
- },
37399
- "modifiers": {
37400
- "hiddenPath": "$service:himiBookingTime.notRegistering"
37401
- }
37402
- }
37403
- ]
37404
- }
37405
- },
37406
- "modifiers": {
37407
- "background": {
37408
- "token": "surfaceElevated"
37409
- },
37410
- "border": {
37411
- "width": 1,
37412
- "color": {
37413
- "token": "border"
37414
- }
37415
- },
37416
- "cornerRadius": 8,
37417
- "padding": {
37418
- "leading": 14,
37419
- "trailing": 14,
37420
- "top": 14,
37421
- "bottom": 14
37422
- },
37423
- "hiddenPath": "$service:himiBookingTime.hideSignIn"
37424
- }
37425
- },
37426
37200
  {
37427
37201
  "id": "himi_time.member_card",
37428
37202
  "body": {
@@ -37571,6 +37345,32 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
37571
37345
  "hiddenPath": "$service:himiBookingTime.notBooking"
37572
37346
  }
37573
37347
  },
37348
+ {
37349
+ "id": "himi_time.book_sign_in",
37350
+ "body": {
37351
+ "package": "himalaya",
37352
+ "type": "Button",
37353
+ "props": {
37354
+ "title": "Book selected time",
37355
+ "style": "primary",
37356
+ "fullWidth": true,
37357
+ "action": {
37358
+ "actionId": "himi.open",
37359
+ "params": {
37360
+ "screenId": "himi_sign_in",
37361
+ "mode": "signin",
37362
+ "intent": "book"
37363
+ }
37364
+ }
37365
+ }
37366
+ },
37367
+ "modifiers": {
37368
+ "cornerRadius": 8,
37369
+ "pressFeedback": "scale",
37370
+ "haptic": "medium",
37371
+ "hiddenPath": "$service:himiBookingTime.hideSignIn"
37372
+ }
37373
+ },
37574
37374
  {
37575
37375
  "id": "himi_time.book",
37576
37376
  "body": {
@@ -39008,140 +38808,6 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
39008
38808
  }
39009
38809
  }
39010
38810
  },
39011
- {
39012
- "id": "himi_rsvp.signin_card",
39013
- "body": {
39014
- "package": "himalaya",
39015
- "type": "VStack",
39016
- "props": {
39017
- "spacing": 12,
39018
- "alignment": "leading",
39019
- "children": [
39020
- {
39021
- "id": "",
39022
- "body": {
39023
- "package": "himalaya",
39024
- "type": "HStack",
39025
- "props": {
39026
- "spacing": 10,
39027
- "children": [
39028
- {
39029
- "id": "",
39030
- "body": {
39031
- "package": "himalaya",
39032
- "type": "Icon",
39033
- "props": {
39034
- "name": "person",
39035
- "size": 18,
39036
- "tint": "accentPrimary"
39037
- }
39038
- },
39039
- "modifiers": {
39040
- "accessibilityRole": "decorative"
39041
- }
39042
- },
39043
- {
39044
- "id": "",
39045
- "body": {
39046
- "package": "himalaya",
39047
- "type": "Text",
39048
- "props": {
39049
- "text": "Sign in to RSVP",
39050
- "style": "bodyEmphasis"
39051
- }
39052
- }
39053
- }
39054
- ]
39055
- }
39056
- }
39057
- },
39058
- {
39059
- "id": "",
39060
- "body": {
39061
- "package": "himalaya",
39062
- "type": "Text",
39063
- "props": {
39064
- "text": "Events can be browsed as a guest. RSVP is saved to your member identity.",
39065
- "style": "body",
39066
- "color": "textSecondary"
39067
- }
39068
- }
39069
- },
39070
- {
39071
- "id": "himi_rsvp.email",
39072
- "body": {
39073
- "package": "himalaya",
39074
- "type": "TextField",
39075
- "props": {
39076
- "placeholder": "Email",
39077
- "value": "",
39078
- "keyboard": "email",
39079
- "autoCapitalization": "none",
39080
- "onChange": {
39081
- "actionId": "tier5.himiEventsRsvp.setEmail"
39082
- }
39083
- }
39084
- }
39085
- },
39086
- {
39087
- "id": "himi_rsvp.password",
39088
- "body": {
39089
- "package": "himalaya",
39090
- "type": "TextField",
39091
- "props": {
39092
- "placeholder": "Password",
39093
- "valuePath": "$service:himiEventsRsvp.password",
39094
- "secure": true,
39095
- "autoCapitalization": "none",
39096
- "onChange": {
39097
- "actionId": "tier5.himiEventsRsvp.setPassword"
39098
- }
39099
- }
39100
- }
39101
- },
39102
- {
39103
- "id": "himi_rsvp.signin",
39104
- "body": {
39105
- "package": "himalaya",
39106
- "type": "Button",
39107
- "props": {
39108
- "title": "Sign in",
39109
- "style": "primary",
39110
- "fullWidth": true,
39111
- "action": {
39112
- "actionId": "tier5.himiEventsRsvp.signIn"
39113
- }
39114
- }
39115
- },
39116
- "modifiers": {
39117
- "cornerRadius": 8,
39118
- "pressFeedback": "scale",
39119
- "haptic": "medium"
39120
- }
39121
- }
39122
- ]
39123
- }
39124
- },
39125
- "modifiers": {
39126
- "background": {
39127
- "token": "surfaceElevated"
39128
- },
39129
- "border": {
39130
- "width": 1,
39131
- "color": {
39132
- "token": "border"
39133
- }
39134
- },
39135
- "cornerRadius": 8,
39136
- "padding": {
39137
- "leading": 14,
39138
- "trailing": 14,
39139
- "top": 14,
39140
- "bottom": 14
39141
- },
39142
- "hiddenPath": "$service:himiEventsRsvp.isMember"
39143
- }
39144
- },
39145
38811
  {
39146
38812
  "id": "himi_rsvp.member_card",
39147
38813
  "body": {
@@ -39257,6 +38923,32 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
39257
38923
  "hiddenPath": "$service:himiEventsRsvp.noSuccess"
39258
38924
  }
39259
38925
  },
38926
+ {
38927
+ "id": "himi_rsvp.confirm_sign_in",
38928
+ "body": {
38929
+ "package": "himalaya",
38930
+ "type": "Button",
38931
+ "props": {
38932
+ "title": "Confirm RSVP",
38933
+ "style": "primary",
38934
+ "fullWidth": true,
38935
+ "action": {
38936
+ "actionId": "himi.open",
38937
+ "params": {
38938
+ "screenId": "himi_sign_in",
38939
+ "mode": "signin",
38940
+ "intent": "rsvp"
38941
+ }
38942
+ }
38943
+ }
38944
+ },
38945
+ "modifiers": {
38946
+ "cornerRadius": 8,
38947
+ "pressFeedback": "scale",
38948
+ "haptic": "medium",
38949
+ "hiddenPath": "$service:himiEventsRsvp.hideGuestRsvp"
38950
+ }
38951
+ },
39260
38952
  {
39261
38953
  "id": "himi_rsvp.confirm",
39262
38954
  "body": {
@@ -160,6 +160,9 @@ function inputId(): string {
160
160
  /** The time a host opened this screen at (`himalaya.input.start`, ISO 8601), or "". Every load
161
161
  * preselects that slot (or opens on its day) until the member picks a day or a time themselves —
162
162
  * not just the first, because the boot load and the screen's own `load` can overlap. */
163
+ /** Counts the customer's picks. A load that sees it move ran while they picked a time. */
164
+ let selectionRevision = 0;
165
+
163
166
  let pendingStart = (() => {
164
167
  const start = (globalThis as { himalaya?: { input?: { start?: unknown } } }).himalaya?.input?.start;
165
168
  return typeof start === "string" && Number.isFinite(Date.parse(start)) ? start : "";
@@ -420,6 +423,48 @@ async function signedInEmail(): Promise<string> {
420
423
  }
421
424
  }
422
425
 
426
+ // A guest's picked time, kept for the sign-in it needs. Hosts key workers by account, so
427
+ // signing in can start a new worker for this screen, with no selection. `himalaya.storage`
428
+ // is shared by every worker of the app on every host, so a member's load of the same
429
+ // service with nothing selected takes the pick back. Reading never clears it: a new worker's
430
+ // boot load and appear load can run at once, and both must restore the same time. It goes
431
+ // when the member picks a time or books, or after 30 minutes.
432
+ const PENDING_PICK_KEY = "himi.booking.pendingPick.v1";
433
+ const PENDING_PICK_TTL_MS = 30 * 60 * 1000;
434
+ type StorageLike = { getItem(key: string): string | null; setItem(key: string, value: string): void };
435
+
436
+ function appStorage(): StorageLike | undefined {
437
+ return (globalThis as { himalaya?: { storage?: StorageLike } }).himalaya?.storage;
438
+ }
439
+
440
+ function writePick(value: string): void {
441
+ try {
442
+ appStorage()?.setItem(PENDING_PICK_KEY, value);
443
+ } catch {
444
+ // No storage on this host: the pick lives only as long as this worker.
445
+ }
446
+ }
447
+
448
+ function rememberPick(serviceId: string, slotId: string): void {
449
+ writePick(JSON.stringify({ serviceId, slotId, at: Date.now() }));
450
+ }
451
+
452
+ function clearPick(): void {
453
+ writePick("");
454
+ }
455
+
456
+ /** The slot a guest picked for `serviceId` in the last 30 minutes, or "". */
457
+ function readPick(serviceId: string): string {
458
+ let pick: { serviceId?: unknown; slotId?: unknown; at?: unknown } | null = null;
459
+ try {
460
+ pick = JSON.parse(appStorage()?.getItem(PENDING_PICK_KEY) || "null");
461
+ } catch {
462
+ // No storage, or a damaged entry: there is no pick to restore.
463
+ }
464
+ const fresh = typeof pick?.at === "number" && Date.now() - pick.at < PENDING_PICK_TTL_MS;
465
+ return fresh && pick?.serviceId === serviceId && typeof pick?.slotId === "string" ? pick.slotId : "";
466
+ }
467
+
423
468
  // Wix Bookings appDefId — the catalogReference.appId that wraps a booking as an
424
469
  // ecom checkout line item, and the payment appId the native cashier settles under.
425
470
  const WIX_BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
@@ -507,7 +552,8 @@ const svc = defineService<S, {
507
552
  bookingsScreenId: "himi_bookings",
508
553
  }),
509
554
  actions: {
510
- load: async (s, arg) => {
555
+ load: async (s, arg): Promise<S> => {
556
+ const revisionAtStart = selectionRevision;
511
557
  stashCopy(arg);
512
558
  if (!s.id) return derive({ ...s, status: "ready", errored: true });
513
559
  let next = derive({ ...s, status: "loading", errored: false, message: "", successMessage: "" });
@@ -544,22 +590,29 @@ const svc = defineService<S, {
544
590
  }
545
591
  allRows = rows;
546
592
  const days = daysOf(rows);
547
- // Keep the customer's day across a reload when it still has times;
548
- // otherwise open on the first day that has any.
593
+ // A reload runs in the background when the screen shows again. A time the customer
594
+ // picked while it ran wins over the selection this load started from.
595
+ const chosen: S = selectionRevision !== revisionAtStart ? (svc.getState?.() ?? next) : next;
549
596
  // A host's `start` (deep entry): that slot preselected, or — taken since — its day.
550
597
  const wantedStart = pendingStart;
551
598
  const startRow = wantedStart ? rows.find((r) => r.id === slotIdAt(slotsById, wantedStart)) : undefined;
552
599
  // The requested day even with no time left on it: "taken since" opens there, nothing selected.
553
600
  const startDay = wantedStart ? days.find((d) => d.id === dayKey(wantedStart)) : undefined;
554
- const keep = days.find((d) => d.id === next.selectedDay && d.hasSlots);
555
- const initialDay = startRow?.dayKey ?? startDay?.id ?? keep?.id ?? days.find((d) => d.hasSlots)?.id ?? days[0]?.id ?? "";
556
- const selectionAlive = !!startRow || slotsById.has(next.selectedSlotId);
601
+ const selectionAlive = !!startRow || slotsById.has(chosen.selectedSlotId);
602
+ // A member with no live selection, and no host start, gets back the time they picked as a guest.
603
+ const picked = selectionAlive || wantedStart || !next.isMember ? undefined : rows.find((row) => row.id === readPick(s.id));
604
+ // Keep the customer's day across a reload when it still has times;
605
+ // otherwise open on the first day that has any.
606
+ const keep = days.find((d) => d.id === chosen.selectedDay && d.hasSlots);
607
+ const initialDay = startRow?.dayKey ?? startDay?.id ?? picked?.dayKey ?? keep?.id ?? days.find((d) => d.hasSlots)?.id ?? days[0]?.id ?? "";
557
608
  const header = serviceHeaderFields(serviceRes.service);
558
609
  return derive(withDay({
559
610
  ...next,
560
611
  days,
561
- selectedSlotId: startRow ? startRow.id : selectionAlive ? next.selectedSlotId : "",
562
- selectedLine: startRow ? [startRow.day, startRow.title, startRow.subtitle].filter(Boolean).join(" · ") : selectionAlive ? next.selectedLine : "",
612
+ selectedSlotId: startRow ? startRow.id : selectionAlive ? chosen.selectedSlotId : picked?.id ?? "",
613
+ selectedLine: startRow ? [startRow.day, startRow.title, startRow.subtitle].filter(Boolean).join(" · ")
614
+ : selectionAlive ? chosen.selectedLine
615
+ : picked ? [picked.day, picked.title, picked.subtitle].filter(Boolean).join(" · ") : "",
563
616
  status: "ready",
564
617
  errored: false,
565
618
  serviceName: serviceRes.service?.name || copyParam("time.serviceNameFallback", "Choose a time"),
@@ -593,7 +646,12 @@ const svc = defineService<S, {
593
646
  ? arg
594
647
  : ((arg as { id?: string; slotId?: string })?.slotId ?? (arg as { id?: string })?.id ?? "");
595
648
  const selected = allRows.find((slot) => slot.id === slotId);
596
- if (selected) pendingStart = "";
649
+ if (selected) {
650
+ selectionRevision++;
651
+ pendingStart = "";
652
+ if (s.isMember) clearPick();
653
+ else rememberPick(s.id, slotId);
654
+ }
597
655
  return derive({
598
656
  ...s,
599
657
  selectedSlotId: selected ? slotId : s.selectedSlotId,
@@ -666,6 +724,8 @@ const svc = defineService<S, {
666
724
  start: hostEventInstant(booking?.startDate) || slot.startDate,
667
725
  end: hostEventInstant(booking?.endDate) || slot.endDate,
668
726
  });
727
+ // The member acts on the pick now; a later visit must not restore it.
728
+ clearPick();
669
729
  // PAID service — FULLY NATIVE in-app payment (mirrors Wix's RN apps): NO
670
730
  // hosted-checkout WebView / browser redirect. The chain is
671
731
  // createBooking → ecom.createCheckout(booking line item) → ecom.createOrder
@@ -35,6 +35,9 @@ interface S {
35
35
  noSuccess: boolean;
36
36
  canRsvp: boolean;
37
37
  cannotRsvp: boolean;
38
+ /** Hides a guest's RSVP button, which opens a sign-in screen: shown only for a guest with
39
+ * an event to RSVP to, as `cannotRsvp` requires an event for a member's. */
40
+ hideGuestRsvp: boolean;
38
41
  errored: boolean;
39
42
  notErrored: boolean;
40
43
  isLoading: boolean;
@@ -74,6 +77,7 @@ function derive(s: S): S {
74
77
  notMember: !s.isMember,
75
78
  canRsvp,
76
79
  cannotRsvp: !canRsvp,
80
+ hideGuestRsvp: s.isMember || !hasEvent,
77
81
  noSuccess: !hasSuccess,
78
82
  notErrored: !s.errored,
79
83
  noEmpty: s.status === "loading" || s.errored || hasEvent,
@@ -144,6 +148,7 @@ const svc = defineService<S, {
144
148
  noSuccess: true,
145
149
  canRsvp: false,
146
150
  cannotRsvp: true,
151
+ hideGuestRsvp: true,
147
152
  errored: false,
148
153
  notErrored: true,
149
154
  isLoading: true,
@@ -365,6 +365,11 @@ function authHeadline(s: S, isRegistering: boolean, isRecovering: boolean): { au
365
365
  authTitle: copyParam("storeAccount.authTitleRsvp", "Sign in to RSVP"),
366
366
  authBody: copyParam("storeAccount.authBodyRsvp", "Sign in to RSVP, then we’ll bring you back."),
367
367
  };
368
+ case "book":
369
+ return {
370
+ authTitle: copyParam("storeAccount.authTitleBook", "Sign in to book"),
371
+ authBody: copyParam("storeAccount.authBodyBook", "Sign in to book this time, then we’ll bring you back."),
372
+ };
368
373
  case "post":
369
374
  return {
370
375
  authTitle: copyParam("storeAccount.authTitlePost", "Sign in to post"),
@@ -392,7 +397,7 @@ interface S extends IapRestoreFields {
392
397
  noMemberImage: boolean;
393
398
  authTitle: string;
394
399
  authBody: string;
395
- /** Mid-flow gate intent (`join_group` | `rsvp` | `post` | ""). */
400
+ /** Mid-flow gate intent (`join_group` | `rsvp` | `book` | `post` | ""). */
396
401
  authIntent: string;
397
402
  authContextName: string;
398
403
  returnScreenId: string;
@@ -9,7 +9,7 @@ interface S {
9
9
  status: "loading" | "ready";
10
10
  orders: Row[];
11
11
  emptyText: string;
12
- /** When true, descriptor hides the guest-only "Go to Account" CTA (hiddenWhen). */
12
+ /** When true, descriptor hides the guest-only "Sign in" CTA (hiddenWhen). */
13
13
  hideAccountCta: boolean;
14
14
  }
15
15
 
@@ -19,7 +19,7 @@ function norm(o: WixPlanOrder): Row {
19
19
  }
20
20
 
21
21
  const EMPTY_MEMBER = "No subscriptions yet";
22
- const EMPTY_GUEST = "Sign in from Account to see subscriptions you’ve joined — we don’t invent sample memberships.";
22
+ const EMPTY_GUEST = "Sign in to see the subscriptions you’ve joined — we don’t invent sample memberships.";
23
23
  const EMPTY_FORBIDDEN = "No subscriptions for this member yet.";
24
24
 
25
25
  /** The signed-in member's email, or "" for anyone else. A visitor's identity is