@wix/agent-skills 1.15.1 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -7
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +4 -1
- package/skills/wix-app/references/APP_TOOLS.md +134 -0
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +1 -1
- package/skills/wix-app/references/DASHBOARD_PAGE.md +0 -10
- package/skills/wix-app/references/DATA_COLLECTION.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +28 -9
- package/skills/wix-app/references/SERVICE_PLUGIN.md +12 -10
- package/skills/wix-app/references/app-tools/TOOLS.md +75 -0
- package/skills/wix-app/references/dashboard-page/DASHBOARD_API.md +2 -2
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW-REFERENCE.md +172 -0
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +138 -0
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +23 -2
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +29 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +1 -1
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +27 -2
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +5 -3
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +18 -1
- package/skills/wix-app/references/editor-react-component/PARTS.md +15 -2
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -3
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +13 -9
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +4 -0
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +3 -1
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +4 -0
- package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +54 -0
- package/skills/wix-app/references/stores/QUERY.md +32 -0
- package/skills/wix-docs/SKILL.md +35 -3
- package/skills/wix-headless/SKILL.md +3 -1
- package/skills/wix-headless/references/CONTENT.md +36 -0
- package/skills/wix-headless/references/DESIGN.md +54 -0
- package/skills/wix-headless/references/DISCOVERY.md +24 -0
- package/skills/wix-headless/references/IMAGE_GENERATION.md +10 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +3 -2
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +32 -23
- package/skills/wix-headless/references/managed/CONNECT.md +1 -1
- package/skills/wix-headless/references/managed/CREATE.md +14 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +25 -0
- package/skills/wix-manage/SKILL.md +123 -7
- package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +41 -0
- package/skills/wix-manage/references/app-installation/app-installation-dashboard-navigation.md +28 -0
- package/skills/wix-manage/references/blog/blog-dashboard-navigation.md +56 -0
- package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +31 -6
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -1
- package/skills/wix-manage/references/bookings/bookings-dashboard-navigation.md +63 -0
- package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +10 -1
- package/skills/wix-manage/references/cms/cms-dashboard-navigation.md +44 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +31 -3
- package/skills/wix-manage/references/cms/cms-publishing-flow.md +287 -0
- package/skills/wix-manage/references/cms/cms-schema-management.md +27 -1
- package/skills/wix-manage/references/contacts/contacts-dashboard-navigation.md +46 -0
- package/skills/wix-manage/references/contacts/create-a-contact.md +82 -0
- package/skills/wix-manage/references/contacts/update-a-contact.md +119 -0
- package/skills/wix-manage/references/dashboard-navigation/dashboard-navigation.md +82 -0
- package/skills/wix-manage/references/dashboard-navigation/routes.json +1733 -0
- package/skills/wix-manage/references/domains/domains-dashboard-navigation.md +32 -0
- package/skills/wix-manage/references/ecommerce/ecom-pricing.md +4 -4
- package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
- package/skills/wix-manage/references/ecommerce/gift-cards/ecom-gift-cards-goal-sell-gift-cards.md +338 -0
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-discount-rule.md +42 -78
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-bundle-and-save.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-stock-mover.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-upsell-boost.md +1 -1
- package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +90 -92
- package/skills/wix-manage/references/events/create-wix-event.md +176 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +174 -0
- package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +44 -0
- package/skills/wix-manage/references/get-paid/get-paid-dashboard-navigation.md +52 -0
- package/skills/wix-manage/references/google-ads/billing-and-payment.md +80 -0
- package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +212 -0
- package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +110 -0
- package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +25 -0
- package/skills/wix-manage/references/google-ads/install-and-create-account.md +155 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +63 -0
- package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +155 -0
- package/skills/wix-manage/references/marketing/marketing-dashboard-navigation.md +52 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +99 -1
- package/skills/wix-manage/references/pricing-plans/pricing-plans-dashboard-navigation.md +45 -0
- package/skills/wix-manage/references/restaurants/restaurants-dashboard-navigation.md +68 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +94 -125
- package/skills/wix-manage/references/site-properties/change-payment-currency-site-properties.md +48 -9
- package/skills/wix-manage/references/site-properties/site-properties-dashboard-navigation.md +31 -0
- package/skills/wix-manage/references/sites/create-headless-site.md +52 -0
- package/skills/wix-manage/references/sites/create-site-from-template.md +2 -25
- package/skills/wix-manage/references/sites/site-import.md +163 -190
- package/skills/wix-manage/references/sites/sites-dashboard-navigation.md +29 -0
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +17 -1
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +26 -8
- package/skills/wix-manage/references/stores/stores-dashboard-navigation.md +65 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +39 -7
- package/skills/wix-replatform/.publish-manifest.json +8 -0
- package/skills/wix-replatform/SKILL.md +693 -0
- package/skills/wix-replatform/lib/artifact-freshness.js +215 -0
- package/skills/wix-replatform/lib/completion-report.js +200 -0
- package/skills/wix-replatform/lib/config-env.js +59 -0
- package/skills/wix-replatform/lib/execution-manifest.js +185 -0
- package/skills/wix-replatform/lib/execution-state.js +173 -0
- package/skills/wix-replatform/lib/import-recovery.js +313 -0
- package/skills/wix-replatform/lib/local-state.js +642 -0
- package/skills/wix-replatform/lib/orchestration-preflight.js +143 -0
- package/skills/wix-replatform/lib/orchestration-router.js +122 -0
- package/skills/wix-replatform/lib/orchestration-state.js +455 -0
- package/skills/wix-replatform/lib/progress-log.js +224 -0
- package/skills/wix-replatform/lib/progress-log.sh +140 -0
- package/skills/wix-replatform/lib/url-preservation-state.js +391 -0
- package/skills/wix-replatform/resources/rp-discovery/SKILL.md +184 -0
- package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +529 -0
- package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +316 -0
- package/skills/wix-replatform/resources/rp-execute-setup/scripts/mint-token.sh +64 -0
- package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +1114 -0
- package/skills/wix-replatform/resources/rp-mapper/SKILL.md +430 -0
- package/skills/wix-replatform/resources/rp-mapper/lib/mapping-resolve.js +217 -0
- package/skills/wix-replatform/resources/rp-mapper/scripts/resolve-mapping.js +148 -0
- package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +378 -0
- package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +183 -0
- package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +478 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/canonical-record.js +136 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fingerprint.js +310 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-layout.js +1081 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-parse.js +618 -0
- package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +949 -0
- package/skills/wix-replatform/resources/rp-source-csv/source-schema.example.json +196 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/bigcommerce.json +66 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/magento.json +60 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/shopify.json +95 -0
- package/skills/wix-replatform/resources/rp-source-csv/vendors/woocommerce.json +82 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +256 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +249 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +444 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +774 -0
- package/skills/wix-replatform/resources/rp-source-wordpress/source-schema.example.json +71 -0
- package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +632 -0
- package/skills/wix-replatform/resources/rp-target-wix/SPEC-product-reviews-and-blog-authorship.md +330 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +21 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +19 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +39 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +36 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +35 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +131 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +21 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +97 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +528 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +14 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +38 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +13 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +53 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +84 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +25 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +38 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +37 -0
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +90 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/audit-summary.js +64 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +141 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +384 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/ndjson.js +260 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +238 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +237 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +238 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +2219 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +31 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge.js +100 -0
- package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +107 -0
- package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +344 -0
- package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +250 -0
- package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +1927 -0
- package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +83 -0
- package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +141 -0
- package/skills/wix-replatform/scripts/artifact-freshness.js +76 -0
- package/skills/wix-replatform/scripts/execution-manifest-validate.js +43 -0
- package/skills/wix-replatform/scripts/execution-state-prepare.js +128 -0
- package/skills/wix-replatform/scripts/orchestration-preflight.js +48 -0
- package/skills/wix-replatform/scripts/orchestration-route.js +46 -0
- package/skills/wix-replatform/scripts/orchestration-state.js +245 -0
- package/skills/wix-vibe-headless/SKILL.md +48 -25
- package/skills/wix-vibe-headless/install/deploy.cjs +95 -0
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +34 -0
- package/skills/wix-vibe-headless/platforms/README.md +8 -4
- package/skills/wix-vibe-headless/platforms/base44.md +133 -98
- package/skills/wix-vibe-headless/platforms/generic.md +99 -64
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +230 -119
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +43 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +25 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/blog/app/context/TaxonomyContext.jsx +35 -0
- package/skills/wix-vibe-headless/references/blog/app/hooks/usePostDetail.js +38 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/Blog.jsx +40 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +54 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +65 -0
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +50 -0
- package/skills/wix-vibe-headless/references/blog/seed/SEED.md +103 -0
- package/skills/wix-vibe-headless/references/blog/seed/seed-blog.js +270 -0
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +260 -149
- package/skills/wix-vibe-headless/references/bookings/app/components/BookingForm.jsx +65 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +47 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceGrid.jsx +13 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/SlotPicker.jsx +126 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +108 -0
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServices.js +58 -0
- package/skills/wix-vibe-headless/references/bookings/app/lib/serviceFacts.js +95 -0
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +94 -0
- package/skills/wix-vibe-headless/references/bookings/app/pages/Services.jsx +89 -0
- package/skills/wix-vibe-headless/references/bookings/{wix-bookings-checkout.js → app/rest/wix-bookings-checkout.js} +1 -1
- package/skills/wix-vibe-headless/references/bookings/{wix-bookings-services.js → app/rest/wix-bookings-services.js} +87 -11
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +117 -0
- package/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.js +333 -0
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +169 -147
- package/skills/wix-vibe-headless/references/cms/app/collection.config.js +31 -0
- package/skills/wix-vibe-headless/references/cms/app/lib/wixImage.js +18 -0
- package/skills/wix-vibe-headless/references/cms/{wix-cms.js → app/rest/wix-cms.js} +47 -2
- package/skills/wix-vibe-headless/references/cms/seed/SEED.md +145 -0
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +228 -131
- package/skills/wix-vibe-headless/references/events/app/components/CategoryFilter.jsx +23 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +43 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventGrid.jsx +13 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventRegistration.jsx +29 -0
- package/skills/wix-vibe-headless/references/events/app/components/RsvpForm.jsx +57 -0
- package/skills/wix-vibe-headless/references/events/app/components/TicketPicker.jsx +78 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventDetail.js +27 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventsList.js +54 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useRsvpForm.js +40 -0
- package/skills/wix-vibe-headless/references/events/app/hooks/useTicketing.js +81 -0
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +48 -0
- package/skills/wix-vibe-headless/references/events/app/pages/Events.jsx +36 -0
- package/skills/wix-vibe-headless/references/events/{wix-events-browse.js → app/rest/wix-events-browse.js} +1 -1
- package/skills/wix-vibe-headless/references/events/seed/SEED.md +107 -0
- package/skills/wix-vibe-headless/references/events/seed/seed-events.js +251 -0
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +276 -188
- package/skills/wix-vibe-headless/references/members/app/components/LoginForm.jsx +101 -0
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +33 -0
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +21 -0
- package/skills/wix-vibe-headless/references/members/app/components/SocialButtons.jsx +29 -0
- package/skills/wix-vibe-headless/references/members/app/context/MemberContext.jsx +54 -0
- package/skills/wix-vibe-headless/references/members/app/hooks/useLoginForm.js +77 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Account.jsx +36 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Callback.jsx +32 -0
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +35 -0
- package/skills/wix-vibe-headless/references/members/{wix-members-auth.js → app/rest/wix-members-auth.js} +6 -3
- package/skills/wix-vibe-headless/references/members/install/Register.jsx +22 -0
- package/skills/wix-vibe-headless/references/members/seed/SEED.md +22 -0
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +233 -104
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +31 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +39 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectGrid.jsx +15 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectMedia.jsx +29 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useCollectionProjects.js +35 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/usePortfolioGallery.js +27 -0
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useProjectDetail.js +25 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +39 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/Portfolio.jsx +27 -0
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +60 -0
- package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +94 -0
- package/skills/wix-vibe-headless/references/portfolio/seed/seed-portfolio.js +236 -0
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +240 -117
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/OrderCard.jsx +35 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +48 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanGrid.jsx +16 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanPrice.jsx +35 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/useMyPlans.js +25 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlanDetail.js +28 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlans.js +30 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/MyPlans.jsx +30 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +53 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/Plans.jsx +28 -0
- package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +86 -0
- package/skills/wix-vibe-headless/references/pricing-plans/seed/seed-pricing-plans.js +251 -0
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +248 -124
- package/skills/wix-vibe-headless/references/restaurants/app/components/ItemDialog.jsx +81 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuItemCard.jsx +71 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuList.jsx +43 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartButton.jsx +21 -0
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +60 -0
- package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +63 -0
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useItemOrder.js +32 -0
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useReservation.js +73 -0
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Menu.jsx +28 -0
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Reservations.jsx +102 -0
- package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +180 -0
- package/skills/wix-vibe-headless/references/restaurants/seed/seed-restaurants.js +452 -0
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +56 -0
- package/skills/wix-vibe-headless/references/shared/{wix-client.js → app/rest/wix-client.js} +6 -8
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +6 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +263 -157
- package/skills/wix-vibe-headless/references/storefront/app/components/CartButton.jsx +20 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +150 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +167 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGallery.jsx +51 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +54 -0
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +119 -0
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +71 -0
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +109 -0
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useShop.js +98 -0
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +27 -0
- package/skills/wix-vibe-headless/references/storefront/app/pages/ProductDetail.jsx +139 -0
- package/skills/wix-vibe-headless/references/storefront/app/pages/Shop.jsx +75 -0
- package/skills/wix-vibe-headless/references/storefront/{wix-store-cart.js → app/rest/wix-store-cart.js} +1 -1
- package/skills/wix-vibe-headless/references/storefront/{wix-store-catalog.js → app/rest/wix-store-catalog.js} +20 -1
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +111 -0
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +299 -0
- /package/skills/wix-vibe-headless/references/blog/{wix-blog.js → app/rest/wix-blog.js} +0 -0
- /package/skills/wix-vibe-headless/references/events/{wix-events-registration.js → app/rest/wix-events-registration.js} +0 -0
- /package/skills/wix-vibe-headless/references/portfolio/{wix-portfolio.js → app/rest/wix-portfolio.js} +0 -0
- /package/skills/wix-vibe-headless/references/pricing-plans/{wix-pricing-plans.js → app/rest/wix-pricing-plans.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-menu.js → app/rest/wix-restaurants-menu.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-ordering.js → app/rest/wix-restaurants-ordering.js} +0 -0
- /package/skills/wix-vibe-headless/references/restaurants/{wix-restaurants-reservations.js → app/rest/wix-restaurants-reservations.js} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// useProductDetail — all PDP logic, no markup: load product by slug, pre-select first in-stock
|
|
2
|
+
// choice per option, resolve the buyer's selections to a Wix variant, gate add-to-cart on stock +
|
|
3
|
+
// mandatory modifiers, and build the addToCart payload. The data paths here (variant resolution,
|
|
4
|
+
// modifier keys, stock gating) are the bug-prone part — keep them verbatim; the PDP page only
|
|
5
|
+
// renders what this returns.
|
|
6
|
+
import { useState, useEffect, useMemo } from "react";
|
|
7
|
+
import { getProductBySlug } from "@/rest/wix-store-catalog";
|
|
8
|
+
import { storeImage } from "@/lib/storeImage";
|
|
9
|
+
import { useCart } from "@/context/CartContext";
|
|
10
|
+
|
|
11
|
+
export function useProductDetail(slug) {
|
|
12
|
+
const { addToCart } = useCart();
|
|
13
|
+
const [product, setProduct] = useState(null);
|
|
14
|
+
const [notFound, setNotFound] = useState(false);
|
|
15
|
+
// A missing product (notFound) and a failed request (error) need different pages: one is a dead
|
|
16
|
+
// link, the other is worth retrying. Without the split, a network blip reads as "product deleted".
|
|
17
|
+
const [error, setError] = useState(null);
|
|
18
|
+
const [adding, setAdding] = useState(false);
|
|
19
|
+
const [selectedOptions, setSelectedOptions] = useState({});
|
|
20
|
+
const [modifierValues, setModifierValues] = useState({});
|
|
21
|
+
const [quantity, setQuantity] = useState(1);
|
|
22
|
+
const [reloadKey, setReloadKey] = useState(0);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
setProduct(null);
|
|
27
|
+
setNotFound(false);
|
|
28
|
+
setError(null);
|
|
29
|
+
getProductBySlug(slug)
|
|
30
|
+
.then((p) => {
|
|
31
|
+
if (cancelled) return;
|
|
32
|
+
if (!p) return setNotFound(true);
|
|
33
|
+
setProduct(p);
|
|
34
|
+
const initial = {};
|
|
35
|
+
(p.options || []).forEach((o) => {
|
|
36
|
+
const first = o.choicesSettings?.choices?.find((c) => c.inStock !== false);
|
|
37
|
+
if (first) initial[o.id] = first.choiceId;
|
|
38
|
+
});
|
|
39
|
+
setSelectedOptions(initial);
|
|
40
|
+
})
|
|
41
|
+
.catch((e) => { if (!cancelled) setError(e?.message || "Couldn't load this product."); });
|
|
42
|
+
return () => { cancelled = true; };
|
|
43
|
+
}, [slug, reloadKey]);
|
|
44
|
+
|
|
45
|
+
const options = product?.options || [];
|
|
46
|
+
const modifiers = product?.modifiers || [];
|
|
47
|
+
const variants = product?.variantsInfo?.variants || [];
|
|
48
|
+
|
|
49
|
+
const variant = useMemo(() => {
|
|
50
|
+
if (options.length === 0) return variants[0] || null;
|
|
51
|
+
if (!options.every((o) => selectedOptions[o.id])) return null;
|
|
52
|
+
return variants.find((v) => (v.choices || []).every((c) =>
|
|
53
|
+
selectedOptions[c.optionChoiceIds?.optionId] === c.optionChoiceIds?.choiceId)) || null;
|
|
54
|
+
}, [options, variants, selectedOptions]);
|
|
55
|
+
|
|
56
|
+
const inStock = variant ? variant.inventoryStatus?.inStock !== false : true;
|
|
57
|
+
const canAdd = useMemo(() => {
|
|
58
|
+
if (options.length > 0 && !variant) return false;
|
|
59
|
+
if (variant && !inStock) return false;
|
|
60
|
+
return modifiers.filter((m) => m.mandatory).every((m) =>
|
|
61
|
+
m.modifierRenderType === "FREE_TEXT" ? !!modifierValues[m.freeTextSettings?.key] : !!modifierValues[m.key]);
|
|
62
|
+
}, [options, variant, inStock, modifiers, modifierValues]);
|
|
63
|
+
|
|
64
|
+
const price = variant?.price?.actualPrice?.formattedAmount || product?.actualPriceRange?.minValue?.formattedAmount || "";
|
|
65
|
+
|
|
66
|
+
// A colour choice carries `linkedMedia` — the shots of the product in that colour. Picking "Dark
|
|
67
|
+
// Green" should move the gallery to the green photo, so resolve the selection to a URL here; only
|
|
68
|
+
// colour-style choices have linked media, so the first hit across the options is the right one.
|
|
69
|
+
const focusMediaUrl = useMemo(() => {
|
|
70
|
+
for (const o of options) {
|
|
71
|
+
const choice = (o.choicesSettings?.choices || []).find((c) => c.choiceId === selectedOptions[o.id]);
|
|
72
|
+
const url = storeImage(choice?.linkedMedia?.[0]);
|
|
73
|
+
if (url) return url;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}, [options, selectedOptions]);
|
|
77
|
+
|
|
78
|
+
const selectOption = (optionId, choiceId) => setSelectedOptions((s) => ({ ...s, [optionId]: choiceId }));
|
|
79
|
+
const setModifier = (key, value) => setModifierValues((s) => ({ ...s, [key]: value }));
|
|
80
|
+
|
|
81
|
+
async function submit() {
|
|
82
|
+
const modifierChoices = {}, customTextFields = {};
|
|
83
|
+
modifiers.forEach((m) => {
|
|
84
|
+
const k = m.modifierRenderType === "FREE_TEXT" ? m.freeTextSettings?.key : m.key;
|
|
85
|
+
if (!k || !modifierValues[k]) return;
|
|
86
|
+
(m.modifierRenderType === "FREE_TEXT" ? customTextFields : modifierChoices)[k] = modifierValues[k];
|
|
87
|
+
});
|
|
88
|
+
setAdding(true);
|
|
89
|
+
try {
|
|
90
|
+
// addToCart reports its own failures through the cart context (it opens the drawer with the
|
|
91
|
+
// reason), so nothing is swallowed by leaving this unguarded beyond resetting the button.
|
|
92
|
+
// Coerce the quantity: the shipped stepper lets the field sit empty mid-edit, and a keyboard
|
|
93
|
+
// submit from that state would otherwise post "" as the quantity.
|
|
94
|
+
await addToCart(product.id, variant?.id, Math.max(1, Number(quantity) || 1), {
|
|
95
|
+
modifierChoices: Object.keys(modifierChoices).length ? modifierChoices : undefined,
|
|
96
|
+
customTextFields: Object.keys(customTextFields).length ? customTextFields : undefined,
|
|
97
|
+
});
|
|
98
|
+
} finally {
|
|
99
|
+
setAdding(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
product, notFound, error, retry: () => setReloadKey((k) => k + 1),
|
|
105
|
+
options, modifiers,
|
|
106
|
+
selectedOptions, selectOption, modifierValues, setModifier,
|
|
107
|
+
quantity, setQuantity, variant, inStock, canAdd, adding, price, submit, focusMediaUrl,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// useShop — all catalog-listing logic, no markup: load the category menu, load a page of products for
|
|
2
|
+
// the active category, append the next page on demand, sort client-side, and surface a failure instead
|
|
3
|
+
// of leaving the page on a spinner. The Shop page only renders what this returns.
|
|
4
|
+
//
|
|
5
|
+
// Load-bearing:
|
|
6
|
+
// • paging is CURSOR-based. Keep the cursor from the previous response and pass it back; a cursor
|
|
7
|
+
// follow-up reuses the original filter, so the category is fixed for the life of that cursor.
|
|
8
|
+
// Changing category starts a fresh query, never a cursor continuation.
|
|
9
|
+
// • queryCategories returns the auto-created "all-products" system category alongside real ones, and
|
|
10
|
+
// `visible` does not flag it — filter it by slug or the menu shows a duplicate of everything.
|
|
11
|
+
// • sorting is done here on the loaded page, not by the API: with cursor paging the server would
|
|
12
|
+
// re-sort per page, so a sort applied across pages already fetched is the honest option. Say so in
|
|
13
|
+
// the UI if the catalog is larger than one page.
|
|
14
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
15
|
+
import { queryProducts, queryProductsByCategory, queryCategories } from "@/rest/wix-store-catalog";
|
|
16
|
+
|
|
17
|
+
const SYSTEM_CATEGORY_SLUG = "all-products";
|
|
18
|
+
export const SORTS = {
|
|
19
|
+
featured: { label: "Featured" },
|
|
20
|
+
priceAsc: { label: "Price: low to high" },
|
|
21
|
+
priceHigh: { label: "Price: high to low" },
|
|
22
|
+
name: { label: "Name: A–Z" },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Prices arrive formatted for display ("$12.00"), so read the numeric value for sorting from the
|
|
26
|
+
// amount when it's there and fall back to digits in the formatted string.
|
|
27
|
+
function priceOf(product) {
|
|
28
|
+
const min = product?.actualPriceRange?.minValue;
|
|
29
|
+
const n = Number(min?.amount ?? String(min?.formattedAmount || "").replace(/[^\d.]/g, ""));
|
|
30
|
+
return Number.isFinite(n) ? n : 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useShop({ pageSize = 24 } = {}) {
|
|
34
|
+
const [categories, setCategories] = useState([]);
|
|
35
|
+
const [activeCategory, setActiveCategory] = useState(null); // null = everything
|
|
36
|
+
const [products, setProducts] = useState(null); // null = first load
|
|
37
|
+
const [cursor, setCursor] = useState(null);
|
|
38
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
39
|
+
const [error, setError] = useState(null);
|
|
40
|
+
const [sort, setSort] = useState("featured");
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
queryCategories({ limit: 100 })
|
|
44
|
+
.then(({ categories: list }) =>
|
|
45
|
+
setCategories((list || []).filter((c) => c.visible !== false && c.slug !== SYSTEM_CATEGORY_SLUG)))
|
|
46
|
+
.catch(() => setCategories([])); // a store without categories is normal, not an error
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
const load = useCallback(async (categoryId) => {
|
|
50
|
+
setProducts(null);
|
|
51
|
+
setCursor(null);
|
|
52
|
+
setError(null);
|
|
53
|
+
try {
|
|
54
|
+
const res = categoryId
|
|
55
|
+
? await queryProductsByCategory(categoryId, { limit: pageSize })
|
|
56
|
+
: await queryProducts({ limit: pageSize });
|
|
57
|
+
setProducts(res.products);
|
|
58
|
+
setCursor(res.nextCursor);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
setError(e?.message || "Couldn't load products.");
|
|
61
|
+
setProducts([]);
|
|
62
|
+
}
|
|
63
|
+
}, [pageSize]);
|
|
64
|
+
|
|
65
|
+
useEffect(() => { load(activeCategory?.id ?? null); }, [activeCategory, load]);
|
|
66
|
+
|
|
67
|
+
const loadMore = async () => {
|
|
68
|
+
if (!cursor || loadingMore) return;
|
|
69
|
+
setLoadingMore(true);
|
|
70
|
+
try {
|
|
71
|
+
const res = activeCategory
|
|
72
|
+
? await queryProductsByCategory(activeCategory.id, { limit: pageSize, cursor })
|
|
73
|
+
: await queryProducts({ limit: pageSize, cursor });
|
|
74
|
+
setProducts((prev) => [...(prev || []), ...res.products]);
|
|
75
|
+
setCursor(res.nextCursor);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
setError(e?.message || "Couldn't load more products.");
|
|
78
|
+
} finally {
|
|
79
|
+
setLoadingMore(false);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const sorted = useMemo(() => {
|
|
84
|
+
if (!products) return null;
|
|
85
|
+
const copy = [...products];
|
|
86
|
+
if (sort === "priceAsc") copy.sort((a, b) => priceOf(a) - priceOf(b));
|
|
87
|
+
else if (sort === "priceHigh") copy.sort((a, b) => priceOf(b) - priceOf(a));
|
|
88
|
+
else if (sort === "name") copy.sort((a, b) => (a.name || "").localeCompare(b.name || ""));
|
|
89
|
+
return copy;
|
|
90
|
+
}, [products, sort]);
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
categories, activeCategory, setActiveCategory,
|
|
94
|
+
products: sorted, loading: products === null, error,
|
|
95
|
+
hasMore: !!cursor, loadMore, loadingMore,
|
|
96
|
+
sort, setSort, retry: () => load(activeCategory?.id ?? null),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// storeImage — turn a Wix Stores image value into a URL the browser can load.
|
|
2
|
+
// Wix returns these protocol-relative (`//static.wixstatic.com/...`), which resolves against the
|
|
3
|
+
// current page. That works on https pages and breaks on anything else, so normalise it once here
|
|
4
|
+
// instead of at each call site — product media, PDP gallery items, and cart line items all carry the
|
|
5
|
+
// same shape. Returns null for a missing value so callers can render a placeholder.
|
|
6
|
+
export function storeImage(value) {
|
|
7
|
+
const url = typeof value === "string" ? value : value?.image?.url || value?.url;
|
|
8
|
+
if (!url) return null;
|
|
9
|
+
if (url.startsWith("//")) return `https:${url}`;
|
|
10
|
+
return url;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// The main catalog image for a product (grid tiles, cart fallbacks).
|
|
14
|
+
export function productImage(product) {
|
|
15
|
+
return storeImage(product?.media?.main?.image?.url);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Every gallery image for a product, main first, de-duplicated — `media.itemsInfo.items` repeats the
|
|
19
|
+
// main image, and video items carry no `image.url`. Returns [{ url, altText }].
|
|
20
|
+
export function productGallery(product) {
|
|
21
|
+
const items = product?.media?.itemsInfo?.items || [];
|
|
22
|
+
const urls = [productImage(product), ...items.map((i) => storeImage(i))];
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
return urls
|
|
25
|
+
.map((url, i) => ({ url, altText: items[i - 1]?.altText || product?.name || "" }))
|
|
26
|
+
.filter(({ url }) => url && !seen.has(url) && seen.add(url));
|
|
27
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// PDP — thin view over useProductDetail (all logic lives in the hook). plainDescription is HTML
|
|
2
|
+
// despite the name; render via dangerouslySetInnerHTML. Styled with base44 design tokens (shadcn Tailwind classes).
|
|
3
|
+
//
|
|
4
|
+
// The price shown follows the selection: once options resolve to a variant the hook returns that
|
|
5
|
+
// variant's price, so a picked size can cost more than the "from" figure the grid tile showed.
|
|
6
|
+
import { useMemo } from "react";
|
|
7
|
+
import { Link, useParams } from "react-router-dom";
|
|
8
|
+
import { useProductDetail } from "@/hooks/useProductDetail";
|
|
9
|
+
import { productGallery } from "@/lib/storeImage";
|
|
10
|
+
import ProductGallery from "@/components/ProductGallery";
|
|
11
|
+
import VariantPicker from "@/components/VariantPicker";
|
|
12
|
+
|
|
13
|
+
export default function ProductDetail() {
|
|
14
|
+
const { slug } = useParams();
|
|
15
|
+
const d = useProductDetail(slug);
|
|
16
|
+
// Memoised so the gallery keeps its selected image across re-renders (see ProductGallery).
|
|
17
|
+
const images = useMemo(() => productGallery(d.product), [d.product]);
|
|
18
|
+
|
|
19
|
+
if (d.error) {
|
|
20
|
+
return (
|
|
21
|
+
<Centered>
|
|
22
|
+
<p className="m-0">{d.error}</p>
|
|
23
|
+
<button onClick={d.retry} className="border border-border bg-card text-foreground rounded-sm py-2 px-4 cursor-pointer text-sm">Try again</button>
|
|
24
|
+
</Centered>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (d.notFound) {
|
|
28
|
+
return (
|
|
29
|
+
<Centered>
|
|
30
|
+
<p className="m-0">We couldn't find that product.</p>
|
|
31
|
+
<Link to="/shop" className="text-primary">Back to shop</Link>
|
|
32
|
+
</Centered>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (!d.product) return <ProductDetailSkeleton />;
|
|
36
|
+
|
|
37
|
+
const compareAt = d.product?.compareAtPriceRange?.minValue?.formattedAmount;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<main className="max-w-[1200px] mx-auto p-4">
|
|
41
|
+
<nav aria-label="Breadcrumb" className="mb-4 text-sm text-muted-foreground">
|
|
42
|
+
<Link to="/shop" className="text-muted-foreground hover:text-foreground">Shop</Link>
|
|
43
|
+
<span aria-hidden="true"> / </span>
|
|
44
|
+
<span className="text-foreground">{d.product.name}</span>
|
|
45
|
+
</nav>
|
|
46
|
+
|
|
47
|
+
<div className="grid gap-8 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
|
|
48
|
+
<ProductGallery images={images} name={d.product.name} focusUrl={d.focusMediaUrl} />
|
|
49
|
+
|
|
50
|
+
<div>
|
|
51
|
+
<h1 className="font-display m-0 mb-2">{d.product.name}</h1>
|
|
52
|
+
|
|
53
|
+
<div className="flex items-baseline gap-2 mb-4">
|
|
54
|
+
<p className="text-[22px] font-semibold m-0">{d.price}</p>
|
|
55
|
+
{compareAt && compareAt !== d.price && (
|
|
56
|
+
<span className="text-muted-foreground line-through">{compareAt}</span>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{d.product.plainDescription && (
|
|
61
|
+
<div className="text-muted-foreground leading-relaxed mb-6"
|
|
62
|
+
dangerouslySetInnerHTML={{ __html: d.product.plainDescription }} />
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
<VariantPicker
|
|
66
|
+
options={d.options} modifiers={d.modifiers}
|
|
67
|
+
selectedOptions={d.selectedOptions} selectOption={d.selectOption}
|
|
68
|
+
modifierValues={d.modifierValues} setModifier={d.setModifier}
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
{/* Tell the buyer WHY the button is dead: an unresolvable combination reads as a broken page. */}
|
|
72
|
+
{d.options.length > 0 && !d.variant && (
|
|
73
|
+
<p className="text-[13px] text-muted-foreground m-0 mt-1">Pick an option from each list to continue.</p>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<div className="flex items-stretch gap-3 mt-4">
|
|
77
|
+
<QuantityStepper value={d.quantity} onChange={d.setQuantity} disabled={!d.inStock} />
|
|
78
|
+
<button disabled={!d.canAdd || d.adding} onClick={d.submit}
|
|
79
|
+
className="flex-1 h-12 px-6 bg-primary text-primary-foreground border-none rounded-md text-[15px] font-semibold cursor-pointer transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed">
|
|
80
|
+
{d.adding ? "Adding…" : d.inStock ? "Add to cart" : "Out of stock"}
|
|
81
|
+
</button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</main>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Segmented −/+ stepper, height-matched to the Add-to-cart button so the row reads as one control.
|
|
90
|
+
// A native number input is avoided on purpose: its spinners are ~10px, OS-rendered (so they ignore
|
|
91
|
+
// the app's tokens), effectively untappable, and they accept "e"/"+"/"1e5" plus scroll-wheel edits.
|
|
92
|
+
// The value stays typeable — inputMode="numeric" brings up a keypad — but only digits survive, and
|
|
93
|
+
// blur restores 1 when the field is left empty. `tabular-nums` keeps the width steady across digits.
|
|
94
|
+
function QuantityStepper({ value, onChange, disabled }) {
|
|
95
|
+
const set = (n) => onChange(Math.max(1, n));
|
|
96
|
+
return (
|
|
97
|
+
<div data-disabled={disabled || undefined}
|
|
98
|
+
className="inline-flex items-stretch h-12 rounded-md border border-input bg-background overflow-hidden data-[disabled]:opacity-50">
|
|
99
|
+
<StepButton label="Decrease quantity" disabled={disabled || value <= 1} onClick={() => set(value - 1)}>−</StepButton>
|
|
100
|
+
<label className="sr-only" htmlFor="pdp-qty">Quantity</label>
|
|
101
|
+
<input id="pdp-qty" type="text" inputMode="numeric" autoComplete="off" disabled={disabled}
|
|
102
|
+
value={value} aria-live="polite"
|
|
103
|
+
onChange={(e) => { const n = e.target.value.replace(/\D/g, ""); onChange(n === "" ? "" : Math.max(1, Number(n))); }}
|
|
104
|
+
onBlur={(e) => { if (!e.target.value) set(1); }}
|
|
105
|
+
className="w-12 text-center bg-transparent text-foreground font-semibold tabular-nums border-x border-input outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:cursor-not-allowed" />
|
|
106
|
+
<StepButton label="Increase quantity" disabled={disabled} onClick={() => set(Number(value || 0) + 1)}>+</StepButton>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function StepButton({ children, label, onClick, disabled }) {
|
|
112
|
+
return (
|
|
113
|
+
<button type="button" onClick={onClick} disabled={disabled} aria-label={label}
|
|
114
|
+
className="w-11 grid place-items-center text-lg leading-none text-muted-foreground bg-transparent border-none cursor-pointer transition-colors hover:bg-muted hover:text-foreground disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent">
|
|
115
|
+
{children}
|
|
116
|
+
</button>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function ProductDetailSkeleton() {
|
|
121
|
+
return (
|
|
122
|
+
<main className="max-w-[1200px] mx-auto p-4" aria-busy="true">
|
|
123
|
+
<div className="grid gap-8 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
|
|
124
|
+
<div className="aspect-square bg-muted rounded-lg animate-pulse" />
|
|
125
|
+
<div className="flex flex-col gap-3">
|
|
126
|
+
<div className="h-7 w-2/3 bg-muted rounded-sm animate-pulse" />
|
|
127
|
+
<div className="h-6 w-24 bg-muted rounded-sm animate-pulse" />
|
|
128
|
+
<div className="h-4 w-full bg-muted rounded-sm animate-pulse mt-3" />
|
|
129
|
+
<div className="h-4 w-5/6 bg-muted rounded-sm animate-pulse" />
|
|
130
|
+
<div className="h-11 w-full bg-muted rounded-sm animate-pulse mt-6" />
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</main>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function Centered({ children }) {
|
|
138
|
+
return <div className="p-12 flex flex-col items-center gap-3 text-center text-muted-foreground">{children}</div>;
|
|
139
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Shop / catalog page — thin view over useShop (all logic lives in the hook). Category menu, sort,
|
|
2
|
+
// cursor paging, plus the loading / empty / failed states a seeded-separately catalog needs.
|
|
3
|
+
// Styled with base44 design tokens (shadcn Tailwind classes).
|
|
4
|
+
import { useShop, SORTS } from "@/hooks/useShop";
|
|
5
|
+
import ProductGrid from "@/components/ProductGrid";
|
|
6
|
+
|
|
7
|
+
export default function Shop() {
|
|
8
|
+
const s = useShop();
|
|
9
|
+
const showCategories = s.categories.length > 0;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<main className="max-w-[1200px] mx-auto p-4">
|
|
13
|
+
<div className="flex flex-wrap items-end justify-between gap-3 mb-4">
|
|
14
|
+
<h1 className="font-display m-0">{s.activeCategory?.name || "Shop"}</h1>
|
|
15
|
+
{s.products?.length > 0 && (
|
|
16
|
+
<label className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
17
|
+
Sort
|
|
18
|
+
<select value={s.sort} onChange={(e) => s.setSort(e.target.value)}
|
|
19
|
+
className="py-1.5 px-2 border border-input rounded-sm bg-background text-foreground text-sm">
|
|
20
|
+
{Object.entries(SORTS).map(([key, { label }]) => <option key={key} value={key}>{label}</option>)}
|
|
21
|
+
</select>
|
|
22
|
+
</label>
|
|
23
|
+
)}
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
{showCategories && (
|
|
27
|
+
<nav aria-label="Categories" className="flex flex-wrap gap-2 mb-6">
|
|
28
|
+
<CategoryChip active={!s.activeCategory} onClick={() => s.setActiveCategory(null)}>All</CategoryChip>
|
|
29
|
+
{s.categories.map((c) => (
|
|
30
|
+
<CategoryChip key={c.id} active={s.activeCategory?.id === c.id} onClick={() => s.setActiveCategory(c)}>
|
|
31
|
+
{c.name}
|
|
32
|
+
</CategoryChip>
|
|
33
|
+
))}
|
|
34
|
+
</nav>
|
|
35
|
+
)}
|
|
36
|
+
|
|
37
|
+
{s.error ? (
|
|
38
|
+
<div role="alert" className="flex flex-col items-center gap-3 py-16 text-center">
|
|
39
|
+
<p className="m-0 text-muted-foreground">{s.error}</p>
|
|
40
|
+
<button onClick={s.retry}
|
|
41
|
+
className="border border-border bg-card text-foreground rounded-sm py-2 px-4 cursor-pointer text-sm">Try again</button>
|
|
42
|
+
</div>
|
|
43
|
+
) : (
|
|
44
|
+
<>
|
|
45
|
+
<ProductGrid
|
|
46
|
+
products={s.products}
|
|
47
|
+
loading={s.loading}
|
|
48
|
+
empty={s.activeCategory ? `Nothing in ${s.activeCategory.name} yet.` : "No products yet."}
|
|
49
|
+
emptyHint={s.activeCategory
|
|
50
|
+
? "Pick another category, or add products to this one from your Wix dashboard."
|
|
51
|
+
: "Add products in your Wix dashboard and they'll appear here — no redeploy needed."}
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
{s.hasMore && (
|
|
55
|
+
<div className="flex justify-center mt-8">
|
|
56
|
+
<button onClick={s.loadMore} disabled={s.loadingMore}
|
|
57
|
+
className="border border-border bg-card text-foreground rounded-sm py-2.5 px-6 cursor-pointer text-sm font-semibold disabled:opacity-60 disabled:cursor-wait">
|
|
58
|
+
{s.loadingMore ? "Loading…" : "Load more"}
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
</>
|
|
63
|
+
)}
|
|
64
|
+
</main>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function CategoryChip({ active, onClick, children }) {
|
|
69
|
+
return (
|
|
70
|
+
<button onClick={onClick} aria-pressed={active}
|
|
71
|
+
className={`py-1.5 px-3 cursor-pointer text-sm rounded-sm border ${
|
|
72
|
+
active ? "border-primary bg-primary text-primary-foreground" : "border-border bg-card text-foreground"
|
|
73
|
+
}`}>{children}</button>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -8,7 +8,7 @@ const STORES_APP_ID = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Wix eCom Cart — key fields for building a cart UI.
|
|
11
|
-
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart/get-cart.md
|
|
11
|
+
* Full model: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/get-current-cart.md
|
|
12
12
|
*
|
|
13
13
|
* id {string}, currency {string},
|
|
14
14
|
* lineItems[].id {string} — lineItemId for update/remove (NOT catalogItemId),
|
|
@@ -13,10 +13,29 @@ import { wixApiRequest } from "./wix-client.js";
|
|
|
13
13
|
* actualPriceRange.minValue.formattedAmount {string} — lowest price with currency symbol,
|
|
14
14
|
* actualPriceRange.maxValue.formattedAmount {string} — highest price with currency symbol,
|
|
15
15
|
* compareAtPriceRange.minValue.formattedAmount {string} — strikethrough price (present when on sale),
|
|
16
|
+
* actualPriceRange/compareAtPriceRange .minValue.amount {string} — the same figures unformatted
|
|
17
|
+
* ("129.00"). Use these for arithmetic: a percent-off badge can't be computed from "€129.00",
|
|
16
18
|
* inventory.availabilityStatus {string} — "IN_STOCK"|"OUT_OF_STOCK"|"PARTIALLY_OUT_OF_STOCK",
|
|
19
|
+
* inventory.preorderStatus {string} — "ENABLED"|"DISABLED". ENABLED + OUT_OF_STOCK is a pre-order,
|
|
20
|
+
* which is buyable — label it "Pre-order" rather than "Sold out",
|
|
21
|
+
* NB: inventory carries no stock COUNT. "Limited stock" (from PARTIALLY_OUT_OF_STOCK) is the most
|
|
22
|
+
* precise scarcity signal a tile can show; "Only 2 left" needs per-variant inventory, i.e. a
|
|
23
|
+
* getProductBySlug call per product,
|
|
24
|
+
* ribbon {object} — { id, name } merchant-set badge: "New", "Sale", "Best Seller". This is the
|
|
25
|
+
* catalogue's own label, so prefer it over anything derived — a "new" badge computed from
|
|
26
|
+
* createdDate flags every product at once right after seeding,
|
|
27
|
+
* additionalRibbons {array} — further { id, name } badges,
|
|
28
|
+
* createdDate / updatedDate {string} — ISO timestamps,
|
|
29
|
+
* variantSummary.variantCount {number} — how many variants exist, without fetching them,
|
|
17
30
|
* options {array} — product options e.g. Size, Color:
|
|
18
31
|
* [{ id, name, optionRenderType "TEXT_CHOICES"|"COLOR_CHOICES"|"SWATCH_CHOICES",
|
|
19
|
-
* choicesSettings.choices [{ choiceId, key, name, inStock, visible,
|
|
32
|
+
* choicesSettings.choices [{ choiceId, key, name, inStock, visible, choiceType, colorCode,
|
|
33
|
+
* linkedMedia }] }],
|
|
34
|
+
* A COLOR/SWATCH option's choices carry choiceType "ONE_COLOR" and colorCode "#395E55" — render
|
|
35
|
+
* those as real colour swatches, not text pills. linkedMedia [{ image.url }] holds that colour's
|
|
36
|
+
* photos, so selecting a swatch can move the gallery to the matching shot. visible false is a
|
|
37
|
+
* retired choice the merchant no longer sells: filter it out. (TEXT_CHOICES choices are
|
|
38
|
+
* choiceType "CHOICE_TEXT" with no colorCode.)
|
|
20
39
|
* modifiers {array} — non-variant customizations (engraving, gift wrap):
|
|
21
40
|
* [{ id, name, mandatory, modifierRenderType "TEXT_CHOICES"|"FREE_TEXT",
|
|
22
41
|
* key, choicesSettings.choices, freeTextSettings.key }],
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Storefront — seeding
|
|
2
|
+
|
|
3
|
+
Seed a Wix Stores catalog by **calling `seed-store.js`** — don't hand-write the REST calls. It's
|
|
4
|
+
a build-time module (run via `exec_tool`, not shipped in the app) that abstracts every Wix Stores
|
|
5
|
+
seed operation. Load it and call **`setupStore` — the one-call path** — with plain data.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// build-time exec_tool
|
|
9
|
+
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
// exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
|
|
12
|
+
const seed = (() => { const m = { exports: {} };
|
|
13
|
+
new Function("module", "exports", "require", fs.readFileSync("/app/.agents/skills/wix-vibe-headless/references/storefront/seed/seed-store.js", "utf8"))(m, m.exports, require);
|
|
14
|
+
return m.exports; })();
|
|
15
|
+
const ctx = { token: accessToken, siteId: WIX_METASITE_ID };
|
|
16
|
+
|
|
17
|
+
// ONE call: install (+ wait for V3) → create products → categories → attach images, ids kept
|
|
18
|
+
// in memory (no hand-threading). Categories map name -> product NAMES. Pass an imageUrl per product
|
|
19
|
+
// to attach its image; omit it to skip images.
|
|
20
|
+
// imageUrl must be the FINAL https://media.base44.com/... url from the COMPLETED generate_image
|
|
21
|
+
// result — not a still-generating /__generating__/<id>.png placeholder (Wix can't fetch that).
|
|
22
|
+
// generate_image runs in the background while you build, so the urls are ready by seed time.
|
|
23
|
+
const result = await seed.setupStore(ctx, {
|
|
24
|
+
products: [
|
|
25
|
+
{ name: "The Glam Rocker", description: "Sequin-studded velvet legend…", price: 49.99, quantity: 12, imageUrl: imageUrls[0] },
|
|
26
|
+
// a product with buyer choices — see "Options, variants and sale prices" below
|
|
27
|
+
{ name: "The Understudy", description: "…", price: 245, quantity: 8, imageUrl: imageUrls[1],
|
|
28
|
+
options: [{ name: "Color", type: "color", choices: [{ name: "Ink", colorCode: "#1B1B2F" }, { name: "Bone", colorCode: "#EDE6D6" }] }] },
|
|
29
|
+
// a product on sale — compareAtPrice drives the strikethrough + the tile's percent-off badge
|
|
30
|
+
{ name: "Encore Jacket", description: "…", price: 68, compareAtPrice: 129, quantity: 5, imageUrl: imageUrls[2] },
|
|
31
|
+
],
|
|
32
|
+
categories: { "Legends": ["The Glam Rocker"], "Rising Stars": [] }, // omit if the brief names none
|
|
33
|
+
});
|
|
34
|
+
// result: { products:[{id,slug,revision,name}], categories:[{id,name}], imagesAttached }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Seeding is additive — never delete or overwrite existing content.** Don't clean up, don't remove
|
|
38
|
+
"sample" data, don't reset. Just add.
|
|
39
|
+
|
|
40
|
+
## Options, variants and sale prices
|
|
41
|
+
|
|
42
|
+
The shipped storefront renders colour options as real swatches, shows a size/colour summary on each
|
|
43
|
+
tile, and puts a percent-off badge on a discounted product. A catalog of plain single-price products
|
|
44
|
+
leaves all of that invisible, so **seed a catalog that exercises it**: unless the brief says
|
|
45
|
+
otherwise, give at least one product a colour option and put one product on sale. Keep it truthful
|
|
46
|
+
to the business — a ceramics studio has glaze colours, a bakery does not.
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
options: [
|
|
50
|
+
{ name: "Color", type: "color", choices: [{ name: "Ink", colorCode: "#1B1B2F" }, { name: "Bone", colorCode: "#EDE6D6" }] },
|
|
51
|
+
{ name: "Size", type: "text", choices: ["Small", "Medium", "Large"] }, // or [{ name: "Small" }, …]
|
|
52
|
+
]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `type: "color"` → `SWATCH_CHOICES` with each choice's `colorCode`, which the PDP draws as a swatch.
|
|
56
|
+
Any other `type` → text pills. Give every colour choice a `colorCode`.
|
|
57
|
+
- Variants are expanded for you: the full cross-product of the options, each carrying the product's
|
|
58
|
+
`price`, `compareAtPrice` and `quantity`. Two options with 2 and 3 choices means 6 variants — keep
|
|
59
|
+
option counts small.
|
|
60
|
+
- `compareAtPrice` (> `price`) is the "was" price: strikethrough on the PDP and a `−N%` badge on the
|
|
61
|
+
tile, computed from the two amounts. It works with or without options.
|
|
62
|
+
|
|
63
|
+
Two things this module does **not** seed, so don't try:
|
|
64
|
+
|
|
65
|
+
- **Ribbons** ("New", "Best Seller"). The tile renders `product.ribbon` when it's there, but ribbons
|
|
66
|
+
are set in the Wix dashboard — tell the merchant that's where to add them.
|
|
67
|
+
- **Per-choice linked media** (`linkedMedia`), which is what makes picking a colour swap the gallery
|
|
68
|
+
photo. Seeded swatches select and price correctly; the gallery just doesn't follow the colour until
|
|
69
|
+
the merchant links photos to choices in the dashboard.
|
|
70
|
+
|
|
71
|
+
## Escape hatch — individual functions
|
|
72
|
+
Reach for the functions below only when the one-call `setupStore` doesn't fit (partial re-seed, custom
|
|
73
|
+
ordering, mid-flow checks). `setupStore` is built from them, in this order:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
await seed.installStoresApp(ctx); // install + wait for the V3 catalog
|
|
77
|
+
const products = await seed.bulkCreateProducts(ctx, [ // → [{id,slug,revision}], in stock by `quantity`
|
|
78
|
+
{ name: "The Glam Rocker", description: "…", price: 49.99, quantity: 12 },
|
|
79
|
+
]);
|
|
80
|
+
const cats = await seed.createCategories(ctx, ["Legends"]); // sequential → [{id,name}]
|
|
81
|
+
await seed.addProductsToCategories(ctx, { [cats[0].id]: [products[0].id] });
|
|
82
|
+
// images: use the FINAL https://media.base44.com/... url only (never a /__generating__/ placeholder)
|
|
83
|
+
await seed.attachProductImages(ctx, products.map((p, i) => ({ id: p.id, url: imageUrls[i], altText: p.slug })));
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Functions
|
|
87
|
+
| fn | does |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `setupStore(ctx, {products, categories?})` | **one-call**: install+wait → products → categories → images |
|
|
90
|
+
| `installStoresApp(ctx)` | install the Wix Stores app on the site (waits for the V3 catalog) |
|
|
91
|
+
| `bulkCreateProducts(ctx, products)` | one bulk create → `[{id,slug,revision}]`; products come out in stock with the `quantity` you pass |
|
|
92
|
+
| `createCategories(ctx, names)` | sequential (shared tree 409s on concurrent) → `[{id,name}]` |
|
|
93
|
+
| `addProductsToCategories(ctx, {catId:[pid]})` | sequential add-items |
|
|
94
|
+
| `attachProductImages(ctx, [{id,url,altText}])` | one bulk media attach; no revision to pass. Wix re-hosts each url server-side; the media can take a little while to appear on read-back (propagation) — normal, not a failure |
|
|
95
|
+
|
|
96
|
+
## Reference
|
|
97
|
+
If a call returns a shape you didn't expect, or you need an operation this module doesn't cover,
|
|
98
|
+
use the **`wix-docs`** skill to search + read the live Wix API reference — never guess. The
|
|
99
|
+
authoritative source recipe is `wix-headless/references/inline-recipes/setup-online-store.md`.
|
|
100
|
+
|
|
101
|
+
Read a method's page before writing its call: it carries the exact body shape, the required
|
|
102
|
+
permission scope, and the response envelope.
|
|
103
|
+
- Install a Wix app onto the site: https://dev.wix.com/docs/api-reference/business-management/app-installation/app-installation/install-app.md
|
|
104
|
+
- Import an image into Wix Media: https://dev.wix.com/docs/api-reference/assets/media/media-manager/files/import-file.md
|
|
105
|
+
- Create Category: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/create-category.md
|
|
106
|
+
- Bulk Update Categories: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-update-categories.md
|
|
107
|
+
- Bulk Add Items To Category: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-add-items-to-category.md
|
|
108
|
+
- Bulk Create Products With Inventory: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-create-products-with-inventory.md
|
|
109
|
+
- Bulk Update Products: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-update-products.md
|
|
110
|
+
- Bulk Create Inventory Items: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/inventory-items-v3/bulk-create-inventory-items.md
|
|
111
|
+
- Query Products: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products.md
|