@wix/himalaya-cli 0.1223.0 → 0.1224.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 (56) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/display.d.ts +3 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +3 -1
  3. package/guides/sdk-reference.json +1 -1
  4. package/package.json +2 -2
  5. package/templates/beauty-salon/dev/index.ts +5 -1
  6. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +15 -2
  7. package/templates/beauty-salon/tier5-src/wix-spaces-create-post/index.ts +14 -4
  8. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +15 -2
  9. package/templates/branded-lite/dev/index.ts +520 -274
  10. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +15 -2
  11. package/templates/branded-lite/tier5-src/wix-spaces-create-post/index.ts +14 -4
  12. package/templates/community-club/dev/index.ts +518 -272
  13. package/templates/community-club/tier5-src/_shared/collection-contract.ts +15 -2
  14. package/templates/community-club/tier5-src/wix-spaces-create-post/index.ts +14 -4
  15. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +15 -2
  16. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  17. package/templates/events-meetups/dev/index.ts +518 -272
  18. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +15 -2
  19. package/templates/events-meetups/tier5-src/wix-spaces-create-post/index.ts +14 -4
  20. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +15 -2
  21. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  22. package/templates/gym-fitness/dev/index.ts +520 -274
  23. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +15 -2
  24. package/templates/gym-fitness/tier5-src/wix-spaces-create-post/index.ts +14 -4
  25. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +15 -2
  26. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  27. package/templates/nonprofit-cause/dev/index.ts +518 -272
  28. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +15 -2
  29. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-post/index.ts +14 -4
  30. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +15 -2
  31. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  32. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +15 -2
  33. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +1 -0
  34. package/templates/podcast-creator/dev/index.ts +518 -272
  35. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +15 -2
  36. package/templates/podcast-creator/tier5-src/wix-spaces-create-post/index.ts +14 -4
  37. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +15 -2
  38. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  39. package/templates/portfolio-creative/dev/index.ts +520 -274
  40. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +15 -2
  41. package/templates/portfolio-creative/tier5-src/wix-spaces-create-post/index.ts +14 -4
  42. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +15 -2
  43. package/templates/restaurant/dev/index.ts +518 -272
  44. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +15 -2
  45. package/templates/restaurant/tier5-src/wix-spaces-create-post/index.ts +14 -4
  46. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +15 -2
  47. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  48. package/templates/retail-store/dev/index.ts +5 -1
  49. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +15 -2
  50. package/templates/retail-store/tier5-src/wix-spaces-create-post/index.ts +14 -4
  51. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +15 -2
  52. package/templates/yoga-studio/dev/index.ts +518 -272
  53. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +15 -2
  54. package/templates/yoga-studio/tier5-src/wix-spaces-create-post/index.ts +14 -4
  55. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +15 -2
  56. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
