@wix/himalaya-cli 0.1614.0 → 0.1615.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 (106) hide show
  1. package/dist/cli.mjs +1 -0
  2. package/package.json +2 -2
  3. package/template-icons/salon-spa.png +0 -0
  4. package/templates/beauty-salon/tier5-src/_shared/branded-load-state.ts +5 -2
  5. package/templates/beauty-salon/tier5-src/_shared/himi-account.ts +31 -0
  6. package/templates/beauty-salon/tier5-src/_shared/himi-booking-saved.ts +88 -0
  7. package/templates/beauty-salon/tier5-src/himi-booking-service/index.ts +50 -3
  8. package/templates/beauty-salon/tier5-src/himi-booking-services/index.ts +70 -3
  9. package/templates/beauty-salon/tier5-src/himi-booking-time/index.ts +40 -8
  10. package/templates/beauty-salon/tier5-src/himi-branded-lite-agenda/index.ts +7 -2
  11. package/templates/branded-lite/tier5-src/_shared/branded-load-state.ts +5 -2
  12. package/templates/branded-lite/tier5-src/_shared/himi-account.ts +31 -0
  13. package/templates/branded-lite/tier5-src/_shared/himi-booking-saved.ts +88 -0
  14. package/templates/branded-lite/tier5-src/himi-booking-service/index.ts +50 -3
  15. package/templates/branded-lite/tier5-src/himi-booking-services/index.ts +70 -3
  16. package/templates/branded-lite/tier5-src/himi-booking-time/index.ts +40 -8
  17. package/templates/branded-lite/tier5-src/himi-branded-lite-agenda/index.ts +7 -2
  18. package/templates/branded-lite/tier5-src/himi-branded-lite-galleries/index.ts +104 -12
  19. package/templates/community-club/tier5-src/_shared/branded-load-state.ts +5 -2
  20. package/templates/community-club/tier5-src/_shared/himi-account.ts +31 -0
  21. package/templates/events-meetups/tier5-src/_shared/branded-load-state.ts +5 -2
  22. package/templates/events-meetups/tier5-src/_shared/himi-account.ts +31 -0
  23. package/templates/gym-fitness/tier5-src/_shared/branded-load-state.ts +5 -2
  24. package/templates/gym-fitness/tier5-src/_shared/himi-account.ts +31 -0
  25. package/templates/gym-fitness/tier5-src/_shared/himi-booking-saved.ts +88 -0
  26. package/templates/gym-fitness/tier5-src/himi-booking-service/index.ts +50 -3
  27. package/templates/gym-fitness/tier5-src/himi-booking-services/index.ts +70 -3
  28. package/templates/gym-fitness/tier5-src/himi-booking-time/index.ts +40 -8
  29. package/templates/gym-fitness/tier5-src/himi-branded-lite-agenda/index.ts +7 -2
  30. package/templates/nonprofit-cause/tier5-src/_shared/branded-load-state.ts +5 -2
  31. package/templates/nonprofit-cause/tier5-src/_shared/himi-account.ts +31 -0
  32. package/templates/podcast-creator/tier5-src/_shared/branded-load-state.ts +5 -2
  33. package/templates/podcast-creator/tier5-src/_shared/himi-account.ts +31 -0
  34. package/templates/portfolio-creative/tier5-src/_shared/branded-load-state.ts +5 -2
  35. package/templates/portfolio-creative/tier5-src/_shared/himi-account.ts +31 -0
  36. package/templates/restaurant/tier5-src/_shared/branded-load-state.ts +5 -2
  37. package/templates/restaurant/tier5-src/_shared/himi-account.ts +31 -0
  38. package/templates/retail-store/tier5-src/_shared/branded-load-state.ts +5 -2
  39. package/templates/retail-store/tier5-src/_shared/himi-account.ts +31 -0
  40. package/templates/salon-spa/README.md +53 -0
  41. package/templates/salon-spa/config.ts +272 -0
  42. package/templates/salon-spa/dev/index.ts +19893 -0
  43. package/templates/salon-spa/himalaya.content.json +49 -0
  44. package/templates/salon-spa/himalaya.template.json +14 -0
  45. package/templates/salon-spa/icon.png +0 -0
  46. package/templates/salon-spa/platforms.json +1 -0
  47. package/templates/salon-spa/tier5-src/_shared/app-locale.ts +61 -0
  48. package/templates/salon-spa/tier5-src/_shared/booking-location.ts +147 -0
  49. package/templates/salon-spa/tier5-src/_shared/booking-session.ts +222 -0
  50. package/templates/salon-spa/tier5-src/_shared/branded-lite-hosted.ts +278 -0
  51. package/templates/salon-spa/tier5-src/_shared/branded-load-state.ts +109 -0
  52. package/templates/salon-spa/tier5-src/_shared/business-chat-copy.ts +40 -0
  53. package/templates/salon-spa/tier5-src/_shared/business-chat-history.ts +48 -0
  54. package/templates/salon-spa/tier5-src/_shared/business-chat-message-model.ts +177 -0
  55. package/templates/salon-spa/tier5-src/_shared/business-chat-model.ts +56 -0
  56. package/templates/salon-spa/tier5-src/_shared/business-chat-presenter.ts +581 -0
  57. package/templates/salon-spa/tier5-src/_shared/business-chat-transport.ts +148 -0
  58. package/templates/salon-spa/tier5-src/_shared/collection-contract.ts +230 -0
  59. package/templates/salon-spa/tier5-src/_shared/collection-query.ts +193 -0
  60. package/templates/salon-spa/tier5-src/_shared/collection-service.ts +400 -0
  61. package/templates/salon-spa/tier5-src/_shared/detail-presence.ts +51 -0
  62. package/templates/salon-spa/tier5-src/_shared/empty-flags.ts +33 -0
  63. package/templates/salon-spa/tier5-src/_shared/engine-intl.ts +508 -0
  64. package/templates/salon-spa/tier5-src/_shared/himi-account.ts +164 -0
  65. package/templates/salon-spa/tier5-src/_shared/himi-booking-saved.ts +88 -0
  66. package/templates/salon-spa/tier5-src/_shared/himi-bookings.ts +318 -0
  67. package/templates/salon-spa/tier5-src/_shared/himi-copy.ts +89 -0
  68. package/templates/salon-spa/tier5-src/_shared/himi-store.ts +7 -0
  69. package/templates/salon-spa/tier5-src/_shared/himi-text.ts +24 -0
  70. package/templates/salon-spa/tier5-src/_shared/host-events.ts +66 -0
  71. package/templates/salon-spa/tier5-src/_shared/hosted-checkout.ts +70 -0
  72. package/templates/salon-spa/tier5-src/_shared/iap-attendance-form.ts +106 -0
  73. package/templates/salon-spa/tier5-src/_shared/iap-attendance-purchase.ts +211 -0
  74. package/templates/salon-spa/tier5-src/_shared/iap-attendance.ts +38 -0
  75. package/templates/salon-spa/tier5-src/_shared/iap-worker.ts +125 -0
  76. package/templates/salon-spa/tier5-src/_shared/local-preferences.ts +38 -0
  77. package/templates/salon-spa/tier5-src/_shared/member-access.ts +52 -0
  78. package/templates/salon-spa/tier5-src/_shared/member-account-routes.ts +52 -0
  79. package/templates/salon-spa/tier5-src/_shared/member-bookings.ts +94 -0
  80. package/templates/salon-spa/tier5-src/_shared/offset-page.ts +10 -0
  81. package/templates/salon-spa/tier5-src/_shared/online-actions.ts +76 -0
  82. package/templates/salon-spa/tier5-src/_shared/owner-load-error.ts +348 -0
  83. package/templates/salon-spa/tier5-src/_shared/payment-setup.ts +6 -0
  84. package/templates/salon-spa/tier5-src/_shared/rich-body.ts +78 -0
  85. package/templates/salon-spa/tier5-src/_shared/rich-content-link.ts +34 -0
  86. package/templates/salon-spa/tier5-src/_shared/weekly-goal.ts +107 -0
  87. package/templates/salon-spa/tier5-src/_shared/wix-service-price.ts +146 -0
  88. package/templates/salon-spa/tier5-src/business-chat/index.ts +669 -0
  89. package/templates/salon-spa/tier5-src/himi-booking-account/index.ts +241 -0
  90. package/templates/salon-spa/tier5-src/himi-booking-bookings/index.ts +466 -0
  91. package/templates/salon-spa/tier5-src/himi-booking-saved/index.ts +103 -0
  92. package/templates/salon-spa/tier5-src/himi-booking-service/index.ts +1170 -0
  93. package/templates/salon-spa/tier5-src/himi-booking-services/index.ts +791 -0
  94. package/templates/salon-spa/tier5-src/himi-booking-time/index.ts +1712 -0
  95. package/templates/salon-spa/tier5-src/himi-branded-lite-agenda/index.ts +950 -0
  96. package/templates/salon-spa/tier5-src/himi-branded-lite-galleries/index.ts +282 -0
  97. package/templates/salon-spa/tier5-src/himi-salon-home/index.ts +544 -0
  98. package/templates/salon-spa/tier5-src/himi-store-account/index.ts +925 -0
  99. package/templates/salon-spa/tokens.json +341 -0
  100. package/templates/yoga-studio/tier5-src/_shared/branded-load-state.ts +5 -2
  101. package/templates/yoga-studio/tier5-src/_shared/himi-account.ts +31 -0
  102. package/templates/yoga-studio/tier5-src/_shared/himi-booking-saved.ts +88 -0
  103. package/templates/yoga-studio/tier5-src/himi-booking-service/index.ts +50 -3
  104. package/templates/yoga-studio/tier5-src/himi-booking-services/index.ts +70 -3
  105. package/templates/yoga-studio/tier5-src/himi-booking-time/index.ts +40 -8
  106. package/templates/yoga-studio/tier5-src/himi-branded-lite-agenda/index.ts +7 -2
