@wix/agent-skills 1.13.0 → 1.15.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.
- package/README.md +4 -1
- package/package.json +1 -1
- package/skills/README.md +23 -0
- package/skills/wix-app/SKILL.md +10 -2
- package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
- package/skills/wix-app/references/CODE_QUALITY.md +1 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
- package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +23 -9
- package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
- package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
- package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
- package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
- package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
- package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
- package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
- package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
- package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
- package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +17 -105
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +15 -15
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
- package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
- package/skills/wix-auth/SKILL.md +12 -0
- package/skills/wix-auth/references/device-flow.md +140 -0
- package/skills/wix-docs/SKILL.md +188 -0
- package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
- package/skills/wix-docs/references/EXTRACTING.md +120 -0
- package/skills/wix-headless/SKILL.md +84 -130
- package/skills/wix-headless/entry/bootstrap.mjs +93 -0
- package/skills/wix-headless/entry/skill.md +78 -0
- package/skills/wix-headless/references/AI_FEATURES.md +178 -0
- package/skills/wix-headless/references/CAPABILITIES.md +84 -0
- package/skills/wix-headless/references/DISCOVERY.md +34 -56
- package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
- package/skills/wix-headless/references/FEEDBACK.md +118 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +97 -0
- package/skills/wix-headless/references/SEED.md +44 -188
- package/skills/wix-headless/references/SETUP.md +32 -103
- package/skills/wix-headless/references/astro.md +98 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +173 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
- package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
- package/skills/wix-headless/references/inline-recipes/setup-bookings.md +228 -0
- package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
- package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +176 -0
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +153 -0
- package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +161 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +117 -0
- package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
- package/skills/wix-headless/references/managed/CONNECT.md +74 -0
- package/skills/wix-headless/references/managed/CREATE.md +130 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +57 -0
- package/skills/wix-headless/references/non-astro.md +59 -0
- package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
- package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +7 -0
- package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +63 -0
- package/skills/wix-manage/SKILL.md +24 -1
- package/skills/wix-manage/references/analytics/query-site-analytics.md +241 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +265 -0
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +436 -0
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +209 -0
- package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +51 -0
- package/skills/wix-vibe-headless/SKILL.md +136 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
- package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +153 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
- package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +215 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
- package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
- package/skills/wix-vibe-headless/references/events/wix-events-browse.js +135 -0
- package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
- package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
- package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +194 -0
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
- package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
- package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
- package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +155 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
- package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +142 -0
- package/skills/wix-headless/references/BUILD-astro.md +0 -244
- package/skills/wix-headless/references/BUILD-own-build.md +0 -167
- package/skills/wix-headless/references/BUILD.md +0 -105
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
- package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
- package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
- package/skills/wix-headless/references/PLAN-connect.md +0 -19
- package/skills/wix-headless/references/PLAN-create.md +0 -17
- package/skills/wix-headless/references/PLAN.md +0 -92
- package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
- package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
- package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
- package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
- package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
- package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
- package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
- package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
- package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
- package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
- package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
- package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
- package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
- package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
- package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
- package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
- package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
- package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
- package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
- package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
- package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
- package/skills/wix-headless/references/astro/templates/Footer.astro +0 -27
- package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
- package/skills/wix-headless/references/astro/templates/Navigation.astro +0 -38
- package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
- package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
- package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
- package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
- package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
- package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
- package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
- package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
- package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
- package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
- package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
- package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
- package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
- package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
- package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
- package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
- package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
- package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
- package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
- package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
- package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
- package/skills/wix-headless/references/astro/templates/global.css +0 -288
- package/skills/wix-headless/references/astro/templates/index.astro +0 -56
- package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
- package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
- package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
- package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
- package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
- package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
- package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
- package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
- package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
- package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
- package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
- package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
- package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
- package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
- package/skills/wix-headless/references/bookings/FLOW.md +0 -246
- package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
- package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
- package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
- package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
- package/skills/wix-headless/references/commands/known-apps.json +0 -6
- package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
- package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
- package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
- package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
- package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
- package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
- package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
- package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
- package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
- package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
- package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
- package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
- package/skills/wix-headless/references/seed-recipes.md +0 -49
- package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
- package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
- package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
- package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
- package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
- package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
- package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
- package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
- package/skills/wix-headless/references/shared/STYLING.md +0 -141
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
- package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
- package/skills/wix-headless/references/verticals/_schema.json +0 -74
- package/skills/wix-headless/references/verticals/_schema.md +0 -95
- package/skills/wix-headless/references/verticals/blog.md +0 -31
- package/skills/wix-headless/references/verticals/bookings.md +0 -41
- package/skills/wix-headless/references/verticals/cms.md +0 -30
- package/skills/wix-headless/references/verticals/ecom.md +0 -25
- package/skills/wix-headless/references/verticals/forms.md +0 -26
- package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
- package/skills/wix-headless/references/verticals/stores.md +0 -33
- package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
- package/skills/wix-headless/scripts/compose.mjs +0 -525
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
- package/skills/wix-headless/scripts/flatten.sh +0 -61
- package/skills/wix-headless/scripts/generate-images.mjs +0 -242
- package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -148
- package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
- package/skills/wix-headless/scripts/scaffold.sh +0 -139
- package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
- package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
- package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
- package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
- package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import Layout from "../../layouts/Layout.astro";
|
|
3
|
-
import ProductCard from "../../components/ProductCard.astro";
|
|
4
|
-
import CategoryRail from "../../components/CategoryRail.astro";
|
|
5
|
-
import { productsV3 } from "@wix/stores";
|
|
6
|
-
import { listStoreCategories } from "../../utils/categories";
|
|
7
|
-
|
|
8
|
-
const PAGE_SIZE = 24;
|
|
9
|
-
const cursor = Astro.url.searchParams.get("cursor") ?? undefined;
|
|
10
|
-
|
|
11
|
-
let productList: any[] = [];
|
|
12
|
-
let nextCursor: string | null = null;
|
|
13
|
-
let prevCursor: string | null = null;
|
|
14
|
-
try {
|
|
15
|
-
let builder = productsV3
|
|
16
|
-
.queryProducts({ fields: ["CURRENCY"] })
|
|
17
|
-
.limit(PAGE_SIZE);
|
|
18
|
-
if (cursor) builder = builder.skipTo(cursor);
|
|
19
|
-
const result = await builder.find();
|
|
20
|
-
productList = result.items ?? [];
|
|
21
|
-
nextCursor = result.cursors?.next ?? null;
|
|
22
|
-
prevCursor = result.cursors?.prev ?? null;
|
|
23
|
-
} catch (err) {
|
|
24
|
-
console.error("[products] listing query failed:", err);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Drop the Wix Gift Card app's auto-mirror products. When the app is enabled,
|
|
28
|
-
// it provisions 5 DIGITAL Stores products (one per denomination) tagged with
|
|
29
|
-
// the "Gift Card" ribbon. Buying them yields a dud line item that does not
|
|
30
|
-
// trigger gift-card issuance — the customer-facing surface for gift cards
|
|
31
|
-
// is /gift-cards (gift-cards pack), not /products.
|
|
32
|
-
productList = productList.filter((p) => p.ribbon?.name !== "Gift Card");
|
|
33
|
-
|
|
34
|
-
const allCategories = await listStoreCategories();
|
|
35
|
-
|
|
36
|
-
// Payload for the client-side AddProductImpression event.
|
|
37
|
-
const impressionContents = productList.map((p, index) => ({
|
|
38
|
-
id: p._id,
|
|
39
|
-
name: p.name,
|
|
40
|
-
price: Number(p.actualPriceRange?.minValue?.amount ?? 0),
|
|
41
|
-
currency: p.actualPriceRange?.minValue?.currency ?? undefined,
|
|
42
|
-
position: index + 1,
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
function pageHref(c: string | null): string | null {
|
|
46
|
-
if (!c) return null;
|
|
47
|
-
const params = new URLSearchParams();
|
|
48
|
-
params.set("cursor", c);
|
|
49
|
-
return `/products?${params.toString()}`;
|
|
50
|
-
}
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
<Layout title="Products">
|
|
54
|
-
<section class="page-header">
|
|
55
|
-
<div class="container-page">
|
|
56
|
-
<h1 class="page-header-title">Our Products</h1>
|
|
57
|
-
</div>
|
|
58
|
-
</section>
|
|
59
|
-
|
|
60
|
-
<CategoryRail
|
|
61
|
-
categories={allCategories}
|
|
62
|
-
activeSlug={null}
|
|
63
|
-
prevHref={pageHref(prevCursor)}
|
|
64
|
-
nextHref={pageHref(nextCursor)}
|
|
65
|
-
/>
|
|
66
|
-
|
|
67
|
-
<section class="section-pad">
|
|
68
|
-
<div class="container-page">
|
|
69
|
-
{productList.length === 0 ? (
|
|
70
|
-
<p class="products-empty">
|
|
71
|
-
No products yet — please check back shortly.
|
|
72
|
-
</p>
|
|
73
|
-
) : (
|
|
74
|
-
<div class="product-grid">
|
|
75
|
-
{productList.map((product, idx) => (
|
|
76
|
-
<ProductCard product={product} index={idx} />
|
|
77
|
-
))}
|
|
78
|
-
</div>
|
|
79
|
-
)}
|
|
80
|
-
</div>
|
|
81
|
-
</section>
|
|
82
|
-
</Layout>
|
|
83
|
-
|
|
84
|
-
<script
|
|
85
|
-
type="application/json"
|
|
86
|
-
id="analytics-impressions"
|
|
87
|
-
is:inline
|
|
88
|
-
set:html={JSON.stringify(impressionContents)}
|
|
89
|
-
/>
|
|
90
|
-
<script>
|
|
91
|
-
import { trackEvent } from "../../utils/analytics";
|
|
92
|
-
function fireImpressions() {
|
|
93
|
-
const el = document.getElementById("analytics-impressions");
|
|
94
|
-
if (!el?.textContent) return;
|
|
95
|
-
trackEvent("AddProductImpression", {
|
|
96
|
-
contents: JSON.parse(el.textContent),
|
|
97
|
-
origin: "Product Listing",
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
document.addEventListener("astro:page-load", fireImpressions);
|
|
101
|
-
</script>
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# Recipe: Seed Initial Blog Posts via REST
|
|
2
|
-
|
|
3
|
-
Create 3 on-brand blog posts so the blog has real content on first preview.
|
|
4
|
-
|
|
5
|
-
> **Critical Rules — Read Before Starting**
|
|
6
|
-
> 1. **Seed content is required** — a blog with zero posts on first preview is a build failure. Do not skip this section.
|
|
7
|
-
> 2. **Re-publish after PATCH** — updating a published post sets `hasUnpublishedChanges: true`. Call `publish` again or visitors see the old version.
|
|
8
|
-
> 3. **Use `@wix/blog`, not `@wix/data`** — blog posts are NOT CMS collections.
|
|
9
|
-
> 4. **`RICH_CONTENT` fieldset required** — without it, `post.richContent` is undefined and rendering fails.
|
|
10
|
-
|
|
11
|
-
> **Required in the Seed phase.** When dispatched with `Scope: seed`, the CLI-minted REST token and discovery context are guaranteed available. Always execute this section — do not skip. A blog with zero posts on first preview is a build failure.
|
|
12
|
-
>
|
|
13
|
-
> **Skip only for standalone invocations** where the Blog skill is invoked directly AND CLI token mint fails after recovery. In that case, the user can create posts manually in the Wix dashboard.
|
|
14
|
-
|
|
15
|
-
## Step 0: Ensure Blog App Is Installed
|
|
16
|
-
|
|
17
|
-
Before querying blog data, verify the Blog app is installed:
|
|
18
|
-
|
|
19
|
-
1. **Probe** — `REST: POST https://www.wixapis.com/blog/v3/posts/query`
|
|
20
|
-
2. **If the API returns a "REQUIRED_APP_NOT_INSTALLED" error** → install the Wix Blog app:
|
|
21
|
-
```
|
|
22
|
-
REST: POST https://www.wixapis.com/apps-installer-service/v1/app-instance/install
|
|
23
|
-
body: {
|
|
24
|
-
"tenant": { "tenantType": "SITE", "id": "<siteId>" },
|
|
25
|
-
"appInstance": { "appDefId": "14bcded7-0066-7c35-14d7-466cb3f09103", "enabled": true }
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
> Translate this prose-HTTP form into the full `curl` tool-call shape — pass `body` as JSON in `-d` (NOT a stringified JSON). See `../shared/AUTHENTICATION.md` for the standard REST headers.
|
|
29
|
-
|
|
30
|
-
Then retry the probe query to confirm installation succeeded.
|
|
31
|
-
3. **If the probe succeeds** → proceed to Step 1.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Step 1: Get Member ID
|
|
36
|
-
|
|
37
|
-
Blog posts require a `memberId` (the post author). Fetch the first site member:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
REST: GET https://www.wixapis.com/members/v1/members
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Extract the first member's `_id` from the response. This will be used as the author for all posts.
|
|
44
|
-
|
|
45
|
-
## Step 2: Design 3 On-Brand Blog Posts
|
|
46
|
-
|
|
47
|
-
No API call — use discovery context (business type, brand name, tone, industry) to plan 3 posts:
|
|
48
|
-
|
|
49
|
-
- Titles, summaries, and rich content appropriate for the business
|
|
50
|
-
- Content should demonstrate different Ricos node types (paragraphs, headings, code blocks, lists, blockquotes)
|
|
51
|
-
- Posts should feel like real content a reader would engage with, not placeholder text
|
|
52
|
-
|
|
53
|
-
**Post design guidelines by business type:**
|
|
54
|
-
|
|
55
|
-
| Business Type | Post 1 | Post 2 | Post 3 |
|
|
56
|
-
|--------------|--------|--------|--------|
|
|
57
|
-
| AI / tech blog | Architecture deep-dive | Practical comparison (X vs Y) | Lessons learned / field report |
|
|
58
|
-
| Skincare / beauty | Ingredient science spotlight | Routine guide (morning/evening) | Myth-busting or FAQ |
|
|
59
|
-
| Food / restaurant | Behind the recipe | Seasonal menu spotlight | Sourcing / farm-to-table story |
|
|
60
|
-
| Fitness / wellness | Training methodology breakdown | Nutrition or recovery guide | Client transformation story |
|
|
61
|
-
| Fashion / retail | Trend analysis | Styling guide | Behind the brand / process |
|
|
62
|
-
| General business | Industry insight | How-to guide | Company news / milestone |
|
|
63
|
-
|
|
64
|
-
Adapt titles, tone, and content to match the brand's voice and style.
|
|
65
|
-
|
|
66
|
-
## Step 3: Create Posts
|
|
67
|
-
|
|
68
|
-
For each post, call `POST /blog/v3/draft-posts` with `publish: true` to create and immediately publish.
|
|
69
|
-
|
|
70
|
-
> Do not include the `media` field — cover images are handled separately by the image agent after post creation.
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
REST: POST https://www.wixapis.com/blog/v3/draft-posts
|
|
74
|
-
body: {
|
|
75
|
-
"draftPost": {
|
|
76
|
-
"title": "<Post Title>",
|
|
77
|
-
"memberId": "<member-id-from-step-1>",
|
|
78
|
-
"richContent": {
|
|
79
|
-
"nodes": [
|
|
80
|
-
{
|
|
81
|
-
"type": "HEADING",
|
|
82
|
-
"id": "<unique-id>",
|
|
83
|
-
"nodes": [
|
|
84
|
-
{ "type": "TEXT", "id": "", "nodes": [], "textData": { "text": "Section Heading", "decorations": [] } }
|
|
85
|
-
],
|
|
86
|
-
"headingData": { "level": 2 }
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"type": "PARAGRAPH",
|
|
90
|
-
"id": "<unique-id>",
|
|
91
|
-
"nodes": [
|
|
92
|
-
{ "type": "TEXT", "id": "", "nodes": [], "textData": { "text": "Paragraph content here.", "decorations": [] } }
|
|
93
|
-
],
|
|
94
|
-
"paragraphData": {}
|
|
95
|
-
}
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
"publish": true
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Ricos JSON Node Reference
|
|
104
|
-
|
|
105
|
-
Use these node types to build rich, varied content:
|
|
106
|
-
|
|
107
|
-
| Node Type | Structure | Use For |
|
|
108
|
-
|-----------|-----------|---------|
|
|
109
|
-
| `PARAGRAPH` | `{ type: "PARAGRAPH", id, nodes: [TEXT], paragraphData: {} }` | Body text |
|
|
110
|
-
| `HEADING` | `{ type: "HEADING", id, nodes: [TEXT], headingData: { level: 2 } }` | Section headers (level 2–4) |
|
|
111
|
-
| `CODE_BLOCK` | `{ type: "CODE_BLOCK", id, nodes: [TEXT], codeBlockData: { language: "javascript" } }` | Code snippets |
|
|
112
|
-
| `BULLETED_LIST` | `{ type: "BULLETED_LIST", id, nodes: [LIST_ITEM] }` | Unordered lists |
|
|
113
|
-
| `LIST_ITEM` | `{ type: "LIST_ITEM", id, nodes: [PARAGRAPH] }` | Items inside BULLETED_LIST |
|
|
114
|
-
| `BLOCKQUOTE` | `{ type: "BLOCKQUOTE", id, nodes: [PARAGRAPH] }` | Pull quotes, callouts |
|
|
115
|
-
| `TEXT` | `{ type: "TEXT", id: "", nodes: [], textData: { text: "...", decorations: [] } }` | Inline text (leaf node inside paragraphs, headings, etc.) |
|
|
116
|
-
|
|
117
|
-
**Tips:**
|
|
118
|
-
- Container nodes (PARAGRAPH, HEADING, etc.) need a unique `id` (use any string, e.g., `"n1"`, `"n2"`). TEXT nodes can use an empty string `""` for `id`.
|
|
119
|
-
- TEXT nodes are always leaf nodes inside PARAGRAPH, HEADING, CODE_BLOCK, etc.
|
|
120
|
-
- LIST_ITEM nodes contain PARAGRAPH nodes (not TEXT directly)
|
|
121
|
-
- Mix at least 3 different node types per post for visual variety
|
|
122
|
-
- Create all 3 posts in sequence (one `curl` call per post)
|
|
123
|
-
|
|
124
|
-
> **Re-publish after PATCH:** If you update a published post (e.g., the image agent adds a cover image via PATCH after creation), it becomes `hasUnpublishedChanges: true`. The agent that PATCHes must call `POST /blog/v3/draft-posts/{draftPostId}/publish` to re-publish. (This seed scope does not attach media — see Step 3.)
|
|
125
|
-
|
|
126
|
-
## Step 4: Verify
|
|
127
|
-
|
|
128
|
-
Query published posts to confirm all 3 exist:
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
REST: POST https://www.wixapis.com/blog/v3/posts/query
|
|
132
|
-
body: {
|
|
133
|
-
"query": {
|
|
134
|
-
"paging": { "limit": 10 }
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Confirm 3 posts are returned. Report post titles to the user.
|
|
140
|
-
|
|
141
|
-
## Step 5: Return Results
|
|
142
|
-
|
|
143
|
-
Emit a structured JSON block at the end of your completion message per `../shared/RETURN_CONTRACT.md`. Do **not** write a sidecar file. Include the created posts' `id` and `title` so the image agent can attach cover images by ID:
|
|
144
|
-
|
|
145
|
-
```json
|
|
146
|
-
{
|
|
147
|
-
"status": "complete",
|
|
148
|
-
"phase": "blog-seed",
|
|
149
|
-
"scope": "seed",
|
|
150
|
-
"summary": "Created and published {n} on-brand blog posts",
|
|
151
|
-
"data": {
|
|
152
|
-
"blogPosts": [
|
|
153
|
-
{ "id": "<uuid>", "title": "<title>" }
|
|
154
|
-
]
|
|
155
|
-
},
|
|
156
|
-
"files": [],
|
|
157
|
-
"errors": []
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
The JSON block MUST be the last content in your message.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blog-implementer
|
|
3
|
-
description: "Implements blog vertical via Wix Blog app — @wix/blog SDK, @wix/ricos rich content, RSS/sitemap. Scopes: seed, components, pages. Extends references/shared/IMPLEMENTER.md."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blog Implementer
|
|
7
|
-
|
|
8
|
-
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, prompt-inlined inputs (read only your `.wix/seeded.json` slice), return contract, style conventions, and common failure modes.
|
|
9
|
-
|
|
10
|
-
## Scope routing
|
|
11
|
-
|
|
12
|
-
| Scope | Phase | Reference |
|
|
13
|
-
|-------|-------|-----------|
|
|
14
|
-
| `seed` | Seed (post seeding via REST with Ricos content) | `./BLOG_CONTENT.md` |
|
|
15
|
-
| `components` | Components (blog service module, RicosViewer, consts, astro.config sitemap) | `../astro/blog/BLOG_SETUP.md` |
|
|
16
|
-
| `pages` | Pages (listing, detail, RSS, BlogPost layout — all under `src/pages/blog/*`; no shared-shell patch, so blog merges into one private build agent) | `../astro/blog/BLOG_PAGES.md` |
|
|
17
|
-
|
|
18
|
-
Cover images are generated by the shared image agent (`image-phase-2-entity` scope) AFTER `seed` completes — not by this agent.
|
|
19
|
-
|
|
20
|
-
## Files this vertical creates / contributes
|
|
21
|
-
|
|
22
|
-
The `components` and `pages` scopes enumerate their own file lists — see `../astro/blog/BLOG_SETUP.md` (components) and `../astro/blog/BLOG_PAGES.md` (pages).
|
|
23
|
-
|
|
24
|
-
## Blog-specific failure modes
|
|
25
|
-
|
|
26
|
-
| Wrong | Right |
|
|
27
|
-
|---|---|
|
|
28
|
-
| `import { items } from "@wix/data"` for blog queries | `import { posts } from "@wix/blog"` — use the Blog SDK |
|
|
29
|
-
| `<article set:html={post.content} />` | `<RicosViewer client:only="react" content={post.richContent} />` — content is Ricos format |
|
|
30
|
-
| Skip `wixMetadata` on `[...slug].astro` | Required for Wix page routing + SEO |
|
|
31
|
-
| Query posts without `RICH_CONTENT` fieldset | Always request `RICH_CONTENT` or `post.richContent` is undefined |
|
|
32
|
-
| Scoped `<style>` for RicosViewer overrides | `<style is:global>` — React islands don't inherit scoped Astro styles |
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
# Bookings — the booking flow (framework-agnostic logic)
|
|
2
|
-
|
|
3
|
-
The booking logic — the step model, the shared state, and the exact `@wix` SDK
|
|
4
|
-
sequence — is the **same on every frontend** (astro, react/vite, vue, plain HTML).
|
|
5
|
-
This file is that shared logic. The per-framework wiring layers on top:
|
|
6
|
-
|
|
7
|
-
- **astro** (`frontendBuild: wix`): `../astro/bookings/SERVICES_PAGES.md` (SSR read pages) + `../astro/bookings/COMPONENTS.md` (the client islands).
|
|
8
|
-
- **own / static** (`frontendBuild: own`/`none`): `../custom/bookings/WIRING.md`.
|
|
9
|
-
|
|
10
|
-
**Code examples are React** (`../astro/templates/bookings/*.tsx` + `bookingDriver.ts`).
|
|
11
|
-
React is directly usable on astro islands and on react/vite. **On any other
|
|
12
|
-
framework, translate the examples** — the SDK calls and payloads are identical;
|
|
13
|
-
only the UI idiom (`useState`/`useEffect` → a store / `onMounted` / signals) and
|
|
14
|
-
the client acquisition differ (see "Client identity" below).
|
|
15
|
-
|
|
16
|
-
The flow covers **appointments and classes**. APPOINTMENT and CLASS share the
|
|
17
|
-
same booking sequence; they differ only in the availability call (`availabilityTimeSlots`
|
|
18
|
-
vs `eventTimeSlots`, noted inline). **Courses and add-ons are out of scope.**
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## 1. The step model
|
|
23
|
-
|
|
24
|
-
A single customer journey, holding one shared selection. The catalog optionally
|
|
25
|
-
filters by **location** and **category**; the slots step optionally filters by
|
|
26
|
-
**staff**. All three auto-skip when there's nothing to choose (≤1 location, ≤1
|
|
27
|
-
category, ≤1 staff), so a plain single-location single-staff site sees the simple
|
|
28
|
-
five-step path:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
0. Location (optional) pick a location — only shown when the site has >1 locations → scopes the catalog + availability
|
|
32
|
-
1. Catalog list bookable services (optionally filtered by location/category) → pick a service
|
|
33
|
-
2. Slots availability calendar (optionally filtered by staff) → pick a time slot
|
|
34
|
-
3. Details the service's booking form (collect contact) → submit
|
|
35
|
-
4. Book run the SDK sequence (§3)
|
|
36
|
-
5a. paid → redirect to the Wix-hosted checkout → returns to the confirmation page
|
|
37
|
-
5b. free/offline → place the order → confirmation page
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
The location/category filters and the staff picker are covered in §§7–8.
|
|
41
|
-
|
|
42
|
-
**The catalog is the entry point but optional** — a visitor can land directly on a
|
|
43
|
-
service page and start at the Slots step. Every step rehydrates from the URL (§2),
|
|
44
|
-
so deep links work.
|
|
45
|
-
|
|
46
|
-
Mapping the steps onto pages/routes is the **target framework's choice** — translate
|
|
47
|
-
the step model to whatever router the framework uses. The skill recommends the routes
|
|
48
|
-
`/services`, `/services/[slug]`, `/booking-confirmation` (advertised in
|
|
49
|
-
`../verticals/bookings.md`). On astro specifically, SSR the catalog + detail pages
|
|
50
|
-
for SEO and run slots + form + book in a `client:only` island.
|
|
51
|
-
|
|
52
|
-
## 2. Shared booking state
|
|
53
|
-
|
|
54
|
-
Steps 1–4 build up one selection (this is exactly `BookParams` + `SelectedSlot`
|
|
55
|
-
in `../astro/templates/bookings/bookingDriver.ts`):
|
|
56
|
-
|
|
57
|
-
- **`service`** — the chosen service object (the booking step reads `service.payment` + `service.bookingPolicy`; the slots step reads `service.staffMemberDetails.staffMembers` for the staff picker — §8).
|
|
58
|
-
- **`slot`** — `{ serviceType, serviceId, localStartDate, localEndDate, timezone, scheduleId?, eventId?, locationId?, locationType?, resource? }`. `resource = { _id, name }` is set only when a specific staff was chosen (§8); otherwise the driver emits the ANY_RESOURCE fallback. `locationId`/`locationType` come from the slot's own `location` (the booking books at that location).
|
|
59
|
-
- **`formSubmission`** — the booking-form values, **keyed by each field's `target`** (§4).
|
|
60
|
-
|
|
61
|
-
Hold it however the framework prefers (React state lifted to a coordinator, a
|
|
62
|
-
store, route loaders + a provider, query-params). **Two rules that bite:**
|
|
63
|
-
|
|
64
|
-
1. **Persist the selection before navigating.** When a step lives on its own
|
|
65
|
-
route, write the selection into the shared state (or the URL) *before* moving
|
|
66
|
-
on, and let the next step **rehydrate from the slug/URL** on direct load /
|
|
67
|
-
refresh. A persistent provider that does not re-init on in-app navigation will
|
|
68
|
-
otherwise show an empty next step.
|
|
69
|
-
2. **The slug is the carry-across key.** `serviceSlug` (the service's
|
|
70
|
-
`mainSlug.name`) identifies the service across the slots + details steps;
|
|
71
|
-
resolve the full service from it when state is lost.
|
|
72
|
-
|
|
73
|
-
## 3. The booking SDK sequence — `bookingDriver.ts`
|
|
74
|
-
|
|
75
|
-
The whole booking — `createBooking → createCart → calculateCart → checkout-or-place`
|
|
76
|
-
— is in **`../astro/templates/bookings/bookingDriver.ts`**, in plain `@wix` SDK
|
|
77
|
-
calls (no React, no UI). **Use it as-is** (it is framework-agnostic); the booking
|
|
78
|
-
step calls `book(params)` then branches on the result. Do not re-author the
|
|
79
|
-
sequence — the payload shapes are exact and easy to get subtly wrong.
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
book({ service, slot, formSubmission, timezone }) →
|
|
83
|
-
createBooking(...) // booking lands CREATED — the cart holds the seat
|
|
84
|
-
createCart(...) // one catalog item per bookingId, appId = BOOKING_APP_ID, channel WEB
|
|
85
|
-
calculateCart(cartId) // → { cart, summary }; totals on summary.priceSummary.total.amount
|
|
86
|
-
isCheckoutRequired(...) ?
|
|
87
|
-
→ { CheckoutRequired, cartId } // paid → navigateToCheckout(cartId, postFlowUrl) → Wix-hosted checkout
|
|
88
|
-
: placeOrder(cartId) → { CheckoutSkipped, orderId } // free / pay-in-person → confirmation
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Key facts the driver encodes (do not deviate):
|
|
92
|
-
- **No `confirmBooking`** — `confirmBooking` is the classic Bookings server-side
|
|
93
|
-
confirm step (it moves a booking from `CREATED` to `CONFIRMED`). Here the **ecom
|
|
94
|
-
cart holds the seat** instead — `placeOrder` (free/offline) or the hosted checkout
|
|
95
|
-
(paid) drives confirmation — so a client-only site completes the whole flow with no
|
|
96
|
-
server elevation.
|
|
97
|
-
- **ANY_RESOURCE fallback (staff)** — when no staff is chosen (the default, and on
|
|
98
|
-
single-staff services), `createBooking` sends
|
|
99
|
-
`resourceSelections:[{ resourceTypeId:"1cd44cf8-756f-41c3-bd90-3e2ffcaf1155", selectionMethod:"ANY_RESOURCE" }]`
|
|
100
|
-
and Wix auto-assigns a bookable staff resource (appointment slots return
|
|
101
|
-
`availableResources:[]` yet book fine this way). A specific staff choice sets
|
|
102
|
-
`slot.resource` instead (§8).
|
|
103
|
-
- **Checkout decision** — `isCheckoutRequired`: if the service's booking policy charges
|
|
104
|
-
a **cancellation fee** (`service.bookingPolicy.cancellationFeePolicy.enabled`) →
|
|
105
|
-
checkout (a card must be on file); else total 0 → place; else `FULL_PAYMENT_OFFLINE`
|
|
106
|
-
→ place; else checkout.
|
|
107
|
-
- **Redirect shape** — paid bookings hand the cart to the Wix-hosted **ecom**
|
|
108
|
-
checkout: `createRedirectSession({ ecomCheckout: { checkoutId: cartId }, callbacks:{ postFlowUrl } })`.
|
|
109
|
-
|
|
110
|
-
### Browse + availability (the read calls the steps make)
|
|
111
|
-
|
|
112
|
-
Mirror what the Bookings SDK does (`services.queryServices` is the raw SDK, not a component):
|
|
113
|
-
|
|
114
|
-
```js
|
|
115
|
-
// Catalog (list) — the filter MUST include appId; pass conditionalFields, then .find().
|
|
116
|
-
// Merge the optional category/location filters into the SAME filter object (§7):
|
|
117
|
-
services.queryServices({
|
|
118
|
-
query: { filter: { appId: BOOKING_APP_ID /* , "category.id": { $eq: catId },
|
|
119
|
-
"locations.business.id": { $hasSome: [locId] } */ }, paging: { limit: 100 } },
|
|
120
|
-
conditionalFields: ["STAFF_MEMBER_DETAILS"],
|
|
121
|
-
}).find(); // → result.items ; filter out s.hidden
|
|
122
|
-
|
|
123
|
-
// Single service by slug — the .eq() builder chain:
|
|
124
|
-
services.queryServices({ conditionalFields: ["STAFF_MEMBER_DETAILS"] })
|
|
125
|
-
.eq("mainSlug.name", slug).eq("appId", BOOKING_APP_ID).limit(1).find();
|
|
126
|
-
|
|
127
|
-
// Availability (APPOINTMENT) — serviceId is a single GUID STRING. The staff (§8)
|
|
128
|
-
// and location filters are OPTIONAL params on the same call:
|
|
129
|
-
availabilityTimeSlots.listAvailabilityTimeSlots({
|
|
130
|
-
serviceId, fromLocalDate, toLocalDate, timeZone, bookable: true, cursorPaging: { limit: 100 },
|
|
131
|
-
// staff: resourceTypes: [{ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, resourceIds: [staffResourceId] }],
|
|
132
|
-
// includeResourceTypeIds: [STAFF_MEMBER_RESOURCE_TYPE_ID],
|
|
133
|
-
// location: locations: [{ _id: locationId, locationType: "BUSINESS" }],
|
|
134
|
-
}); // slots: result.timeSlots[] (localStartDate/localEndDate/scheduleId at top level)
|
|
135
|
-
|
|
136
|
-
// Availability (CLASS) — different namespace, PLURAL serviceIds; slots carry eventInfo.eventId, no scheduleId.
|
|
137
|
-
// CLASS filters staff + location via eventFilter instead:
|
|
138
|
-
eventTimeSlots.listEventTimeSlots({
|
|
139
|
-
serviceIds: [serviceId], fromLocalDate, toLocalDate, timeZone, includeNonBookable: false,
|
|
140
|
-
// eventFilter: { "resources.id": { $hasSome: [staffResourceId] }, "location.id": [locationId] },
|
|
141
|
-
});
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
`fromLocalDate`/`toLocalDate` are **local** date strings `YYYY-MM-DDThh:mm:ss`
|
|
145
|
-
(no `Z`) with an explicit `timeZone`.
|
|
146
|
-
|
|
147
|
-
## 4. The booking form — schema-driven (`@wix/forms`)
|
|
148
|
-
|
|
149
|
-
The booking form is a **`@wix/forms` form** attached to the service
|
|
150
|
-
(`service.form._id`, namespace `wix.bookings.v2.bookings`). Render it
|
|
151
|
-
**schema-driven** — read the field list and render inputs by field type — the
|
|
152
|
-
**same renderer the forms vertical uses** (`../astro/forms/CONTACT_FORM.md`):
|
|
153
|
-
|
|
154
|
-
1. Fetch the form by `service.form._id` (`@wix/forms` `getForm`; server-side/elevated
|
|
155
|
-
where the framework allows). Read `form.formFields`.
|
|
156
|
-
2. Keep `fieldType === "INPUT" && !hidden` **and** a recognized string
|
|
157
|
-
`componentType` (`TEXT_INPUT` / `PHONE_INPUT` / `DROPDOWN`). For each, take
|
|
158
|
-
`inputOptions.target`, `inputOptions.required`, `inputOptions.stringOptions.componentType`,
|
|
159
|
-
and the label from `inputOptions.stringOptions.{textInputOptions|dropdownOptions|phoneInputOptions}.label`.
|
|
160
|
-
**Skip complex, object-valued fields** — e.g. a multi-line `ADDRESS` — they carry
|
|
161
|
-
no string `componentType`; rendering them as a text input sends a string and
|
|
162
|
-
`createBooking` rejects it with **"must be object"**. The booking enforces only
|
|
163
|
-
the contact basics (`first_name`/`last_name`/`email`), so omitting an optional
|
|
164
|
-
complex field is safe (do **not** default unknown field types to a text input).
|
|
165
|
-
3. Render generic inputs by `componentType`: `TEXT_INPUT` → text input,
|
|
166
|
-
`DROPDOWN` → select, `PHONE_INPUT` → `type=tel`; treat `identifier === "TEXT_AREA"`
|
|
167
|
-
(or `target` containing `message`) as a textarea (its `componentType` is still `TEXT_INPUT`).
|
|
168
|
-
4. Collect the values into an object **keyed by each field's `target`** — that is
|
|
169
|
-
`formSubmission`. Pass it to `book()`. Only include fields the visitor filled;
|
|
170
|
-
never send a value for a field you didn't render.
|
|
171
|
-
|
|
172
|
-
**Do not** submit `contactDetails`, and **do not** hardcode field names — key by
|
|
173
|
-
`target`. The default booking form's targets are snake_case `first_name` /
|
|
174
|
-
`last_name` / `email` / `phone`. Reference example: `../astro/templates/bookings/BookingForm.tsx`.
|
|
175
|
-
|
|
176
|
-
## 5. The availability calendar (slots step)
|
|
177
|
-
|
|
178
|
-
**Group slots by day — don't render a flat list of every slot with time-only
|
|
179
|
-
labels** (the visitor can't tell which day a slot is on; that flat grid was the
|
|
180
|
-
original usability failure). The calendar **shape is your / the brand's choice** —
|
|
181
|
-
week, month, or N-day. The reference example uses a **week view** (a 7-day strip
|
|
182
|
-
with week navigation → the picked day's times); fetch availability for the visible
|
|
183
|
-
window (`fromLocalDate`/`toLocalDate` = that window's bounds), group by calendar
|
|
184
|
-
day, and offer a **"check next availability"** action that probes forward when the
|
|
185
|
-
window is empty. Reference example:
|
|
186
|
-
`../astro/templates/bookings/AvailabilityCalendar.tsx`.
|
|
187
|
-
|
|
188
|
-
## 6. Client identity (per framework)
|
|
189
|
-
|
|
190
|
-
The SDK calls are identical everywhere; only how you get the client differs:
|
|
191
|
-
- **astro** — SSR read pages use the ambient `@wix/essentials` client
|
|
192
|
-
(`auth.elevate(services.queryServices)(...)`); browser islands call the `@wix`
|
|
193
|
-
modules **ambiently** too (the `@wix/astro` visitor client, like the ecom
|
|
194
|
-
`CartView` island) — no `createClient`/`OAuthStrategy`.
|
|
195
|
-
- **own / static** — acquire a visitor client once:
|
|
196
|
-
`createClient({ modules, auth: OAuthStrategy({ clientId: appId }) })` and call
|
|
197
|
-
the same functions on it. CDN imports for `none`, bundled for `own`.
|
|
198
|
-
|
|
199
|
-
## 7. Catalog filters — location & category (both auto-skip)
|
|
200
|
-
|
|
201
|
-
Both filter the **catalog query** and re-render the list. The cleanest
|
|
202
|
-
framework-agnostic shape is a re-query driven by a query param (`?locationId`,
|
|
203
|
-
`?category`) — links/SSR on a static catalog, or a store/router on a SPA. **Show
|
|
204
|
-
each filter only when there is more than one choice**, exactly as the SoT does.
|
|
205
|
-
|
|
206
|
-
**Location** (the SoT is location-first; here it auto-skips ≤1 location):
|
|
207
|
-
1. `services.queryLocations()` → `{ businessLocations: { locations: [{ _id, type, business: { name } }] }, customLocations: { exists }, customerLocations: { exists } }`.
|
|
208
|
-
2. Count = `businessLocations.locations.length + (customLocations.exists ? 1 : 0) + (customerLocations.exists ? 1 : 0)`. **Show the selector only when count > 1.**
|
|
209
|
-
3. The chosen location filters the catalog query:
|
|
210
|
-
- a real business id → `filter["locations.business.id"] = { $hasSome: [id] }`;
|
|
211
|
-
- the synthetic `"custom"` / `"customer"` → `filter["locations.type"] = { $hasSome: ["CUSTOM"|"CUSTOMER"] }` (the SoT's synthetic-id mapping).
|
|
212
|
-
4. **Scope availability to exactly ONE location — always, on a multi-location service.** `listAvailabilityTimeSlots` returns **one slot per location** per time ("if `locations` is not specified, returns time slots for all locations where the service is available"), so an unscoped call on a 2-location service shows every time **twice**. Pass a single-element filter: APPOINTMENT → `locations: [{ _id, locationType: "BUSINESS" }]`; CLASS → `eventFilter: { "location.id": [id] }`. (Staff does **not** multiply rows — one slot carries many `availableResources`; only location does.) The slots step builds its location list from **`queryLocations()`** (the site's real business locations, whose ids the availability engine recognizes) **intersected with the service's own location ids** — not from `service.locations` alone, whose entries can carry an id the availability engine doesn't recognize (scoping to it returns zero slots). A **location picker** defaults to the catalog-carried location (or the first) and scopes the call when there's a real location to scope to; when the site has no business locations the list is empty and the call stays unscoped (one location → no duplicates). The booked slot carries its own `location`, so the booking books at that location (the driver maps it).
|
|
213
|
-
|
|
214
|
-
**Category:**
|
|
215
|
-
1. `categoriesV2.queryCategories().find()` → `result.items: [{ _id, name }]`. **Non-fatal** — if it fails, render the catalog without the bar.
|
|
216
|
-
2. **Show the bar only when `items.length > 1`** (a fresh site has one default category).
|
|
217
|
-
3. The chosen category filters the catalog query: `filter["category.id"] = { $eq: categoryId }`. A service carries its category at `service.category._id`.
|
|
218
|
-
|
|
219
|
-
Reference example: `../astro/templates/bookings/services/index.astro` (both filters, link-driven, auto-skipping).
|
|
220
|
-
|
|
221
|
-
## 8. Staff / resource selection (auto-skips ≤1 staff)
|
|
222
|
-
|
|
223
|
-
A service carries its staff on the service object via the **`STAFF_MEMBER_DETAILS`
|
|
224
|
-
conditional field** (already requested in the catalog/detail query):
|
|
225
|
-
`service.staffMemberDetails.staffMembers: [{ staffMemberId, name }]`. The id used
|
|
226
|
-
for filtering and booking is **`staffMemberId`** — which, **despite the name, IS the
|
|
227
|
-
resource GUID** (it matches the service's `staffMemberIds` and the Staff Members API
|
|
228
|
-
`resourceId`). Below, `resourceId` is that value.
|
|
229
|
-
|
|
230
|
-
1. Show a "Any staff member / pick one" control above the calendar **only when
|
|
231
|
-
there is more than one staff**.
|
|
232
|
-
2. Selecting a staff re-fetches availability filtered to that resource:
|
|
233
|
-
- APPOINTMENT → `resourceTypes: [{ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, resourceIds: [resourceId] }]` (plus `includeResourceTypeIds: [STAFF_MEMBER_RESOURCE_TYPE_ID]`);
|
|
234
|
-
- CLASS → `eventFilter: { "resources.id": { $hasSome: [resourceId] } }`.
|
|
235
|
-
3. When a slot is picked **with a specific staff selected**, set `slot.resource = { _id: resourceId, name }`. The driver books that resource. With **"any staff"**, leave `resource` unset — the driver emits the **ANY_RESOURCE fallback** and Wix auto-assigns a bookable resource (appointment slots can return `availableResources: []` yet book fine this way).
|
|
236
|
-
|
|
237
|
-
`STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155"` (exported from `bookingDriver.ts`). Reference example: `../astro/templates/bookings/AvailabilityCalendar.tsx` (the staff `<select>` + the filtered fetch).
|
|
238
|
-
|
|
239
|
-
## 9. Out of scope
|
|
240
|
-
|
|
241
|
-
Waitlist, on-site manage/cancel, payment/deposit breakdown, and multi-service /
|
|
242
|
-
day-range are out of scope. (Waitlist and manage/cancel have **no headless SoT** —
|
|
243
|
-
neither the components nor the vibe plugin implement join/cancel logic, only
|
|
244
|
-
display-only policy flags — so they are deliberately not built here.) Show
|
|
245
|
-
bookable slots only; post-booking self-service is handled by the Wix-hosted flow /
|
|
246
|
-
member area.
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: bookings-implementer
|
|
3
|
-
description: "Implements the Wix Bookings vertical — services catalog (with optional location + category filters), a service detail page with a week-calendar availability picker (with optional staff selection), a schema-driven booking form, and confirmation, for appointments and classes. The booking runs client-side via the @wix SDK (ecom Cart V2 — no confirmBooking). Scopes: seed, components, pages. Extends references/shared/IMPLEMENTER.md."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Bookings Implementer
|
|
7
|
-
|
|
8
|
-
Extends `references/shared/IMPLEMENTER.md`. Read that file first for phase routing, REST auth + doc lookups, the `.wix/seeded.json` read pattern, the return contract, style conventions, and common failure modes.
|
|
9
|
-
|
|
10
|
-
## The logic lives in one place
|
|
11
|
-
|
|
12
|
-
The booking **logic** — the step model, the shared selection state, the exact
|
|
13
|
-
`@wix` SDK sequence, and the schema-driven form — is framework-agnostic and lives
|
|
14
|
-
in **`./FLOW.md`**. Read it first. The astro guides below are the astro *wiring* of
|
|
15
|
-
that logic; the code examples under `<SKILL_ROOT>/references/astro/templates/bookings/`
|
|
16
|
-
are React (the astro islands use them directly). For an own/static build, the wiring guide is
|
|
17
|
-
`../custom/bookings/WIRING.md` (same logic, client-side `@wix/sdk`).
|
|
18
|
-
|
|
19
|
-
## Scope routing
|
|
20
|
-
|
|
21
|
-
| Scope | Phase | Reference |
|
|
22
|
-
|-------|-------|-----------|
|
|
23
|
-
| `seed` | Seed — create services (and staff) via the Wix Bookings REST API | `./SERVICES_DATA.md` |
|
|
24
|
-
| `components` | Components — the client islands (week calendar, schema-driven form, the flow coordinator) | `./FLOW.md` + `../astro/bookings/COMPONENTS.md` |
|
|
25
|
-
| `pages` | Pages — `/services` listing, `/services/[slug]` detail, `/booking-confirmation`, + nav/home links | `./FLOW.md` + `../astro/bookings/SERVICES_PAGES.md` |
|
|
26
|
-
|
|
27
|
-
## Templates — read and adapt (don't invent)
|
|
28
|
-
|
|
29
|
-
Canonical examples live at `<SKILL_ROOT>/references/astro/templates/bookings/`.
|
|
30
|
-
Your `components` and `pages` scopes **read these and adapt them** — adapt brand
|
|
31
|
-
copy, headings, and styling; keep the SDK calls, payload shapes, and the data
|
|
32
|
-
flow (re-authoring the SDK wiring from scratch is the main source of API-shape bugs).
|
|
33
|
-
|
|
34
|
-
Components (`components` scope — TSX only, no CSS):
|
|
35
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/AvailabilityCalendar.tsx` — the week calendar (week strip → the day's slots; APPOINTMENT via `availabilityTimeSlots`, CLASS via `eventTimeSlots`).
|
|
36
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/BookingForm.tsx` — the schema-driven form (renders the `@wix/forms` field list, keys values by `target`) that drives `bookingDriver.book()`.
|
|
37
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/ServiceBookingFlow.tsx` — the coordinator island (holds the selected slot; calendar → form → redirect).
|
|
38
|
-
|
|
39
|
-
Pages (`pages` scope):
|
|
40
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/ServiceCard.astro`
|
|
41
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/services/index.astro`, `<SKILL_ROOT>/references/astro/templates/bookings/services/[slug].astro`
|
|
42
|
-
- `<SKILL_ROOT>/references/astro/templates/bookings/booking-confirmation.astro`
|
|
43
|
-
|
|
44
|
-
### Pre-copied by the orchestrator (do NOT write these yourself)
|
|
45
|
-
Mechanical, brand-agnostic — the orchestrator copies them before dispatch (BUILD-astro.md § build wave). Rely on them at the listed paths:
|
|
46
|
-
- `src/components/bookingDriver.ts` ← `<SKILL_ROOT>/references/astro/templates/bookings/bookingDriver.ts` — the booking SDK sequence (`book()`, `navigateToCheckout()`). The islands import it; never re-author it.
|
|
47
|
-
- `src/components/SeoTags.astro` ← `<SKILL_ROOT>/references/astro/templates/bookings/SeoTags.astro` — renders `service.seoData.tags`; imported by `services/[slug].astro`.
|
|
48
|
-
- `src/styles/components-bookings.css` ← `<SKILL_ROOT>/references/astro/templates/bookings/components-bookings.css` — the flow's component classes.
|
|
49
|
-
|
|
50
|
-
If a pre-copied file is missing at runtime, that's an orchestrator-side bug — return `status: "partial"` with `errors: [{code: "UTILITY_TEMPLATE_NOT_PRECOPIED", path: "<missing>"}]`; do not author your own version.
|
|
51
|
-
|
|
52
|
-
## Pre-return file-existence assertion (pages scope)
|
|
53
|
-
|
|
54
|
-
Before returning `status: "complete"` from `pages`, verify on disk:
|
|
55
|
-
- `src/pages/services/index.astro`
|
|
56
|
-
- `src/pages/services/[slug].astro`
|
|
57
|
-
- `src/pages/booking-confirmation.astro`
|
|
58
|
-
|
|
59
|
-
If any declared file is missing, return `status: "partial"` with `errors: [{ code: "PHASE4_FILE_MISSING", path: "<expected path>" }]`.
|
|
60
|
-
|
|
61
|
-
## Dependencies (Setup installs these — see SETUP.md)
|
|
62
|
-
|
|
63
|
-
`@wix/bookings @wix/essentials @wix/forms @wix/redirects @wix/auto_sdk_ecom_cart-v-2`.
|
|
64
|
-
`@wix/forms` renders the booking-form schema; `@wix/redirects` + `@wix/auto_sdk_ecom_cart-v-2` run the cart/checkout sequence.
|
|
65
|
-
|
|
66
|
-
## CSS ownership — bookings pack
|
|
67
|
-
|
|
68
|
-
Bookings-specific CSS lives in `src/styles/components-bookings.css` (pre-copied — see above), NOT in `global.css`. Classes the pack owns: `.service-card*`, `.service-grid`, `.availability-*`, `.time-slot*`, `.booking-*`. If `global.css` ships a partial rule for any of these, flag it (`{code:"GLOBAL_CSS_LEAK", class:"<name>"}`) and override in `components-bookings.css`.
|
|
69
|
-
|
|
70
|
-
## Bookings-specific failure modes
|
|
71
|
-
|
|
72
|
-
| Wrong | Right |
|
|
73
|
-
|-------|-------|
|
|
74
|
-
| Stop after `createBooking` returns | `createBooking` leaves the booking **`CREATED`**. The seat is held by the **ecom Cart V2**: continue `createCart → calculateCart → isCheckoutRequired ? hosted-checkout redirect : placeOrder`. The whole sequence is in `bookingDriver.ts` — use it. |
|
|
75
|
-
| Hardcode `selectedPaymentOption: "ONLINE"` on `createBooking` | **Derive** it from the service: `online && !inPerson → "ONLINE"`, `!online && inPerson → "OFFLINE"`, else `"ONLINE"`. A free / pay-in-person service is the only kind that reaches `placeOrder`, and booking it `ONLINE` makes the cart reject it with **`INSUFFICIENT_INVENTORY`** (`available_quantity: 0`). `bookingDriver.ts` derives this for you (don't pass `selectedPaymentOption` to `book()`). |
|
|
76
|
-
| Pass `contactDetails` to `createBooking` | Pass **`formSubmission`**, keyed by each field's **`target`** (default booking form: snake_case `first_name`/`last_name`/`email`/`phone`). `contactDetails` is what Wix derives back onto the response. |
|
|
77
|
-
| Hardcode the booking-form fields | Render the form **schema-driven**: read `service.form._id`'s fields via `@wix/forms`, render by `componentType`, key values by `target` (same renderer as `../astro/forms/CONTACT_FORM.md`). |
|
|
78
|
-
| Render every form field as a text input (default unknown types to `TEXT_INPUT`) | Render **only** fields with a recognized string `componentType` (`TEXT_INPUT`/`PHONE_INPUT`/`DROPDOWN`). **Skip complex, object-valued fields** (e.g. the default booking form's multi-line `ADDRESS`) — sending a string for them fails `createBooking` with **"must be object"**. Only `first_name`/`last_name`/`email` are enforced, so skipping optional complex fields is safe. |
|
|
79
|
-
| Call `queryServices` without an `appId` filter | Always include `appId` (`13d21c63-…`) in the filter. List: `queryServices({ query:{ filter:{ appId }, paging }, conditionalFields:["STAFF_MEMBER_DETAILS"] }).find()`. Single: the `.eq("mainSlug.name", slug).eq("appId", …).limit(1).find()` builder chain. |
|
|
80
|
-
| Build `createClient({ auth: OAuthStrategy({ clientId: import.meta.env.* }) })` in `.astro` SSR | The `*_WIX_CLIENT_ID` env var is client-only → `undefined` at server render → 500. SSR reads use the ambient `@wix/essentials`: `auth.elevate(services.queryServices)(...)`. |
|
|
81
|
-
| Build an `OAuthStrategy` client inside an astro browser island | Astro islands call the `@wix` modules **ambiently** (the `@wix/astro` visitor client, like the ecom `CartView`) — no `createClient`. (Own/static builds DO use `OAuthStrategy` — `../custom/bookings/WIRING.md`.) |
|
|
82
|
-
| Pass UTC ISO strings to `listAvailabilityTimeSlots` | Use **local** date strings `YYYY-MM-DDThh:mm:ss` (no `Z`) + a separate `timeZone`. |
|
|
83
|
-
| `availability.queryAvailability` / `import { availability }` | APPOINTMENT → `availabilityTimeSlots.listAvailabilityTimeSlots({ serviceId: <string> })`; CLASS → `eventTimeSlots.listEventTimeSlots({ serviceIds: [<string>] })`. The `availability` namespace does not exist. |
|
|
84
|
-
| Read slot time from `timeSlot.slot.startDate` | Slot fields are at the **top level**: `localStartDate`, `localEndDate`, `scheduleId` (APPOINTMENT). CLASS slots carry `eventInfo.eventId` and have **no** `scheduleId`. |
|
|
85
|
-
| Dump every slot in one flat, time-only grid | **Group slots by day** — a flat time-only grid hides which day a slot is on. The calendar shape (week / month / N-day) is your choice; the example uses a week strip → the picked day's times. |
|
|
86
|
-
| Read the staff id from `staffMember._id` / `.resourceId` on `staffMemberDetails.staffMembers` | The field is **`staffMemberId`** (on `service.staffMemberDetails.staffMembers[]`) — despite the name it IS the resource GUID (matches the service's `staffMemberIds`). Filter availability: APPOINTMENT `resourceTypes:[{ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, resourceIds:[staffMemberId] }]`; CLASS `eventFilter:{ "resources.id": { $hasSome:[staffMemberId] } }`. Record the choice as `slot.resource = { _id: staffMemberId, name }`; leave it unset for "any staff" (ANY_RESOURCE fallback). Show the picker only when >1 staff. |
|
|
87
|
-
| Always show the location selector / staff picker / category bar | **Auto-skip** each when there's nothing to choose: location count ≤1 (`businessLocations.length + custom?.exists + customer?.exists`), staff ≤1, categories ≤1. Single-location single-staff sites see the simple flow. |
|
|
88
|
-
| Filter the catalog by a synthetic location id, or hardcode the location filter | Real business id → `filter["locations.business.id"] = { $hasSome:[id] }`; synthetic `"custom"`/`"customer"` → `filter["locations.type"] = { $hasSome:["CUSTOM"|"CUSTOMER"] }`. Carry the choice to the detail page so availability is scoped too. |
|
|
89
|
-
| Call `listAvailabilityTimeSlots` **without** a `locations` filter on a multi-location service | It returns **one slot per location** per time → every time appears **N×** (duplicated rows). **Always scope availability to one business location** (`locations: [{ _id, locationType:"BUSINESS" }]`; CLASS: `eventFilter:{ "location.id":[id] }`). Build the calendar's location list from **`queryLocations()`** (the site's real business locations) intersected with the service's location ids — NOT from `service.locations` alone (its entries can carry an id the availability engine doesn't recognize → scoping to it returns 0 slots even though the service has availability). Default to the carried/first, picker when >1; no business locations → unscoped. Staff does **not** cause duplicates — one slot carries many `availableResources`; only location multiplies rows. |
|
|
90
|
-
| Let a `queryCategories` failure break the catalog | Categories are **non-fatal** — wrap in try/catch and render the catalog without the bar. Category filter: `filter["category.id"] = { $eq: categoryId }`; a service's category is `service.category._id`. |
|
|
91
|
-
| Read service slug from `service.slug` / name from `service.info.name` | V2 is flat: slug `service.mainSlug.name` (fallback `supportedSlugs[0].name`), `service.name`, `service.description`, `service.tagLine`; duration `service.schedule.availabilityConstraints.sessionDurations[0]`; price `service.payment.fixed.price.value` (string); media `service.media.mainMedia.image.url`. |
|
|
92
|
-
| Use `service.id` / `result.booking.id` | Entity ids are `_id` (underscore): `service._id`, `booking._id`. |
|
|
93
|
-
| Mount the calendar/form without `client:only="react"` | Availability + booking are timezone/session-specific — always `client:only="react"`. SSR only the read pages (catalog/detail) for SEO. |
|
|
94
|
-
| Omit try/catch on the booking | `createBooking` can reject (e.g. a slot taken between fetch and submit, or strict phone validation). Catch and surface a friendly message; don't crash. |
|
|
95
|
-
| Ship a payment/deposit breakdown, multi-service stacking, waitlist, or on-site manage/cancel | Out of scope. **Waitlist and manage/cancel have no headless SoT** (the components + vibe plugin expose only display-only policy flags — no join/cancel logic) — do not invent them on the REST/anonymous-token layer. Post-booking self-service is handled by the Wix-hosted flow / member area. |
|