@wix/agent-skills 1.16.0 → 1.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +72 -8
- package/skills/wix-app/references/CODE_QUALITY.md +0 -1
- package/skills/wix-app/references/DASHBOARD_MODAL.md +21 -14
- package/skills/wix-app/references/DASHBOARD_PAGE.md +30 -184
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +19 -3
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +172 -0
- package/skills/wix-app/references/dashboard-page/WDS_LAYOUT.md +187 -0
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +12 -7
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +31 -2
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +6 -1
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +60 -0
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +6 -6
- package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +167 -0
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -2
- package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +187 -0
- package/skills/wix-app/scripts/scan-a11y-code.cjs +777 -0
- package/skills/wix-app/scripts/scan-a11y-eslint.cjs +201 -0
- package/skills/wix-base44-connector/SKILL.md +185 -0
- package/skills/wix-base44-connector/scripts/utils.js +212 -0
- package/skills/wix-design-system/SKILL.md +19 -15
- package/skills/wix-docs/SKILL.md +75 -1
- package/skills/wix-headless/references/CAPABILITIES.md +16 -3
- package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
- package/skills/wix-headless/references/SEED.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +560 -102
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +210 -143
- package/skills/wix-manage/SKILL.md +20 -17
- package/skills/wix-manage/references/accessibility/scan-site-accessibility.md +98 -0
- package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +1 -2
- package/skills/wix-manage/references/domains/domain-search-purchase-and-connect.md +2603 -0
- package/skills/wix-manage/references/events/create-wix-event.md +112 -90
- package/skills/wix-manage/references/events/manage-wix-events.md +33 -94
- package/skills/wix-manage/references/sites/create-headless-site.md +1 -0
- package/skills/wix-manage/references/sites/manage-oauth-apps.md +154 -0
- package/skills/wix-manage/references/sites/read-site-context.md +125 -0
- package/skills/wix-manage/references/sites/site-import.md +67 -15
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +273 -0
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +1 -1
- package/skills/wix-vibe-headless/SKILL.md +2 -1
- package/skills/wix-vibe-headless/install/deploy.cjs +63 -3
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +104 -0
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +74 -0
- package/skills/wix-vibe-headless/platforms/base44.md +15 -40
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/blog/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +1 -2
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +7 -12
- package/skills/wix-vibe-headless/references/events/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +1 -1
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +5 -3
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +75 -13
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +29 -86
- package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +3 -8
- package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +78 -105
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +92 -0
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useVariantOptions.js +76 -0
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +1 -1
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +44 -5
- package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +0 -326
- package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +0 -115
- package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +0 -264
- package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +0 -37
- package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +0 -86
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +0 -119
- package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +0 -103
- package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +0 -76
- package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +0 -82
- package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +0 -89
- package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +0 -124
- package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +0 -91
- package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +0 -71
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +0 -70
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +0 -73
- package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +0 -98
- package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +0 -278
- package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +0 -94
- package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +0 -43
- package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +0 -66
- package/skills/wix-app/references/auto-patterns-dashboard/views.md +0 -76
- package/skills/wix-app/scripts/generate-auto-patterns.js +0 -510
- package/skills/wix-manage/references/domains/domain-search-and-purchase.md +0 -437
- package/skills/wix-manage/references/stores/create-product-from-image.md +0 -1031
- package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +0 -623
- package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +0 -106
|
@@ -33,11 +33,11 @@ so you don't need to open them:**
|
|
|
33
33
|
| `components/CollectionCard.jsx`, `CollectionGrid.jsx` | collections listing UI (grid + card, with empty state) |
|
|
34
34
|
| `components/ProjectCard.jsx`, `ProjectGrid.jsx` | projects listing UI (grid + card, with empty state) |
|
|
35
35
|
| `components/ProjectMedia.jsx` | one gallery item — branches on `item.type` (IMAGE / VIDEO) |
|
|
36
|
-
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP
|
|
36
|
+
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
|
|
37
37
|
| `pages/Portfolio.jsx` | collections gallery route (`/portfolio`) |
|
|
38
38
|
| `pages/CollectionPage.jsx` | collection page route (`/collection/:slug`) |
|
|
39
39
|
| `pages/ProjectDetail.jsx` | project detail route (`/project/:slug`) |
|
|
40
|
-
| `rest/wix-config.js` |
|
|
40
|
+
| `rest/wix-config.js` | the two ids, written by the install step |
|
|
41
41
|
| `rest/wix-client.js` + `rest/wix-portfolio.js` | REST transport + portfolio read helpers |
|
|
42
42
|
|
|
43
43
|
They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
|
|
@@ -47,11 +47,8 @@ a real fallback — a runtime error, or a field the snippets don't cover (see "F
|
|
|
47
47
|
end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
|
|
48
48
|
`references/portfolio/app/` → `src/`.)
|
|
49
49
|
|
|
50
|
-
## STEP 2 — Credentials
|
|
51
|
-
Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
|
|
52
|
-
one place both ids live.
|
|
53
50
|
|
|
54
|
-
## STEP
|
|
51
|
+
## STEP 2 — Theme (nothing to style on the shipped components)
|
|
55
52
|
The shipped components carry **no palette of their own** — they render from base44's design tokens in
|
|
56
53
|
`src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`, `--muted`,
|
|
57
54
|
`--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`, `text-foreground`,
|
|
@@ -59,10 +56,10 @@ The shipped components carry **no palette of their own** — they render from ba
|
|
|
59
56
|
are **already set to the brand by the design phase**, so the shipped pages are themed with zero work
|
|
60
57
|
here. To adjust the palette, edit `index.css` (`:root` **and** `.dark`) — the base44 way; **never add
|
|
61
58
|
a parallel theme file (e.g. a `theme.css`) or restyle the shipped JSX.** Build the Home/Header you add
|
|
62
|
-
(STEP
|
|
59
|
+
(STEP 3) from the **same** base44 tokens/classes so it matches automatically. A dark brand is just
|
|
63
60
|
base44's dark palette in `index.css` — no per-component work.
|
|
64
61
|
|
|
65
|
-
## STEP
|
|
62
|
+
## STEP 3 — Wire routes (surgical `find_replace` on `src/App.jsx`, never a rewrite)
|
|
66
63
|
**No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
|
|
67
64
|
`App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
|
|
68
65
|
replace it. Portfolio is read-only with no cross-page state, so there's **no provider to wrap** (no
|
|
@@ -122,7 +119,7 @@ function Layout() {
|
|
|
122
119
|
|
|
123
120
|
## What you build (not shipped)
|
|
124
121
|
The **home / landing page**, the **`Header`** and a **`Footer`** — the two you drop into the `Layout`
|
|
125
|
-
(STEP
|
|
122
|
+
(STEP 3) so they wrap every route — plus the overall brand story, styled with the same base44
|
|
126
123
|
tokens/classes. **Compose the shipped pieces** — a featured strip is just `queryCollections` (or
|
|
127
124
|
`queryProjects`) + the shipped `CollectionGrid` / `ProjectGrid`; the nav is a link to `/portfolio`:
|
|
128
125
|
|
|
@@ -204,9 +201,8 @@ links its own reference page inline; the whole area is here:
|
|
|
204
201
|
- Portfolio (collections, projects, project items): https://dev.wix.com/docs/api-reference/business-solutions/portfolio.md
|
|
205
202
|
|
|
206
203
|
## Hard rules
|
|
207
|
-
- Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
|
|
208
204
|
- Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
|
|
209
|
-
- Header/footer live in a `Layout` around `<Outlet/>` (STEP
|
|
205
|
+
- Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped
|
|
210
206
|
`Portfolio`/`CollectionPage`/`ProjectDetail` to add chrome.
|
|
211
207
|
- The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your
|
|
212
208
|
`Header` is plain in-flow markup (not `position:fixed`).
|
|
@@ -16,7 +16,7 @@ first, then thread their real ids into each project.
|
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
18
|
// build-time exec_tool
|
|
19
|
-
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
19
|
+
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
20
20
|
const fs = require("fs");
|
|
21
21
|
// exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
|
|
22
22
|
const seed = (() => { const m = { exports: {} };
|
|
@@ -36,11 +36,11 @@ map, so you don't need to open them:**
|
|
|
36
36
|
| `components/PlanCard.jsx` | plan card — name, description, price, perks, Subscribe (when `buyable`) |
|
|
37
37
|
| `components/PlanGrid.jsx` | responsive plans grid + empty state |
|
|
38
38
|
| `components/OrderCard.jsx` | one member order row for the "my plans" list |
|
|
39
|
-
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP
|
|
39
|
+
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
|
|
40
40
|
| `pages/Plans.jsx` | the plans-table route (`/plans`) — grid + paging + checkout |
|
|
41
41
|
| `pages/PlanDetail.jsx` | the plan-detail route (`/plans/:slug`) — perks, terms, checkout |
|
|
42
42
|
| `pages/MyPlans.jsx` | the member's memberships + post-checkout confirmation (`/my-plans`) |
|
|
43
|
-
| `rest/wix-config.js` |
|
|
43
|
+
| `rest/wix-config.js` | the two ids, written by the install step |
|
|
44
44
|
| `rest/wix-client.js` + `rest/wix-pricing-plans.js` | REST transport + plans/orders/checkout helpers |
|
|
45
45
|
|
|
46
46
|
They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
|
|
@@ -50,11 +50,8 @@ a real fallback — a runtime error, or a field the snippets don't cover (see "F
|
|
|
50
50
|
end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
|
|
51
51
|
`references/pricing-plans/app/` → `src/`.)
|
|
52
52
|
|
|
53
|
-
## STEP 2 — Credentials
|
|
54
|
-
Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
|
|
55
|
-
one place both ids live.
|
|
56
53
|
|
|
57
|
-
## STEP
|
|
54
|
+
## STEP 2 — Theme (nothing to style on the shipped components)
|
|
58
55
|
The shipped components carry **no palette of their own** — they render from base44's design tokens
|
|
59
56
|
in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`,
|
|
60
57
|
`--muted`, `--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`,
|
|
@@ -62,10 +59,10 @@ in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `
|
|
|
62
59
|
`font-display`). Those tokens are **already set to the brand by the design phase**, so the shipped
|
|
63
60
|
pages are themed with zero work here. To adjust the palette, edit `index.css` (`:root` **and**
|
|
64
61
|
`.dark`) — the base44 way; **never add a parallel theme file (e.g. a `theme.css`) or restyle the
|
|
65
|
-
shipped JSX.** Build the Home/Header you add (STEP
|
|
62
|
+
shipped JSX.** Build the Home/Header you add (STEP 3) from the **same** base44 tokens/classes so it
|
|
66
63
|
matches automatically. A dark brand is just base44's dark palette in `index.css` — no per-component work.
|
|
67
64
|
|
|
68
|
-
## STEP
|
|
65
|
+
## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
|
|
69
66
|
**No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
|
|
70
67
|
`App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
|
|
71
68
|
replace it. Pricing Plans needs **no** cross-page provider (checkout is a redirect, not client
|
|
@@ -125,7 +122,7 @@ function Layout() {
|
|
|
125
122
|
|
|
126
123
|
## What you build (not shipped)
|
|
127
124
|
The **home / landing page**, the **`Header`** and a **`Footer`** — the two you drop into the
|
|
128
|
-
`Layout` (STEP
|
|
125
|
+
`Layout` (STEP 3) so they wrap every route — plus the overall brand story, styled with the same
|
|
129
126
|
base44 tokens/classes. **Compose the shipped pieces** — a "featured plans" strip is just `queryPlans` +
|
|
130
127
|
the shipped `PlanGrid`; the nav is links to `/plans` and `/my-plans`:
|
|
131
128
|
|
|
@@ -214,9 +211,8 @@ Fallback only — when you hit an error or need something not shown here: read t
|
|
|
214
211
|
file under `src/`, or look it up via the **`wix-docs`** skill.
|
|
215
212
|
|
|
216
213
|
## Hard rules
|
|
217
|
-
- Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
|
|
218
214
|
- Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
|
|
219
|
-
- Header/footer live in a `Layout` around `<Outlet/>` (STEP
|
|
215
|
+
- Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped
|
|
220
216
|
`Plans`/`PlanDetail`/`MyPlans` to add chrome.
|
|
221
217
|
- The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your
|
|
222
218
|
`Header` is plain in-flow markup (not `position:fixed`).
|
|
@@ -9,7 +9,7 @@ plain data.
|
|
|
9
9
|
|
|
10
10
|
```js
|
|
11
11
|
// build-time exec_tool
|
|
12
|
-
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
12
|
+
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
// exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
|
|
15
15
|
const seed = (() => { const m = { exports: {} };
|
|
@@ -35,9 +35,9 @@ so you don't need to open them:**
|
|
|
35
35
|
| `components/ItemDialog.jsx` | dish detail modal — description, price/variants, modifier groups (display), quantity, add-to-order |
|
|
36
36
|
| `components/OrderCartButton.jsx` | header order **icon** button with a live-count badge |
|
|
37
37
|
| `components/OrderCartDrawer.jsx` | slide-over order cart (mount once; opens from `useOrderCart`) |
|
|
38
|
-
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP
|
|
38
|
+
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
|
|
39
39
|
| `pages/Menu.jsx`, `pages/Reservations.jsx` | the two shipped routes (`/menu`, `/reservations`) |
|
|
40
|
-
| `rest/wix-config.js` |
|
|
40
|
+
| `rest/wix-config.js` | the two ids, written by the install step |
|
|
41
41
|
| `rest/wix-client.js` | REST transport + visitor-token mint/refresh (the refresh token IS the cart identity) |
|
|
42
42
|
| `rest/wix-restaurants-menu.js` | menu read helpers — `getFullMenu` (the assembled tree; start here) + raw `list*` |
|
|
43
43
|
| `rest/wix-restaurants-ordering.js` | ordering — operations, add/update/remove, `checkout` |
|
|
@@ -50,11 +50,8 @@ real fallback — a runtime error, or a field the snippets don't cover (see "Fal
|
|
|
50
50
|
end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
|
|
51
51
|
`references/restaurants/app/` → `src/`.)
|
|
52
52
|
|
|
53
|
-
## STEP 2 — Credentials
|
|
54
|
-
Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
|
|
55
|
-
one place both ids live.
|
|
56
53
|
|
|
57
|
-
## STEP
|
|
54
|
+
## STEP 2 — Theme (nothing to style on the shipped components)
|
|
58
55
|
The shipped components (menu, item dialog, order cart, reservations) carry **no palette of their
|
|
59
56
|
own** — they render from base44's design tokens in `src/index.css` (`:root`/`.dark`: `--background`,
|
|
60
57
|
`--foreground`, `--card`, `--primary`, `--muted`, `--border`, `--radius`, `--font-*`) via shadcn
|
|
@@ -62,11 +59,11 @@ Tailwind classes (`bg-card`, `text-foreground`, `bg-primary`, `text-muted-foregr
|
|
|
62
59
|
`border-border`, `rounded-lg`, `font-display`). Those tokens are **already set to the brand by the
|
|
63
60
|
design phase**, so the shipped pages are themed with zero work here. To adjust the palette, edit
|
|
64
61
|
`index.css` (`:root` **and** `.dark`) — the base44 way; **never add a parallel theme file (e.g. a
|
|
65
|
-
`theme.css`) or restyle the shipped JSX.** Build the Home/Header you add (STEP
|
|
62
|
+
`theme.css`) or restyle the shipped JSX.** Build the Home/Header you add (STEP 3) from the **same**
|
|
66
63
|
base44 tokens/classes so it matches automatically. A dark brand is just base44's dark palette in
|
|
67
64
|
`index.css` — no per-component work.
|
|
68
65
|
|
|
69
|
-
## STEP
|
|
66
|
+
## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
|
|
70
67
|
**No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
|
|
71
68
|
`App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
|
|
72
69
|
replace it.
|
|
@@ -129,7 +126,7 @@ function Layout() {
|
|
|
129
126
|
|
|
130
127
|
## What you build (not shipped)
|
|
131
128
|
The **home / landing page**, the **`Header`** (mount `<OrderCartButton/>` in it) and a **`Footer`** —
|
|
132
|
-
the two you drop into the `Layout` (STEP
|
|
129
|
+
the two you drop into the `Layout` (STEP 3) so they wrap every route — plus the overall brand story,
|
|
133
130
|
styled with the same base44 tokens/classes. The nav is an `<OrderCartButton/>` (a clean order-**icon**
|
|
134
131
|
button with a live-count badge — render it as-is, don't wrap it in your own text button) + links to
|
|
135
132
|
`/menu` and `/reservations`:
|
|
@@ -229,9 +226,8 @@ Fallback only — when you hit an error or need something not shown here: read t
|
|
|
229
226
|
file under `src/`, or look it up via the **`wix-docs`** skill.
|
|
230
227
|
|
|
231
228
|
## Hard rules
|
|
232
|
-
- Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
|
|
233
229
|
- Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
|
|
234
|
-
- Header/footer live in a `Layout` around `<Outlet/>` (STEP
|
|
230
|
+
- Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped `Menu`/`Reservations` to add chrome.
|
|
235
231
|
- The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your `Header` is plain in-flow markup (not `position:fixed`).
|
|
236
232
|
- Order through the shipped cart: `addItem()` → `checkout()` (redirect-session) — never a hand-built `/checkout`, ordering, or reservation URL.
|
|
237
233
|
- Reservations: offer only `AVAILABLE` slots; pass the hold's `revision` into reserve; `firstName` + `phone` (E.164) are mandatory.
|
|
@@ -17,7 +17,7 @@ across exec calls. Online ordering and table reservations run only when the plan
|
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
// build-time exec_tool
|
|
20
|
-
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
20
|
+
const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
|
|
21
21
|
const fs = require("fs");
|
|
22
22
|
// exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
|
|
23
23
|
const seed = (() => { const m = { exports: {} };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Preview-only banner linking the running app to its Wix Business Manager (the back office).
|
|
2
2
|
// Renders while the app is previewed or run locally, never on the published site, and is
|
|
3
3
|
// dismissible (persisted per site).
|
|
4
|
-
// Mount it at the top of your Layout's fixed region, ABOVE <Header/> (see INSTRUCTIONS STEP
|
|
4
|
+
// Mount it at the top of your Layout's fixed region, ABOVE <Header/> (see INSTRUCTIONS STEP 3) —
|
|
5
5
|
// banner + header ride together as one fixed block, so nothing drifts or gaps on scroll.
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import { WIX_METASITE_ID } from "@/rest/wix-config";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
// Wix credentials
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Wix credentials. The install step writes this file: deploy.cjs --client-id … --metasite-id …
|
|
2
|
+
// If you're seeing the placeholders below, re-run that command with the ids from the prompt rather
|
|
3
|
+
// than typing them in here.
|
|
4
|
+
// Safe to commit: WIX_CLIENT_ID is a public, buyer-facing id (it only mints anonymous visitor
|
|
5
|
+
// tokens); WIX_METASITE_ID just identifies the site.
|
|
4
6
|
// wix-client.js and wix-manage-banner.js read their ids from here.
|
|
5
7
|
export const WIX_CLIENT_ID = "<YOUR-CLIENT-ID>";
|
|
6
8
|
export const WIX_METASITE_ID = "<YOUR-METASITE-ID>";
|
|
@@ -24,15 +24,18 @@ so you don't need to open them:**
|
|
|
24
24
|
| `context/CartContext.jsx` | `useCart()` provider: server cart, add/update/remove, checkout |
|
|
25
25
|
| `hooks/useProductDetail.js` | PDP data — product + variant resolution for a slug, plus load/add state |
|
|
26
26
|
| `hooks/useShop.js` | catalog listing — category menu, cursor paging, sort, failure state |
|
|
27
|
-
| `
|
|
27
|
+
| `hooks/useProductCard.js` | headless data layer for a grid tile — returns `leftBadges`, `promoBadge`, `priceDisplay`, `compareAtDisplay`, `colors`, `optionLabel`, `isQuickAddable`, `image`, `hoverImage`; **always use this to build your own product card UI on the grid** |
|
|
28
|
+
| `components/ProductCard.jsx` | reference implementation of a grid tile built on `useProductCard` — read it for inspiration, build your own component rather than using it directly |
|
|
29
|
+
| `components/ProductGrid.jsx` | reference grid layout (2-col mobile → auto-fill desktop) with skeleton + empty state — read it for the skeleton/empty-state patterns, build your own layout rather than using it directly |
|
|
28
30
|
| `components/ProductGallery.jsx` | PDP main image + thumbnails |
|
|
29
31
|
| `lib/storeImage.js` | `productImage()` / `productGallery()` / `storeImage()` — normalise Wix image urls |
|
|
30
32
|
| `components/CartButton.jsx` | header cart **icon** button with a live-count badge |
|
|
31
33
|
| `components/CartDrawer.jsx` | slide-over cart (mount once; opens from `useCart`) |
|
|
32
|
-
| `
|
|
33
|
-
| `components/
|
|
34
|
+
| `hooks/useVariantOptions.js` | headless data layer for options/modifiers — returns `optionGroups` + `modifierGroups` (normalised, render-agnostic); **always use this to build your own variant UI on the PDP** |
|
|
35
|
+
| `components/VariantPicker.jsx` | reference implementation of a variant selector built on `useVariantOptions` (pills + colour swatches) — read it for inspiration, but build your own component rather than using it directly |
|
|
36
|
+
| `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
|
|
34
37
|
| `pages/Shop.jsx`, `pages/ProductDetail.jsx` | the two shipped routes (`/shop`, `/product/:slug`) |
|
|
35
|
-
| `rest/wix-config.js` |
|
|
38
|
+
| `rest/wix-config.js` | the two ids, written by the install step |
|
|
36
39
|
| `rest/wix-client.js` + `rest/wix-store-*.js` | REST transport + catalog/cart helpers |
|
|
37
40
|
|
|
38
41
|
They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
|
|
@@ -42,11 +45,8 @@ real fallback — a runtime error, or a field the snippets don't cover (see "Fal
|
|
|
42
45
|
end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
|
|
43
46
|
`references/storefront/app/` → `src/`.)
|
|
44
47
|
|
|
45
|
-
## STEP 2 — Credentials
|
|
46
|
-
Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
|
|
47
|
-
one place both ids live.
|
|
48
48
|
|
|
49
|
-
## STEP
|
|
49
|
+
## STEP 2 — Theme (nothing to style on the shipped components)
|
|
50
50
|
The shipped components carry **no palette of their own** — they render from base44's design tokens
|
|
51
51
|
in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`,
|
|
52
52
|
`--muted`, `--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`,
|
|
@@ -54,10 +54,10 @@ in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `
|
|
|
54
54
|
`font-display`). Those tokens are **already set to the brand by the design phase**, so the shipped
|
|
55
55
|
pages are themed with zero work here. To adjust the palette, edit `index.css` (`:root` **and**
|
|
56
56
|
`.dark`) — the base44 way; **never add a parallel theme file (e.g. a `theme.css`) or restyle the
|
|
57
|
-
shipped JSX.** Build the Home/Header you add (STEP
|
|
57
|
+
shipped JSX.** Build the Home/Header you add (STEP 3) from the **same** base44 tokens/classes so it
|
|
58
58
|
matches automatically. A dark brand is just base44's dark palette in `index.css` — no per-component work.
|
|
59
59
|
|
|
60
|
-
## STEP
|
|
60
|
+
## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
|
|
61
61
|
**No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
|
|
62
62
|
`App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
|
|
63
63
|
replace it.
|
|
@@ -73,6 +73,69 @@ replace it.
|
|
|
73
73
|
banner is dismissed.
|
|
74
74
|
- Routes under the Layout: `/shop` → `Shop`, `/product/:slug` → `ProductDetail` (both shipped, as-is).
|
|
75
75
|
**You add `/` → your own Home** page.
|
|
76
|
+
- **Build your own variant selector on the PDP — this is required, not optional, and it's your chance to be creative.** `ProductDetail.jsx` ships with a `VariantPicker` import — remove it and replace with your own component built on `useVariantOptions`. Design the controls to fit the brief: the business type, the tone, the audience. A fashion brand might want large colour swatches and a size chart link; a tech store might want a compact dropdown. `VariantPicker.jsx` is in `src/` for reference — read it, don't use it:
|
|
77
|
+
- **Build your own product card for the grid — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships with a `ProductCard` import — replace it with your own component built on `useProductCard`. The hook hands you everything the tile needs (badges, price display, colour dots, quick-add flag, images) — you decide the layout, shape, hover behaviour, and CTA style. A lifestyle brand might want full-bleed images with an overlay gradient; a tech store might want a compact horizontal list item. `ProductCard.jsx` is in `src/` for reference — read it, don't use it:
|
|
78
|
+
- **Build your own grid layout — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships as a reference (2-col mobile → auto-fill desktop, 220px min) — replace it with an arrangement that fits the brief. A curated boutique might want a 3-col asymmetric editorial layout; a high-volume store might want a dense 4-col grid; a featured strip on the home page might want horizontal scroll. Keep the skeleton and empty-state patterns from `ProductGrid.jsx` (copy them into your own component) — the states themselves are correct, just the layout is yours to choose.
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
import { Link } from "react-router-dom";
|
|
82
|
+
import { useCart } from "@/context/CartContext";
|
|
83
|
+
import { useProductCard } from "@/hooks/useProductCard";
|
|
84
|
+
|
|
85
|
+
export default function MyProductCard({ product }) {
|
|
86
|
+
const { addToCart } = useCart();
|
|
87
|
+
const {
|
|
88
|
+
isSoldOut, isPreorder,
|
|
89
|
+
leftBadges, // [{ type: 'pre-order'|'sold-out'|'limited-stock', label }] — render left side of image
|
|
90
|
+
promoBadge, // { type: 'discount'|'ribbon', label } | null — render right side
|
|
91
|
+
priceDisplay, // "€10" or "€10 – €20" (range when variants differ)
|
|
92
|
+
compareAtDisplay, // original price string | null
|
|
93
|
+
colors, // hex strings → render as dots (slice to how many you want)
|
|
94
|
+
optionLabel, // "3 sizes · 2 materials" or empty string
|
|
95
|
+
isQuickAddable, // true for single-variant, in-stock products
|
|
96
|
+
image, // primary image URL | null
|
|
97
|
+
hoverImage, // second image URL | null
|
|
98
|
+
} = useProductCard(product);
|
|
99
|
+
|
|
100
|
+
// Then render however you want:
|
|
101
|
+
return (
|
|
102
|
+
<div>
|
|
103
|
+
{/* image, badges, price, colour dots, quick-add or "Choose options" CTA */}
|
|
104
|
+
{isQuickAddable && <button onClick={() => addToCart(product.id)}>Quick add</button>}
|
|
105
|
+
{!isQuickAddable && !isSoldOut && <Link to={`/product/${product.slug}`}>Choose options</Link>}
|
|
106
|
+
{isSoldOut && isPreorder && <Link to={`/product/${product.slug}`}>Pre-order</Link>}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```jsx
|
|
113
|
+
import { useVariantOptions } from "@/hooks/useVariantOptions";
|
|
114
|
+
|
|
115
|
+
// options/modifiers/selectedOptions/modifierValues come from useProductDetail:
|
|
116
|
+
const { optionGroups, modifierGroups } = useVariantOptions(options, modifiers, selectedOptions, modifierValues);
|
|
117
|
+
|
|
118
|
+
// optionGroups: [{ id, name, isColor, choices: [{ choiceId, name, colorCode, isColorSwatch, inStock, selected }] }]
|
|
119
|
+
// modifierGroups: [{ key, name, mandatory, type: 'choices'|'text', choices?: [{ key, name, selected }], value?: string }]
|
|
120
|
+
|
|
121
|
+
// Then render however you want:
|
|
122
|
+
optionGroups.map((group) =>
|
|
123
|
+
group.choices.map((c) =>
|
|
124
|
+
c.isColorSwatch
|
|
125
|
+
? <MySwatch key={c.choiceId} color={c.colorCode} active={c.selected} disabled={!c.inStock}
|
|
126
|
+
onClick={() => selectOption(group.id, c.choiceId)} />
|
|
127
|
+
: <MyPill key={c.choiceId} active={c.selected} disabled={!c.inStock}
|
|
128
|
+
onClick={() => selectOption(group.id, c.choiceId)}>{c.name}</MyPill>
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
modifierGroups.map((m) =>
|
|
132
|
+
m.type === "text"
|
|
133
|
+
? <MyInput key={m.key} label={m.name} value={m.value} onChange={(v) => setModifier(m.key, v)} />
|
|
134
|
+
: m.choices.map((c) =>
|
|
135
|
+
<MyPill key={c.key} active={c.selected} onClick={() => setModifier(m.key, c.key)}>{c.name}</MyPill>
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
```
|
|
76
139
|
|
|
77
140
|
```jsx
|
|
78
141
|
import { useRef, useState, useEffect } from "react";
|
|
@@ -120,7 +183,7 @@ function Layout() {
|
|
|
120
183
|
|
|
121
184
|
## What you build (not shipped)
|
|
122
185
|
The **home / landing page**, the **`Header`** (mount `<CartButton/>` in it) and a **`Footer`** — the
|
|
123
|
-
two you drop into the `Layout` (STEP
|
|
186
|
+
two you drop into the `Layout` (STEP 3) so they wrap every route — plus the overall brand story,
|
|
124
187
|
styled from the same base44 tokens/classes. **Compose the shipped pieces** — a
|
|
125
188
|
featured strip is just `queryProducts` + the shipped `ProductGrid`; the nav is a `<CartButton/>`
|
|
126
189
|
(a clean cart-**icon** button with a live-count badge — render it as-is, don't wrap it in your own
|
|
@@ -243,9 +306,8 @@ reference page inline; these are the areas they sit in:
|
|
|
243
306
|
- Headless redirect session (hosted checkout): https://dev.wix.com/docs/api-reference/business-management/headless/redirects.md
|
|
244
307
|
|
|
245
308
|
## Hard rules
|
|
246
|
-
- Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
|
|
247
309
|
- Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
|
|
248
|
-
- Header/footer live in a `Layout` around `<Outlet/>` (STEP
|
|
310
|
+
- Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped `Shop`/`ProductDetail` to add chrome.
|
|
249
311
|
- The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your `Header` is plain in-flow markup (not `position:fixed`).
|
|
250
312
|
- Checkout goes through the shipped cart (redirect-session) — never a hand-built `/checkout` URL.
|
|
251
313
|
- Render live Wix data or the shipped empty state — never mock products.
|
|
@@ -1,72 +1,29 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// grid, the PDP gallery and the cart all normalise them the same way.
|
|
5
|
-
//
|
|
6
|
-
// Everything here comes from the list query — the tile costs no extra request. What the list query
|
|
7
|
-
// does NOT carry is a stock COUNT (inventory has availabilityStatus only), so "Limited stock" is as
|
|
8
|
-
// precise as a tile can honestly get; "Only 2 left" would need per-variant inventory per product.
|
|
1
|
+
// Reference implementation of a product grid tile built on useProductCard.
|
|
2
|
+
// Styled with base44 design tokens (shadcn Tailwind classes: bg-card / text-foreground / border-border).
|
|
3
|
+
// Build your own card on useProductCard instead — read this for inspiration, don't use it directly.
|
|
9
4
|
import { useState } from "react";
|
|
10
5
|
import { Link } from "react-router-dom";
|
|
11
|
-
import { productGallery, productImage } from "@/lib/storeImage";
|
|
12
6
|
import { useCart } from "@/context/CartContext";
|
|
7
|
+
import { useProductCard } from "@/hooks/useProductCard";
|
|
13
8
|
|
|
14
|
-
// Percent off, from the raw amounts — the formatted strings ("€129.00") can't be subtracted.
|
|
15
|
-
function discountPercent(product) {
|
|
16
|
-
const now = Number(product?.actualPriceRange?.minValue?.amount);
|
|
17
|
-
const was = Number(product?.compareAtPriceRange?.minValue?.amount);
|
|
18
|
-
if (!now || !was || was <= now) return null;
|
|
19
|
-
return Math.round(((was - now) / was) * 100);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// What the buyer gets to choose, previewed on the tile: colour options become real dots (the colours
|
|
23
|
-
// say more than "3 colors" could), everything else becomes "4 sizes". Pluralised from the option's own
|
|
24
|
-
// name, so the wording follows the merchant's catalogue rather than a hardcoded English word.
|
|
25
|
-
function optionsPreview(product) {
|
|
26
|
-
const labels = [], colors = [];
|
|
27
|
-
for (const o of product?.options || []) {
|
|
28
|
-
const choices = (o.choicesSettings?.choices || []).filter((c) => c.visible !== false);
|
|
29
|
-
if (!choices.length) continue;
|
|
30
|
-
const swatches = choices.map((c) => c.colorCode).filter(Boolean);
|
|
31
|
-
if (swatches.length) { colors.push(...swatches); continue; }
|
|
32
|
-
const n = o.name.toLowerCase();
|
|
33
|
-
labels.push(`${choices.length} ${choices.length === 1 || n.endsWith("s") ? n : `${n}s`}`);
|
|
34
|
-
}
|
|
35
|
-
return { label: labels.join(" · "), colors };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// One rule for every chip on the tile, so a new badge has an obvious home instead of a fresh colour:
|
|
39
|
-
// promotional shouts in the brand colour, informational sits quietly on a card surface, and only a
|
|
40
|
-
// blocking state is destructive. Each background travels with its own foreground token.
|
|
41
9
|
const badge = "px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide rounded-full";
|
|
42
|
-
const badgePromo
|
|
43
|
-
const badgeInfo
|
|
44
|
-
const badgeBlocked
|
|
10
|
+
const badgePromo = `${badge} bg-primary text-primary-foreground`;
|
|
11
|
+
const badgeInfo = `${badge} bg-card text-foreground border border-border`;
|
|
12
|
+
const badgeBlocked = `${badge} bg-destructive text-destructive-foreground`;
|
|
13
|
+
|
|
14
|
+
const badgeClass = { "pre-order": badgeInfo, "sold-out": badgeBlocked, "limited-stock": badgeInfo };
|
|
45
15
|
|
|
46
16
|
export default function ProductCard({ product }) {
|
|
47
17
|
const { addToCart } = useCart();
|
|
48
18
|
const [adding, setAdding] = useState(false);
|
|
49
19
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const status = product?.inventory?.availabilityStatus;
|
|
57
|
-
const soldOut = status === "OUT_OF_STOCK";
|
|
58
|
-
const preorder = product?.inventory?.preorderStatus === "ENABLED";
|
|
59
|
-
const discount = discountPercent(product);
|
|
60
|
-
// A merchant-set ribbon ("New", "Best Seller") is the catalogue's own badge, so it beats anything
|
|
61
|
-
// derived here — a "new" label computed from createdDate would flag the whole catalogue at once
|
|
62
|
-
// right after seeding. Suppressed when a discount badge is showing, since that ribbon is "Sale".
|
|
63
|
-
const ribbon = product?.ribbon?.name;
|
|
64
|
-
const { label: optionLabel, colors } = optionsPreview(product);
|
|
65
|
-
const hasOptions = (product?.options?.length || 0) > 0;
|
|
20
|
+
const {
|
|
21
|
+
isSoldOut, isPreorder, leftBadges, promoBadge,
|
|
22
|
+
priceDisplay, compareAtDisplay,
|
|
23
|
+
colors, optionLabel,
|
|
24
|
+
isQuickAddable, image, hoverImage,
|
|
25
|
+
} = useProductCard(product);
|
|
66
26
|
|
|
67
|
-
// No options means a single variant, which the cart resolves from the product id alone — so the
|
|
68
|
-
// tile can add straight to the cart with no extra request. The drawer opening is the confirmation
|
|
69
|
-
// (and carries the reason when it fails), which is why there's no "Added ✓" state to fake here.
|
|
70
27
|
const quickAdd = async () => {
|
|
71
28
|
setAdding(true);
|
|
72
29
|
await addToCart(product.id);
|
|
@@ -79,11 +36,8 @@ export default function ProductCard({ product }) {
|
|
|
79
36
|
<Link to={`/product/${product.slug}`} aria-label={product.name} className="block w-full h-full">
|
|
80
37
|
{image ? (
|
|
81
38
|
<>
|
|
82
|
-
{/* Fade out only when there IS a second shot behind it — a one-photo product would
|
|
83
|
-
otherwise hover to an empty frame. */}
|
|
84
39
|
<img src={image} alt={product.name} loading="lazy"
|
|
85
40
|
className={`w-full h-full object-cover ${hoverImage ? "transition-opacity duration-300 md:group-hover:opacity-0" : ""}`} />
|
|
86
|
-
{/* Second catalogue shot on hover. `hidden` below md so phones never fetch it. */}
|
|
87
41
|
{hoverImage && (
|
|
88
42
|
<img src={hoverImage} alt="" aria-hidden="true" loading="lazy"
|
|
89
43
|
className="hidden md:block absolute inset-0 w-full h-full object-cover opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
|
|
@@ -99,51 +53,40 @@ export default function ProductCard({ product }) {
|
|
|
99
53
|
</Link>
|
|
100
54
|
|
|
101
55
|
<div className="absolute top-2 left-2 flex flex-col items-start gap-1 pointer-events-none">
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
56
|
+
{leftBadges.map((b) => (
|
|
57
|
+
<span key={b.type} className={badgeClass[b.type]}>{b.label}</span>
|
|
58
|
+
))}
|
|
105
59
|
</div>
|
|
106
|
-
{/* Discount and ribbon are mutually exclusive, so both can claim the promotional slot. */}
|
|
107
60
|
<div className="absolute top-2 right-2 pointer-events-none">
|
|
108
|
-
{
|
|
109
|
-
: ribbon ? <span className={badgePromo}>{ribbon}</span> : null}
|
|
61
|
+
{promoBadge && <span className={badgePromo}>{promoBadge.label}</span>}
|
|
110
62
|
</div>
|
|
111
63
|
|
|
112
|
-
{
|
|
113
|
-
{(!soldOut || preorder) && (
|
|
64
|
+
{(!isSoldOut || isPreorder) && (
|
|
114
65
|
<div className="absolute inset-x-2 bottom-2 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
|
|
115
|
-
{
|
|
116
|
-
CTA is the brand colour rather than "whatever the text colour is". */}
|
|
117
|
-
{hasOptions || soldOut ? (
|
|
118
|
-
<Link to={`/product/${product.slug}`}
|
|
119
|
-
className="block w-full text-center py-2.5 px-4 rounded-full bg-primary text-primary-foreground no-underline text-sm font-semibold">
|
|
120
|
-
{soldOut ? "Pre-order" : "Choose options"}
|
|
121
|
-
</Link>
|
|
122
|
-
) : (
|
|
66
|
+
{isQuickAddable ? (
|
|
123
67
|
<button onClick={quickAdd} disabled={adding}
|
|
124
68
|
className="w-full py-2.5 px-4 rounded-full bg-primary text-primary-foreground border-none text-sm font-semibold cursor-pointer transition-opacity hover:opacity-90 disabled:opacity-60 disabled:cursor-wait">
|
|
125
69
|
{adding ? "Adding…" : "Quick add"}
|
|
126
70
|
</button>
|
|
71
|
+
) : (
|
|
72
|
+
<Link to={`/product/${product.slug}`}
|
|
73
|
+
className="block w-full text-center py-2.5 px-4 rounded-full bg-primary text-primary-foreground no-underline text-sm font-semibold">
|
|
74
|
+
{isSoldOut ? "Pre-order" : "Choose options"}
|
|
75
|
+
</Link>
|
|
127
76
|
)}
|
|
128
77
|
</div>
|
|
129
78
|
)}
|
|
130
79
|
</div>
|
|
131
80
|
|
|
132
81
|
<div className="p-3 flex flex-col gap-1">
|
|
133
|
-
{/* Side by side once the tile is wide enough; stacked on a phone's two-up grid, where a long
|
|
134
|
-
name wrapping to three lines against a top-right price reads as broken. */}
|
|
135
82
|
<div className="flex flex-col gap-1 md:flex-row md:items-baseline md:justify-between md:gap-3">
|
|
136
|
-
{/* Clamped to two lines: a 220px tile turns a long product name into three or four ragged
|
|
137
|
-
lines beside a top-right price. The full name is on the tile's link and the PDP. */}
|
|
138
83
|
<h3 className="m-0 font-display text-[15px] font-semibold line-clamp-2">
|
|
139
84
|
<Link to={`/product/${product.slug}`} className="no-underline text-foreground">{product.name}</Link>
|
|
140
85
|
</h3>
|
|
141
86
|
<div className="flex gap-2 items-baseline flex-wrap md:justify-end">
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{compareAt && compareAt !== price && (
|
|
146
|
-
<span className="text-muted-foreground line-through text-[13px] whitespace-nowrap">{compareAt}</span>
|
|
87
|
+
<span className="font-semibold whitespace-nowrap">{priceDisplay}</span>
|
|
88
|
+
{compareAtDisplay && (
|
|
89
|
+
<span className="text-muted-foreground line-through text-[13px] whitespace-nowrap">{compareAtDisplay}</span>
|
|
147
90
|
)}
|
|
148
91
|
</div>
|
|
149
92
|
</div>
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
// empty.
|
|
3
|
-
//
|
|
4
|
-
// Two columns on phones — a 220px minmax leaves room for only one, and a one-up catalog scrolls
|
|
5
|
-
// forever — then auto-fill from md up.
|
|
6
|
-
//
|
|
7
|
-
// The empty state carries weight here — a catalog is seeded separately from the build, so it is
|
|
8
|
-
// routinely empty when the merchant first opens the page. Say why, rather than showing a bare line.
|
|
1
|
+
// Reference implementation of a product grid — 2-col mobile, auto-fill desktop, with a skeleton
|
|
2
|
+
// loading state and a descriptive empty state. Build your own layout instead (see INSTRUCTIONS.md
|
|
3
|
+
// STEP 3) — read this for the skeleton/empty-state patterns, don't use it directly.
|
|
9
4
|
import ProductCard from "./ProductCard";
|
|
10
5
|
|
|
11
6
|
// Skeleton tiles match ProductCard's aspect-square + two text lines, so nothing shifts when the real
|