@wix/agent-skills 1.20.0 → 1.20.1
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-headless-fast/SKILL.md +200 -34
- package/skills/wix-headless-fast/entry/skill.md +10 -2
- package/skills/wix-headless-fast/install/attach.mjs +253 -0
- package/skills/wix-headless-fast/install/deploy.mjs +109 -9
- package/skills/wix-headless-fast/install/fast-path.mjs +29 -34
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +224 -60
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +24 -116
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +16 -64
- package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +137 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +78 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +117 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +13 -60
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +47 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +10 -28
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +2 -4
- package/skills/wix-headless-fast/references/blog/rest/posts.ts +42 -0
- package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +62 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +179 -33
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +2 -1
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +26 -132
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +18 -49
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +218 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +169 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +57 -142
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +92 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +90 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +22 -61
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +3 -1
- package/skills/wix-headless-fast/references/bookings/rest/booking.ts +113 -0
- package/skills/wix-headless-fast/references/bookings/rest/services.ts +55 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +10 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +236 -62
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +19 -52
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +16 -36
- package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +158 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +93 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +177 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +13 -41
- package/skills/wix-headless-fast/references/cms/rest/items.ts +125 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +239 -83
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +1 -1
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +7 -5
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +2 -2
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +28 -110
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +19 -54
- package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +130 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +97 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +13 -94
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +113 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +156 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +38 -69
- package/skills/wix-headless-fast/references/events/rest/events.ts +57 -0
- package/skills/wix-headless-fast/references/events/rest/registration.ts +83 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +214 -26
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +35 -267
- package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +332 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +238 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +28 -243
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +216 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +32 -204
- package/skills/wix-headless-fast/references/forms/rest/forms.ts +34 -0
- package/skills/wix-headless-fast/references/forms/rest/submissions.ts +56 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +15 -8
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +314 -63
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +105 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +36 -65
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +43 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +11 -32
- package/skills/wix-headless-fast/references/members/rest/auth.ts +215 -0
- package/skills/wix-headless-fast/references/members/rest/members.ts +26 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +238 -59
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +35 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +8 -38
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +23 -50
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +16 -34
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +22 -49
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +82 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +177 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +31 -118
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +81 -0
- package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +75 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +212 -51
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +9 -20
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +7 -32
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +12 -33
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +17 -34
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +54 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +106 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +64 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +7 -55
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +43 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +11 -34
- package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +31 -0
- package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +20 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +240 -47
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +14 -35
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +28 -171
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +89 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +23 -117
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +124 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +45 -114
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +193 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +94 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +15 -87
- package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +60 -0
- package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +169 -0
- package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +68 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +23 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +45 -4
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +13 -0
- package/skills/wix-headless-fast/references/shared/rest/client.ts +155 -0
- package/skills/wix-headless-fast/references/shared/rest/config.ts +3 -0
- package/skills/wix-headless-fast/references/shared/rest/media.ts +65 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +182 -63
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +95 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +29 -58
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +29 -163
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +26 -300
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +121 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +49 -162
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +309 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +47 -370
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +147 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +289 -0
- package/skills/wix-headless-fast/references/storefront/rest/cart.ts +132 -0
- package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +123 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +25 -9
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +6 -35
- package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -40
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +3 -0
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +6 -0
- package/skills/wix-headless-fast/install/pin-agents-md.mjs +0 -57
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Wix Data reads/writes over REST — the twin of app/wix/cms/items.ts. Same exports, same DTOs; the
|
|
2
|
+
// rules and mappers come from items-core (the SAME file the SDK transport uses, deployed flat next
|
|
3
|
+
// to this one by deploy.mjs --stack static), so this file is only the transport: one fetch with a
|
|
4
|
+
// literal body per function. The reads are safe from a browser with a visitor token on any
|
|
5
|
+
// collection whose read permission is ANYONE; the writes succeed only where the collection's
|
|
6
|
+
// insert/update/remove permission covers the caller (403 WDE0027 otherwise — a permissions step,
|
|
7
|
+
// not a code bug). Porting: keep the paths, keep the bodies, port the core once.
|
|
8
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-data-items.md
|
|
9
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/get-data-item.md
|
|
10
|
+
// docs: https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/patch-data-item.md
|
|
11
|
+
import { WixApiError, wixRequest } from "./client.js";
|
|
12
|
+
import { imgSrc } from "./media.js";
|
|
13
|
+
import { DEFAULT_LIMIT, patchModifications, queryBody, restHasNext, restWriteData, toItem, toPage, type Raw } from "./items-core.js";
|
|
14
|
+
import type { CmsFilter, CmsItem, CmsPage, CmsQuery } from "./types.js";
|
|
15
|
+
|
|
16
|
+
const notFound = (e: unknown): boolean => e instanceof WixApiError && e.status === 404;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Query one page of a collection. An empty result on a PUBLIC collection is a seed permissions bug
|
|
20
|
+
* (read must be "ANYONE"), not a query bug.
|
|
21
|
+
* POST /wix-data/v2/items/query { dataCollectionId, query: { filter, sort, paging: { limit, offset } }, includeReferences: [{ field }], returnTotalCount }
|
|
22
|
+
* → { dataItems: [{ id, dataCollectionId, data: { _id, …fields } }], pagingMetadata: { count, offset, total?, hasNext } }
|
|
23
|
+
*/
|
|
24
|
+
export async function queryItems(collectionId: string, query: CmsQuery = {}): Promise<CmsPage> {
|
|
25
|
+
const { limit = DEFAULT_LIMIT, skip = 0 } = query;
|
|
26
|
+
const res = await wixRequest<Raw>("/wix-data/v2/items/query", { body: queryBody(collectionId, query) });
|
|
27
|
+
const items = ((res?.dataItems ?? []) as Raw[]).map((d) => d.data ?? {});
|
|
28
|
+
return toPage(items, restHasNext(res?.pagingMetadata, skip, limit), res?.pagingMetadata?.total, imgSrc);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fetch one item by `_id`. Null when not found (404 WDE0073).
|
|
33
|
+
* GET /wix-data/v2/items/{id}?dataCollectionId=…&includeReferences.field=… → { dataItem: { data } }
|
|
34
|
+
*/
|
|
35
|
+
export async function getItemById(
|
|
36
|
+
collectionId: string,
|
|
37
|
+
itemId: string,
|
|
38
|
+
{ include = [] }: { include?: string[] } = {},
|
|
39
|
+
): Promise<CmsItem | null> {
|
|
40
|
+
try {
|
|
41
|
+
const res = await wixRequest<Raw>(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, {
|
|
42
|
+
method: "GET",
|
|
43
|
+
query: { dataCollectionId: collectionId, ...(include.length ? { "includeReferences.field": include } : {}) },
|
|
44
|
+
});
|
|
45
|
+
return res?.dataItem?.data ? toItem(res.dataItem.data, imgSrc) : null;
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (notFound(e)) return null;
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fetch the first item whose `field` equals `value` — slug-style routing (Wix Data has no native
|
|
54
|
+
* get-by-slug). Null when not found → render a not-found state, never invent an item.
|
|
55
|
+
*/
|
|
56
|
+
export async function getItemBy(
|
|
57
|
+
collectionId: string,
|
|
58
|
+
field: string,
|
|
59
|
+
value: string | number,
|
|
60
|
+
{ include = [] }: { include?: string[] } = {},
|
|
61
|
+
): Promise<CmsItem | null> {
|
|
62
|
+
const page = await queryItems(collectionId, { filters: [{ field, op: "eq", value }], limit: 1, include });
|
|
63
|
+
return page.items[0] ?? null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Count items matching the filters — empty-state logic and result counts (returnTotalCount). */
|
|
67
|
+
export async function countItems(collectionId: string, filters: CmsFilter[] = []): Promise<number> {
|
|
68
|
+
const page = await queryItems(collectionId, { filters, limit: 1, withTotal: true });
|
|
69
|
+
return page.total ?? page.items.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Insert an item (visitor form / member submission). Never set `_owner` — Wix stamps it from the
|
|
74
|
+
* caller's identity. Date fields must be Date objects (the core spells them `{ $date }` on the wire;
|
|
75
|
+
* an ISO string is stored as text and breaks date queries).
|
|
76
|
+
* POST /wix-data/v2/items { dataCollectionId, dataItem: { data } } → { dataItem: { data } }
|
|
77
|
+
*/
|
|
78
|
+
export async function insertItem(collectionId: string, data: Record<string, unknown>): Promise<CmsItem> {
|
|
79
|
+
const res = await wixRequest<Raw>("/wix-data/v2/items", { body: { dataCollectionId: collectionId, dataItem: { data: restWriteData(data) } } });
|
|
80
|
+
return toItem(res?.dataItem?.data ?? {}, imgSrc);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* REPLACE an item — fields omitted from `item` are WIPED (update does not patch). Spread the full
|
|
85
|
+
* item you hold and override; for an id + a few changed fields use patchItemFields. Wrap each date
|
|
86
|
+
* field back into a Date (`new Date(iso)`) before updating.
|
|
87
|
+
* PUT /wix-data/v2/items/{id} { dataCollectionId, dataItem: { id, data } } → { dataItem: { data } }
|
|
88
|
+
*/
|
|
89
|
+
export async function updateItem(collectionId: string, item: CmsItem): Promise<CmsItem> {
|
|
90
|
+
const res = await wixRequest<Raw>(`/wix-data/v2/items/${encodeURIComponent(item._id)}`, {
|
|
91
|
+
method: "PUT",
|
|
92
|
+
body: { dataCollectionId: collectionId, dataItem: { id: item._id, data: restWriteData(item) } },
|
|
93
|
+
});
|
|
94
|
+
return toItem(res?.dataItem?.data ?? {}, imgSrc);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Patch only the named fields — the safe partial change.
|
|
99
|
+
* PATCH /wix-data/v2/items/{id} { dataCollectionId, patch: { dataItemId, fieldModifications: [{ fieldPath, action: "SET_FIELD", setFieldOptions: { value } }] } }
|
|
100
|
+
*/
|
|
101
|
+
export async function patchItemFields(
|
|
102
|
+
collectionId: string,
|
|
103
|
+
itemId: string,
|
|
104
|
+
fields: Record<string, unknown>,
|
|
105
|
+
): Promise<CmsItem> {
|
|
106
|
+
const res = await wixRequest<Raw>(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, {
|
|
107
|
+
method: "PATCH",
|
|
108
|
+
body: { dataCollectionId: collectionId, patch: { dataItemId: itemId, fieldModifications: patchModifications(fields) } },
|
|
109
|
+
});
|
|
110
|
+
return toItem(res?.dataItem?.data ?? {}, imgSrc);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Remove an item by `_id`. Irreversible. Returns the removed item (null if it didn't exist).
|
|
115
|
+
* DELETE /wix-data/v2/items/{id}?dataCollectionId=… → { dataItem: { data } }
|
|
116
|
+
*/
|
|
117
|
+
export async function removeItem(collectionId: string, itemId: string): Promise<CmsItem | null> {
|
|
118
|
+
try {
|
|
119
|
+
const res = await wixRequest<Raw>(`/wix-data/v2/items/${encodeURIComponent(itemId)}`, { method: "DELETE", query: { dataCollectionId: collectionId } });
|
|
120
|
+
return res?.dataItem?.data ? toItem(res.dataItem.data, imgSrc) : null;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
if (notFound(e)) return null;
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -87,6 +87,15 @@ every content item an image on an IMAGE field (a content site without images loo
|
|
|
87
87
|
**Seeding is additive — never delete or overwrite existing content** (an existing collection
|
|
88
88
|
is left as-is; its items are appended); ask first if a cleanup seems needed.
|
|
89
89
|
|
|
90
|
+
## Supplied content
|
|
91
|
+
|
|
92
|
+
The general rules are in `references/shared/SUPPLIED-CONTENT.md`. For the CMS the user's data
|
|
93
|
+
defines the schema: each distinct table, sheet or file becomes a collection, its columns become
|
|
94
|
+
`fields` with a type read from the values (`TEXT`, `NUMBER`, `DATE`, `IMAGE` for image URLs,
|
|
95
|
+
`RICH_TEXT` for HTML), and its rows become `items`. Keep the user's column names as field keys. A
|
|
96
|
+
column whose values point at rows of another table becomes a `MULTI_REFERENCE`. When a column's
|
|
97
|
+
type is ambiguous, ask.
|
|
98
|
+
|
|
90
99
|
## Escape hatch — individual functions
|
|
91
100
|
`setupCms` composes exported steps — `installDataApp`, `createCollection`, `importImage`,
|
|
92
101
|
`bulkInsertItems`, `insertReferences`, `verifyItems`, plus `makeCtx()` — import them only
|
|
@@ -1,149 +1,305 @@
|
|
|
1
1
|
# Events — playbook
|
|
2
2
|
|
|
3
3
|
The events machinery ships as files — event reads (list, by slug, by id), the visitor-public
|
|
4
|
-
ticket-tier read, the exact `reserve → hosted-checkout redirect` sequence,
|
|
5
|
-
end-to-end. **The presentation
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
ticket-tier read, the exact `reserve → hosted-checkout redirect` sequence, RSVP, the registration
|
|
5
|
+
controls, and the SEO plumbing, typed end-to-end. **The presentation doesn't ship — you build it**
|
|
6
|
+
on the shipped hooks/DTOs: the event card and the events index, the event page's layout, the home
|
|
7
|
+
page, and the brand. You never write registration logic; you never skip designing.
|
|
8
8
|
|
|
9
9
|
## The file map (deployed into `src/`)
|
|
10
10
|
|
|
11
|
-
**
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
**On Astro and React the shipped files are tested and work as they are** — this table and the
|
|
12
|
+
contracts below are everything you need to use them, so don't spend the run reading their source;
|
|
13
|
+
wire them and build your surfaces. Reading them is the right move when something is off (a runtime
|
|
14
|
+
error, a field this playbook doesn't cover) or when the brief wants a behaviour they don't offer —
|
|
15
|
+
then read the file that owns it and change or extend it. On `lib`, `static`, and a port the
|
|
16
|
+
components don't deploy at all, and each wiring section below opens with the files to read before
|
|
17
|
+
writing their equivalents. Files you edit: `SiteLayout.astro`, `styles/global.css`, and the two
|
|
18
|
+
island imports in the shipped pages. Files you **create**: your listing component (skeleton below)
|
|
19
|
+
and your home page.
|
|
14
20
|
|
|
15
21
|
| file | what it is |
|
|
16
22
|
|---|---|
|
|
17
|
-
| `wix/config.ts` · `wix/sdk.ts`
|
|
18
|
-
| `wix/
|
|
19
|
-
| `wix/events/
|
|
20
|
-
| `wix/events/
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
23
|
+
| `wix/config.ts` · `wix/sdk.ts` | shared auth seam (deploy configures it — nothing to set by hand) |
|
|
24
|
+
| `wix/media.ts` · `wix/money.ts` | `imgAttrs(url, sizes, ratio)` — every `<img>` attribute for a DTO image (`src`, `srcSet`, `sizes`, lazy): `<img {...imgAttrs(e.imageUrl, "33vw", 2 / 3)} alt={e.title} />`; `imgSrc()` / `imgSrcSet()` / `formatMoney()` underneath |
|
|
25
|
+
| `wix/events/types.ts` | the DTOs (`EventSummary`, `EventDetail`, `TicketTier`, `RegistrationResult`) — contracts inlined below |
|
|
26
|
+
| `wix/events/events.ts` | `fetchEvents` (live events, soonest first), `fetchEventBySlug`, `fetchEventById` (the post-checkout confirmation read) — the transport; the rules and DTO mappers are in `events-core.ts` beside it (shared with the REST layer) |
|
|
27
|
+
| `wix/events/registration.ts` | `fetchTicketTiers`, `startTicketCheckout`, `submitRsvp` — the exact reserve→redirect sequence and the RSVP body; rules in `registration-core.ts` beside it |
|
|
28
|
+
| `wix/events/events-store.ts` · `registration-store.ts` | the listing and registration state machines, framework-free (`createEventsStore()`, `createRegistrationStore(event)` — `getState`/`subscribe` + actions, one instance per surface); the hooks below bind them to React, every other stack uses them directly |
|
|
29
|
+
| `hooks/events/useEvents.ts` | React binding of `events-store.ts`: listing + category filter — contract below |
|
|
30
|
+
| `hooks/events/useEventRegistration.ts` | React binding of `registration-store.ts`: tiers, quantities, checkout, the RSVP form — contract below |
|
|
31
|
+
| `components/events/EventRegistrationView.tsx` | the registration controls for one event — branches on `registrationType` (tier picker with clamped steppers and the gated CTA; the three-field RSVP form with its confirmed / waitlisted / declined states; the external link; the honest closed state) — **wire as-is** in your event page's registration column (`<EventRegistrationView client:only="react" event={event} />`); build your own on `useEventRegistration` only when the brief wants more |
|
|
32
|
+
| `components/events/EventConfirmationView.tsx` | the hosted checkout's thank-you island — reads `?orderNumber=&eventId=`, echoes the order number, fetches the event for context, an honest "no order" state on a direct visit — **wire as-is** |
|
|
33
|
+
| `components/events/EventsView.tsx` (+ `EventCard`) | **reference** listing — correct and plain; the shipped `events.astro` mounts it so the site works before you design; you build your own on `useEvents` and swap the import |
|
|
34
|
+
| `styles/global.css` | **the design system**: Tailwind v4 + the `@theme` token block (colors, radii, fonts — shared across verticals). Everything, shipped and yours, styles from these tokens |
|
|
25
35
|
|
|
26
36
|
Astro stack additionally gets:
|
|
27
37
|
|
|
28
38
|
| file | what it is |
|
|
29
39
|
|---|---|
|
|
30
|
-
| `layouts/SiteLayout.astro` | site chrome — **yours to brand** (keep the `seo-tags` slot + global.css import). If another vertical is also deployed, its layout won — add an Events nav link there |
|
|
31
|
-
| `pages/events.astro` | SSR listing — **keep the frontmatter**, swap the island import to YOUR component |
|
|
32
|
-
| `pages/events/[slug].astro` | SSR
|
|
40
|
+
| `layouts/SiteLayout.astro` | the site chrome — **yours to brand** (keep the `seo-tags` slot + the global.css import). If another vertical is also deployed, its layout won — add an Events nav link there |
|
|
41
|
+
| `pages/events.astro` | SSR listing — **keep the frontmatter**, swap the island import to YOUR listing component |
|
|
42
|
+
| `pages/events/[slug].astro` | SSR event page with owner-editable SEO — **keep the frontmatter and the SEO pieces** (`wixMetadata`, `loadSEOTagsServiceConfig`, `<SEO.Tags>`) exactly; the markup between them is yours to redesign around the shipped registration island (`client:only="react"`) |
|
|
33
43
|
| `pages/event-confirmation.astro` | the hosted checkout's thank-you landing (`?orderNumber=&eventId=`) — **keep the route**; the shipped checkout callbacks point at it |
|
|
34
44
|
|
|
35
45
|
## What you build — the design job
|
|
36
46
|
|
|
37
|
-
1. **The event card + events index** — your tile (image, date eyebrow, title,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
2. **The event page's
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
the
|
|
44
|
-
|
|
45
|
-
link out; closed → an honest closed state — on `useEventRegistration`, which owns ALL
|
|
46
|
-
registration logic; you own how it looks.
|
|
47
|
-
3. **The home page** — hero, featured/next events (fetch in frontmatter → your components),
|
|
47
|
+
1. **The event card + events index** — your tile (image, date eyebrow, title, venue/online,
|
|
48
|
+
price-from, sold-out badge) and rhythm, the category filter (only when >1 category),
|
|
49
|
+
skeletons while loading, an honest empty state — on `useEvents`.
|
|
50
|
+
2. **The event page's layout** — hero image, date, venue, about, with the shipped
|
|
51
|
+
`EventRegistrationView` as the registration column; design the page for the brand (an
|
|
52
|
+
editorial split, a sticky registration column, a full-bleed hero). Registration logic never
|
|
53
|
+
leaves the shipped component and hook.
|
|
54
|
+
3. **The home page** — hero, next/featured events (fetch in frontmatter → your components),
|
|
48
55
|
brand story.
|
|
49
56
|
|
|
50
|
-
Plus the **theme** (`@theme` block, one edit) and the **chrome** (`SiteLayout`, one pass).
|
|
51
|
-
|
|
57
|
+
Plus the **theme** (`@theme` block, one edit) and the **chrome** (`SiteLayout`, one pass). Style
|
|
58
|
+
everything with Tailwind utilities on the tokens.
|
|
52
59
|
|
|
53
|
-
###
|
|
60
|
+
### What a complete events site shows (recommended defaults)
|
|
61
|
+
|
|
62
|
+
Defaults for a brief that says nothing about them; the prompt wins when it asks for something
|
|
63
|
+
else. Look at the seeded events before designing (how many, ticketed or RSVP, images, categories).
|
|
64
|
+
|
|
65
|
+
- **Home:** what the events are and the next one in the first screen; real events under truthful
|
|
66
|
+
headings; a link to the index.
|
|
67
|
+
- **Index:** a real card — image, date, title, venue, price-from, link — in the first screen; the
|
|
68
|
+
category filter only when the events carry more than one category; `events === null` →
|
|
69
|
+
skeletons, `[]` → an honest empty state; `error` → a short inline message.
|
|
70
|
+
- **Event page:** image, date, title, venue and the registration control in the first screen at
|
|
71
|
+
390px wide too — a bounded image band on a phone (`max-h-[45vh]`), the registration column
|
|
72
|
+
under it, the two-column split from `md`; `aboutParagraphs` after the registration control,
|
|
73
|
+
never between the date and the action; `addToCalendar.google` as a link when present.
|
|
74
|
+
- **Registration:** the shipped `EventRegistrationView` — `TICKETING` shows the tier picker,
|
|
75
|
+
`RSVP` the three-field form, `EXTERNAL` the link out, closed or `NONE` the closed state;
|
|
76
|
+
confirmed and waitlisted read differently.
|
|
77
|
+
- **Confirmation:** `/event-confirmation` — the shipped island; the only success surface for a
|
|
78
|
+
ticket order.
|
|
79
|
+
- **Copy:** nothing the organizer didn't supply — no invented "X spots left", no fake urgency, no
|
|
80
|
+
Wix IDs in visible text.
|
|
81
|
+
|
|
82
|
+
### The contracts your components consume (everything you need — read the source when something is off)
|
|
54
83
|
|
|
55
84
|
```ts
|
|
56
85
|
// EventSummary (tiles) — display-ready:
|
|
57
|
-
// { id, slug, title, shortDescription /* plain text */, dateLabel /* "
|
|
86
|
+
// { id, slug, title, shortDescription /* plain text */, dateLabel /* "6 November 2026, 19:30–23:00 GMT-8" */,
|
|
58
87
|
// startDateIso, locationName, locationType: "VENUE"|"ONLINE"|"TBD", imageUrl,
|
|
59
88
|
// registrationType: "RSVP"|"TICKETING"|"EXTERNAL"|"NONE",
|
|
60
|
-
// priceLabel /* "From €
|
|
61
|
-
// EventDetail adds: aboutParagraphs: string[], registrationOpen, rsvpResponseType,
|
|
89
|
+
// priceLabel /* "From €45" | "Free" | "" */, soldOut, categories: [{ id, name }] }
|
|
90
|
+
// EventDetail adds: aboutParagraphs: string[], registrationOpen, rsvpResponseType: "YES_ONLY"|"YES_AND_NO",
|
|
62
91
|
// externalUrl, addToCalendar: { google, ics }.
|
|
63
92
|
// TicketTier: { id, name, description, price /* "€45.00" | "Free" */, free,
|
|
64
93
|
// limitPerCheckout, saleStatus /* only "SALE_STARTED" is buyable */ }
|
|
65
94
|
|
|
66
95
|
// useEvents({ initialEvents? }) →
|
|
67
96
|
// { events: EventSummary[]|null /* null = loading → skeletons */,
|
|
68
|
-
// categories: [{id,name}] /* derived from the loaded events */,
|
|
97
|
+
// categories: [{ id, name }] /* derived from the loaded events */,
|
|
69
98
|
// activeCategoryId, setActiveCategoryId(id|null), error }
|
|
99
|
+
// Only UPCOMING/STARTED events arrive, soonest first — a past event is never listed or linked.
|
|
70
100
|
|
|
71
101
|
// useEventRegistration(event: EventDetail) →
|
|
72
102
|
// { tiers: TicketTier[]|null, // TICKETING only; null = loading
|
|
73
|
-
// quantities, setQuantity(tierId, qty), // clamped; sale
|
|
103
|
+
// quantities, setQuantity(tierId, qty), // clamped 0..limitPerCheckout; ignored for tiers not on sale
|
|
74
104
|
// ticketCount, canCheckout, // gate the tickets CTA on canCheckout
|
|
75
105
|
// checkout(): Promise<RegistrationResult>, // reserve → the browser redirects to Wix checkout
|
|
76
106
|
// rsvpValues, setRsvpValue(field, value), // built-in fields: firstName/lastName/email
|
|
77
107
|
// canRsvp, // gate the RSVP CTA on this
|
|
78
108
|
// rsvp(attending?): Promise<RegistrationResult>, // attending=false only for YES_AND_NO
|
|
79
|
-
// submitting, confirmed, error } // confirmed: rsvpConfirmed
|
|
80
|
-
//
|
|
109
|
+
// submitting, confirmed, error } // confirmed: { kind: "rsvpConfirmed", status: "YES"|"NO"|"WAITLIST" }
|
|
110
|
+
// checkout() and rsvp() reject on refusal (sold out, sale ended, closed registration, payment
|
|
111
|
+
// method not configured) AND record .error — render it where the control is.
|
|
81
112
|
```
|
|
82
113
|
|
|
114
|
+
### The listing component you create — skeleton
|
|
115
|
+
|
|
116
|
+
Hooks first, branches after (an early return above a hook changes hook order between renders and
|
|
117
|
+
React throws). The island renders on the server too (`client:load` SSRs), so nothing in a render
|
|
118
|
+
path may throw — render every state totally.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
// src/components/events/<YourListing>.tsx — YOU build it; events.astro mounts it (swap the import).
|
|
122
|
+
import { useEvents } from "../../hooks/events/useEvents";
|
|
123
|
+
import { imgAttrs } from "../../wix/media";
|
|
124
|
+
import type { EventSummary } from "../../wix/events/types";
|
|
125
|
+
|
|
126
|
+
export default function EventsIndex(props: { initialEvents?: EventSummary[] }) {
|
|
127
|
+
const { events, categories, activeCategoryId, setActiveCategoryId, error } = useEvents(props);
|
|
128
|
+
// …you implement the render:
|
|
129
|
+
// • a filter row only when categories.length > 1 ("All" + one pill per category, the active
|
|
130
|
+
// one marked by activeCategoryId; setActiveCategoryId(null) clears)
|
|
131
|
+
// • error → a short inline message
|
|
132
|
+
// • events === null → skeleton tiles; [] → your honest empty state
|
|
133
|
+
// • else YOUR grid of YOUR tiles: image via <img {...imgAttrs(e.imageUrl, "(min-width: 1024px) 33vw, 50vw", 2 / 3)} alt={e.title} />
|
|
134
|
+
// (an empty imageUrl gives {} — render your placeholder then), dateLabel as the eyebrow,
|
|
135
|
+
// title WRAPPING (no truncate), locationType === "ONLINE" ? "Online" : locationName,
|
|
136
|
+
// priceLabel when non-empty, a sold-out badge from e.soldOut; the tile links to `/events/${e.slug}`
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The event page is the shipped `pages/events/[slug].astro`: keep its frontmatter and SEO pieces
|
|
141
|
+
and redesign the markup between them; the registration column stays
|
|
142
|
+
`<EventRegistrationView client:only="react" event={event} />`. Home: `fetchEvents({ limit: 3 })`
|
|
143
|
+
in frontmatter → your cards.
|
|
144
|
+
|
|
145
|
+
### The reference files for stacks where the components don't deploy
|
|
146
|
+
|
|
147
|
+
On `lib`, `static`, and a port, nothing under `components/` or `hooks/` arrives. The state
|
|
148
|
+
machines behind the hooks do arrive — `wix/events/events-store.ts`, `registration-store.ts` — so
|
|
149
|
+
you never rewrite them: create a store per surface, `subscribe`, render from `getState()`, call
|
|
150
|
+
its actions. Their `*State` interfaces are the render contract; read those. What you write is the
|
|
151
|
+
rendering — index, event page, tier picker, RSVP form, confirmation — and for that read these
|
|
152
|
+
first; they are tested code for exactly that behaviour:
|
|
153
|
+
|
|
154
|
+
1. `components/events/EventRegistrationView.tsx` — the branch on `registrationType`; the stepper
|
|
155
|
+
pair per tier (disabled at 0 and at `limitPerCheckout`, both disabled when the tier isn't on
|
|
156
|
+
sale, with the "Sale hasn't started" / "Sale ended" line); the CTA gated by `canCheckout` /
|
|
157
|
+
`canRsvp` and `submitting`; `error` rendered beside the control; the confirmed vs waitlisted vs
|
|
158
|
+
declined copy; the closed state distinguishing sold out from closed.
|
|
159
|
+
2. `components/events/EventConfirmationView.tsx` — the honest "no order" state on a direct visit;
|
|
160
|
+
the order number echoed, never invented order details; the event fetched by `eventId` for
|
|
161
|
+
context; the calendar link.
|
|
162
|
+
3. `components/events/EventsView.tsx` — the filter pills, the skeleton grid, the empty state, the
|
|
163
|
+
card's fields in order.
|
|
164
|
+
|
|
165
|
+
All under `references/events/app/`.
|
|
166
|
+
|
|
83
167
|
### Wiring — Astro (default)
|
|
84
168
|
|
|
85
169
|
1. Set the `@theme` tokens (one edit); brand `SiteLayout.astro` (one pass — merge into the
|
|
86
170
|
existing layout instead if another vertical is deployed).
|
|
87
|
-
2. Write your
|
|
88
|
-
references)
|
|
89
|
-
`pages/events/[slug].astro
|
|
90
|
-
|
|
91
|
-
**Author your surfaces in as few messages as possible** — batch multiple Writes per
|
|
92
|
-
message.
|
|
171
|
+
2. Write your listing component under `src/components/events/` (a new name — don't overwrite the
|
|
172
|
+
references) and swap the import in `pages/events.astro` (`client:load` with the SSR props).
|
|
173
|
+
Redesign the markup of `pages/events/[slug].astro` around the shipped registration island
|
|
174
|
+
(`client:only="react"`). Keep `pages/event-confirmation.astro`'s route.
|
|
93
175
|
3. Write `pages/index.astro` (home) — it exists from the scaffold; Read it before overwriting.
|
|
94
176
|
|
|
177
|
+
### Wiring — another JS framework (`--stack lib`: Vue, Svelte, Solid, plain Vite)
|
|
178
|
+
|
|
179
|
+
Read the reference files listed above before writing any surface.
|
|
180
|
+
|
|
181
|
+
`deploy.mjs events --stack lib` put the data layer in `src/wix/` and nothing else: `sdk.ts` (the
|
|
182
|
+
visitor client, configured with the public client id), `media.ts`, `money.ts`, and `wix/events/`
|
|
183
|
+
— `events.ts`, `registration.ts`, `types.ts`, the `*-core.ts` rules, and the two stores
|
|
184
|
+
`events-store.ts`, `registration-store.ts`. None of it is React. The hooks and components don't
|
|
185
|
+
ship on this stack; the stores replace the hooks, and you write the components in your framework
|
|
186
|
+
to the contracts on this page:
|
|
187
|
+
|
|
188
|
+
- bind the stores with your framework's external-store primitive (Vue: `shallowRef` updated in
|
|
189
|
+
`subscribe`; Svelte: `readable(store.getState(), (set) => store.subscribe(() => set(store.getState())))`;
|
|
190
|
+
Solid: a signal set in `subscribe`). `createEventsStore({ initialEvents? })` per listing
|
|
191
|
+
(`start()` when mounted, `stop()` when unmounted), `createRegistrationStore(event)` per event
|
|
192
|
+
page. State in, actions out — exactly the hooks' contracts above;
|
|
193
|
+
- your tier picker, RSVP form, and confirmation to the reference files above.
|
|
194
|
+
|
|
195
|
+
Routes `/events`, `/events/:slug` (via `fetchEventBySlug`, null → your 404), `/event-confirmation`
|
|
196
|
+
(the checkout callbacks point at it); dev server on 4321; a static build goes through
|
|
197
|
+
`npx @wix/cli@latest release` with `site.outputDirectory` pointing at the build folder, an SSR
|
|
198
|
+
build is hosted by you. Page title and description from the event's `title` / `shortDescription`.
|
|
199
|
+
|
|
200
|
+
### Wiring — static site (`--stack static`, no bundler)
|
|
201
|
+
|
|
202
|
+
Read the reference files listed above before writing any surface.
|
|
203
|
+
|
|
204
|
+
`deploy.mjs events --stack static --out site` put the REST layer in `site/js/wix/` (browser ESM,
|
|
205
|
+
the `.ts` beside each `.js` for reading). Everything the visitor loads lives under `site/` —
|
|
206
|
+
pages, styles, `js/` — and `wix.config.json`'s `site.outputDirectory` is `"./site"`; the project
|
|
207
|
+
root (config, plan, seed output) is never the upload. Same function names and DTOs as the table
|
|
208
|
+
above, so the contracts on this page hold unchanged: `fetchEvents`, `fetchEventBySlug`,
|
|
209
|
+
`fetchEventById` from `./js/wix/events.js`; `fetchTicketTiers`, `startTicketCheckout`, `submitRsvp`
|
|
210
|
+
from `./js/wix/registration.js`. The state machines ship too: `createEventsStore` from
|
|
211
|
+
`./js/wix/events-store.js` (`start()` once the page is up; `setActiveCategoryId`) and
|
|
212
|
+
`createRegistrationStore` from `./js/wix/registration-store.js`. No components ship — you write the
|
|
213
|
+
rendering in plain JS: one render function per surface that reads `getState()`, called from
|
|
214
|
+
`subscribe`, with the surface's controls calling the store's actions. Pages are `events.html`,
|
|
215
|
+
`event.html?slug=…`, `event-confirmation.html` — Wix static hosting serves files, not
|
|
216
|
+
directories, so the hosted checkout must return to those files: create the registration store
|
|
217
|
+
with its paths, `createRegistrationStore(event, { paths: { confirmation: "/event-confirmation.html",
|
|
218
|
+
event: "/event.html?slug=" + event.slug } })` (Wix appends `?orderNumber=&eventId=` to the
|
|
219
|
+
confirmation URL; read them there and `fetchEventById`). Set `document.title` and the meta
|
|
220
|
+
description from `title` / `shortDescription` once the event loads. The visitor token persists in
|
|
221
|
+
`localStorage` on its own; never mint per page. `npx @wix/cli@latest release` uploads `site/`.
|
|
222
|
+
|
|
223
|
+
### Wiring — server-rendered, another language (Flask, Laravel, Rails, …)
|
|
224
|
+
|
|
225
|
+
Read the reference files listed above before writing any surface.
|
|
226
|
+
|
|
227
|
+
Run `deploy.mjs events --stack static` in the project folder anyway: `js/wix/` is both the
|
|
228
|
+
browser-side code and the readable spec. Then split by where the call runs. **Reads on the
|
|
229
|
+
server:** port `js/wix/events.ts` and `events-core.ts` to your language — the same three functions
|
|
230
|
+
returning the same DTO shapes as dicts, one anonymous visitor token per process for these public
|
|
231
|
+
reads (mint and refresh per `client.ts`); the query body is literal (`filter.status $in
|
|
232
|
+
UPCOMING/STARTED`, `sort dateAndTimeSettings.startDate ASC`, a positive `paging.limit`, the
|
|
233
|
+
`fields` array — a body without the limit answers `total: N, events: []` with no error) — and
|
|
234
|
+
render the index and the event page in your templates to the contracts above, so titles and
|
|
235
|
+
dates are in the HTML. **Registering in the browser:** the tier picker and the RSVP form on
|
|
236
|
+
`./js/wix/registration-store.js` (pass the rendered `EventDetail` as JSON in a script tag, plus
|
|
237
|
+
the `paths` your routes use), the confirmation page on `./js/wix/events.js`'s `fetchEventById` —
|
|
238
|
+
exactly as the static wiring above; the browser owns the visitor's token, so the server never
|
|
239
|
+
handles per-visitor tokens. Routes stay `/events`, `/events/<slug>`, `/event-confirmation`. Add
|
|
240
|
+
your public https origin to the OAuth app's allowed domains before checkout can return.
|
|
241
|
+
|
|
242
|
+
**Pre-rendered (Frozen-Flask, Pelican, any static-site generator) → Wix-hosted.** Same port for
|
|
243
|
+
the reads, run at build time with one anonymous token; the generator emits every event page
|
|
244
|
+
(a URL generator over `fetchEvents()` — the live list is small, but raise `limit` if it isn't).
|
|
245
|
+
Run `deploy.mjs events --stack static --out <build dir>` so `js/wix/` is inside the output the
|
|
246
|
+
pages import from, point `site.outputDirectory` at that folder, `wix release`. Pages sit at
|
|
247
|
+
different depths (`/`, `/events/…`): give the templates one base path to `js/wix/` (a template
|
|
248
|
+
variable, or root-relative `/js/wix/…`), never a relative `./js/wix/` — it breaks one level down.
|
|
249
|
+
The frozen index is the first paint; the category filter still runs client-side on it through
|
|
250
|
+
`createEventsStore({ initialEvents })`. Registration runs in the browser as above. Close with the
|
|
251
|
+
live URL, the rebuild + release command, and one line for the owner: dashboard edits to events
|
|
252
|
+
reach the site when that command runs; registration is live regardless.
|
|
253
|
+
|
|
95
254
|
### Wiring — React SPA (Vite etc.)
|
|
96
255
|
|
|
97
|
-
Import `./styles/global.css` once at the app entry (needs `@tailwindcss/vite` in the vite
|
|
98
|
-
plugins — deploy added the dep). Routes: `/events` → your listing; `/events/:slug` →
|
|
99
|
-
|
|
100
|
-
`/event-confirmation` → the
|
|
256
|
+
Import `./styles/global.css` once at the app entry (needs `@tailwindcss/vite` in the vite config
|
|
257
|
+
plugins — deploy already added the dep). Routes: `/events` → your listing; `/events/:slug` →
|
|
258
|
+
`fetchEventBySlug(slug)` client-side (null → your 404 view), then your event page with the shipped
|
|
259
|
+
`EventRegistrationView`; `/event-confirmation` → the shipped `EventConfirmationView` (the checkout
|
|
260
|
+
callbacks point at that path). Deploy wrote the public client id into `wix/config.ts`; nothing
|
|
261
|
+
else to configure.
|
|
101
262
|
|
|
102
263
|
## Hard rules
|
|
103
264
|
|
|
104
265
|
- **Registration logic only through the shipped exports** — `useEventRegistration` /
|
|
105
|
-
`startTicketCheckout` / `submitRsvp` own the sequence
|
|
106
|
-
`createRedirectSession({ eventsCheckout })`, rsvpV2). Never
|
|
107
|
-
hand-build a checkout/ticket-form URL, never route it through an API
|
|
108
|
-
the whole flow runs client-side as the visitor.
|
|
266
|
+
`createRegistrationStore` / `startTicketCheckout` / `submitRsvp` own the sequence
|
|
267
|
+
(visitor-public tier read, reserve, `createRedirectSession({ eventsCheckout })`, rsvpV2). Never
|
|
268
|
+
re-derive any of it, never hand-build a checkout/ticket-form URL, never route it through an API
|
|
269
|
+
route or elevate — the whole flow runs client-side as the visitor.
|
|
109
270
|
- **Branch on `registrationType`** — never render an RSVP event with a ticket picker or a
|
|
110
|
-
ticketed event with an RSVP form; respect `registrationOpen`.
|
|
111
|
-
|
|
112
|
-
|
|
271
|
+
ticketed event with an RSVP form; respect `registrationOpen`. Where the shipped components
|
|
272
|
+
deploy that is `EventRegistrationView`.
|
|
273
|
+
- **The RSVP form is built-in** — exactly first name, last name, email; never fetch a form schema
|
|
274
|
+
or add fields.
|
|
113
275
|
- **Gate CTAs on `canCheckout` / `canRsvp`** and surface `error` — `checkout()`/`rsvp()` can
|
|
114
|
-
reject (sold out, sale ended,
|
|
115
|
-
|
|
276
|
+
reject (sold out, sale ended, closed, payment method not configured); the message is for the
|
|
277
|
+
visitor.
|
|
116
278
|
- **Confirmed states reflect REAL success**: RSVP → render only from `confirmed` (and say
|
|
117
|
-
"waitlisted", not "confirmed", for status `WAITLIST`). Tickets → the only success surface
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
-
|
|
279
|
+
"waitlisted", not "confirmed", for status `WAITLIST`). Tickets → the only success surface is
|
|
280
|
+
`/event-confirmation` with Wix's `?orderNumber=` params; a visitor merely returning to the event
|
|
281
|
+
page is NOT a success signal.
|
|
282
|
+
- **Prices and labels come from the DTOs as-is** — `priceLabel`, `TicketTier.price`; no computed
|
|
283
|
+
totals, no currency symbols of your own.
|
|
284
|
+
- Where the shipped components deploy (Astro, React): theme via the `@theme` tokens, and your
|
|
285
|
+
markup uses Tailwind utilities on the same tokens — one design system across shipped and written
|
|
286
|
+
code. Where they don't (`lib`, `static`, a port): style with whatever your stack does well, on
|
|
287
|
+
one token set of your own.
|
|
121
288
|
- Live data or an honest empty state — never mock events, tiers, or "X spots left".
|
|
122
|
-
- Keep the
|
|
289
|
+
- Keep the event page's SEO pieces exactly as shipped (Astro).
|
|
290
|
+
- **Call every hook before any conditional return.** Hooks first, branches after.
|
|
291
|
+
- **Browsing and registering need no login.** They run on the visitor session the shipped client
|
|
292
|
+
already holds; don't gate events behind sign-in unless the brief asks for accounts.
|
|
123
293
|
|
|
124
294
|
## Point the user to their dashboard
|
|
125
295
|
|
|
126
296
|
Give the owner the dashboard link plus the Events page — the deploy step's JSON printed
|
|
127
|
-
`dashboardUrl`; append `/events` for event management. **Selling paid tickets needs a premium
|
|
128
|
-
|
|
297
|
+
`dashboardUrl`; append `/events` for event management. **Selling paid tickets needs a premium plan
|
|
298
|
+
+ a connected payment method** (free/RSVP events work without) — until then `checkout()` surfaces
|
|
299
|
+
"Ticket sales aren't switched on yet"; mention it, don't treat it as a code failure.
|
|
129
300
|
|
|
130
301
|
## Seeding
|
|
131
302
|
|
|
132
|
-
Per `seed/SEED.md` — plain-data `plan.json` into `seed-events.mjs` from the project root.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
## Verify (before declaring done)
|
|
137
|
-
|
|
138
|
-
- [ ] `/events` renders live events SSR (view-source shows titles) through YOUR components;
|
|
139
|
-
empty catalog shows your honest empty state.
|
|
140
|
-
- [ ] An event page branches correctly: ticketed shows your tier picker (prices, steppers,
|
|
141
|
-
CTA disabled at zero tickets); RSVP shows the three-field form.
|
|
142
|
-
- [ ] An RSVP submits end-to-end: `rsvp()` resolves, your confirmed state renders.
|
|
143
|
-
- [ ] A ticketed checkout reserves and redirects to the Wix-hosted checkout (or surfaces the
|
|
144
|
-
"connect a payment method" message when the site has none — that's correct behavior).
|
|
145
|
-
- [ ] `/event-confirmation` renders (the honest no-order state on a direct visit).
|
|
146
|
-
- [ ] Event page view-source carries the SEO tags (Astro).
|
|
147
|
-
- [ ] Card/index/registration surfaces/home are YOUR designs on the tokens; data-layer/hook
|
|
148
|
-
files unedited.
|
|
149
|
-
- [ ] Dashboard links handed to the owner (+ the paid-tickets premium note when relevant).
|
|
303
|
+
Per `seed/SEED.md` — plain-data `plan.json` into `seed-events.mjs` from the project root. Seed
|
|
304
|
+
events that exercise the UI (a ticketed event with 2 tiers, another ticketed one, a free RSVP
|
|
305
|
+
event; future dates; an image per event) unless the brief says otherwise.
|
package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx
CHANGED
|
@@ -41,7 +41,7 @@ export default function EventConfirmationView({ eventsHref = "/events" }: { even
|
|
|
41
41
|
return (
|
|
42
42
|
<div className="mx-auto max-w-lg py-16 text-center">
|
|
43
43
|
<p className="eyebrow">Order {params.orderNumber}</p>
|
|
44
|
-
<h1 className="mt-2 text-2xl font-semibold tracking-tight">You're going
|
|
44
|
+
<h1 className="mt-2 text-2xl font-semibold tracking-tight">You're going!</h1>
|
|
45
45
|
<p className="mt-3 text-sm text-muted-foreground">
|
|
46
46
|
{event ? `${event.title}${event.dateLabel ? ` — ${event.dateLabel}` : ""}. ` : ""}
|
|
47
47
|
Your tickets are on their way to your email.
|
package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// The registration controls for one event — wire as-is (the events counterpart of storefront's
|
|
2
|
+
// OptionPicker): branches on event.registrationType — TICKETING renders the tier picker (quantity
|
|
3
|
+
// steppers clamped by limitPerCheckout, tiers not on sale unbuyable, the CTA gated by canCheckout),
|
|
4
|
+
// RSVP the built-in name+email form with its confirmed / waitlisted / declined states, EXTERNAL a
|
|
5
|
+
// link out, NONE/closed an honest closed state — on the @theme tokens. The event page's layout
|
|
6
|
+
// around it is yours; build your own on useEventRegistration only when the brief wants more.
|
|
5
7
|
// Mount client:only — it runs visitor-session SDK calls and redirects.
|
|
6
8
|
import { useEventRegistration } from "../../hooks/events/useEventRegistration";
|
|
7
9
|
import type { EventDetail } from "../../wix/events/types";
|
|
@@ -36,7 +38,7 @@ export default function EventRegistrationView({ event }: { event: EventDetail })
|
|
|
36
38
|
? "You're on the waitlist"
|
|
37
39
|
: confirmed.status === "NO"
|
|
38
40
|
? "Thanks for letting us know"
|
|
39
|
-
: "You're in!
|
|
41
|
+
: "You're in!"}
|
|
40
42
|
</p>
|
|
41
43
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
42
44
|
{confirmed.status === "WAITLIST"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Correct and complete; per the skill's model you design and build your own on useEvents.
|
|
3
3
|
import type { ComponentType, ReactNode } from "react";
|
|
4
4
|
import { useEvents } from "../../hooks/events/useEvents";
|
|
5
|
+
import { imgAttrs } from "../../wix/media";
|
|
5
6
|
import type { EventSummary } from "../../wix/events/types";
|
|
6
7
|
|
|
7
8
|
export interface LinkLikeProps {
|
|
@@ -32,9 +33,8 @@ export function EventCard({
|
|
|
32
33
|
<div className="relative aspect-[3/2] overflow-hidden rounded-lg bg-secondary">
|
|
33
34
|
{event.imageUrl && (
|
|
34
35
|
<img
|
|
35
|
-
|
|
36
|
+
{...imgAttrs(event.imageUrl, "(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw", 2 / 3)}
|
|
36
37
|
alt={event.title}
|
|
37
|
-
loading="lazy"
|
|
38
38
|
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.03]"
|
|
39
39
|
/>
|
|
40
40
|
)}
|