@wix/himalaya-cli 0.1151.0 → 0.1153.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 (41) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/extensions.d.ts +33 -2
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/wix-data/src/index.d.ts +14 -2
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-bookings.mjs +71 -5
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-data.mjs +3 -2
  5. package/dist/cli.mjs +141 -29
  6. package/dist/preview-assets.generated.bin +0 -0
  7. package/guides/cms-collections.md +4 -1
  8. package/guides/getting-started.md +3 -1
  9. package/guides/himi-skill.md +3 -1
  10. package/guides/owner-lite-site-binding.md +26 -0
  11. package/guides/push-cli-onboarding.md +42 -1
  12. package/guides/sdk-reference.json +23 -11
  13. package/guides/templates.md +1 -0
  14. package/guides/wix-api-usage.md +5 -1
  15. package/package.json +2 -2
  16. package/templates/beauty-salon/dev/index.ts +6 -3
  17. package/templates/community-club/dev/index.ts +6 -3
  18. package/templates/community-club-owner/dev/index.ts +44 -1
  19. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  20. package/templates/events-meetups/dev/index.ts +6 -3
  21. package/templates/events-meetups-owner/dev/index.ts +44 -1
  22. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  23. package/templates/gym-fitness/dev/index.ts +6 -3
  24. package/templates/gym-fitness-owner/dev/index.ts +44 -1
  25. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  26. package/templates/nonprofit-cause/dev/index.ts +6 -3
  27. package/templates/nonprofit-cause-owner/dev/index.ts +44 -1
  28. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  29. package/templates/owner-lite/dev/index.ts +44 -1
  30. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +52 -8
  31. package/templates/podcast-creator/dev/index.ts +6 -3
  32. package/templates/podcast-creator-owner/dev/index.ts +44 -1
  33. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  34. package/templates/portfolio-creative/dev/index.ts +6 -3
  35. package/templates/restaurant/dev/index.ts +6 -3
  36. package/templates/restaurant-owner/dev/index.ts +44 -1
  37. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
  38. package/templates/retail-store/dev/index.ts +7 -3
  39. package/templates/yoga-studio/dev/index.ts +6 -3
  40. package/templates/yoga-studio-owner/dev/index.ts +44 -1
  41. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +52 -8
@@ -82836,6 +82836,48 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
82836
82836
  }
82837
82837
  }
82838
82838
  },