@@ -8,6 +8,9 @@ export interface EventRow {
8
8
  dateLine: string;
9
9
  /** "Past" once the event has finished, else "". */
10
10
  statusLabel: string;
11
+ /** Has the event finished? The SAME `eventIsPast` answer `statusLabel` reads — one call,
12
+ * both fields — so a card can never say "Past" and still offer Register. */
13
+ isPast: boolean;
11
14
  location: string;
12
15
  status: string;
13
16
  image: string;
@@ -222,6 +222,7 @@ function normalizeEventAt(e, now) {
222
222
  const start = startDateOf(e);
223
223
  const date = start ? new Date(start) : null;
224
224
  const image = eventImage(e);
225
+ const past = eventIsPast(e, now);
225
226
  return {
226
227
  id,
227
228
  title,
@@ -230,7 +231,8 @@ function normalizeEventAt(e, now) {
230
231
  dateLine: eventWhen(e, now),
231
232
  // "Past" for a finished event, "" otherwise — the shared collection row prints it in the
232
233
  // meta line, so an event from last month no longer reads as current.
233
- statusLabel: eventIsPast(e, now) ? "Past" : "",
234
+ statusLabel: past ? "Past" : "",
235
+ isPast: past,
234
236
  location: eventLocation(e),
235
237
  status: titleCase(e.eventStatus) || titleCase(e.status),
236
238
  image,
@@ -15665,7 +15665,7 @@
15665
15665
  "name": "EventRow",
15666
15666
  "kind": "interface",
15667
15667
  "jsdoc": "",
15668
- "decl": "export interface EventRow {\n id: string;\n title: string;\n day: string;\n month: string;\n dateLine: string;\n /** \"Past\" once the event has finished, else \"\". */\n statusLabel: string;\n location: string;\n status: string;\n image: string;\n hasImage: boolean;\n noImage: boolean;\n detailScreenId: string;\n}"
15668
+ "decl": "export interface EventRow {\n id: string;\n title: string;\n day: string;\n month: string;\n dateLine: string;\n /** \"Past\" once the event has finished, else \"\". */\n statusLabel: string;\n /** Has the event finished? The SAME `eventIsPast` answer `statusLabel` reads — one call,\n * both fields — so a card can never say \"Past\" and still offer Register. */\n isPast: boolean;\n location: string;\n status: string;\n image: string;\n hasImage: boolean;\n noImage: boolean;\n detailScreenId: string;\n}"
15669
15669
  },
15670
15670
  {
15671
15671
  "name": "titleCase",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1223.0",
3
+ "version": "0.1224.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "ce93359cc91d235e50b11e676527e9379dca8824663561b1740a3405"
70
+ "falconPackageHash": "6b76728b30d33797b71068367615815eb886dc05bb87adbcc4577a32"
71
71
  }
@@ -21370,6 +21370,7 @@ const SCREENS = runtimeSetting({
21370
21370
  "props": {
21371
21371
  "id": "title",
21372
21372
  "label": "Title",
21373
+ "required": true,
21373
21374
  "kind": "text",
21374
21375
  "text": {
21375
21376
  "placeholder": "Add a title",
@@ -21417,7 +21418,10 @@ const SCREENS = runtimeSetting({
21417
21418
  "style": "primary",
21418
21419
  "fullWidth": true,
21419
21420
  "action": {
21420
- "actionId": "tier5.spacesCreatePost.submit"
21421
+ "actionId": "tier5.spacesCreatePost.submit",
21422
+ "params": {
21423
+ "postScreenId": "himi_blog_post"
21424
+ }
21421
21425
  }
21422
21426
  }
21423
21427
  }
@@ -37,6 +37,13 @@ export interface CollectionConfig {
37
37
  * `screenId` opens that screen with the row id; without it the verb reuses the
38
38
  * row's own tap action (the detail screen that owns the verb). */
39
39
  primaryVerb?: { label: string; screenId?: string };
40
+ /** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
41
+ * DOMAIN sets once the record has finished — computed by the same rule as the row's own
42
+ * "Past" label, in the same place, so the two cannot disagree. A finished
43
+ * record's `primaryVerb` (Register, Book…) is transactional and would act on something
44
+ * that is over, so it is swapped for a non-transactional "View details" that opens the
45
+ * row's own detail. Without a row tap action the verb is simply hidden. */
46
+ endedWhen?: string;
40
47
  /** A catalog card whose one expected per-item action is "favorite".
41
48
  * The ellipsis overflow is replaced by an always-visible heart toggle wired to
42
49
  * the same `collectionSave` reducer. A dropdown `Menu` anchored inside a
@@ -57,7 +64,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
57
64
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
58
65
  defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
59
66
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
60
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
67
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
61
68
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
62
69
  himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
63
70
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
@@ -180,7 +187,13 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
180
187
  collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
181
188
  collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
182
189
  collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
183
- collectionNoIdentity: !collectionId(row) })),
190
+ collectionNoIdentity: !collectionId(row),
191
+ // Older cached EventRows predate `isPast` but already stored `statusLabel: "Past"`.
192
+ // Keep their ended state through an offline render until the row is refreshed.
193
+ ...(config.endedWhen ? (() => {
194
+ const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
195
+ return { collectionEnded: ended, collectionNotEnded: !ended };
196
+ })() : {}) })),
184
197
  filters, loaded: rows.length,
185
198
  };
186
199
  }
@@ -2,7 +2,7 @@
2
2
  // Holds the typed title + body; submit creates AND publishes a real blog post via
3
3
  // the Wix Blog API, then the new post shows up in ws_blog. (Forum has no member
4
4
  // create endpoint in the SDK; blog is the available authored-content write.)
5
- import { defineService } from "@himalaya/state";
5
+ import { defineService, navigate } from "@himalaya/state";
6
6
  import { createDraftPost, publishDraftPost } from "@himalaya/wix-blog";
7
7
  import { memberAccess } from "../_shared/member-access.js";
8
8
 
@@ -38,7 +38,7 @@ function val(arg?: unknown): string {
38
38
  return typeof arg === "string" ? arg : ((arg as { value?: string; text?: string })?.value ?? (arg as { text?: string })?.text ?? "");
39
39
  }
40
40
 
41
- const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S) => Promise<S> }>("spacesCreatePost", {
41
+ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?: unknown) => S; setBody: (s: S, a?: unknown) => S; focusBody: (s: S) => S; submit: (s: S, a?: unknown) => Promise<S> }>("spacesCreatePost", {
42
42
  state: { status: "ready", title: "", body: "", hint: "Share something with the community.", bodyFocus: "", needsSignIn: false, noSignIn: true, hideForm: true },
43
43
  actions: {
44
44
  load: async (s) => {
@@ -50,7 +50,11 @@ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?:
50
50
  // Title's Next key lands here. The keyboard stays up (that is the "next"
51
51
  // contract) and the body field takes focus.
52
52
  focusBody: (s) => ({ ...s, bodyFocus: bumpToken(s.bodyFocus) }),
53
- submit: async (s) => {
53
+ // `postScreenId` (a submit param) names the screen that shows ONE post, so a host
54
+ // with one lands the author on what they just posted. Several apps share this
55
+ // worker and each owns its own post screen, so the descriptor names it; without
56
+ // it the composer clears and confirms in place.
57
+ submit: async (s, a) => {
54
58
  if (s.needsSignIn) return { ...s, hint: "Sign in to post." };
55
59
  const title = s.title.trim();
56
60
  if (!title) return { ...s, hint: "Please enter a title." };
@@ -73,7 +77,13 @@ const svc = defineService<S, { load: (s: S) => Promise<S>; setTitle: (s: S, a?:
73
77
  // steals focus, so those transitions must clear it (see himi-store-account's
74
78
  // NO_FOCUS on its mode switches). Same token, opposite fix; the deciding
75
79
  // question is always whether the field stays mounted.
76
- return { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
80
+ const posted: S = { ...s, status: "ready", title: "", body: "", bodyFocus: s.bodyFocus, hint: `Posted “${title}” ✓ — it now appears in Blog.` };
81
+ // Only after the worker has the post's id — a failure takes the catch below and
82
+ // keeps every character the user typed.
83
+ const postScreenId = (a as { postScreenId?: unknown } | undefined)?.postScreenId;
84
+ return id && typeof postScreenId === "string" && postScreenId
85
+ ? navigate(posted, { screenId: postScreenId, args: { id } })
86
+ : posted;
77
87
  } catch {
78
88
  return { ...s, status: "ready", hint: "Couldn't post — check your connection and try again." };
79
89
  } finally {
@@ -37,6 +37,13 @@ export interface CollectionConfig {
37
37
  * `screenId` opens that screen with the row id; without it the verb reuses the
38
38
  * row's own tap action (the detail screen that owns the verb). */
39
39
  primaryVerb?: { label: string; screenId?: string };
40
+ /** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
41
+ * DOMAIN sets once the record has finished — computed by the same rule as the row's own
42
+ * "Past" label, in the same place, so the two cannot disagree. A finished
43
+ * record's `primaryVerb` (Register, Book…) is transactional and would act on something
44
+ * that is over, so it is swapped for a non-transactional "View details" that opens the
45
+ * row's own detail. Without a row tap action the verb is simply hidden. */
46
+ endedWhen?: string;
40
47
  /** A catalog card whose one expected per-item action is "favorite".
41
48
  * The ellipsis overflow is replaced by an always-visible heart toggle wired to
42
49
  * the same `collectionSave` reducer. A dropdown `Menu` anchored inside a
@@ -57,7 +64,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
57
64
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
58
65
  defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
59
66
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
60
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
67
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
61
68
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
62
69
  himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
63
70
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
@@ -180,7 +187,13 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
180
187
  collectionSubtitle: scalar(row[config.subtitle ?? '']) || scalar(row.subtitle) || scalar(row.description) || scalar(row.meta),
181
188
  collectionImage: scalar(row[config.image ?? '']), collectionNoImage: !scalar(row[config.image ?? '']), collectionHasImage: !!scalar(row[config.image ?? '']),
182
189
  collectionSaved: prefs.savedIds.includes(collectionId(row)), collectionNotSaved: !prefs.savedIds.includes(collectionId(row)), collectionSelected: prefs.selectedIds.includes(collectionId(row)),
183
- collectionNoIdentity: !collectionId(row) })),
190
+ collectionNoIdentity: !collectionId(row),
191
+ // Older cached EventRows predate `isPast` but already stored `statusLabel: "Past"`.
192
+ // Keep their ended state through an offline render until the row is refreshed.
193
+ ...(config.endedWhen ? (() => {
194
+ const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
195
+ return { collectionEnded: ended, collectionNotEnded: !ended };
196
+ })() : {}) })),
184
197
  filters, loaded: rows.length,
185
198
  };
186
199
  }