@wix/agent-skills 1.17.0 → 1.18.1
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/package.json +1 -1
- package/skills/wix-app/SKILL.md +115 -12
- package/skills/wix-app/references/APP_MARKET_REVIEW.md +1 -1
- package/skills/wix-app/references/BACKEND_API.md +18 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +6 -0
- package/skills/wix-app/references/DASHBOARD_MODAL.md +10 -2
- package/skills/wix-app/references/DASHBOARD_PAGE.md +32 -70
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +74 -83
- package/skills/wix-app/references/SITE_PLUGIN.md +12 -0
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +74 -68
- package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +79 -0
- package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +73 -0
- package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +52 -0
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +184 -0
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +12 -11
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +1 -0
- package/skills/wix-app/references/editor-react-component/BROWSER-SUPPORT.md +56 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +16 -8
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +47 -14
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +3 -0
- package/skills/wix-base44-connector/SKILL.md +133 -44
- package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +78 -0
- package/skills/wix-base44-connector/scripts/utils.js +216 -30
- package/skills/wix-headless/entry/skill.md +1 -1
- package/skills/wix-headless/references/CAPABILITIES.md +9 -1
- package/skills/wix-headless/references/DESIGN.md +1 -0
- package/skills/wix-headless/references/DISCOVERY.md +2 -1
- package/skills/wix-headless/references/IMAGE_GENERATION.md +1 -0
- package/skills/wix-headless/references/SDK_HANDOFF.md +4 -1
- package/skills/wix-headless/references/SEED.md +2 -1
- package/skills/wix-headless/references/SETUP.md +1 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +2 -2
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +26 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +26 -10
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +8 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +254 -0
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +57 -10
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +13 -0
- package/skills/wix-headless/references/inline-recipes/setup-rentals.md +254 -0
- package/skills/wix-headless/references/non-astro.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +166 -0
- package/skills/wix-headless-fast/entry/skill.md +99 -0
- package/skills/wix-headless-fast/install/deploy.mjs +433 -0
- package/skills/wix-headless-fast/install/fast-path.mjs +141 -0
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +137 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +113 -0
- package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +42 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +123 -0
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +70 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +99 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +76 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +62 -0
- package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +99 -0
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +24 -0
- package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +16156 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +72 -0
- package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +287 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +129 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +143 -0
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +119 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +138 -0
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +54 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +206 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +96 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +70 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +76 -0
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +22 -0
- package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +11962 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +61 -0
- package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +302 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +179 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +83 -0
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +57 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +151 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +75 -0
- package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +11262 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +103 -0
- package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +324 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +66 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +176 -0
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +118 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +117 -0
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +59 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +136 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +128 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +61 -0
- package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +12 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +80 -0
- package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +21 -0
- package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +11884 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +86 -0
- package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +276 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +150 -0
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +317 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +269 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +231 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +135 -0
- package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +10593 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +108 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +320 -0
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +63 -0
- package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +60 -0
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +155 -0
- package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +78 -0
- package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +47 -0
- package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +66 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +88 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +53 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +122 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +50 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +21 -0
- package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +42 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +10 -0
- package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +11 -0
- package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +10329 -0
- package/skills/wix-headless-fast/references/members/seed/SEED.md +39 -0
- package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +94 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +134 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +110 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +90 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +115 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +59 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +39 -0
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +165 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +55 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +40 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +30 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +21 -0
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +29 -0
- package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +10197 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +78 -0
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +258 -0
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +133 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +58 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +126 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +37 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +39 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +75 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +45 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +30 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +40 -0
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +21 -0
- package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +11226 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +71 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +279 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +142 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +55 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +43 -0
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +177 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +165 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +103 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +254 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +146 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +168 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +47 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +22 -0
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +12 -0
- package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +12560 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +81 -0
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +479 -0
- package/skills/wix-headless-fast/references/shared/CONTENT.md +22 -0
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +73 -0
- package/skills/wix-headless-fast/references/shared/DESIGN.md +26 -0
- package/skills/wix-headless-fast/references/shared/app/styles/global.css +52 -0
- package/skills/wix-headless-fast/references/shared/app/wix/config.ts +15 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +27 -0
- package/skills/wix-headless-fast/references/shared/app/wix/money.ts +24 -0
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +76 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +46 -0
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +46 -0
- package/skills/wix-headless-fast/references/shared/seed/images.mjs +145 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +149 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +26 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +121 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +100 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +130 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +48 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +66 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +103 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +48 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +140 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +80 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +101 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +221 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +215 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +109 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +46 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +56 -0
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +23 -0
- package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +12247 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +85 -0
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +415 -0
- package/skills/wix-manage/SKILL.md +113 -89
- package/skills/wix-manage/references/dashboard-navigation/routes.json +12 -52
- package/skills/wix-manage/references/ecommerce/shipping/ecom-shipping-api.md +0 -2
- package/skills/wix-manage/references/forms/create-form.md +272 -184
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +13 -10
- package/skills/wix-manage/references/forms/update-form.md +122 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +141 -0
- package/skills/wix-manage/references/google-business-profile/connect-google-business-profile.md +134 -0
- package/skills/wix-manage/references/google-business-profile/manage-google-business-profile-locations.md +182 -0
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +3 -0
- package/skills/wix-manage/references/media/generate-image-with-ai.md +86 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +37 -7
- package/skills/wix-manage/references/stores/update-product-with-options.md +83 -0
- package/skills/wix-vibe-headless/SKILL.md +18 -4
- package/skills/wix-vibe-headless/install/deploy.cjs +12 -4
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +2 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44.md +27 -61
- package/skills/wix-vibe-headless/platforms/generic.md +6 -3
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +67 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +128 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +5 -3
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +250 -0
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +12 -5
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +41 -0
- package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.js +267 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +6 -3
- package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +486 -0
- package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +425 -0
- package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +187 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +238 -0
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +108 -0
- package/skills/wix-vibe-headless/references/forms/seed/SEED.md +1836 -0
- package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +13 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +137 -84
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +15 -8
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +37 -0
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +18 -6
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +30 -7
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +46 -10
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +0 -110
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +0 -51
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +0 -49
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +0 -92
- package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +0 -139
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Pricing Plans — seeding
|
|
2
|
+
|
|
3
|
+
Seed by **running `seed-pricing-plans.mjs` with a plan file** — don't hand-write the REST
|
|
4
|
+
calls. The script mints its own site token via the Wix CLI (logged-in session +
|
|
5
|
+
`wix.config.json` required), installs the Pricing Plans app if needed, and creates everything
|
|
6
|
+
in the right order.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# from the project root (where wix.config.json lives):
|
|
10
|
+
node <SKILL_ROOT>/references/pricing-plans/seed/seed-pricing-plans.mjs plan.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`plan.json` is plain data — write it from the brief. **Default to 3 plans** — a tier ladder
|
|
14
|
+
the grid can render (e.g. free / monthly / yearly), each with 3–4 perks; together they
|
|
15
|
+
exercise every price shape the UI handles. Plans have no seedable images (the owner adds one
|
|
16
|
+
per plan in the dashboard).
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"plans": [
|
|
21
|
+
{ "name": "Community", "type": "free",
|
|
22
|
+
"description": "Get a taste — member perks, zero commitment.",
|
|
23
|
+
"perks": ["Member-only newsletter", "Community events access"] },
|
|
24
|
+
{ "name": "Studio Membership", "price": "29.00",
|
|
25
|
+
"type": "recurring", "billingCycle": { "period": "MONTH", "count": 1 },
|
|
26
|
+
"description": "The full experience, month to month.",
|
|
27
|
+
"perks": ["Unlimited group classes", "10% off workshops", "Priority booking"] },
|
|
28
|
+
{ "name": "Annual Pass", "price": "290.00",
|
|
29
|
+
"type": "recurring", "billingCycle": { "period": "YEAR", "count": 1 },
|
|
30
|
+
"description": "Two months free, billed yearly.",
|
|
31
|
+
"perks": ["Everything in Studio Membership", "2 guest passes", "Annual member gift"] }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `type` — `"recurring"` (default; `billingCycle` defaults to `{ "period": "MONTH", "count": 1 }`),
|
|
37
|
+
`"one-time"` (bills once, then ends; `billingCycle: null` makes it never expire), or
|
|
38
|
+
`"free"` (amount forced to `"0"` + a per-member-lifetime purchase limit).
|
|
39
|
+
- `price` — a decimal string (`"29.00"`; a number is stringified). Currency is site-derived —
|
|
40
|
+
never sent.
|
|
41
|
+
- `perks` — display-only bullets on the plan card; they grant nothing by themselves.
|
|
42
|
+
- `termsAndConditions` (plain text), `visibility` (`"PUBLIC"`), `buyable` (`true`) — optional
|
|
43
|
+
overrides. A `buyable: false` plan renders without a subscribe CTA (merchant-assigned).
|
|
44
|
+
|
|
45
|
+
## Covering bookings services (only when bookings is also seeded)
|
|
46
|
+
|
|
47
|
+
A plan can COVER bookings services — members then book them with the membership. The link
|
|
48
|
+
goes through the Benefit Programs API, not the plan object, keyed by the **bookings seed's
|
|
49
|
+
service ids** — so seed bookings FIRST, then pass the ids:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "name": "Studio Membership", "price": "29.00",
|
|
53
|
+
"perks": ["Unlimited group classes"],
|
|
54
|
+
"coveredServiceIds": ["<bookings service id>", "…"] }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Unlimited coverage is the default; a limited pack (e.g. 8 sessions) instead uses
|
|
58
|
+
`"bookingsCoverage": { "serviceIds": [...], "creditAmount": 8 }`. A plans-only run just
|
|
59
|
+
omits both — coverage is skipped automatically.
|
|
60
|
+
|
|
61
|
+
**Seeding is additive — never delete or overwrite existing content**; ask first if a cleanup
|
|
62
|
+
seems needed.
|
|
63
|
+
|
|
64
|
+
## Escape hatch — individual functions
|
|
65
|
+
`setupPricingPlans` composes exported steps — `installPricingPlansApp`, `createPlans`,
|
|
66
|
+
`attachBookingsCoverage` (and its `getProgramDefinition`, `createPoolDefinition`,
|
|
67
|
+
`createBenefitItems` sub-steps), plus `makeCtx()` — import them only for a partial re-seed.
|
|
68
|
+
|
|
69
|
+
## Reference
|
|
70
|
+
Unexpected shape or an uncovered operation → read the live Wix API reference; the
|
|
71
|
+
authoritative source recipe is `wix-headless/references/inline-recipes/setup-pricing-plans.md`.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Pricing Plans seed — a BUILD-TIME script, never shipped in the app. Run from the project
|
|
2
|
+
// root (where wix.config.json lives) with a plan file:
|
|
3
|
+
//
|
|
4
|
+
// node <SKILL_ROOT>/references/pricing-plans/seed/seed-pricing-plans.mjs plan.json
|
|
5
|
+
//
|
|
6
|
+
// It mints its own site token via the Wix CLI, installs the Wix Pricing Plans app if needed,
|
|
7
|
+
// creates the plans (Plans V3 — one create call per plan; there is no bulk-create), and —
|
|
8
|
+
// only when a plan carries bookings service ids — wires the plan to COVER those services
|
|
9
|
+
// through the Benefit Programs API. Prints a JSON result to stdout.
|
|
10
|
+
//
|
|
11
|
+
// Plan shape (see SEED.md):
|
|
12
|
+
// { "plans": [{ "name", "description"?, "price"? (decimal string; a number is stringified),
|
|
13
|
+
// "type"?: "recurring"|"one-time"|"free",
|
|
14
|
+
// "billingCycle"?: { "period": "DAY"|"WEEK"|"MONTH"|"YEAR", "count" } | null,
|
|
15
|
+
// "perks"?: ["..."], "termsAndConditions"?, "visibility"?, "buyable"?,
|
|
16
|
+
// "coveredServiceIds"?: ["<bookings service id>"],
|
|
17
|
+
// "bookingsCoverage"?: { "serviceIds"?, "creditAmount"? } }] }
|
|
18
|
+
//
|
|
19
|
+
// Seeding is ADDITIVE — never deletes or overwrites existing content. Unexpected shapes →
|
|
20
|
+
// read the live API reference; authoritative source recipe:
|
|
21
|
+
// wix-headless/references/inline-recipes/setup-pricing-plans.md.
|
|
22
|
+
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { readFileSync } from "node:fs";
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
25
|
+
|
|
26
|
+
const API = "https://www.wixapis.com";
|
|
27
|
+
const PRICING_PLANS_APP_ID = "1522827f-c56c-a5c9-2ac9-00f9e6ae12d3";
|
|
28
|
+
const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97"; // coverage providerAppId — NOT the Plans id
|
|
29
|
+
const PP_NAMESPACE = "@wix/pricing-plans"; // literal, with the @ and the slash, in every coverage call
|
|
30
|
+
|
|
31
|
+
export function makeCtx({ cwd = process.cwd() } = {}) {
|
|
32
|
+
const config = JSON.parse(readFileSync(`${cwd}/wix.config.json`, "utf8"));
|
|
33
|
+
const siteId = config.siteId ?? config.projectId;
|
|
34
|
+
if (!siteId) throw new Error("wix.config.json has no siteId — is this a Wix CLI project?");
|
|
35
|
+
const token = execFileSync("npx", ["@wix/cli@latest", "token", "--site", siteId], {
|
|
36
|
+
encoding: "utf8",
|
|
37
|
+
cwd,
|
|
38
|
+
}).trim();
|
|
39
|
+
if (!token) throw new Error("The Wix CLI returned no token — run `npx @wix/cli@latest login` first.");
|
|
40
|
+
return { token, siteId };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function req(ctx, path, { method = "POST", body } = {}) {
|
|
44
|
+
const res = await fetch(API + path, {
|
|
45
|
+
method,
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Bearer ${ctx.token}`,
|
|
48
|
+
"wix-site-id": ctx.siteId,
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
},
|
|
51
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
52
|
+
});
|
|
53
|
+
const json = await res.json().catch(() => ({}));
|
|
54
|
+
if (!res.ok) throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(json).slice(0, 400)}`);
|
|
55
|
+
return json;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
59
|
+
|
|
60
|
+
// billingTerms selects the plan type (recurring | one-time | free). recurring: billingCycle
|
|
61
|
+
// {period,count} (default MONTH/1) + ON_PURCHASE + UNTIL_CANCELLED. one-time: endType
|
|
62
|
+
// CYCLES_COMPLETED + billingCycleCount 1 (bill once, then ends); an unlimited one-time
|
|
63
|
+
// instead sets billingCycle:null + UNTIL_CANCELLED. free: MUST be the no-cycle unlimited
|
|
64
|
+
// shape — a $0 variant with a billingCycle is rejected ("Free pricing variant cannot be
|
|
65
|
+
// recurring").
|
|
66
|
+
function buildBillingTerms(plan) {
|
|
67
|
+
const type = plan.type || "recurring";
|
|
68
|
+
if (type === "free") {
|
|
69
|
+
return { billingCycle: null, startType: "ON_PURCHASE", endType: "UNTIL_CANCELLED" };
|
|
70
|
+
}
|
|
71
|
+
if (type === "one-time") {
|
|
72
|
+
if (plan.billingCycle === null) {
|
|
73
|
+
return { billingCycle: null, startType: "ON_PURCHASE", endType: "UNTIL_CANCELLED" };
|
|
74
|
+
}
|
|
75
|
+
const cycle = plan.billingCycle || { period: "MONTH", count: 1 };
|
|
76
|
+
return {
|
|
77
|
+
billingCycle: cycle,
|
|
78
|
+
startType: "ON_PURCHASE",
|
|
79
|
+
endType: "CYCLES_COMPLETED",
|
|
80
|
+
cyclesCompletedDetails: { billingCycleCount: 1 },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// recurring
|
|
84
|
+
const cycle = plan.billingCycle || { period: "MONTH", count: 1 };
|
|
85
|
+
return { billingCycle: cycle, startType: "ON_PURCHASE", endType: "UNTIL_CANCELLED" };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// One pricingVariant with one pricingStrategy (schema allows ≤20 but is "currently limited
|
|
89
|
+
// to 1"). Amounts are decimal STRINGS ("20.00"); the variant id is a REQUIRED
|
|
90
|
+
// client-supplied GUID — omitting it returns 400.
|
|
91
|
+
function buildPricingVariant(plan) {
|
|
92
|
+
const free = (plan.type || "recurring") === "free";
|
|
93
|
+
const amount = free ? "0" : String(plan.price);
|
|
94
|
+
return {
|
|
95
|
+
id: randomUUID(),
|
|
96
|
+
name: plan.variantName || "Standard",
|
|
97
|
+
billingTerms: buildBillingTerms(plan),
|
|
98
|
+
pricingStrategies: [{ flatRate: { amount } }],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// plain plan -> Plans V3 create body. status:"ACTIVE" is REQUIRED (omitting → 400
|
|
103
|
+
// "status value is required"); currency is NOT sent (read-only, site-derived); perks are
|
|
104
|
+
// display-only bullets, each with a REQUIRED client-supplied GUID.
|
|
105
|
+
function buildPlan(plan) {
|
|
106
|
+
const free = (plan.type || "recurring") === "free";
|
|
107
|
+
return {
|
|
108
|
+
name: plan.name,
|
|
109
|
+
description: plan.description,
|
|
110
|
+
status: "ACTIVE",
|
|
111
|
+
visibility: plan.visibility || "PUBLIC",
|
|
112
|
+
buyable: plan.buyable ?? true,
|
|
113
|
+
buyerCanCancel: plan.buyerCanCancel ?? true,
|
|
114
|
+
...(plan.termsAndConditions ? { termsAndConditions: plan.termsAndConditions } : {}),
|
|
115
|
+
pricingVariants: [buildPricingVariant(plan)],
|
|
116
|
+
perks: (plan.perks ?? []).map((p) => ({
|
|
117
|
+
id: randomUUID(),
|
|
118
|
+
description: typeof p === "string" ? p : p.description,
|
|
119
|
+
})),
|
|
120
|
+
// free: cap lifetime reuse. Array + maxCount per the SDK schema (PurchaseLimit[]); the
|
|
121
|
+
// recipe's older `{ type, count }` object form predates it.
|
|
122
|
+
...(free ? { purchaseLimits: [{ type: "PER_MEMBER_LIFETIME", maxCount: 1 }] } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---- operations ----------------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
// docs: https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/platform/about-apps-created-by-wix.md
|
|
129
|
+
export async function installPricingPlansApp(ctx) {
|
|
130
|
+
try {
|
|
131
|
+
await req(ctx, "/apps-installer-service/v1/app-instance/install", { body: {
|
|
132
|
+
tenant: { tenantType: "SITE", id: ctx.siteId },
|
|
133
|
+
appInstance: { appDefId: PRICING_PLANS_APP_ID, enabled: true },
|
|
134
|
+
} });
|
|
135
|
+
} catch {
|
|
136
|
+
/* already installed is fine */
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Create the plan(s) — Plans V3 has NO bulk-create: one POST per plan. Keep plan.id: the
|
|
141
|
+
// frontend orders by it AND it is the coverage externalId.
|
|
142
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/plans-v3/create-plan.md
|
|
143
|
+
export async function createPlans(ctx, plans) {
|
|
144
|
+
const out = [];
|
|
145
|
+
for (const plan of plans) {
|
|
146
|
+
const r = await req(ctx, "/pricing-plans/v3/plans", { body: { plan: buildPlan(plan) } });
|
|
147
|
+
out.push({
|
|
148
|
+
id: r.plan?.id,
|
|
149
|
+
slug: r.plan?.slug,
|
|
150
|
+
name: plan.name,
|
|
151
|
+
coveredServiceIds: plan.coveredServiceIds,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---- bookings coverage via Benefit Programs (SKIP when no bookings in the run) -------------------
|
|
158
|
+
// Strictly ordered: plan.id -> programDefinition.id -> itemSetId -> items. Do NOT parallelize.
|
|
159
|
+
|
|
160
|
+
// 2a: READ the auto-created program definition (the Plans app creates it; you never create
|
|
161
|
+
// it). Provisioning is ~immediate but async — retry ONCE after a short backoff, don't loop.
|
|
162
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/benefit-programs/program-definitions/introduction.md
|
|
163
|
+
export async function getProgramDefinition(ctx, planId) {
|
|
164
|
+
const path = `/benefit-programs/v1/program-definitions/by-namespace-and-external-id?externalId=${planId}&namespace=${encodeURIComponent(PP_NAMESPACE)}`;
|
|
165
|
+
try {
|
|
166
|
+
const r = await req(ctx, path, { method: "GET" });
|
|
167
|
+
if (r.programDefinition?.id) return r.programDefinition.id;
|
|
168
|
+
} catch {
|
|
169
|
+
/* fall through to the single insurance retry */
|
|
170
|
+
}
|
|
171
|
+
await sleep(1000);
|
|
172
|
+
const r = await req(ctx, path, { method: "GET" });
|
|
173
|
+
return r.programDefinition?.id;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 2b: create ONE pool definition with EXACTLY ONE benefit naming Bookings as the provider.
|
|
177
|
+
// price "0" = unlimited (default; no creditConfiguration). Limited pack: pass creditAmount →
|
|
178
|
+
// price "1" + details.creditConfiguration (a SIBLING of benefits[], NOT inside a benefit —
|
|
179
|
+
// nesting it 400s with "Price should be 0 when credit pool is not set up").
|
|
180
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/benefit-programs/pool-definitions/create-pool-definition.md
|
|
181
|
+
export async function createPoolDefinition(ctx, programDefinitionId, { creditAmount } = {}) {
|
|
182
|
+
const limited = creditAmount != null;
|
|
183
|
+
const details = {
|
|
184
|
+
...(limited ? { creditConfiguration: { amount: String(creditAmount) } } : {}),
|
|
185
|
+
benefits: [
|
|
186
|
+
{
|
|
187
|
+
benefitKey: randomUUID(),
|
|
188
|
+
displayName: "Bookings sessions",
|
|
189
|
+
providerAppId: BOOKINGS_APP_ID,
|
|
190
|
+
price: limited ? "1" : "0",
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
const r = await req(ctx, "/benefit-programs/v1/pool-definitions", {
|
|
195
|
+
body: {
|
|
196
|
+
poolDefinition: {
|
|
197
|
+
namespace: PP_NAMESPACE,
|
|
198
|
+
displayName: "Bookings benefit",
|
|
199
|
+
programDefinitionIds: [programDefinitionId],
|
|
200
|
+
details,
|
|
201
|
+
},
|
|
202
|
+
cascade: "IMMEDIATELY",
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
// itemSetId lives at poolDefinition.details.benefits[i].itemSetId — one benefit here, so [0].
|
|
206
|
+
return r.poolDefinition?.details?.benefits?.[0]?.itemSetId;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 2c: bulk-create the benefit items — ONE item per covered service (externalId = bookings
|
|
210
|
+
// service id). Up to 100 per call; category is an empty string; namespace/providerAppId
|
|
211
|
+
// repeat 2b's values.
|
|
212
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/benefit-programs/items/bulk-create-items.md
|
|
213
|
+
export async function createBenefitItems(ctx, itemSetId, serviceIds) {
|
|
214
|
+
return req(ctx, "/benefit-programs/v1/bulk/items/create", {
|
|
215
|
+
body: {
|
|
216
|
+
items: serviceIds.map((externalId) => ({
|
|
217
|
+
namespace: PP_NAMESPACE,
|
|
218
|
+
category: "",
|
|
219
|
+
providerAppId: BOOKINGS_APP_ID,
|
|
220
|
+
itemSetId,
|
|
221
|
+
externalId,
|
|
222
|
+
})),
|
|
223
|
+
returnEntity: true,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Wire a plan to COVER bookings services (per plan). Runs 2a → 2b → 2c in order.
|
|
230
|
+
* serviceIds come from the BOOKINGS seed — seed bookings first.
|
|
231
|
+
*/
|
|
232
|
+
export async function attachBookingsCoverage(ctx, planId, serviceIds, { creditAmount } = {}) {
|
|
233
|
+
const programDefinitionId = await getProgramDefinition(ctx, planId);
|
|
234
|
+
if (!programDefinitionId) throw new Error(`No program definition for plan ${planId} — is the Pricing Plans app installed?`);
|
|
235
|
+
const itemSetId = await createPoolDefinition(ctx, programDefinitionId, { creditAmount });
|
|
236
|
+
await createBenefitItems(ctx, itemSetId, serviceIds);
|
|
237
|
+
return { itemSetId, serviceIds };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* ONE-CALL seed: install → create plans (ids kept in memory) → per covering plan, wire
|
|
242
|
+
* bookings coverage (2a → 2b → 2c, strictly ordered). The default path.
|
|
243
|
+
*/
|
|
244
|
+
export async function setupPricingPlans(ctx, { plans = [] } = {}) {
|
|
245
|
+
await installPricingPlansApp(ctx);
|
|
246
|
+
const created = await createPlans(ctx, plans);
|
|
247
|
+
const coverageAttached = [];
|
|
248
|
+
for (let i = 0; i < created.length; i++) {
|
|
249
|
+
const cov = plans[i].bookingsCoverage || {};
|
|
250
|
+
const serviceIds = cov.serviceIds || created[i].coveredServiceIds;
|
|
251
|
+
if (!serviceIds?.length) continue; // plans-only plan — coverage skipped
|
|
252
|
+
const r = await attachBookingsCoverage(ctx, created[i].id, serviceIds, { creditAmount: cov.creditAmount });
|
|
253
|
+
coverageAttached.push({ planId: created[i].id, ...r });
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
plans: created,
|
|
257
|
+
coverageAttached,
|
|
258
|
+
benefitsCreated: coverageAttached.reduce((n, c) => n + c.serviceIds.length, 0),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
|
|
265
|
+
if (invokedDirectly) {
|
|
266
|
+
const planPath = process.argv[2];
|
|
267
|
+
if (!planPath) {
|
|
268
|
+
console.error("usage: node seed-pricing-plans.mjs <plan.json> (run from the project root)");
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
272
|
+
const ctx = makeCtx();
|
|
273
|
+
setupPricingPlans(ctx, plan)
|
|
274
|
+
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
275
|
+
.catch((e) => {
|
|
276
|
+
console.error(e.message);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Restaurants — playbook
|
|
2
|
+
|
|
3
|
+
The restaurant machinery ships as files — the assembled menu tree (menus → sections → items
|
|
4
|
+
with variants, modifiers, labels), the dish-ordering cart on the eCom current cart with the
|
|
5
|
+
exact restaurant `catalogReference`, the hosted checkout, and the table-reservation
|
|
6
|
+
hold → reserve flow, typed end-to-end. **The presentation is yours**: you design and
|
|
7
|
+
implement the dish card, the menu surface, the order-cart chrome, and the reservations
|
|
8
|
+
surface on the shipped hooks/DTOs, plus the home page and the brand. You never write
|
|
9
|
+
ordering or reservation logic; you never skip designing.
|
|
10
|
+
|
|
11
|
+
## The file map (deployed into `src/`)
|
|
12
|
+
|
|
13
|
+
**Don't read the shipped files** — this table and the contracts below are everything you
|
|
14
|
+
need. Open a shipped file's source only on a real fallback (runtime error / uncovered field),
|
|
15
|
+
or to read a reference component's pattern.
|
|
16
|
+
|
|
17
|
+
| file | what it is |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `wix/config.ts` · `wix/sdk.ts` · `wix/media.ts` · `wix/money.ts` | shared auth seam + helpers (deploy configures; nothing to set) |
|
|
20
|
+
| `wix/restaurants/types.ts` | the DTOs (`MenuData`, `MenuSection`, `MenuItem`, `OrderCart`, `ReservationSlot`, …) — contracts below |
|
|
21
|
+
| `wix/restaurants/menu.ts` | `fetchMenus` — the whole display-ordered menu tree, one call |
|
|
22
|
+
| `wix/restaurants/ordering.ts` · `order-store.ts` | dish add-to-cart (Orders-app catalogReference) + eCom Cart V2 + shared cart state (module store — spans Astro islands) |
|
|
23
|
+
| `wix/restaurants/reservations.ts` | locations, AVAILABLE slots, `holdReservation`, `completeReservation` — the exact hold→reserve sequence lives here |
|
|
24
|
+
| `hooks/restaurants/useMenus.ts` | menu browsing + menu switching — contract below |
|
|
25
|
+
| `hooks/restaurants/useOrderCart.ts` | order-cart state + actions — contract below |
|
|
26
|
+
| `hooks/restaurants/useReservation.ts` | the whole reservation state machine — contract below |
|
|
27
|
+
| `components/restaurants/MenuView.tsx` (+ `MenuItemCard`) · `OrderCartButton.tsx` · `OrderCartDrawer.tsx` · `ReservationView.tsx` | **REFERENCE implementations** — correct, plain; build your own instead of shipping them |
|
|
28
|
+
| `styles/global.css` | the design system: Tailwind v4 + the `@theme` token block (shared across verticals) |
|
|
29
|
+
|
|
30
|
+
Astro stack additionally gets:
|
|
31
|
+
|
|
32
|
+
| file | what it is |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `layouts/SiteLayout.astro` | site chrome — **yours to brand** (keep the `seo-tags` slot, global.css import, and one OrderCartButton + one OrderCartDrawer, both `client:only`). If another vertical is also deployed, its layout won — merge the Menu/Reservations links + cart mounts there |
|
|
35
|
+
| `pages/menu.astro` | SSR menu — **keep the frontmatter**, swap the island import to YOUR component |
|
|
36
|
+
| `pages/reservations.astro` | reservations — the island stays `client:only="react"` (availability is timezone-specific); swap the import to YOUR component |
|
|
37
|
+
|
|
38
|
+
## What you build — the design job
|
|
39
|
+
|
|
40
|
+
1. **The dish card + menu surface** — your card (photo treatment, price/variants, labels,
|
|
41
|
+
sold-out state, add-to-order) and your menu layout (menu tabs when >1, section navigation,
|
|
42
|
+
section rhythm), with skeletons while loading and an honest empty state — on `useMenus` +
|
|
43
|
+
`useOrderCart`.
|
|
44
|
+
2. **The order-cart chrome** — your header order button (live count) and your cart panel
|
|
45
|
+
(lines, quantity stepper, remove, subtotal, checkout CTA) — on `useOrderCart`, which owns
|
|
46
|
+
ALL cart logic; you own how it looks.
|
|
47
|
+
3. **The reservations surface** — party/date/time query, slot pills, the 10-minute-hold
|
|
48
|
+
details form (first name + phone required), and the confirmed vs pending-approval states —
|
|
49
|
+
on `useReservation`.
|
|
50
|
+
4. **The home page** — hero, a featured-dishes strip (fetch `fetchMenus()` in frontmatter →
|
|
51
|
+
your cards; `item.featured` marks highlights), hours/location story, reserve CTA.
|
|
52
|
+
|
|
53
|
+
Plus the **theme** (`@theme` block, one edit) and the **chrome** (`SiteLayout`, one pass).
|
|
54
|
+
Style everything with Tailwind utilities on the tokens.
|
|
55
|
+
|
|
56
|
+
### The contracts your components consume
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// MenuData → sections → items, all display-ordered:
|
|
60
|
+
// MenuItem = { id, name, description, price /* display string; null when variant-priced */,
|
|
61
|
+
// marketPrice /* true → "Market price", not orderable */,
|
|
62
|
+
// variants: [{ variantId, name, price }], imageUrl, labels: [{ id, name, iconUrl }],
|
|
63
|
+
// modifierGroups: [{ id, name, required, minSelections, maxSelections,
|
|
64
|
+
// modifiers: [{ id, name, preSelected, additionalCharge, inStock }] }],
|
|
65
|
+
// inStock, featured }
|
|
66
|
+
// ⚠ MENU prices carry NO currency symbol unless the platform formatted them — render as
|
|
67
|
+
// given; if plain decimals, prefix the brand's currency yourself. Order-cart prices ARE
|
|
68
|
+
// formatted. Modifier groups are DISPLAY-ONLY (selections aren't sent on the cart line).
|
|
69
|
+
|
|
70
|
+
// useMenus({ initialMenus? }) →
|
|
71
|
+
// { menus: MenuData[]|null /* null = loading → skeletons */,
|
|
72
|
+
// activeMenuId, setActiveMenuId(id), activeMenu: MenuData|null, error }
|
|
73
|
+
|
|
74
|
+
// useOrderCart() →
|
|
75
|
+
// { cart: { lines, itemCount, subtotal, currency }|null,
|
|
76
|
+
// ordering: boolean|null, // false → show "ordering unavailable"; null = resolving
|
|
77
|
+
// busy, error, open,
|
|
78
|
+
// addToOrder(itemId, { menuId, sectionId }, qty?), // ids from the render context — see hard rules
|
|
79
|
+
// updateQuantity(lineItemId, qty), removeLine(lineItemId),
|
|
80
|
+
// checkout(), // browser redirects to the Wix-hosted checkout
|
|
81
|
+
// openCart(), closeCart(), refresh() }
|
|
82
|
+
// OrderLine = { lineItemId, itemName, quantity, unitPrice, linePrice, imageUrl,
|
|
83
|
+
// descriptionLines, status /* not "IN_STOCK" → can't check out */ }
|
|
84
|
+
|
|
85
|
+
// useReservation() →
|
|
86
|
+
// { locations: [{ id, partySizeMin, partySizeMax, approvalMode, onlineReservationsEnabled }]|null,
|
|
87
|
+
// location, setLocationId(id), // picker only when locations.length > 1
|
|
88
|
+
// date, setDate("YYYY-MM-DD"), time, setTime("HH:mm"), partySize, setPartySize(n),
|
|
89
|
+
// slots: ReservationSlot[]|null, findSlots(), // AVAILABLE only; null until findSlots ran
|
|
90
|
+
// held, holdSlot(slot), // 10-minute hold → render the details form
|
|
91
|
+
// reservee, setReserveeField(field, value), // firstName + phone (E.164) required
|
|
92
|
+
// canConfirm, confirm(), // gate the CTA on canConfirm
|
|
93
|
+
// confirmed: { reservationId, status: "RESERVED"|"REQUESTED" }|null,
|
|
94
|
+
// reset(), loading, error }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Wiring — Astro (default)
|
|
98
|
+
|
|
99
|
+
1. Set the `@theme` tokens (one edit); brand `SiteLayout.astro` (one pass — merge into the
|
|
100
|
+
winning layout instead if another vertical is also deployed).
|
|
101
|
+
2. Write your components under `src/components/restaurants/` (new names — don't overwrite the
|
|
102
|
+
references), swap the island imports in `pages/menu.astro` and `pages/reservations.astro`.
|
|
103
|
+
Menu island: `client:load` with the SSR props; reservations island and the cart chrome:
|
|
104
|
+
`client:only="react"`. **Author your surfaces in as few messages as possible** — batch
|
|
105
|
+
multiple Writes per message.
|
|
106
|
+
3. Write `pages/index.astro` (home) — it exists from the scaffold; Read it before overwriting.
|
|
107
|
+
|
|
108
|
+
### Wiring — React SPA (Vite etc.)
|
|
109
|
+
|
|
110
|
+
Import `./styles/global.css` once at the app entry (needs `@tailwindcss/vite` in the vite
|
|
111
|
+
plugins — deploy added the dep). Routes: `/menu` → your menu surface (`useMenus()` fetches
|
|
112
|
+
client-side when no `initialMenus`); `/reservations` → your reservation surface. Mount your
|
|
113
|
+
order button in the header and your drawer once.
|
|
114
|
+
|
|
115
|
+
## Hard rules
|
|
116
|
+
|
|
117
|
+
- **Ordering logic only through the shipped exports** — `useOrderCart`/`addToOrder` own the
|
|
118
|
+
restaurant `catalogReference` (the Orders app id + `operationId`/`menuId`/`sectionId` — all
|
|
119
|
+
three, no `variantId`), the line-refusal checks, and the checkout redirect. Never re-derive
|
|
120
|
+
any of it, never hand-build a checkout URL.
|
|
121
|
+
- **Thread `menuId` + `sectionId` from the render context** — each dish is rendered inside a
|
|
122
|
+
known section of a known menu (the `fetchMenus` tree); pass those ids to `addToOrder`.
|
|
123
|
+
Never look them up again.
|
|
124
|
+
- **Modifier groups are display-only** — show them on the dish (diners read them; staff sees
|
|
125
|
+
choices at the counter), but don't invent a way to send selections: that cart-line shape
|
|
126
|
+
isn't documented. Send quantity only, as shipped.
|
|
127
|
+
- **Reservations only through `useReservation`** — AVAILABLE-only slots, the hold's
|
|
128
|
+
`revision`, the firstName+phone requirement, and the RESERVED/REQUESTED split all live in
|
|
129
|
+
the data layer. Gate the CTA on `canConfirm`, surface `error` (holds expire in 10 minutes —
|
|
130
|
+
the hook already restarts the flow).
|
|
131
|
+
- **The confirmed state must reflect REAL success**: render it only from `confirmed`, and
|
|
132
|
+
render `REQUESTED` as "pending the restaurant's approval", never as confirmed. A visitor
|
|
133
|
+
returning from the hosted checkout is NOT an order-success signal.
|
|
134
|
+
- **Honest unavailability**: `ordering === false` → an "ordering unavailable" state on the
|
|
135
|
+
add button; `onlineReservationsEnabled === false` → a "call us to book" notice (the toggle
|
|
136
|
+
is premium-gated). Never mock menus, dishes, prices, slots, or availability.
|
|
137
|
+
- Theme via the `@theme` tokens; no parallel theme files, no hardcoded palettes.
|
|
138
|
+
|
|
139
|
+
## Point the user to their dashboard
|
|
140
|
+
|
|
141
|
+
Give the owner the dashboard link (`https://manage.wix.com/dashboard/<siteId>`) plus:
|
|
142
|
+
`…/wix-restaurants-menus-new` (edit the menu), `…/wix-restaurants-orders-new/settings`
|
|
143
|
+
(fulfillment: pickup/delivery hours, fees), `…/wix-table-reservations/table-reservations`
|
|
144
|
+
(tables, availability). Real paid orders need a premium plan + a connected payment method,
|
|
145
|
+
and the online-reservations toggle itself is premium — mention both.
|
|
146
|
+
|
|
147
|
+
## Seeding
|
|
148
|
+
|
|
149
|
+
Per `seed/SEED.md` — plain-data `plan.json` into `seed-restaurants.mjs` from the project
|
|
150
|
+
root. Seed a menu that exercises the UI (a few sections, an image per dish) and turn on the
|
|
151
|
+
ordering + reservations add-ons when the restaurant takes orders/bookings.
|
|
152
|
+
|
|
153
|
+
## Verify (before declaring done)
|
|
154
|
+
|
|
155
|
+
- [ ] `/menu` renders the live menu SSR (view-source shows dish names) through YOUR
|
|
156
|
+
components, sections in seeded order; an empty backend shows your honest empty state.
|
|
157
|
+
- [ ] Add to order works (drawer opens, count badge is live); quantity ± / remove work; the
|
|
158
|
+
order survives a reload (same visitor token); checkout redirects to the Wix-hosted
|
|
159
|
+
checkout — or, with no ordering operation, your "ordering unavailable" state shows.
|
|
160
|
+
- [ ] `/reservations`: only AVAILABLE times offered; hold → details form → confirm produces
|
|
161
|
+
your RESERVED or REQUESTED state; a non-premium site shows your honest
|
|
162
|
+
"reservations aren't open yet" notice instead of a broken form.
|
|
163
|
+
- [ ] Dish card / menu / cart chrome / reservations / home are YOUR designs on the tokens;
|
|
164
|
+
data-layer/hook files unedited.
|
|
165
|
+
- [ ] Dashboard links handed to the owner, with the premium notes.
|
package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// REFERENCE menu surface: menu tabs (when >1), section nav, and the dish card, on the
|
|
2
|
+
// @theme tokens. Correct and complete; per the skill's model you design and build your own
|
|
3
|
+
// on useMenus + useOrderCart. Note the load-bearing wiring: each card threads its menuId +
|
|
4
|
+
// sectionId from the render context into addToOrder — never re-derive them.
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { useMenus } from "../../hooks/restaurants/useMenus";
|
|
7
|
+
import { useOrderCart } from "../../hooks/restaurants/useOrderCart";
|
|
8
|
+
import type { MenuData, MenuItem } from "../../wix/restaurants/types";
|
|
9
|
+
|
|
10
|
+
export interface MenuItemCardProps {
|
|
11
|
+
item: MenuItem;
|
|
12
|
+
menuId: string;
|
|
13
|
+
sectionId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function MenuItemCard({ item, menuId, sectionId }: MenuItemCardProps) {
|
|
17
|
+
const { addToOrder, ordering, busy } = useOrderCart();
|
|
18
|
+
const [error, setError] = useState<string | null>(null);
|
|
19
|
+
const canAdd = ordering === true && item.inStock && !item.marketPrice && !busy;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex gap-4 rounded-lg border border-border p-4">
|
|
23
|
+
{item.imageUrl && (
|
|
24
|
+
<img
|
|
25
|
+
src={item.imageUrl}
|
|
26
|
+
alt={item.name}
|
|
27
|
+
loading="lazy"
|
|
28
|
+
className="h-24 w-24 flex-shrink-0 rounded-md bg-secondary object-cover"
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
<div className="min-w-0 flex-1">
|
|
32
|
+
<div className="flex items-baseline justify-between gap-3">
|
|
33
|
+
<p className="text-sm font-semibold">{item.name}</p>
|
|
34
|
+
<p className="whitespace-nowrap text-sm text-foreground">
|
|
35
|
+
{item.marketPrice ? "Market price" : (item.price ?? "")}
|
|
36
|
+
</p>
|
|
37
|
+
</div>
|
|
38
|
+
{item.labels.length > 0 && (
|
|
39
|
+
<p className="mt-0.5 text-xs text-muted-foreground">{item.labels.map((l) => l.name).join(" · ")}</p>
|
|
40
|
+
)}
|
|
41
|
+
{item.description && (
|
|
42
|
+
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">{item.description}</p>
|
|
43
|
+
)}
|
|
44
|
+
{item.variants.length > 0 && (
|
|
45
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
46
|
+
{item.variants.map((v) => `${v.name} ${v.price}`).join(" · ")}
|
|
47
|
+
</p>
|
|
48
|
+
)}
|
|
49
|
+
{item.modifierGroups.length > 0 && (
|
|
50
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
51
|
+
{/* Modifier groups are DISPLAY-ONLY — selections aren't sent on the cart line. */}
|
|
52
|
+
Options: {item.modifierGroups.map((g) => g.name).join(", ")}
|
|
53
|
+
</p>
|
|
54
|
+
)}
|
|
55
|
+
<div className="mt-2 flex items-center gap-3">
|
|
56
|
+
{ordering !== null && !item.marketPrice && (
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
disabled={!canAdd}
|
|
60
|
+
onClick={() =>
|
|
61
|
+
addToOrder(item.id, { menuId, sectionId }).catch((e: unknown) =>
|
|
62
|
+
setError(e instanceof Error ? e.message : String(e)),
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
className="rounded-full bg-primary px-4 py-1.5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-40"
|
|
66
|
+
>
|
|
67
|
+
{!item.inStock ? "Sold out" : ordering === false ? "Ordering unavailable" : "Add to order"}
|
|
68
|
+
</button>
|
|
69
|
+
)}
|
|
70
|
+
{error && <p className="text-xs text-red-600">{error}</p>}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface MenuViewProps {
|
|
78
|
+
initialMenus?: MenuData[];
|
|
79
|
+
emptyMessage?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const tab = (active: boolean) =>
|
|
83
|
+
`rounded-full border px-4 py-1.5 text-sm font-medium transition-colors ${
|
|
84
|
+
active
|
|
85
|
+
? "border-primary bg-primary text-primary-foreground"
|
|
86
|
+
: "border-border text-foreground hover:bg-secondary"
|
|
87
|
+
}`;
|
|
88
|
+
|
|
89
|
+
export default function MenuView({
|
|
90
|
+
initialMenus,
|
|
91
|
+
emptyMessage = "The menu is being written — check back soon.",
|
|
92
|
+
}: MenuViewProps) {
|
|
93
|
+
const { menus, activeMenuId, setActiveMenuId, activeMenu, error } = useMenus({ initialMenus });
|
|
94
|
+
|
|
95
|
+
if (menus === null) {
|
|
96
|
+
return (
|
|
97
|
+
<div aria-busy="true">
|
|
98
|
+
{Array.from({ length: 4 }, (_, i) => (
|
|
99
|
+
<div key={i} className="mb-4 h-24 animate-pulse rounded-lg bg-secondary" />
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (menus.length === 0) {
|
|
105
|
+
return <p className="py-16 text-center text-muted-foreground">{error ?? emptyMessage}</p>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div>
|
|
110
|
+
{menus.length > 1 && (
|
|
111
|
+
<div className="mb-8 flex flex-wrap gap-2" role="group" aria-label="Menus">
|
|
112
|
+
{menus.map((m) => (
|
|
113
|
+
<button key={m.id} type="button" className={tab(m.id === activeMenuId)} onClick={() => setActiveMenuId(m.id)}>
|
|
114
|
+
{m.name}
|
|
115
|
+
</button>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
{activeMenu && activeMenu.sections.length > 1 && (
|
|
120
|
+
<nav className="mb-8 flex flex-wrap gap-x-5 gap-y-1 text-sm" aria-label="Sections">
|
|
121
|
+
{activeMenu.sections.map((s) => (
|
|
122
|
+
<a key={s.id} href={`#section-${s.id}`} className="text-muted-foreground no-underline transition-colors hover:text-foreground">
|
|
123
|
+
{s.name}
|
|
124
|
+
</a>
|
|
125
|
+
))}
|
|
126
|
+
</nav>
|
|
127
|
+
)}
|
|
128
|
+
{error && <p className="mb-4 text-sm text-red-600">{error}</p>}
|
|
129
|
+
{activeMenu?.sections.map((section) => (
|
|
130
|
+
<section key={section.id} id={`section-${section.id}`} className="mb-12">
|
|
131
|
+
<h2 className="text-lg font-semibold tracking-tight">{section.name}</h2>
|
|
132
|
+
{section.description && <p className="mt-1 text-sm text-muted-foreground">{section.description}</p>}
|
|
133
|
+
<div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
134
|
+
{section.items.map((item) => (
|
|
135
|
+
<MenuItemCard key={item.id} item={item} menuId={activeMenu.id} sectionId={section.id} />
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
</section>
|
|
139
|
+
))}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|