@wix/agent-skills 1.19.2 → 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/SERVICE_PLUGIN.md +14 -1
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +7 -6
- 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-headless/references/CAPABILITIES.md +2 -2
- package/skills/wix-headless/references/inline-recipes/experience-store.md +4 -3
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +98 -18
- 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 +4 -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/seo/manage-seo-suggestions.md +351 -0
- 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 +43 -2
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +7 -1
|
@@ -10,14 +10,24 @@ export interface ProductSummary {
|
|
|
10
10
|
id: string;
|
|
11
11
|
slug: string;
|
|
12
12
|
name: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The price the buyer pays for the cheapest variant, formatted (an automatic discount already
|
|
15
|
+
* applied). When `price !== maxPrice` the product is a RANGE — render "price – maxPrice".
|
|
16
|
+
*/
|
|
14
17
|
price: string;
|
|
15
|
-
/** Highest price, formatted — differs from `price` when variants are priced differently. */
|
|
18
|
+
/** Highest variant price, formatted — differs from `price` when variants are priced differently. */
|
|
16
19
|
maxPrice: string;
|
|
17
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Struck "was" price, formatted; null when not on sale — and ALWAYS null for a range (a lone
|
|
22
|
+
* struck minimum beside a range claims a saving that may not apply to the variant picked).
|
|
23
|
+
*/
|
|
18
24
|
compareAtPrice: string | null;
|
|
19
|
-
/**
|
|
25
|
+
/** The primary merchant ribbon ("New", "Best Seller"); null when none. Same as ribbons[0]. */
|
|
20
26
|
ribbon: string | null;
|
|
27
|
+
/** EVERY merchant ribbon, primary first ("New", "Sale") — render all of them, one shared style. */
|
|
28
|
+
ribbons: string[];
|
|
29
|
+
/** The cheapest variant's id — what a direct add sends for a product with no options; null when unknown. */
|
|
30
|
+
minPriceVariantId: string | null;
|
|
21
31
|
availability: Availability;
|
|
22
32
|
/** OUT_OF_STOCK but pre-orderable — label "Pre-order", not "Sold out". */
|
|
23
33
|
preorder: boolean;
|
|
@@ -27,6 +37,11 @@ export interface ProductSummary {
|
|
|
27
37
|
hoverImageUrl: string;
|
|
28
38
|
/** e.g. "2 colors · 3 sizes"; "" for a single-variant product. */
|
|
29
39
|
optionsSummary: string;
|
|
40
|
+
/**
|
|
41
|
+
* Hex colors of a color option's visible choices, catalog order — render as small dots on the
|
|
42
|
+
* tile (a preview, not a picker: selection happens in QuickAdd or on the PDP). [] when none.
|
|
43
|
+
*/
|
|
44
|
+
swatches: string[];
|
|
30
45
|
/** True when the product can be added to the cart with no choices (single variant, in stock). */
|
|
31
46
|
quickAddable: boolean;
|
|
32
47
|
}
|
|
@@ -60,15 +75,21 @@ export interface ProductVariant {
|
|
|
60
75
|
variantId: string;
|
|
61
76
|
/** The option selections this variant answers to: optionName -> choiceName. */
|
|
62
77
|
choices: Record<string, string>;
|
|
78
|
+
/** The price the buyer pays, formatted — an automatic discount beats the regular price. */
|
|
63
79
|
price: string;
|
|
80
|
+
/** Struck "was" price, formatted; null unless it is real and higher than `price`. */
|
|
64
81
|
compareAtPrice: string | null;
|
|
65
82
|
inStock: boolean;
|
|
83
|
+
/** Out of stock but pre-orderable — still buyable (the add carries preOrderRequested). */
|
|
84
|
+
preorderEnabled: boolean;
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
/** A product as the detail page needs it. */
|
|
69
88
|
export interface ProductDetail extends ProductSummary {
|
|
70
89
|
/** Product description as an HTML string — render with innerHTML, not as text. */
|
|
71
90
|
descriptionHtml: string;
|
|
91
|
+
/** Merchant info sections (materials, shipping, care…) — title + HTML; render as sections or accordions. */
|
|
92
|
+
infoSections: { title: string; html: string }[];
|
|
72
93
|
/** Every gallery image as a resolved https URL, main image first, de-duplicated. */
|
|
73
94
|
gallery: string[];
|
|
74
95
|
options: ProductOption[];
|
|
@@ -80,6 +101,36 @@ export interface Category {
|
|
|
80
101
|
id: string;
|
|
81
102
|
slug: string;
|
|
82
103
|
name: string;
|
|
104
|
+
/** Plain-text description when the merchant wrote one; "" otherwise. */
|
|
105
|
+
description: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface FacetChoice {
|
|
109
|
+
/** The option choice id — what searchCatalog({ choiceIds }) filters on. */
|
|
110
|
+
id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
/** Hex color for a swatch facet; null for a text facet. */
|
|
113
|
+
colorCode: string | null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** One filterable option across the catalog (or a category): "Color" with its choices. */
|
|
117
|
+
export interface Facet {
|
|
118
|
+
name: string;
|
|
119
|
+
isColor: boolean;
|
|
120
|
+
choices: FacetChoice[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The catalog's (or category's) lowest and highest product price, as numbers in site currency — the slider's bounds. */
|
|
124
|
+
export interface PriceRange {
|
|
125
|
+
min: number;
|
|
126
|
+
max: number;
|
|
127
|
+
currency: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** What the filter panel needs beyond the product page: the facets and the price bounds of the scope. */
|
|
131
|
+
export interface FacetData {
|
|
132
|
+
facets: Facet[];
|
|
133
|
+
priceRange: PriceRange | null;
|
|
83
134
|
}
|
|
84
135
|
|
|
85
136
|
export interface CartLine {
|
|
@@ -97,6 +148,11 @@ export interface CartLine {
|
|
|
97
148
|
descriptionLines: string[];
|
|
98
149
|
/** Not IN_STOCK → the line can't be checked out as-is. */
|
|
99
150
|
status: string;
|
|
151
|
+
/**
|
|
152
|
+
* The recurring plan's terms for a subscription line — "Monthly plan · every month · 12 payments";
|
|
153
|
+
* "" for a one-time purchase. A subscription line must read as one in the cart.
|
|
154
|
+
*/
|
|
155
|
+
subscription: string;
|
|
100
156
|
}
|
|
101
157
|
|
|
102
158
|
export interface Cart {
|
|
@@ -105,5 +161,7 @@ export interface Cart {
|
|
|
105
161
|
itemCount: number;
|
|
106
162
|
/** Formatted subtotal (after discounts) — from the cart estimate, not hand-summed. */
|
|
107
163
|
subtotal: string;
|
|
164
|
+
/** Formatted CART-level discount (a coupon or cart rule) — "" when none; item discounts are already in subtotal. */
|
|
165
|
+
discount: string;
|
|
108
166
|
currency: string;
|
|
109
167
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
// The site chrome for every storefront page — THIS file (header/footer/nav) plus the @theme
|
|
3
3
|
// token block in src/styles/global.css are what you brand. Keep:
|
|
4
|
-
// - the <slot name="seo-tags" /> in <head> (the product page's owner-editable SEO lands there
|
|
4
|
+
// - the <slot name="seo-tags" /> in <head> (the product page's owner-editable SEO lands there,
|
|
5
|
+
// including the <title> — which is why ours is printed only when the slot is empty)
|
|
5
6
|
// - the global.css import (Tailwind + the design tokens every shipped component reads)
|
|
6
7
|
// - one <CartButton> in the header and one <CartDrawer> per page (both client:only —
|
|
7
8
|
// they read browser cart state and must not server-render)
|
|
9
|
+
// - category links in the nav (or your own menu/footer) — every category page reachable by
|
|
10
|
+
// a real link (`/category/<slug>`), from the live tree, so a category the owner adds later
|
|
11
|
+
// shows up with no code change
|
|
8
12
|
import CartButton from "../components/storefront/CartButton";
|
|
9
13
|
import CartDrawer from "../components/storefront/CartDrawer";
|
|
14
|
+
import { fetchCategories } from "../wix/storefront/catalog";
|
|
15
|
+
import type { Category } from "../wix/storefront/types";
|
|
10
16
|
import "../styles/global.css";
|
|
11
17
|
|
|
12
18
|
interface Props {
|
|
@@ -14,6 +20,15 @@ interface Props {
|
|
|
14
20
|
description?: string;
|
|
15
21
|
}
|
|
16
22
|
const { title, description } = Astro.props;
|
|
23
|
+
|
|
24
|
+
// Non-fatal: the chrome renders without the links if the read fails. Six in the header keeps a
|
|
25
|
+
// large tree from crowding it — put the rest in a menu or the footer, but keep them linked.
|
|
26
|
+
let navCategories: Category[] = [];
|
|
27
|
+
try {
|
|
28
|
+
navCategories = (await fetchCategories()).slice(0, 6);
|
|
29
|
+
} catch {
|
|
30
|
+
/* the shop link still reaches everything */
|
|
31
|
+
}
|
|
17
32
|
---
|
|
18
33
|
|
|
19
34
|
<!doctype html>
|
|
@@ -21,7 +36,8 @@ const { title, description } = Astro.props;
|
|
|
21
36
|
<head>
|
|
22
37
|
<meta charset="UTF-8" />
|
|
23
38
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
24
|
-
<
|
|
39
|
+
{/* The product page's <SEO.Tags> renders the <title>; print ours only when no page filled the slot — two titles otherwise. */}
|
|
40
|
+
{!Astro.slots.has("seo-tags") && <title>{title}</title>}
|
|
25
41
|
{description && <meta name="description" content={description} />}
|
|
26
42
|
<slot name="seo-tags" />
|
|
27
43
|
</head>
|
|
@@ -31,6 +47,9 @@ const { title, description } = Astro.props;
|
|
|
31
47
|
<a href="/" class="text-base font-bold tracking-tight text-foreground no-underline">Brand Name</a>
|
|
32
48
|
<nav class="flex items-center gap-6 text-sm">
|
|
33
49
|
<a href="/shop" class="text-foreground no-underline transition-colors hover:text-muted-foreground">Shop</a>
|
|
50
|
+
{navCategories.map((c) => (
|
|
51
|
+
<a href={`/category/${c.slug}`} class="hidden text-foreground no-underline transition-colors hover:text-muted-foreground md:inline">{c.name}</a>
|
|
52
|
+
))}
|
|
34
53
|
<CartButton client:only="react" />
|
|
35
54
|
</nav>
|
|
36
55
|
</div>
|
|
@@ -75,7 +75,11 @@ the shipped UI**: give at least one product a color option and put one product o
|
|
|
75
75
|
truthfully to the business (a ceramics studio has glaze colors; a bakery doesn't).
|
|
76
76
|
|
|
77
77
|
**Seeding is additive — never delete or overwrite existing content.** No cleanup, no removing
|
|
78
|
-
"sample" data, no resets. If a cleanup genuinely seems needed, ask the user first.
|
|
78
|
+
"sample" data, no resets. If a cleanup genuinely seems needed, ask the user first. The one
|
|
79
|
+
exception is built in: the fast path passes `--fresh-site` because it created the site seconds
|
|
80
|
+
earlier, and the seed then deletes the Stores install's sample products before creating yours
|
|
81
|
+
(`samplesDeleted` in the result). Never pass that flag by hand against a site that existed before.
|
|
82
|
+
Categories are idempotent by name too — a re-run reuses "Donuts" instead of creating a second one.
|
|
79
83
|
|
|
80
84
|
**A bulk create can partially succeed.** The result carries `failures: [{ name, error }]` next
|
|
81
85
|
to `products` — read it. A non-empty `failures` means those products are genuinely absent, not
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Storefront seed — a BUILD-TIME script, never shipped in the app. Run it from the project
|
|
2
2
|
// root (where wix.config.json lives) with a plan file:
|
|
3
3
|
//
|
|
4
|
-
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json
|
|
4
|
+
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json [--fresh-site]
|
|
5
5
|
//
|
|
6
6
|
// It mints its own site token via the Wix CLI (the token never leaves this process), installs
|
|
7
7
|
// the Wix Stores app if needed, waits for the V3 catalog, bulk-creates products (variants
|
|
@@ -331,11 +331,34 @@ async function stockOptionlessProducts(ctx, created) {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
// Existing categories by name (for idempotent reruns) — a re-run of the seed must reuse
|
|
335
|
+
// "Donuts", not create a second one. Empty map on any failure (falls back to create).
|
|
336
|
+
export async function queryCategoriesByNames(ctx, names) {
|
|
337
|
+
const out = new Map();
|
|
338
|
+
if (!names.length) return out;
|
|
339
|
+
try {
|
|
340
|
+
const r = await req(ctx, "/categories/v1/categories/query", {
|
|
341
|
+
body: { treeReference: { appNamespace: "@wix/stores", treeKey: null }, query: { cursorPaging: { limit: 100 } } },
|
|
342
|
+
});
|
|
343
|
+
const wanted = new Set(names);
|
|
344
|
+
for (const c of r.categories ?? []) if (wanted.has(c.name) && !out.has(c.name)) out.set(c.name, c.id);
|
|
345
|
+
} catch (e) {
|
|
346
|
+
console.error(`category name pre-check failed (creating everything): ${String(e.message).slice(0, 120)}`);
|
|
347
|
+
}
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
|
|
334
351
|
// Categories share the @wix/stores tree revision — concurrent creates 409, so: sequential.
|
|
352
|
+
// Idempotent by name: a name that already exists is reused, never duplicated.
|
|
335
353
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/create-category.md
|
|
336
354
|
export async function createCategories(ctx, names) {
|
|
355
|
+
const existing = await queryCategoriesByNames(ctx, names);
|
|
337
356
|
const out = [];
|
|
338
357
|
for (const name of names) {
|
|
358
|
+
if (existing.has(name)) {
|
|
359
|
+
out.push({ id: existing.get(name), name });
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
339
362
|
const r = await req(ctx, "/categories/v1/categories", {
|
|
340
363
|
body: { category: { name, visible: true }, treeReference: { appNamespace: "@wix/stores", treeKey: null } },
|
|
341
364
|
});
|
|
@@ -344,6 +367,24 @@ export async function createCategories(ctx, names) {
|
|
|
344
367
|
return out;
|
|
345
368
|
}
|
|
346
369
|
|
|
370
|
+
// The Stores install drops sample products ("I'm a product") into every new catalog. On a site the
|
|
371
|
+
// fast path JUST created nothing else can exist, so they are deleted before seeding — otherwise the
|
|
372
|
+
// storefront ships with the samples between the real products. Only called with --fresh-site; a
|
|
373
|
+
// seed against an existing site never deletes anything (SEED.md: seeding is additive).
|
|
374
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-delete-products.md
|
|
375
|
+
export async function deleteAllProducts(ctx) {
|
|
376
|
+
let deleted = 0;
|
|
377
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
378
|
+
const r = await req(ctx, "/stores/v3/products/query", { body: { query: { cursorPaging: { limit: 100 } } } });
|
|
379
|
+
const ids = (r.products ?? []).map((p) => p.id).filter(Boolean);
|
|
380
|
+
if (!ids.length) break;
|
|
381
|
+
await req(ctx, "/stores/v3/bulk/products/delete", { body: { productIds: ids } });
|
|
382
|
+
deleted += ids.length;
|
|
383
|
+
if (ids.length < 100) break;
|
|
384
|
+
}
|
|
385
|
+
return deleted;
|
|
386
|
+
}
|
|
387
|
+
|
|
347
388
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-add-items-to-category.md
|
|
348
389
|
export async function addProductsToCategories(ctx, mapping) {
|
|
349
390
|
for (const [categoryId, productIds] of Object.entries(mapping)) {
|
|
@@ -421,12 +462,22 @@ async function setSiteCurrency(ctx, currency) {
|
|
|
421
462
|
* threaded in memory. This is the default path — call it once instead of the individual
|
|
422
463
|
* functions.
|
|
423
464
|
*/
|
|
424
|
-
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}) {
|
|
465
|
+
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}, { freshSite = false } = {}) {
|
|
425
466
|
validateProducts(products);
|
|
426
467
|
await installStoresApp(ctx);
|
|
427
468
|
// Before any product exists: a product's price is stored in the site currency at create time,
|
|
428
469
|
// so switching afterwards leaves the catalog priced in the old one.
|
|
429
470
|
if (currency) await setSiteCurrency(ctx, currency);
|
|
471
|
+
// A just-created site holds only the install's sample products — clear them (never on an
|
|
472
|
+
// existing site: the flag is the fast path's, set only when it created the site itself).
|
|
473
|
+
let samplesDeleted = 0;
|
|
474
|
+
if (freshSite) {
|
|
475
|
+
try {
|
|
476
|
+
samplesDeleted = await deleteAllProducts(ctx);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
console.error(`sample cleanup failed (seeding on top): ${String(e.message).slice(0, 120)}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
430
481
|
|
|
431
482
|
// Idempotent by name: an errored bulk create (429/5xx) may still have applied server-side,
|
|
432
483
|
// and SKILL.md tells the agent to re-run a failed seed — creating only the names that don't
|
|
@@ -478,21 +529,23 @@ export async function setupStore(ctx, { products = [], categories = {}, currency
|
|
|
478
529
|
// failures is part of the result, not an exception: a partial seed still leaves a usable
|
|
479
530
|
// store, and the agent needs the names to report rather than silently shipping a short
|
|
480
531
|
// catalog. Re-run the seed to retry them — existing names are skipped, not duplicated.
|
|
481
|
-
return { products: withNames, categories: cats, imagesAttached, failures };
|
|
532
|
+
return { products: withNames, categories: cats, imagesAttached, samplesDeleted, failures };
|
|
482
533
|
}
|
|
483
534
|
|
|
484
535
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
485
536
|
|
|
486
537
|
const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
|
|
487
538
|
if (invokedDirectly) {
|
|
488
|
-
const
|
|
539
|
+
const args = process.argv.slice(2);
|
|
540
|
+
const freshSite = args.includes("--fresh-site");
|
|
541
|
+
const planPath = args.find((a) => !a.startsWith("--"));
|
|
489
542
|
if (!planPath) {
|
|
490
|
-
console.error("usage: node seed-store.mjs <plan.json> (run from the project root)");
|
|
543
|
+
console.error("usage: node seed-store.mjs <plan.json> [--fresh-site] (run from the project root)");
|
|
491
544
|
process.exit(1);
|
|
492
545
|
}
|
|
493
546
|
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
494
547
|
const ctx = makeCtx();
|
|
495
|
-
setupStore(ctx, plan)
|
|
548
|
+
setupStore(ctx, plan, { freshSite })
|
|
496
549
|
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
497
550
|
.catch((e) => {
|
|
498
551
|
console.error(e.message);
|
|
@@ -48,6 +48,9 @@ Generate an SEO content plan and read its blog post topics, or troubleshoot an e
|
|
|
48
48
|
### [Manage Google Search Console for a Wix Site](references/seo/manage-google-search-console.md)
|
|
49
49
|
"Connect a Wix site to Google Search Console and drive its setup through the public GSC Connection and Site Readiness APIs: check connection and readiness, start the Google authorization, verify ownership, add the property, submit the sitemap, request indexing, read search performance, run URL inspection, recover a stale connection, or disconnect. The site owner authorizes in their own browser via a single-use connect URL."
|
|
50
50
|
|
|
51
|
+
### [Generate AI SEO Suggestions for a Wix Site](references/seo/manage-seo-suggestions.md)
|
|
52
|
+
"Generate AI-written SEO text for a Wix site through the public Tag Suggestions and Page Optimization APIs: title tag and meta description options for a page, alt text for the images of a page, store product, or blog post (one image or up to 20 per call), corrected heading levels for a page's headings, and a whole-page rewrite of title, description, headings, and body text aimed at the page's focus keyword, returned as before/after pairs. Use it when the user wants to improve, optimize, audit, or write the SEO text of a page or the homepage. Suggestions are returned for review; applying one is a separate SEO tags write."
|
|
53
|
+
|
|
51
54
|
---
|
|
52
55
|
|
|
53
56
|
## Accessibility
|
|
@@ -251,7 +254,7 @@ Buy a domain through Wix or connect one the user already owns — intent, availa
|
|
|
251
254
|
"Creates an event with the Wix Events V3 API — the required request body, ISO-8601 date and time settings, venue/online/TBD location, RSVP vs ticketed registration, guest capacity, short vs rich-text descriptions, ticket tiers and pricing, and recurring series. Covers the exact field shapes and the API's misleading validation messages. Use when the user wants to create an event, set its date, location, description, guest limit or ticket prices, or set up a repeating event."
|
|
252
255
|
|
|
253
256
|
### [Manage Wix Events — Publishing, Cancelling, Cloning and Counting](references/events/manage-wix-events.md)
|
|
254
|
-
"Operates on events that already exist with the Wix Events V3 API — publishing a draft, cancelling, deleting, cloning, updating an event's date or details, and counting events. Use when the user wants to publish or cancel an event, duplicate one, move an event's date, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
257
|
+
"Operates on events that already exist with the Wix Events V3 API — finding an event by title, publishing a draft, cancelling, deleting (one or by filter), cloning, updating an event's date or details, and counting events. Use when the user wants to find, publish or cancel an event, duplicate one, move an event's date, delete events in bulk, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
255
258
|
|
|
256
259
|
---
|
|
257
260
|
|
|
@@ -56,6 +56,32 @@ curl -X POST 'https://www.wixapis.com/events/v3/events' \
|
|
|
56
56
|
`title`, `location`, `dateAndTimeSettings` and `registration.initialType` are required. The event
|
|
57
57
|
is created published (`status: "UPCOMING"`).
|
|
58
58
|
|
|
59
|
+
**What comes back** — the created event under `event`; the id you need for every follow-up call is
|
|
60
|
+
`event.id`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"event": {
|
|
65
|
+
"id": "<EVENT_ID>",
|
|
66
|
+
"title": "Summer Gala",
|
|
67
|
+
"slug": "summer-gala",
|
|
68
|
+
"status": "UPCOMING",
|
|
69
|
+
"location": { "name": "Grand Hall", "type": "VENUE", "locationTbd": false },
|
|
70
|
+
"dateAndTimeSettings": {
|
|
71
|
+
"startDate": "2026-09-15T19:00:00Z", "endDate": "2026-09-15T22:00:00Z",
|
|
72
|
+
"timeZoneId": "America/New_York", "recurrenceStatus": "ONE_TIME"
|
|
73
|
+
},
|
|
74
|
+
"createdDate": "...", "updatedDate": "..."
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Update, cancel, publish and clone return the same `{ "event": { ... } }`; delete returns
|
|
80
|
+
`{ "eventId": "..." }`. Some fields are returned only when the request's `fields` array asks for
|
|
81
|
+
them: `"DETAILS"` adds `shortDescription`, `"TEXTS"` adds `description`, `"REGISTRATION"` adds
|
|
82
|
+
`registration` (including `rsvp.limit`), `"URLS"` adds `eventPageUrl`. Without `fields`, read the
|
|
83
|
+
result from the top-level fields above and do not treat a missing `registration` as an error.
|
|
84
|
+
|
|
59
85
|
> **Do not add `"draft": true` unless the user asked for a draft.** Draft events require the
|
|
60
86
|
> `WIX_EVENTS.READ_DRAFT_EVENTS` permission, and without it every follow-up call fails `403` —
|
|
61
87
|
> adding ticket definitions, querying the event, fetching it by slug, even publishing it. The
|
|
@@ -154,6 +180,26 @@ curl -X POST 'https://www.wixapis.com/events/v3/ticket-definitions' \
|
|
|
154
180
|
| Free | `{ "fixedPrice": { "value": "0", "currency": "USD" } }` |
|
|
155
181
|
| Donation, with a minimum | `{ "guestPrice": { "value": "5.00", "currency": "USD" } }` |
|
|
156
182
|
|
|
183
|
+
The response is the definition under `ticketDefinition`:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"ticketDefinition": {
|
|
188
|
+
"id": "<TICKET_DEFINITION_ID>",
|
|
189
|
+
"eventId": "<EVENT_ID>",
|
|
190
|
+
"revision": "1",
|
|
191
|
+
"name": "General Admission",
|
|
192
|
+
"limited": true, "initialLimit": 100, "actualLimit": 100,
|
|
193
|
+
"pricingMethod": { "fixedPrice": { "value": "25.00", "currency": "USD" } },
|
|
194
|
+
"feeType": "FEE_ADDED_AT_CHECKOUT",
|
|
195
|
+
"saleStatus": "SALE_STARTED"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Keep `revision` — `PATCH /events/v3/ticket-definitions/{id}` requires the current value, and it
|
|
201
|
+
increments on every update.
|
|
202
|
+
|
|
157
203
|
- **`feeType` is required** — `FEE_ADDED_AT_CHECKOUT` or `FEE_INCLUDED`.
|
|
158
204
|
- **`value` is a string.** `"value": 10` fails `400 Unexpected value for field value`.
|
|
159
205
|
- **No writable `free` flag** — `pricingMethod.free` is read-only; a free ticket is a `fixedPrice`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Manage Wix Events — Publishing, Cancelling, Cloning and Counting"
|
|
3
|
-
description: "Operates on events that already exist with the Wix Events V3 API — publishing a draft, cancelling, deleting, cloning, updating an event's date or details, and counting events. Use when the user wants to publish or cancel an event, duplicate one, move an event's date, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
3
|
+
description: "Operates on events that already exist with the Wix Events V3 API — finding an event by title, publishing a draft, cancelling, deleting (one or by filter), cloning, updating an event's date or details, and counting events. Use when the user wants to find, publish or cancel an event, duplicate one, move an event's date, delete events in bulk, or count their events. Creating an event, its tickets or a recurring series is a separate recipe."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Manage Wix Events — Publishing, Cancelling, Cloning and Counting
|
|
@@ -28,6 +28,36 @@ curl -X POST 'https://www.wixapis.com/apps-installer-service/v1/app-instance/ins
|
|
|
28
28
|
`appDefId` nests under `appInstance`, not at the root — at the root it fails with
|
|
29
29
|
`400 appInstance must not be empty`. See [Install Wix Apps](../app-installation/install-wix-apps.md).
|
|
30
30
|
|
|
31
|
+
## Find an event
|
|
32
|
+
|
|
33
|
+
Every action below takes an `eventId`. When the user names the event instead, look it up with
|
|
34
|
+
`POST /events/v3/events/query` — `title` supports `$eq` and, for several names at once, `$in`; there is
|
|
35
|
+
no contains match:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
curl -X POST 'https://www.wixapis.com/events/v3/events/query' \
|
|
39
|
+
-H 'Content-Type: application/json' \
|
|
40
|
+
-H 'Authorization: <AUTH>' \
|
|
41
|
+
--data-binary '{
|
|
42
|
+
"query": {
|
|
43
|
+
"filter": { "title": { "$eq": "Open House" } },
|
|
44
|
+
"paging": { "limit": 100 }
|
|
45
|
+
},
|
|
46
|
+
"fields": ["DETAILS"],
|
|
47
|
+
"includeDrafts": false
|
|
48
|
+
}'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`filter` and `paging` are siblings under `query`; `paging` nested inside `filter` fails `400`. Matches
|
|
52
|
+
come back in `events[]` with the id at `events[].id`, and `pagingMetadata.total` is the count.
|
|
53
|
+
|
|
54
|
+
> **Keep `includeDrafts: false` unless the user is asking about draft events.** Setting it to `true`
|
|
55
|
+
> needs `WIX_EVENTS.READ_DRAFT_EVENTS`, and a caller without it gets
|
|
56
|
+
> `403 Requires WIX_EVENTS.READ_DRAFT_EVENTS permission` rather than more results — so do not flip
|
|
57
|
+
> it as a precaution on an ordinary lookup, and do not probe for it and fall back. If the user does
|
|
58
|
+
> want drafts and the call returns that `403`, say draft events are not readable from this
|
|
59
|
+
> connection instead of answering from published events alone.
|
|
60
|
+
|
|
31
61
|
## Publish, cancel and delete
|
|
32
62
|
|
|
33
63
|
| Action | Call | Resulting `status` |
|
|
@@ -35,10 +65,27 @@ curl -X POST 'https://www.wixapis.com/apps-installer-service/v1/app-instance/ins
|
|
|
35
65
|
| Publish a draft | `POST /events/v3/events/{eventId}/publish` | `UPCOMING` |
|
|
36
66
|
| Cancel | `POST /events/v3/events/{eventId}/cancel` | `CANCELED` |
|
|
37
67
|
| Delete | `DELETE /events/v3/events/{eventId}` | — |
|
|
68
|
+
| Delete several | `POST /events/v3/bulk/events/delete-by-filter` | — |
|
|
38
69
|
|
|
39
70
|
Publish and cancel take an empty body. Publishing is irreversible — a published event cannot
|
|
40
71
|
return to `DRAFT`. Cancelling closes registration but keeps the event; deleting removes it.
|
|
41
72
|
|
|
73
|
+
Publish, cancel, clone and update all return `{ "event": { "id", "title", "slug", "status", ... } }`
|
|
74
|
+
— the same event object [Create an Event](create-wix-event.md#create-the-event) shows in full.
|
|
75
|
+
Delete returns `{ "eventId": "..." }` and the bulk delete below returns `{}`.
|
|
76
|
+
|
|
77
|
+
To delete a set of events in one call, `POST /events/v3/bulk/events/delete-by-filter`:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{ "filter": { "filter": { "id": { "$in": ["<EVENT_ID>", "<EVENT_ID>"] } } } }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The request's one field, `filter`, takes a whole query-shaped object — the same object Query Events
|
|
84
|
+
takes under `query`, with the conditions under its own `filter` key — not a bare condition map. That
|
|
85
|
+
is why the key appears twice: `filter.filter.<field>`. A single level,
|
|
86
|
+
`{ "filter": { "id": { "$in": [...] } } }`, is not the documented shape. Resolve the ids with the
|
|
87
|
+
query above first; one `DELETE` per event also works but costs a call each.
|
|
88
|
+
|
|
42
89
|
> **Draft events need the `WIX_EVENTS.READ_DRAFT_EVENTS` permission.** Without it, publishing a
|
|
43
90
|
> draft fails `403` — as does querying it, fetching it by slug, or adding ticket definitions to
|
|
44
91
|
> it. If you hit that `403`, the event was still created; the way forward is to create events
|
|
@@ -104,6 +151,9 @@ even when the site has events, so it is not the way to answer "how many events d
|
|
|
104
151
|
- **An invalid enum value reports as a missing one** — a value outside an enum returns
|
|
105
152
|
`<field> value is required` rather than "invalid value". If a field you *did* send is reported
|
|
106
153
|
as required, suspect the value, not its presence.
|
|
154
|
+
- **`includeDrafts` stays `false` unless the request is about drafts** — see
|
|
155
|
+
[Find an event](#find-an-event). The `WIX_EVENTS.READ_DRAFT_EVENTS` note on the Query Events
|
|
156
|
+
reference describes the permission; it does not mean the caller has it.
|
|
107
157
|
- Dates are always ISO-8601 strings, never `{seconds, nanos}`, and `timeZoneId` is required
|
|
108
158
|
whenever `dateAndTimeSettings` is sent — see [Create an Event](create-wix-event.md).
|
|
109
159
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Manage Campaign Lifecycle"
|
|
3
|
-
description: "Manages existing Google Ads campaigns on a Wix site: list/get, launch (first activation) vs resume (reactivate after a pause), pause a running campaign — optionally with a scheduled auto-resume date — update name/budget/targeting, change the daily budget, delete permanently, and read status history / change log. Use when the user wants to 'pause my Google ad', 'resume my campaign', 'stop the campaign', 'change my daily budget', 'rename the campaign', 'delete this campaign', 'list my Google Ads campaigns', or 'why did my campaign status change'. Requires an existing Google Ads account and campaign. REST base https://www.wixapis.com/google
|
|
3
|
+
description: "Manages existing Google Ads campaigns on a Wix site: list/get, launch (first activation) vs resume (reactivate after a pause), pause a running campaign — optionally with a scheduled auto-resume date — update name/budget/targeting, change the daily budget, delete permanently, and read status history / change log. Use when the user wants to 'pause my Google ad', 'resume my campaign', 'stop the campaign', 'change my daily budget', 'rename the campaign', 'delete this campaign', 'list my Google Ads campaigns', or 'why did my campaign status change'. Requires an existing Google Ads account and campaign. REST base https://www.wixapis.com/_serverless/pa-google/v1."
|
|
4
4
|
---
|
|
5
5
|
# RECIPE: Manage Campaign Lifecycle
|
|
6
6
|
|
|
7
|
-
Operate on campaigns that already exist. Base URL: `https://www.wixapis.com/google
|
|
7
|
+
Operate on campaigns that already exist. Base URL: `https://www.wixapis.com/_serverless/pa-google/v1`; `<AUTH>` = `Authorization` header, and body calls also need `Content-Type: application/json`.
|
|
8
8
|
|
|
9
9
|
**Answer directly.** When the user asks *how* to do something, give the endpoint, an example request, and the key behavior right away — use `{campaignId}` as a placeholder rather than asking which campaign. Only pause to confirm when you are about to **execute** a launch, resume, or delete on the user's behalf (those spend money or are irreversible); explaining how never requires confirmation.
|
|
10
10
|
|
|
@@ -21,14 +21,14 @@ Operate on campaigns that already exist. Base URL: `https://www.wixapis.com/goog
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
# Launch (first activation) → status LIVE
|
|
24
|
-
curl -X POST 'https://www.wixapis.com/google
|
|
24
|
+
curl -X POST 'https://www.wixapis.com/_serverless/pa-google/v1/campaigns/{campaignId}/launch' -H 'Authorization: <AUTH>' -H 'Content-Type: application/json' -d '{}'
|
|
25
25
|
|
|
26
26
|
# Pause a running campaign (settings + data preserved) → status PAUSED
|
|
27
|
-
curl -X POST 'https://www.wixapis.com/google
|
|
27
|
+
curl -X POST 'https://www.wixapis.com/_serverless/pa-google/v1/campaigns/{campaignId}/pause' -H 'Authorization: <AUTH>' -H 'Content-Type: application/json' \
|
|
28
28
|
-d '{ "scheduledResumeDate": "2024-04-15T08:00:00.000Z" }'
|
|
29
29
|
|
|
30
30
|
# Resume a paused campaign → status LIVE
|
|
31
|
-
curl -X POST 'https://www.wixapis.com/google
|
|
31
|
+
curl -X POST 'https://www.wixapis.com/_serverless/pa-google/v1/campaigns/{campaignId}/resume' -H 'Authorization: <AUTH>' -H 'Content-Type: application/json' -d '{}'
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
- **Pause** stops delivery immediately without losing configuration. Optional body fields: `scheduledResumeDate` (ISO 8601 — the campaign **auto-resumes** at that time; pass `null` to cancel a scheduled resume), `resumeReminderDate` (dashboard reminder), `turnAutoRenewOff`. Send `{}` to pause with no auto-resume. To temporarily stop a campaign, **pause it — never delete**.
|
|
@@ -39,7 +39,7 @@ curl -X POST 'https://www.wixapis.com/google-ads/v1/campaigns/{campaignId}/resum
|
|
|
39
39
|
`PATCH /v1/campaigns/{id}` — send only the fields you're changing, plus the required `id` and `accountId`. Budget is in **micros** (`20000000` = $20.00/day).
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
curl -X PATCH 'https://www.wixapis.com/google
|
|
42
|
+
curl -X PATCH 'https://www.wixapis.com/_serverless/pa-google/v1/campaigns/{campaignId}' -H 'Authorization: <AUTH>' -H 'Content-Type: application/json' \
|
|
43
43
|
-d '{ "campaign": { "id": "...", "accountId": "...", "name": "New name", "budget": { "amountMicros": "20000000" } } }'
|
|
44
44
|
```
|
|
45
45
|
|