@@ -0,0 +1,241 @@
1
+ // Tier-5 bundle: himi-booking — visitor/member account state.
2
+ import { defineService } from "@himalaya/state";
3
+ import { dispatch } from "@himalaya/dispatch";
4
+ import { queryBookings, type WixBooking } from "@himalaya/wix-bookings";
5
+ import { loadMemberProfile, strOf, fmtDateTime, titleCase, type AccountActivityRow } from "../_shared/himi-account";
6
+ import { stashCopy, copyParam } from "../_shared/himi-copy.js";
7
+
8
+ interface S {
9
+ status: "loading" | "ready";
10
+ isMember: boolean;
11
+ notMember: boolean;
12
+ identityLine: string;
13
+ detailLine: string;
14
+ memberName: string;
15
+ memberEmail: string;
16
+ memberImage: string;
17
+ memberInitials: string;
18
+ hasMemberImage: boolean;
19
+ noMemberImage: boolean;
20
+ authTitle: string;
21
+ authBody: string;
22
+ email: string;
23
+ password: string;
24
+ firstName: string;
25
+ lastName: string;
26
+ message: string;
27
+ isLoading: boolean;
28
+ notLoading: boolean;
29
+ isRegistering: boolean;
30
+ notRegistering: boolean;
31
+ activityTitle: string;
32
+ activitySummary: string;
33
+ activityRows: AccountActivityRow[];
34
+ activityEmptyText: string;
35
+ hasActivity: boolean;
36
+ noActivity: boolean;
37
+ servicesScreenId: string;
38
+ bookingsScreenId: string;
39
+ }
40
+
41
+ function derive(s: S): S {
42
+ const isRegistering = !!s.isRegistering;
43
+ const hasActivity = s.activityRows.length > 0;
44
+ return {
45
+ ...s,
46
+ isLoading: s.status === "loading",
47
+ notLoading: s.status !== "loading",
48
+ notMember: !s.isMember,
49
+ isRegistering,
50
+ notRegistering: !isRegistering,
51
+ hasActivity,
52
+ noActivity: !hasActivity,
53
+ authTitle: isRegistering
54
+ ? copyParam("bookingAccount.authTitleRegister", "Create account")
55
+ : copyParam("bookingAccount.authTitle", "Welcome"),
56
+ authBody: isRegistering
57
+ ? copyParam("bookingAccount.authBodyRegister", "Create a member account to book services and see your history.")
58
+ : copyParam("bookingAccount.authBody", "Sign in to book services and see your history."),
59
+ };
60
+ }
61
+
62
+ function bookingEmail(booking: WixBooking): string {
63
+ const contact = booking.contactDetails as { email?: string; contactEmail?: string } | undefined;
64
+ return (contact?.email ?? contact?.contactEmail ?? "").toLowerCase();
65
+ }
66
+
67
+ function bookingTitle(booking: WixBooking): string {
68
+ const entity = booking.bookedEntity as { serviceName?: string; title?: string } | undefined;
69
+ return entity?.serviceName || entity?.title || "Booking";
70
+ }
71
+
72
+ function bookingStart(booking: WixBooking): string | undefined {
73
+ const entity = booking.bookedEntity as { slot?: { startDate?: string } } | undefined;
74
+ return entity?.slot?.startDate || booking.startDate || booking.createdDate;
75
+ }
76
+
77
+ function bookingRow(booking: WixBooking): AccountActivityRow {
78
+ const start = bookingStart(booking);
79
+ return {
80
+ id: booking.id || start || bookingTitle(booking),
81
+ title: bookingTitle(booking),
82
+ subtitle: fmtDateTime(start) || "Booked time",
83
+ meta: titleCase(booking.paymentStatus) || "",
84
+ status: titleCase(booking.status) || "Booked",
85
+ };
86
+ }
87
+
88
+ async function bookingRows(memberEmail: string): Promise<AccountActivityRow[]> {
89
+ if (!memberEmail) return [];
90
+ const res = await queryBookings({ query: { paging: { limit: 50 } } });
91
+ return (res.bookings ?? [])
92
+ .filter((booking) => bookingEmail(booking) === memberEmail.toLowerCase())
93
+ .sort((a, b) => (Date.parse(bookingStart(b) || "") || 0) - (Date.parse(bookingStart(a) || "") || 0))
94
+ .slice(0, 3)
95
+ .map(bookingRow);
96
+ }
97
+
98
+ async function probe(s: S): Promise<S> {
99
+ const profile = await loadMemberProfile(copyParam("bookingAccount.guestDetail", "Sign in to book services and see your booking history."));
100
+ let activityRows: AccountActivityRow[] = [];
101
+ if (profile.isMember) {
102
+ try {
103
+ activityRows = await bookingRows(profile.memberEmail);
104
+ } catch {
105
+ activityRows = [];
106
+ }
107
+ }
108
+ return derive({
109
+ ...s,
110
+ status: "ready",
111
+ isMember: profile.isMember,
112
+ identityLine: profile.identityLine,
113
+ detailLine: profile.detailLine,
114
+ memberName: profile.memberName,
115
+ memberEmail: profile.memberEmail,
116
+ memberImage: profile.memberImage,
117
+ memberInitials: profile.memberInitials,
118
+ hasMemberImage: profile.hasMemberImage,
119
+ noMemberImage: profile.noMemberImage,
120
+ email: profile.memberEmail || s.email,
121
+ message: "",
122
+ activityRows,
123
+ activityTitle: copyParam("bookingAccount.activityTitle", "Recent bookings"),
124
+ activitySummary: activityRows.length
125
+ ? `${activityRows.length} recent ${activityRows.length === 1 ? "booking" : "bookings"}`
126
+ : copyParam("bookingAccount.activityHint", "Bookings are loaded from Wix Bookings."),
127
+ activityEmptyText: profile.isMember
128
+ ? copyParam("bookingAccount.activityEmptyMember", "No bookings for this member yet.")
129
+ : copyParam("bookingAccount.activityEmptyGuest", "Sign in to see bookings."),
130
+ });
131
+ }
132
+
133
+ function optimisticMember(s: S, email: string): S {
134
+ return derive({
135
+ ...s,
136
+ status: "ready",
137
+ isMember: true,
138
+ identityLine: copyParam("bookingAccount.signedIn", "Signed in"),
139
+ detailLine: email,
140
+ memberName: email,
141
+ memberEmail: email,
142
+ memberImage: "",
143
+ memberInitials: "M",
144
+ hasMemberImage: false,
145
+ noMemberImage: true,
146
+ email,
147
+ password: "",
148
+ firstName: "",
149
+ lastName: "",
150
+ isRegistering: false,
151
+ message: "",
152
+ });
153
+ }
154
+
155
+ const svc = defineService<S, {
156
+ load: (s: S, arg?: unknown) => Promise<S>;
157
+ refresh: (s: S, arg?: unknown) => Promise<S>;
158
+ setEmail: (s: S, arg?: unknown) => S;
159
+ setPassword: (s: S, arg?: unknown) => S;
160
+ setFirstName: (s: S, arg?: unknown) => S;
161
+ setLastName: (s: S, arg?: unknown) => S;
162
+ showRegister: (s: S) => S;
163
+ showSignIn: (s: S) => S;
164
+ signIn: (s: S) => Promise<S>;
165
+ register: (s: S) => Promise<S>;
166
+ signOut: (s: S) => Promise<S>;
167
+ }>("himiBookingAccount", {
168
+ state: derive({
169
+ status: "loading",
170
+ isMember: false,
171
+ notMember: true,
172
+ identityLine: "",
173
+ detailLine: "",
174
+ memberName: "",
175
+ memberEmail: "",
176
+ memberImage: "",
177
+ memberInitials: "M",
178
+ hasMemberImage: false,
179
+ noMemberImage: true,
180
+ authTitle: "Welcome",
181
+ authBody: "Sign in to book services and see your history.",
182
+ email: "",
183
+ password: "",
184
+ firstName: "",
185
+ lastName: "",
186
+ message: "",
187
+ isLoading: true,
188
+ notLoading: false,
189
+ isRegistering: false,
190
+ notRegistering: true,
191
+ activityTitle: "Recent bookings",
192
+ activitySummary: "",
193
+ activityRows: [],
194
+ activityEmptyText: "No bookings for this member yet.",
195
+ hasActivity: false,
196
+ noActivity: true,
197
+ servicesScreenId: "himi_services",
198
+ bookingsScreenId: "himi_bookings",
199
+ }),
200
+ actions: {
201
+ load: (s, arg) => { stashCopy(arg); return probe(s); },
202
+ refresh: (s, arg) => { stashCopy(arg); return probe(s); },
203
+ setEmail: (s, arg) => derive({ ...s, email: strOf(arg), message: "" }),
204
+ setPassword: (s, arg) => derive({ ...s, password: strOf(arg), message: "" }),
205
+ setFirstName: (s, arg) => derive({ ...s, firstName: strOf(arg), message: "" }),
206
+ setLastName: (s, arg) => derive({ ...s, lastName: strOf(arg), message: "" }),
207
+ showRegister: (s) => derive({ ...s, isRegistering: true, message: "" }),
208
+ showSignIn: (s) => derive({ ...s, isRegistering: false, message: "" }),
209
+ signIn: async (s) => {
210
+ if (!s.email || !s.password) return derive({ ...s, message: copyParam("bookingAccount.validationEmailPassword", "Enter your email and password.") });
211
+ try {
212
+ await dispatch("himi.wix.signInDirect", { email: s.email, password: s.password });
213
+ const next = await probe(optimisticMember(s, s.email));
214
+ return derive({ ...next, password: "", message: next.isMember ? "" : copyParam("bookingAccount.signInCheckDetails", "Sign in failed. Check your details.") });
215
+ } catch {
216
+ return derive({ ...s, password: "", message: copyParam("bookingAccount.signInFailed", "Sign in failed. Try again.") });
217
+ }
218
+ },
219
+ register: async (s) => {
220
+ if (!s.email || !s.password) return derive({ ...s, message: copyParam("bookingAccount.validationEmailPassword", "Enter your email and password.") });
221
+ try {
222
+ await dispatch("himi.wix.registerDirect", {
223
+ email: s.email,
224
+ password: s.password,
225
+ firstName: s.firstName,
226
+ lastName: s.lastName,
227
+ });
228
+ const next = await probe(optimisticMember(s, s.email));
229
+ return derive({ ...next, password: "", message: next.isMember ? "" : copyParam("bookingAccount.registerTrySignIn", "Account creation failed. Try signing in.") });
230
+ } catch {
231
+ return derive({ ...s, password: "", message: copyParam("bookingAccount.registerFailed", "Account creation failed. Try another email.") });
232
+ }
233
+ },
234
+ signOut: async (s) => {
235
+ try { await dispatch("himi.wix.signOut"); } catch { /* ignore */ }
236
+ return probe({ ...s, isMember: false, isRegistering: false, message: "" });
237
+ },
238
+ },
239
+ });
240
+ svc.load();
241
+ export { svc };