@wix/agent-skills 1.17.0 → 1.18.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/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 +14 -3
- 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 +19 -1
- 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-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 +18 -2
- 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/stores/create-product-catalog-v3.md +21 -6
- 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 +23 -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 +470 -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 +1762 -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,81 @@
|
|
|
1
|
+
# Restaurants — seeding
|
|
2
|
+
|
|
3
|
+
Seed by **running `seed-restaurants.mjs` with a plan file** — don't hand-write the REST calls.
|
|
4
|
+
The script mints its own site token via the Wix CLI (logged-in session + `wix.config.json`
|
|
5
|
+
required), installs the Restaurants apps, builds each menu bottom-up (items → sections →
|
|
6
|
+
menu, all visible), imports+attaches images, and configures ordering/reservations.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# from the project root (where wix.config.json lives):
|
|
10
|
+
node <SKILL_ROOT>/references/restaurants/seed/seed-restaurants.mjs plan.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`plan.json` is plain data — write it from the brief. **Default to one menu with ~3 sections
|
|
14
|
+
of 2–3 items each** (the seed shows the shape; the owner adds the rest in the dashboard),
|
|
15
|
+
every item with an image (a menu without photos looks broken) — the default is an
|
|
16
|
+
`imagePrompt` (AI-generated, ~1 Wix AI credit per image, account-billed): brand-contextual —
|
|
17
|
+
subject, aesthetic/mood, palette, lighting — always ending "no text, no watermarks". At least one image in the set shows the real subject of the business — the actual product/space/service, not abstract decoration. For an asset the user actually
|
|
18
|
+
supplied use `imagePath` (a file on this machine — uploaded to Wix Media) or `imageUrl`
|
|
19
|
+
(their own hosted URL; verify it with `curl -sI` → 200) — never a stock-photo or guessed URL. Images resolve in parallel and never
|
|
20
|
+
block the seed, a failed image leaves that item text-only — and both add-ons on for a
|
|
21
|
+
restaurant that takes orders and reservations:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"menus": [
|
|
26
|
+
{ "name": "Dinner", "description": "Evening menu", "sections": [
|
|
27
|
+
{ "name": "Antipasti", "description": "To start", "items": [
|
|
28
|
+
{ "name": "Bruschetta al Pomodoro", "description": "Grilled sourdough, San Marzano tomatoes, basil.",
|
|
29
|
+
"price": 9.5, "imageUrl": "https://…" },
|
|
30
|
+
{ "name": "Burrata", "description": "Creamy burrata, heirloom tomatoes, olive oil.",
|
|
31
|
+
"price": 14, "imageUrl": "https://…" }
|
|
32
|
+
] },
|
|
33
|
+
{ "name": "Mains", "items": [
|
|
34
|
+
{ "name": "Tagliatelle al Ragù", "description": "Slow-braised beef ragù, parmigiano.",
|
|
35
|
+
"price": 22, "imageUrl": "https://…" },
|
|
36
|
+
{ "name": "Branzino", "description": "Whole roasted sea bass, lemon, herbs.",
|
|
37
|
+
"price": 28, "imageUrl": "https://…" }
|
|
38
|
+
] },
|
|
39
|
+
{ "name": "Dolci", "items": [
|
|
40
|
+
{ "name": "Tiramisù", "description": "Espresso-soaked ladyfingers, mascarpone.",
|
|
41
|
+
"price": 10, "imageUrl": "https://…" }
|
|
42
|
+
] }
|
|
43
|
+
] }
|
|
44
|
+
],
|
|
45
|
+
"ordering": { "address": {
|
|
46
|
+
"name": "Trattoria Lumina", "timeZone": "America/New_York",
|
|
47
|
+
"address": { "country": "US", "subdivision": "US-NY", "city": "New York", "postalCode": "10012",
|
|
48
|
+
"streetAddress": { "number": "18", "name": "Prince Street" },
|
|
49
|
+
"formattedAddress": "18 Prince Street, New York, NY 10012" } } },
|
|
50
|
+
"reservations": { "partySize": { "min": 1, "max": 10 } }
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `price` — a number; stored as a decimal string in the **site currency** (never send one).
|
|
55
|
+
- `ordering` — installs the Orders app, which **auto-provisions** a working setup (ENABLED
|
|
56
|
+
operation, Pickup + Delivery methods, every menu orderable); the seed verifies it. The
|
|
57
|
+
`address` is **required for real ordering** — without one, ordering is "testing only" and
|
|
58
|
+
checkout breaks. If the brief names no address, pass `"ordering": true` and the result
|
|
59
|
+
carries a note to flag the owner. Completing a **paid** order additionally needs a premium
|
|
60
|
+
plan + a connected payment method (dashboard) — mention it, don't treat it as a failure.
|
|
61
|
+
- `reservations` — installs Table Reservations, which auto-provisions a default reservation
|
|
62
|
+
location; `partySize` (or a full `configuration`) is a partial PATCH. The final
|
|
63
|
+
**enable-online-reservations toggle is premium-only**: on a free site the result carries
|
|
64
|
+
`reservations.premiumRequired: true` — expected, tell the owner, don't retry.
|
|
65
|
+
- Fresh Menus installs ship a sample "Dinner Menu"; when THIS run installs the app the seed
|
|
66
|
+
removes that sample (`sampleMenuRemoved: true`). On a site that already had the app,
|
|
67
|
+
**seeding is strictly additive — never delete or overwrite existing content**; ask first
|
|
68
|
+
if a cleanup seems needed.
|
|
69
|
+
|
|
70
|
+
## Escape hatch — individual functions
|
|
71
|
+
`setupRestaurants` composes exported steps — `installMenusApp`, `installOrdersApp`,
|
|
72
|
+
`installTableReservationsApp`, `removeSampleMenu`, `createMenu`, `importImage`,
|
|
73
|
+
`attachItemImages`, `setBusinessLocation`, `listOperationsWithRetry`, `enableOperation`,
|
|
74
|
+
`queryMenuOrderingSettings`, `updateMenuOrderingSettings`, `listReservationLocationsWithRetry`,
|
|
75
|
+
`updateReservationLocation`, `enableOnlineReservations`, plus `makeCtx()` — import them only
|
|
76
|
+
for a partial re-seed.
|
|
77
|
+
|
|
78
|
+
## Reference
|
|
79
|
+
Unexpected shape or an uncovered operation → read the live Wix API reference; the
|
|
80
|
+
authoritative source recipes are `wix-headless/references/inline-recipes/setup-restaurants.md`,
|
|
81
|
+
`setup-restaurant-orders.md`, and `setup-restaurant-reservations.md`.
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
// Restaurants 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/restaurants/seed/seed-restaurants.mjs plan.json
|
|
5
|
+
//
|
|
6
|
+
// It mints its own site token via the Wix CLI, installs the Wix Restaurants Menus app (plus
|
|
7
|
+
// Orders / Table Reservations when the plan asks), builds each menu BOTTOM-UP (bulk items →
|
|
8
|
+
// bulk sections → menu — every child exists before its parent, visible:true at every level),
|
|
9
|
+
// imports+attaches item images, and configures the add-ons. Prints a JSON result to stdout.
|
|
10
|
+
//
|
|
11
|
+
// Plan shape (see SEED.md):
|
|
12
|
+
// { "menus": [{ "name", "description"?, "sections": [{ "name", "description"?,
|
|
13
|
+
// "items": [{ "name", "description"?, "price", "imageUrl"? | "imagePrompt"? }] }] }],
|
|
14
|
+
// "ordering"?: true | { "address"? }, // menu-first add-on; address is STEP 0
|
|
15
|
+
// "reservations"?: true | { "partySize"? { "min","max" }, "address"? } }
|
|
16
|
+
//
|
|
17
|
+
// Seeding is ADDITIVE — with ONE recipe-sanctioned exception: when THIS run installs the
|
|
18
|
+
// Menus app onto a site that didn't have it, the install's own sample "Dinner Menu" is
|
|
19
|
+
// removed (it's provably not owner content). Nothing else is ever deleted. Unexpected
|
|
20
|
+
// shapes → read the live API reference; authoritative source recipes:
|
|
21
|
+
// wix-headless/references/inline-recipes/setup-restaurants.md, setup-restaurant-orders.md,
|
|
22
|
+
// setup-restaurant-reservations.md.
|
|
23
|
+
import { execFileSync } from "node:child_process";
|
|
24
|
+
import { readFileSync } from "node:fs";
|
|
25
|
+
import { resolveItemImages } from "../../shared/seed/images.mjs";
|
|
26
|
+
|
|
27
|
+
const API = "https://www.wixapis.com";
|
|
28
|
+
const MENUS_APP_ID = "b278a256-2757-4f19-9313-c05c783bec92";
|
|
29
|
+
const ORDERS_APP_ID = "9a5d83fd-8570-482e-81ab-cfa88942ee60";
|
|
30
|
+
const TABLE_RESERVATIONS_APP_ID = "f9c07de2-5341-40c6-b096-8eb39de391fb";
|
|
31
|
+
|
|
32
|
+
export function makeCtx({ cwd = process.cwd() } = {}) {
|
|
33
|
+
const config = JSON.parse(readFileSync(`${cwd}/wix.config.json`, "utf8"));
|
|
34
|
+
const siteId = config.siteId ?? config.projectId;
|
|
35
|
+
if (!siteId) throw new Error("wix.config.json has no siteId — is this a Wix CLI project?");
|
|
36
|
+
const token = execFileSync("npx", ["@wix/cli@latest", "token", "--site", siteId], {
|
|
37
|
+
encoding: "utf8",
|
|
38
|
+
cwd,
|
|
39
|
+
}).trim();
|
|
40
|
+
if (!token) throw new Error("The Wix CLI returned no token — run `npx @wix/cli@latest login` first.");
|
|
41
|
+
return { token, siteId };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function req(ctx, path, { method = "POST", body } = {}) {
|
|
45
|
+
const res = await fetch(API + path, {
|
|
46
|
+
method,
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: `Bearer ${ctx.token}`,
|
|
49
|
+
"wix-site-id": ctx.siteId,
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
},
|
|
52
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
53
|
+
});
|
|
54
|
+
const json = await res.json().catch(() => ({}));
|
|
55
|
+
if (!res.ok) throw new Error(`${method} ${path} -> ${res.status}: ${JSON.stringify(json).slice(0, 400)}`);
|
|
56
|
+
return json;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
60
|
+
|
|
61
|
+
// ---- app installs --------------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
// docs: https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/platform/about-apps-created-by-wix.md
|
|
64
|
+
async function installApp(ctx, appDefId) {
|
|
65
|
+
try {
|
|
66
|
+
await req(ctx, "/apps-installer-service/v1/app-instance/install", { body: {
|
|
67
|
+
tenant: { tenantType: "SITE", id: ctx.siteId },
|
|
68
|
+
appInstance: { appDefId, enabled: true },
|
|
69
|
+
} });
|
|
70
|
+
} catch {
|
|
71
|
+
/* already installed is fine */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function installMenusApp(ctx) { return installApp(ctx, MENUS_APP_ID); }
|
|
75
|
+
export async function installOrdersApp(ctx) { return installApp(ctx, ORDERS_APP_ID); }
|
|
76
|
+
export async function installTableReservationsApp(ctx) { return installApp(ctx, TABLE_RESERVATIONS_APP_ID); }
|
|
77
|
+
|
|
78
|
+
/** True when the Menus API answers — i.e. the app is already on the site. */
|
|
79
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/list-menus.md
|
|
80
|
+
export async function menusAppPresent(ctx) {
|
|
81
|
+
try {
|
|
82
|
+
await req(ctx, "/restaurants/menus/v1/menus", { method: "GET" });
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---- menu (setup-restaurants.md) -----------------------------------------------------------------
|
|
90
|
+
// Everything is Restaurants Menus V1 on /restaurants/menus/v1/... . REST flattens the
|
|
91
|
+
// protobuf wrappers: plain values ("visible": true), never {"value": …}.
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Recipe STEP 0 — a FRESH Menus-app install ships a populated sample "Dinner Menu"
|
|
95
|
+
* (~4 sections, ~21 items) that would render next to the seeded menu. Call ONLY when this
|
|
96
|
+
* run installed the app onto a site that didn't have it (menusAppPresent was false) — then
|
|
97
|
+
* everything present is provably the install's own sample. Polls briefly (the sample
|
|
98
|
+
* provisions async), then deletes children before parents. No-op when nothing appears.
|
|
99
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/list-items.md
|
|
100
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/list-sections.md
|
|
101
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-delete-items.md
|
|
102
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/bulk-delete-sections.md
|
|
103
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/delete-menu.md
|
|
104
|
+
*/
|
|
105
|
+
export async function removeSampleMenu(ctx, { tries = 8, delayMs = 2000 } = {}) {
|
|
106
|
+
let menus = [];
|
|
107
|
+
for (let i = 0; i < tries; i++) {
|
|
108
|
+
const r = await req(ctx, "/restaurants/menus/v1/menus", { method: "GET" });
|
|
109
|
+
menus = r.menus ?? [];
|
|
110
|
+
if (menus.length) break;
|
|
111
|
+
if (i < tries - 1) await sleep(delayMs);
|
|
112
|
+
}
|
|
113
|
+
if (!menus.length) return { removed: false };
|
|
114
|
+
|
|
115
|
+
const itemsRes = await req(ctx, "/restaurants/menus/v1/items", { method: "GET" });
|
|
116
|
+
const sectionsRes = await req(ctx, "/restaurants/menus/v1/sections", { method: "GET" });
|
|
117
|
+
const itemIds = (itemsRes.items ?? []).map((it) => it.id).filter(Boolean);
|
|
118
|
+
const sectionIds = (sectionsRes.sections ?? []).map((s) => s.id).filter(Boolean);
|
|
119
|
+
if (itemIds.length) {
|
|
120
|
+
await req(ctx, "/restaurants/menus/v1/bulk/items/delete", { method: "DELETE", body: { ids: itemIds } });
|
|
121
|
+
}
|
|
122
|
+
if (sectionIds.length) {
|
|
123
|
+
await req(ctx, "/restaurants/menus/v1/bulk/sections/delete", { method: "DELETE", body: { ids: sectionIds } });
|
|
124
|
+
}
|
|
125
|
+
for (const m of menus) {
|
|
126
|
+
await req(ctx, `/restaurants/menus/v1/menus/${m.id}`, { method: "DELETE" }); // no bulk delete for menus
|
|
127
|
+
}
|
|
128
|
+
return { removed: true, menus: menus.map((m) => m.name) };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build ONE menu BOTTOM-UP (items → sections → menu) in three bulk phases.
|
|
133
|
+
* price -> priceInfo.price as a decimal STRING; currency is the site's (send none).
|
|
134
|
+
* visible:true is baked in at every level — required to render on the live site.
|
|
135
|
+
* Returns { menuId, name, sectionIds, itemIds, items: [{ id, revision, price }] } — the
|
|
136
|
+
* per-item revision/price feed the image pass (Update Item is a full replace).
|
|
137
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-create-items.md
|
|
138
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/sections/bulk-create-sections.md
|
|
139
|
+
* docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/create-menu.md
|
|
140
|
+
*/
|
|
141
|
+
export async function createMenu(ctx, menu) {
|
|
142
|
+
// STEP 1 — bulk-create every item across all sections in ONE request.
|
|
143
|
+
const flat = [];
|
|
144
|
+
menu.sections.forEach((sec, si) => (sec.items || []).forEach((it) => flat.push({ ...it, _section: si })));
|
|
145
|
+
const itemRes = await req(ctx, "/restaurants/menus/v1/bulk/items/create", {
|
|
146
|
+
body: {
|
|
147
|
+
items: flat.map((it) => ({
|
|
148
|
+
name: it.name,
|
|
149
|
+
...(it.description ? { description: it.description } : {}),
|
|
150
|
+
priceInfo: { price: String(it.price) },
|
|
151
|
+
visible: true,
|
|
152
|
+
})),
|
|
153
|
+
returnEntity: true,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
// created items are under results[].item (results[].itemMetadata.success is the per-item flag)
|
|
157
|
+
const createdItems = (itemRes.results ?? []).map((r) => r.item);
|
|
158
|
+
const itemIdsBySection = menu.sections.map(() => []);
|
|
159
|
+
flat.forEach((it, i) => {
|
|
160
|
+
const id = createdItems[i]?.id;
|
|
161
|
+
if (id) itemIdsBySection[it._section].push(id);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// STEP 2 — bulk-create sections, each carrying the itemIds of its items in display order.
|
|
165
|
+
const secRes = await req(ctx, "/restaurants/menus/v1/bulk/sections/create", {
|
|
166
|
+
body: {
|
|
167
|
+
sections: menu.sections.map((sec, si) => ({
|
|
168
|
+
name: sec.name,
|
|
169
|
+
...(sec.description ? { description: sec.description } : {}),
|
|
170
|
+
visible: true,
|
|
171
|
+
itemIds: itemIdsBySection[si],
|
|
172
|
+
})),
|
|
173
|
+
returnEntity: true,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const sectionIds = (secRes.results ?? []).map((r) => r.item?.id);
|
|
177
|
+
|
|
178
|
+
// STEP 3 — create the menu (single create wraps in `menu`), carrying its sectionIds.
|
|
179
|
+
// businessLocationId omitted -> binds to the site's default (main) location.
|
|
180
|
+
const menuRes = await req(ctx, "/restaurants/menus/v1/menus", {
|
|
181
|
+
body: {
|
|
182
|
+
menu: {
|
|
183
|
+
name: menu.name,
|
|
184
|
+
...(menu.description ? { description: menu.description } : {}),
|
|
185
|
+
visible: true,
|
|
186
|
+
sectionIds,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
menuId: menuRes.menu?.id,
|
|
192
|
+
name: menu.name,
|
|
193
|
+
sectionIds,
|
|
194
|
+
itemIds: createdItems.map((it) => it?.id),
|
|
195
|
+
items: createdItems.map((it, i) => ({ id: it?.id, revision: it?.revision, price: flat[i]?.price })),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Restaurants binds an item image by Wix Media file ID — an external url must be imported
|
|
200
|
+
// first; a plan `imagePrompt` is generated (Wix AI, 1 credit) then imported. Both live in the
|
|
201
|
+
// shared util (parallel, resilient, never blocks the seed).
|
|
202
|
+
export { importImage } from "../../shared/seed/images.mjs";
|
|
203
|
+
|
|
204
|
+
// Image pass. Update Item is a FULL-ENTITY REPLACE with NO field mask — each entry MUST echo
|
|
205
|
+
// the item's current `revision` AND `priceInfo`, or it fails 428 MISSING_ITEM_PRICING and the
|
|
206
|
+
// image does NOT apply. `image` is an OBJECT { id, url, height, width } (never a bare string);
|
|
207
|
+
// the binding field is the Wix Media file `id`.
|
|
208
|
+
// items: [{ id, revision, price, image: { id, url, height, width } }]
|
|
209
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/bulk-update-item.md
|
|
210
|
+
export async function attachItemImages(ctx, items) {
|
|
211
|
+
return req(ctx, "/restaurants/menus/v1/bulk/items/update", {
|
|
212
|
+
body: {
|
|
213
|
+
items: items.map((it) => ({
|
|
214
|
+
item: {
|
|
215
|
+
id: it.id,
|
|
216
|
+
revision: it.revision,
|
|
217
|
+
priceInfo: { price: String(it.price) },
|
|
218
|
+
image: it.image,
|
|
219
|
+
},
|
|
220
|
+
})),
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ---- business location (shared STEP 0 for ordering + reservations) ------------------------------
|
|
226
|
+
// Update Location is a FULL OVERRIDE — send the WHOLE `location` object (omitted fields are
|
|
227
|
+
// wiped), echo `default:true` (omitting it 400s CHANGE_DEFAULT_FORBIDDEN) and the current
|
|
228
|
+
// `revision`. address.country is a 2-letter ISO code. Without a real address, ordering is
|
|
229
|
+
// "testing only" and checkout breaks — a placeholder must be flagged to the owner.
|
|
230
|
+
// location: { name, timeZone, email?, phone?, address: { country, subdivision, city,
|
|
231
|
+
// postalCode, streetAddress: { number, name }, formattedAddress } }
|
|
232
|
+
// docs: https://dev.wix.com/docs/api-reference/business-management/locations/list-locations.md
|
|
233
|
+
// docs: https://dev.wix.com/docs/api-reference/business-management/locations/create-location.md
|
|
234
|
+
// docs: https://dev.wix.com/docs/api-reference/business-management/locations/update-location.md
|
|
235
|
+
export async function setBusinessLocation(ctx, location) {
|
|
236
|
+
const list = await req(ctx, "/locations/v1/locations", { method: "GET" });
|
|
237
|
+
const def = (list.locations ?? []).find((l) => l.default);
|
|
238
|
+
if (!def) {
|
|
239
|
+
// No default location at all (a bare site can have none) -> CREATE one; operations
|
|
240
|
+
// auto-bind on first-location-add.
|
|
241
|
+
const r = await req(ctx, "/locations/v1/locations", { body: { location: { ...location, default: true } } });
|
|
242
|
+
return r.location;
|
|
243
|
+
}
|
|
244
|
+
const r = await req(ctx, `/locations/v1/locations/${def.id}`, {
|
|
245
|
+
method: "PUT",
|
|
246
|
+
body: { location: { ...location, id: def.id, revision: def.revision, default: true } },
|
|
247
|
+
});
|
|
248
|
+
return r.location;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ---- online ordering (setup-restaurant-orders.md) ------------------------------------------------
|
|
252
|
+
// The Orders-app install AUTO-provisions a working setup (an ENABLED operation with Pickup +
|
|
253
|
+
// Delivery attached, every menu ordering-enabled) — these helpers VERIFY it; they never POST
|
|
254
|
+
// an operation. Each micro-service is on its OWN host prefix — do not normalize.
|
|
255
|
+
|
|
256
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/operations/list-operations.md
|
|
257
|
+
export async function listOperations(ctx) {
|
|
258
|
+
const r = await req(ctx, "/restaurants-operations/v1/operations", { method: "GET" });
|
|
259
|
+
return r.operations ?? [];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// A fresh install provisions the operation ASYNC — poll until it lands.
|
|
263
|
+
export async function listOperationsWithRetry(ctx, { tries = 15, delayMs = 2000 } = {}) {
|
|
264
|
+
for (let i = 0; i < tries; i++) {
|
|
265
|
+
const ops = await listOperations(ctx).catch(() => []);
|
|
266
|
+
if (ops.length) return ops;
|
|
267
|
+
if (i < tries - 1) await sleep(delayMs);
|
|
268
|
+
}
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Normally already ENABLED — only PATCH when DISABLED/PAUSED_UNTIL. revision mandatory + current.
|
|
273
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/operations/update-operation.md
|
|
274
|
+
export async function enableOperation(ctx, operationId, revision) {
|
|
275
|
+
return req(ctx, `/restaurants-operations/v1/operations/${operationId}`, {
|
|
276
|
+
method: "PATCH",
|
|
277
|
+
body: { operation: { revision, onlineOrderingStatus: "ENABLED" } },
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/menu-ordering-settings/query-menu-ordering-settings.md
|
|
282
|
+
export async function queryMenuOrderingSettings(ctx) {
|
|
283
|
+
const r = await req(ctx, "/menu-ordering-settings/v1/menu-ordering-settings/query", { body: { query: {} } });
|
|
284
|
+
return r.menuOrderingSettings ?? [];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Only when an entry shows onlineOrderingEnabled:false / operationId:"none".
|
|
288
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/menu-ordering-settings/update-menu-ordering-settings.md
|
|
289
|
+
export async function updateMenuOrderingSettings(ctx, settingsId, patch) {
|
|
290
|
+
return req(ctx, `/menu-ordering-settings/v1/menu-ordering-settings/${settingsId}`, {
|
|
291
|
+
method: "PATCH",
|
|
292
|
+
body: { menuOrderingSettings: patch },
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ---- table reservations (setup-restaurant-reservations.md) ---------------------------------------
|
|
297
|
+
// The install AUTO-provisions one default reservation location with a complete config; the
|
|
298
|
+
// one thing OFF is onlineReservationsEnabled (premium-gated). A reservation location cannot
|
|
299
|
+
// be created via this API — discover, configure, enable. Post-Jan-2026 field names:
|
|
300
|
+
// partySize (not partiesSize), approval (not manualApproval).
|
|
301
|
+
|
|
302
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/list-reservation-locations.md
|
|
303
|
+
export async function listReservationLocations(ctx) {
|
|
304
|
+
const r = await req(ctx, "/table-reservations/reservation-locations/v1/reservation-locations", { method: "GET" });
|
|
305
|
+
return r.reservationLocations ?? [];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export async function listReservationLocationsWithRetry(ctx, { tries = 15, delayMs = 2000 } = {}) {
|
|
309
|
+
for (let i = 0; i < tries; i++) {
|
|
310
|
+
const locs = await listReservationLocations(ctx).catch(() => []);
|
|
311
|
+
if (locs.length) return locs;
|
|
312
|
+
if (i < tries - 1) await sleep(delayMs);
|
|
313
|
+
}
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Partial PATCH; revision mandatory; works on a non-premium site. The `location` object
|
|
318
|
+
// (address/name) is IMMUTABLE here — only touch `configuration`.
|
|
319
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/update-reservation-location.md
|
|
320
|
+
export async function updateReservationLocation(ctx, reservationLocationId, revision, configuration) {
|
|
321
|
+
return req(ctx, `/table-reservations/reservation-locations/v1/reservation-locations/${reservationLocationId}`, {
|
|
322
|
+
method: "PATCH",
|
|
323
|
+
body: { reservationLocation: { id: reservationLocationId, revision, configuration } },
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// PREMIUM-ONLY: on a non-premium site this THROWS `428 PREMIUM_ONLY` — expected and
|
|
328
|
+
// non-fatal; the caller records it and continues (never retry-spiral, never fail the seed).
|
|
329
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/update-reservation-location.md
|
|
330
|
+
export async function enableOnlineReservations(ctx, reservationLocationId, revision) {
|
|
331
|
+
return req(ctx, `/table-reservations/reservation-locations/v1/reservation-locations/${reservationLocationId}`, {
|
|
332
|
+
method: "PATCH",
|
|
333
|
+
body: {
|
|
334
|
+
reservationLocation: {
|
|
335
|
+
id: reservationLocationId,
|
|
336
|
+
revision,
|
|
337
|
+
configuration: { onlineReservations: { onlineReservationsEnabled: true } },
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* ONE-CALL seed: Menus install (+ sample cleanup when fresh) → menus bottom-up → images →
|
|
345
|
+
* ordering add-on → reservations add-on, ids threaded in memory. The default path.
|
|
346
|
+
*/
|
|
347
|
+
export async function setupRestaurants(ctx, plan) {
|
|
348
|
+
const menusPlan = plan.menus ?? [];
|
|
349
|
+
const wasPresent = await menusAppPresent(ctx);
|
|
350
|
+
await installMenusApp(ctx);
|
|
351
|
+
let sampleMenuRemoved = false;
|
|
352
|
+
if (!wasPresent) {
|
|
353
|
+
const r = await removeSampleMenu(ctx).catch(() => ({ removed: false }));
|
|
354
|
+
sampleMenuRemoved = r.removed === true;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const createdMenus = [];
|
|
358
|
+
for (const m of menusPlan) createdMenus.push(await createMenu(ctx, m));
|
|
359
|
+
|
|
360
|
+
// image pass — resolve each item's image (import by url / generate by prompt) in ONE
|
|
361
|
+
// parallel wave (restaurants binds by file id), then bulk full-replace with revision +
|
|
362
|
+
// priceInfo echoed. Never block on image failure.
|
|
363
|
+
let imagesAttached = 0;
|
|
364
|
+
const imageItems = [];
|
|
365
|
+
menusPlan.forEach((m, mi) => {
|
|
366
|
+
const flat = m.sections.flatMap((s) => s.items || []);
|
|
367
|
+
flat.forEach((it, i) => {
|
|
368
|
+
const created = createdMenus[mi]?.items?.[i];
|
|
369
|
+
if ((it.imageUrl || it.imagePrompt) && created?.id) {
|
|
370
|
+
imageItems.push({ ...created, imageUrl: it.imageUrl, imagePrompt: it.imagePrompt, name: it.name });
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
const files = await resolveItemImages(ctx, imageItems.map((it) => ({
|
|
375
|
+
url: it.imageUrl,
|
|
376
|
+
path: it.imagePath,
|
|
377
|
+
prompt: it.imagePrompt,
|
|
378
|
+
displayName: `${it.name || "item"}.png`,
|
|
379
|
+
})));
|
|
380
|
+
const toAttach = imageItems
|
|
381
|
+
.map((it, i) => (files[i]
|
|
382
|
+
? { id: it.id, revision: it.revision ?? "1", price: it.price,
|
|
383
|
+
image: { id: files[i].id, url: files[i].url, width: 1024, height: 1024 } }
|
|
384
|
+
: null))
|
|
385
|
+
.filter(Boolean);
|
|
386
|
+
if (toAttach.length) {
|
|
387
|
+
try {
|
|
388
|
+
await attachItemImages(ctx, toAttach);
|
|
389
|
+
imagesAttached = toAttach.length;
|
|
390
|
+
} catch {
|
|
391
|
+
/* the items stay text-only */
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// shared STEP 0 address — set once across both add-ons.
|
|
396
|
+
let locationSet = false;
|
|
397
|
+
const ensureLocation = async (address) => {
|
|
398
|
+
if (address && !locationSet) {
|
|
399
|
+
await setBusinessLocation(ctx, address);
|
|
400
|
+
locationSet = true;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const ordering = { enabled: false, addressSet: false };
|
|
405
|
+
if (plan.ordering) {
|
|
406
|
+
const cfg = typeof plan.ordering === "object" ? plan.ordering : {};
|
|
407
|
+
await installOrdersApp(ctx); // auto-provisions operation + methods + per-menu settings
|
|
408
|
+
await ensureLocation(cfg.address);
|
|
409
|
+
ordering.addressSet = locationSet;
|
|
410
|
+
const ops = await listOperationsWithRetry(ctx);
|
|
411
|
+
const op = ops.find((o) => o.default) ?? ops[0];
|
|
412
|
+
if (!op) throw new Error("No ordering operation appeared — the Orders app install may not have completed; re-run the seed.");
|
|
413
|
+
if (op.onlineOrderingStatus !== "ENABLED") await enableOperation(ctx, op.id, op.revision);
|
|
414
|
+
// confirm each menu is orderable (auto-created + auto-enabled per menu; PATCH only when off)
|
|
415
|
+
const settings = await queryMenuOrderingSettings(ctx);
|
|
416
|
+
for (const s of settings) {
|
|
417
|
+
if (s.onlineOrderingEnabled === false || s.operationId === "none") {
|
|
418
|
+
await updateMenuOrderingSettings(ctx, s.id, {
|
|
419
|
+
revision: s.revision,
|
|
420
|
+
operationId: op.id,
|
|
421
|
+
onlineOrderingEnabled: true,
|
|
422
|
+
availability: { type: "ALWAYS_AVAILABLE", timeZone: cfg.address?.timeZone ?? "America/New_York" },
|
|
423
|
+
}).catch(() => {});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
ordering.enabled = true;
|
|
427
|
+
ordering.operationId = op.id;
|
|
428
|
+
if (!ordering.addressSet) ordering.note = "No address in the plan — ordering is 'testing only' until the owner sets the real business address.";
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const reservations = { enabled: false };
|
|
432
|
+
if (plan.reservations) {
|
|
433
|
+
const cfg = typeof plan.reservations === "object" ? plan.reservations : {};
|
|
434
|
+
await installTableReservationsApp(ctx); // auto-provisions the default reservation location
|
|
435
|
+
await ensureLocation(cfg.address);
|
|
436
|
+
let [loc] = await listReservationLocationsWithRetry(ctx);
|
|
437
|
+
if (!loc) throw new Error("No reservation location appeared — the Table Reservations install may not have completed; re-run the seed.");
|
|
438
|
+
const configuration = cfg.configuration ?? (cfg.partySize ? { onlineReservations: { partySize: cfg.partySize } } : null);
|
|
439
|
+
if (configuration) {
|
|
440
|
+
await updateReservationLocation(ctx, loc.id, loc.revision, configuration);
|
|
441
|
+
[loc] = await listReservationLocations(ctx); // re-read for the bumped revision
|
|
442
|
+
}
|
|
443
|
+
reservations.reservationLocationId = loc.id;
|
|
444
|
+
try {
|
|
445
|
+
await enableOnlineReservations(ctx, loc.id, loc.revision);
|
|
446
|
+
reservations.enabled = true;
|
|
447
|
+
} catch {
|
|
448
|
+
// 428 PREMIUM_ONLY on a free site — expected; record, don't fail.
|
|
449
|
+
reservations.premiumRequired = true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
menus: createdMenus.map(({ items, ...m }) => m),
|
|
455
|
+
imagesAttached,
|
|
456
|
+
sampleMenuRemoved,
|
|
457
|
+
ordering,
|
|
458
|
+
reservations,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
463
|
+
|
|
464
|
+
const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
|
|
465
|
+
if (invokedDirectly) {
|
|
466
|
+
const planPath = process.argv[2];
|
|
467
|
+
if (!planPath) {
|
|
468
|
+
console.error("usage: node seed-restaurants.mjs <plan.json> (run from the project root)");
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
472
|
+
const ctx = makeCtx();
|
|
473
|
+
setupRestaurants(ctx, plan)
|
|
474
|
+
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
475
|
+
.catch((e) => {
|
|
476
|
+
console.error(e.message);
|
|
477
|
+
process.exit(1);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Content floor — the copy you write on every authored surface
|
|
2
|
+
|
|
3
|
+
**A fallback, not an override.** If the user supplied copy, voice, or tone, use theirs. The
|
|
4
|
+
one exception is the first rule — a comprehension floor, not a style preference.
|
|
5
|
+
|
|
6
|
+
- **Say what the business is, literally, above the fold.** A first-time visitor must know
|
|
7
|
+
what the site *is* and can *do* from the hero alone. The business category ("ceramics
|
|
8
|
+
studio", "trattoria", "yoga studio") appears verbatim in the H1, the kicker above it, or
|
|
9
|
+
the first clause of the subtitle. A witty H1 is welcome — beside an explicit category,
|
|
10
|
+
never instead of one.
|
|
11
|
+
- **The subtitle leads with the offer** — the verb the visitor cares about comes early
|
|
12
|
+
("Book…", "Order…", "Browse…"), flavor after.
|
|
13
|
+
- **Kickers carry information or don't exist.** "Handmade in Portland · Since 2019" earns its
|
|
14
|
+
place; "The Collection" above a Collection heading doesn't. A voice-led H1 pairs with an
|
|
15
|
+
informative kicker (that's where the category goes).
|
|
16
|
+
- **Titles and subtitles end without a period.** Paragraphs keep theirs.
|
|
17
|
+
- **CTA labels are short, concrete, and not all-caps** — the actual action, matching the one
|
|
18
|
+
primary CTA per page (`DESIGN.md`).
|
|
19
|
+
- **No fluff.** Every line moves a visitor's understanding forward — the H1 and subtitle most
|
|
20
|
+
of all.
|
|
21
|
+
- **A caption describes what its image actually shows.** A slogan overlaid on a photo is not
|
|
22
|
+
a caption; if none is warranted, write none.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Custom operations
|
|
2
|
+
|
|
3
|
+
Shipped vertical code covers its advertised contract. A brief can also require an operation
|
|
4
|
+
outside that contract: a member submission, a file derived in the browser, moderation, a
|
|
5
|
+
custom business write, or a third-party service.
|
|
6
|
+
|
|
7
|
+
## Decide the boundary first
|
|
8
|
+
|
|
9
|
+
1. **Caller-permitted Wix operation** — add one function to the appropriate `src/wix/<vertical>/`
|
|
10
|
+
data layer. Use the documented SDK method; components call that function, never the SDK.
|
|
11
|
+
2. **Privileged Wix operation** — on Wix-managed Astro, create a narrow `src/pages/api/` endpoint.
|
|
12
|
+
Authenticate/authorize the caller and validate the exact input before elevating the one SDK
|
|
13
|
+
call with `auth.elevate()`. On another stack, the equivalent belongs on a server using
|
|
14
|
+
server-only credentials. Never expose an elevated method or credential to the browser.
|
|
15
|
+
3. **No documented Wix path** — simplify the feature to a supported design. Do not infer a
|
|
16
|
+
contract from generated types, SDK implementation files, or `node_modules`.
|
|
17
|
+
|
|
18
|
+
## The research rule
|
|
19
|
+
|
|
20
|
+
Read the relevant shipped playbook first. For a genuine gap, use `wix-docs` to read the
|
|
21
|
+
official method or feature guide, then implement that contract. One documentation lookup and
|
|
22
|
+
one type check are enough before coding. Investigate further only after a concrete error.
|
|
23
|
+
|
|
24
|
+
## User-created public content
|
|
25
|
+
|
|
26
|
+
Use the CMS vertical when a visitor or member submits content that the app must later list or
|
|
27
|
+
show publicly. Seed the collection with the appropriate permission preset and never trust the
|
|
28
|
+
browser to choose an owner, collection, or privileged action.
|
|
29
|
+
|
|
30
|
+
For a small browser-created artifact, the official Wix-managed Astro pattern is a validated
|
|
31
|
+
server endpoint that writes the contribution with elevation. For binary media, first confirm
|
|
32
|
+
the Media Files API's authorization requirements: generating a media upload URL requires
|
|
33
|
+
Manage Media Manager permission, so it is not a browser-side member call. Validate identity,
|
|
34
|
+
MIME type, size, and destination before any elevated call. The authoritative references are:
|
|
35
|
+
|
|
36
|
+
- `wix-docs`: **Upload Images to CMS** for the secure CMS contribution flow.
|
|
37
|
+
- `wix-docs`: **Generate File Upload URL** and **Upload API** for Media Manager files.
|
|
38
|
+
|
|
39
|
+
Keep the endpoint specific to the allowed contribution. An endpoint that accepts arbitrary
|
|
40
|
+
SDK method names, collection ids, or upload destinations is a privilege-escalation bug.
|
|
41
|
+
|
|
42
|
+
## Optional Media Upload capability
|
|
43
|
+
|
|
44
|
+
Use this only when a site actually needs a normal file upload (not a small canvas artifact
|
|
45
|
+
stored as a CMS data URL). Put the policy in the site plan; the Fast deployer ships the shared
|
|
46
|
+
client helper and Astro endpoint once, plus a generated read-only policy module:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"capabilities": {
|
|
51
|
+
"mediaUpload": {
|
|
52
|
+
"policies": [{
|
|
53
|
+
"id": "gallery-artwork",
|
|
54
|
+
"accept": ["image/png", "image/jpeg"],
|
|
55
|
+
"maxBytes": 5242880
|
|
56
|
+
}]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generated policy lives at `src/wix/media-upload/policies.generated.ts`; do not hand-edit
|
|
63
|
+
it. The shared browser helper is `src/wix/media-upload/client.ts`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const media = await uploadMedia("gallery-artwork", pngBlob, "my-drawing.png");
|
|
67
|
+
// media.id and media.url are the Wix Media reference to store in the intended CMS image field.
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The browser can select only the **named policy** and the bytes. The deployed API route fixes
|
|
71
|
+
the destination and elevates only `files.generateFileUploadUrl`. A vertical uses the same
|
|
72
|
+
capability by putting its own policy in its plan. Do not copy, fork, or widen the endpoint in
|
|
73
|
+
vertical code.
|