@wix/agent-skills 1.19.1 → 1.20.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/references/EDITOR_REACT_COMPONENT.md +1 -1
- package/skills/wix-app/references/SERVICE_PLUGIN.md +14 -1
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +7 -2
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +53 -28
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +17 -4
- package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +3 -4
- package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +4 -0
- package/skills/wix-app/references/service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md +118 -0
- package/skills/wix-app/references/service-plugin/BOOKING-POLICY.md +65 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-PRICING.md +71 -0
- package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +11 -3
- package/skills/wix-app/references/service-plugin/BOOKINGS-VALIDATION.md +137 -0
- package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +23 -16
- package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +4 -0
- package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +4 -2
- package/skills/wix-app/references/service-plugin/REALTIME-PERMISSIONS.md +45 -0
- package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +15 -0
- package/skills/wix-app/references/service-plugin/TOOLS_PROVIDER.md +4 -0
- package/skills/wix-app/references/service-plugin/VALIDATIONS.md +4 -0
- package/skills/wix-base44-connector/SKILL.md +46 -24
- package/skills/wix-headless/SKILL.md +1 -0
- package/skills/wix-headless/references/CAPABILITIES.md +7 -2
- package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
- package/skills/wix-headless/references/inline-recipes/experience-store.md +49 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +431 -115
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
- package/skills/wix-headless/references/managed/CREATE.md +5 -0
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +8 -8
- package/skills/wix-headless-fast/install/fast-path.mjs +3 -1
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +13 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +227 -56
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +37 -3
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +266 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +165 -0
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +48 -15
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +129 -11
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +36 -6
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +190 -24
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +62 -4
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +21 -2
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +5 -1
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +59 -6
- package/skills/wix-manage/SKILL.md +8 -5
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
- package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
- package/skills/wix-manage/references/events/create-wix-event.md +46 -0
- package/skills/wix-manage/references/events/manage-wix-events.md +51 -1
- package/skills/wix-manage/references/google-ads/manage-campaign-lifecycle.md +6 -6
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
- package/skills/wix-manage/references/seo/manage-content-plan.md +1 -1
- package/skills/wix-manage/references/seo/manage-seo-suggestions.md +351 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +264 -34
- package/skills/wix-vibe-headless/install/deploy.cjs +33 -2
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -4
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44.md +19 -4
- package/skills/wix-vibe-headless/platforms/generic.md +6 -0
- package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +30 -0
- package/skills/wix-vibe-headless/references/_shared/routing.md +86 -0
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +44 -0
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +42 -0
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +1 -1
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +1 -1
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +43 -0
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +1 -1
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +36 -0
- package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +9 -0
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +110 -27
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +47 -35
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
package/package.json
CHANGED
|
@@ -110,7 +110,7 @@ extension configuration, and supporting files.
|
|
|
110
110
|
| Trigger | Read |
|
|
111
111
|
| --- | --- |
|
|
112
112
|
| Interactive/selectable part or custom state | [`DESIGN-STATES.md`](editor-react-component/DESIGN-STATES.md) |
|
|
113
|
-
|
|
|
113
|
+
| Creating interactive components or changing interactions/callbacks | [`FUNCTION-HANDLERS.md`](editor-react-component/FUNCTION-HANDLERS.md) |
|
|
114
114
|
| Browser APIs, effects, or time-dependent output | [`SSR.md`](editor-react-component/SSR.md) |
|
|
115
115
|
| Non-established CSS feature or DOM API, or user asks for one by name | [`BROWSER-SUPPORT.md`](editor-react-component/BROWSER-SUPPORT.md) |
|
|
116
116
|
| `npx wix build` or manifest generation exits with an error | [`MANIFEST-ERRORS.md`](editor-react-component/MANIFEST-ERRORS.md) |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
# Wix Service Plugin (SPI) Builder
|
|
3
3
|
|
|
4
|
-
Service plugins are a set of APIs defined by Wix that let you inject custom logic into the existing backend flows of Wix business solutions or introduce entirely new flows. When you implement a service plugin, Wix calls your custom functions during specific flows. Common use cases include eCommerce customization (shipping, fees, payment settings, validations)
|
|
4
|
+
Service plugins are a set of APIs defined by Wix that let you inject custom logic into the existing backend flows of Wix business solutions or introduce entirely new flows. When you implement a service plugin, Wix calls your custom functions during specific flows. Common use cases include eCommerce customization (shipping, fees, payment settings, validations), Bookings customization (staff sorting, booking validation, policies, pricing, availability), Realtime channel permissions, and exposing tools to the Wix AI assistant.
|
|
5
5
|
|
|
6
6
|
## Scaffold
|
|
7
7
|
|
|
@@ -16,12 +16,19 @@ Use `wix generate --params` with `extensionType: SERVICE_PLUGIN`. `pluginType` i
|
|
|
16
16
|
| `ECOM_PAYMENT_SETTINGS` | Payment Settings | No |
|
|
17
17
|
| `GIFT_CARDS_PROVIDER` | Gift Cards Provider | **Yes** |
|
|
18
18
|
| `STAFF_SORTING_PROVIDER` | Bookings Staff Sorting | No |
|
|
19
|
+
| `BOOKINGS_VALIDATION_PROVIDER` | Bookings Validation | No |
|
|
20
|
+
| `BOOKING_POLICY_PROVIDER` | Booking Policy | No |
|
|
21
|
+
| `BOOKINGS_PRICING_PROVIDER` | Bookings Pricing Provider | No |
|
|
22
|
+
| `AVAILABILITY_TIME_SLOTS_CONFIGURATION_PROVIDER` | Availability Time Slots Configuration | No |
|
|
19
23
|
| `REALTIME_PERMISSIONS_PROVIDER` | Realtime Permissions Provider | No |
|
|
24
|
+
| `TOOLS_PROVIDER_CONFIG` | Tools Provider | No |
|
|
20
25
|
|
|
21
26
|
> **Singular types** — `ECOM_SHIPPING_RATES`, `ECOM_DISCOUNTS_TRIGGER`, and `GIFT_CARDS_PROVIDER` are **singular**: only one component of each type is allowed per app. Never scaffold or include two components of the same singular type in the same app payload.
|
|
22
27
|
|
|
23
28
|
`name` must be lowercase alphanumeric + hyphens, max 19 characters. The CLI generates the folder, `plugin.ts`, the builder file, the UUID, and the `src/extensions.ts` registration with the appropriate builder method for the SPI type. Some SPI types (e.g., `ECOM_SHIPPING_RATES`) get a `description` placeholder field in the generated builder — replace it with your real copy.
|
|
24
29
|
|
|
30
|
+
> **Missing dependency after scaffolding:** `wix generate` adds the SPI's SDK package (e.g. `@wix/bookings`, `@wix/realtime`) to `package.json`, but does not always install it into `node_modules`. If `tsc`/`wix build` reports the import as unresolved right after scaffolding, run `npm install <package>` for just that package (no need for a full reinstall) before debugging further.
|
|
31
|
+
|
|
25
32
|
## References
|
|
26
33
|
|
|
27
34
|
**You MUST read the relevant reference document before implementing an SPI**, and **call `ReadFullDocsMethodSchema`** with the docs URL it points at to get the exact request/response types — **do NOT edit code until you have the schema**. If the schema alone isn't enough, follow up with `ReadFullDocsArticle` on the same URL for prose explanations and additional code examples. Each reference also contains the correct imports, handler signatures, response structures, and a worked example.
|
|
@@ -35,6 +42,12 @@ Use `wix generate --params` with `extensionType: SERVICE_PLUGIN`. `pluginType` i
|
|
|
35
42
|
| Shipping Rates | [SHIPPING-RATES.md](service-plugin/SHIPPING-RATES.md) |
|
|
36
43
|
| Validations | [VALIDATIONS.md](service-plugin/VALIDATIONS.md) |
|
|
37
44
|
| Bookings Staff Sorting | [BOOKINGS-STAFF-SORTING.md](service-plugin/BOOKINGS-STAFF-SORTING.md) |
|
|
45
|
+
| Bookings Validation | [BOOKINGS-VALIDATION.md](service-plugin/BOOKINGS-VALIDATION.md) |
|
|
46
|
+
| Booking Policy | [BOOKING-POLICY.md](service-plugin/BOOKING-POLICY.md) |
|
|
47
|
+
| Bookings Pricing Provider | [BOOKINGS-PRICING.md](service-plugin/BOOKINGS-PRICING.md) |
|
|
48
|
+
| Availability Time Slots Configuration | [AVAILABILITY-TIME-SLOTS-CONFIGURATION.md](service-plugin/AVAILABILITY-TIME-SLOTS-CONFIGURATION.md) |
|
|
49
|
+
| Realtime Permissions Provider | [REALTIME-PERMISSIONS.md](service-plugin/REALTIME-PERMISSIONS.md) |
|
|
50
|
+
| Tools Provider | [TOOLS_PROVIDER.md](service-plugin/TOOLS_PROVIDER.md) |
|
|
38
51
|
|
|
39
52
|
## Implementation Pattern
|
|
40
53
|
|
|
@@ -192,13 +192,18 @@ the `a11y` contract, and SSR safety. Verify what it cannot see:
|
|
|
192
192
|
meaning stays in the accessibility tree.
|
|
193
193
|
- State hidden through `--display` or transforms agrees with focusability and
|
|
194
194
|
the accessibility tree; disabled and inert states behave consistently.
|
|
195
|
-
- Custom widgets (tabs, menus, dialogs, sliders) implement their
|
|
196
|
-
pattern
|
|
195
|
+
- Custom widgets (tabs, menus, dialogs, sliders) implement their full APG
|
|
196
|
+
keyboard pattern (arrow keys, Home/End, Escape, roving `tabIndex`) and
|
|
197
|
+
structural relationships, or use a plain native element instead of
|
|
198
|
+
borrowing the role.
|
|
197
199
|
- Interactive controls have a hit area of at least 24×24 CSS px and visible
|
|
198
200
|
focus.
|
|
199
201
|
- The root implements the direction contract, and every `ReactNode` slot
|
|
200
202
|
isolates nested content with `dir="ltr"`.
|
|
201
203
|
|
|
204
|
+
- An auto-rotating set of readable parallel items uses `aria-live="off"` while
|
|
205
|
+
it is rotating and `aria-live="polite"` while it is stopped.
|
|
206
|
+
|
|
202
207
|
## Pre-Fix Checks for Non-Interactive Controls
|
|
203
208
|
|
|
204
209
|
Before adding `role="button"`, `tabIndex`, and keyboard handlers to a non-native
|
|
@@ -9,10 +9,12 @@ Every such component must ship an on-stage **play/pause button**.
|
|
|
9
9
|
|
|
10
10
|
- [Apply When](#apply-when)
|
|
11
11
|
- [Required Contract](#required-contract)
|
|
12
|
+
- [Announce Manually Selected Parallel Content](#announce-manually-selected-parallel-content)
|
|
12
13
|
- [Define Props](#1-define-props)
|
|
13
14
|
- [Manage Playback State](#2-manage-playback-state)
|
|
14
15
|
- [Add the Play/Pause Button](#3-add-the-playpause-button)
|
|
15
16
|
- [Suppress Autoplay in `component.preview.tsx`](#4-suppress-autoplay-in-componentpreviewtsx)
|
|
17
|
+
- [Edit the generated `component.preview.tsx`](#edit-the-generated-componentpreviewtsx)
|
|
16
18
|
- [Checklist](#checklist)
|
|
17
19
|
|
|
18
20
|
## Apply When
|
|
@@ -41,12 +43,26 @@ implementation detail.
|
|
|
41
43
|
existing playback prop names and add only the missing safety contract.
|
|
42
44
|
Carousels, sliders, slideshows, and galleries **must** define `autoPlay`
|
|
43
45
|
(default `true`).
|
|
44
|
-
2. **Playback state** —
|
|
46
|
+
2. **Playback state** — play/pause toggle state, derived `isPlaying`, and
|
|
47
|
+
`handlePause` / `handleResume`
|
|
45
48
|
3. **Play/pause button** — overlay `<button>` with inline SVG icon and CSS
|
|
46
49
|
positioning; it must be a named part with `elementProps` wiring, a hover
|
|
47
50
|
design state, and a standalone `:focus-visible` keyboard indicator
|
|
48
51
|
4. **Modify `component.preview.tsx`** — suppress autoplay in editor design mode
|
|
49
|
-
5. **Respect `prefers-reduced-motion`** —
|
|
52
|
+
5. **Respect `prefers-reduced-motion`** — start paused when the OS requests reduced motion
|
|
53
|
+
|
|
54
|
+
### Announce Manually Selected Parallel Content
|
|
55
|
+
|
|
56
|
+
For carousel-like components that swap readable parallel items, make the item
|
|
57
|
+
wrapper a live region:
|
|
58
|
+
|
|
59
|
+
- Use `aria-live={isPlaying ? 'off' : 'polite'}` and `aria-atomic="false"`.
|
|
60
|
+
- `isPlaying = isPlayOn && !isHovered && !isStoppedByFocus`. Reduced motion
|
|
61
|
+
starts paused; editor mode, `autoPlay={false}`, and pause set it false.
|
|
62
|
+
Keyboard focus remains stopped until the rotation control restarts; hover
|
|
63
|
+
pauses temporarily.
|
|
64
|
+
- Previous/next changes items. Use root `onMouseEnter`/`onMouseLeave`;
|
|
65
|
+
leaving resumes unless focus has stopped rotation.
|
|
50
66
|
|
|
51
67
|
## 1. Define Props
|
|
52
68
|
|
|
@@ -83,40 +99,48 @@ support repeat behavior; do not add it only to match the example.
|
|
|
83
99
|
|
|
84
100
|
## 2. Manage Playback State
|
|
85
101
|
|
|
86
|
-
Track play/pause state with `useState`. Initialize it from `autoPlay`
|
|
102
|
+
Track play/pause button state with `useState`. Initialize it from `autoPlay`
|
|
103
|
+
and reduced motion; reduced motion starts paused, but a visitor can start it.
|
|
87
104
|
|
|
88
105
|
```tsx
|
|
89
106
|
import { useReducedMotion } from '@wix/react-component-utils';
|
|
90
107
|
|
|
91
|
-
// inside the component:
|
|
92
108
|
const reducedMotion = useReducedMotion();
|
|
93
109
|
|
|
94
|
-
const [
|
|
95
|
-
|
|
96
|
-
// when autoPlay prop changes, re-sync (but still respect reduced motion)
|
|
97
|
-
React.useEffect(() => {
|
|
98
|
-
setIsPlaying((autoPlay ?? true) && !reducedMotion);
|
|
99
|
-
}, [autoPlay]);
|
|
110
|
+
const [isPlayOn, setIsPlayOn] = React.useState(() => (autoPlay ?? true) && !reducedMotion);
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
// (visitor may have manually paused — don't restart against their will)
|
|
112
|
+
const skipInitialAutoPlaySync = React.useRef(true);
|
|
103
113
|
React.useEffect(() => {
|
|
104
|
-
if (
|
|
105
|
-
|
|
114
|
+
if (skipInitialAutoPlaySync.current) {
|
|
115
|
+
skipInitialAutoPlaySync.current = false;
|
|
116
|
+
return;
|
|
106
117
|
}
|
|
107
|
-
|
|
118
|
+
setIsPlayOn(autoPlay ?? true);
|
|
119
|
+
}, [autoPlay]);
|
|
108
120
|
|
|
109
|
-
const handlePause = () =>
|
|
110
|
-
const handleResume = () =>
|
|
121
|
+
const handlePause = () => setIsPlayOn(false);
|
|
122
|
+
const handleResume = () => setIsPlayOn(true);
|
|
111
123
|
```
|
|
112
124
|
|
|
113
|
-
|
|
125
|
+
`reducedMotion` is a runtime signal, not a manifest/data prop.
|
|
126
|
+
|
|
127
|
+
Derive runtime playback from `isPlayOn` plus only needed conditions. Without
|
|
128
|
+
another condition, use `const isPlaying = isPlayOn`. A carousel, slideshow,
|
|
129
|
+
slider, or gallery also uses hover and focus-stop state:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
const isPlaying = isPlayOn && !isHovered && !isStoppedByFocus;
|
|
133
|
+
```
|
|
114
134
|
|
|
115
|
-
Pass `isPlaying` to the
|
|
135
|
+
Pass `isPlaying` to the renderer. The button uses `isPlayOn` and toggles
|
|
136
|
+
between `handlePause` / `handleResume` on click. In a carousel-like component,
|
|
137
|
+
focus entering anything except that button sets `isPlayOn` to `false` and
|
|
138
|
+
`isStoppedByFocus` to `true`; `handleResume` sets `isPlayOn` to `true` and
|
|
139
|
+
clears `isStoppedByFocus`.
|
|
116
140
|
|
|
117
141
|
## 3. Add the Play/Pause Button
|
|
118
142
|
|
|
119
|
-
Create play/pause icons that visually match the component's style. Use simple recognizable shapes — a triangle for play, two rectangles for pause — implemented as inline SVG so there is no external icon dependency. Size
|
|
143
|
+
Create play/pause icons that visually match the component's style. Use simple recognizable shapes — a triangle for play, two rectangles for pause — implemented as inline SVG so there is no external icon dependency. Size and color of SVG should be controlled by CSS variables declared on the button level: --icon-size and --icon-color.
|
|
120
144
|
|
|
121
145
|
Position the button absolutely so it overlays the content without pushing other elements out of place:
|
|
122
146
|
|
|
@@ -128,14 +152,14 @@ Position the button absolutely so it overlays the content without pushing other
|
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
.playButton {
|
|
155
|
+
--icon-size: 20px;
|
|
156
|
+
--icon-color: #ffffff;
|
|
131
157
|
position: absolute;
|
|
132
158
|
inset-inline-end: 5px;
|
|
133
159
|
inset-block-start: 5px;
|
|
134
160
|
}
|
|
135
161
|
|
|
136
|
-
/* Design-state selectors: pair native pseudo-class with editor-injected modifier class.
|
|
137
|
-
The editor applies the modifier class (e.g. my-animation-play-button--hover) when
|
|
138
|
-
the site owner previews that state in the design panel, so both selectors must exist. */
|
|
162
|
+
/* Design-state selectors: pair native pseudo-class with editor-injected modifier class. */
|
|
139
163
|
.playButton:global(.my-animation-play-button--hover),
|
|
140
164
|
.playButton:hover {
|
|
141
165
|
/* e.g. background: rgba(255, 255, 255, 1); */
|
|
@@ -177,12 +201,12 @@ Wire the named part completely:
|
|
|
177
201
|
styles.playButton,
|
|
178
202
|
elementProps?.playButton?.className,
|
|
179
203
|
)}
|
|
180
|
-
onClick={
|
|
204
|
+
onClick={isPlayOn ? handlePause : handleResume}
|
|
181
205
|
aria-label={
|
|
182
|
-
|
|
206
|
+
isPlayOn ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
|
|
183
207
|
}
|
|
184
208
|
>
|
|
185
|
-
{
|
|
209
|
+
{isPlayOn ? <PauseIcon /> : <PlayIcon />}
|
|
186
210
|
</button>
|
|
187
211
|
```
|
|
188
212
|
|
|
@@ -233,8 +257,7 @@ const ComponentNamePreview: FC<ComponentProps<typeof Component>> = (props) => {
|
|
|
233
257
|
};
|
|
234
258
|
```
|
|
235
259
|
|
|
236
|
-
|
|
237
|
-
In preview mode (`isEditMode` is `false`) → both use the user's configured values.
|
|
260
|
+
Design mode stops autoplay and exposes the control; preview uses the user's values.
|
|
238
261
|
|
|
239
262
|
## Checklist
|
|
240
263
|
|
|
@@ -243,6 +266,8 @@ In preview mode (`isEditMode` is `false`) → both use the user's configured val
|
|
|
243
266
|
present only when repeat behavior is supported.
|
|
244
267
|
- [ ] Reduced motion starts paused and never restarts playback automatically.
|
|
245
268
|
- [ ] The play/pause button is a fully wired named part with a stable accessible name.
|
|
269
|
+
- [ ] Both icons inherit button-owned `--icon-size` for width and height and
|
|
270
|
+
`--icon-color` via `color` and `currentColor` for fill or stroke.
|
|
246
271
|
- [ ] Hover has a paired editor design state; `:focus-visible` remains a
|
|
247
272
|
standalone keyboard indicator.
|
|
248
273
|
- [ ] Hover-only visibility changes behavior, not the button's editable styling surface.
|
|
@@ -33,6 +33,10 @@ export type PlanCardProps = {
|
|
|
33
33
|
heading?: string;
|
|
34
34
|
plans?: Array<Plan>;
|
|
35
35
|
onClick?: (event: React.MouseEvent) => void;
|
|
36
|
+
onMouseIn?: (event: React.MouseEvent) => void;
|
|
37
|
+
onMouseOut?: (event: React.MouseEvent) => void;
|
|
38
|
+
onFocus?: (event: React.FocusEvent) => void;
|
|
39
|
+
onBlur?: (event: React.FocusEvent) => void;
|
|
36
40
|
|
|
37
41
|
elementProps?: {
|
|
38
42
|
cta?: { className?: string; href?: string };
|
|
@@ -47,9 +51,8 @@ Rules:
|
|
|
47
51
|
code. Do not add one-off ARIA props or spread the whole object.
|
|
48
52
|
- Expose only content and behavior that the site owner controls. Keep derived
|
|
49
53
|
values internal.
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
internal.
|
|
54
|
+
- Default to common optional SDK callbacks by capability; add specialized ones
|
|
55
|
+
when requested. Keep implementation handlers internal.
|
|
53
56
|
- Use `Array<T>`, not `T[]`, for exported arrays.
|
|
54
57
|
|
|
55
58
|
## Numeric Range Constraints
|
|
@@ -176,7 +179,16 @@ export const defaultProps = {
|
|
|
176
179
|
### Wix Data Types
|
|
177
180
|
|
|
178
181
|
Use `Image`, `Link`, `Video`, `Audio`, `VectorArt`, `RichText` from
|
|
179
|
-
`@wix/editor-react-types`.
|
|
182
|
+
`@wix/editor-react-types`. Model authored media with the corresponding Wix
|
|
183
|
+
media type, whether it is a top-level prop or a field in an array item. Name
|
|
184
|
+
the field for the media itself (`image`, `video`, `audio`, and so on), not for
|
|
185
|
+
one representation of it.
|
|
186
|
+
|
|
187
|
+
Do not represent media as a URL/source string or split its metadata across
|
|
188
|
+
primitive props. Preserve the media object through the public contract so the
|
|
189
|
+
component can consume all of its supported data. See
|
|
190
|
+
`node_modules/@wix/react-component-schema/dist/editor-react-types.d.ts` for the
|
|
191
|
+
full list.
|
|
180
192
|
|
|
181
193
|
## Defaults and Resources
|
|
182
194
|
|
|
@@ -242,5 +254,6 @@ Do not extract tiny fragments merely to satisfy a line-count threshold.
|
|
|
242
254
|
- [ ] Named inner parts have `elementProps` wiring; leaf components avoid exported `children`.
|
|
243
255
|
- [ ] One-body-visible arrays use the active-item contract and render all bodies.
|
|
244
256
|
- [ ] Array elements are objects with semantic named fields. No separate `id` field added to item types; React keys use item fields (stable unique → slug → index), not a typed `id`.
|
|
257
|
+
- [ ] Authored media uses the corresponding Wix media type, not URL/source strings or flattened metadata.
|
|
245
258
|
- [ ] Defaults live only in the props file (no JSX fallbacks).
|
|
246
259
|
- [ ] Resources are Wix-hosted, prop-supplied, or locally bundled.
|
|
@@ -1,57 +1,92 @@
|
|
|
1
1
|
# Function Handlers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
When creating a component or changing interactions, expose common optional
|
|
4
|
+
callbacks by capability, without requiring an explicit request.
|
|
5
|
+
|
|
6
|
+
## Default Support
|
|
7
|
+
|
|
8
|
+
Apply defaults to the public interaction surface:
|
|
9
|
+
|
|
10
|
+
| Component capability | Default optional props |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| Pointer/click interaction, including native controls | `onClick`, `onMouseIn`, `onMouseOut` |
|
|
13
|
+
| Focusable control | `onFocus`, `onBlur` |
|
|
14
|
+
| Editable value, text, or selection | `onChange` |
|
|
15
|
+
|
|
16
|
+
Combine matching rows; leave decorative elements alone. Explicit API restrictions
|
|
17
|
+
override defaults. Specialized callbacks (`onDblClick`, `onPlay`, `onEnded`,
|
|
18
|
+
`onActivate`, etc.) require a request.
|
|
19
|
+
|
|
20
|
+
Classify composite controls by their interactive descendants. For example, a
|
|
21
|
+
radio-based rating input matches all three rows and exposes all six callbacks.
|
|
5
22
|
|
|
6
23
|
## Standard SDK Handlers
|
|
7
24
|
|
|
8
25
|
Use the exact SDK prop name and wire it to the matching React DOM event.
|
|
9
26
|
|
|
10
|
-
| SDK prop | React DOM prop | Type |
|
|
11
|
-
| --- | --- | --- |
|
|
12
|
-
| `onClick` | `onClick` | `(event: React.MouseEvent) => void` |
|
|
13
|
-
| `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` |
|
|
14
|
-
| `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` |
|
|
15
|
-
| `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` |
|
|
16
|
-
| `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` |
|
|
17
|
-
| `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` |
|
|
18
|
-
| `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` |
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
| SDK prop | React DOM prop | Type | Zero config `domEvent` |
|
|
28
|
+
| --- | --- | --- | --- |
|
|
29
|
+
| `onClick` | `onClick` | `(event: React.MouseEvent) => void` | `POINTER` |
|
|
30
|
+
| `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` | `POINTER` |
|
|
31
|
+
| `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` | `CHANGE` |
|
|
32
|
+
| `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` | `FOCUS` |
|
|
33
|
+
| `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` | `FOCUS` |
|
|
34
|
+
| `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` | `POINTER` |
|
|
35
|
+
| `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` | `POINTER` |
|
|
36
|
+
|
|
37
|
+
React `onChange` fires on live text edits and selection changes. Pass that
|
|
38
|
+
`ChangeEvent` directly. Do not synthesize a commit callback from blur or cast a
|
|
39
|
+
`FocusEvent` to `ChangeEvent`.
|
|
40
|
+
|
|
41
|
+
Callbacks only notify; they never enable controlled mode. Internal handlers run
|
|
42
|
+
component logic, then call the optional callback once, preserving event payloads
|
|
43
|
+
and disabled/read-only guards. Behavior must work without callbacks or consumer
|
|
44
|
+
prop updates. Forward directly when no internal logic is needed. Add controlled
|
|
45
|
+
state only when requested; preserve existing controlled APIs when editing.
|
|
21
46
|
|
|
22
47
|
```tsx
|
|
48
|
+
import * as React from 'react';
|
|
49
|
+
|
|
23
50
|
export type ActionProps = {
|
|
24
51
|
onClick?: (event: React.MouseEvent) => void;
|
|
25
|
-
onDblClick?: (event: React.MouseEvent) => void;
|
|
26
52
|
onMouseIn?: (event: React.MouseEvent) => void;
|
|
27
53
|
onMouseOut?: (event: React.MouseEvent) => void;
|
|
54
|
+
onFocus?: (event: React.FocusEvent) => void;
|
|
55
|
+
onBlur?: (event: React.FocusEvent) => void;
|
|
28
56
|
};
|
|
29
57
|
|
|
30
58
|
export const Action: React.FC<ActionProps> = (props) => {
|
|
31
|
-
const { onClick,
|
|
59
|
+
const { onClick, onMouseIn, onMouseOut, onFocus, onBlur } = props;
|
|
60
|
+
const [count, setCount] = React.useState(0);
|
|
61
|
+
|
|
62
|
+
const handleClick = (event: React.MouseEvent) => {
|
|
63
|
+
setCount((value) => value + 1);
|
|
64
|
+
onClick?.(event);
|
|
65
|
+
};
|
|
32
66
|
|
|
33
67
|
return (
|
|
34
68
|
<button
|
|
35
69
|
type="button"
|
|
36
|
-
onClick={
|
|
37
|
-
onDoubleClick={onDblClick}
|
|
70
|
+
onClick={handleClick}
|
|
38
71
|
onMouseEnter={onMouseIn}
|
|
39
72
|
onMouseLeave={onMouseOut}
|
|
40
|
-
|
|
73
|
+
onFocus={onFocus}
|
|
74
|
+
onBlur={onBlur}
|
|
75
|
+
>
|
|
76
|
+
Clicked {count} times
|
|
77
|
+
</button>
|
|
41
78
|
);
|
|
42
79
|
};
|
|
43
80
|
```
|
|
44
81
|
|
|
45
|
-
|
|
46
|
-
`onMouseLeave
|
|
82
|
+
Zero config requires SDK names, not `onDoubleClick`, `onMouseEnter`, or
|
|
83
|
+
`onMouseLeave`, in public props.
|
|
47
84
|
|
|
48
85
|
## Custom Callbacks
|
|
49
86
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Use this pattern:
|
|
87
|
+
Custom notifications use `() => void`; zero config emits no event parameter for
|
|
88
|
+
unrecognized callback names. Custom callbacks must not declare React event
|
|
89
|
+
parameters. Use a standard SDK handler above when the event is public:
|
|
55
90
|
|
|
56
91
|
```tsx
|
|
57
92
|
export type VideoPlayerProps = {
|
|
@@ -62,14 +97,19 @@ export type VideoPlayerProps = {
|
|
|
62
97
|
|
|
63
98
|
export const VideoPlayer: React.FC<VideoPlayerProps> = (props) => {
|
|
64
99
|
const { onPlay, onPause, onEnded } = props;
|
|
65
|
-
return
|
|
100
|
+
return (
|
|
101
|
+
<video
|
|
102
|
+
controls
|
|
103
|
+
onPlay={() => onPlay?.()}
|
|
104
|
+
onPause={() => onPause?.()}
|
|
105
|
+
onEnded={() => onEnded?.()}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
66
108
|
};
|
|
67
109
|
```
|
|
68
110
|
|
|
69
111
|
## Checklist
|
|
70
112
|
|
|
71
|
-
- [ ]
|
|
72
|
-
- [ ]
|
|
73
|
-
- [ ]
|
|
74
|
-
- [ ] Custom callbacks do not leak React event objects.
|
|
75
|
-
- [ ] Internal handlers remain implementation details.
|
|
113
|
+
- [ ] Defaults match capabilities; specialized callbacks are requested.
|
|
114
|
+
- [ ] SDK names, DOM mappings, and payloads match the contract.
|
|
115
|
+
- [ ] Internal behavior works independently and notifies callbacks once.
|
|
@@ -12,8 +12,7 @@ contracts changed by the request:
|
|
|
12
12
|
2. Separate user-authored content and stable behavior from derived values and
|
|
13
13
|
visual styling.
|
|
14
14
|
3. Identify interaction and selectable states affected by the request.
|
|
15
|
-
4.
|
|
16
|
-
requests a public callback.
|
|
15
|
+
4. Include common callbacks by capability; keep implementation handlers internal.
|
|
17
16
|
5. Identify runtime concerns such as browser APIs, autoplay, editor-only
|
|
18
17
|
behavior, or live site context before implementation.
|
|
19
18
|
|
|
@@ -49,8 +48,8 @@ export const ProfileCard: React.FC<ProfileCardProps> = (props) => {
|
|
|
49
48
|
- When local state intentionally mirrors a prop, synchronize it when that prop
|
|
50
49
|
changes. Do not use effect-managed state for a value that can be derived
|
|
51
50
|
during render.
|
|
52
|
-
-
|
|
53
|
-
|
|
51
|
+
- Internal handlers run component behavior, then notify optional callbacks.
|
|
52
|
+
Callback presence never enables controlled mode.
|
|
54
53
|
|
|
55
54
|
### Structure and Parts
|
|
56
55
|
|
|
@@ -87,6 +87,10 @@ additionalFees.provideHandlers({
|
|
|
87
87
|
});
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
## Manual Setup Required
|
|
91
|
+
|
|
92
|
+
None — confirmed live. Once the app is installed and released, Wix calls this plugin automatically on every cart/checkout calculation; no dashboard configuration or extra API call is needed to "turn it on."
|
|
93
|
+
|
|
90
94
|
## Key Implementation Notes
|
|
91
95
|
|
|
92
96
|
1. **Elevate permissions for API calls** - Use `auth.elevate` from `@wix/essentials` when calling Wix APIs from service plugins
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Availability Time Slots Configuration Service Plugin Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Availability Time Slots Configuration SPI lets you customize how Wix calculates available time slots for booking services: duration, split interval, buffer time, locations, and required/available resources. Wix calls your `listAvailabilityTimeSlotConfigurations` handler whenever a customer views available times or books a service. [Resources](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/introduction) must already exist before time slot calculation runs.
|
|
6
|
+
|
|
7
|
+
## ⚠️ `resourceTypeId` is not a placeholder you can invent
|
|
8
|
+
|
|
9
|
+
`resourceTypes[].resourceTypeId` must be a real resource type ID or Wix rejects every availability query for the service — confirmed live: a made-up GUID here 500s `List Availability Time Slots` and fails booking creation with `FAILED_VALIDATING_AVAILABILITY`, for every service on the site, not just the one you're configuring. If the service is staffed (the common case), use the universal Wix Bookings staff-member resource type ID, which is the same across every site — it is not something you provision or look up per site:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
const STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For a non-staff resource (a room, piece of equipment, etc.), get the real ID from [Get Resource](https://dev.wix.com/docs/api-reference/business-solutions/bookings/resources/resources-v2/get-resource) (`resource.typeId`) — never fabricate one.
|
|
16
|
+
|
|
17
|
+
## ⚠️ `resourceTypes[].resourceIds` — populate it, don't leave it implicit
|
|
18
|
+
|
|
19
|
+
`resourceIds` ("IDs of resources available for booking within this resource type... may be a subset of all resources in the type") tells Wix *which* resources of that type are valid for this specific service. Omitting it (relying on `resourceTypeId` alone) was part of the same live 500 chain above — once `resourceTypeId` and `scheduleId` were both fixed, availability queries still failed until `resourceIds` was populated too. Get the service's actual assigned staff from [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.staffMemberIds`.
|
|
20
|
+
|
|
21
|
+
## ⚠️ `scheduleId` is required — omitting it also 500s
|
|
22
|
+
|
|
23
|
+
The response schema requires `scheduleId` (confirmed live: leaving it out 500s the same as a bad `resourceTypeId`), but it's easy to miss since it's not mentioned in the request/response summary above. Fetch it from the service itself — [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.schedule._id` (REST docs call this field `id`; the SDK's `Service.schedule` type names it `_id`):
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { auth } from "@wix/essentials";
|
|
27
|
+
import { services } from "@wix/bookings";
|
|
28
|
+
|
|
29
|
+
const elevatedGetService = auth.elevate(services.getService);
|
|
30
|
+
const service = await elevatedGetService(serviceId);
|
|
31
|
+
const scheduleId = service.schedule?._id;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## ⚠️ `locations[]._id` is required when `locationType` is `BUSINESS`
|
|
35
|
+
|
|
36
|
+
The schema notes `_id` is "returned only for business locations" — read that as *required whenever `locationType: BUSINESS`*, not optional. Omitting it was the third and last piece of the same live 500 chain. Get the service's actual configured locations from [Get Service](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/get-service)'s `service.locations[]` (each has its own `_id`) rather than hardcoding a bare `{ locationType: "BUSINESS" }`.
|
|
37
|
+
|
|
38
|
+
## Manual Setup Required
|
|
39
|
+
|
|
40
|
+
None for activation — Wix calls this plugin automatically once installed and released. But get all three fields above right, or every availability query for every service on the site fails with a bare 500, not just a validation error on the one service you're configuring.
|
|
41
|
+
|
|
42
|
+
## Request and Response Schema
|
|
43
|
+
|
|
44
|
+
Before implementing, call `ReadFullDocsMethodSchema` on the docs URL to get the full request/response types.
|
|
45
|
+
|
|
46
|
+
| Handler | Docs URL |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `listAvailabilityTimeSlotConfigurations` | https://dev.wix.com/docs/api-reference/business-solutions/bookings/time-slots/availability-time-slots-configuration-service-plugin/list-availability-time-slot-configurations?apiView=SDK |
|
|
49
|
+
|
|
50
|
+
The request's `services` array (up to 100 entries) lists each service to configure, with the customer's `customerChoices` (selected duration variant and/or add-ons) — the same service GUID can appear more than once with different choices. Your response's `configurations` array must contain exactly one configuration per requested entry, **in the same order**, with `resourceTypes`, `locations`, and `scheduleId` fully populated.
|
|
51
|
+
|
|
52
|
+
For services with a bookable duration **range** (the customer picks how long to book), return `durationUnit` (`HOUR` or `DAY`) with `minDuration`/`maxDuration` instead of a fixed `duration`; for hourly ranges also return `duration.defaultInMinutes` as the minimum.
|
|
53
|
+
|
|
54
|
+
## Example: Duration From Add-Ons, Single Resource Type
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { availabilityTimeSlotsConfiguration } from "@wix/bookings/service-plugins";
|
|
58
|
+
import { auth } from "@wix/essentials";
|
|
59
|
+
import { services } from "@wix/bookings";
|
|
60
|
+
|
|
61
|
+
// Universal Wix Bookings staff-member resource type ID — same on every site, not per-site data.
|
|
62
|
+
const STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
|
|
63
|
+
|
|
64
|
+
availabilityTimeSlotsConfiguration.provideHandlers({
|
|
65
|
+
listAvailabilityTimeSlotConfigurations: async (payload) => {
|
|
66
|
+
const { request } = payload;
|
|
67
|
+
// services is optional on the request type — default it, or the .map()
|
|
68
|
+
// calls below fail `tsc` with "possibly undefined."
|
|
69
|
+
const { services: requestedServices = [] } = request;
|
|
70
|
+
|
|
71
|
+
// scheduleId is required in the response but isn't in the request — fetch it per service.
|
|
72
|
+
const elevatedGetService = auth.elevate(services.getService);
|
|
73
|
+
const serviceById = new Map(
|
|
74
|
+
await Promise.all(
|
|
75
|
+
requestedServices.map(async ({ serviceId }) =>
|
|
76
|
+
[serviceId, serviceId ? await elevatedGetService(serviceId) : undefined] as const
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const configurations = requestedServices.map(({ serviceId, customerChoices }) => {
|
|
82
|
+
const baseDuration = 60;
|
|
83
|
+
const addOnMinutes = (customerChoices?.addOnIds ?? []).length * 15;
|
|
84
|
+
const service = serviceById.get(serviceId);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
serviceId,
|
|
88
|
+
resourceTypes: [
|
|
89
|
+
{
|
|
90
|
+
resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID,
|
|
91
|
+
resourceIds: service?.staffMemberIds ?? [],
|
|
92
|
+
numberOfResourcesRequired: 1,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
locations: (service?.locations ?? []).map((location) => ({
|
|
96
|
+
_id: location._id,
|
|
97
|
+
locationType: availabilityTimeSlotsConfiguration.LocationType.BUSINESS,
|
|
98
|
+
})),
|
|
99
|
+
splitIntervalInMinutes: baseDuration + addOnMinutes,
|
|
100
|
+
duration: { defaultInMinutes: baseDuration + addOnMinutes },
|
|
101
|
+
bufferTimeInMinutes: 10,
|
|
102
|
+
scheduleId: service?.schedule?._id,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return { configurations };
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Key Implementation Notes
|
|
112
|
+
|
|
113
|
+
1. **One configuration per requested entry, same order** — the response array must line up positionally with the request's `services`, even when the same service GUID repeats with different `customerChoices`.
|
|
114
|
+
2. **Prefer `addOnIds` over `durationInMinutes`** in the request — when both are present they must agree, so let Wix Bookings compute total duration from add-ons rather than passing a duration you'd have to keep in sync.
|
|
115
|
+
3. **`splitIntervalInMinutes` vs `duration`** — when the split interval matches the service duration, `splitIntervalInMinutes` is `duration.defaultInMinutes + bufferTimeInMinutes`.
|
|
116
|
+
4. **Duration ranges** — use `durationUnit` for services where the customer picks the length of the booking; leave `duration` empty for `DAY`-unit ranges.
|
|
117
|
+
5. **Called on the hot path** — this handler runs on every availability query, so keep it fast.
|
|
118
|
+
6. **`services` is optional on the request type** — default it to `[]` when destructuring, or the `.map()` calls fail `tsc` with "possibly undefined."
|