@vitrine-kit/vitrine 0.3.1 → 0.4.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/dist/index.js +313 -135
- package/kit/registry/README.md +9 -0
- package/kit/registry/_index.json +74 -0
- package/kit/registry/cart/docs/cart.md +13 -0
- package/kit/registry/cart/feature.json +51 -0
- package/kit/registry/cart/files/app/(frontend)/cart/page.tsx +21 -0
- package/kit/registry/cart/files/app/api/cart/route.ts +41 -0
- package/kit/registry/cart/files/components/cart/AddToCart.tsx +40 -0
- package/kit/registry/cart/files/components/cart/CartIndicator.tsx +11 -0
- package/kit/registry/cart/files/components/cart/CartLineItem.tsx +27 -0
- package/kit/registry/cart/files/components/cart/CartSummary.tsx +31 -0
- package/kit/registry/cart/files/components/cart/CartView.tsx +31 -0
- package/kit/registry/cart/files/lib/cart/data.ts +12 -0
- package/kit/registry/cart/files/lib/cart/register.ts +10 -0
- package/kit/registry/catalog/docs/catalog.md +18 -0
- package/kit/registry/catalog/feature.json +43 -0
- package/kit/registry/catalog/files/components/catalog/CategoryNav.tsx +26 -0
- package/kit/registry/catalog/files/components/catalog/ProductCard.tsx +42 -0
- package/kit/registry/catalog/files/components/catalog/ProductGrid.tsx +25 -0
- package/kit/registry/catalog/files/lib/catalog/data.ts +24 -0
- package/kit/registry/catalog/files/lib/catalog/register.ts +8 -0
- package/kit/registry/checkout/docs/checkout.md +17 -0
- package/kit/registry/checkout/feature.json +47 -0
- package/kit/registry/checkout/files/app/api/checkout/route.ts +14 -0
- package/kit/registry/checkout/files/components/checkout/CheckoutButton.tsx +46 -0
- package/kit/registry/checkout/files/lib/checkout/fulfill.ts +72 -0
- package/kit/registry/checkout/files/lib/checkout/register.ts +8 -0
- package/kit/registry/checkout-paddle/docs/checkout-paddle.md +25 -0
- package/kit/registry/checkout-paddle/feature.json +62 -0
- package/kit/registry/checkout-paddle/files/app/api/webhooks/paddle/route.ts +22 -0
- package/kit/registry/checkout-paddle/files/lib/checkout-paddle/provider.ts +80 -0
- package/kit/registry/checkout-paddle/files/lib/checkout-paddle/register.ts +7 -0
- package/kit/registry/checkout-stripe/docs/checkout-stripe.md +19 -0
- package/kit/registry/checkout-stripe/feature.json +54 -0
- package/kit/registry/checkout-stripe/files/app/api/webhooks/stripe/route.ts +23 -0
- package/kit/registry/checkout-stripe/files/lib/checkout-stripe/provider.ts +80 -0
- package/kit/registry/checkout-stripe/files/lib/checkout-stripe/register.ts +7 -0
- package/kit/registry/checkout-yookassa/docs/checkout-yookassa.md +23 -0
- package/kit/registry/checkout-yookassa/feature.json +52 -0
- package/kit/registry/checkout-yookassa/files/app/api/webhooks/yookassa/route.ts +22 -0
- package/kit/registry/checkout-yookassa/files/lib/checkout-yookassa/provider.ts +92 -0
- package/kit/registry/checkout-yookassa/files/lib/checkout-yookassa/register.ts +7 -0
- package/kit/registry/product-page/docs/product-page.md +16 -0
- package/kit/registry/product-page/feature.json +39 -0
- package/kit/registry/product-page/files/components/product/ProductGallery.tsx +37 -0
- package/kit/registry/product-page/files/components/product/ProductView.tsx +42 -0
- package/kit/registry/product-page/files/lib/product/data.ts +10 -0
- package/kit/registry/seo/docs/seo.md +14 -0
- package/kit/registry/seo/feature.json +35 -0
- package/kit/registry/seo/files/components/seo/JsonLd.tsx +15 -0
- package/kit/registry/seo/files/lib/seo/jsonld.ts +29 -0
- package/kit/registry/seo/files/lib/seo/metadata.ts +35 -0
- package/kit/registry/stubs/route-glue.d.ts +45 -0
- package/kit/registry/tsconfig.json +21 -0
- package/kit/registry/tsconfig.routes.json +9 -0
- package/kit/templates/README.md +27 -0
- package/kit/templates/backend-payload/files/.dockerignore +8 -0
- package/kit/templates/backend-payload/files/Dockerfile +29 -0
- package/kit/templates/backend-payload/files/app/(payload)/admin/[[...segments]]/not-found.tsx +18 -0
- package/kit/templates/backend-payload/files/app/(payload)/admin/[[...segments]]/page.tsx +18 -0
- package/kit/templates/backend-payload/files/app/(payload)/admin/importMap.js +3 -0
- package/kit/templates/backend-payload/files/app/(payload)/api/[...slug]/route.ts +18 -0
- package/kit/templates/backend-payload/files/app/(payload)/api/graphql/route.ts +6 -0
- package/kit/templates/backend-payload/files/app/(payload)/api/graphql-playground/route.ts +5 -0
- package/kit/templates/backend-payload/files/app/(payload)/custom.scss +1 -0
- package/kit/templates/backend-payload/files/app/(payload)/layout.tsx +23 -0
- package/kit/templates/backend-payload/files/docker-compose.yml +35 -0
- package/kit/templates/backend-payload/files/lib/adapter/db-decision.ts +43 -0
- package/kit/templates/backend-payload/files/lib/adapter/db-ping.ts +29 -0
- package/kit/templates/backend-payload/files/lib/adapter/db.ts +27 -0
- package/kit/templates/backend-payload/files/lib/adapter/index.ts +31 -0
- package/kit/templates/backend-payload/files/lib/adapter/map.ts +112 -0
- package/kit/templates/backend-payload/files/lib/adapter/payload-catalog.ts +79 -0
- package/kit/templates/backend-payload/files/lib/adapter/payload-commerce.ts +154 -0
- package/kit/templates/backend-payload/files/lib/adapter/payload-types.ts +50 -0
- package/kit/templates/backend-payload/files/lib/seed/admin.ts +23 -0
- package/kit/templates/backend-payload/files/lib/seed/demo.ts +79 -0
- package/kit/templates/backend-payload/files/lib/seed/guards.ts +5 -0
- package/kit/templates/backend-payload/files/lib/seed/richtext.ts +26 -0
- package/kit/templates/backend-payload/files/lib/seed/run.ts +51 -0
- package/kit/templates/backend-payload/files/next.config.mjs +10 -0
- package/kit/templates/backend-payload/files/payload.config.ts +32 -0
- package/kit/templates/backend-payload/files/seed-assets/placeholder-1.svg +5 -0
- package/kit/templates/backend-payload/files/seed-assets/placeholder-2.svg +5 -0
- package/kit/templates/backend-payload/files/seed-assets/placeholder-3.svg +5 -0
- package/kit/templates/backend-payload/files/seed-assets/placeholder-4.svg +5 -0
- package/kit/templates/backend-payload/files/seed-assets/placeholder-5.svg +5 -0
- package/kit/templates/backend-vendure/files/.dockerignore +8 -0
- package/kit/templates/backend-vendure/files/Dockerfile +24 -0
- package/kit/templates/backend-vendure/files/docker-compose.yml +50 -0
- package/kit/templates/backend-vendure/files/lib/adapter/graphql.ts +27 -0
- package/kit/templates/backend-vendure/files/lib/adapter/index.ts +20 -0
- package/kit/templates/backend-vendure/files/lib/adapter/map.ts +107 -0
- package/kit/templates/backend-vendure/files/lib/adapter/vendure-catalog.ts +62 -0
- package/kit/templates/backend-vendure/files/lib/adapter/vendure-commerce.ts +76 -0
- package/kit/templates/backend-vendure/files/lib/adapter/vendure-types.ts +61 -0
- package/kit/templates/backend-vendure/files/lib/seed/populate.ts +22 -0
- package/kit/templates/backend-vendure/files/src/index.ts +15 -0
- package/kit/templates/backend-vendure/files/vendure-config.ts +56 -0
- package/kit/templates/base/files/.claude/commands/add-feature.md +18 -0
- package/kit/templates/base/files/.claude/commands/design.md +14 -0
- package/kit/templates/base/files/.claude/commands/doctor.md +15 -0
- package/kit/templates/base/files/.claude/commands/setup.md +15 -0
- package/kit/templates/base/files/.claude/commands/update.md +16 -0
- package/kit/templates/base/files/.gitattributes +12 -0
- package/kit/templates/base/files/AGENTS.md +20 -0
- package/kit/templates/base/files/app/(frontend)/categories/[slug]/page.tsx +28 -0
- package/kit/templates/base/files/app/(frontend)/layout.tsx +32 -0
- package/kit/templates/base/files/app/(frontend)/page.tsx +27 -0
- package/kit/templates/base/files/app/(frontend)/products/[slug]/page.tsx +43 -0
- package/kit/templates/base/files/app/globals.css +6 -0
- package/kit/templates/base/files/components/layout/SiteFooter.tsx +15 -0
- package/kit/templates/base/files/components/layout/SiteHeader.tsx +23 -0
- package/kit/templates/base/files/design/README.md +9 -0
- package/kit/templates/base/files/lib/site.ts +10 -0
- package/kit/templates/base/files/next.config.mjs +7 -0
- package/kit/templates/base/files/postcss.config.mjs +7 -0
- package/kit/templates/base/files/public/.gitkeep +0 -0
- package/kit/templates/base/files/tailwind.config.ts +13 -0
- package/kit/templates/base/files/tsconfig.json +25 -0
- package/kit/templates/tsconfig.json +18 -0
- package/package.json +9 -5
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Vendure server config. DB: Postgres (DATABASE_URL) or the embedded better-sqlite3
|
|
2
|
+
// in dev (zero-config, §18 equivalent). Superadmin — from env (change it for prod).
|
|
3
|
+
// The Stripe plugin is wired in at tier=full with payments.
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { DefaultJobQueuePlugin, DefaultSearchPlugin, type VendureConfig } from '@vendure/core';
|
|
7
|
+
import { AssetServerPlugin } from '@vendure/asset-server-plugin';
|
|
8
|
+
|
|
9
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const IS_DEV = process.env.NODE_ENV !== 'production';
|
|
11
|
+
|
|
12
|
+
function dbConnectionOptions(): VendureConfig['dbConnectionOptions'] {
|
|
13
|
+
const url = process.env.DATABASE_URL;
|
|
14
|
+
if (url) {
|
|
15
|
+
return { type: 'postgres', url, synchronize: IS_DEV } as VendureConfig['dbConnectionOptions'];
|
|
16
|
+
}
|
|
17
|
+
if (!IS_DEV) {
|
|
18
|
+
// In production there's no silent fallback to SQLite (§18).
|
|
19
|
+
throw new Error('[vitrine] DATABASE_URL is required in production');
|
|
20
|
+
}
|
|
21
|
+
// dev zero-config: embedded SQLite (no DB server).
|
|
22
|
+
return {
|
|
23
|
+
type: 'better-sqlite3',
|
|
24
|
+
database: path.join(dirname, '.vitrine', 'vendure.sqlite'),
|
|
25
|
+
synchronize: true,
|
|
26
|
+
} as VendureConfig['dbConnectionOptions'];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const config: VendureConfig = {
|
|
30
|
+
apiOptions: {
|
|
31
|
+
port: Number(process.env.VENDURE_PORT ?? 3001),
|
|
32
|
+
adminApiPath: 'admin-api',
|
|
33
|
+
shopApiPath: 'shop-api',
|
|
34
|
+
},
|
|
35
|
+
authOptions: {
|
|
36
|
+
tokenMethod: ['bearer', 'cookie'],
|
|
37
|
+
superadminCredentials: {
|
|
38
|
+
identifier: process.env.VENDURE_SUPERADMIN_USERNAME ?? 'superadmin',
|
|
39
|
+
// dev default; for prod you must set VENDURE_SUPERADMIN_PASSWORD.
|
|
40
|
+
password: process.env.VENDURE_SUPERADMIN_PASSWORD ?? 'superadmin',
|
|
41
|
+
},
|
|
42
|
+
cookieOptions: { secret: process.env.VENDURE_COOKIE_SECRET ?? 'dev-cookie-secret' },
|
|
43
|
+
},
|
|
44
|
+
dbConnectionOptions: dbConnectionOptions(),
|
|
45
|
+
paymentOptions: {
|
|
46
|
+
paymentMethodHandlers: [
|
|
47
|
+
// + StripePlugin handler (env STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET) for tier=full with payments.
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
plugins: [
|
|
51
|
+
DefaultJobQueuePlugin.init({}),
|
|
52
|
+
DefaultSearchPlugin.init({ bufferUpdates: false }),
|
|
53
|
+
AssetServerPlugin.init({ route: 'assets', assetUploadDir: path.join(dirname, 'static', 'assets') }),
|
|
54
|
+
// StripePlugin.init({ storeCustomersInStripe: true }),
|
|
55
|
+
],
|
|
56
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Add a feature from the Vitrine registry and style it
|
|
3
|
+
argument-hint: <feature>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Add the `$ARGUMENTS` feature to the project:
|
|
7
|
+
|
|
8
|
+
1. `vitrine list` — check that the feature is available and not yet installed (show the list).
|
|
9
|
+
2. `vitrine add $ARGUMENTS` — copies files, registers slots/blueprint, merges in
|
|
10
|
+
env and dependencies, updates `site.config.ts`, `vitrine.json`, and the table in `CLAUDE.md`.
|
|
11
|
+
The feature's dependencies are pulled in automatically.
|
|
12
|
+
3. If the feature added keys to `.env.example` — suggest filling them in `.env`.
|
|
13
|
+
4. `vitrine design apply` — style the new feature for the current brand (only if `/design`
|
|
14
|
+
has an export; otherwise skip).
|
|
15
|
+
5. `pnpm typecheck` (or `pnpm build`) — make sure the build is green.
|
|
16
|
+
6. Don't edit generated files by hand. Remind the user to commit the changes.
|
|
17
|
+
|
|
18
|
+
To remove a feature — `vitrine remove <feature>` (only for `removable` ones).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Apply the design from /design to the tokens (vitrine design apply)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Apply the client design:
|
|
6
|
+
|
|
7
|
+
1. Check that the `/design` folder has a brand export (something besides `README.md`: tokens,
|
|
8
|
+
screenshots, assets). If it's empty — ask the user to add the export and stop.
|
|
9
|
+
2. `vitrine design apply` — a wrapper over Claude Code: it sets ONLY the CSS variable values
|
|
10
|
+
in `theme/client.css`, without touching logic/data/routing/structure. The token set is closed.
|
|
11
|
+
3. Show the `theme/client.css` diff and briefly describe the palette/typography/spacing changes.
|
|
12
|
+
4. The step is idempotent — re-running converges. Run it again if needed.
|
|
13
|
+
|
|
14
|
+
If Claude Code isn't found in PATH — point to it via `--bin` or `VITRINE_CLAUDE_BIN`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Check project consistency and fix discrepancies
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Check the project's health:
|
|
6
|
+
|
|
7
|
+
1. `vitrine doctor` — reconciles `vitrine.json` ↔ files ↔ packages (`package.json`) ↔ env (`.env.example`).
|
|
8
|
+
2. Work through the output. Each issue has a suggested fix:
|
|
9
|
+
- missing file/slot/provider/dependency → `vitrine add <feature>` (reinstalls and regenerates);
|
|
10
|
+
- version in the repo ≠ registry → `vitrine update <feature>`;
|
|
11
|
+
- feature not found in the registry → `vitrine kit update`;
|
|
12
|
+
- missing env key → add it to `.env.example`/`.env`;
|
|
13
|
+
- no design-instruction block in `CLAUDE.md` → refresh it (`kit update` brings it).
|
|
14
|
+
3. Apply the fixes (prefer CLI commands over hand edits), then re-run `vitrine doctor`
|
|
15
|
+
until it's green. Report anything left that needs a user decision (e.g. secrets).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: First-time project setup — dependencies, env, start the dev server
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Bring this Vitrine project up locally. Work step by step, stop on errors:
|
|
6
|
+
|
|
7
|
+
1. Check Node >= 20 (`node -v`). If lower, ask to upgrade (Node 20 LTS).
|
|
8
|
+
2. `pnpm install` (the `@vitrine-kit/*` packages are public on npm — no token needed).
|
|
9
|
+
3. If there's no `.env` — `cp .env.example .env`. Go through the keys and suggest what to fill in
|
|
10
|
+
(production requires a real `DATABASE_URL`; dev has a SQLite fallback).
|
|
11
|
+
4. Start the dev server; see `README.md` for backend specifics:
|
|
12
|
+
- Payload: `pnpm dev` → storefront `http://localhost:3000`, admin `/admin`.
|
|
13
|
+
- Vendure: `pnpm vendure` (Shop API on :3001, separate terminal) + `pnpm dev` (:3000).
|
|
14
|
+
5. Make sure the page opens. Briefly report the status and the next step
|
|
15
|
+
(`/design` for branding or `/add-feature` for features).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Update Vitrine features (kit update → diff → update → doctor)
|
|
3
|
+
argument-hint: [feature]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Update features from the registry while keeping the client's edits:
|
|
7
|
+
|
|
8
|
+
1. `vitrine kit update` — refresh the local registry/templates cache from GitHub
|
|
9
|
+
(needs `gh` and `tar`; cache in `~/.vitrine`).
|
|
10
|
+
2. `vitrine kit status` — see what's newer than what's installed.
|
|
11
|
+
3. `vitrine diff $ARGUMENTS` — preview changes without writing (for each feature being updated).
|
|
12
|
+
4. `vitrine update $ARGUMENTS` — 3-way merge (base = your snapshot in `.vitrine/originals/`).
|
|
13
|
+
Without an argument it updates all features.
|
|
14
|
+
5. If the merge produced conflicts — open the files with git markers (`<<<<<<<` / `=======` / `>>>>>>>`),
|
|
15
|
+
resolve them by hand, keeping both the client's edit and the new version from the registry.
|
|
16
|
+
6. `vitrine doctor` — check consistency. Run `pnpm build` and report the result.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Normalize line endings to LF — keeps `vitrine update` 3-way merges clean on Windows
|
|
2
|
+
# (.vitrine/originals and the registry are LF; a CRLF working tree would look all-changed).
|
|
3
|
+
* text=auto eol=lf
|
|
4
|
+
|
|
5
|
+
# Binary assets — no normalization.
|
|
6
|
+
*.png binary
|
|
7
|
+
*.jpg binary
|
|
8
|
+
*.jpeg binary
|
|
9
|
+
*.gif binary
|
|
10
|
+
*.ico binary
|
|
11
|
+
*.woff binary
|
|
12
|
+
*.woff2 binary
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This project is a storefront built on **Vitrine** (an agentic starter kit). The canonical guide for
|
|
4
|
+
the AI agent and the full CLI command reference live in [`CLAUDE.md`](./CLAUDE.md). Read it
|
|
5
|
+
before working.
|
|
6
|
+
|
|
7
|
+
In short:
|
|
8
|
+
|
|
9
|
+
- All starter-kit operations go through the `vitrine` CLI (`add` / `remove` / `list` /
|
|
10
|
+
`update` / `diff` / `doctor` / `kit update` / `design apply`), not manual edits.
|
|
11
|
+
The full command table is in `CLAUDE.md`.
|
|
12
|
+
- **Don't edit generated/managed files** (`lib/slots.ts`, `lib/payments.ts`,
|
|
13
|
+
`lib/blueprint.ts`, the managed regions of `site.config.ts`, `vitrine.json`, the feature table in
|
|
14
|
+
`CLAUDE.md`, `.env*`) — the CLI overwrites them from state.
|
|
15
|
+
- **Design = token values only** in `theme/client.css` (via `vitrine design apply`):
|
|
16
|
+
don't touch logic/data/routing/component structure.
|
|
17
|
+
- **The user makes commits** — don't commit without an explicit request.
|
|
18
|
+
|
|
19
|
+
Ready-made flows for Claude Code are the slash commands in [`.claude/commands/`](./.claude/commands)
|
|
20
|
+
(`/setup`, `/add-feature`, `/design`, `/update`, `/doctor`).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Category listing. Filtered by category slug via the CatalogSource contract.
|
|
2
|
+
import { notFound } from 'next/navigation';
|
|
3
|
+
import { Slot } from '@vitrine-kit/core/react';
|
|
4
|
+
import { getCatalogSource } from '@/lib/adapter';
|
|
5
|
+
import { loadCategories, loadProducts } from '@/lib/catalog/data';
|
|
6
|
+
import { ProductGrid } from '@/components/catalog/ProductGrid';
|
|
7
|
+
|
|
8
|
+
interface PageProps {
|
|
9
|
+
params: Promise<{ slug: string }>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default async function CategoryPage({ params }: PageProps) {
|
|
13
|
+
const { slug } = await params;
|
|
14
|
+
const source = await getCatalogSource();
|
|
15
|
+
const category = (await loadCategories(source)).find((c) => c.slug === slug);
|
|
16
|
+
if (!category) notFound();
|
|
17
|
+
|
|
18
|
+
const products = await loadProducts(source, { category: slug });
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex flex-col gap-section">
|
|
22
|
+
<Slot name="category.header" category={category} />
|
|
23
|
+
<h1 className="font-heading text-fg">{category.title}</h1>
|
|
24
|
+
<ProductGrid products={products} />
|
|
25
|
+
<Slot name="category.below-products" />
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Root storefront layout (route group (frontend)). The Payload admin lives in
|
|
2
|
+
// its own route group (payload) with its own layout — so there's no root app/layout,
|
|
3
|
+
// and each group renders its own <html> (the Payload 3 + Next pattern).
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
import type { Metadata } from 'next';
|
|
6
|
+
import '../globals.css';
|
|
7
|
+
import { siteConfig } from '@/site.config';
|
|
8
|
+
import { registerSlots } from '@/lib/slots';
|
|
9
|
+
import { Slot } from '@vitrine-kit/core/react';
|
|
10
|
+
import { SiteHeader } from '@/components/layout/SiteHeader';
|
|
11
|
+
import { SiteFooter } from '@/components/layout/SiteFooter';
|
|
12
|
+
|
|
13
|
+
// Register installed features' slots (lib/slots.ts is generated by the CLI).
|
|
14
|
+
registerSlots();
|
|
15
|
+
|
|
16
|
+
export const metadata: Metadata = {
|
|
17
|
+
title: { default: 'Vitrine', template: '%s · Vitrine' },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default function FrontendLayout({ children }: { children: ReactNode }) {
|
|
21
|
+
return (
|
|
22
|
+
<html lang={siteConfig.i18n.defaultLocale}>
|
|
23
|
+
<body className="min-h-screen bg-bg font-sans text-fg antialiased">
|
|
24
|
+
<Slot name="global.banner-top" />
|
|
25
|
+
<SiteHeader />
|
|
26
|
+
<main className="mx-auto w-full max-w-container px-gutter py-section">{children}</main>
|
|
27
|
+
<SiteFooter />
|
|
28
|
+
<Slot name="global.body-end" />
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Storefront home: catalog listing. Data via the CatalogSource contract
|
|
2
|
+
// (backend-agnostic); the grid is the catalog feature's component.
|
|
3
|
+
import { Slot } from '@vitrine-kit/core/react';
|
|
4
|
+
import { getCatalogSource } from '@/lib/adapter';
|
|
5
|
+
import { loadProducts } from '@/lib/catalog/data';
|
|
6
|
+
import { ProductGrid } from '@/components/catalog/ProductGrid';
|
|
7
|
+
|
|
8
|
+
export default async function HomePage() {
|
|
9
|
+
const source = await getCatalogSource();
|
|
10
|
+
const products = await loadProducts(source, { perPage: 12 });
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="flex flex-col gap-section">
|
|
14
|
+
<Slot name="home.hero" />
|
|
15
|
+
<section aria-labelledby="catalog-heading" className="flex flex-col gap-gutter">
|
|
16
|
+
<h1 id="catalog-heading" className="font-heading text-fg">
|
|
17
|
+
Catalog
|
|
18
|
+
</h1>
|
|
19
|
+
<Slot name="catalog.grid-top" />
|
|
20
|
+
<ProductGrid products={products} />
|
|
21
|
+
<Slot name="catalog.grid-bottom" />
|
|
22
|
+
</section>
|
|
23
|
+
<Slot name="home.sections" />
|
|
24
|
+
<Slot name="home.bottom" />
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Product page. ProductView (the product-page feature) hosts the product.* slots.
|
|
2
|
+
// Metadata and JSON-LD come from the seo feature.
|
|
3
|
+
import type { Metadata } from 'next';
|
|
4
|
+
import { notFound } from 'next/navigation';
|
|
5
|
+
import { getCatalogSource } from '@/lib/adapter';
|
|
6
|
+
import { loadProduct } from '@/lib/catalog/data';
|
|
7
|
+
import { ProductView } from '@/components/product/ProductView';
|
|
8
|
+
import { buildProductMetadata } from '@/lib/seo/metadata';
|
|
9
|
+
import { productJsonLd } from '@/lib/seo/jsonld';
|
|
10
|
+
import { JsonLd } from '@/components/seo/JsonLd';
|
|
11
|
+
import { baseUrl } from '@/lib/site';
|
|
12
|
+
|
|
13
|
+
interface PageProps {
|
|
14
|
+
params: Promise<{ slug: string }>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
|
18
|
+
const { slug } = await params;
|
|
19
|
+
const source = await getCatalogSource();
|
|
20
|
+
const product = await loadProduct(source, slug);
|
|
21
|
+
if (!product) return {};
|
|
22
|
+
const meta = buildProductMetadata(product, { baseUrl });
|
|
23
|
+
return {
|
|
24
|
+
title: meta.title,
|
|
25
|
+
description: meta.description,
|
|
26
|
+
alternates: meta.canonical ? { canonical: meta.canonical } : undefined,
|
|
27
|
+
openGraph: meta.openGraph,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default async function ProductPage({ params }: PageProps) {
|
|
32
|
+
const { slug } = await params;
|
|
33
|
+
const source = await getCatalogSource();
|
|
34
|
+
const product = await loadProduct(source, slug);
|
|
35
|
+
if (!product) notFound();
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<>
|
|
39
|
+
<JsonLd data={productJsonLd(product, { baseUrl })} />
|
|
40
|
+
<ProductView product={product} />
|
|
41
|
+
</>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Storefront footer. Hosts the global.footer slot.
|
|
2
|
+
import { Slot } from '@vitrine-kit/core/react';
|
|
3
|
+
|
|
4
|
+
export function SiteFooter() {
|
|
5
|
+
return (
|
|
6
|
+
<footer className="mt-section border-t border-border">
|
|
7
|
+
<div className="mx-auto max-w-container px-gutter py-section text-muted-fg">
|
|
8
|
+
<Slot
|
|
9
|
+
name="global.footer"
|
|
10
|
+
fallback={<p>© {new Date().getFullYear()}</p>}
|
|
11
|
+
/>
|
|
12
|
+
</div>
|
|
13
|
+
</footer>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Storefront header. Hosts the global.header-nav slot (where the catalog feature's
|
|
2
|
+
// CategoryNav mounts) and passes it categories from the active CatalogSource.
|
|
3
|
+
import { Slot } from '@vitrine-kit/core/react';
|
|
4
|
+
import { getCatalogSource } from '@/lib/adapter';
|
|
5
|
+
|
|
6
|
+
export async function SiteHeader() {
|
|
7
|
+
const source = await getCatalogSource();
|
|
8
|
+
const categories = await source.listCategories();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<header className="border-b border-border">
|
|
12
|
+
<div className="mx-auto flex max-w-container items-center justify-between gap-gutter px-gutter py-unit">
|
|
13
|
+
<a href="/" className="font-heading text-fg">
|
|
14
|
+
<Slot name="global.header-start" fallback={<span>Vitrine</span>} />
|
|
15
|
+
</a>
|
|
16
|
+
<Slot name="global.header-nav" categories={categories} />
|
|
17
|
+
<div className="flex items-center gap-unit">
|
|
18
|
+
<Slot name="global.header-actions" />
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /design — export from Claude Design
|
|
2
|
+
|
|
3
|
+
Drop your design export here (React/HTML/CSS, tokens, screenshots) and run
|
|
4
|
+
`vitrine design apply`. The AI step reads this folder and the instruction from `CLAUDE.md`,
|
|
5
|
+
then sets **only the token values** in `theme/client.css` (palette, typography, spacing, radii,
|
|
6
|
+
shadows).
|
|
7
|
+
|
|
8
|
+
This folder is isolated from behavior: its contents don't affect logic, data, routing, or a11y —
|
|
9
|
+
only the look, via tokens. Re-running the design step is safe.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Aggregated site parameters from site.config + env. Safe to import on both
|
|
2
|
+
// the server and the client (public values only).
|
|
3
|
+
import { siteConfig } from '@/site.config';
|
|
4
|
+
|
|
5
|
+
/** Base URL for canonical/OG. Overridden by NEXT_PUBLIC_SITE_URL. */
|
|
6
|
+
export const baseUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000';
|
|
7
|
+
|
|
8
|
+
export const currency = siteConfig.i18n.currency;
|
|
9
|
+
export const defaultLocale = siteConfig.i18n.defaultLocale;
|
|
10
|
+
export const locales = siteConfig.i18n.locales;
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The client's Tailwind: the Vitrine preset maps classes to the token contract's
|
|
2
|
+
// CSS variables (--vt-*). The variable values live in theme/client.css (design step).
|
|
3
|
+
import type { Config } from 'tailwindcss';
|
|
4
|
+
import { vitrinePreset } from '@vitrine-kit/contracts';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
presets: [vitrinePreset as Partial<Config>],
|
|
8
|
+
content: [
|
|
9
|
+
'./app/**/*.{ts,tsx}',
|
|
10
|
+
'./components/**/*.{ts,tsx}',
|
|
11
|
+
'./lib/**/*.{ts,tsx}',
|
|
12
|
+
],
|
|
13
|
+
} satisfies Config;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Bundler",
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"allowJs": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"plugins": [{ "name": "next" }],
|
|
18
|
+
"paths": {
|
|
19
|
+
"@/*": ["./*"],
|
|
20
|
+
"@payload-config": ["./payload.config.ts"]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
24
|
+
"exclude": ["node_modules", ".vitrine"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"noEmit": true,
|
|
5
|
+
"types": ["node"]
|
|
6
|
+
},
|
|
7
|
+
"//": "We typecheck only the pure template files (contracts + node) — the zero-config critical logic and the mappers. Files that import Next/Payload are checked when the client is instantiated (the stack is installed there).",
|
|
8
|
+
"include": [
|
|
9
|
+
"backend-payload/files/lib/adapter/db-decision.ts",
|
|
10
|
+
"backend-payload/files/lib/adapter/db-ping.ts",
|
|
11
|
+
"backend-payload/files/lib/adapter/payload-types.ts",
|
|
12
|
+
"backend-payload/files/lib/adapter/map.ts",
|
|
13
|
+
"backend-payload/files/lib/seed/demo.ts",
|
|
14
|
+
"backend-payload/files/lib/seed/guards.ts",
|
|
15
|
+
"backend-vendure/files/lib/adapter/vendure-types.ts",
|
|
16
|
+
"backend-vendure/files/lib/adapter/map.ts"
|
|
17
|
+
]
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitrine-kit/vitrine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Vitrine CLI — feature install primitive, init (wizard), add, update, doctor, design apply.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
7
10
|
"bin": {
|
|
8
11
|
"vitrine": "./dist/index.js"
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
11
|
-
"dist"
|
|
14
|
+
"dist",
|
|
15
|
+
"kit"
|
|
12
16
|
],
|
|
13
17
|
"publishConfig": {
|
|
14
18
|
"access": "public"
|
|
@@ -21,11 +25,11 @@
|
|
|
21
25
|
"dependencies": {
|
|
22
26
|
"@clack/prompts": "^0.8.2",
|
|
23
27
|
"commander": "^12.1.0",
|
|
24
|
-
"@vitrine-kit/contracts": "1.2.
|
|
28
|
+
"@vitrine-kit/contracts": "1.2.1"
|
|
25
29
|
},
|
|
26
30
|
"scripts": {
|
|
27
|
-
"build": "tsup src/index.ts --format esm --clean",
|
|
28
|
-
"typecheck": "tsc --noEmit",
|
|
31
|
+
"build": "node scripts/generate-kit-versions.mjs && tsup src/index.ts --format esm --clean && node scripts/copy-kit.mjs",
|
|
32
|
+
"typecheck": "node scripts/generate-kit-versions.mjs && tsc --noEmit",
|
|
29
33
|
"test": "vitest run --passWithNoTests"
|
|
30
34
|
}
|
|
31
35
|
}
|