create-brainerce-store 1.84.0 → 1.86.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/README.md +130 -92
- package/dist/index.js +39 -17
- package/messages/en.json +9 -2
- package/messages/he.json +9 -2
- package/package.json +1 -1
- package/templates/nextjs/base/.env.local.ejs +10 -1
- package/templates/nextjs/base/AGENTS.md.ejs +229 -207
- package/templates/nextjs/base/AI-GUIDE.md +4 -0
- package/templates/nextjs/base/CLAUDE.md.ejs +240 -218
- package/templates/nextjs/base/scripts/connect.mjs +115 -18
- package/templates/nextjs/base/scripts/env-file.mjs +93 -0
- package/templates/nextjs/base/scripts/fetch-store-info.mjs +109 -83
- package/templates/nextjs/base/src/app/api/auth/reset-callback/route.ts +2 -1
- package/templates/nextjs/base/src/app/category/[slug]/page.tsx +2 -0
- package/templates/nextjs/base/src/app/checkout/page.tsx +1325 -1302
- package/templates/nextjs/base/src/app/forgot-password/page.tsx +3 -2
- package/templates/nextjs/base/src/app/icon.svg +11 -0
- package/templates/nextjs/base/src/app/layout.tsx.ejs +50 -21
- package/templates/nextjs/base/src/app/opengraph-image.tsx +5 -1
- package/templates/nextjs/base/src/app/reset-password/page.tsx +25 -138
- package/templates/nextjs/base/src/app/reset-password/reset-password-form.tsx +138 -0
- package/templates/nextjs/base/src/components/account/order-payment-block.tsx +17 -2
- package/templates/nextjs/base/src/components/auth/oauth-buttons.tsx +174 -137
- package/templates/nextjs/base/src/components/checkout/payment-step.tsx +130 -12
- package/templates/nextjs/base/src/core/lib/store-name.ts.ejs +56 -0
- package/templates/nextjs/base/src/core/lib/tracking.ts +25 -14
- package/templates/nextjs/base/src/core/lib/use-store-name.ts +18 -0
- package/templates/nextjs/base/src/ui/home/hero-section.tsx +27 -27
- package/templates/nextjs/base/src/ui/layout/header-search.tsx +2 -2
- package/templates/nextjs/base/src/ui/layout/site-footer.tsx.ejs +160 -160
- package/templates/nextjs/base/src/ui/layout/site-header.tsx.ejs +2 -2
- package/templates/nextjs/base/src/ui/product/price-display.tsx +85 -62
- package/templates/nextjs/base/src/ui/product/product-client-section.tsx +539 -537
- package/templates/nextjs/designs/atelier/app-overlay/layout.tsx.ejs +374 -345
- package/templates/nextjs/designs/atelier/globals.css +408 -402
- package/templates/nextjs/designs/atelier/messages-patch/en.json +91 -96
- package/templates/nextjs/designs/atelier/messages-patch/he.json +91 -96
- package/templates/nextjs/designs/atelier/ui/cart/cart-bundle-offer.tsx +131 -128
- package/templates/nextjs/designs/atelier/ui/cart/cart-item.tsx +2 -1
- package/templates/nextjs/designs/atelier/ui/cart/cart-upgrade-banner.tsx +222 -219
- package/templates/nextjs/designs/atelier/ui/home/category-tiles.tsx +178 -151
- package/templates/nextjs/designs/atelier/ui/home/hero-section.tsx +61 -31
- package/templates/nextjs/designs/atelier/ui/layout/header-search.tsx +2 -2
- package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +161 -155
- package/templates/nextjs/designs/atelier/ui/product/frequently-bought-together.tsx +232 -227
- package/templates/nextjs/designs/atelier/ui/product/price-display.tsx +90 -69
- package/templates/nextjs/designs/atelier/ui/product/product-card.tsx +271 -268
- package/templates/nextjs/designs/atelier/ui/product/product-client-section.tsx +560 -553
- package/templates/nextjs/designs/atelier/ui/product/recommendation-section.tsx +113 -110
- package/templates/nextjs/designs/atelier/ui/shared/no-photo-tile.tsx +73 -0
- package/templates/nextjs/ui-canvas/product/price-display.tsx +73 -52
- package/templates/nextjs/ui-canvas/product/product-client-section.tsx +428 -426
package/README.md
CHANGED
|
@@ -1,92 +1,130 @@
|
|
|
1
|
-
# create-brainerce-store
|
|
2
|
-
|
|
3
|
-
Scaffold a production-ready e-commerce storefront connected to [Brainerce](https://brainerce.com) — cart, checkout, variants, coupons, auth, SEO, and i18n wired out of the box.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm create brainerce-store@latest my-store -- --connection-id vc_xxxxxxxx
|
|
7
|
-
# or
|
|
8
|
-
pnpm create brainerce-store my-store --connection-id vc_xxxxxxxx
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Run it with no flags for a fully interactive setup. The CLI fetches your store's name, currency, language, and locales from the connection ID (`vc_*`, from your Brainerce dashboard), so the generated store matches the real channel from the first `dev` run.
|
|
12
|
-
|
|
13
|
-
## The design
|
|
14
|
-
|
|
15
|
-
Every scaffold ships **Atelier** — a complete, AI-built storefront look (not just a color theme): warm editorial gallery with soft washes + glass surfaces, a floating hero collage, near-full-bleed product grid, styled popover dropdowns, a mini-cart drawer that opens on add-to-cart, and an RTL-safe fly-to-cart micro-interaction. Fully de-branded — driven by your store's name, catalog and dashboard content.
|
|
16
|
-
|
|
17
|
-
It's a starting point, not a lock-in: the entire look lives in `src/ui/` + `globals.css` and is designed to be rewritten (see below). The shipped texts are **
|
|
18
|
-
|
|
19
|
-
## Blank-canvas mode
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm create brainerce-store@latest my-store -- --connection-id vc_xxx --canvas
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`--canvas` ships `src/ui/` as bare, semantic, essentially-unstyled skeletons (`DESIGN ME` markers included) with zero aesthetic opinion — commerce stays fully wired. It's the starting point for designing a storefront from scratch with an AI session, and the authoring substrate for new gallery designs. Canvas mode skips the design pack entirely.
|
|
26
|
-
|
|
27
|
-
## AI-first by default
|
|
28
|
-
|
|
29
|
-
Every scaffolded store is ready to be redesigned by an AI coding session:
|
|
30
|
-
|
|
31
|
-
- **`CLAUDE.md`** — auto-loaded by Claude Code; teaches the session the one rule (`core/` is the platform's, `ui/` is yours) before it touches anything.
|
|
32
|
-
- **`AGENTS.md`** — the same context for other agentic tools.
|
|
33
|
-
- **`/design <your art-direction brief>`** — a ready-made slash command that walks the full redesign: concept → implementation in `ui/` → verification (tsc, real browser, screenshots, RTL checks).
|
|
34
|
-
- **`AI-GUIDE.md`** — the design contract: file map, per-page hook contracts, verification loop, and hard-won RTL/i18n gotchas from real agency builds.
|
|
35
|
-
|
|
36
|
-
## Architecture (core/ui)
|
|
37
|
-
|
|
38
|
-
- `src/core/` — platform-owned commerce: client, auth, providers, and data/behavior hooks that return state + handlers, never JSX. Don't modify.
|
|
39
|
-
- `src/ui/` — 100% replaceable presentation. Delete-and-rebuild is encouraged; the store keeps working.
|
|
40
|
-
- `src/app/` — thin routes composing the two (~10 lines each).
|
|
41
|
-
- An ESLint boundary rule enforces the split in both directions.
|
|
42
|
-
|
|
43
|
-
Full details: [TEMPLATE-ARCHITECTURE.md](./TEMPLATE-ARCHITECTURE.md).
|
|
44
|
-
|
|
45
|
-
## Flags
|
|
46
|
-
|
|
47
|
-
| Flag | Description | Default |
|
|
48
|
-
| ---------------------- | ------------------------------------------------------ | ------------------ |
|
|
49
|
-
| `--connection-id <id>` | Brainerce vibe-coded connection ID (`vc_*`) | prompted |
|
|
50
|
-
| `--
|
|
51
|
-
| `--
|
|
52
|
-
| `--
|
|
53
|
-
| `--
|
|
54
|
-
| `--
|
|
55
|
-
| `--
|
|
56
|
-
| `--no-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
# create-brainerce-store
|
|
2
|
+
|
|
3
|
+
Scaffold a production-ready e-commerce storefront connected to [Brainerce](https://brainerce.com) — cart, checkout, variants, coupons, auth, SEO, and i18n wired out of the box.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create brainerce-store@latest my-store -- --connection-id vc_xxxxxxxx
|
|
7
|
+
# or
|
|
8
|
+
pnpm create brainerce-store my-store --connection-id vc_xxxxxxxx
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Run it with no flags for a fully interactive setup. The CLI fetches your store's name, currency, language, and locales from the connection ID (`vc_*`, from your Brainerce dashboard), so the generated store matches the real channel from the first `dev` run.
|
|
12
|
+
|
|
13
|
+
## The design
|
|
14
|
+
|
|
15
|
+
Every scaffold ships **Atelier** — a complete, AI-built storefront look (not just a color theme): warm editorial gallery with soft washes + glass surfaces, a floating hero collage, near-full-bleed product grid, styled popover dropdowns, a mini-cart drawer that opens on add-to-cart, and an RTL-safe fly-to-cart micro-interaction. Fully de-branded — driven by your store's name, catalog and dashboard content.
|
|
16
|
+
|
|
17
|
+
It's a starting point, not a lock-in: the entire look lives in `src/ui/` + `globals.css` and is designed to be rewritten (see below). The shipped texts are **vertical-neutral placeholder content** (like any template's sample copy: they name no product category) — your catalog replaces the imagery automatically, and the copy (hero, story, footer tagline) lives in `messages/` ready to be rewritten in your own voice by you or an AI session. On a brand-new store with no products yet, the homepage hero shows a designed placeholder that swaps for your first published products.
|
|
18
|
+
|
|
19
|
+
## Blank-canvas mode
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm create brainerce-store@latest my-store -- --connection-id vc_xxx --canvas
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`--canvas` ships `src/ui/` as bare, semantic, essentially-unstyled skeletons (`DESIGN ME` markers included) with zero aesthetic opinion — commerce stays fully wired. It's the starting point for designing a storefront from scratch with an AI session, and the authoring substrate for new gallery designs. Canvas mode skips the design pack entirely.
|
|
26
|
+
|
|
27
|
+
## AI-first by default
|
|
28
|
+
|
|
29
|
+
Every scaffolded store is ready to be redesigned by an AI coding session:
|
|
30
|
+
|
|
31
|
+
- **`CLAUDE.md`** — auto-loaded by Claude Code; teaches the session the one rule (`core/` is the platform's, `ui/` is yours) before it touches anything.
|
|
32
|
+
- **`AGENTS.md`** — the same context for other agentic tools.
|
|
33
|
+
- **`/design <your art-direction brief>`** — a ready-made slash command that walks the full redesign: concept → implementation in `ui/` → verification (tsc, real browser, screenshots, RTL checks).
|
|
34
|
+
- **`AI-GUIDE.md`** — the design contract: file map, per-page hook contracts, verification loop, and hard-won RTL/i18n gotchas from real agency builds.
|
|
35
|
+
|
|
36
|
+
## Architecture (core/ui)
|
|
37
|
+
|
|
38
|
+
- `src/core/` — platform-owned commerce: client, auth, providers, and data/behavior hooks that return state + handlers, never JSX. Don't modify.
|
|
39
|
+
- `src/ui/` — 100% replaceable presentation. Delete-and-rebuild is encouraged; the store keeps working.
|
|
40
|
+
- `src/app/` — thin routes composing the two (~10 lines each).
|
|
41
|
+
- An ESLint boundary rule enforces the split in both directions.
|
|
42
|
+
|
|
43
|
+
Full details: [TEMPLATE-ARCHITECTURE.md](./TEMPLATE-ARCHITECTURE.md).
|
|
44
|
+
|
|
45
|
+
## Flags
|
|
46
|
+
|
|
47
|
+
| Flag | Description | Default |
|
|
48
|
+
| ---------------------- | ------------------------------------------------------ | ------------------ |
|
|
49
|
+
| `--connection-id <id>` | Brainerce vibe-coded connection ID (`vc_*`) | prompted |
|
|
50
|
+
| `--defer-connection` | Scaffold now, connect later with `npm run connect` | off |
|
|
51
|
+
| `--canvas` | Bare unstyled `src/ui/` skeletons for AI-driven design | off |
|
|
52
|
+
| `--language <lang>` | Store language (`en`, `he`). **One-way** — see below | fetched from store |
|
|
53
|
+
| `--pkg-manager <pm>` | `npm`, `pnpm`, `yarn`, `bun` | auto-detected |
|
|
54
|
+
| `--framework <fw>` | `nextjs` (Vite/Remix coming) | `nextjs` |
|
|
55
|
+
| `--api-url <url>` | Brainerce API base URL (or `BRAINERCE_API_URL` env) | auto-discovered |
|
|
56
|
+
| `--no-git` | Skip git initialization | git on |
|
|
57
|
+
| `--no-install` | Skip dependency installation | install on |
|
|
58
|
+
|
|
59
|
+
Pass `.` as the project name to scaffold into the current directory.
|
|
60
|
+
|
|
61
|
+
`--language` is the one decision that cannot be changed after scaffolding: it
|
|
62
|
+
picks which `messages/` ship and the `<html lang>` / `dir` of every page.
|
|
63
|
+
`npm run connect` and `npm run setup` refresh the store name and currency from
|
|
64
|
+
the live channel, never the language — a store in the wrong language is
|
|
65
|
+
re-scaffolded, not adjusted. Pass it explicitly when you know it (for example
|
|
66
|
+
`--language he` for a Hebrew merchant); without a channel to read, a deferred
|
|
67
|
+
scaffold assumes `en`.
|
|
68
|
+
|
|
69
|
+
## Building first, connecting later
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm create brainerce-store@latest my-store -- --defer-connection --language he
|
|
73
|
+
cd my-store
|
|
74
|
+
npm run connect # one browser approval; creates a store + channel if needed
|
|
75
|
+
npm run build # NEXT_PUBLIC_* values are baked at build time
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`--defer-connection` writes a placeholder channel id and skips the approval,
|
|
79
|
+
so an AI builder can construct the whole storefront without stopping. The
|
|
80
|
+
scaffold then knows no store: the name it bakes is the project directory name
|
|
81
|
+
and the currency is `USD`. `npm run connect` writes the real channel id and
|
|
82
|
+
runs `npm run setup`, which fetches the real name and currency into
|
|
83
|
+
`.env.local`. At runtime the storefront prefers the live store name and
|
|
84
|
+
currency from the API (server components, `<title>`, JSON-LD, the Open Graph
|
|
85
|
+
card) and falls back to those `.env.local` values, then to the scaffold
|
|
86
|
+
literal, so a connected store is branded correctly on the first request
|
|
87
|
+
without a rebuild. The `NEXT_PUBLIC_*` fallbacks the client bundle carries are
|
|
88
|
+
inlined at build time, though, so **rebuild before deploying and restart a
|
|
89
|
+
running dev server** after connecting — `connect` says so when it finishes.
|
|
90
|
+
|
|
91
|
+
`npm run connect -- --seed-products '[{"name":"Burr Grinder","basePrice":389}]'`
|
|
92
|
+
seeds a starter catalog in the same approval and reports, per product, whether
|
|
93
|
+
it was created and whether it is published to this channel; a product that
|
|
94
|
+
exists but is not published is named so you can publish it from the dashboard
|
|
95
|
+
rather than add it twice. On PowerShell put the JSON in
|
|
96
|
+
`BRAINERCE_SEED_PRODUCTS` instead of the flag.
|
|
97
|
+
|
|
98
|
+
## Reading the connection
|
|
99
|
+
|
|
100
|
+
Before scaffolding, the CLI reads the channel from `/api/vc/<id>/info` so the
|
|
101
|
+
generated project gets the real store name, currency and language rather than
|
|
102
|
+
guesses. It walks production first, then staging, and stops at the first
|
|
103
|
+
environment that answers. When it has to fall back, it says so and names the
|
|
104
|
+
URL the project will be wired to.
|
|
105
|
+
|
|
106
|
+
That endpoint checks the request's `Origin` against the channel's **Domain**
|
|
107
|
+
and **Allowed Origins**, and the match counts the **port**. A Test channel with
|
|
108
|
+
an empty Domain box accepts anything; one with `localhost:3000` recorded will
|
|
109
|
+
refuse a bare `http://localhost`. The CLI therefore presents
|
|
110
|
+
`http://localhost:3000` first and plain `http://localhost` second, and reports
|
|
111
|
+
what every environment said rather than only the last one. A `403` from
|
|
112
|
+
production must never surface as "not found on staging".
|
|
113
|
+
|
|
114
|
+
A channel it cannot read is a hard stop, not a warning. Build-time
|
|
115
|
+
`NEXT_PUBLIC_*` values are inlined into the client bundle and cannot be
|
|
116
|
+
corrected at deploy time, so guessing `USD` / `en` here would ship a storefront
|
|
117
|
+
quoting the wrong currency.
|
|
118
|
+
|
|
119
|
+
## Requirements
|
|
120
|
+
|
|
121
|
+
Node `^20.19.0 || ^22.13.0 || >=24.0.0`.
|
|
122
|
+
|
|
123
|
+
## After scaffolding
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
cd my-store
|
|
127
|
+
pnpm dev # store at http://localhost:3000
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Want a unique look? Open the folder in Claude Code and run `/design <your art-direction brief>`.
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "create-brainerce-store",
|
|
34
|
-
version: "1.
|
|
34
|
+
version: "1.86.0",
|
|
35
35
|
description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
|
|
36
36
|
homepage: "https://brainerce.com",
|
|
37
37
|
repository: {
|
|
@@ -629,8 +629,10 @@ function toJsStringLiteral(value) {
|
|
|
629
629
|
return JSON.stringify(value);
|
|
630
630
|
}
|
|
631
631
|
function toEnvLiteral(value) {
|
|
632
|
-
const clean = stripControlChars(value).replace(
|
|
633
|
-
return `"${clean}"`;
|
|
632
|
+
const clean = stripControlChars(value).replace(/\$/g, "\\$");
|
|
633
|
+
if (!clean.includes('"') && !/\\[nr]/.test(clean)) return `"${clean}"`;
|
|
634
|
+
if (!clean.includes("'")) return `'${clean}'`;
|
|
635
|
+
return `\`${clean.replace(/`/g, "")}\``;
|
|
634
636
|
}
|
|
635
637
|
function isValidLocale(locale) {
|
|
636
638
|
return typeof locale === "string" && /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$/.test(locale);
|
|
@@ -682,15 +684,19 @@ async function scaffold(options) {
|
|
|
682
684
|
/** Raw store name for prose contexts (markdown docs) — JS/env contexts
|
|
683
685
|
* must keep using the escaped storeNameJs / storeNameEnv variants. */
|
|
684
686
|
storeName: cleanStoreName,
|
|
687
|
+
/** The scaffold-time name as a JS literal. In the base template it lands
|
|
688
|
+
* in ONE place, `src/core/lib/store-name.ts.ejs`, as the LAST fallback
|
|
689
|
+
* behind live `storeInfo.name` and `NEXT_PUBLIC_STORE_NAME` — under
|
|
690
|
+
* `--defer-connection` it is only the project directory name, so
|
|
691
|
+
* nothing else may bake it. The atelier `app-overlay/layout.tsx.ejs`
|
|
692
|
+
* resolves the name the same way and splices nothing. */
|
|
685
693
|
storeNameJs: toJsStringLiteral(cleanStoreName),
|
|
686
|
-
/** Pre-escaped for splicing into a JSON string value (no wrapping
|
|
687
|
-
* quotes) — used by the `{{storeName}}` token in messages-patch files. */
|
|
688
|
-
storeNameJsonEscaped: JSON.stringify(cleanStoreName).slice(1, -1),
|
|
689
694
|
titleTemplateJs: toJsStringLiteral(`%s | ${cleanStoreName}`),
|
|
690
695
|
storeNameEnv: toEnvLiteral(cleanStoreName),
|
|
691
696
|
currencyEnv: toEnvLiteral(cleanCurrency),
|
|
692
697
|
apiBaseUrlEnv: toEnvLiteral(cleanApiBaseUrl),
|
|
693
698
|
language: options.language,
|
|
699
|
+
deferred: options.deferred === true,
|
|
694
700
|
direction,
|
|
695
701
|
fontImport: fontConfig.fontImport,
|
|
696
702
|
fontVariable: fontConfig.fontVariable,
|
|
@@ -767,11 +773,7 @@ async function scaffold(options) {
|
|
|
767
773
|
if (!await import_fs_extra.default.pathExists(patchFile)) continue;
|
|
768
774
|
const targetFile = import_path.default.join(targetMessages, file);
|
|
769
775
|
const base = await import_fs_extra.default.readJson(targetFile);
|
|
770
|
-
const
|
|
771
|
-
/\{\{storeName\}\}/g,
|
|
772
|
-
templateVars.storeNameJsonEscaped
|
|
773
|
-
);
|
|
774
|
-
const patch = JSON.parse(patchText);
|
|
776
|
+
const patch = await import_fs_extra.default.readJson(patchFile);
|
|
775
777
|
await import_fs_extra.default.writeJson(targetFile, deepMerge(base, patch), { spaces: 2 });
|
|
776
778
|
}
|
|
777
779
|
}
|
|
@@ -1127,10 +1129,25 @@ async function runSeed(apiBaseUrl, deviceCode, products) {
|
|
|
1127
1129
|
}
|
|
1128
1130
|
const created = Array.isArray(body.created) ? body.created : [];
|
|
1129
1131
|
const failed = Array.isArray(body.failed) ? body.failed : [];
|
|
1130
|
-
|
|
1132
|
+
const reportsPublish = created.some((p) => typeof p.published === "boolean");
|
|
1133
|
+
const live = reportsPublish ? created.filter((p) => p.published !== false) : created;
|
|
1134
|
+
const unpublished = reportsPublish ? created.filter((p) => p.published === false) : [];
|
|
1135
|
+
if (live.length > 0) {
|
|
1131
1136
|
logger.success(
|
|
1132
|
-
` Seeded ${
|
|
1137
|
+
` Seeded${reportsPublish ? " and published" : ""} ${live.length} product(s): ${live.map((p) => p.name).join(", ")}`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
for (const p of unpublished) {
|
|
1141
|
+
logger.info(
|
|
1142
|
+
` Created but NOT published to this sales channel: ${p.name} \u2014 ${p.publishError || "the publish did not land"}`
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
if (unpublished.length > 0) {
|
|
1146
|
+
logger.info(
|
|
1147
|
+
` ${unpublished.length} product(s) exist in the store but will not show on the storefront.`
|
|
1133
1148
|
);
|
|
1149
|
+
logger.info(" Do NOT re-add them. Publish them from the dashboard: Products > the product >");
|
|
1150
|
+
logger.info(" sales channels > toggle this channel on.");
|
|
1134
1151
|
}
|
|
1135
1152
|
for (const f of failed) {
|
|
1136
1153
|
logger.info(` Not created: ${f.name} \u2014 ${f.reason}`);
|
|
@@ -1475,6 +1492,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
|
|
|
1475
1492
|
language: language || storeInfo.language,
|
|
1476
1493
|
i18n: storeInfo.i18n,
|
|
1477
1494
|
scaffoldInPlace,
|
|
1495
|
+
deferred: connectionId === DEFERRED_CONNECTION_ID,
|
|
1478
1496
|
canvas,
|
|
1479
1497
|
design: canvas ? void 0 : design || "atelier"
|
|
1480
1498
|
});
|
|
@@ -1560,7 +1578,10 @@ Your store will be running at http://localhost:3000
|
|
|
1560
1578
|
`
|
|
1561
1579
|
This store is not connected yet. \`connect\` opens one browser approval,
|
|
1562
1580
|
writes the sales channel id into .env.local, and creates a store and a
|
|
1563
|
-
channel for you if you have neither.
|
|
1581
|
+
channel for you if you have neither. It then fetches the store's real name
|
|
1582
|
+
and currency into .env.local \u2014 until it runs, the storefront calls itself
|
|
1583
|
+
"${projectName}". Those values are baked in at build time, so build (or
|
|
1584
|
+
restart \`dev\`) AFTER connecting.
|
|
1564
1585
|
|
|
1565
1586
|
Know what this store sells? Seed the catalog in that same command \u2014 the
|
|
1566
1587
|
approval opens a short window that needs no admin key, and it is the only
|
|
@@ -1576,9 +1597,10 @@ instead.) Without it the store starts empty and products come from the dashboard
|
|
|
1576
1597
|
Your store will be running at http://localhost:3000`);
|
|
1577
1598
|
logger.info(
|
|
1578
1599
|
`
|
|
1579
|
-
The storefront ships with
|
|
1580
|
-
|
|
1581
|
-
|
|
1600
|
+
The storefront ships with vertical-neutral placeholder copy (hero, story,
|
|
1601
|
+
footer tagline) \u2014 a starting point, not the merchant's voice. Your real
|
|
1602
|
+
products replace the imagery automatically; the texts live in messages/
|
|
1603
|
+
and should be rewritten in the store's own words (or let an AI session do it).`
|
|
1582
1604
|
);
|
|
1583
1605
|
logger.info(
|
|
1584
1606
|
`
|
package/messages/en.json
CHANGED
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
"monthDecember": "December",
|
|
63
63
|
"giftCard": "Gift card",
|
|
64
64
|
"amountDue": "Amount due",
|
|
65
|
-
"amountCharged": "Amount charged"
|
|
65
|
+
"amountCharged": "Amount charged",
|
|
66
|
+
"estimatedPrice": "Estimated",
|
|
67
|
+
"estimatedPriceTooltip": "Price shown in {currency} for reference. You'll be charged in {baseCurrency}."
|
|
66
68
|
},
|
|
67
69
|
"nav": {
|
|
68
70
|
"products": "Products",
|
|
@@ -247,12 +249,15 @@
|
|
|
247
249
|
"payment": "Payment",
|
|
248
250
|
"securePayment": "Secure payment",
|
|
249
251
|
"preparingPayment": "Preparing payment...",
|
|
252
|
+
"paymentModalClosed": "The payment page was closed. Open it again when you are ready, or go back a step.",
|
|
253
|
+
"openPaymentModal": "Continue to payment",
|
|
250
254
|
"loadingPaymentOptions": "Loading payment options...",
|
|
251
255
|
"paymentNotConfigured": "Payment Not Configured",
|
|
252
256
|
"paymentNotConfiguredDesc": "Payment has not been set up for this store yet. Please contact the store owner.",
|
|
253
257
|
"paymentError": "Payment Error",
|
|
254
258
|
"paymentUnavailable": "Payment is unavailable for this store right now. Please contact the store.",
|
|
255
259
|
"paymentRedirectBlocked": "Payment redirect blocked for security reasons. Please contact support.",
|
|
260
|
+
"paymentLoadFailed": "The payment form didn't load. You can continue in a new tab instead.",
|
|
256
261
|
"sandboxTitle": "Sandbox Mode",
|
|
257
262
|
"sandboxDescription": "This is a test order. No real payment will be processed.",
|
|
258
263
|
"completeTestOrder": "Complete Test Order",
|
|
@@ -371,6 +376,7 @@
|
|
|
371
376
|
"lastNamePlaceholder": "Doe",
|
|
372
377
|
"atLeastChars": "At least 8 characters",
|
|
373
378
|
"orContinueWith": "or continue with",
|
|
379
|
+
"socialLoginUnavailable": "Sign-in with {provider} is not available right now. Please use your email and password.",
|
|
374
380
|
"tooShort": "Too short",
|
|
375
381
|
"weak": "Weak",
|
|
376
382
|
"fair": "Fair",
|
|
@@ -494,7 +500,8 @@
|
|
|
494
500
|
"paid": "Paid",
|
|
495
501
|
"refundedStatus": "Refunded",
|
|
496
502
|
"partiallyRefundedStatus": "Partially refunded",
|
|
497
|
-
"statusTimeline": "Timeline"
|
|
503
|
+
"statusTimeline": "Timeline",
|
|
504
|
+
"downloadInvoice": "Download invoice"
|
|
498
505
|
},
|
|
499
506
|
"checkoutAddress": {
|
|
500
507
|
"saveToProfile": "Save this address to my profile?",
|
package/messages/he.json
CHANGED
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
"monthDecember": "דצמבר",
|
|
63
63
|
"giftCard": "כרטיס מתנה",
|
|
64
64
|
"amountDue": "לתשלום",
|
|
65
|
-
"amountCharged": "חויב בפועל"
|
|
65
|
+
"amountCharged": "חויב בפועל",
|
|
66
|
+
"estimatedPrice": "מוערך",
|
|
67
|
+
"estimatedPriceTooltip": "המחיר המוצג הוא הערכה ב-{currency}. החיוב בפועל יתבצע ב-{baseCurrency}."
|
|
66
68
|
},
|
|
67
69
|
"nav": {
|
|
68
70
|
"products": "מוצרים",
|
|
@@ -247,12 +249,15 @@
|
|
|
247
249
|
"payment": "תשלום",
|
|
248
250
|
"securePayment": "תשלום מאובטח",
|
|
249
251
|
"preparingPayment": "מכין תשלום...",
|
|
252
|
+
"paymentModalClosed": "דף התשלום נסגר. כשתהיו מוכנים, פתחו אותו שוב או חזרו לשלב הקודם.",
|
|
253
|
+
"openPaymentModal": "המשך לתשלום",
|
|
250
254
|
"loadingPaymentOptions": "טוען אפשרויות תשלום...",
|
|
251
255
|
"paymentNotConfigured": "תשלום לא מוגדר",
|
|
252
256
|
"paymentNotConfiguredDesc": "התשלום עדיין לא הוגדר לחנות זו. אנא פנו לבעל החנות.",
|
|
253
257
|
"paymentError": "שגיאת תשלום",
|
|
254
258
|
"paymentUnavailable": "לא ניתן לשלם בחנות זו כרגע. ניתן לפנות לחנות.",
|
|
255
259
|
"paymentRedirectBlocked": "הפניית התשלום נחסמה מטעמי אבטחה. אנא פנו לתמיכה.",
|
|
260
|
+
"paymentLoadFailed": "טופס התשלום לא נטען. אפשר להמשיך בכרטיסייה חדשה במקום.",
|
|
256
261
|
"sandboxTitle": "מצב בדיקה",
|
|
257
262
|
"sandboxDescription": "זוהי הזמנת בדיקה. לא יבוצע תשלום אמיתי.",
|
|
258
263
|
"completeTestOrder": "השלם הזמנת בדיקה",
|
|
@@ -371,6 +376,7 @@
|
|
|
371
376
|
"lastNamePlaceholder": "ישראלי",
|
|
372
377
|
"atLeastChars": "לפחות 8 תווים",
|
|
373
378
|
"orContinueWith": "או המשיכו עם",
|
|
379
|
+
"socialLoginUnavailable": "ההתחברות דרך {provider} אינה זמינה כרגע. אפשר להתחבר עם אימייל וסיסמה.",
|
|
374
380
|
"tooShort": "קצרה מדי",
|
|
375
381
|
"weak": "חלשה",
|
|
376
382
|
"fair": "סבירה",
|
|
@@ -494,7 +500,8 @@
|
|
|
494
500
|
"paid": "שולם",
|
|
495
501
|
"refundedStatus": "הוחזר",
|
|
496
502
|
"partiallyRefundedStatus": "הוחזר חלקית",
|
|
497
|
-
"statusTimeline": "ציר זמן"
|
|
503
|
+
"statusTimeline": "ציר זמן",
|
|
504
|
+
"downloadInvoice": "הורדת חשבונית"
|
|
498
505
|
},
|
|
499
506
|
"checkoutAddress": {
|
|
500
507
|
"saveToProfile": "לשמור כתובת זו בפרופיל שלי?",
|
package/package.json
CHANGED
|
@@ -4,7 +4,16 @@ NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID=<%= connectionId %>
|
|
|
4
4
|
# @deprecated will be removed when SDK 2.0 ships.
|
|
5
5
|
NEXT_PUBLIC_BRAINERCE_CONNECTION_ID=<%= connectionId %>
|
|
6
6
|
|
|
7
|
-
# Store
|
|
7
|
+
# Store name and currency. The storefront prefers the LIVE values from the
|
|
8
|
+
# Brainerce API wherever it can (server components, <title>, JSON-LD, the OG
|
|
9
|
+
# card); these are the fallback the client bundle carries for the moments
|
|
10
|
+
# before that data arrives, and what renders if the API is unreachable.
|
|
11
|
+
# Refresh them with `npm run setup` (`npm run connect` runs it for you).
|
|
12
|
+
#
|
|
13
|
+
# ⛔ NEXT_PUBLIC_* values are inlined at BUILD time. Editing this file changes
|
|
14
|
+
# nothing already built: run `npm run build` before deploying, and restart a
|
|
15
|
+
# running dev server. Values are quoted the way Next.js's env loader reads
|
|
16
|
+
# them; `setup` and `connect` write the same form (see scripts/env-file.mjs).
|
|
8
17
|
NEXT_PUBLIC_STORE_NAME=<%- storeNameEnv %>
|
|
9
18
|
NEXT_PUBLIC_STORE_CURRENCY=<%- currencyEnv %>
|
|
10
19
|
|