82839
+ {
82840
+ "id": "event_body",
82841
+ "body": {
82842
+ "package": "himalaya",
82843
+ "type": "RichContent",
82844
+ "props": {
82845
+ "documentJsonPath": "$service:himiOwnerLiteEvent.bodyJson",
82846
+ "onLink": {
82847
+ "actionId": "tier5.himiOwnerLiteEvent.openLink"
82848
+ },
82849
+ "fallback": {
82850
+ "id": "",
82851
+ "body": {
82852
+ "package": "himalaya",
82853
+ "type": "Text",
82854
+ "props": {
82855
+ "text": "$service:himiOwnerLiteEvent.bodyText",
82856
+ "style": "body",
82857
+ "color": "textPrimary",
82858
+ "collapseWhenEmpty": true
82859
+ }
82860
+ }
82861
+ }
82862
+ }
82863
+ }
82864
+ },
82865
+ {
82866
+ "id": "event_body_text",
82867
+ "body": {
82868
+ "package": "himalaya",
82869
+ "type": "Text",
82870
+ "props": {
82871
+ "text": "$service:himiOwnerLiteEvent.bodyText",
82872
+ "style": "body",
82873
+ "color": "textPrimary",
82874
+ "collapseWhenEmpty": true
82875
+ }
82876
+ },
82877
+ "modifiers": {
82878
+ "hiddenPath": "$service:himiOwnerLiteEvent.hasBodyJson"
82879
+ }
82880
+ },
82839
82881
  {
82840
82882
  "id": "event_info_card",
82841
82883
  "body": {
@@ -83173,7 +83215,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
83173
83215
  "contentType": "application/javascript+ts-bundle",
83174
83216
  "declaredCapabilities": [
83175
83217
  "wix_api",
83176
- "local_database"
83218
+ "local_database",
83219
+ "open_url"
83177
83220
  ],
83178
83221
  "minBinaryVersion": "0.1.0",
83179
83222
  "bundleVersion": "0.0.1"
@@ -11,7 +11,9 @@ import { normalizeEvent, eventDescription } from "../_shared/himi-events.js";
11
11
  import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
12
12
  import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
13
13
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
14
- import { get as dbGet, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
14
+ import { openRichLink } from "../_shared/rich-content-link.js";
15
+ import { richBody } from "../_shared/rich-body.js";
16
+ import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
15
17
 
16
18
  // Offline-first (data-offline): the event DETAIL falls back to the last-synced
17
19
  // local `owner_events` row (put by the event form's write-behind) on a failed
@@ -20,6 +22,7 @@ import { get as dbGet, remove as dbRemove, enqueue as dbEnqueue, type DbEntity }
20
22
  // ENQUEUED for replay on reconnect. Every local-db call is wrapped so a build
21
23
  // without the `local_database` grant behaves exactly as before (online-only).
22
24
  const ENTITY = "owner_events";
25
+ async function cachePut(row: DbEntity): Promise<void> { try { await dbUpsert(ENTITY, row); } catch { /* capability not granted */ } }
23
26
  async function cacheDrop(id: string): Promise<void> { try { await dbRemove(ENTITY, id); } catch { /* capability not granted */ } }
24
27
  async function cacheGet(id: string): Promise<DbEntity | null> { try { return await dbGet(ENTITY, id); } catch { return null; } }
25
28
  async function queueWrite(actionId: string, params: Record<string, string>): Promise<void> { try { await dbEnqueue(actionId, params); } catch { /* capability not granted */ } }
@@ -33,6 +36,13 @@ interface EventState {
33
36
  title: string;
34
37
  subtitle: string;
35
38
  summary: string;
39
+ // Body ([rico] #3499) — RichContent binds bodyJson; the plain text shows when there is no document.
40
+ /** The v3 Ricos `description` (read with `fields=TEXTS`) as ONE document string; "" when there is none. */
41
+ bodyJson: string;
42
+ /** bodyJson !== "" — gates the plain text, because RichContent draws nothing for "". */
43
+ hasBodyJson: boolean;
44
+ /** The description's plain text (the document's own text, else the legacy description). */
45
+ bodyText: string;
36
46
  initial: string;
37
47
  image: string;
38
48
  dateLine: string;
@@ -55,6 +65,17 @@ interface EventState {
55
65
  }
56
66
 
57
67
  /* eslint-disable @typescript-eslint/no-explicit-any */
68
+ /** [rico] #3499 — keep the description's plain text with the event's cached row, so the offline detail shows it.
69
+ * Merged into the row the events list (or the form) wrote rather than replacing it; with no row yet, the
70
+ * loaded detail's own fields become one. */
71
+ async function cacheBodyText(s: EventState): Promise<void> {
72
+ if (s.status !== "ready" || !s.id) return;
73
+ const prev = await cacheGet(s.id);
74
+ await cachePut(prev ? { ...prev, bodyText: s.bodyText } : {
75
+ id: s.id, title: s.title, subtitle: s.subtitle, image: s.image, dateLine: s.dateLine, location: s.location,
76
+ statusLabel: s.statusLabel, registration: s.registration, bodyText: s.bodyText,
77
+ });
78
+ }
58
79
  function idFrom(arg: unknown, fallback = ""): string {
59
80
  return typeof arg === "string" ? arg : String((arg as any)?.id ?? fallback);
60
81
  }
@@ -97,6 +118,9 @@ function shape(s: Partial<EventState>): EventState {
97
118
  title: s.title ?? "",
98
119
  subtitle: s.subtitle ?? "",
99
120
  summary: s.summary ?? "",
121
+ bodyJson: s.bodyJson ?? "",
122
+ hasBodyJson: !!s.bodyJson,
123
+ bodyText: s.bodyText ?? "",
100
124
  initial: s.initial ?? "?",
101
125
  image,
102
126
  dateLine: s.dateLine ?? "—",
@@ -128,7 +152,9 @@ function demoDetail(s: EventState, id: string): EventState | null {
128
152
  status: "ready",
129
153
  title: d.title,
130
154
  subtitle: [d.dateLine, d.location].filter(Boolean).join(" · "),
131
- summary: d.description,
155
+ summary: "",
156
+ bodyJson: "",
157
+ bodyText: d.description,
132
158
  initial: (d.title[0] || "?").toUpperCase(),
133
159
  image: "",
134
160
  dateLine: d.dateLine,
@@ -151,7 +177,9 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
151
177
  status: "ready",
152
178
  title,
153
179
  subtitle: String(row.subtitle ?? [dateLine, location].filter((x) => x && x !== "—").join(" · ")),
154
- summary: String(row.summary ?? row.description ?? ""),
180
+ summary: "",
181
+ bodyJson: "",
182
+ bodyText: String(row.bodyText ?? row.summary ?? row.description ?? ""),
155
183
  initial: String(row.initial ?? (title[0] || "?")).slice(0, 1).toUpperCase(),
156
184
  image: String(row.image ?? ""),
157
185
  dateLine: dateLine || "—",
@@ -164,21 +192,27 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
164
192
  }
165
193
 
166
194
  async function refresh(prev: EventState, id: string): Promise<EventState> {
167
- const res = await getEvent(id);
195
+ // A bare read returns the base fieldset: DETAILS adds `shortDescription` (the teaser and the legacy body),
196
+ // TEXTS the Ricos `description`.
197
+ const res = await getEvent(id, { fields: ["DETAILS", "TEXTS"] });
168
198
  const ev = (res.event ?? res) as WixEvent;
169
199
  if (!ev || !(ev as any).id) {
170
200
  return shape({ ...prev, id, status: "error", title: "", error: copyParam("ownerEvent.errorNotFound", "event not found") });
171
201
  }
172
202
  const row = normalizeEvent(ev); // reuses date/location/status normalization
173
203
  const image = /^https?:\/\//.test(row.image) ? row.image : "";
174
- const desc = (eventDescription(ev) || "").trim();
204
+ const { bodyJson, bodyText } = richBody((ev as unknown as { description?: unknown }).description, (eventDescription(ev) || "").trim());
205
+ // The teaser stays above the body only when it says something the body does not.
206
+ const teaser = String((ev as any).shortDescription ?? "").trim();
175
207
  return shape({
176
208
  ...prev,
177
209
  id,
178
210
  status: "ready",
179
211
  title: row.title,
180
212
  subtitle: [row.dateLine, row.location].filter((x) => x && x !== "—").join(" · "),
181
- summary: desc,
213
+ summary: teaser && teaser !== bodyText.trim() ? teaser : "",
214
+ bodyJson,
215
+ bodyText,
182
216
  initial: (row.title[0] || "?").toUpperCase(),
183
217
  image,
184
218
  dateLine: row.dateLine || "—",
@@ -194,6 +228,7 @@ const svc = defineService<EventState, {
194
228
  load: (s: EventState, arg?: unknown) => Promise<EventState>;
195
229
  retry: (s: EventState) => Promise<EventState>;
196
230
  delete: (s: EventState) => Promise<EventState>;
231
+ openLink: (s: EventState, arg?: unknown) => Promise<EventState>;
197
232
  }>("himiOwnerLiteEvent", {
198
233
  state: shape({ status: "loading" }),
199
234
  actions: {
@@ -208,7 +243,9 @@ const svc = defineService<EventState, {
208
243
  return shape({ ...s, status: "error", error: "Sample item not found." });
209
244
  }
210
245
  try {
211
- return await refresh(shape({ ...s, id, status: "loading" }), id);
246
+ const loaded = await refresh(shape({ ...s, id, status: "loading" }), id);
247
+ await cacheBodyText(loaded);
248
+ return loaded;
212
249
  } catch (e) {
213
250
  // Offline: fall back to the last-synced local row before the demo/error paths.
214
251
  const cached = await cacheGet(id);
@@ -229,7 +266,9 @@ const svc = defineService<EventState, {
229
266
  return shape({ ...s, status: "error", error: "Sample item not found." });
230
267
  }
231
268
  try {
232
- return await refresh(shape({ ...s, status: "loading" }), s.id);
269
+ const loaded = await refresh(shape({ ...s, status: "loading" }), s.id);
270
+ await cacheBodyText(loaded);
271
+ return loaded;
233
272
  } catch (e) {
234
273
  const cached = await cacheGet(s.id);
235
274
  if (cached) return cachedDetail(s, cached);
@@ -256,6 +295,11 @@ const svc = defineService<EventState, {
256
295
  return shape({ ...s, actionMessage: ownerLoadError(e) });
257
296
  }
258
297
  },
298
+ // [rico] #3499 — a link tapped in the RichContent description.
299
+ openLink: async (s, arg) => shape({
300
+ ...s,
301
+ actionMessage: (await openRichLink(arg)) ? "" : copyParam("ownerEvent.linkOpenFailed", "This link could not be opened."),
302
+ }),
259
303
  },
260
304
  });
261
305
 
@@ -2016,7 +2016,7 @@ const SCREENS = runtimeSetting({
2016
2016
  "verticals": "[\"plans\",\"feed\",\"events\",\"blog\",\"podcasts\"]",
2017
2017
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Latest episode\",\"dataSource\":\"episodeFirst.section0\",\"emptyWhen\":[\"noLatestPost\"]},{\"id\":\"section_1\",\"label\":\"All episodes\",\"dataSource\":\"episodeFirst.section1\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_2\",\"label\":\"Discover 3\",\"dataSource\":\"episodeFirst.section2\"},{\"id\":\"section_3\",\"label\":\"Latest activity\",\"dataSource\":\"episodeFirst.section3\"}]",
2018
2018
  "ownerDefaultLayout": "episodeFirst",
2019
- "copy": "{\"home.allEpisodesCta\":\"Browse all episodes\",\"home.episodesTitle\":\"All episodes\",\"home.latestEpisodeCaption\":\"Latest episode\",\"home.listenCta\":\"Listen now\",\"home.listenersCaption\":\"Our listeners\",\"home.notesCta\":\"Read the show notes\",\"home.onboardingBody\":\"Catch the latest episode, read the show notes, meet other listeners and support the show — all in one place.\",\"home.supportSubtitle\":\"Join a membership and keep new episodes coming.\",\"home.supportTitle\":\"Support the show\"}"
2019
+ "copy": "{\"home.allEpisodesCta\":\"Browse all episodes\",\"home.episodesTitle\":\"All episodes\",\"home.latestEpisodeCaption\":\"Latest episode\",\"home.listenCta\":\"Listen now\",\"home.listenersCaption\":\"Our listeners\",\"home.notesCta\":\"Read the show notes\",\"home.onboardingBody\":\"Catch the latest episode, read the show notes, meet other listeners and support the show — all in one place.\",\"home.profileBody\":\"Sign in for RSVPs, memberships and saved posts.\",\"home.supportSubtitle\":\"Join a membership and keep new episodes coming.\",\"home.supportTitle\":\"Support the show\"}"
2020
2020
  }
2021
2021
  }
2022
2022
  }
@@ -2067,7 +2067,7 @@ const SCREENS = runtimeSetting({
2067
2067
  "package": "himalaya",
2068
2068
  "type": "Text",
2069
2069
  "props": {
2070
- "text": "Orders, bookings, RSVPs and saved content in one place.",
2070
+ "text": "RSVPs, memberships and saved content in one place.",
2071
2071
  "style": "body",
2072
2072
  "color": "textSecondary"
2073
2073
  }
@@ -3380,7 +3380,10 @@ const SCREENS = runtimeSetting({
3380
3380
  "bottom": 28
3381
3381
  },
3382
3382
  "onAppear": {
3383
- "actionId": "tier5.himiStoreAccount.refresh"
3383
+ "actionId": "tier5.himiStoreAccount.refresh",
3384
+ "params": {
3385
+ "copy": "{\"account.subtitle\":\"RSVPs, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — RSVPs and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — RSVPs and memberships stay with you.\"}"
3386
+ }
3384
3387
  }
3385
3388
  }
3386
3389
  },
@@ -76663,6 +76663,48 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
76663
76663
  }
76664
76664
  }
76665
76665
  },
76666
+ {
76667
+ "id": "event_body",
76668
+ "body": {
76669
+ "package": "himalaya",
76670
+ "type": "RichContent",
76671
+ "props": {
76672
+ "documentJsonPath": "$service:himiOwnerLiteEvent.bodyJson",
76673
+ "onLink": {
76674
+ "actionId": "tier5.himiOwnerLiteEvent.openLink"
76675
+ },
76676
+ "fallback": {
76677
+ "id": "",
76678
+ "body": {
76679
+ "package": "himalaya",
76680
+ "type": "Text",
76681
+ "props": {
76682
+ "text": "$service:himiOwnerLiteEvent.bodyText",
76683
+ "style": "body",
76684
+ "color": "textPrimary",
76685
+ "collapseWhenEmpty": true
76686
+ }
76687
+ }
76688
+ }
76689
+ }
76690
+ }
76691
+ },
76692
+ {
76693
+ "id": "event_body_text",
76694
+ "body": {
76695
+ "package": "himalaya",
76696
+ "type": "Text",
76697
+ "props": {
76698
+ "text": "$service:himiOwnerLiteEvent.bodyText",
76699
+ "style": "body",
76700
+ "color": "textPrimary",
76701
+ "collapseWhenEmpty": true
76702
+ }
76703
+ },
76704
+ "modifiers": {
76705
+ "hiddenPath": "$service:himiOwnerLiteEvent.hasBodyJson"
76706
+ }
76707
+ },
76666
76708
  {
76667
76709
  "id": "event_info_card",
76668
76710
  "body": {
@@ -77001,7 +77043,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
77001
77043
  "contentType": "application/javascript+ts-bundle",
77002
77044
  "declaredCapabilities": [
77003
77045
  "wix_api",
77004
- "local_database"
77046
+ "local_database",
77047
+ "open_url"
77005
77048
  ],
77006
77049
  "minBinaryVersion": "0.1.0",
77007
77050
  "bundleVersion": "0.0.1"
@@ -11,7 +11,9 @@ import { normalizeEvent, eventDescription } from "../_shared/himi-events.js";
11
11
  import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
12
12
  import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
13
13
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
14
- import { get as dbGet, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
14
+ import { openRichLink } from "../_shared/rich-content-link.js";
15
+ import { richBody } from "../_shared/rich-body.js";
16
+ import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
15
17
 
16
18
  // Offline-first (data-offline): the event DETAIL falls back to the last-synced
17
19
  // local `owner_events` row (put by the event form's write-behind) on a failed
@@ -20,6 +22,7 @@ import { get as dbGet, remove as dbRemove, enqueue as dbEnqueue, type DbEntity }
20
22
  // ENQUEUED for replay on reconnect. Every local-db call is wrapped so a build
21
23
  // without the `local_database` grant behaves exactly as before (online-only).
22
24
  const ENTITY = "owner_events";
25
+ async function cachePut(row: DbEntity): Promise<void> { try { await dbUpsert(ENTITY, row); } catch { /* capability not granted */ } }
23
26
  async function cacheDrop(id: string): Promise<void> { try { await dbRemove(ENTITY, id); } catch { /* capability not granted */ } }
24
27
  async function cacheGet(id: string): Promise<DbEntity | null> { try { return await dbGet(ENTITY, id); } catch { return null; } }
25
28
  async function queueWrite(actionId: string, params: Record<string, string>): Promise<void> { try { await dbEnqueue(actionId, params); } catch { /* capability not granted */ } }
@@ -33,6 +36,13 @@ interface EventState {
33
36
  title: string;
34
37
  subtitle: string;
35
38
  summary: string;
39
+ // Body ([rico] #3499) — RichContent binds bodyJson; the plain text shows when there is no document.
40
+ /** The v3 Ricos `description` (read with `fields=TEXTS`) as ONE document string; "" when there is none. */
41
+ bodyJson: string;
42
+ /** bodyJson !== "" — gates the plain text, because RichContent draws nothing for "". */
43
+ hasBodyJson: boolean;
44
+ /** The description's plain text (the document's own text, else the legacy description). */
45
+ bodyText: string;
36
46
  initial: string;
37
47
  image: string;
38
48
  dateLine: string;
@@ -55,6 +65,17 @@ interface EventState {
55
65
  }
56
66
 
57
67
  /* eslint-disable @typescript-eslint/no-explicit-any */
68
+ /** [rico] #3499 — keep the description's plain text with the event's cached row, so the offline detail shows it.
69
+ * Merged into the row the events list (or the form) wrote rather than replacing it; with no row yet, the
70
+ * loaded detail's own fields become one. */
71
+ async function cacheBodyText(s: EventState): Promise<void> {
72
+ if (s.status !== "ready" || !s.id) return;
73
+ const prev = await cacheGet(s.id);
74
+ await cachePut(prev ? { ...prev, bodyText: s.bodyText } : {
75
+ id: s.id, title: s.title, subtitle: s.subtitle, image: s.image, dateLine: s.dateLine, location: s.location,
76
+ statusLabel: s.statusLabel, registration: s.registration, bodyText: s.bodyText,
77
+ });
78
+ }
58
79
  function idFrom(arg: unknown, fallback = ""): string {
59
80
  return typeof arg === "string" ? arg : String((arg as any)?.id ?? fallback);
60
81
  }
@@ -97,6 +118,9 @@ function shape(s: Partial<EventState>): EventState {
97
118
  title: s.title ?? "",
98
119
  subtitle: s.subtitle ?? "",
99
120
  summary: s.summary ?? "",
121
+ bodyJson: s.bodyJson ?? "",
122
+ hasBodyJson: !!s.bodyJson,
123
+ bodyText: s.bodyText ?? "",
100
124
  initial: s.initial ?? "?",
101
125
  image,
102
126
  dateLine: s.dateLine ?? "—",
@@ -128,7 +152,9 @@ function demoDetail(s: EventState, id: string): EventState | null {
128
152
  status: "ready",
129
153
  title: d.title,
130
154
  subtitle: [d.dateLine, d.location].filter(Boolean).join(" · "),
131
- summary: d.description,
155
+ summary: "",
156
+ bodyJson: "",
157
+ bodyText: d.description,
132
158
  initial: (d.title[0] || "?").toUpperCase(),
133
159
  image: "",
134
160
  dateLine: d.dateLine,
@@ -151,7 +177,9 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
151
177
  status: "ready",
152
178
  title,
153
179
  subtitle: String(row.subtitle ?? [dateLine, location].filter((x) => x && x !== "—").join(" · ")),
154
- summary: String(row.summary ?? row.description ?? ""),
180
+ summary: "",
181
+ bodyJson: "",
182
+ bodyText: String(row.bodyText ?? row.summary ?? row.description ?? ""),
155
183
  initial: String(row.initial ?? (title[0] || "?")).slice(0, 1).toUpperCase(),
156
184
  image: String(row.image ?? ""),
157
185
  dateLine: dateLine || "—",
@@ -164,21 +192,27 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
164
192
  }
165
193
 
166
194
  async function refresh(prev: EventState, id: string): Promise<EventState> {
167
- const res = await getEvent(id);
195
+ // A bare read returns the base fieldset: DETAILS adds `shortDescription` (the teaser and the legacy body),
196
+ // TEXTS the Ricos `description`.
197
+ const res = await getEvent(id, { fields: ["DETAILS", "TEXTS"] });
168
198
  const ev = (res.event ?? res) as WixEvent;
169
199
  if (!ev || !(ev as any).id) {
170
200
  return shape({ ...prev, id, status: "error", title: "", error: copyParam("ownerEvent.errorNotFound", "event not found") });
171
201
  }
172
202
  const row = normalizeEvent(ev); // reuses date/location/status normalization
173
203
  const image = /^https?:\/\//.test(row.image) ? row.image : "";
174
- const desc = (eventDescription(ev) || "").trim();
204
+ const { bodyJson, bodyText } = richBody((ev as unknown as { description?: unknown }).description, (eventDescription(ev) || "").trim());
205
+ // The teaser stays above the body only when it says something the body does not.
206
+ const teaser = String((ev as any).shortDescription ?? "").trim();
175
207
  return shape({
176
208
  ...prev,
177
209
  id,
178
210
  status: "ready",
179
211
  title: row.title,
180
212
  subtitle: [row.dateLine, row.location].filter((x) => x && x !== "—").join(" · "),
181
- summary: desc,
213
+ summary: teaser && teaser !== bodyText.trim() ? teaser : "",
214
+ bodyJson,
215
+ bodyText,
182
216
  initial: (row.title[0] || "?").toUpperCase(),
183
217
  image,
184
218
  dateLine: row.dateLine || "—",
@@ -194,6 +228,7 @@ const svc = defineService<EventState, {
194
228
  load: (s: EventState, arg?: unknown) => Promise<EventState>;
195
229
  retry: (s: EventState) => Promise<EventState>;
196
230
  delete: (s: EventState) => Promise<EventState>;
231
+ openLink: (s: EventState, arg?: unknown) => Promise<EventState>;
197
232
  }>("himiOwnerLiteEvent", {
198
233
  state: shape({ status: "loading" }),
199
234
  actions: {
@@ -208,7 +243,9 @@ const svc = defineService<EventState, {
208
243
  return shape({ ...s, status: "error", error: "Sample item not found." });
209
244
  }
210
245
  try {
211
- return await refresh(shape({ ...s, id, status: "loading" }), id);
246
+ const loaded = await refresh(shape({ ...s, id, status: "loading" }), id);
247
+ await cacheBodyText(loaded);
248
+ return loaded;
212
249
  } catch (e) {
213
250
  // Offline: fall back to the last-synced local row before the demo/error paths.
214
251
  const cached = await cacheGet(id);
@@ -229,7 +266,9 @@ const svc = defineService<EventState, {
229
266
  return shape({ ...s, status: "error", error: "Sample item not found." });
230
267
  }
231
268
  try {
232
- return await refresh(shape({ ...s, status: "loading" }), s.id);
269
+ const loaded = await refresh(shape({ ...s, status: "loading" }), s.id);
270
+ await cacheBodyText(loaded);
271
+ return loaded;
233
272
  } catch (e) {
234
273
  const cached = await cacheGet(s.id);
235
274
  if (cached) return cachedDetail(s, cached);
@@ -256,6 +295,11 @@ const svc = defineService<EventState, {
256
295
  return shape({ ...s, actionMessage: ownerLoadError(e) });
257
296
  }
258
297
  },
298
+ // [rico] #3499 — a link tapped in the RichContent description.
299
+ openLink: async (s, arg) => shape({
300
+ ...s,
301
+ actionMessage: (await openRichLink(arg)) ? "" : copyParam("ownerEvent.linkOpenFailed", "This link could not be opened."),
302
+ }),
259
303
  },
260
304
  });
261
305
 
@@ -1886,7 +1886,7 @@ const SCREENS = runtimeSetting({
1886
1886
  "verticals": "[\"store\",\"portfolio\",\"events\",\"blog\"]",
1887
1887
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"Selected work\",\"dataSource\":\"showcaseFirst.section0\",\"emptyWhen\":[\"noPostCards\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"showcaseFirst.section1\"},{\"id\":\"section_2\",\"label\":\"Prints & editions\",\"dataSource\":\"showcaseFirst.section2\",\"emptyWhen\":[\"noFeaturedProducts\"]},{\"id\":\"section_3\",\"label\":\"Exhibitions & fairs\",\"dataSource\":\"showcaseFirst.section3\"}]",
1888
1888
  "ownerDefaultLayout": "showcaseFirst",
1889
- "copy": "{\"home.aboutTitle\":\"About the studio\",\"home.contactCta\":\"Get in touch\",\"home.exhibitionsTitle\":\"Exhibitions & fairs\",\"home.onboardingBody\":\"Browse the latest work, pick up a print, and catch the next exhibition — all in one place.\",\"home.printsTitle\":\"Prints & editions\",\"home.viewWorkCta\":\"View all work\",\"home.workSubtitle\":\"The latest pieces, straight from the studio.\",\"home.workTitle\":\"Selected work\"}"
1889
+ "copy": "{\"home.aboutTitle\":\"About the studio\",\"home.contactCta\":\"Get in touch\",\"home.exhibitionsTitle\":\"Exhibitions & fairs\",\"home.onboardingBody\":\"Browse the latest work, pick up a print, and catch the next exhibition — all in one place.\",\"home.printsTitle\":\"Prints & editions\",\"home.profileBody\":\"Sign in for orders, RSVPs and saved posts.\",\"home.viewWorkCta\":\"View all work\",\"home.workSubtitle\":\"The latest pieces, straight from the studio.\",\"home.workTitle\":\"Selected work\"}"
1890
1890
  }
1891
1891
  }
1892
1892
  }
@@ -1937,7 +1937,7 @@ const SCREENS = runtimeSetting({
1937
1937
  "package": "himalaya",
1938
1938
  "type": "Text",
1939
1939
  "props": {
1940
- "text": "Orders, bookings, RSVPs and saved content in one place.",
1940
+ "text": "Orders, RSVPs and saved content in one place.",
1941
1941
  "style": "body",
1942
1942
  "color": "textSecondary"
1943
1943
  }
@@ -3249,7 +3249,10 @@ const SCREENS = runtimeSetting({
3249
3249
  "bottom": 28
3250
3250
  },
3251
3251
  "onAppear": {
3252
- "actionId": "tier5.himiStoreAccount.refresh"
3252
+ "actionId": "tier5.himiStoreAccount.refresh",
3253
+ "params": {
3254
+ "copy": "{\"account.subtitle\":\"Orders, RSVPs and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart and RSVPs stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart and RSVPs stay with you.\"}"
3255
+ }
3253
3256
  }
3254
3257
  }
3255
3258
  },
@@ -1942,7 +1942,7 @@ const SCREENS = runtimeSetting({
1942
1942
  "verticals": "[\"restaurants\",\"events\",\"blog\"]",
1943
1943
  "homeModules": "[{\"id\":\"saved\",\"label\":\"Saved on this device\",\"dataSource\":\"device.saved_items\",\"emptyWhen\":[\"noSavedContent\"]},{\"id\":\"section_0\",\"label\":\"__HIMI_APP_NAME__\",\"dataSource\":\"menuFirst.section0\",\"emptyWhen\":[\"hasDishHeroImage\"]},{\"id\":\"section_1\",\"label\":\"Discover 2\",\"dataSource\":\"menuFirst.section1\",\"emptyWhen\":[\"noMenuCategories\"]},{\"id\":\"section_2\",\"label\":\"Popular dishes\",\"dataSource\":\"menuFirst.section2\",\"emptyWhen\":[\"noPopularDishes\"]},{\"id\":\"section_3\",\"label\":\"Find us\",\"dataSource\":\"menuFirst.section3\"}]",
1944
1944
  "ownerDefaultLayout": "menuFirst",
1945
- "copy": "{\"home.eventsTitle\":\"Wine nights & events\",\"home.findUsTitle\":\"Find us\",\"home.hoursTitle\":\"Opening hours\",\"home.onboardingBody\":\"Browse the menu, reserve a table, and keep up with wine nights and kitchen news — all in one place.\",\"home.popularSubtitle\":\"The plates everyone keeps coming back for.\",\"home.popularTitle\":\"Popular dishes\",\"home.reserveCta\":\"Reserve a table\"}"
1945
+ "copy": "{\"home.eventsTitle\":\"Wine nights & events\",\"home.findUsTitle\":\"Find us\",\"home.hoursTitle\":\"Opening hours\",\"home.onboardingBody\":\"Browse the menu, reserve a table, and keep up with wine nights and kitchen news — all in one place.\",\"home.popularSubtitle\":\"The plates everyone keeps coming back for.\",\"home.popularTitle\":\"Popular dishes\",\"home.profileBody\":\"Sign in for reservations, RSVPs and saved posts.\",\"home.reserveCta\":\"Reserve a table\"}"
1946
1946
  }
1947
1947
  }
1948
1948
  }
@@ -1993,7 +1993,7 @@ const SCREENS = runtimeSetting({
1993
1993
  "package": "himalaya",
1994
1994
  "type": "Text",
1995
1995
  "props": {
1996
- "text": "Orders, bookings, RSVPs and saved content in one place.",
1996
+ "text": "Reservations, RSVPs and saved content in one place.",
1997
1997
  "style": "body",
1998
1998
  "color": "textSecondary"
1999
1999
  }
@@ -3217,7 +3217,10 @@ const SCREENS = runtimeSetting({
3217
3217
  "bottom": 28
3218
3218
  },
3219
3219
  "onAppear": {
3220
- "actionId": "tier5.himiStoreAccount.refresh"
3220
+ "actionId": "tier5.himiStoreAccount.refresh",
3221
+ "params": {
3222
+ "copy": "{\"account.subtitle\":\"Reservations, RSVPs and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — reservations and RSVPs stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — reservations and RSVPs stay with you.\"}"
3223
+ }
3221
3224
  }
3222
3225
  }
3223
3226
  },
@@ -81263,6 +81263,48 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
81263
81263
  }
81264
81264
  }
81265
81265
  },
81266
+ {
81267
+ "id": "event_body",
81268
+ "body": {
81269
+ "package": "himalaya",
81270
+ "type": "RichContent",
81271
+ "props": {
81272
+ "documentJsonPath": "$service:himiOwnerLiteEvent.bodyJson",
81273
+ "onLink": {
81274
+ "actionId": "tier5.himiOwnerLiteEvent.openLink"
81275
+ },
81276
+ "fallback": {
81277
+ "id": "",
81278
+ "body": {
81279
+ "package": "himalaya",
81280
+ "type": "Text",
81281
+ "props": {
81282
+ "text": "$service:himiOwnerLiteEvent.bodyText",
81283
+ "style": "body",
81284
+ "color": "textPrimary",
81285
+ "collapseWhenEmpty": true
81286
+ }
81287
+ }
81288
+ }
81289
+ }
81290
+ }
81291
+ },
81292
+ {
81293
+ "id": "event_body_text",
81294
+ "body": {
81295
+ "package": "himalaya",
81296
+ "type": "Text",
81297
+ "props": {
81298
+ "text": "$service:himiOwnerLiteEvent.bodyText",
81299
+ "style": "body",
81300
+ "color": "textPrimary",
81301
+ "collapseWhenEmpty": true
81302
+ }
81303
+ },
81304
+ "modifiers": {
81305
+ "hiddenPath": "$service:himiOwnerLiteEvent.hasBodyJson"
81306
+ }
81307
+ },
81266
81308
  {
81267
81309
  "id": "event_info_card",
81268
81310
  "body": {
@@ -81601,7 +81643,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
81601
81643
  "contentType": "application/javascript+ts-bundle",
81602
81644
  "declaredCapabilities": [
81603
81645
  "wix_api",
81604
- "local_database"
81646
+ "local_database",
81647
+ "open_url"
81605
81648
  ],
81606
81649
  "minBinaryVersion": "0.1.0",
81607
81650
  "bundleVersion": "0.0.1"