@wix/agent-skills 1.7.0 → 1.8.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 (117) hide show
  1. package/README.md +3 -7
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +2 -0
  4. package/skills/wix-app/references/STORES_VERSIONING.md +1 -0
  5. package/skills/wix-design-system/SKILL.md +4 -0
  6. package/skills/wix-headless/SKILL.md +55 -32
  7. package/skills/wix-headless/references/BUILD-astro.md +246 -0
  8. package/skills/wix-headless/references/BUILD-own-build.md +167 -0
  9. package/skills/wix-headless/references/BUILD.md +50 -252
  10. package/skills/wix-headless/references/DESIGN_SYSTEM.md +94 -34
  11. package/skills/wix-headless/references/DISCOVERY-connect.md +55 -0
  12. package/skills/wix-headless/references/DISCOVERY-create.md +238 -0
  13. package/skills/wix-headless/references/DISCOVERY.md +41 -422
  14. package/skills/wix-headless/references/PLAN-connect.md +19 -0
  15. package/skills/wix-headless/references/PLAN-create.md +17 -0
  16. package/skills/wix-headless/references/PLAN.md +37 -27
  17. package/skills/wix-headless/references/SEED.md +27 -18
  18. package/skills/wix-headless/references/SETUP.md +32 -213
  19. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +5 -4
  20. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +337 -0
  21. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +428 -0
  22. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +5 -5
  23. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +10 -10
  24. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +12 -13
  25. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +10 -12
  26. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +8 -8
  27. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +50 -27
  28. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +7 -8
  29. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +5 -5
  30. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +3 -3
  31. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +5 -19
  32. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +2 -2
  33. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +1 -1
  34. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +6 -6
  35. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +4 -4
  36. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +3 -3
  37. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +3 -3
  38. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +9 -9
  39. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +5 -4
  40. package/skills/wix-headless/references/astro/templates/Footer.astro +1 -1
  41. package/skills/wix-headless/references/astro/templates/Layout.astro +1 -1
  42. package/skills/wix-headless/references/astro/templates/Navigation.astro +1 -1
  43. package/skills/wix-headless/references/astro/templates/astro.config.mjs +1 -1
  44. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +218 -0
  45. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +194 -0
  46. package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +84 -0
  47. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +25 -0
  48. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +50 -0
  49. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +45 -0
  50. package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +34 -0
  51. package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +74 -0
  52. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +111 -0
  53. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +413 -0
  54. package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +76 -0
  55. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +81 -0
  56. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +65 -0
  57. package/skills/wix-headless/references/astro/templates/global.css +2 -2
  58. package/skills/wix-headless/references/astro/templates/index.astro +1 -1
  59. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +19 -9
  60. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +3 -3
  61. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +106 -0
  62. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +295 -0
  63. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +4 -2
  64. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +27 -1
  65. package/skills/wix-headless/references/commands/known-apps.json +2 -1
  66. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +142 -0
  67. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +71 -21
  68. package/skills/wix-headless/references/custom/blog/WIRING.md +52 -0
  69. package/skills/wix-headless/references/custom/bookings/WIRING.md +88 -0
  70. package/skills/wix-headless/references/custom/cms/WIRING.md +97 -0
  71. package/skills/wix-headless/references/custom/ecom/WIRING.md +67 -0
  72. package/skills/wix-headless/references/custom/forms/WIRING.md +101 -0
  73. package/skills/wix-headless/references/custom/stores/WIRING.md +80 -0
  74. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +5 -5
  75. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +2 -2
  76. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +3 -3
  77. package/skills/wix-headless/references/images/INSTRUCTIONS.md +16 -15
  78. package/skills/wix-headless/references/seed-recipes.md +2 -2
  79. package/skills/wix-headless/references/shared/AGENTS_MD.md +47 -0
  80. package/skills/wix-headless/references/shared/AUTHENTICATION.md +3 -5
  81. package/skills/wix-headless/references/shared/DESIGN_MD.md +85 -0
  82. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +29 -28
  83. package/skills/wix-headless/references/shared/IMPLEMENTER.md +87 -27
  84. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +2 -2
  85. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +24 -313
  86. package/skills/wix-headless/references/shared/STYLING.md +8 -18
  87. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +39 -9
  88. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +17 -14
  89. package/skills/wix-headless/references/verticals/_schema.json +1 -1
  90. package/skills/wix-headless/references/verticals/_schema.md +3 -3
  91. package/skills/wix-headless/references/verticals/blog.md +1 -1
  92. package/skills/wix-headless/references/verticals/bookings.md +41 -0
  93. package/skills/wix-headless/references/verticals/cms.md +1 -1
  94. package/skills/wix-headless/references/verticals/ecom.md +2 -2
  95. package/skills/wix-headless/references/verticals/gift-cards.md +1 -1
  96. package/skills/wix-headless/references/verticals/stores.md +1 -1
  97. package/skills/wix-headless/scripts/check-manifest.mjs +143 -6
  98. package/skills/wix-headless/scripts/compose.mjs +540 -0
  99. package/skills/wix-headless/scripts/emit-design-tokens.mjs +128 -51
  100. package/skills/wix-headless/scripts/flatten.sh +61 -0
  101. package/skills/wix-headless/scripts/generate-images.mjs +242 -0
  102. package/skills/wix-headless/scripts/scaffold.sh +47 -16
  103. package/skills/wix-headless/scripts/write-decorative-json.mjs +62 -0
  104. package/skills/wix-manage/SKILL.md +1 -1
  105. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +1 -1
  106. package/skills/wix-manage/references/cms/cms-data-items-crud.md +1 -1
  107. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +1 -1
  108. package/skills/wix-manage/references/cms/cms-schema-management.md +1 -1
  109. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +10 -7
  110. package/skills/wix-manage/references/forms/create-form.md +49 -1
  111. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +1 -1
  112. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +1 -1
  113. package/skills/wix-headless/references/astro/COMPOSE.md +0 -142
  114. package/skills/wix-headless/scripts/finalize-run-json.mjs +0 -141
  115. package/skills/wix-headless/scripts/init-site-json.mjs +0 -101
  116. package/skills/wix-headless/scripts/preview.sh +0 -45
  117. package/skills/wix-headless/scripts/release.sh +0 -73
