@wix/agent-skills 1.6.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 (195) hide show
  1. package/README.md +5 -9
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +51 -42
  4. package/skills/wix-app/references/BACKEND_EVENT.md +10 -65
  5. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +101 -406
  6. package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +14 -111
  7. package/skills/wix-app/references/DASHBOARD_MODAL.md +56 -205
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +19 -98
  9. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +6 -108
  10. package/skills/wix-app/references/DATA_COLLECTION.md +113 -198
  11. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +41 -268
  12. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +27 -76
  13. package/skills/wix-app/references/SERVICE_PLUGIN.md +35 -188
  14. package/skills/wix-app/references/SITE_PLUGIN.md +8 -92
  15. package/skills/wix-app/references/STORES_VERSIONING.md +1 -0
  16. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +0 -44
  17. package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +19 -330
  18. package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +0 -12
  19. package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +7 -0
  20. package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +0 -12
  21. package/skills/wix-app/references/dashboard-plugin/SLOTS.md +3 -15
  22. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +2 -18
  23. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +2 -42
  24. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +1 -11
  25. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +1 -11
  26. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +2 -40
  27. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +2 -19
  28. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +2 -18
  29. package/skills/wix-app/references/site-plugin/SLOTS.md +17 -68
  30. package/skills/wix-design-system/SKILL.md +4 -0
  31. package/skills/wix-headless/SKILL.md +131 -386
  32. package/skills/wix-headless/references/BUILD-astro.md +246 -0
  33. package/skills/wix-headless/references/BUILD-own-build.md +167 -0
  34. package/skills/wix-headless/references/BUILD.md +105 -0
  35. package/skills/wix-headless/references/DESIGN_SYSTEM.md +175 -0
  36. package/skills/wix-headless/references/DISCOVERY-connect.md +55 -0
  37. package/skills/wix-headless/references/DISCOVERY-create.md +238 -0
  38. package/skills/wix-headless/references/DISCOVERY.md +50 -306
  39. package/skills/wix-headless/references/PLAN-connect.md +19 -0
  40. package/skills/wix-headless/references/PLAN-create.md +17 -0
  41. package/skills/wix-headless/references/PLAN.md +92 -0
  42. package/skills/wix-headless/references/SEED.md +214 -0
  43. package/skills/wix-headless/references/SETUP.md +73 -248
  44. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_PAGES.md +6 -5
  45. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_SETUP.md +2 -2
  46. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +337 -0
  47. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +428 -0
  48. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +214 -0
  49. package/skills/wix-headless/references/{cms → astro/cms}/FAQ_KNOWLEDGE_BASE.md +14 -12
  50. package/skills/wix-headless/references/{cms → astro/cms}/PORTFOLIO.md +15 -16
  51. package/skills/wix-headless/references/{cms → astro/cms}/RESOURCE_LIBRARY.md +13 -15
  52. package/skills/wix-headless/references/{cms → astro/cms}/TEAM_DIRECTORY.md +11 -11
  53. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +354 -0
  54. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_PAGES.md +8 -9
  55. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_WIRING.md +6 -6
  56. package/skills/wix-headless/references/{ecom → astro/ecom}/COMPONENTS_CSS.md +6 -6
  57. package/skills/wix-headless/references/{forms → astro/forms}/CONTACT_FORM.md +18 -32
  58. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/COMPONENTS.md +3 -3
  59. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/PAGES.md +2 -2
  60. package/skills/wix-headless/references/{stores → astro/stores}/BACK_IN_STOCK.md +7 -7
  61. package/skills/wix-headless/references/{stores → astro/stores}/CATEGORY_PAGES.md +12 -13
  62. package/skills/wix-headless/references/{stores → astro/stores}/COMPONENTS_CSS.md +7 -7
  63. package/skills/wix-headless/references/{stores → astro/stores}/HOME_AND_NAV.md +11 -9
  64. package/skills/wix-headless/references/{stores → astro/stores}/PRODUCT_PAGES.md +11 -9
  65. package/skills/wix-headless/references/{stores → astro/stores}/SHARED_WIRING.md +8 -7
  66. package/skills/wix-headless/references/astro/templates/Footer.astro +27 -0
  67. package/skills/wix-headless/references/astro/templates/Layout.astro +132 -0
  68. package/skills/wix-headless/references/astro/templates/Navigation.astro +38 -0
  69. package/skills/wix-headless/references/astro/templates/astro.config.mjs +41 -0
  70. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +218 -0
  71. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +194 -0
  72. package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +84 -0
  73. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +25 -0
  74. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +50 -0
  75. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +45 -0
  76. package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +34 -0
  77. package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +74 -0
  78. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +111 -0
  79. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +413 -0
  80. package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +76 -0
  81. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +81 -0
  82. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +65 -0
  83. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +302 -0
  84. package/skills/wix-headless/references/astro/templates/global.css +286 -0
  85. package/skills/wix-headless/references/astro/templates/index.astro +56 -0
  86. package/skills/wix-headless/{templates → references/astro/templates}/stores/back-in-stock.ts +1 -1
  87. package/skills/wix-headless/{templates → references/astro/templates}/stores/components-stores.css +138 -33
  88. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +28 -19
  89. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +6 -6
  90. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +106 -0
  91. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +295 -0
  92. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +35 -232
  93. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +39 -8
  94. package/skills/wix-headless/references/commands/known-apps.json +2 -1
  95. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +142 -0
  96. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +88 -0
  97. package/skills/wix-headless/references/custom/blog/WIRING.md +52 -0
  98. package/skills/wix-headless/references/custom/bookings/WIRING.md +88 -0
  99. package/skills/wix-headless/references/custom/cms/WIRING.md +97 -0
  100. package/skills/wix-headless/references/custom/ecom/WIRING.md +67 -0
  101. package/skills/wix-headless/references/custom/forms/WIRING.md +101 -0
  102. package/skills/wix-headless/references/custom/stores/WIRING.md +80 -0
  103. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +12 -12
  104. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +6 -6
  105. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +13 -13
  106. package/skills/wix-headless/references/images/INSTRUCTIONS.md +58 -60
  107. package/skills/wix-headless/references/seed-recipes.md +49 -0
  108. package/skills/wix-headless/references/shared/AGENTS_MD.md +47 -0
  109. package/skills/wix-headless/references/shared/AUTHENTICATION.md +108 -0
  110. package/skills/wix-headless/references/shared/BUILD_NOISE.md +2 -2
  111. package/skills/wix-headless/references/shared/DESIGN_MD.md +85 -0
  112. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +41 -0
  113. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +40 -39
  114. package/skills/wix-headless/references/shared/IMPLEMENTER.md +92 -41
  115. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +3 -3
  116. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +32 -312
  117. package/skills/wix-headless/references/shared/STYLING.md +48 -21
  118. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +59 -29
  119. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +31 -28
  120. package/skills/wix-headless/references/verticals/_schema.json +31 -117
  121. package/skills/wix-headless/references/verticals/_schema.md +60 -150
  122. package/skills/wix-headless/references/verticals/blog.md +5 -99
  123. package/skills/wix-headless/references/verticals/bookings.md +41 -0
  124. package/skills/wix-headless/references/verticals/cms.md +9 -100
  125. package/skills/wix-headless/references/verticals/ecom.md +6 -77
  126. package/skills/wix-headless/references/verticals/forms.md +5 -85
  127. package/skills/wix-headless/references/verticals/gift-cards.md +6 -103
  128. package/skills/wix-headless/references/verticals/stores.md +10 -144
  129. package/skills/wix-headless/scripts/check-manifest.mjs +148 -11
  130. package/skills/wix-headless/scripts/compose.mjs +540 -0
  131. package/skills/wix-headless/scripts/emit-design-tokens.mjs +136 -53
  132. package/skills/wix-headless/scripts/flatten.sh +61 -0
  133. package/skills/wix-headless/scripts/generate-images.mjs +242 -0
  134. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +67 -43
  135. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +3 -3
  136. package/skills/wix-headless/scripts/scaffold.sh +113 -27
  137. package/skills/wix-headless/scripts/seed-utilities.sh +50 -12
  138. package/skills/wix-headless/scripts/write-decorative-json.mjs +62 -0
  139. package/skills/wix-manage/SKILL.md +4 -8
  140. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +2 -0
  141. package/skills/wix-manage/references/cms/cms-data-items-crud.md +4 -0
  142. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +2 -0
  143. package/skills/wix-manage/references/cms/cms-schema-management.md +4 -0
  144. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +10 -7
  145. package/skills/wix-manage/references/ecommerce/api-shipping.md +443 -0
  146. package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +5 -6
  147. package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +4 -5
  148. package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +4 -5
  149. package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +1 -1
  150. package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +1 -2
  151. package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +2 -2
  152. package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +4 -5
  153. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +7 -7
  154. package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +209 -72
  155. package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +9 -10
  156. package/skills/wix-manage/references/ecommerce/skill-graph.md +4 -4
  157. package/skills/wix-manage/references/forms/create-form.md +50 -0
  158. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +2 -0
  159. package/skills/wix-manage/references/stores/create-product-from-image.md +91 -38
  160. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +2 -0
  161. package/skills/wix-app/references/custom-element-widget/default-thumbnail.png +0 -0
  162. package/skills/wix-app/references/data-collection/extension-template.ts +0 -107
  163. package/skills/wix-headless/references/ORCHESTRATION.md +0 -269
  164. package/skills/wix-headless/references/commands/install-app.md +0 -42
  165. package/skills/wix-headless/references/commands/mcp-bootstrap.md +0 -28
  166. package/skills/wix-headless/references/designer/INSTRUCTIONS.md +0 -828
  167. package/skills/wix-headless/references/shared/MCP_PREFIX.md +0 -105
  168. package/skills/wix-headless/scripts/copy-utility-templates.mjs +0 -153
  169. package/skills/wix-headless/scripts/finalize-run-json.mjs +0 -141
  170. package/skills/wix-headless/scripts/init-site-json.mjs +0 -63
  171. package/skills/wix-headless/scripts/preview.sh +0 -45
  172. package/skills/wix-headless/scripts/release.sh +0 -73
  173. package/skills/wix-headless/templates/ecom/components-ecom.css +0 -215
  174. package/skills/wix-manage/references/events/list-events.md +0 -203
  175. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartBadge.tsx +0 -0
  176. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartView.tsx +0 -0
  177. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/cart.astro +0 -0
  178. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/discounts.ts +0 -0
  179. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/thank-you.astro +0 -0
  180. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/GiftCardPurchase.tsx +0 -0
  181. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_home-teaser-snippet.astro +0 -0
  182. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_nav-snippet.astro +0 -0
  183. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/components-gift-cards.css +0 -0
  184. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.astro +0 -0
  185. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.ts +0 -0
  186. /package/skills/wix-headless/{templates → references/astro/templates}/stores/AddToCartButton.tsx +0 -0
  187. /package/skills/wix-headless/{templates → references/astro/templates}/stores/BackInStockForm.tsx +0 -0
  188. /package/skills/wix-headless/{templates → references/astro/templates}/stores/CategoryRail.astro +0 -0
  189. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductCard.astro +0 -0
  190. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductPurchase.tsx +0 -0
  191. /package/skills/wix-headless/{templates → references/astro/templates}/stores/SeoTags.astro +0 -0
  192. /package/skills/wix-headless/{templates → references/astro/templates}/stores/categories.ts +0 -0
  193. /package/skills/wix-headless/{templates → references/astro/templates}/stores/category/[slug].astro +0 -0
  194. /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/[slug].astro +0 -0
  195. /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/index.astro +0 -0
