@wix/agent-skills 1.8.0 → 1.10.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 +2 -1
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +14 -0
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +11 -6
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +3 -17
- package/skills/wix-app/references/editor-react-component/SSR.md +119 -0
- package/skills/wix-design-system/SKILL.md +1 -1
- package/skills/wix-headless/references/BUILD-astro.md +5 -7
- package/skills/wix-headless/references/DESIGN_SYSTEM.md +6 -16
- package/skills/wix-headless/references/SETUP.md +1 -1
- package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +41 -333
- package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +77 -428
- package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +2 -2
- package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +7 -10
- package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +2 -2
- package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +3 -3
- package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +2 -2
- package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +7 -7
- package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +4 -4
- package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +2 -2
- package/skills/wix-headless/references/astro/templates/Footer.astro +4 -4
- package/skills/wix-headless/references/astro/templates/Navigation.astro +4 -4
- package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +298 -142
- package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +104 -162
- package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +35 -14
- package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +11 -2
- package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +23 -85
- package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +245 -0
- package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +209 -198
- package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +114 -19
- package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +131 -15
- package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +3 -3
- package/skills/wix-headless/references/astro/templates/ecom/cart.astro +3 -3
- package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +17 -17
- package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +3 -3
- package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +4 -4
- package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +19 -18
- package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +3 -3
- package/skills/wix-headless/references/astro/templates/global.css +17 -15
- package/skills/wix-headless/references/astro/templates/index.astro +4 -4
- package/skills/wix-headless/references/astro/templates/stores/components-stores.css +31 -31
- package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +4 -4
- package/skills/wix-headless/references/bookings/FLOW.md +246 -0
- package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +65 -76
- package/skills/wix-headless/references/bookings/SERVICES_DATA.md +60 -0
- package/skills/wix-headless/references/custom/bookings/WIRING.md +169 -60
- package/skills/wix-headless/references/shared/DESIGN_MD.md +2 -3
- package/skills/wix-headless/references/shared/IMPLEMENTER.md +4 -4
- package/skills/wix-headless/references/shared/STYLING.md +15 -18
- package/skills/wix-headless/references/stores/INSTRUCTIONS.md +2 -2
- package/skills/wix-headless/references/verticals/bookings.md +9 -9
- package/skills/wix-headless/scripts/compose.mjs +4 -19
- package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -3
- package/skills/wix-manage/SKILL.md +12 -0
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -27
- package/skills/wix-manage/references/bookings/create-appointment-service.md +254 -0
- package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +37 -0
- package/skills/wix-manage/references/bookings/create-class-service.md +233 -0
- package/skills/wix-manage/references/bookings/create-course-service.md +237 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +2 -1
- package/skills/wix-manage/references/cms/cms-schema-management.md +1 -1
- package/skills/wix-manage/references/stores/create-product-from-image.md +154 -2
- package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +0 -84
- package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +0 -34
- package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +0 -74
- package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +0 -76
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Create Course Service"
|
|
3
|
+
description: "Create a course booking service — e.g. 'create a 6-week photography workshop', 'set up a training program', 'add a bootcamp course for $300', 'create a teacher training course'. Handles group capacity, full-course pricing, and fixed series defaults via bulkCreateServices API. Staff assignment is not used for courses."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Course Service from Prompt
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- User wants to create a multi-session course: "create a training program", "set up a 6-week workshop", "add a bootcamp course", "create a teacher training course"
|
|
11
|
+
- The service type is COURSE — a fixed series with pre-defined start and end dates
|
|
12
|
+
- Customers must book the entire course (all sessions), unlike CLASS where they can pick individual sessions
|
|
13
|
+
- For general service creation where the type is ambiguous, see [Create Booking Service from Prompt](./create-booking-service-from-prompt.md)
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- **Wix Bookings app installed** (App ID: `13d21c63-b5ec-5912-8397-c3a5ddb27a97`). Use [List Installed Apps](../app-installation/list-installed-apps.md) to verify, and [Install Wix Apps](../app-installation/install-wix-apps.md) to install if missing.
|
|
18
|
+
- For full API field definitions, validation rules, and troubleshooting, see [Create and Update Booking Services](./create-and-update-booking-services.md)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Step 1: Gather Business Context
|
|
23
|
+
|
|
24
|
+
Run these queries to collect site data for informed defaults.
|
|
25
|
+
|
|
26
|
+
### 1a. Query Service Categories
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
curl -X POST 'https://www.wixapis.com/bookings/v2/categories/query' \
|
|
30
|
+
-H 'Authorization: <AUTH>' \
|
|
31
|
+
-H 'Content-Type: application/json' \
|
|
32
|
+
-d '{ "query": {} }'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 1b. Get Site Currency
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
curl -X GET 'https://www.wixapis.com/site-properties/v4/properties' \
|
|
39
|
+
-H 'Authorization: <AUTH>'
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Extract `properties.paymentCurrency`. Fall back to `USD` if unavailable.
|
|
43
|
+
|
|
44
|
+
### 1c. Query Existing Services (Duplicate Check)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
curl -X POST 'https://www.wixapis.com/bookings/v2/services/query' \
|
|
48
|
+
-H 'Authorization: <AUTH>' \
|
|
49
|
+
-H 'Content-Type: application/json' \
|
|
50
|
+
-d '{ "query": { "paging": { "limit": 100 } } }'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Warn the user if a service with a similar name already exists.
|
|
54
|
+
|
|
55
|
+
> **Note:** Staff queries are optional for COURSE services since `staffMemberIds` is ignored by the API. However, querying staff can still be useful for context (e.g., mentioning the instructor in the description).
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Step 2: Apply Course Defaults
|
|
60
|
+
|
|
61
|
+
For any fields the user did not explicitly specify:
|
|
62
|
+
|
|
63
|
+
| Field | Default | Notes |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| Capacity | 10 | `defaultCapacity` — required for COURSE |
|
|
66
|
+
| Online booking | Enabled | `onlineBooking.enabled: true` |
|
|
67
|
+
|
|
68
|
+
### Pricing (if not specified)
|
|
69
|
+
|
|
70
|
+
- Use the site's currency from Step 1b
|
|
71
|
+
- If user specifies a price → `rateType: "FIXED"` (for the entire course)
|
|
72
|
+
- If user says "free" → `rateType: "NO_FEE"`, `options.inPerson: true`, `options.online: false`
|
|
73
|
+
- If no price mentioned → infer from context (workshops ~$100-300, training programs ~$200-500, bootcamps ~$150-400) or default to free
|
|
74
|
+
- **Course pricing is for the full course**, not per session — remind the user of this in the summary
|
|
75
|
+
|
|
76
|
+
### Capacity (if not specified)
|
|
77
|
+
|
|
78
|
+
- Default: 10 participants
|
|
79
|
+
- If user mentions group size → use their number
|
|
80
|
+
- Typical ranges: intensive 5-10, standard 10-20, lecture-style 20-50
|
|
81
|
+
|
|
82
|
+
### Category
|
|
83
|
+
|
|
84
|
+
- If categories exist → assign the most relevant one (e.g., "Training", "Workshops")
|
|
85
|
+
- If no categories exist → create one:
|
|
86
|
+
```bash
|
|
87
|
+
curl -X POST 'https://www.wixapis.com/bookings/v2/categories' \
|
|
88
|
+
-H 'Authorization: <AUTH>' \
|
|
89
|
+
-H 'Content-Type: application/json' \
|
|
90
|
+
-d '{ "category": { "name": "General" } }'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Service Name & Description
|
|
94
|
+
|
|
95
|
+
- Use the user's wording for the service name
|
|
96
|
+
- Generate a brief, professional description (1-2 sentences) mentioning it's a multi-session course
|
|
97
|
+
- If the user specified the number of sessions or duration, include that in the description
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Step 3: Create the Course Service
|
|
102
|
+
|
|
103
|
+
**CRITICAL: COURSE services do NOT use `staffMemberIds` or `sessionDurations`.** These fields are ignored. Use `defaultCapacity` instead.
|
|
104
|
+
|
|
105
|
+
**Paid course:**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
109
|
+
-H 'Authorization: <AUTH>' \
|
|
110
|
+
-H 'Content-Type: application/json' \
|
|
111
|
+
-d '{
|
|
112
|
+
"services": [{
|
|
113
|
+
"name": "<SERVICE_NAME>",
|
|
114
|
+
"description": "<GENERATED_DESCRIPTION>",
|
|
115
|
+
"type": "COURSE",
|
|
116
|
+
|
|
117
|
+
"onlineBooking": { "enabled": true },
|
|
118
|
+
"defaultCapacity": <CAPACITY>,
|
|
119
|
+
"payment": {
|
|
120
|
+
"rateType": "FIXED",
|
|
121
|
+
"options": { "online": true, "inPerson": false },
|
|
122
|
+
"fixed": {
|
|
123
|
+
"price": { "value": "<PRICE>", "currency": "<CURRENCY>" }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"category": {
|
|
127
|
+
"id": "<CATEGORY_ID>"
|
|
128
|
+
}
|
|
129
|
+
}]
|
|
130
|
+
}'
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Free course:**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
curl -X POST 'https://www.wixapis.com/bookings/v2/bulk/services/create' \
|
|
137
|
+
-H 'Authorization: <AUTH>' \
|
|
138
|
+
-H 'Content-Type: application/json' \
|
|
139
|
+
-d '{
|
|
140
|
+
"services": [{
|
|
141
|
+
"name": "<SERVICE_NAME>",
|
|
142
|
+
"description": "<GENERATED_DESCRIPTION>",
|
|
143
|
+
"type": "COURSE",
|
|
144
|
+
|
|
145
|
+
"onlineBooking": { "enabled": true },
|
|
146
|
+
"defaultCapacity": <CAPACITY>,
|
|
147
|
+
"payment": {
|
|
148
|
+
"rateType": "NO_FEE",
|
|
149
|
+
"options": { "online": false, "inPerson": true }
|
|
150
|
+
},
|
|
151
|
+
"category": {
|
|
152
|
+
"id": "<CATEGORY_ID>"
|
|
153
|
+
}
|
|
154
|
+
}]
|
|
155
|
+
}'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### COURSE-Specific Reminders
|
|
159
|
+
|
|
160
|
+
- Do **NOT** include `staffMemberIds` — it is ignored for COURSE services
|
|
161
|
+
- Do **NOT** include `schedule.availabilityConstraints.sessionDurations` — not used for COURSE
|
|
162
|
+
- `defaultCapacity` is **required** — sets max participants for the entire course
|
|
163
|
+
- Customers must book the **entire course** (all sessions), not individual sessions
|
|
164
|
+
- After creation, course sessions must be scheduled separately via `bulkCreateEvents` using the returned `service.schedule.id` (see [Create and Update Booking Services](./create-and-update-booking-services.md))
|
|
165
|
+
|
|
166
|
+
Save the `serviceId` from the response: `results[0].item.service.id`
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Step 4: Summary Message
|
|
171
|
+
|
|
172
|
+
Provide a summary including:
|
|
173
|
+
|
|
174
|
+
1. **What was created** — service name, total course price, capacity
|
|
175
|
+
2. **Assumptions made** — list defaults used (e.g., "I set the capacity to 10 participants since you didn't specify")
|
|
176
|
+
3. **Pricing clarification** — note that the price is for the entire course, not per session
|
|
177
|
+
4. **Schedule note** — remind the user that course sessions (dates and times) still need to be set up
|
|
178
|
+
5. **Next steps** — "Click Save to finalize, then set up the course schedule"
|
|
179
|
+
6. **Offer to adjust** — "Want me to change the price, capacity, or description?"
|
|
180
|
+
|
|
181
|
+
**Example:**
|
|
182
|
+
|
|
183
|
+
> I created **"Yoga Teacher Training"**:
|
|
184
|
+
>
|
|
185
|
+
> - **Type**: Course (customers book the full program)
|
|
186
|
+
> - **Price**: $300 for the full course
|
|
187
|
+
> - **Capacity**: 10 participants
|
|
188
|
+
> - **Category**: Training
|
|
189
|
+
>
|
|
190
|
+
> I assumed a capacity of 10 since you didn't specify. The price of $300 covers the entire course — customers pay once for all sessions. You can review and adjust the details in the service form.
|
|
191
|
+
>
|
|
192
|
+
> **Next step:** You'll need to set up the course schedule (specific session dates and times) in the service form.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Error Handling
|
|
197
|
+
|
|
198
|
+
| Error | Cause | Action |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| 428 "App not installed" | Bookings not installed | Install using [Install Wix Apps](../app-installation/install-wix-apps.md) |
|
|
201
|
+
| 400 "INVALID_PAYMENT_OPTIONS" | Payment misconfigured | Free: `inPerson: true`, `online: false`. Paid: price > 0 |
|
|
202
|
+
| 403 | Permission denied | Inform user they lack permission |
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Payment Validation Quick Reference
|
|
207
|
+
|
|
208
|
+
| rateType | `options.online` | `options.inPerson` | Valid? |
|
|
209
|
+
|----------|------------------|-------------------|--------|
|
|
210
|
+
| FIXED | true | false | ✓ |
|
|
211
|
+
| FIXED | false | true | ✓ |
|
|
212
|
+
| FIXED | true | true | ✓ |
|
|
213
|
+
| NO_FEE | false | true | ✓ |
|
|
214
|
+
| NO_FEE | true | false | ✗ |
|
|
215
|
+
| Any | false | false | ✗ |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## What This Skill Does NOT Cover
|
|
220
|
+
|
|
221
|
+
- **Course schedule setup** — individual session events (dates/times) must be configured separately via Calendar Events API. See [Create and Update Booking Services](./create-and-update-booking-services.md) Step 3.
|
|
222
|
+
- **Pricing plans** — memberships or installment payments are separate. See [Create and Update Pricing Plans](../pricing-plans/create-and-update-pricing-plans.md)
|
|
223
|
+
- **Instructor assignment** — `staffMemberIds` is ignored for COURSE. Staff association is managed through calendar events.
|
|
224
|
+
- **Service images** — requires Media Manager API (see [create-and-update-booking-services.md](./create-and-update-booking-services.md))
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## API Documentation Reference
|
|
229
|
+
|
|
230
|
+
- [Bulk Create Services](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/bulk-create-services)
|
|
231
|
+
- [Query Services](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/query-services)
|
|
232
|
+
- [Query Categories](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/categories-v2/query-categories)
|
|
233
|
+
- [Create Category](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/categories-v2/create-category)
|
|
234
|
+
- [Bulk Create Events](https://dev.wix.com/docs/api-reference/business-management/calendar/events-v3/bulk-create-event)
|
|
235
|
+
- [Site Properties](https://dev.wix.com/docs/api-reference/business-management/site-management/site-properties/properties/read)
|
|
236
|
+
- [About Service Types](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/about-service-types)
|
|
237
|
+
- [About Service Payments](https://dev.wix.com/docs/api-reference/business-solutions/bookings/services/services-v2/about-service-payments)
|
|
@@ -10,7 +10,7 @@ This recipe covers basic Create, Read, Update, Delete (CRUD) operations for Wix
|
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
|
-
1. Wix CMS enabled on the site
|
|
13
|
+
1. Wix CMS enabled on the site (appDefId: `e593b0bd-b783-45b8-97c2-873d42aacaf4`)
|
|
14
14
|
2. Collections already created (see [CMS Schema Management](cms-schema-management.md))
|
|
15
15
|
3. API access with CMS permissions
|
|
16
16
|
|
|
@@ -439,6 +439,7 @@ curl -X DELETE \
|
|
|
439
439
|
| `WDE0007` | Bulk update: wrong ID field name | Use `id` not `_id` at element level |
|
|
440
440
|
| `WDE0080` | Validation failed (multiple causes) | Bulk update: don't include `_id` in `data`; Bulk patch: use `patches` array not `dataItems` |
|
|
441
441
|
| `WDE0303` | Can't set multi-reference field via data operations | Use reference endpoints: `insert-references`, `replace-references` |
|
|
442
|
+
| `WDE0110` | Wix CMS (Wix Data) application is not installed | Install application with appDefId: `e593b0bd-b783-45b8-97c2-873d42aacaf4` |
|
|
442
443
|
|
|
443
444
|
---
|
|
444
445
|
|
|
@@ -10,7 +10,7 @@ This recipe covers managing the structure (schema) of Wix CMS collections using
|
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
13
|
-
1. Wix CMS
|
|
13
|
+
1. Wix CMS application installed on the site (appDefId: `e593b0bd-b783-45b8-97c2-873d42aacaf4`)
|
|
14
14
|
2. API access with CMS permissions (Manage Data Collections scope)
|
|
15
15
|
|
|
16
16
|
## Required APIs
|
|
@@ -183,6 +183,30 @@ Examine the images for visible product attributes that should become variant opt
|
|
|
183
183
|
- If multiple images show the **same product from different angles**, treat them as additional product media — NOT as separate variants.
|
|
184
184
|
- If only one image is provided and a distinctive color is visible, suggest that single color as a choice and ask the user if the product comes in other colors.
|
|
185
185
|
|
|
186
|
+
### 4h. Subscription Eligibility Detection (silent — no user interaction)
|
|
187
|
+
|
|
188
|
+
Based on the product name, description, and image analysis, determine whether this product is **subscription-eligible**. A product qualifies if it is a consumable that gets depleted at a predictable rate — something customers buy repeatedly on a regular schedule.
|
|
189
|
+
|
|
190
|
+
**Eligible categories (examples):**
|
|
191
|
+
- Food & beverage: coffee beans/pods, tea, protein powder, meal kits
|
|
192
|
+
- Supplements & health: vitamins, probiotics, protein supplements
|
|
193
|
+
- Pet: pet food, flea/tick treatments, cat litter
|
|
194
|
+
- Personal care: skincare routine products, razor blades/cartridges, deodorant, shampoo/conditioner
|
|
195
|
+
- Household: cleaning supplies, paper towels, toilet paper, laundry detergent
|
|
196
|
+
- Baby: diapers, formula, wipes
|
|
197
|
+
- Media: print/digital newspapers, magazines
|
|
198
|
+
- Candles (daily-use scented candles with high burn frequency)
|
|
199
|
+
|
|
200
|
+
**NOT eligible (do not suggest):**
|
|
201
|
+
- Furniture, art, décor (one-time purchase)
|
|
202
|
+
- Electronics, tools, appliances
|
|
203
|
+
- Standard clothing, apparel, shoes (not subscription-typical)
|
|
204
|
+
- Jewelry, watches
|
|
205
|
+
- Books (single title), games, toys
|
|
206
|
+
- Any unique or collectible item
|
|
207
|
+
|
|
208
|
+
Store the result internally as `subscription_eligible: true/false`. If `true`, also note a brief reason (e.g., `"coffee beans — daily consumable"`). No user interaction at this step.
|
|
209
|
+
|
|
186
210
|
---
|
|
187
211
|
|
|
188
212
|
## V3 STEP 4: Present Review Card to User (INTERACTIVE)
|
|
@@ -207,6 +231,7 @@ Present a structured review card using the markdown format below. This provides
|
|
|
207
231
|
| Images | [count] uploaded |
|
|
208
232
|
| SEO | [first 60 chars of meta description]… |
|
|
209
233
|
| Tone | [Matched from existing site products / Neutral e-commerce] |
|
|
234
|
+
| Subscription | [e.g. Monthly · 10% off] or [Not applicable] |
|
|
210
235
|
|
|
211
236
|
**Info Sections**
|
|
212
237
|
- **[Title 1]:** [one-line summary]
|
|
@@ -221,7 +246,7 @@ After the card, present the action options:
|
|
|
221
246
|
|
|
222
247
|
> 1. **Refine** — tell me what to change (e.g., "make the description shorter", "the price should be lower")
|
|
223
248
|
> 2. **Regenerate** — I'll start the analysis over, optionally with additional context from you
|
|
224
|
-
> 3. **Approve** —
|
|
249
|
+
> 3. **Approve** — lock in these details and continue to the next steps
|
|
225
250
|
|
|
226
251
|
If the user provided a text note that **contradicts** what's visible in the image (e.g., image shows blue but note says "available in red"), ask the user to clarify before proceeding.
|
|
227
252
|
|
|
@@ -229,6 +254,68 @@ If the user provided a text note that **contradicts** what's visible in the imag
|
|
|
229
254
|
|
|
230
255
|
---
|
|
231
256
|
|
|
257
|
+
## V3 STEP 4.5: Suggest Subscription (INTERACTIVE, conditional)
|
|
258
|
+
|
|
259
|
+
> **Run this step ONLY if `subscription_eligible: true` was set in V3 STEP 3 section 4h.** Otherwise skip directly to V3 STEP 5.
|
|
260
|
+
|
|
261
|
+
Present a short, non-pushy suggestion card:
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
**This product looks like a great candidate for a subscription plan.**
|
|
266
|
+
Customers who subscribe to *[product name]* get auto-refills on a schedule they choose — boosting repeat revenue for you.
|
|
267
|
+
|
|
268
|
+
Would you like to add a subscription option?
|
|
269
|
+
- **Yes** — set it up now (takes ~30 seconds)
|
|
270
|
+
- **No** — create as a one-time purchase only
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
If the user says **No**, proceed directly to V3 STEP 5.
|
|
275
|
+
|
|
276
|
+
If the user says **Yes**, ask the following 4 questions (can be presented together as a single form):
|
|
277
|
+
|
|
278
|
+
**Q1 — Billing frequency**
|
|
279
|
+
> "How often should subscribers be charged and receive this product?"
|
|
280
|
+
|
|
281
|
+
| Choice | `frequency` | `interval` |
|
|
282
|
+
|--------|------------|-----------|
|
|
283
|
+
| Every week | `WEEK` | `1` |
|
|
284
|
+
| Every 2 weeks | `WEEK` | `2` |
|
|
285
|
+
| Every month *(Recommended)* | `MONTH` | `1` |
|
|
286
|
+
| Every 2 months | `MONTH` | `2` |
|
|
287
|
+
| Every 3 months | `MONTH` | `3` |
|
|
288
|
+
| Every 6 months | `MONTH` | `6` |
|
|
289
|
+
| Every year | `YEAR` | `1` |
|
|
290
|
+
|
|
291
|
+
**Q2 — Duration**
|
|
292
|
+
> "How long should the subscription run?"
|
|
293
|
+
|
|
294
|
+
- **Ongoing** — renews automatically until the customer cancels (omit `billingCycles`)
|
|
295
|
+
- **Fixed** — ends after N cycles (ask: "How many cycles?") → set `billingCycles: N`
|
|
296
|
+
|
|
297
|
+
**Q3 — Subscriber discount (optional incentive)**
|
|
298
|
+
> "Would you like to offer a discount to customers who subscribe?"
|
|
299
|
+
|
|
300
|
+
- No discount (omit `discount` block)
|
|
301
|
+
- **10% off** *(Recommended)* → `type: "PERCENTAGE", amount: 10`
|
|
302
|
+
- Custom percentage → ask for value → `type: "PERCENTAGE", amount: [value]`
|
|
303
|
+
- Fixed amount off → ask for value → `type: "AMOUNT", amount: [value]`
|
|
304
|
+
|
|
305
|
+
**Q4 — Allow one-time purchase alongside subscription**
|
|
306
|
+
> "Can customers also buy this product as a one-time purchase (without subscribing)?"
|
|
307
|
+
|
|
308
|
+
- **Yes — sell both ways** *(Recommended)* → `allowOneTimePurchases: true`
|
|
309
|
+
- No — subscription only → `allowOneTimePurchases: false`
|
|
310
|
+
|
|
311
|
+
After collecting the user's answers, show a brief confirmation line before moving on:
|
|
312
|
+
|
|
313
|
+
> Subscription plan configured: [e.g. Monthly · Ongoing · 10% off · One-time purchases also allowed]
|
|
314
|
+
|
|
315
|
+
Store the collected subscription data for use in V3 STEP 6.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
232
319
|
## V3 STEP 5: Suggest Options and Ask User (INTERACTIVE)
|
|
233
320
|
|
|
234
321
|
**Present the detected options from V3 STEP 4g and ask the user if they want to add product options.**
|
|
@@ -255,10 +342,74 @@ If no options were detected:
|
|
|
255
342
|
|
|
256
343
|
## V3 STEP 6: Create the Product
|
|
257
344
|
|
|
345
|
+
Before making the API call, ask the user one final time:
|
|
346
|
+
|
|
347
|
+
> Everything is ready. Shall I create the product now?
|
|
348
|
+
> - **Yes, create it** — I'll submit the product to your store now.
|
|
349
|
+
> - **No, go back** — return to a previous step to make changes.
|
|
350
|
+
|
|
351
|
+
**Wait for confirmation.** Only proceed with the API call if the user says Yes.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
258
355
|
**API Endpoint:** `POST https://www.wixapis.com/stores/v3/products`
|
|
259
356
|
|
|
260
357
|
Build the request body with all confirmed fields. The write must be **atomic** — either all fields save or none do.
|
|
261
358
|
|
|
359
|
+
**If a subscription was configured in V3 STEP 4.5**, include a `subscriptionDetails` block in the product body (see rules below). Otherwise omit it entirely.
|
|
360
|
+
|
|
361
|
+
**`subscriptionDetails` rules:**
|
|
362
|
+
- `title`: auto-generate from frequency (e.g. `"Monthly Subscription"`, `"Weekly Subscription"`, `"Quarterly Subscription"`)
|
|
363
|
+
- `description`: if a discount was set, write a short sentence referencing it (e.g. `"Subscribe and save 10% on every order."`). Omit if no discount.
|
|
364
|
+
- `visible`: always `true`
|
|
365
|
+
- `frequency` + `interval`: from user's Q1 answer
|
|
366
|
+
- `billingCycles`: include only for fixed-duration subscriptions (from Q2). Omit for ongoing.
|
|
367
|
+
- `discount`: include only if a discount was chosen (from Q3). Omit the entire block if no discount.
|
|
368
|
+
- `allowOneTimePurchases`: from user's Q4 answer
|
|
369
|
+
|
|
370
|
+
**Example `subscriptionDetails` block (monthly, ongoing, 10% off, one-time also allowed):**
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
"subscriptionDetails": {
|
|
374
|
+
"subscriptions": [
|
|
375
|
+
{
|
|
376
|
+
"title": "Monthly Subscription",
|
|
377
|
+
"description": "Subscribe and save 10% on every order.",
|
|
378
|
+
"visible": true,
|
|
379
|
+
"frequency": "MONTH",
|
|
380
|
+
"interval": 1,
|
|
381
|
+
"discount": {
|
|
382
|
+
"type": "PERCENTAGE",
|
|
383
|
+
"amount": 10
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
"allowOneTimePurchases": true
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Example with fixed duration and flat amount discount (every 2 months, 6 cycles, $5 off, subscription only):**
|
|
392
|
+
|
|
393
|
+
```json
|
|
394
|
+
"subscriptionDetails": {
|
|
395
|
+
"subscriptions": [
|
|
396
|
+
{
|
|
397
|
+
"title": "Bi-Monthly Subscription",
|
|
398
|
+
"description": "Subscribe and save $5 on every order.",
|
|
399
|
+
"visible": true,
|
|
400
|
+
"frequency": "MONTH",
|
|
401
|
+
"interval": 2,
|
|
402
|
+
"billingCycles": 6,
|
|
403
|
+
"discount": {
|
|
404
|
+
"type": "AMOUNT",
|
|
405
|
+
"amount": 5
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
"allowOneTimePurchases": false
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
262
413
|
---
|
|
263
414
|
|
|
264
415
|
### Path A: Simple Product (No Options)
|
|
@@ -650,8 +801,9 @@ Before reporting success to the user, verify ALL of the following:
|
|
|
650
801
|
- [ ] V3 STEP 2 completed: User provided 1-3 images, all uploaded to Media Manager with wixstatic.com URLs.
|
|
651
802
|
- [ ] V3 STEP 3 completed: Images analyzed from user's message, all product fields generated based on what's visible in the image(s).
|
|
652
803
|
- [ ] V3 STEP 4 completed: User reviewed and approved the generated details.
|
|
804
|
+
- [ ] V3 STEP 4.5 completed (if applicable): Subscription eligibility was evaluated. If eligible, user was asked and their choice (Yes/No) was applied before proceeding to STEP 5.
|
|
653
805
|
- [ ] V3 STEP 5 completed: User confirmed, modified, or skipped product options.
|
|
654
|
-
- [ ] V3 STEP 6 completed:
|
|
806
|
+
- [ ] V3 STEP 6 completed: User confirmed "create now", product was created via API and you received a product ID.
|
|
655
807
|
|
|
656
808
|
---
|
|
657
809
|
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { createClient, OAuthStrategy } from "@wix/sdk";
|
|
3
|
-
import { bookings } from "@wix/bookings";
|
|
4
|
-
import { WIX_CLIENT_ID } from "astro:env/client";
|
|
5
|
-
|
|
6
|
-
// ManageBooking.tsx — client:only="react" island for /manage-booking.
|
|
7
|
-
// The server (manage-booking.astro) already minted an anonymous action token
|
|
8
|
-
// (app-elevated) and passed it in. The cancel call is a NO-AUTH anonymous method,
|
|
9
|
-
// so the visitor client runs it directly with the token. Reschedule-in-place
|
|
10
|
-
// needs a verified class V2Slot shape, so we offer cancel-and-rebook instead.
|
|
11
|
-
|
|
12
|
-
interface Props {
|
|
13
|
-
token: string | null;
|
|
14
|
-
revision: string;
|
|
15
|
-
serviceName: string;
|
|
16
|
-
startDate: string | null;
|
|
17
|
-
status: string | null;
|
|
18
|
-
canCancel: boolean;
|
|
19
|
-
showSummary?: boolean; // false when embedded under a page that already shows the service/date
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const wixClient = createClient({
|
|
23
|
-
modules: { bookings },
|
|
24
|
-
auth: OAuthStrategy({ clientId: WIX_CLIENT_ID }),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const fmt = (iso: string | null) => {
|
|
28
|
-
if (!iso) return null;
|
|
29
|
-
const d = new Date(iso);
|
|
30
|
-
return Number.isNaN(d.getTime())
|
|
31
|
-
? null
|
|
32
|
-
: d.toLocaleString([], { weekday: "long", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export default function ManageBooking({ token, revision, serviceName, startDate, status, canCancel, showSummary }: Props) {
|
|
36
|
-
const [state, setState] = useState<"idle" | "canceling" | "canceled" | "error">(
|
|
37
|
-
status === "CANCELED" ? "canceled" : "idle",
|
|
38
|
-
);
|
|
39
|
-
const when = fmt(startDate);
|
|
40
|
-
|
|
41
|
-
const handleCancel = async () => {
|
|
42
|
-
if (!token) return;
|
|
43
|
-
setState("canceling");
|
|
44
|
-
try {
|
|
45
|
-
await wixClient.bookings.bookingsCancelBookingAnonymously(token, revision);
|
|
46
|
-
setState("canceled");
|
|
47
|
-
} catch (err) {
|
|
48
|
-
console.error("[manage-booking] cancel failed:", err);
|
|
49
|
-
setState("error");
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
if (state === "canceled") {
|
|
54
|
-
return (
|
|
55
|
-
<div className="manage-booking">
|
|
56
|
-
<p className="manage-booking-status">
|
|
57
|
-
Your booking for <strong>{serviceName}</strong>{when ? <> on <strong>{when}</strong></> : null} is canceled.
|
|
58
|
-
</p>
|
|
59
|
-
<a href="/services" className="booking-submit" style={{ display: "inline-block" }}>Book another service</a>
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className="manage-booking">
|
|
66
|
-
{showSummary !== false && (
|
|
67
|
-
<p className="manage-booking-summary">
|
|
68
|
-
<strong>{serviceName}</strong>{when ? <> · {when}</> : null}
|
|
69
|
-
</p>
|
|
70
|
-
)}
|
|
71
|
-
{state === "error" && <p className="booking-error">Something went wrong canceling your booking. Please try again.</p>}
|
|
72
|
-
{canCancel ? (
|
|
73
|
-
<button type="button" className="booking-submit" onClick={handleCancel} disabled={state === "canceling"}>
|
|
74
|
-
{state === "canceling" ? "Canceling…" : "Cancel this booking"}
|
|
75
|
-
</button>
|
|
76
|
-
) : (
|
|
77
|
-
<p className="manage-booking-note">This booking can no longer be canceled online.</p>
|
|
78
|
-
)}
|
|
79
|
-
<p className="manage-booking-note">
|
|
80
|
-
Need a different time? Cancel above, then <a href="/services" className="manage-booking-link">book another time</a>.
|
|
81
|
-
</p>
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { APIRoute } from "astro";
|
|
2
|
-
import { auth } from "@wix/essentials";
|
|
3
|
-
import { bookings } from "@wix/bookings";
|
|
4
|
-
|
|
5
|
-
// POST /api/confirm-booking { bookingId, revision }
|
|
6
|
-
//
|
|
7
|
-
// createBooking() leaves the booking CREATED — which does NOT occupy a seat, so
|
|
8
|
-
// the class/appointment stays bookable and OVERBOOKS (remainingCapacity never
|
|
9
|
-
// drops). confirmBooking() promotes it to CONFIRMED (holds the seat); with
|
|
10
|
-
// flowControlSettings.checkAvailabilityValidation it REJECTS the confirm if the
|
|
11
|
-
// session filled up since the slot was picked — the real overbooking guard.
|
|
12
|
-
// confirmBooking requires Manage Bookings, so elevate (app identity) here;
|
|
13
|
-
// confirmBooking is a real SDK method, so auth.elevate(...) works directly.
|
|
14
|
-
|
|
15
|
-
const json = (ok: boolean, status: number, extra: Record<string, unknown> = {}) =>
|
|
16
|
-
new Response(JSON.stringify({ ok, ...extra }), { status, headers: { "Content-Type": "application/json" } });
|
|
17
|
-
|
|
18
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
19
|
-
let body: any;
|
|
20
|
-
try { body = await request.json(); } catch { return json(false, 400, { error: "Invalid request body." }); }
|
|
21
|
-
const { bookingId, revision } = body ?? {};
|
|
22
|
-
if (!bookingId || !revision) return json(false, 400, { error: "Missing bookingId or revision." });
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const res = await auth.elevate(bookings.confirmBooking)(bookingId, String(revision), {
|
|
26
|
-
paymentStatus: "NOT_PAID", // pay-on-site reservation
|
|
27
|
-
flowControlSettings: { checkAvailabilityValidation: true },
|
|
28
|
-
});
|
|
29
|
-
return json(true, 200, { status: res.booking?.status });
|
|
30
|
-
} catch (err) {
|
|
31
|
-
console.error("[api/confirm-booking] failed:", err);
|
|
32
|
-
return json(false, 409, { error: "unavailable" }); // most likely: session just filled up
|
|
33
|
-
}
|
|
34
|
-
};
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { APIRoute } from "astro";
|
|
2
|
-
import { auth, httpClient } from "@wix/essentials";
|
|
3
|
-
|
|
4
|
-
// POST /api/waitlist { sessionId, totalParticipants, contactDetails:{firstName,lastName,email,phone} }
|
|
5
|
-
//
|
|
6
|
-
// Native Wix Bookings waitlist. There is NO v2/v3 waitlist API — createBooking
|
|
7
|
-
// can't waitlist; the only join is the legacy v1 Register To Waitlist
|
|
8
|
-
// (POST /bookings/v1/waitlist/register), which needs Manage Bookings + a Wix
|
|
9
|
-
// contactId. So this runs server-side, elevated to app identity. CLASS only.
|
|
10
|
-
//
|
|
11
|
-
// `sessionId` is the v1-encoded session id (the `waitingResource`); the browser
|
|
12
|
-
// gets it from the SESSION_CAPACITY_EXCEEDED error createBooking returns for a
|
|
13
|
-
// full session ("…on session <id>").
|
|
14
|
-
//
|
|
15
|
-
// IMPORTANT: auth.elevate() only grants permissions for a real SDK function. The
|
|
16
|
-
// waitlist + Contacts calls are raw REST (no SDK wrapper), so elevate the
|
|
17
|
-
// authenticated fetch ITSELF — NOT a closure that calls it (a closure runs
|
|
18
|
-
// un-elevated/visitor and 403s).
|
|
19
|
-
|
|
20
|
-
const json = (ok: boolean, status: number, extra: Record<string, unknown> = {}) =>
|
|
21
|
-
new Response(JSON.stringify({ ok, ...extra }), { status, headers: { "Content-Type": "application/json" } });
|
|
22
|
-
|
|
23
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
24
|
-
let body: any;
|
|
25
|
-
try { body = await request.json(); } catch { return json(false, 400, { error: "Invalid request body." }); }
|
|
26
|
-
const { sessionId, totalParticipants = 1, contactDetails } = body ?? {};
|
|
27
|
-
const email: string | undefined = contactDetails?.email;
|
|
28
|
-
if (!sessionId || !email) return json(false, 400, { error: "Missing sessionId or email." });
|
|
29
|
-
|
|
30
|
-
const ef = auth.elevate(httpClient.fetchWithAuth as typeof fetch) as typeof fetch;
|
|
31
|
-
const api = (url: string, payload: unknown) =>
|
|
32
|
-
ef(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) });
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// 1. Resolve a contactId (reuse by email; create if absent).
|
|
36
|
-
let contactId: string | undefined;
|
|
37
|
-
const q = await api("https://www.wixapis.com/contacts/v4/contacts/query", {
|
|
38
|
-
query: { filter: { "info.emails.email": email } },
|
|
39
|
-
});
|
|
40
|
-
contactId = ((await q.json().catch(() => ({}))) as any).contacts?.[0]?.id;
|
|
41
|
-
if (!contactId) {
|
|
42
|
-
const c = await api("https://www.wixapis.com/contacts/v4/contacts", {
|
|
43
|
-
info: {
|
|
44
|
-
name: { first: contactDetails.firstName, last: contactDetails.lastName || undefined },
|
|
45
|
-
emails: { items: [{ email, primary: true }] },
|
|
46
|
-
...(contactDetails.phone ? { phones: { items: [{ phone: contactDetails.phone, primary: true }] } } : {}),
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
contactId = ((await c.json().catch(() => ({}))) as any).contact?.id;
|
|
50
|
-
}
|
|
51
|
-
if (!contactId) throw new Error("could not resolve contactId");
|
|
52
|
-
|
|
53
|
-
// 2. Register on the native waitlist.
|
|
54
|
-
const reg = await api("https://www.wixapis.com/bookings/v1/waitlist/register", {
|
|
55
|
-
waitingResource: sessionId,
|
|
56
|
-
formInfo: {
|
|
57
|
-
paymentSelection: [{ rateLabel: "general", numberOfParticipants: totalParticipants }],
|
|
58
|
-
contactDetails: {
|
|
59
|
-
contactId,
|
|
60
|
-
firstName: contactDetails.firstName,
|
|
61
|
-
lastName: contactDetails.lastName || undefined,
|
|
62
|
-
email,
|
|
63
|
-
phone: contactDetails.phone || undefined,
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
const rj: any = await reg.json().catch(() => ({}));
|
|
68
|
-
if (!reg.ok || !rj.registrationId) throw new Error(`register ${reg.status}: ${JSON.stringify(rj).slice(0, 200)}`);
|
|
69
|
-
return json(true, 200, { registrationId: rj.registrationId });
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.error("[api/waitlist] failed:", err);
|
|
72
|
-
return json(false, 502, { error: "Waitlist registration failed." });
|
|
73
|
-
}
|
|
74
|
-
};
|