@@ -0,0 +1,428 @@
1
+ # Bookings Pages — Phase 4
2
+
3
+ You are the Phase 4 Pages agent for the bookings vertical. Your scope: write the route files. Read `references/shared/IMPLEMENTER.md` + `references/shared/STYLING.md` first.
4
+
5
+ > **Start from the canonical templates — don't invent the SSR data access.** Verified templates live at `<SKILL_ROOT>/references/astro/templates/bookings/`: `services/index.astro`, `services/[slug].astro`, `booking-confirmation.astro`, `manage-booking.astro` (plus the pre-copied API endpoints `api/confirm-booking.ts`, `api/waitlist.ts`). **Read and adapt them** (copy, headings, styling) — this doc explains the *why*. The SSR query pattern (`@wix/essentials` `auth.elevate`), the confirm step, and the manage/waitlist endpoints are live-verified.
6
+
7
+ ---
8
+
9
+ ## Files you own
10
+
11
+ | File | Route | Description |
12
+ |------|-------|-------------|
13
+ | `src/pages/services/index.astro` | `/services` | SSR services listing grid |
14
+ | `src/pages/services/[slug].astro` | `/services/[slug]` | SSR service detail + client-side booking flow |
15
+ | `src/pages/booking-confirmation.astro` | `/booking-confirmation` | Status-aware confirmation + inline cancel |
16
+ | `src/pages/manage-booking.astro` | `/manage-booking` | Per-booking view/cancel (anonymous token) |
17
+
18
+ ---
19
+
20
+ ## Step 1 — Read your seeded slice
21
+
22
+ Read your `seeded.bookings` slice from **`.wix/seeded.json`** (written once by the orchestrator at the seed gate — see `BUILD-astro.md` § "The `.wix/seeded.json` handoff"): `services[{id, slug, name, type, durationMinutes, price, currency}]` + `staff[{id, resourceId, name}]`. Use it to know the seeded slugs/names/types while authoring — do **NOT** import it into route files; the pages query the **live SDK** at request time (below). If your slice is absent, fail loud (`status: "partial"`, `errors: [{code: "SEEDED_JSON_SLICE_MISSING", missing: "seeded.bookings"}]`) — do not render an empty page.
23
+
24
+ ---
25
+
26
+ ## Page 1 — `/services/index.astro`
27
+
28
+ ### Frontmatter
29
+
30
+ ```typescript
31
+ ---
32
+ import Layout from '../../layouts/Layout.astro';
33
+ import ServiceCard from '../../components/ServiceCard.astro';
34
+ import { services } from '@wix/bookings';
35
+ import { auth } from '@wix/essentials';
36
+
37
+ // SSR data access: use the ambient @wix/astro server client via @wix/essentials —
38
+ // the SAME pattern the CMS pages use (`auth.elevate(fn)` + a direct module import).
39
+ // Do NOT build a `createClient`/`OAuthStrategy` here keyed off `import.meta.env`:
40
+ // the `*_WIX_CLIENT_ID` env vars are CLIENT-only in @wix/astro and `undefined`
41
+ // during server render, so `createClient`/`OAuthStrategy` throws and the page
42
+ // returns HTTP 500 in production. (It "works" in `astro dev` only because
43
+ // `.env.local` is loaded there — the 500 surfaces only in the deployed runtime.)
44
+ // Client-side islands access the ID differently — see COMPONENTS.md (`astro:env/client`).
45
+ let serviceList: any[] = [];
46
+ try {
47
+ // queryServices uses the QUERY BUILDER, then `.find()` — NOT a
48
+ // `{ query: { filter, paging } }` object. In @wix/bookings the object form
49
+ // returns ZERO items with no error; the builder is what actually queries.
50
+ const result = await auth.elevate(services.queryServices)()
51
+ .limit(100)
52
+ .find();
53
+ // Exclude hidden services from the public listing (filter in JS — robust
54
+ // regardless of whether `hidden` is a queryable builder field).
55
+ serviceList = (result.items ?? []).filter((s: any) => !s.hidden);
56
+ } catch (err) {
57
+ console.error('[services] query failed:', err);
58
+ }
59
+
60
+ // Services V2: slug lives at service.mainSlug.name. ID is service._id (underscore).
61
+ const getSlug = (service: any): string =>
62
+ service.mainSlug?.name ??
63
+ service.supportedSlugs?.[0]?.name ??
64
+ service._id; // fallback to ID if slug is absent
65
+
66
+ // Services V2: price uses `value` (string), not `amount`
67
+ const formatPrice = (service: any): string | undefined => {
68
+ const payment = service.payment;
69
+ if (!payment || payment.rateType === 'NO_FEE') return undefined;
70
+ if (payment.rateType === 'FIXED' && payment.fixed?.price) {
71
+ const { value, currency } = payment.fixed.price;
72
+ if (!value) return undefined;
73
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency ?? 'USD' }).format(Number(value));
74
+ }
75
+ return undefined;
76
+ };
77
+
78
+ // Duration: lives at service.schedule.availabilityConstraints.sessionDurations[0]
79
+ const getDuration = (service: any): number =>
80
+ service.schedule?.availabilityConstraints?.sessionDurations?.[0] ?? 60;
81
+ ---
82
+ ```
83
+
84
+ ### Template
85
+
86
+ ```astro
87
+ <Layout title="Services">
88
+ <main class="py-4xl">
89
+ <section class="container mx-auto px-lg">
90
+ <header class="mb-3xl">
91
+ <h1 class="font-display text-4xl mb-sm">Our Services</h1>
92
+ <!-- Optional tagline from brand -->
93
+ </header>
94
+
95
+ {serviceList.length === 0 ? (
96
+ <p class="text-secondary">Services coming soon — check back shortly.</p>
97
+ ) : (
98
+ <div class="service-grid">
99
+ {serviceList.map((service) => (
100
+ <ServiceCard
101
+ id={service._id}
102
+ slug={getSlug(service)}
103
+ name={service.name ?? 'Untitled'}
104
+ tagLine={service.tagLine}
105
+ durationMinutes={getDuration(service)}
106
+ price={formatPrice(service)}
107
+ imageUrl={service.media?.mainMedia?.image?.url ?? service.media?.items?.[0]?.image?.url}
108
+ type={service.type ?? 'APPOINTMENT'}
109
+ />
110
+ ))}
111
+ </div>
112
+ )}
113
+ </section>
114
+ </main>
115
+ </Layout>
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Page 2 — `/services/[slug].astro`
121
+
122
+ ### Frontmatter
123
+
124
+ ```typescript
125
+ ---
126
+ // File is at src/pages/services/[slug].astro
127
+ // ../../ from src/pages/services/ → src/
128
+ import Layout from '../../layouts/Layout.astro';
129
+ import ServiceBookingFlow from '../../components/ServiceBookingFlow';
130
+ import { services } from '@wix/bookings';
131
+ import { auth } from '@wix/essentials';
132
+
133
+ const { slug } = Astro.params;
134
+
135
+ // SSR via @wix/essentials — same ambient pattern as the listing page above and
136
+ // the CMS pages. No createClient/OAuthStrategy, no import.meta.env (those 500 at
137
+ // server render — see the listing-page note).
138
+ let service: any = null;
139
+ try {
140
+ // Query builder + `.find()` (the object form returns 0 items). Fetch the small
141
+ // catalog and match the slug in JS — robust without relying on a nested-field
142
+ // (`mainSlug.name`) builder filter.
143
+ const result = await auth.elevate(services.queryServices)()
144
+ .limit(100)
145
+ .find();
146
+ service =
147
+ (result.items ?? []).find(
148
+ (s: any) => (s.mainSlug?.name ?? s.supportedSlugs?.[0]?.name) === slug,
149
+ ) ?? null;
150
+ } catch (err) {
151
+ console.error(`[services/${slug}] query failed:`, err);
152
+ }
153
+
154
+ if (!service) {
155
+ return Astro.redirect('/services', 302);
156
+ }
157
+
158
+ // Services V2: flat fields — `name`, `tagLine`, not `info.name`, `info.tagLine`
159
+ // Price uses `value` (string), not `amount`
160
+ const formatPrice = (payment: any): string | undefined => {
161
+ if (!payment || payment.rateType === 'NO_FEE') return undefined;
162
+ if (payment.rateType === 'FIXED' && payment.fixed?.price) {
163
+ const { value, currency } = payment.fixed.price;
164
+ if (!value) return undefined;
165
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency ?? 'USD' }).format(Number(value));
166
+ }
167
+ return undefined;
168
+ };
169
+
170
+ const price = formatPrice(service.payment);
171
+ // Duration: service.schedule.availabilityConstraints.sessionDurations[0]
172
+ const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0] ?? 60;
173
+ // Media: service.media.mainMedia.image.url (V2 shape)
174
+ const imageUrl = service.media?.mainMedia?.image?.url ?? service.media?.items?.[0]?.image?.url;
175
+ ---
176
+ ```
177
+
178
+ ### Template
179
+
180
+ ```astro
181
+ <!--
182
+ Width: use `container-reading` (max-width: var(--container-prose) + auto margins),
183
+ NOT a bare `max-w-<size>`. In this Tailwind v4 setup any `max-w-<size>` whose key
184
+ the Composer didn't emit in @theme `--container-*` silently falls back to the
185
+ `--spacing-*` scale — e.g. `max-w-4xl` → ~7rem, a ~112px column (one word per line).
186
+ `container-reading` is a project-owned utility that always resolves. See STYLING.md.
187
+ -->
188
+ <Layout title={service.name ?? 'Service'}>
189
+ <main class="py-4xl">
190
+ <div class="container-reading px-lg">
191
+ <!-- Back link -->
192
+ <a href="/services" class="text-secondary hover:text-primary text-sm mb-xl block">
193
+ ← All Services
194
+ </a>
195
+
196
+ <!-- Optional banner image — render ONLY when the service has media. The
197
+ default seed is text-only, so most services have no image; rendering a
198
+ fixed-ratio placeholder there collapses to a thin bar and crowds the
199
+ text. Single full-width column when image-less. -->
200
+ {imageUrl && (
201
+ <img src={imageUrl} alt={service.name} class="w-full h-64 object-cover rounded-lg mb-xl" />
202
+ )}
203
+
204
+ <!-- Service hero — single column -->
205
+ <div class="flex flex-col gap-md mb-3xl">
206
+ {/* V2 flat fields: name, tagLine, description (not info.name etc.) */}
207
+ <h1 class="font-display text-4xl">{service.name}</h1>
208
+ {service.tagLine && (
209
+ <p class="text-secondary text-lg">{service.tagLine}</p>
210
+ )}
211
+ <div class="flex gap-md items-center text-sm">
212
+ <span class="font-medium">{duration} min</span>
213
+ {price && <span class="text-primary font-bold text-lg">{price}</span>}
214
+ </div>
215
+ {service.description && (
216
+ <p class="text-base leading-relaxed">{service.description}</p>
217
+ )}
218
+ </div>
219
+
220
+ <!-- Booking flow — client-side islands -->
221
+ <section class="border-t border-[--color-border] pt-2xl">
222
+ <h2 class="font-display text-2xl mb-xl">Book This Service</h2>
223
+ <ServiceBookingFlow
224
+ serviceId={service._id}
225
+ serviceName={service.name ?? 'this service'}
226
+ serviceType={service.type ?? 'APPOINTMENT'}
227
+ client:only="react"
228
+ />
229
+ </section>
230
+ </div>
231
+ </main>
232
+ </Layout>
233
+ ```
234
+
235
+ ### ServiceBookingFlow coordinator
236
+
237
+ Because `AvailabilityCalendar` and `BookingForm` share state (the selected slot) and need to transition between each other, write a thin coordinator island `ServiceBookingFlow.tsx` that manages this state:
238
+
239
+ ```typescript
240
+ // src/components/ServiceBookingFlow.tsx
241
+ import { useState } from 'react';
242
+ import AvailabilityCalendar from './AvailabilityCalendar';
243
+ import BookingForm from './BookingForm';
244
+
245
+ interface Props {
246
+ serviceId: string;
247
+ serviceName: string;
248
+ serviceType: 'APPOINTMENT' | 'CLASS'; // threaded into both islands
249
+ }
250
+
251
+ export default function ServiceBookingFlow({ serviceId, serviceName, serviceType }: Props) {
252
+ const [selectedSlot, setSelectedSlot] = useState<any>(null);
253
+
254
+ const handleSuccess = (bookingId: string, startDate?: string) => {
255
+ const params = new URLSearchParams({ bookingId });
256
+ if (startDate) params.set('startDate', startDate);
257
+ if (serviceName) params.set('service', serviceName);
258
+ window.location.href = `/booking-confirmation?${params.toString()}`;
259
+ };
260
+
261
+ if (!selectedSlot) {
262
+ return (
263
+ <AvailabilityCalendar
264
+ serviceId={serviceId}
265
+ serviceName={serviceName}
266
+ serviceType={serviceType}
267
+ onSlotSelected={setSelectedSlot}
268
+ />
269
+ );
270
+ }
271
+
272
+ return (
273
+ <BookingForm
274
+ serviceId={serviceId}
275
+ serviceName={serviceName}
276
+ serviceType={serviceType}
277
+ slot={selectedSlot}
278
+ onSuccess={handleSuccess}
279
+ onCancel={() => setSelectedSlot(null)}
280
+ />
281
+ );
282
+ }
283
+ ```
284
+
285
+ Add `ServiceBookingFlow` to the files you own and include it in your pre-return assertion.
286
+
287
+ ---
288
+
289
+ ## Page 3 — `/booking-confirmation.astro`
290
+
291
+ ### Behavior — show the real status + cancel inline
292
+
293
+ The confirmation page **re-fetches the booking** to show its **actual status** (`CONFIRMED` vs `PENDING` approval) — so the user sees whether the booking was approved, not just an optimistic "you're booked" — and embeds the cancel action. It re-uses the **anonymous-action-token** flow: the server elevates (app identity) to mint a per-booking token (`getAnonymousActionToken`), then reads it with the no-auth `bookingsGetBookingAnonymously`, falling back to the redirect params (`?bookingId=...&startDate=...&service=...`) if the lookup fails. Cancel is the same `<ManageBooking>` island used by `/manage-booking` (pass `showSummary={false}` to avoid duplicating the headline). See the canonical template `booking-confirmation.astro`.
294
+
295
+ ```typescript
296
+ // frontmatter (essentials):
297
+ const bookingId = Astro.url.searchParams.get('bookingId');
298
+ const { token } = bookingId ? await auth.elevate(bookings.getAnonymousActionToken)(bookingId) : { token: null };
299
+ const { booking, allowedAnonymousActions } = token ? await bookings.bookingsGetBookingAnonymously(token) : {};
300
+ const status = booking?.status ?? null; // CONFIRMED | PENDING | …
301
+ const isPending = status === 'PENDING' || status === 'PENDING_APPROVAL';
302
+ // Render: "You're booked / confirmed" vs "Booking received — awaiting confirmation" by status,
303
+ // a Status badge, and <ManageBooking … canCancel={!!allowedAnonymousActions?.cancel} showSummary={false} />.
304
+ ```
305
+
306
+ > **Two different `elevate`s, don't conflate them:** `@wix/essentials` `auth.elevate(fn)` works in `@wix/astro` SSR (used here to mint the token and across the listing/detail/CMS pages); the unavailable one is `wixClient.auth.elevate` (the instance method on a hand-built `createClient({ auth: OAuthStrategy(...) })`).
307
+
308
+ ### Template
309
+
310
+ Use the canonical template verbatim and adapt copy/styling: `<SKILL_ROOT>/references/astro/templates/bookings/booking-confirmation.astro`. It renders a status-aware headline ("You're booked / confirmed" vs "Booking received — awaiting confirmation"), a Status badge, and the embedded `<ManageBooking … showSummary={false} />` cancel — wrapped in `max-w-prose` (a built-in literal; never a bare `max-w-<size>`, which can fall through to the spacing scale).
311
+
312
+ ---
313
+
314
+ ## Server endpoints + manage-booking page (verified live)
315
+
316
+ Some bookings operations require **Manage Bookings** scope and so **cannot** run from the browser `OAuthStrategy` client — they must be Astro **API routes** (`output: "server"`) that elevate to app identity. Elevation rule (important): `auth.elevate()` only grants permissions for a real **SDK function**; for raw REST with no SDK wrapper, elevate the authenticated fetch itself.
317
+
318
+ ### `src/pages/api/confirm-booking.ts` — hold the seat (always, for create flows)
319
+ `createBooking` returns `CREATED`, which holds nothing. Confirm to occupy the seat + validate availability:
320
+ ```typescript
321
+ import { auth } from "@wix/essentials";
322
+ import { bookings } from "@wix/bookings";
323
+ // POST { bookingId, revision }
324
+ await auth.elevate(bookings.confirmBooking)(bookingId, String(revision), {
325
+ paymentStatus: "NOT_PAID",
326
+ flowControlSettings: { checkAvailabilityValidation: true },
327
+ });
328
+ ```
329
+
330
+ ### `src/pages/api/waitlist.ts` — native waitlist (v1; no v2/v3 exists)
331
+ Register on the native waitlist when a session is full. Resolve a `contactId` (Contacts API, by email → create if missing), then `POST /bookings/v1/waitlist/register`. All via the **elevated authenticated fetch** (raw REST, so elevate `fetchWithAuth`, not a closure):
332
+ ```typescript
333
+ import { auth, httpClient } from "@wix/essentials";
334
+ const ef = auth.elevate(httpClient.fetchWithAuth as typeof fetch) as typeof fetch;
335
+ const api = (url: string, body: unknown) =>
336
+ ef(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
337
+
338
+ // 1. contactId — query then create
339
+ let contactId = (await (await api("https://www.wixapis.com/contacts/v4/contacts/query",
340
+ { query: { filter: { "info.emails.email": email } } })).json()).contacts?.[0]?.id;
341
+ if (!contactId) contactId = (await (await api("https://www.wixapis.com/contacts/v4/contacts",
342
+ { info: { name: { first }, emails: { items: [{ email, primary: true }] } } })).json()).contact?.id;
343
+
344
+ // 2. register — waitingResource is the v1 session id from the SESSION_CAPACITY_EXCEEDED message
345
+ await api("https://www.wixapis.com/bookings/v1/waitlist/register", {
346
+ waitingResource: sessionId,
347
+ formInfo: {
348
+ paymentSelection: [{ rateLabel: "general", numberOfParticipants: totalParticipants }],
349
+ contactDetails: { contactId, firstName: first, email },
350
+ },
351
+ });
352
+ ```
353
+
354
+ ### `src/pages/manage-booking.astro` + `ManageBooking.tsx` — view/cancel (anonymous-token flow)
355
+ A plain visitor token can't list/cancel bookings (`queryExtendedBookings`/`cancelBooking` are APP/MEMBER only). The supported visitor path is the **anonymous action token**: the page (SSR) elevates to **mint** a per-booking token, then the no-auth anonymous methods act on it (the token can safely go to the browser):
356
+ ```typescript
357
+ // manage-booking.astro frontmatter — bookingId from ?bookingId=
358
+ const { token } = await auth.elevate(bookings.getAnonymousActionToken)(bookingId); // APP identity → elevate
359
+ const { booking, allowedAnonymousActions } = await bookings.bookingsGetBookingAnonymously(token); // no auth
360
+ // Pass token + booking.revision + allowedAnonymousActions.cancel to a <ManageBooking client:only> island.
361
+ ```
362
+ In the island (no-auth, visitor client OK):
363
+ ```typescript
364
+ await wixClient.bookings.bookingsCancelBookingAnonymously(token, revision); // cancel
365
+ ```
366
+ Reschedule-in-place uses `bookingsRescheduleBookingAnonymously(token, slot, { revision })`, but the CLASS `V2Slot` shape isn't fully pinned — prefer **cancel-and-rebook** (link to the service) unless you've verified the class slot shape live.
367
+
368
+ Link to it from the confirmation page: `/manage-booking?bookingId=<id>&service=<name>`.
369
+
370
+ ---
371
+
372
+ ## Navigation contribution
373
+
374
+ After writing your three route files, patch `src/layouts/Navigation.astro` to add the Services nav link. Insert at the `<!-- nav:links -->` marker:
375
+
376
+ ```astro
377
+ <!-- nav:links -->
378
+ <a href="/services">Services</a>
379
+ ```
380
+
381
+ Follow the marker discipline rules in `references/shared/IMPLEMENTER.md` — preserve the marker line, insert after it, do not disturb adjacent content.
382
+
383
+ ---
384
+
385
+ ## Home page contribution
386
+
387
+ Patch `src/pages/index.astro` to add a brief services teaser section. Insert at the `<!-- home:bookings -->` marker (emitted by `compose.mjs` when bookings is loaded). You are a **shell-patcher** (BUILD-astro § build wave, chain B) — `index.astro`/`Navigation.astro` patches are serialized against the other shell-patching verticals. The teaser should:
388
+ - Show the first 3 services (query the live SDK like `/services` does; your `seeded.bookings` slice tells you what exists)
389
+ - Include a "View All Services" CTA linking to `/services`
390
+ - Use `<ServiceCard>` for each service entry
391
+
392
+ If the `<!-- home:bookings -->` marker is absent from the file, skip the home contribution — do NOT invent a new insertion point.
393
+
394
+ ---
395
+
396
+ ## Pre-return file-existence assertion
397
+
398
+ Before returning `status: "complete"`, verify all of these files exist on disk:
399
+ - `src/pages/services/index.astro`
400
+ - `src/pages/services/[slug].astro`
401
+ - `src/pages/booking-confirmation.astro`
402
+ - `src/pages/manage-booking.astro`
403
+ - `src/components/ServiceBookingFlow.tsx`
404
+
405
+ Missing file → return `status: "partial"` with `errors: [{ code: "PHASE4_FILE_MISSING", path: "<path>" }]`.
406
+
407
+ ---
408
+
409
+ ## Return contract
410
+
411
+ ```json
412
+ {
413
+ "status": "complete",
414
+ "phase": "pages",
415
+ "vertical": "bookings",
416
+ "files": [
417
+ "src/pages/services/index.astro",
418
+ "src/pages/services/[slug].astro",
419
+ "src/pages/booking-confirmation.astro",
420
+ "src/pages/manage-booking.astro",
421
+ "src/components/ServiceBookingFlow.tsx"
422
+ ],
423
+ "contributions": [
424
+ { "file": "src/layouts/Navigation.astro", "marker": "nav:links" },
425
+ { "file": "src/pages/index.astro", "marker": "home:bookings" }
426
+ ]
427
+ }
428
+ ```
@@ -4,7 +4,7 @@ The **rendering / code-patterns** half of CMS foundations: service module templa
4
4
 
5
5
  > **Seeding lives in the business track.** Creating collections and inserting/verifying items via REST is **not** here — it is in `<SKILL_ROOT>/references/cms/CMS_FOUNDATIONS.md` (the `seed` scope's doc). This file is consumed by the `pages` scope; that file by the `seed` scope. The `cms/INSTRUCTIONS.md` router points each scope at the right half.
6
6
 
7
- > **Import note (read first).** Page code in this plugin uses `import * as items from "@wix/wix-data-items-sdk"` rather than the documented `import { items } from "@wix/data"`. The Wix-headless docs still show the `@wix/data` form, but `@wix/data` 1.0.448 dropped the `items` re-export — only sub-namespaces (`backups`, `collections`, `permissions`, …) remain, and the documented form fails the build with `'items' is not exported by '@wix/data'`. The actual `items` API (with `query`, `insert`, `update`, `remove`, `bulkInsert`, etc.) lives in `@wix/wix-data-items-sdk`, which `@wix/data` depends on transitively. Importing from there directly works on every current `@wix/data` version. SETUP.md's per-pack install table (Step 4c) installs `@wix/wix-data-items-sdk` (alongside `@wix/data` and `@wix/essentials`) for the cms pack so it's always present. Use the wix-data-items-sdk import everywhere.
7
+ > **Import note (read first).** The Wix Data items API is the **`items`** named export of **`@wix/data`**: `import { items } from "@wix/data"`, then `items.query` / `items.insert` / `items.update` / `items.remove` (plus `items.bulkInsert` / `bulkSave` / `bulkPatch` / …). This is the documented form and what every CMS page here uses. Do **not** import from the internal `@wix/wix-data-items-sdk`. SETUP.md's per-pack install table (Step 4c) installs `@wix/data` + `@wix/essentials` for the cms pack.
8
8
 
9
9
  ## Rendering Ricos rich-text fields
10
10
 
@@ -14,7 +14,7 @@ For SSR Astro pages (about, faq, portfolio, team, resource), import the seeded `
14
14
 
15
15
  ```astro
16
16
  ---
17
- import * as items from "@wix/wix-data-items-sdk";
17
+ import { items } from "@wix/data";
18
18
  import { auth } from "@wix/essentials";
19
19
  import { renderRicos } from "../utils/ricos";
20
20
 
@@ -46,7 +46,7 @@ The util covers the common subset (PARAGRAPH, HEADING 1-6, BULLETED_LIST / ORDER
46
46
 
47
47
  ## Prerequisites (page-side)
48
48
 
49
- - `@wix/data` + `@wix/wix-data-items-sdk` + `@wix/essentials` installed (collected by SETUP.md Step 4c — do NOT install independently).
49
+ - `@wix/data` + `@wix/essentials` installed (collected by SETUP.md Step 4c — do NOT install independently).
50
50
  - The collections + items already created and seeded by the `seed` scope (see `../../cms/CMS_FOUNDATIONS.md`). Pages only **read**.
51
51
 
52
52
  ## Collection ID Format
@@ -65,7 +65,7 @@ For pages that just need to list items from a collection:
65
65
 
66
66
  ```astro
67
67
  ---
68
- import * as items from "@wix/wix-data-items-sdk";
68
+ import { items } from "@wix/data";
69
69
  import { auth } from "@wix/essentials";
70
70
 
71
71
  // Always elevate queries for permission safety
@@ -93,7 +93,7 @@ const myItems = results.map((item) => ({
93
93
  Every CMS use case follows the same `src/lib/{usecase}.ts` pattern — a typed interface and query functions that pages import.
94
94
 
95
95
  ```typescript
96
- import * as items from "@wix/wix-data-items-sdk";
96
+ import { items } from "@wix/data";
97
97
  import { auth } from "@wix/essentials";
98
98
  import { media } from "@wix/sdk";
99
99
 
@@ -6,7 +6,7 @@ Build a FAQ page using `@wix/data` — Q&A pairs organized by category with acco
6
6
 
7
7
  ## Collection Schema
8
8
 
9
- Create a collection in the Wix dashboard → CMS with these fields:
9
+ The schema the `seed` scope creates via REST (see `../../cms/CMS_FOUNDATIONS.md`) — fields:
10
10
 
11
11
  | Field | Type | Purpose |
12
12
  |-------|------|---------|
@@ -32,7 +32,7 @@ Create a collection in the Wix dashboard → CMS with these fields:
32
32
  ### 1. FAQ Service Module (`src/lib/faq.ts`)
33
33
 
34
34
  ```typescript
35
- import * as items from "@wix/wix-data-items-sdk";
35
+ import { items } from "@wix/data";
36
36
  import { auth } from "@wix/essentials";
37
37
 
38
38
  const COLLECTION_ID = "FAQ";
@@ -169,7 +169,7 @@ export default function FaqSection({ categories }: Props) {
169
169
  }
170
170
  ```
171
171
 
172
- > **Styling note:** Uses `.faq-search`, `.faq-section`, `.faq-category`, `.faq-item`, `.faq-question`, `.faq-toggle`, `.faq-answer` from the designed component's `<style is:global>` block. See `COMPONENT_PATTERNS.md` → FAQ Accordion.
172
+ > **Styling note:** Uses `.faq-search`, `.faq-section`, `.faq-category`, `.faq-item`, `.faq-question`, `.faq-toggle`, `.faq-answer` from the designed component's `<style is:global>` block. See `references/shared/STYLING.md` (FAQ Accordion).
173
173
 
174
174
  Key details:
175
175
  - `<details>/<summary>` for native accordion behavior — no JavaScript needed for open/close
@@ -222,10 +222,10 @@ Key details:
222
222
 
223
223
  ## Testing
224
224
 
225
- 1. Create a "FAQ" collection in the Wix dashboard → CMS with the schema above
226
- 2. Add 6+ FAQ items across 2+ categories, all with `published: true`
227
- 3. Run `npx @wix/cli@latest dev`
228
- 4. `/faq` — shows all questions grouped by category
229
- 5. Click a question — accordion opens with the answer
230
- 6. Type in search — filters questions in real-time
231
- 7. Click a category jump link — scrolls to that section
225
+ Assumes the `seed` scope has already created the "FAQ" collection and inserted items via REST (see `../../cms/CMS_FOUNDATIONS.md`) — with 6+ FAQ items across 2+ categories, all `published: true`.
226
+
227
+ 1. Run `npx @wix/cli@latest dev`
228
+ 2. `/faq` — shows all questions grouped by category
229
+ 3. Click a question — accordion opens with the answer
230
+ 4. Type in search — filters questions in real-time
231
+ 5. Click a category jump link — scrolls to that section
@@ -6,7 +6,7 @@ Build a visual portfolio using `@wix/data` — project grid with category filter
6
6
 
7
7
  ## Collection Schema
8
8
 
9
- Create a collection in the Wix dashboard → CMS with these fields:
9
+ The schema the `seed` scope creates via REST (see `../../cms/CMS_FOUNDATIONS.md`) — fields:
10
10
 
11
11
  | Field | Type | Purpose |
12
12
  |-------|------|---------|
@@ -38,7 +38,7 @@ Create a collection in the Wix dashboard → CMS with these fields:
38
38
  ### 1. Portfolio Service Module (`src/lib/portfolio.ts`)
39
39
 
40
40
  ```typescript
41
- import * as items from "@wix/wix-data-items-sdk";
41
+ import { items } from "@wix/data";
42
42
  import { auth } from "@wix/essentials";
43
43
  import { media } from "@wix/sdk";
44
44
 
@@ -150,7 +150,7 @@ const { categories, activeCategory, basePath } = Astro.props;
150
150
  </nav>
151
151
  ```
152
152
 
153
- > **Styling note:** CategoryFilter styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Category Filter.
153
+ > **Styling note:** CategoryFilter styling is created by the design skill. See `references/shared/STYLING.md` (Category Filter).
154
154
 
155
155
  > Server-rendered, no JavaScript. Category is read from `Astro.url.searchParams` in the page.
156
156
 
@@ -189,7 +189,7 @@ const { project } = Astro.props;
189
189
  </a>
190
190
  ```
191
191
 
192
- > **Styling note:** ProjectCard styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Project Card.
192
+ > **Styling note:** ProjectCard styling is created by the design skill. See `references/shared/STYLING.md` (Project Card).
193
193
 
194
194
  ### 4. Project Grid Page (`src/pages/work/index.astro`)
195
195
 
@@ -376,7 +376,7 @@ export default function ImageGallery({ images, alt }: Props) {
376
376
  }
377
377
  ```
378
378
 
379
- > **Styling note:** Uses `.gallery-grid`, `.gallery-item`, `.lightbox-overlay`, `.lightbox-image`, `.lightbox-nav`, `.lightbox-close` from the designed component's `<style is:global>` block. See `COMPONENT_PATTERNS.md` → Image Gallery / Lightbox.
379
+ > **Styling note:** Uses `.gallery-grid`, `.gallery-item`, `.lightbox-overlay`, `.lightbox-image`, `.lightbox-nav`, `.lightbox-close` from the designed component's `<style is:global>` block. See `references/shared/STYLING.md` (Image Gallery / Lightbox).
380
380
 
381
381
  Key details:
382
382
  - Renders as a grid; clicking opens a lightbox overlay
@@ -426,7 +426,7 @@ Project showcase image for [TITLE]. [BRAND AESTHETIC]. Color palette: [BRAND COL
426
426
  Gallery image [N] for [TITLE]: [describe a specific aspect/angle of the project]. [BRAND AESTHETIC]. [BRAND COLORS]. No text, no watermarks
427
427
  ```
428
428
 
429
- **Patch fields:** `coverImage`, optionally `galleryImages` (as an array of wixstatic URLs)
429
+ **Patch field:** `galleryImages` (as an array of wixstatic URLs)
430
430
 
431
431
  Example patch:
432
432
 
@@ -445,10 +445,9 @@ body: {
445
445
 
446
446
  ## Testing
447
447
 
448
- 1. Create a "Projects" collection in the Wix dashboard → CMS with the schema above
449
- 2. Add 3+ projects with cover images, categories, and at least one with gallery images
450
- 3. Mark 1–2 as `featured: true`
451
- 4. Run `npx @wix/cli@latest dev`
452
- 5. `/work` — grid with category filter tabs
453
- 6. Click a category — filters projects
454
- 7. Click a project — detail page with gallery lightbox
448
+ Assumes the `seed` scope has already created the "Projects" collection and inserted items via REST (see `../../cms/CMS_FOUNDATIONS.md`) — 3+ projects with categories (and, after the image pass, cover/gallery images), 1–2 marked `featured: true`.
449
+
450
+ 1. Run `npx @wix/cli@latest dev`
451
+ 2. `/work` — grid with category filter tabs
452
+ 3. Click a category — filters projects
453
+ 4. Click a project — detail page with gallery lightbox
@@ -6,7 +6,7 @@ Build a downloadable resource library using `@wix/data` — file listings with c
6
6
 
7
7
  ## Collection Schema
8
8
 
9
- Create a collection in the Wix dashboard → CMS with these fields:
9
+ The schema the `seed` scope creates via REST (see `../../cms/CMS_FOUNDATIONS.md`) — fields:
10
10
 
11
11
  | Field | Type | Purpose |
12
12
  |-------|------|---------|
@@ -36,7 +36,7 @@ Create a collection in the Wix dashboard → CMS with these fields:
36
36
  ### 1. Resources Service Module (`src/lib/resources.ts`)
37
37
 
38
38
  ```typescript
39
- import * as items from "@wix/wix-data-items-sdk";
39
+ import { items } from "@wix/data";
40
40
  import { auth } from "@wix/essentials";
41
41
  import { media } from "@wix/sdk";
42
42
 
@@ -146,8 +146,7 @@ const fileType = type.toUpperCase();
146
146
  ```
147
147
 
148
148
  Key details:
149
- - Color-coded by file type — red for PDF, blue for DOC, green for XLS, yellow for ZIP
150
- - Falls back to neutral gray for unknown types
149
+ - The SVG uses `stroke="currentColor"` — any per-file-type coloring is purely CSS-driven (e.g. a `.file-type-pdf` class on the wrapper), not set here
151
150
  - File type label rendered inside the document icon SVG
152
151
 
153
152
  ### 3. Resource Card (`src/components/ResourceCard.astro`)
@@ -187,7 +186,7 @@ const { resource } = Astro.props;
187
186
  </a>
188
187
  ```
189
188
 
190
- > **Styling note:** ResourceCard styling is created by the design skill. See `COMPONENT_PATTERNS.md` → Resource Card.
189
+ > **Styling note:** ResourceCard styling is created by the design skill. See `references/shared/STYLING.md` (Resource Card).
191
190
 
192
191
  ### 4. Resource Listing Page (`src/pages/resources/index.astro`)
193
192
 
@@ -347,10 +346,9 @@ body: {
347
346
 
348
347
  ## Testing
349
348
 
350
- 1. Create a "Resources" collection in the Wix dashboard → CMS with the schema above
351
- 2. Add 4+ resources across 2+ categories with different file types (PDF, DOC, ZIP)
352
- 3. Include `fileUrl` links (can be any downloadable URL for testing)
353
- 4. Run `npx @wix/cli@latest dev`
354
- 5. `/resources` — shows all resources with category filters and file type badges
355
- 6. Click a category — filters resources
356
- 7. Click a resource — detail page with download button and related resources
349
+ Assumes the `seed` scope has already created the "Resources" collection and inserted items via REST (see `../../cms/CMS_FOUNDATIONS.md`) — 4+ resources across 2+ categories with different file types (PDF, DOC, ZIP) and `fileUrl` links (any downloadable URL for testing).
350
+
351
+ 1. Run `npx @wix/cli@latest dev`
352
+ 2. `/resources` — shows all resources with category filters and file type badges
353
+ 3. Click a category — filters resources
354
+ 4. Click a resource — detail page with download button and related resources