@@ -0,0 +1,76 @@
1
+ ---
2
+ // src/pages/manage-booking.astro — per-booking manage page (view + cancel).
3
+ // Reached from the confirmation page or a shared manage link:
4
+ // /manage-booking?bookingId=<id>&service=<name>
5
+ //
6
+ // A visitor token can't list/cancel bookings (queryExtendedBookings/cancelBooking
7
+ // are APP/MEMBER identity). The supported visitor path is the ANONYMOUS ACTION
8
+ // TOKEN: the server elevates (app identity) to mint a per-booking token, then the
9
+ // no-auth bookingsGet/CancelBookingAnonymously methods act on it. Minting MUST be
10
+ // server-side; the token is then safe to hand to the browser island.
11
+ import Layout from "../layouts/Layout.astro";
12
+ import ManageBooking from "../components/ManageBooking";
13
+ import { auth } from "@wix/essentials";
14
+ import { bookings } from "@wix/bookings";
15
+
16
+ const bookingId = Astro.url.searchParams.get("bookingId");
17
+
18
+ let token: string | null = null;
19
+ let booking: any = null;
20
+ let allowed = { cancel: false, reschedule: false };
21
+ let error: string | null = null;
22
+
23
+ if (!bookingId) {
24
+ error = "No booking was specified.";
25
+ } else {
26
+ try {
27
+ const tokenRes = await auth.elevate(bookings.getAnonymousActionToken)(bookingId); // APP identity → elevate
28
+ token = tokenRes.token ?? null;
29
+ if (token) {
30
+ const res = await bookings.bookingsGetBookingAnonymously(token); // no auth
31
+ booking = res.booking ?? null;
32
+ allowed = {
33
+ cancel: !!res.allowedAnonymousActions?.cancel,
34
+ reschedule: !!res.allowedAnonymousActions?.reschedule,
35
+ };
36
+ }
37
+ } catch (err) {
38
+ console.error("[manage-booking] load failed:", err);
39
+ error = "We couldn't load this booking — the link may have expired.";
40
+ }
41
+ }
42
+
43
+ const serviceName =
44
+ booking?.bookedEntity?.title ?? Astro.url.searchParams.get("service") ?? "your booking";
45
+ const startDate =
46
+ (booking?.startDate as string | undefined) ??
47
+ booking?.bookedEntity?.slot?.startDate ??
48
+ Astro.url.searchParams.get("startDate") ??
49
+ null;
50
+ const status = (booking?.status as string | undefined) ?? null;
51
+ const revision = (booking?.revision as string | undefined) ?? "";
52
+ ---
53
+
54
+ <Layout title="Manage your booking">
55
+ <section class="container-reading px-lg py-4xl">
56
+ <a href="/services" class="inline-block text-sm text-mute hover:text-ink mb-xl">← All services</a>
57
+ <hr class="editorial-rule w-16 ml-0" />
58
+ <h1 class="font-display text-4xl md:text-5xl leading-tight mb-xl">Manage your booking</h1>
59
+
60
+ {
61
+ error ? (
62
+ <p class="booking-error">{error}</p>
63
+ ) : (
64
+ <ManageBooking
65
+ token={token}
66
+ revision={revision}
67
+ serviceName={serviceName}
68
+ startDate={startDate}
69
+ status={status}
70
+ canCancel={allowed.cancel}
71
+ client:only="react"
72
+ />
73
+ )
74
+ }
75
+ </section>
76
+ </Layout>
@@ -0,0 +1,81 @@
1
+ ---
2
+ // src/pages/services/[slug].astro — SSR service detail + client-side booking flow.
3
+ // Two dirs deep inside src/ → import with ../../ (not three ../).
4
+ import Layout from "../../layouts/Layout.astro";
5
+ import SeoTags from "../../components/SeoTags.astro";
6
+ import ServiceBookingFlow from "../../components/ServiceBookingFlow";
7
+ import { services } from "@wix/bookings";
8
+ import { auth } from "@wix/essentials";
9
+
10
+ const { slug } = Astro.params;
11
+
12
+ let service: any = null;
13
+ try {
14
+ // Ambient SSR via @wix/essentials; query BUILDER (object form returns 0).
15
+ // Fetch the small catalog and match the slug in JS (robust without a
16
+ // nested-field `mainSlug.name` builder filter).
17
+ const result = await auth.elevate(services.queryServices)().limit(100).find();
18
+ service =
19
+ (result.items ?? []).find(
20
+ (s: any) => (s.mainSlug?.name ?? s.supportedSlugs?.[0]?.name) === slug,
21
+ ) ?? null;
22
+ } catch (err) {
23
+ console.error(`[services/${slug}] detail query failed:`, err);
24
+ }
25
+
26
+ if (!service) return Astro.redirect("/services", 302);
27
+
28
+ const formatPrice = (p: any): string | undefined => {
29
+ if (!p || p.rateType === "NO_FEE") return undefined;
30
+ if (p.rateType === "FIXED" && p.fixed?.price) {
31
+ const { value, currency } = p.fixed.price;
32
+ if (!value) return undefined;
33
+ return new Intl.NumberFormat("en-US", { style: "currency", currency: currency ?? "USD" }).format(Number(value));
34
+ }
35
+ return undefined;
36
+ };
37
+ const price = formatPrice(service.payment);
38
+ const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0] ?? 60;
39
+ const imageUrl = service.media?.mainMedia?.image?.url ?? service.media?.items?.[0]?.image?.url;
40
+ const serviceName = service.name ?? "Service";
41
+ const seoTags = service.seoData?.tags ?? [];
42
+ ---
43
+
44
+ <!--
45
+ Width: use `container-reading` (max-width: var(--container-prose) + auto margins),
46
+ NOT a bare `max-w-<size>` — in this Tailwind v4 setup an un-emitted max-w-<size>
47
+ key falls back to the spacing scale (e.g. max-w-4xl → ~7rem, one word per line).
48
+ -->
49
+ <Layout title={serviceName} description={service.tagLine ?? service.description}>
50
+ <SeoTags tags={seoTags} slot="head" />
51
+ <section class="container-reading px-lg py-4xl">
52
+ <a href="/services" class="inline-block text-sm text-mute hover:text-ink mb-xl">← All services</a>
53
+
54
+ <!-- Optional banner image — only when the service has media (image-less is the
55
+ common seed case; rendering a fixed placeholder collapses the layout). -->
56
+ {imageUrl && (
57
+ <img src={imageUrl} alt={serviceName} class="w-full h-64 object-cover rounded-lg mb-xl" />
58
+ )}
59
+
60
+ <div class="flex flex-col gap-md mb-3xl">
61
+ <hr class="editorial-rule w-16 ml-0" />
62
+ <h1 class="font-display text-4xl md:text-5xl leading-tight">{serviceName}</h1>
63
+ {service.tagLine && <p class="text-lg text-mute leading-relaxed max-w-prose">{service.tagLine}</p>}
64
+ <div class="flex items-center gap-md text-sm text-mute">
65
+ <span>{duration} min</span>
66
+ {price && <span class="text-ink font-medium">{price}</span>}
67
+ </div>
68
+ {service.description && <p class="text-ink leading-relaxed max-w-prose">{service.description}</p>}
69
+ </div>
70
+
71
+ <section class="border-t border-rule pt-2xl">
72
+ <h2 class="font-display text-3xl leading-tight mb-xl">Book this service</h2>
73
+ <ServiceBookingFlow
74
+ serviceId={service._id}
75
+ serviceName={serviceName}
76
+ serviceType={service.type ?? "APPOINTMENT"}
77
+ client:only="react"
78
+ />
79
+ </section>
80
+ </section>
81
+ </Layout>
@@ -0,0 +1,65 @@
1
+ ---
2
+ // src/pages/services/index.astro — SSR services listing grid (APPOINTMENT or CLASS).
3
+ // One dir deep inside src/pages → ../../ reaches src/. Adapt copy/headings to the
4
+ // brand; keep the SSR data wiring.
5
+ import Layout from "../../layouts/Layout.astro";
6
+ import ServiceCard from "../../components/ServiceCard.astro";
7
+ import { services } from "@wix/bookings";
8
+ import { auth } from "@wix/essentials";
9
+
10
+ let serviceList: any[] = [];
11
+ try {
12
+ // Ambient @wix/astro SSR client via @wix/essentials (same pattern as CMS pages).
13
+ // Do NOT build createClient({ auth: OAuthStrategy({ clientId: import.meta.env.* }) }) —
14
+ // that env var is client-only and undefined at server render → 500.
15
+ // queryServices uses the query BUILDER; the object form returns 0 items.
16
+ const result = await auth.elevate(services.queryServices)().limit(100).find();
17
+ serviceList = (result.items ?? []).filter((s: any) => !s.hidden);
18
+ } catch (err) {
19
+ console.error("[services] listing query failed:", err);
20
+ }
21
+
22
+ const getSlug = (s: any): string => s.mainSlug?.name ?? s.supportedSlugs?.[0]?.name ?? s._id;
23
+ const formatPrice = (s: any): string | undefined => {
24
+ const p = s.payment;
25
+ if (!p || p.rateType === "NO_FEE") return undefined;
26
+ if (p.rateType === "FIXED" && p.fixed?.price) {
27
+ const { value, currency } = p.fixed.price;
28
+ if (!value) return undefined;
29
+ return new Intl.NumberFormat("en-US", { style: "currency", currency: currency ?? "USD" }).format(Number(value));
30
+ }
31
+ return undefined;
32
+ };
33
+ const getDuration = (s: any): number => s.schedule?.availabilityConstraints?.sessionDurations?.[0] ?? 60;
34
+ ---
35
+
36
+ <Layout title="Services">
37
+ <section class="max-w-6xl mx-auto px-lg py-4xl">
38
+ <header class="flex flex-col gap-md max-w-prose mb-3xl">
39
+ <hr class="editorial-rule w-16 ml-0" />
40
+ <h1 class="font-display text-4xl md:text-5xl leading-tight">Our Services</h1>
41
+ <!-- Optional brand tagline -->
42
+ </header>
43
+
44
+ {
45
+ serviceList.length === 0 ? (
46
+ <p class="text-mute">Services coming soon — check back shortly.</p>
47
+ ) : (
48
+ <div class="service-grid">
49
+ {serviceList.map((s) => (
50
+ <ServiceCard
51
+ id={s._id}
52
+ slug={getSlug(s)}
53
+ name={s.name ?? "Untitled"}
54
+ tagLine={s.tagLine ?? s.description}
55
+ durationMinutes={getDuration(s)}
56
+ price={formatPrice(s)}
57
+ imageUrl={s.media?.mainMedia?.image?.url ?? s.media?.items?.[0]?.image?.url}
58
+ type={s.type ?? "APPOINTMENT"}
59
+ />
60
+ ))}
61
+ </div>
62
+ )
63
+ }
64
+ </section>
65
+ </Layout>
@@ -0,0 +1,302 @@
1
+ /* src/styles/components-ecom.css
2
+ *
3
+ * Ecom-pack component CSS. Owned by the ecom Phase 3 components agent.
4
+ *
5
+ * This file is the SINGLE source of truth for ecom-component classes.
6
+ * The designer's global.css does NOT publish .cart-summary, .cart-total,
7
+ * .cart-empty, or .checkout-btn — those all live here. See
8
+ * references/shared/STYLING.md § "Component-specific CSS is owned by the
9
+ * component, not the designer".
10
+ *
11
+ * Class groups in this file:
12
+ * - .cart-summary / .cart-total / .cart-empty / .checkout-btn → /cart page summary panel and empty state
13
+ * - .cart-item / .cart-item-* / .qty-* → CartView.tsx line items
14
+ * - .cart-discount / .cart-applied-discounts → CartView.tsx discount rows
15
+ * - .cart-badge / .cart-badge-* → CartBadge.tsx (mounted in Navigation)
16
+ *
17
+ * Rules use direct CSS (`var(--token)`) only — no `@apply`. Tailwind v4
18
+ * `@apply` in a non-entrypoint file only resolves utilities whose theme keys
19
+ * the designer's `@theme` block declares. Earlier revisions of this template
20
+ * used `@apply text-bark/70 bg-tobacco …` (stale token vocabulary) and
21
+ * `@apply size-6` (built-in utility that needs `@reference` to be visible
22
+ * here), and both classes built-failed against current designer output.
23
+ * Direct `var(--color-ink)` / `var(--spacing-sm)` calls work against any
24
+ * designer-published token set as long as the core scales exist (they always
25
+ * do). Use fallback chains (`var(--color-paper-warm, var(--color-paper, var(--color-cream)))`)
26
+ * for sometimes-published tokens.
27
+ */
28
+
29
+ /* =========================================================================
30
+ Cart summary panel + total + checkout button + empty state.
31
+ Owned here, NOT in global.css.
32
+ ========================================================================= */
33
+ .cart-summary {
34
+ background: var(--color-paper-warm, var(--color-paper, var(--color-cream)));
35
+ border: 1px solid var(--color-rule);
36
+ padding: var(--spacing-xl);
37
+ border-radius: var(--radius-md);
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: var(--spacing-sm);
41
+ }
42
+ @media (min-width: 768px) {
43
+ .cart-summary {
44
+ position: sticky;
45
+ top: var(--spacing-xl);
46
+ }
47
+ }
48
+
49
+ .cart-empty {
50
+ text-align: center;
51
+ padding-block: var(--spacing-3xl);
52
+ color: var(--color-mute);
53
+ }
54
+ .cart-empty .checkout-btn {
55
+ display: inline-flex;
56
+ width: auto;
57
+ max-width: 18rem;
58
+ }
59
+
60
+ /* .checkout-btn — primary CTA, full-width inside .cart-summary. Self-contained;
61
+ does NOT @apply the designer's `btn` utility (Tailwind v4 only @applies
62
+ utilities declared with `@utility`, and the designer may or may not have
63
+ declared it under that exact name). Composing the same visual shape inline
64
+ keeps this file portable across designer vocabularies. */
65
+ .checkout-btn {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ padding: var(--spacing-md) var(--spacing-lg);
70
+ font-family: var(--font-display);
71
+ font-size: 0.9375rem;
72
+ font-weight: 500;
73
+ background: var(--color-ink);
74
+ color: var(--color-paper);
75
+ border: 1px solid var(--color-ink);
76
+ border-radius: var(--radius-sm);
77
+ cursor: pointer;
78
+ width: 100%;
79
+ margin-top: var(--spacing-md);
80
+ transition: background-color 150ms ease, box-shadow 150ms ease;
81
+ }
82
+ .checkout-btn:hover:not(:disabled) {
83
+ background: var(--color-ink-soft, var(--color-ink));
84
+ }
85
+ .checkout-btn:disabled {
86
+ opacity: 0.5;
87
+ cursor: not-allowed;
88
+ }
89
+
90
+ /* --- Cart badge icon (CartBadge.tsx) --- */
91
+ /* CartBadge.tsx renders an inline SVG bag icon + optional count span. */
92
+ .cart-badge {
93
+ position: relative;
94
+ display: inline-flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ }
98
+ .cart-badge-icon {
99
+ width: 1.5rem;
100
+ height: 1.5rem;
101
+ flex-shrink: 0;
102
+ }
103
+ .cart-badge-count {
104
+ position: absolute;
105
+ top: -0.25rem;
106
+ right: -0.5rem;
107
+ min-width: 1.25rem;
108
+ height: 1.25rem;
109
+ padding: 0 0.25rem;
110
+ display: inline-flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ border-radius: 9999px;
114
+ background: var(--color-ink);
115
+ color: var(--color-paper);
116
+ font-family: var(--font-body);
117
+ font-size: 0.75rem;
118
+ font-variant-numeric: tabular-nums;
119
+ line-height: 1;
120
+ }
121
+
122
+ /* --- Cart line items (CartView.tsx) --- */
123
+ .cart-item {
124
+ display: flex;
125
+ flex-direction: row;
126
+ gap: var(--spacing-md);
127
+ }
128
+ .cart-item-image {
129
+ flex-shrink: 0;
130
+ }
131
+ .cart-item-image-link {
132
+ display: block;
133
+ flex-shrink: 0;
134
+ cursor: pointer;
135
+ }
136
+ .cart-item-image-link:hover .cart-item-image,
137
+ .cart-item-image-link:focus-visible .cart-item-image {
138
+ border-color: var(--color-ink);
139
+ }
140
+ .cart-item-name-link {
141
+ color: inherit;
142
+ cursor: pointer;
143
+ text-decoration: none;
144
+ border-bottom: 1px solid var(--color-mute);
145
+ padding-bottom: 1px;
146
+ transition: color 150ms ease, border-color 150ms ease;
147
+ }
148
+ .cart-item-name-link:hover,
149
+ .cart-item-name-link:focus-visible {
150
+ color: var(--color-accent);
151
+ outline: none;
152
+ border-bottom-color: var(--color-accent);
153
+ }
154
+ .cart-item-info {
155
+ flex: 1;
156
+ min-width: 0;
157
+ }
158
+
159
+ /* --- Description lines & modifiers --- */
160
+ .cart-item-option {
161
+ font-family: var(--font-body);
162
+ font-size: 0.875rem;
163
+ color: var(--color-ink-soft, var(--color-mute));
164
+ margin: 0.125rem 0;
165
+ }
166
+ .cart-item-modifiers {
167
+ margin-top: 0.25rem;
168
+ }
169
+
170
+ /* --- Quantity stepper --- */
171
+ .cart-item-qty {
172
+ display: inline-flex;
173
+ align-items: center;
174
+ gap: var(--spacing-sm);
175
+ margin-top: var(--spacing-sm);
176
+ width: fit-content;
177
+ }
178
+ .qty-btn {
179
+ width: 1.75rem;
180
+ height: 1.75rem;
181
+ display: inline-flex;
182
+ align-items: center;
183
+ justify-content: center;
184
+ border: 1px solid var(--color-ink);
185
+ border-radius: var(--radius-sm);
186
+ background: transparent;
187
+ color: var(--color-ink);
188
+ cursor: pointer;
189
+ font-family: var(--font-body);
190
+ font-size: 1rem;
191
+ line-height: 1;
192
+ }
193
+ .qty-btn:disabled {
194
+ opacity: 0.3;
195
+ cursor: not-allowed;
196
+ }
197
+ .qty-value {
198
+ font-family: var(--font-body);
199
+ font-size: 0.875rem;
200
+ min-width: 1.5rem;
201
+ text-align: center;
202
+ font-variant-numeric: tabular-nums;
203
+ }
204
+
205
+ /* --- Unavailable state --- */
206
+ .cart-item-unavailable {
207
+ font-family: var(--font-body);
208
+ font-size: 0.875rem;
209
+ font-weight: 500;
210
+ color: var(--color-error, #c0392b);
211
+ }
212
+
213
+ /* --- Price + remove --- */
214
+ .cart-item-actions {
215
+ display: flex;
216
+ flex-direction: column;
217
+ align-items: flex-end;
218
+ gap: 0.25rem;
219
+ flex-shrink: 0;
220
+ }
221
+ .cart-item-prices {
222
+ display: flex;
223
+ align-items: center;
224
+ gap: var(--spacing-sm);
225
+ }
226
+ .cart-item-full-price {
227
+ font-family: var(--font-body);
228
+ font-size: 0.875rem;
229
+ color: var(--color-ink-soft, var(--color-mute));
230
+ text-decoration: line-through;
231
+ }
232
+ .cart-item-unit-price {
233
+ font-family: var(--font-body);
234
+ font-weight: 600;
235
+ white-space: nowrap;
236
+ }
237
+ .cart-item-line-total {
238
+ font-family: var(--font-body);
239
+ font-size: 0.75rem;
240
+ color: var(--color-ink-soft, var(--color-mute));
241
+ margin: 0;
242
+ }
243
+ .cart-item-remove {
244
+ background: transparent;
245
+ border: none;
246
+ font-family: var(--font-body);
247
+ font-size: 0.875rem;
248
+ color: var(--color-mute);
249
+ cursor: pointer;
250
+ text-decoration: underline;
251
+ }
252
+
253
+ /* --- Cart summary: discount + total rows ---
254
+ Subtotal uses the designer's .cart-subtotal contract class in global.css.
255
+ These rows appear only when priceSummary exposes a discount or a total
256
+ that differs from subtotal (see CartView.tsx extractSummary). */
257
+ .cart-discount {
258
+ display: flex;
259
+ align-items: baseline;
260
+ justify-content: space-between;
261
+ gap: var(--spacing-sm);
262
+ font-family: var(--font-body);
263
+ font-size: 0.875rem;
264
+ margin-top: var(--spacing-sm);
265
+ }
266
+ .cart-discount-name {
267
+ font-size: 0.75rem;
268
+ color: var(--color-ink-soft, var(--color-mute));
269
+ }
270
+ .cart-discount-amount {
271
+ white-space: nowrap;
272
+ }
273
+ .cart-total {
274
+ border-top: 1px solid var(--color-rule);
275
+ padding-top: var(--spacing-md);
276
+ margin-top: var(--spacing-sm);
277
+ font-family: var(--font-display);
278
+ font-size: 1.25rem;
279
+ font-weight: 500;
280
+ display: flex;
281
+ justify-content: space-between;
282
+ align-items: baseline;
283
+ }
284
+
285
+ /* Applied-discounts row — name-only (no amount) for cases where the API
286
+ reports a promo NAME via appliedDiscounts[].discountName but no cart-level
287
+ amount in priceSummary.discount (common for line-item automatic discounts
288
+ — the savings already show inline via fullPrice/price strikethrough). */
289
+ .cart-applied-discounts {
290
+ display: flex;
291
+ align-items: baseline;
292
+ justify-content: space-between;
293
+ gap: var(--spacing-sm);
294
+ font-family: var(--font-body);
295
+ font-size: 0.875rem;
296
+ margin-top: var(--spacing-sm);
297
+ }
298
+ .cart-applied-discounts-name {
299
+ text-align: right;
300
+ color: var(--color-ink-soft, var(--color-mute));
301
+ font-style: italic;
302
+ }