@forgecart/cli 2.202610052310.0 → 2.202610060357.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/storefront-shadcn/.forgecartignore +2 -0
- package/templates/storefront-shadcn/Procfile +1 -0
- package/templates/storefront-shadcn/README.md +229 -0
- package/templates/storefront-shadcn/SEO-MIGRATION.md +708 -0
- package/templates/storefront-shadcn/components.json +21 -0
- package/templates/storefront-shadcn/next.config.js +105 -0
- package/templates/storefront-shadcn/package.json +39 -0
- package/templates/storefront-shadcn/postcss.config.js +5 -0
- package/templates/storefront-shadcn/src/app/%5F%5Ffc/identify/route.ts +205 -0
- package/templates/storefront-shadcn/src/app/%5F%5Ffc/track/route.ts +189 -0
- package/templates/storefront-shadcn/src/app/%5F%5Fforge_beacon/route.ts +87 -0
- package/templates/storefront-shadcn/src/app/api/%5F%5Fbackend/methods/route.ts +27 -0
- package/templates/storefront-shadcn/src/app/cart/page.tsx +53 -0
- package/templates/storefront-shadcn/src/app/checkout/page.tsx +53 -0
- package/templates/storefront-shadcn/src/app/error.tsx +23 -0
- package/templates/storefront-shadcn/src/app/global-error.tsx +23 -0
- package/templates/storefront-shadcn/src/app/globals.css +156 -0
- package/templates/storefront-shadcn/src/app/layout.tsx +185 -0
- package/templates/storefront-shadcn/src/app/page.tsx +217 -0
- package/templates/storefront-shadcn/src/app/pages/[slug]/not-found.tsx +24 -0
- package/templates/storefront-shadcn/src/app/pages/[slug]/page.tsx +115 -0
- package/templates/storefront-shadcn/src/app/ping/route.ts +21 -0
- package/templates/storefront-shadcn/src/app/products/[slug]/not-found.tsx +18 -0
- package/templates/storefront-shadcn/src/app/products/[slug]/page.tsx +317 -0
- package/templates/storefront-shadcn/src/app/products/page.tsx +107 -0
- package/templates/storefront-shadcn/src/app/register/page.tsx +54 -0
- package/templates/storefront-shadcn/src/app/reset-password/page.tsx +60 -0
- package/templates/storefront-shadcn/src/app/robots.ts +69 -0
- package/templates/storefront-shadcn/src/app/sitemap.ts +106 -0
- package/templates/storefront-shadcn/src/app/verify/page.tsx +157 -0
- package/templates/storefront-shadcn/src/components/CartView.tsx +333 -0
- package/templates/storefront-shadcn/src/components/ForgeErrorBeacon.tsx +102 -0
- package/templates/storefront-shadcn/src/components/ForgeTracker.tsx +479 -0
- package/templates/storefront-shadcn/src/components/ForgecartDesigner.tsx +43 -0
- package/templates/storefront-shadcn/src/components/Header.tsx +73 -0
- package/templates/storefront-shadcn/src/components/LanguageSwitcher.tsx +96 -0
- package/templates/storefront-shadcn/src/components/LocaleLink.tsx +49 -0
- package/templates/storefront-shadcn/src/components/ProductCard.tsx +59 -0
- package/templates/storefront-shadcn/src/components/ProductPurchase.tsx +235 -0
- package/templates/storefront-shadcn/src/components/account/AccountMessage.tsx +63 -0
- package/templates/storefront-shadcn/src/components/account/RegisterForm.tsx +257 -0
- package/templates/storefront-shadcn/src/components/account/RequestPasswordResetForm.tsx +93 -0
- package/templates/storefront-shadcn/src/components/account/ResetPasswordForm.tsx +163 -0
- package/templates/storefront-shadcn/src/components/checkout/AddressStep.tsx +271 -0
- package/templates/storefront-shadcn/src/components/checkout/CheckoutFlow.tsx +551 -0
- package/templates/storefront-shadcn/src/components/checkout/CheckoutGate.tsx +55 -0
- package/templates/storefront-shadcn/src/components/checkout/PaymentElementForm.tsx +140 -0
- package/templates/storefront-shadcn/src/components/checkout/PaymentFormEmbed.tsx +89 -0
- package/templates/storefront-shadcn/src/components/checkout/RatesStep.tsx +115 -0
- package/templates/storefront-shadcn/src/components/ui/alert.tsx +75 -0
- package/templates/storefront-shadcn/src/components/ui/badge.tsx +40 -0
- package/templates/storefront-shadcn/src/components/ui/button.tsx +64 -0
- package/templates/storefront-shadcn/src/components/ui/card.tsx +28 -0
- package/templates/storefront-shadcn/src/components/ui/input.tsx +26 -0
- package/templates/storefront-shadcn/src/components/ui/label.tsx +22 -0
- package/templates/storefront-shadcn/src/components/ui/native-select.tsx +27 -0
- package/templates/storefront-shadcn/src/components/ui/skeleton.tsx +21 -0
- package/templates/storefront-shadcn/src/components/ui/utils.ts +16 -0
- package/templates/storefront-shadcn/src/instrumentation.ts +109 -0
- package/templates/storefront-shadcn/src/lib/account/account-link.ts +76 -0
- package/templates/storefront-shadcn/src/lib/account/register-state.ts +133 -0
- package/templates/storefront-shadcn/src/lib/account/reset-password-state.ts +111 -0
- package/templates/storefront-shadcn/src/lib/account/verify-state.ts +56 -0
- package/templates/storefront-shadcn/src/lib/account-actions.ts +76 -0
- package/templates/storefront-shadcn/src/lib/account-session.ts +47 -0
- package/templates/storefront-shadcn/src/lib/action-result.ts +30 -0
- package/templates/storefront-shadcn/src/lib/asset-alt.ts +34 -0
- package/templates/storefront-shadcn/src/lib/backend-actions.ts +20 -0
- package/templates/storefront-shadcn/src/lib/backend-client.ts +47 -0
- package/templates/storefront-shadcn/src/lib/cart-context.tsx +236 -0
- package/templates/storefront-shadcn/src/lib/checkout-session.ts +185 -0
- package/templates/storefront-shadcn/src/lib/content/page-metadata.ts +113 -0
- package/templates/storefront-shadcn/src/lib/content/render-fields.tsx +256 -0
- package/templates/storefront-shadcn/src/lib/content/resolve-page.ts +143 -0
- package/templates/storefront-shadcn/src/lib/error-messages.ts +24 -0
- package/templates/storefront-shadcn/src/lib/experiments.ts +333 -0
- package/templates/storefront-shadcn/src/lib/forgecart.ts +464 -0
- package/templates/storefront-shadcn/src/lib/format.ts +89 -0
- package/templates/storefront-shadcn/src/lib/identify-forward.ts +152 -0
- package/templates/storefront-shadcn/src/lib/locale/channel-locales-loader.ts +169 -0
- package/templates/storefront-shadcn/src/lib/locale/channel-locales-map.ts +46 -0
- package/templates/storefront-shadcn/src/lib/locale/channel-locales.ts +191 -0
- package/templates/storefront-shadcn/src/lib/locale/grammar.ts +194 -0
- package/templates/storefront-shadcn/src/lib/locale/localized-path.ts +55 -0
- package/templates/storefront-shadcn/src/lib/locale/middleware-plan.ts +107 -0
- package/templates/storefront-shadcn/src/lib/locale/request-binding.ts +80 -0
- package/templates/storefront-shadcn/src/lib/locale/request-locale.ts +66 -0
- package/templates/storefront-shadcn/src/lib/marketing-params.ts +213 -0
- package/templates/storefront-shadcn/src/lib/money.ts +50 -0
- package/templates/storefront-shadcn/src/lib/seo/alternates.ts +123 -0
- package/templates/storefront-shadcn/src/lib/seo/json-ld.ts +266 -0
- package/templates/storefront-shadcn/src/lib/seo/metadata.ts +419 -0
- package/templates/storefront-shadcn/src/lib/seo/noindex.ts +218 -0
- package/templates/storefront-shadcn/src/lib/seo/public-origin.ts +166 -0
- package/templates/storefront-shadcn/src/lib/seo/redirect-plan.ts +86 -0
- package/templates/storefront-shadcn/src/lib/seo/resolve-path.ts +107 -0
- package/templates/storefront-shadcn/src/lib/seo/scaffolded-routes.ts +83 -0
- package/templates/storefront-shadcn/src/lib/seo/sidecar.ts +75 -0
- package/templates/storefront-shadcn/src/lib/seo/site-verification.ts +98 -0
- package/templates/storefront-shadcn/src/lib/seo/sitemap-cache.ts +114 -0
- package/templates/storefront-shadcn/src/lib/seo/sitemap-entries.ts +321 -0
- package/templates/storefront-shadcn/src/lib/session-actions.ts +61 -0
- package/templates/storefront-shadcn/src/lib/session-cookies.ts +98 -0
- package/templates/storefront-shadcn/src/lib/shop-config.ts +51 -0
- package/templates/storefront-shadcn/src/lib/shop-session.ts +151 -0
- package/templates/storefront-shadcn/src/lib/track-forward.ts +200 -0
- package/templates/storefront-shadcn/src/lib/uuid.ts +19 -0
- package/templates/storefront-shadcn/src/middleware.ts +379 -0
- package/templates/storefront-shadcn/src/seo/redirects.ts +44 -0
- package/templates/storefront-shadcn/src/server/app.module.ts +18 -0
- package/templates/storefront-shadcn/src/server/backend-api.ts +26 -0
- package/templates/storefront-shadcn/src/server/backend-method.decorator.ts +23 -0
- package/templates/storefront-shadcn/src/server/bootstrap.ts +122 -0
- package/templates/storefront-shadcn/src/server/customer-extras/customer-extras.module.ts +13 -0
- package/templates/storefront-shadcn/src/server/customer-extras/service/customer-extras.service.ts +58 -0
- package/templates/storefront-shadcn/src/server/customer-extras/type/customer-extras.types.ts +11 -0
- package/templates/storefront-shadcn/src/server/forge/live-revision.ts +158 -0
- package/templates/storefront-shadcn/src/server/forgecart/forgecart-client.factory.ts +69 -0
- package/templates/storefront-shadcn/src/server/forgecart/forgecart.module.ts +9 -0
- package/templates/storefront-shadcn/src/server/runner.ts +90 -0
- package/templates/storefront-shadcn/src/server/types.ts +36 -0
- package/templates/storefront-shadcn/tsconfig.json +25 -0
- package/templates/storefront-shadcn-sdk-floor.json +1174 -0
- package/templates/template-set.json +10 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
web: npm run start
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Storefront
|
|
2
|
+
|
|
3
|
+
A [Next.js](https://nextjs.org) (App Router) storefront for a ForgeCart
|
|
4
|
+
channel: product browsing, a server-backed cart, and a complete multi-step
|
|
5
|
+
checkout — all speaking the ForgeCart shop GraphQL API through the generated
|
|
6
|
+
`@forgecart/sdk` client, styled with [shadcn/ui](https://ui.shadcn.com)
|
|
7
|
+
components on Tailwind CSS.
|
|
8
|
+
|
|
9
|
+
This directory is the scaffold copied verbatim into your project by
|
|
10
|
+
`forgecart init --ui shadcn`. Nothing here is compiled by the ForgeCart
|
|
11
|
+
monorepo; it ships as-is in the published `@forgecart/cli` package.
|
|
12
|
+
|
|
13
|
+
To migrate a storefront built before the locale and SEO layer, follow [`SEO-MIGRATION.md`](SEO-MIGRATION.md).
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
The storefront reads three environment variables (server-side only):
|
|
18
|
+
|
|
19
|
+
| Variable | Purpose |
|
|
20
|
+
| ------------------------- | -------------------------------------------------------------- |
|
|
21
|
+
| `FORGECART_CHANNEL_TOKEN` | Identifies the channel; sent as the `forgecart-token` header. |
|
|
22
|
+
| `FORGECART_SHOP_API_URL` | Base URL of the channel's shop GraphQL API (`.../shop-api`). |
|
|
23
|
+
| `FORGECART_ADMIN_SECRET` | Channel admin secret for server-side admin operations. |
|
|
24
|
+
|
|
25
|
+
`forgecart init` writes all of them to `.env` — it is the sole author of that
|
|
26
|
+
file, and `.env` is the only env file this project ever has (no `.env.example`,
|
|
27
|
+
no `.env.local`). None of the values ever reaches the
|
|
28
|
+
browser: every module that constructs an SDK client starts with
|
|
29
|
+
`import 'server-only'`, so a client component importing one is a **build
|
|
30
|
+
error**, and the workspace-pod image build additionally asserts no SDK code
|
|
31
|
+
lands in the client chunks.
|
|
32
|
+
|
|
33
|
+
## The backend gate (`sdk.backend`)
|
|
34
|
+
|
|
35
|
+
The storefront embeds a real NestJS backend under `src/server/` — booted as
|
|
36
|
+
an application context (no HTTP server; Next.js owns HTTP) in
|
|
37
|
+
`instrumentation.ts`, with normal `@Module`/`@Injectable` structure. A
|
|
38
|
+
service method decorated with `@BackendMethod()` (and declared in
|
|
39
|
+
`backend-api.ts`) becomes callable from any client component through the
|
|
40
|
+
typed proxy:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { backend } from '@/lib/backend-client';
|
|
44
|
+
|
|
45
|
+
const result = await backend.assignCustomerHash({});
|
|
46
|
+
if (!result.ok) renderError(result.error);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
One Server Action is the transport — no routes, no fetch calls, no
|
|
50
|
+
websockets. Methods receive a per-invocation session `{ admin, shop,
|
|
51
|
+
hasSession }`: `admin` is the channel-admin SDK gate (from
|
|
52
|
+
`FORGECART_ADMIN_SECRET`) and each method owns what that authority may be
|
|
53
|
+
used for; inputs are untrusted. The decorator is the allowlist, boot fails
|
|
54
|
+
loudly if the decorator scan and `backend-api.ts` drift, and in dev
|
|
55
|
+
`/api/__backend/methods` lists the live registry. The worked example
|
|
56
|
+
(`src/server/customer-extras/`) mints a random referral hash for the
|
|
57
|
+
signed-in customer and stores it as an ACF custom-field entry.
|
|
58
|
+
|
|
59
|
+
## Design system
|
|
60
|
+
|
|
61
|
+
Styling is Tailwind v4 + shadcn/ui. The components are **vendored**, the way
|
|
62
|
+
shadcn/ui intends: they live in [`src/components/ui/`](src/components/ui/)
|
|
63
|
+
(`button`, `card`, `input`, `native-select`, `label`, `alert`, `badge`,
|
|
64
|
+
`skeleton`) as source you own and edit, built on `class-variance-authority`,
|
|
65
|
+
`clsx`, `tailwind-merge` and the Radix primitives they import. Their `cn`
|
|
66
|
+
helper is [`src/components/ui/utils.ts`](src/components/ui/utils.ts), and
|
|
67
|
+
[`components.json`](components.json) points the shadcn/ui CLI at both, so
|
|
68
|
+
`npx shadcn@latest add <component>` lands a new component beside them.
|
|
69
|
+
|
|
70
|
+
Every component uses **semantic** tokens only (`bg-card`, `text-foreground`,
|
|
71
|
+
`border-input`, `bg-primary`, …), never a palette colour. The single source of
|
|
72
|
+
brand truth is the `:root` block in [`src/app/globals.css`](src/app/globals.css),
|
|
73
|
+
exposed to Tailwind through its `@theme inline` block — restyling the whole
|
|
74
|
+
storefront means editing those variables and nothing else. The stylesheet's
|
|
75
|
+
header carries the mapping from the daisyUI theme tokens of the daisyUI
|
|
76
|
+
storefront onto these variables, so a brand written for one row carries over
|
|
77
|
+
to the other.
|
|
78
|
+
|
|
79
|
+
## Data flow
|
|
80
|
+
|
|
81
|
+
**Transport.** The generated SDK carries every operation over graphql-ws — a
|
|
82
|
+
per-request client is a per-request websocket connection. All SDK calls run on
|
|
83
|
+
the server; nothing SDK-shaped ships to the browser.
|
|
84
|
+
|
|
85
|
+
- [`src/lib/forgecart.ts`](src/lib/forgecart.ts) — the channel-scoped
|
|
86
|
+
singleton client for anonymous reads (products, selling plans) via typed
|
|
87
|
+
helpers (`getProducts`, `getProductBySlug`, `getFeaturedProducts`,
|
|
88
|
+
`getChannelSellingPlanGroups`, …), plus the SDK type re-exports the
|
|
89
|
+
components render. Pure display helpers (`formatPrice`,
|
|
90
|
+
`getStartingPrice`, plan labels) live in
|
|
91
|
+
[`src/lib/format.ts`](src/lib/format.ts), which client components import
|
|
92
|
+
freely — it carries no SDK runtime.
|
|
93
|
+
- [`src/lib/shop-action.ts`](src/lib/shop-action.ts) — the Server-Action
|
|
94
|
+
runner: builds a per-request client around the shopper's
|
|
95
|
+
`forgecart-session` cookie, runs one operation, persists any newly minted
|
|
96
|
+
session token, translates failure into the typed envelope, and
|
|
97
|
+
`dispose()`s the client so no action leaks its socket.
|
|
98
|
+
- [`src/lib/action-result.ts`](src/lib/action-result.ts) — `ActionResult<T>`:
|
|
99
|
+
Server Actions **never throw** across the RSC boundary (Next production
|
|
100
|
+
redacts thrown action errors). Failures arrive in-band as the SDK's
|
|
101
|
+
`ExtractedError` (`code`, server-localized `message`, `variables`), which
|
|
102
|
+
the UI renders inline.
|
|
103
|
+
|
|
104
|
+
**Cart.** [`src/lib/cart-actions.ts`](src/lib/cart-actions.ts) drives the
|
|
105
|
+
shop **order** API — the real cart, never browser storage. The interactive
|
|
106
|
+
state lives in [`src/lib/cart-context.tsx`](src/lib/cart-context.tsx): seeded
|
|
107
|
+
with the server-fetched order, updated from each action's returned order, and
|
|
108
|
+
carrying the typed `error` channel the cart UI renders. Coupons
|
|
109
|
+
(`applyCoupon`/`removeCoupon`) recalculate discounts and totals server-side;
|
|
110
|
+
setting a line's quantity to `0` removes it.
|
|
111
|
+
|
|
112
|
+
**Checkout.** [`src/lib/checkout-actions.ts`](src/lib/checkout-actions.ts) +
|
|
113
|
+
[`src/components/checkout/`](src/components/checkout/) implement the
|
|
114
|
+
reference flow behind a numbered step indicator (an ordered list whose current
|
|
115
|
+
step carries `aria-current="step"` — shadcn/ui ships no stepper):
|
|
116
|
+
|
|
117
|
+
1. **Contact** — guest email + name (`setCustomerForOrder`).
|
|
118
|
+
2. **Address** — type-ahead via the shop `addressAutocomplete` API (optional
|
|
119
|
+
capability; manual entry always works), country select populated from
|
|
120
|
+
`availableCountries` (never a hardcoded list), and the **required phone
|
|
121
|
+
number** the API enforces.
|
|
122
|
+
3. **Shipping** — `refreshShippingRateGroups` quotes carriers explicitly (the
|
|
123
|
+
read query never does), rates render grouped per shipment with per-group
|
|
124
|
+
carrier failures, and the customer picks — the ArrangingPayment
|
|
125
|
+
transition requires a selection in every group.
|
|
126
|
+
4. **Payment** — eligible providers → `createPaymentSession` →
|
|
127
|
+
`getSessionTemplate` renders the provider's hosted form in
|
|
128
|
+
[`PaymentFormEmbed`](src/components/checkout/PaymentFormEmbed.tsx), which
|
|
129
|
+
listens for the origin-pinned `iframe-ready` / `payment-success` /
|
|
130
|
+
`payment-error` handshake. No provider-specific code exists in the
|
|
131
|
+
template; coupon edits freeze once a session exists (the amount is
|
|
132
|
+
locked at session creation).
|
|
133
|
+
5. **Confirmation** — the order code and summary.
|
|
134
|
+
|
|
135
|
+
Errors render where they belong: field-level for coded field errors
|
|
136
|
+
(`SHIPPING_ADDRESS_PHONE_REQUIRED`, `SHIPPING_ADDRESS_COUNTRY_*`,
|
|
137
|
+
`COUPON_CODE_*`) — the field marked `aria-invalid`, which is also what paints
|
|
138
|
+
its destructive border — a step-level `Alert` (`role="alert"`) otherwise, with
|
|
139
|
+
[`src/app/error.tsx`](src/app/error.tsx) and `global-error.tsx` as render
|
|
140
|
+
backstops.
|
|
141
|
+
|
|
142
|
+
## Sessions
|
|
143
|
+
|
|
144
|
+
The anonymous session is minted by the API on the first cart mutation and
|
|
145
|
+
persisted into the session cookie PAIR — the httpOnly `forgecart-session` plus
|
|
146
|
+
its JS-readable `forgecart-session-client` mirror, whose one contract lives in
|
|
147
|
+
[`src/lib/session-cookies.ts`](src/lib/session-cookies.ts) — by the action
|
|
148
|
+
runner. The `/__fc/track` and `/__fc/identify` relays may **establish** that
|
|
149
|
+
pair for pure browsers but never replace an existing cookie, and the tracker
|
|
150
|
+
defers its first flush so a fast add-to-cart wins the first-touch race. The
|
|
151
|
+
mirror is what the browser's shop socket boots its auth from: written without
|
|
152
|
+
it, a relay-established session would never reach the client at all.
|
|
153
|
+
|
|
154
|
+
## Account links
|
|
155
|
+
|
|
156
|
+
Two routes exist because the PLATFORM links to them from the e-mails it sends
|
|
157
|
+
a shopper, not because anything in this storefront navigates there:
|
|
158
|
+
|
|
159
|
+
| Route | Mail | What it does |
|
|
160
|
+
| ---------------------------- | ------------------- | ------------------------------------------------------------ |
|
|
161
|
+
| `/verify?token=…` | e-mail verification | consumes the token on RENDER and reports the outcome |
|
|
162
|
+
| `/reset-password?token=…` | password reset | renders the new-password form; the SUBMIT consumes the token |
|
|
163
|
+
| `/reset-password` (no token) | — | asks for an address and has a new link sent |
|
|
164
|
+
|
|
165
|
+
**Do not rename or prefix them.** The path and the `?token=` parameter are the
|
|
166
|
+
contract with the mail templates, and a link already sitting in a shopper's
|
|
167
|
+
inbox cannot be re-issued. They are in the once-per-store set
|
|
168
|
+
([`src/lib/locale/grammar.ts`](src/lib/locale/grammar.ts)), so they never carry
|
|
169
|
+
a locale prefix, and in `NOINDEX_PATHS`
|
|
170
|
+
([`src/lib/seo/noindex.ts`](src/lib/seo/noindex.ts)), so they are `noindex`,
|
|
171
|
+
absent from the XML sitemap and `Disallow`ed in `robots.txt` — a crawler that
|
|
172
|
+
fetched one would spend a real shopper's one-shot token.
|
|
173
|
+
|
|
174
|
+
The COPY is yours to rewrite. The states behind it are not invented per page:
|
|
175
|
+
[`src/lib/account/`](src/lib/account/) maps the shop API's `ErrorCode`s onto a
|
|
176
|
+
small state set (verified / link-invalid / wrong-store / …) that both pages
|
|
177
|
+
render, and each surface stamps `data-fc-account-mode` + `data-fc-account-state`
|
|
178
|
+
onto the element it renders, so the state is readable from the served HTML.
|
|
179
|
+
|
|
180
|
+
## Project layout
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
components.json # the shadcn/ui CLI's config: style, stylesheet, aliases
|
|
184
|
+
src/
|
|
185
|
+
lib/
|
|
186
|
+
forgecart.ts # server-only shop client + typed reads + SDK types
|
|
187
|
+
format.ts # pure display helpers (client-safe)
|
|
188
|
+
shop-action.ts # per-request action runner (envelope + dispose)
|
|
189
|
+
action-result.ts # ActionResult<T> + fallback error
|
|
190
|
+
cart-actions.ts # cart Server Actions (incl. coupons)
|
|
191
|
+
checkout-actions.ts # checkout Server Actions
|
|
192
|
+
cart-context.tsx # client cart state + typed error channel
|
|
193
|
+
account-actions.ts # password-reset Server Actions
|
|
194
|
+
account/ # pure account-link rules (token, states, ErrorCode map)
|
|
195
|
+
components/
|
|
196
|
+
ui/ # the vendored shadcn/ui components + their cn helper
|
|
197
|
+
Header.tsx # top nav with live cart count
|
|
198
|
+
ProductCard.tsx # product tile
|
|
199
|
+
ProductPurchase.tsx # variant picker + purchase options + add-to-cart
|
|
200
|
+
CartView.tsx # cart lines, coupons, totals, checkout CTA
|
|
201
|
+
checkout/
|
|
202
|
+
CheckoutFlow.tsx # the stepped flow orchestrator
|
|
203
|
+
AddressStep.tsx # autocomplete + countries + phone
|
|
204
|
+
RatesStep.tsx # grouped radio-card rate selection
|
|
205
|
+
PaymentFormEmbed.tsx # origin-checked hosted-form host
|
|
206
|
+
account/
|
|
207
|
+
AccountMessage.tsx # the one outcome block both account pages use
|
|
208
|
+
ResetPasswordForm.tsx # set a new password from a reset token
|
|
209
|
+
RequestPasswordResetForm.tsx # ask for a reset link
|
|
210
|
+
app/
|
|
211
|
+
layout.tsx # html shell + header/footer + CartProvider
|
|
212
|
+
globals.css # Tailwind + the :root brand tokens + @theme inline
|
|
213
|
+
error.tsx # route render backstop
|
|
214
|
+
global-error.tsx # root render backstop
|
|
215
|
+
page.tsx # home: featured products
|
|
216
|
+
products/ # grid + [slug] detail
|
|
217
|
+
cart/page.tsx # cart
|
|
218
|
+
checkout/page.tsx # the stepped checkout
|
|
219
|
+
verify/page.tsx # e-mail verification link target
|
|
220
|
+
reset-password/page.tsx # password-reset link target + request form
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Development
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npm install
|
|
227
|
+
npm run dev # Turbopack dev server (what workspace pods run)
|
|
228
|
+
npm run build # production build (what deploys run; see Procfile)
|
|
229
|
+
```
|