@wix/agent-skills 1.20.1 → 1.22.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 +71 -113
- package/skills/wix-app/references/APP_TOOLS.md +2 -1
- package/skills/wix-app/references/BACKEND_EVENT.md +3 -3
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +41 -152
- package/skills/wix-app/references/DASHBOARD_MODAL.md +4 -4
- package/skills/wix-app/references/DASHBOARD_PAGE.md +11 -25
- package/skills/wix-app/references/DATA_COLLECTION.md +16 -219
- package/skills/wix-app/references/SDK_MODULE_MAP.md +44 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +36 -190
- package/skills/wix-app/references/app-tools/TOOLS.md +3 -1
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +21 -0
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +182 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +97 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +184 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +203 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +44 -0
- package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +17 -0
- package/skills/wix-app/references/dashboard-page/QUERY_AND_PAGING.md +182 -0
- package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +133 -0
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +10 -5
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +4 -18
- package/skills/wix-app/references/data-collection/LIFECYCLE.md +68 -0
- package/skills/wix-app/references/data-collection/PERMISSIONS.md +47 -0
- package/skills/wix-app/references/data-collection/SCHEMA.md +181 -0
- package/skills/wix-app/references/data-collection/WIX_DATA.md +38 -66
- package/skills/wix-app/references/site-plugin/EXAMPLES.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +144 -221
- package/skills/wix-headless-fast/cold-start/bootstrap.mjs +156 -0
- package/skills/wix-headless-fast/{entry/skill.md → cold-start/cold-start.md} +15 -15
- package/skills/wix-headless-fast/guides/existing-site.md +76 -0
- package/skills/wix-headless-fast/guides/reference-mode.md +97 -0
- package/skills/wix-headless-fast/install/AGENTS.md +35 -0
- package/skills/wix-headless-fast/install/agents-md.mjs +39 -0
- package/skills/wix-headless-fast/install/attach.mjs +161 -86
- package/skills/wix-headless-fast/install/deploy.mjs +21 -60
- package/skills/wix-headless-fast/install/lock.mjs +55 -0
- package/skills/wix-headless-fast/install/setup.mjs +259 -0
- package/skills/wix-headless-fast/install/templates.mjs +168 -0
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-coupon.md +10 -19
- package/skills/wix-manage/references/events/create-wix-event.md +5 -1
- package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +27 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +83 -37
- package/skills/wix-headless-fast/install/fast-path.mjs +0 -181
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +0 -301
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +0 -136
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +0 -112
- package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +0 -42
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +0 -31
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +0 -22
- package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +0 -137
- package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +0 -78
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +0 -117
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +0 -52
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +0 -47
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +0 -58
- package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +0 -62
- package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +0 -97
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +0 -24
- package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +0 -16156
- package/skills/wix-headless-fast/references/blog/rest/posts.ts +0 -42
- package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +0 -62
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +0 -81
- package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +0 -287
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +0 -275
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +0 -144
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +0 -119
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +0 -32
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +0 -23
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +0 -218
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +0 -169
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +0 -121
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +0 -92
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +0 -90
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +0 -57
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +0 -72
- package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +0 -76
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +0 -22
- package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +0 -11962
- package/skills/wix-headless-fast/references/bookings/rest/booking.ts +0 -113
- package/skills/wix-headless-fast/references/bookings/rest/services.ts +0 -55
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +0 -71
- package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +0 -302
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +0 -353
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +0 -50
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +0 -37
- package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +0 -158
- package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +0 -93
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +0 -177
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +0 -123
- package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +0 -75
- package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +0 -41
- package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +0 -11262
- package/skills/wix-headless-fast/references/cms/rest/items.ts +0 -125
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +0 -112
- package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +0 -324
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +0 -305
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +0 -66
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +0 -178
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +0 -118
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +0 -35
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +0 -24
- package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +0 -130
- package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +0 -97
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +0 -55
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +0 -113
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +0 -156
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +0 -97
- package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +0 -61
- package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +0 -12
- package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +0 -80
- package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +0 -21
- package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +0 -11884
- package/skills/wix-headless-fast/references/events/rest/events.ts +0 -57
- package/skills/wix-headless-fast/references/events/rest/registration.ts +0 -83
- package/skills/wix-headless-fast/references/events/seed/SEED.md +0 -95
- package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +0 -276
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +0 -338
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +0 -85
- package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +0 -332
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +0 -238
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +0 -54
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +0 -216
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +0 -59
- package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +0 -135
- package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +0 -10593
- package/skills/wix-headless-fast/references/forms/rest/forms.ts +0 -34
- package/skills/wix-headless-fast/references/forms/rest/submissions.ts +0 -56
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +0 -116
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +0 -327
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +0 -314
- package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +0 -60
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +0 -155
- package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +0 -78
- package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +0 -47
- package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +0 -66
- package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +0 -105
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +0 -59
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +0 -53
- package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +0 -122
- package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +0 -43
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +0 -29
- package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +0 -21
- package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +0 -42
- package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +0 -10
- package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +0 -11
- package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +0 -10329
- package/skills/wix-headless-fast/references/members/rest/auth.ts +0 -215
- package/skills/wix-headless-fast/references/members/rest/members.ts +0 -26
- package/skills/wix-headless-fast/references/members/seed/SEED.md +0 -39
- package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +0 -94
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +0 -313
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +0 -110
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +0 -90
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +0 -35
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +0 -85
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +0 -32
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +0 -21
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +0 -31
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +0 -82
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +0 -58
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +0 -177
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +0 -78
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +0 -81
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +0 -55
- package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +0 -30
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +0 -21
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +0 -29
- package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +0 -10197
- package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +0 -75
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +0 -86
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +0 -258
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +0 -294
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +0 -47
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +0 -101
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +0 -41
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +0 -16
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +0 -22
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +0 -54
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +0 -106
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +0 -64
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +0 -27
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +0 -43
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +0 -22
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +0 -30
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +0 -41
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +0 -40
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +0 -21
- package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +0 -11226
- package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +0 -31
- package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +0 -20
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +0 -79
- package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +0 -279
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +0 -358
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +0 -142
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +0 -26
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +0 -121
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +0 -155
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +0 -34
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +0 -43
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +0 -34
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +0 -155
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +0 -89
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +0 -71
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +0 -103
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +0 -124
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +0 -185
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +0 -193
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +0 -94
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +0 -74
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +0 -168
- package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +0 -47
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +0 -22
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +0 -12
- package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +0 -12560
- package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +0 -60
- package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +0 -169
- package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +0 -68
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +0 -89
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +0 -479
- package/skills/wix-headless-fast/references/shared/CONTENT.md +0 -22
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +0 -73
- package/skills/wix-headless-fast/references/shared/DESIGN.md +0 -26
- package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +0 -23
- package/skills/wix-headless-fast/references/shared/app/styles/global.css +0 -52
- package/skills/wix-headless-fast/references/shared/app/wix/config.ts +0 -15
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +0 -81
- package/skills/wix-headless-fast/references/shared/app/wix/money.ts +0 -24
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +0 -89
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +0 -46
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +0 -46
- package/skills/wix-headless-fast/references/shared/rest/client.ts +0 -155
- package/skills/wix-headless-fast/references/shared/rest/config.ts +0 -3
- package/skills/wix-headless-fast/references/shared/rest/media.ts +0 -65
- package/skills/wix-headless-fast/references/shared/seed/images.mjs +0 -145
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +0 -608
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +0 -26
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +0 -155
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +0 -266
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +0 -95
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +0 -136
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +0 -48
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +0 -39
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +0 -34
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +0 -121
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +0 -107
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +0 -138
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +0 -309
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +0 -133
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +0 -147
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +0 -289
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +0 -167
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +0 -65
- package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +0 -12247
- package/skills/wix-headless-fast/references/storefront/rest/cart.ts +0 -132
- package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +0 -123
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +0 -124
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +0 -525
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
// Booking rules, request builders, and DTO mapping — transport-agnostic, imported by BOTH transports:
|
|
2
|
-
// ./booking.ts (the SDK) and the REST twin in references/bookings/rest/booking.ts (fetch). The
|
|
3
|
-
// availability request bodies, the slot mapper, the form-field rules, the createBooking body, the
|
|
4
|
-
// cart body, and the checkout-or-place decision live HERE, once — these are the payloads that are
|
|
5
|
-
// exact and easy to get subtly wrong. Raw entities may carry `_id` (SDK) or `id` (REST). Imports are
|
|
6
|
-
// type-only so a strip to JS emits no imports.
|
|
7
|
-
import type { BookingFormField, BookingResult, ServiceDetail, Slot } from "./types";
|
|
8
|
-
|
|
9
|
-
export type Raw = Record<string, any>;
|
|
10
|
-
|
|
11
|
-
export const rawId = (raw: Raw | undefined | null): string => raw?._id ?? raw?.id ?? "";
|
|
12
|
-
|
|
13
|
-
// Copies of services-core's ids (same values) so this file stands alone when stripped.
|
|
14
|
-
/** The Wix Bookings app id — the cart line's catalogReference.appId. */
|
|
15
|
-
export const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
|
|
16
|
-
/** Staff-member resource type id (ANY_RESOURCE fallback + staff filtering). */
|
|
17
|
-
export const STAFF_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
|
|
18
|
-
|
|
19
|
-
const pad = (n: number) => String(n).padStart(2, "0");
|
|
20
|
-
/** Local wall-clock "YYYY-MM-DDThh:mm:ss" (NO Z) — the format the availability APIs require. */
|
|
21
|
-
export function toLocalDateString(d: Date): string {
|
|
22
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** The visitor's IANA time zone — availability and the booking are computed in it. */
|
|
26
|
-
export const defaultTimeZone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
27
|
-
|
|
28
|
-
// ---- availability ----------------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
export interface SlotsWindow {
|
|
31
|
-
from?: Date;
|
|
32
|
-
days?: number;
|
|
33
|
-
timeZone?: string;
|
|
34
|
-
staffId?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** [from, from + days) as the local strings both time-slot APIs take. */
|
|
38
|
-
export function slotsRange({ from = new Date(), days = 7 }: SlotsWindow): { fromLocalDate: string; toLocalDate: string } {
|
|
39
|
-
const to = new Date(from.getTime() + days * 24 * 3600 * 1000);
|
|
40
|
-
return { fromLocalDate: toLocalDateString(from), toLocalDate: toLocalDateString(to) };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The List Availability Time Slots body for an APPOINTMENT service: bookable slots only, the staff
|
|
45
|
-
* resources included on each slot, optionally narrowed to one staff member.
|
|
46
|
-
*/
|
|
47
|
-
export function appointmentSlotsRequest(serviceId: string, { timeZone = defaultTimeZone(), staffId, ...window }: SlotsWindow): Raw {
|
|
48
|
-
return {
|
|
49
|
-
serviceId,
|
|
50
|
-
...slotsRange(window),
|
|
51
|
-
timeZone,
|
|
52
|
-
bookable: true,
|
|
53
|
-
cursorPaging: { limit: 100 },
|
|
54
|
-
includeResourceTypeIds: [STAFF_RESOURCE_TYPE_ID],
|
|
55
|
-
...(staffId ? { resourceTypes: [{ resourceTypeId: STAFF_RESOURCE_TYPE_ID, resourceIds: [staffId] }] } : {}),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** The List Event Time Slots body for a CLASS service: its sessions, bookable only, optionally one staff member's. */
|
|
60
|
-
export function classSlotsRequest(serviceId: string, { timeZone = defaultTimeZone(), staffId, ...window }: SlotsWindow): Raw {
|
|
61
|
-
return {
|
|
62
|
-
serviceIds: [serviceId],
|
|
63
|
-
...slotsRange(window),
|
|
64
|
-
timeZone,
|
|
65
|
-
includeNonBookable: false,
|
|
66
|
-
...(staffId ? { eventFilter: { "resources.id": { $hasSome: [staffId] } } } : {}),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function toSlot(raw: Raw): Slot {
|
|
71
|
-
const start: string = raw.localStartDate ?? "";
|
|
72
|
-
const staff = ((raw.availableResources ?? []) as Raw[])
|
|
73
|
-
.flatMap((ar) => (ar.resources ?? []) as Raw[])
|
|
74
|
-
.map((r) => ({ id: rawId(r), name: r.name ?? "" }))
|
|
75
|
-
.filter((r) => r.id);
|
|
76
|
-
let label = start.slice(11, 16);
|
|
77
|
-
try {
|
|
78
|
-
label = new Date(start).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
|
|
79
|
-
} catch {
|
|
80
|
-
/* keep the hh:mm fallback */
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
startLocal: start,
|
|
84
|
-
endLocal: raw.localEndDate ?? "",
|
|
85
|
-
dayKey: start.slice(0, 10),
|
|
86
|
-
label,
|
|
87
|
-
scheduleId: raw.scheduleId ?? null,
|
|
88
|
-
eventId: raw.eventInfo?.eventId ?? null,
|
|
89
|
-
staff,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface BookingDay {
|
|
94
|
-
dayKey: string;
|
|
95
|
-
dayLabel: string;
|
|
96
|
-
slots: Slot[];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const dayLabel = (dayKey: string): string => {
|
|
100
|
-
try {
|
|
101
|
-
return new Date(`${dayKey}T12:00:00`).toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
|
|
102
|
-
} catch {
|
|
103
|
-
return dayKey;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/** Slots grouped by day, days in order — what a slot picker renders. */
|
|
108
|
-
export function groupSlotsByDay(slots: Slot[]): BookingDay[] {
|
|
109
|
-
const byDay = new Map<string, Slot[]>();
|
|
110
|
-
for (const s of slots) byDay.set(s.dayKey, [...(byDay.get(s.dayKey) ?? []), s]);
|
|
111
|
-
return [...byDay.entries()]
|
|
112
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
113
|
-
.map(([dayKey, daySlots]) => ({ dayKey, dayLabel: dayLabel(dayKey), slots: daySlots }));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ---- the booking form -------------------------------------------------------------------------------
|
|
117
|
-
|
|
118
|
-
export const FALLBACK_FIELDS: BookingFormField[] = [
|
|
119
|
-
{ target: "first_name", label: "First Name", type: "STRING", required: true },
|
|
120
|
-
{ target: "last_name", label: "Last Name", type: "STRING", required: true },
|
|
121
|
-
{ target: "email", label: "Email", type: "EMAIL", required: true },
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
const FIELD_TYPES = ["STRING", "EMAIL", "PHONE", "NUMBER", "URL"];
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Flat, render-ready fields from a form summary — deleted fields and non-text types dropped, values
|
|
128
|
-
* keyed by `target`. `required` comes from the full form schema (`fields[].validation.required`,
|
|
129
|
-
* keyed by target); the summary doesn't carry it. Without the schema every field counts as required.
|
|
130
|
-
* ALWAYS non-empty: contact basics when the schema is missing or unusable.
|
|
131
|
-
*/
|
|
132
|
-
export function toFormFields(summary: Raw | null | undefined, form?: Raw | null): BookingFormField[] {
|
|
133
|
-
const requiredByTarget = new Map<string, boolean>(
|
|
134
|
-
((form?.fields ?? []) as Raw[]).filter((f) => f.target).map((f) => [f.target as string, f.validation?.required === true]),
|
|
135
|
-
);
|
|
136
|
-
const fields = ((summary?.fields ?? []) as Raw[])
|
|
137
|
-
.filter((f) => !f.deleted)
|
|
138
|
-
.filter((f) => f.type && FIELD_TYPES.includes(f.type))
|
|
139
|
-
.map((f) => ({
|
|
140
|
-
target: f.target ?? "",
|
|
141
|
-
label: f.label ?? f.target ?? "",
|
|
142
|
-
type: f.type as BookingFormField["type"],
|
|
143
|
-
...(Array.isArray(f.options) && f.options.length ? { options: f.options as string[] } : {}),
|
|
144
|
-
required: requiredByTarget.get(f.target ?? "") ?? true,
|
|
145
|
-
}))
|
|
146
|
-
.filter((f) => f.target);
|
|
147
|
-
return fields.length ? fields : FALLBACK_FIELDS;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// ---- createBooking → cart → checkout-or-place --------------------------------------------------------
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* The Create Booking `booking` object. APPOINTMENT: the SLOT's scheduleId (never service.schedule.id)
|
|
154
|
-
* plus start/end as local wall-clock with the time zone; CLASS: the eventId. A chosen staff member
|
|
155
|
-
* is the slot's `resource`; otherwise ANY_RESOURCE of the staff type. `idKey` spells the resource's
|
|
156
|
-
* id the way the transport wants it: `_id` on the SDK, `id` on REST.
|
|
157
|
-
*/
|
|
158
|
-
export function bookingRequest(
|
|
159
|
-
service: Pick<ServiceDetail, "id" | "paymentOption">,
|
|
160
|
-
slot: Slot,
|
|
161
|
-
{ staffId, timeZone = defaultTimeZone(), idKey = "id" }: { staffId?: string; timeZone?: string; idKey?: "_id" | "id" } = {},
|
|
162
|
-
): Raw {
|
|
163
|
-
const staff = staffId ? slot.staff.find((s) => s.id === staffId) : undefined;
|
|
164
|
-
return {
|
|
165
|
-
selectedPaymentOption: service.paymentOption,
|
|
166
|
-
totalParticipants: 1,
|
|
167
|
-
bookedEntity: {
|
|
168
|
-
slot: {
|
|
169
|
-
serviceId: service.id,
|
|
170
|
-
scheduleId: slot.scheduleId ?? undefined,
|
|
171
|
-
eventId: slot.eventId ?? undefined,
|
|
172
|
-
startDate: slot.startLocal,
|
|
173
|
-
endDate: slot.endLocal,
|
|
174
|
-
timezone: timeZone,
|
|
175
|
-
...(staff
|
|
176
|
-
? { resource: { [idKey]: staff.id, name: staff.name } }
|
|
177
|
-
: { resourceSelections: [{ resourceTypeId: STAFF_RESOURCE_TYPE_ID, selectionMethod: "ANY_RESOURCE" }] }),
|
|
178
|
-
location: { locationType: "OWNER_BUSINESS" },
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** The Create Cart body that holds the booked seat: one line whose catalogItemId is the BOOKING id. */
|
|
185
|
-
export function bookingCartRequest(bookingId: string): Raw {
|
|
186
|
-
return {
|
|
187
|
-
catalogItems: [{ quantity: 1, catalogReference: { catalogItemId: bookingId, appId: BOOKINGS_APP_ID } }],
|
|
188
|
-
cart: { source: { channelType: "WEB" } },
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** The booking id out of a Create Booking response (wrapped { booking } on both transports). */
|
|
193
|
-
export const bookingIdOf = (res: Raw | null | undefined): string => rawId(res?.booking);
|
|
194
|
-
|
|
195
|
-
/** The cart id out of a Create Cart response — the SDK may unwrap the cart, REST wraps it. */
|
|
196
|
-
export const cartIdOf = (res: Raw | null | undefined): string => rawId(res) || rawId(res?.cart);
|
|
197
|
-
|
|
198
|
-
/** The cart's total from a Calculate Cart response (`summary.priceSummary.total`); 0 when absent. */
|
|
199
|
-
export const cartTotal = (calc: Raw | null | undefined): number => Number(calc?.summary?.priceSummary?.total?.amount ?? 0);
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Hosted checkout is required for anything that costs money paid online, and always when the
|
|
203
|
-
* service carries a cancellation fee (a card must be on file). Otherwise the order is placed
|
|
204
|
-
* directly — free and pay-in-person bookings confirm without a checkout.
|
|
205
|
-
*/
|
|
206
|
-
export function checkoutRequired(service: Pick<ServiceDetail, "paymentOption" | "cancellationFeeEnabled">, total: number): boolean {
|
|
207
|
-
return service.cancellationFeeEnabled || (total > 0 && service.paymentOption !== "OFFLINE");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** The Create Redirect Session body for a cart's checkout; the cart id IS the checkout id. */
|
|
211
|
-
export function checkoutRedirectRequest(cartId: string, origin: string): Raw {
|
|
212
|
-
return { ecomCheckout: { checkoutId: cartId }, callbacks: origin ? { postFlowUrl: `${origin}/` } : {} };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** A placed order → the confirmed result; the order id is wherever the transport put it. */
|
|
216
|
-
export function confirmedResult(bookingId: string, order: Raw | null | undefined): BookingResult {
|
|
217
|
-
return { kind: "confirmed", bookingId, orderId: order?.orderId ?? (rawId(order?.order) || null) };
|
|
218
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
// The whole booking state machine for one service as a framework-free store — the logic behind
|
|
2
|
-
// useBookingFlow, usable from React (useBookingFlow wraps it), from a static page's booking surface,
|
|
3
|
-
// from Vue/Svelte, or as the specification for a port: the availability window (day-grouped slots,
|
|
4
|
-
// week paging, optional staff filter), the schema-driven form, and book(). All correctness (slot
|
|
5
|
-
// scheduleId vs eventId, ANY_RESOURCE, formSubmission, payment option, checkout-or-place) lives in
|
|
6
|
-
// the data layer; this store orchestrates. A late response from a superseded window is dropped.
|
|
7
|
-
// One store per booking surface: createBookingFlowStore(service), not a singleton.
|
|
8
|
-
import { bookService, fetchBookingForm, fetchSlots } from "./booking";
|
|
9
|
-
import { groupSlotsByDay, type BookingDay } from "./booking-core";
|
|
10
|
-
import type { BookingFormField, BookingResult, ServiceDetail, Slot } from "./types";
|
|
11
|
-
|
|
12
|
-
export type { BookingDay };
|
|
13
|
-
|
|
14
|
-
const WEEK = 7 * 24 * 3600 * 1000;
|
|
15
|
-
|
|
16
|
-
/** Everything a booking surface renders from. Read it with getState() or through a subscription. */
|
|
17
|
-
export interface BookingFlowState {
|
|
18
|
-
/** Slots grouped by day, in order — [{ dayKey, dayLabel, slots }]. null while loading. */
|
|
19
|
-
days: BookingDay[] | null;
|
|
20
|
-
/** The 7-day window start; page with nextWeek/prevWeek (prev clamps to today). */
|
|
21
|
-
windowStart: Date;
|
|
22
|
-
/** Staff filter — render a picker only when service.staff.length > 1. */
|
|
23
|
-
staffId: string | undefined;
|
|
24
|
-
selectedSlot: Slot | null;
|
|
25
|
-
/** Schema-driven form fields (never empty — contact basics fallback). */
|
|
26
|
-
formFields: BookingFormField[];
|
|
27
|
-
values: Record<string, string>;
|
|
28
|
-
/** True when a slot is selected and every field has a value. */
|
|
29
|
-
canBook: boolean;
|
|
30
|
-
booking: boolean;
|
|
31
|
-
/** Set after a free/offline booking completes — the only REAL success signal. */
|
|
32
|
-
confirmed: BookingResult | null;
|
|
33
|
-
error: string | null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface BookingFlowStore {
|
|
37
|
-
getState(): BookingFlowState;
|
|
38
|
-
subscribe(listener: () => void): () => void;
|
|
39
|
-
/** Load the first window and the form. Call once when mounted (a browser — availability is timezone-specific). */
|
|
40
|
-
start(): void;
|
|
41
|
-
/** Stop reacting; drop late responses. */
|
|
42
|
-
stop(): void;
|
|
43
|
-
nextWeek(): void;
|
|
44
|
-
prevWeek(): void;
|
|
45
|
-
setStaffId(id: string | undefined): void;
|
|
46
|
-
setSelectedSlot(slot: Slot | null): void;
|
|
47
|
-
setValue(target: string, value: string): void;
|
|
48
|
-
/** Books the selected slot. On "redirect" the browser is already navigating; otherwise `confirmed` is set. Rejects with the refusal (also in .error). */
|
|
49
|
-
book(): Promise<BookingResult>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function createBookingFlowStore(service: ServiceDetail): BookingFlowStore {
|
|
53
|
-
let windowStart = new Date();
|
|
54
|
-
let staffId: string | undefined;
|
|
55
|
-
let slots: Slot[] | null = null;
|
|
56
|
-
let selectedSlot: Slot | null = null;
|
|
57
|
-
let formFields: BookingFormField[] = [];
|
|
58
|
-
let values: Record<string, string> = {};
|
|
59
|
-
let booking = false;
|
|
60
|
-
let confirmed: BookingResult | null = null;
|
|
61
|
-
let error: string | null = null;
|
|
62
|
-
let started = false;
|
|
63
|
-
let generation = 0;
|
|
64
|
-
const listeners = new Set<() => void>();
|
|
65
|
-
let snapshot: BookingFlowState | null = null;
|
|
66
|
-
const emit = () => {
|
|
67
|
-
snapshot = null;
|
|
68
|
-
for (const fn of listeners) fn();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
function getState(): BookingFlowState {
|
|
72
|
-
if (snapshot) return snapshot;
|
|
73
|
-
snapshot = {
|
|
74
|
-
days: slots === null ? null : groupSlotsByDay(slots),
|
|
75
|
-
windowStart,
|
|
76
|
-
staffId,
|
|
77
|
-
selectedSlot,
|
|
78
|
-
formFields,
|
|
79
|
-
values,
|
|
80
|
-
canBook: !!selectedSlot && formFields.every((f) => !f.required || (values[f.target] ?? "").trim().length > 0),
|
|
81
|
-
booking,
|
|
82
|
-
confirmed,
|
|
83
|
-
error,
|
|
84
|
-
};
|
|
85
|
-
return snapshot;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// The window or the staff filter changed: clear the picker and load the slots for the new selection.
|
|
89
|
-
function loadSlots(): void {
|
|
90
|
-
if (!started) return;
|
|
91
|
-
const id = ++generation;
|
|
92
|
-
slots = null;
|
|
93
|
-
selectedSlot = null;
|
|
94
|
-
emit();
|
|
95
|
-
fetchSlots(service, { from: windowStart, days: 7, staffId })
|
|
96
|
-
.then((s) => {
|
|
97
|
-
if (!started || generation !== id) return; // superseded — drop it
|
|
98
|
-
slots = s;
|
|
99
|
-
emit();
|
|
100
|
-
})
|
|
101
|
-
.catch((e) => {
|
|
102
|
-
if (!started || generation !== id) return;
|
|
103
|
-
slots = [];
|
|
104
|
-
error = e instanceof Error ? e.message : String(e);
|
|
105
|
-
emit();
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
getState,
|
|
111
|
-
subscribe(fn) {
|
|
112
|
-
listeners.add(fn);
|
|
113
|
-
return () => listeners.delete(fn);
|
|
114
|
-
},
|
|
115
|
-
start() {
|
|
116
|
-
if (started) return;
|
|
117
|
-
started = true;
|
|
118
|
-
loadSlots();
|
|
119
|
-
fetchBookingForm(service.formId).then((f) => { if (started) { formFields = f; emit(); } });
|
|
120
|
-
},
|
|
121
|
-
stop() {
|
|
122
|
-
started = false;
|
|
123
|
-
generation++;
|
|
124
|
-
},
|
|
125
|
-
nextWeek() {
|
|
126
|
-
windowStart = new Date(windowStart.getTime() + WEEK);
|
|
127
|
-
loadSlots();
|
|
128
|
-
},
|
|
129
|
-
prevWeek() {
|
|
130
|
-
const prev = new Date(windowStart.getTime() - WEEK);
|
|
131
|
-
windowStart = prev < new Date() ? new Date() : prev;
|
|
132
|
-
loadSlots();
|
|
133
|
-
},
|
|
134
|
-
setStaffId(id) {
|
|
135
|
-
if (id === staffId) return;
|
|
136
|
-
staffId = id;
|
|
137
|
-
loadSlots();
|
|
138
|
-
},
|
|
139
|
-
setSelectedSlot(slot) {
|
|
140
|
-
selectedSlot = slot;
|
|
141
|
-
emit();
|
|
142
|
-
},
|
|
143
|
-
setValue(target, value) {
|
|
144
|
-
values = { ...values, [target]: value };
|
|
145
|
-
emit();
|
|
146
|
-
},
|
|
147
|
-
async book() {
|
|
148
|
-
if (!selectedSlot) throw new Error("Pick a time first.");
|
|
149
|
-
booking = true;
|
|
150
|
-
error = null;
|
|
151
|
-
emit();
|
|
152
|
-
try {
|
|
153
|
-
const result = await bookService(service, selectedSlot, values, { staffId });
|
|
154
|
-
if (result.kind === "redirect") {
|
|
155
|
-
window.location.href = result.url;
|
|
156
|
-
} else {
|
|
157
|
-
confirmed = result;
|
|
158
|
-
}
|
|
159
|
-
return result;
|
|
160
|
-
} catch (e) {
|
|
161
|
-
error = e instanceof Error ? e.message : String(e);
|
|
162
|
-
throw e;
|
|
163
|
-
} finally {
|
|
164
|
-
booking = false;
|
|
165
|
-
emit();
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// Availability, the booking form, and the createBooking → Cart V2 → checkout-or-place sequence
|
|
2
|
-
// over the SDK. The payload shapes are exact and easy to get subtly wrong — they are built in
|
|
3
|
-
// ./booking-core (shared with the REST twin in references/bookings/rest/); this file is the
|
|
4
|
-
// transport only. Copy as-is; extend by calling these exports, never by editing them. Failures
|
|
5
|
-
// are loud.
|
|
6
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-availability-time-slots.md
|
|
7
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/time-slots-v2/list-event-time-slots.md
|
|
8
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/create-booking.md
|
|
9
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/create-cart.md
|
|
10
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/calculate-cart.md
|
|
11
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/place-order.md
|
|
12
|
-
// docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
|
|
13
|
-
// docs: https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/get-form-summary.md
|
|
14
|
-
import { availabilityTimeSlots, eventTimeSlots, bookings as bookingsModule } from "@wix/bookings";
|
|
15
|
-
import { createCart, calculateCart, placeOrder } from "@wix/auto_sdk_ecom_cart-v-2";
|
|
16
|
-
import { redirects as redirectsModule } from "@wix/redirects";
|
|
17
|
-
import { wixFetch, wixModule } from "../sdk";
|
|
18
|
-
import {
|
|
19
|
-
FALLBACK_FIELDS,
|
|
20
|
-
appointmentSlotsRequest,
|
|
21
|
-
bookingCartRequest,
|
|
22
|
-
bookingIdOf,
|
|
23
|
-
bookingRequest,
|
|
24
|
-
cartIdOf,
|
|
25
|
-
cartTotal,
|
|
26
|
-
checkoutRedirectRequest,
|
|
27
|
-
checkoutRequired,
|
|
28
|
-
classSlotsRequest,
|
|
29
|
-
confirmedResult,
|
|
30
|
-
defaultTimeZone,
|
|
31
|
-
toFormFields,
|
|
32
|
-
toLocalDateString,
|
|
33
|
-
toSlot,
|
|
34
|
-
type Raw,
|
|
35
|
-
type SlotsWindow,
|
|
36
|
-
} from "./booking-core";
|
|
37
|
-
import type { BookingFormField, BookingResult, ServiceDetail, Slot } from "./types";
|
|
38
|
-
|
|
39
|
-
export { toLocalDateString };
|
|
40
|
-
|
|
41
|
-
const apptSlots = wixModule(availabilityTimeSlots);
|
|
42
|
-
const classSlots = wixModule(eventTimeSlots);
|
|
43
|
-
const bookings = wixModule(bookingsModule);
|
|
44
|
-
const cart = wixModule({ createCart, calculateCart, placeOrder });
|
|
45
|
-
const redirects = wixModule(redirectsModule);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Bookable slots for a service in [from, from + days) — APPOINTMENT and CLASS use different APIs;
|
|
49
|
-
* this branches for you. Dates are local wall-clock; timeZone defaults to the visitor's.
|
|
50
|
-
*/
|
|
51
|
-
export async function fetchSlots(service: Pick<ServiceDetail, "id" | "type">, window: SlotsWindow = {}): Promise<Slot[]> {
|
|
52
|
-
const res: Raw =
|
|
53
|
-
service.type === "CLASS"
|
|
54
|
-
? await classSlots.listEventTimeSlots(classSlotsRequest(service.id, window) as any)
|
|
55
|
-
: await apptSlots.listAvailabilityTimeSlots(appointmentSlotsRequest(service.id, window) as any);
|
|
56
|
-
return ((res.timeSlots ?? []) as Raw[]).map(toSlot);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The service's booking-form fields, flat and render-ready (values are keyed by `target`).
|
|
61
|
-
* ALWAYS returns a non-empty list — contact basics when the schema is missing/unusable —
|
|
62
|
-
* so the form can render unconditionally.
|
|
63
|
-
*/
|
|
64
|
-
export async function fetchBookingForm(formId: string | null): Promise<BookingFormField[]> {
|
|
65
|
-
if (!formId) return FALLBACK_FIELDS;
|
|
66
|
-
try {
|
|
67
|
-
// Over wixFetch, not the @wix/forms `forms` module: that generated module is 15 MB and would
|
|
68
|
-
// ride into the booking island's client chunk (11 MB per visitor). The summary has labels and
|
|
69
|
-
// types; only the full schema says which fields are required.
|
|
70
|
-
const id = encodeURIComponent(formId);
|
|
71
|
-
const json = async (path: string): Promise<Raw | null> => {
|
|
72
|
-
const r = await wixFetch(path);
|
|
73
|
-
return r.ok ? ((await r.json()) as Raw) : null;
|
|
74
|
-
};
|
|
75
|
-
const [res, form] = await Promise.all([
|
|
76
|
-
json(`/form-schema-service/v4/forms/${id}/summary`),
|
|
77
|
-
json(`/form-schema-service/v4/forms/${id}`).catch(() => null),
|
|
78
|
-
]);
|
|
79
|
-
if (!res) return FALLBACK_FIELDS;
|
|
80
|
-
return toFormFields(res.formSummary, form?.form);
|
|
81
|
-
} catch {
|
|
82
|
-
return FALLBACK_FIELDS;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Book a slot: createBooking → createCart (holds the seat) → calculateCart →
|
|
88
|
-
* hosted checkout (paid) or placeOrder (free / pay-in-person). Call from the browser.
|
|
89
|
-
* `formValues` is the object your inputs wrote, keyed by field `target` — passed as the
|
|
90
|
-
* formSubmission DIRECTLY. Throws with a friendly message on refusal (slot taken, invalid
|
|
91
|
-
* form) — surface it, don't swallow it.
|
|
92
|
-
*/
|
|
93
|
-
export async function bookService(
|
|
94
|
-
service: ServiceDetail,
|
|
95
|
-
slot: Slot,
|
|
96
|
-
formValues: Record<string, unknown>,
|
|
97
|
-
{ staffId, timeZone = defaultTimeZone() }: { staffId?: string; timeZone?: string } = {},
|
|
98
|
-
): Promise<BookingResult> {
|
|
99
|
-
const created: Raw = await bookings.createBooking(
|
|
100
|
-
bookingRequest(service, slot, { staffId, timeZone, idKey: "_id" }) as any,
|
|
101
|
-
{ formSubmission: formValues } as any,
|
|
102
|
-
);
|
|
103
|
-
const bookingId = bookingIdOf(created);
|
|
104
|
-
if (!bookingId) throw new Error("The booking couldn't be created — the slot may have just been taken.");
|
|
105
|
-
|
|
106
|
-
const newCart: Raw = await cart.createCart(bookingCartRequest(bookingId) as any);
|
|
107
|
-
const cartId = cartIdOf(newCart);
|
|
108
|
-
if (!cartId) throw new Error("The booking couldn't be reserved — please try again.");
|
|
109
|
-
|
|
110
|
-
const calc: Raw = await cart.calculateCart(cartId);
|
|
111
|
-
if (checkoutRequired(service, cartTotal(calc))) {
|
|
112
|
-
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
113
|
-
const session: Raw = await redirects.createRedirectSession(checkoutRedirectRequest(cartId, origin));
|
|
114
|
-
const url = session?.redirectSession?.fullUrl;
|
|
115
|
-
if (!url) throw new Error("Checkout couldn't start — please try again.");
|
|
116
|
-
return { kind: "redirect", url };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const order: Raw = await cart.placeOrder(cartId);
|
|
120
|
-
return confirmedResult(bookingId, order);
|
|
121
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// Service rules and DTO mapping — transport-agnostic, imported by BOTH transports: ./services.ts
|
|
2
|
-
// (the SDK, managed Astro and React) and the REST twin in references/bookings/rest/services.ts
|
|
3
|
-
// (fetch, a static site or a port to another language). Every rule about price, "Free", duration,
|
|
4
|
-
// media, staff, and the payment option lives HERE, once. A raw entity may come from the SDK (`_id`,
|
|
5
|
-
// media as a `wix:image://` string) or from REST (`id`, media as an { id, url, width, height }
|
|
6
|
-
// object); the mappers accept both. Imports are type-only so a strip to JS emits no imports.
|
|
7
|
-
import type { BookingCategory, ServiceDetail, ServiceSummary, ServiceType } from "./types";
|
|
8
|
-
|
|
9
|
-
/** A raw Services V2 entity as either transport returns it. */
|
|
10
|
-
export type Raw = Record<string, any>;
|
|
11
|
-
/** Media value + size → https URL. Injected: the SDK transport scales through @wix/sdk, REST through the URL form. */
|
|
12
|
-
export type ImgSrc = (value: any, width: number, height: number) => string;
|
|
13
|
-
|
|
14
|
-
/** The Wix Bookings app id — the cart's catalogReference.appId and the services filter. */
|
|
15
|
-
export const BOOKINGS_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
|
|
16
|
-
/** Staff-member resource type id (ANY_RESOURCE fallback + staff filtering). */
|
|
17
|
-
export const STAFF_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
|
|
18
|
-
/** Requested on every service read — without it `staffMemberDetails` is absent and the DTO's staff is []. */
|
|
19
|
-
export const SERVICES_CONDITIONAL_FIELDS = ["STAFF_MEMBER_DETAILS"] as const;
|
|
20
|
-
|
|
21
|
-
export const rawId = (raw: Raw | undefined | null): string => raw?._id ?? raw?.id ?? "";
|
|
22
|
-
|
|
23
|
-
/** The filter every services read shares: this app's services, optionally one slug (`mainSlug.name`). */
|
|
24
|
-
export function servicesFilter(slug?: string): Raw {
|
|
25
|
-
return { appId: BOOKINGS_APP_ID, ...(slug ? { "mainSlug.name": slug } : {}) };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Hidden services never reach a tile — the same rule on both transports. */
|
|
29
|
-
export const isVisible = (raw: Raw): boolean => !raw.hidden;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* A media value the image helpers can resolve. The SDK hands over a `wix:image://v1/<id>/<name>#…`
|
|
33
|
-
* string (or an https URL); REST hands over the image OBJECT with a bare file id — rebuilt into the
|
|
34
|
-
* same `wix:image://` form the SDK produces, so imgSrc scales both identically.
|
|
35
|
-
*/
|
|
36
|
-
export function mediaValue(image: unknown): unknown {
|
|
37
|
-
if (!image || typeof image === "string") return image;
|
|
38
|
-
const o = image as Raw;
|
|
39
|
-
const url: string = o.url ?? "";
|
|
40
|
-
if (/^(https?:|wix:image:)/.test(url)) return url;
|
|
41
|
-
if (o.id) return `wix:image://v1/${o.id}/${encodeURIComponent(o.filename ?? "")}#originWidth=${o.width ?? 0}&originHeight=${o.height ?? 0}`;
|
|
42
|
-
return url;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function formatPrice(value: string | undefined, currency: string | undefined): string {
|
|
46
|
-
if (value == null || value === "" || Number(value) === 0) return "Free";
|
|
47
|
-
try {
|
|
48
|
-
return new Intl.NumberFormat(undefined, { style: "currency", currency: currency || "USD" }).format(Number(value));
|
|
49
|
-
} catch {
|
|
50
|
-
return `${value} ${currency ?? ""}`.trim();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function toSummary(raw: Raw, imgSrc: ImgSrc): ServiceSummary {
|
|
55
|
-
const price = raw.payment?.fixed?.price;
|
|
56
|
-
const free = raw.payment?.rateType === "NO_FEE" || !price?.value || Number(price.value) === 0;
|
|
57
|
-
return {
|
|
58
|
-
id: rawId(raw),
|
|
59
|
-
slug: raw.mainSlug?.name ?? raw.supportedSlugs?.[0]?.name ?? "",
|
|
60
|
-
name: raw.name ?? "",
|
|
61
|
-
tagLine: raw.tagLine ?? "",
|
|
62
|
-
type: (raw.type ?? "APPOINTMENT") as ServiceType,
|
|
63
|
-
price: free ? "Free" : formatPrice(price?.value, price?.currency),
|
|
64
|
-
free,
|
|
65
|
-
durationMinutes: raw.schedule?.availabilityConstraints?.sessionDurations?.[0] ?? null,
|
|
66
|
-
imageUrl: imgSrc(mediaValue(raw.media?.mainMedia?.image), 800, 800),
|
|
67
|
-
categoryId: rawId(raw.category) || null,
|
|
68
|
-
staff: ((raw.staffMemberDetails?.staffMembers ?? []) as Raw[])
|
|
69
|
-
.map((m) => ({ id: m.staffMemberId ?? "", name: m.name ?? "" }))
|
|
70
|
-
.filter((m) => m.id),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function toDetail(raw: Raw, imgSrc: ImgSrc): ServiceDetail {
|
|
75
|
-
const summary = toSummary(raw, imgSrc);
|
|
76
|
-
const online = raw.payment?.options?.online === true;
|
|
77
|
-
const inPerson = raw.payment?.options?.inPerson === true;
|
|
78
|
-
// Derive — never hardcode "ONLINE": a free/pay-in-person service booked ONLINE gets
|
|
79
|
-
// rejected by the cart with INSUFFICIENT_INVENTORY.
|
|
80
|
-
const paymentOption: "ONLINE" | "OFFLINE" = !online && inPerson ? "OFFLINE" : "ONLINE";
|
|
81
|
-
return {
|
|
82
|
-
...summary,
|
|
83
|
-
description: raw.description ?? "",
|
|
84
|
-
formId: rawId(raw.form) || null,
|
|
85
|
-
paymentOption,
|
|
86
|
-
cancellationFeeEnabled: raw.bookingPolicy?.cancellationFeePolicy?.enabled === true,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function toCategory(raw: Raw): BookingCategory {
|
|
91
|
-
return { id: rawId(raw), name: raw.name ?? "" };
|
|
92
|
-
}
|