create-brainerce-store 1.52.4 → 1.53.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.
@@ -1,251 +1,255 @@
1
- <% if (i18nEnabled) { %>
2
- import type { Metadata } from 'next';
3
- import { Assistant, Frank_Ruhl_Libre } from 'next/font/google';
4
- import { StoreProvider } from '@/core/providers/store-provider';
5
- import { AnnouncementBar } from '@/ui/layout/announcement-bar';
6
- import { SiteHeader } from '@/ui/layout/site-header';
7
- import { SiteFooter } from '@/ui/layout/site-footer';
8
- import { CartDrawer } from '@/ui/cart/cart-drawer';
9
- import { BrainerceBotWidget } from '@/components/brainerce-bot';
10
- import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
11
- import { getDirection, supportedLocales } from '@/i18n';
12
- import { getNonce } from '@/core/lib/nonce';
13
- import '../globals.css';
14
-
15
- // Atelier: Assistant (clean Hebrew grotesque) for body, Frank Ruhl Libre
16
- // (Hebrew serif — the gallery voice) for display. Exposed as CSS variables
17
- // consumed by tailwind's font-sans / font-serif and the h1-h3 base styles.
18
- const sans = Assistant({
19
- subsets: ['hebrew', 'latin'],
20
- weight: ['400', '500', '600', '700'],
21
- variable: '--font-sans',
22
- });
23
- const serif = Frank_Ruhl_Libre({
24
- subsets: ['hebrew', 'latin'],
25
- weight: ['400', '500', '700'],
26
- variable: '--font-serif',
27
- });
28
-
29
- const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
30
-
31
- export const metadata: Metadata = {
32
- metadataBase: new URL(baseUrl),
33
- title: {
34
- default: <%- storeNameJs %>,
35
- template: <%- titleTemplateJs %>,
36
- },
37
- description: <%- storeNameJs %>,
38
- alternates: {
39
- canonical: '/',
40
- },
41
- openGraph: {
42
- siteName: <%- storeNameJs %>,
43
- type: 'website',
44
- },
45
- robots: {
46
- index: true,
47
- follow: true,
48
- },
49
- };
50
-
51
- const organizationJsonLd = {
52
- '@context': 'https://schema.org',
53
- '@type': 'Organization',
54
- name: <%- storeNameJs %>,
55
- url: baseUrl,
56
- };
57
-
58
- export function generateStaticParams() {
59
- return supportedLocales.map((locale) => ({ locale }));
60
- }
61
-
62
- export default async function RootLayout({
63
- children,
64
- params,
65
- }: {
66
- children: React.ReactNode;
67
- params: Promise<{ locale: string }>;
68
- }) {
69
- const { locale } = await params;
70
- const dir = getDirection(locale);
71
- const nonce = await getNonce();
72
-
73
- // Merchant-driven layout chrome — fetched server-side. Each call falls back
74
- // to null/[] on 404 so the layout never crashes when the merchant hasn't
75
- // seeded a particular content type yet. New stores ship with default rows
76
- // seeded by the backend (StoresService.seedDefaultContent).
77
- const client = getServerClient(locale);
78
- const [announcements, siteHeader, siteFooter, storeInfo] = await Promise.all([
79
- client.content.announcement.list(locale).catch(() => []),
80
- client.content.header.get('main', locale).catch(() => null),
81
- client.content.footer.get('main', locale).catch(() => null),
82
- // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
83
- // render with the real currency, feature flags, and i18n config at frame 0
84
- // — without this, Googlebot sees USD/defaults baked into the HTML.
85
- fetchStoreInfo(locale),
86
- ]);
87
-
88
- return (
89
- <html lang={locale} dir={dir}>
90
- <head>
91
- <script
92
- type="application/ld+json"
93
- nonce={nonce}
94
- suppressHydrationWarning
95
- dangerouslySetInnerHTML={{
96
- __html: JSON.stringify(organizationJsonLd)
97
- .replace(/</g, '\\u003c')
98
- .replace(/>/g, '\\u003e')
99
- .replace(/&/g, '\\u0026'),
100
- }}
101
- />
102
- {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
103
- SPA route changes. Cookieless (no consent banner needed); the
104
- merchant can toggle it per sales channel in the dashboard. */}
105
- <script
106
- defer
107
- src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
108
- data-channel={
109
- process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
110
- }
111
- nonce={nonce}
112
- suppressHydrationWarning
113
- />
114
- </head>
115
- <body className={`${sans.variable} ${serif.variable} font-sans`}>
116
- <StoreProvider locale={locale} initialStoreInfo={storeInfo}>
117
- <div className="min-h-screen flex flex-col">
118
- <AnnouncementBar announcements={announcements} />
119
- <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
120
- <main className="flex-1">{children}</main>
121
- <SiteFooter footer={siteFooter} storeName={<%- storeNameJs %>} locale={locale} />
122
- </div>
123
- <CartDrawer />
124
- <BrainerceBotWidget />
125
- </StoreProvider>
126
- </body>
127
- </html>
128
- );
129
- }
130
- <% } else { %>
131
- import type { Metadata } from 'next';
132
- import { Assistant, Frank_Ruhl_Libre } from 'next/font/google';
133
- import { StoreProvider } from '@/core/providers/store-provider';
134
- import { AnnouncementBar } from '@/ui/layout/announcement-bar';
135
- import { SiteHeader } from '@/ui/layout/site-header';
136
- import { SiteFooter } from '@/ui/layout/site-footer';
137
- import { CartDrawer } from '@/ui/cart/cart-drawer';
138
- import { BrainerceBotWidget } from '@/components/brainerce-bot';
139
- import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
140
- import { getNonce } from '@/core/lib/nonce';
141
- import './globals.css';
142
-
143
- // Atelier: Assistant (clean Hebrew grotesque) for body, Frank Ruhl Libre
144
- // (Hebrew serif the gallery voice) for display. Exposed as CSS variables
145
- // consumed by tailwind's font-sans / font-serif and the h1-h3 base styles.
146
- const sans = Assistant({
147
- subsets: ['hebrew', 'latin'],
148
- weight: ['400', '500', '600', '700'],
149
- variable: '--font-sans',
150
- });
151
- const serif = Frank_Ruhl_Libre({
152
- subsets: ['hebrew', 'latin'],
153
- weight: ['400', '500', '700'],
154
- variable: '--font-serif',
155
- });
156
-
157
- const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
158
-
159
- export const metadata: Metadata = {
160
- metadataBase: new URL(baseUrl),
161
- title: {
162
- default: <%- storeNameJs %>,
163
- template: <%- titleTemplateJs %>,
164
- },
165
- description: <%- storeNameJs %>,
166
- alternates: {
167
- canonical: '/',
168
- },
169
- openGraph: {
170
- siteName: <%- storeNameJs %>,
171
- locale: '<%= ogLocale %>',
172
- type: 'website',
173
- },
174
- robots: {
175
- index: true,
176
- follow: true,
177
- },
178
- };
179
-
180
- const organizationJsonLd = {
181
- '@context': 'https://schema.org',
182
- '@type': 'Organization',
183
- name: <%- storeNameJs %>,
184
- url: baseUrl,
185
- };
186
-
187
- export default async function RootLayout({
188
- children,
189
- }: {
190
- children: React.ReactNode;
191
- }) {
192
- const nonce = await getNonce();
193
-
194
- // Merchant-driven layout chrome — fetched server-side. Each call falls back
195
- // to null/[] on 404 so the layout never crashes when the merchant hasn't
196
- // seeded a particular content type yet. New stores ship with default rows
197
- // seeded by the backend (StoresService.seedDefaultContent).
198
- const client = getServerClient();
199
- const [announcements, siteHeader, siteFooter, storeInfo] = await Promise.all([
200
- client.content.announcement.list().catch(() => []),
201
- client.content.header.get('main').catch(() => null),
202
- client.content.footer.get('main').catch(() => null),
203
- // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
204
- // render with the real currency, feature flags, and i18n config at frame 0
205
- // without this, Googlebot sees USD/defaults baked into the HTML.
206
- fetchStoreInfo(),
207
- ]);
208
-
209
- return (
210
- <html lang="<%= language %>" dir="<%= direction %>">
211
- <head>
212
- <script
213
- type="application/ld+json"
214
- nonce={nonce}
215
- suppressHydrationWarning
216
- dangerouslySetInnerHTML={{
217
- __html: JSON.stringify(organizationJsonLd)
218
- .replace(/</g, '\\u003c')
219
- .replace(/>/g, '\\u003e')
220
- .replace(/&/g, '\\u0026'),
221
- }}
222
- />
223
- {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
224
- SPA route changes. Cookieless (no consent banner needed); the
225
- merchant can toggle it per sales channel in the dashboard. */}
226
- <script
227
- defer
228
- src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
229
- data-channel={
230
- process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
231
- }
232
- nonce={nonce}
233
- suppressHydrationWarning
234
- />
235
- </head>
236
- <body className={`${sans.variable} ${serif.variable} font-sans`}>
237
- <StoreProvider initialStoreInfo={storeInfo}>
238
- <div className="min-h-screen flex flex-col">
239
- <AnnouncementBar announcements={announcements} />
240
- <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
241
- <main className="flex-1">{children}</main>
242
- <SiteFooter footer={siteFooter} storeName={<%- storeNameJs %>} />
243
- </div>
244
- <CartDrawer />
245
- <BrainerceBotWidget />
246
- </StoreProvider>
247
- </body>
248
- </html>
249
- );
250
- }
251
- <% } %>
1
+ <% if (i18nEnabled) { %>
2
+ import type { Metadata } from 'next';
3
+ import { Assistant, Frank_Ruhl_Libre } from 'next/font/google';
4
+ import { StoreProvider } from '@/core/providers/store-provider';
5
+ import { AnnouncementBar } from '@/ui/layout/announcement-bar';
6
+ import { SiteHeader } from '@/ui/layout/site-header';
7
+ import { SiteFooter } from '@/ui/layout/site-footer';
8
+ import { CartDrawer } from '@/ui/cart/cart-drawer';
9
+ import { BrainerceBotWidget } from '@/components/brainerce-bot';
10
+ import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
11
+ import { getDirection, getMessages, supportedLocales } from '@/i18n';
12
+ import { getNonce } from '@/core/lib/nonce';
13
+ import '../globals.css';
14
+
15
+ // Atelier: Assistant (clean Hebrew grotesque) for body, Frank Ruhl Libre
16
+ // (Hebrew serif — the gallery voice) for display. Exposed as CSS variables
17
+ // consumed by tailwind's font-sans / font-serif and the h1-h3 base styles.
18
+ const sans = Assistant({
19
+ subsets: ['hebrew', 'latin'],
20
+ weight: ['400', '500', '600', '700'],
21
+ variable: '--font-sans',
22
+ });
23
+ const serif = Frank_Ruhl_Libre({
24
+ subsets: ['hebrew', 'latin'],
25
+ weight: ['400', '500', '700'],
26
+ variable: '--font-serif',
27
+ });
28
+
29
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
30
+
31
+ export const metadata: Metadata = {
32
+ metadataBase: new URL(baseUrl),
33
+ title: {
34
+ default: <%- storeNameJs %>,
35
+ template: <%- titleTemplateJs %>,
36
+ },
37
+ description: <%- storeNameJs %>,
38
+ alternates: {
39
+ canonical: '/',
40
+ },
41
+ openGraph: {
42
+ siteName: <%- storeNameJs %>,
43
+ type: 'website',
44
+ },
45
+ robots: {
46
+ index: true,
47
+ follow: true,
48
+ },
49
+ };
50
+
51
+ const organizationJsonLd = {
52
+ '@context': 'https://schema.org',
53
+ '@type': 'Organization',
54
+ name: <%- storeNameJs %>,
55
+ url: baseUrl,
56
+ };
57
+
58
+ export function generateStaticParams() {
59
+ return supportedLocales.map((locale) => ({ locale }));
60
+ }
61
+
62
+ export default async function RootLayout({
63
+ children,
64
+ params,
65
+ }: {
66
+ children: React.ReactNode;
67
+ params: Promise<{ locale: string }>;
68
+ }) {
69
+ const { locale } = await params;
70
+ const dir = getDirection(locale);
71
+ const nonce = await getNonce();
72
+
73
+ // Merchant-driven layout chrome — fetched server-side. Each call falls back
74
+ // to null/[] on 404 so the layout never crashes when the merchant hasn't
75
+ // seeded a particular content type yet. New stores ship with default rows
76
+ // seeded by the backend (StoresService.seedDefaultContent).
77
+ const client = getServerClient(locale);
78
+ const [announcements, siteHeader, siteFooter, storeInfo, messages] = await Promise.all([
79
+ client.content.announcement.list(locale).catch(() => []),
80
+ client.content.header.get('main', locale).catch(() => null),
81
+ client.content.footer.get('main', locale).catch(() => null),
82
+ // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
83
+ // render with the real currency, feature flags, and i18n config at frame 0
84
+ // — without this, Googlebot sees USD/defaults baked into the HTML.
85
+ fetchStoreInfo(locale),
86
+ // SSR-fetch UI message strings so translated text renders at frame 0 —
87
+ // without this, every page (and every locale switch) shows raw
88
+ // "namespace.key" strings until the client-only fetch resolves.
89
+ getMessages(locale),
90
+ ]);
91
+
92
+ return (
93
+ <html lang={locale} dir={dir}>
94
+ <head>
95
+ <script
96
+ type="application/ld+json"
97
+ nonce={nonce}
98
+ suppressHydrationWarning
99
+ dangerouslySetInnerHTML={{
100
+ __html: JSON.stringify(organizationJsonLd)
101
+ .replace(/</g, '\\u003c')
102
+ .replace(/>/g, '\\u003e')
103
+ .replace(/&/g, '\\u0026'),
104
+ }}
105
+ />
106
+ {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
107
+ SPA route changes. Cookieless (no consent banner needed); the
108
+ merchant can toggle it per sales channel in the dashboard. */}
109
+ <script
110
+ defer
111
+ src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
112
+ data-channel={
113
+ process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
114
+ }
115
+ nonce={nonce}
116
+ suppressHydrationWarning
117
+ />
118
+ </head>
119
+ <body className={`${sans.variable} ${serif.variable} font-sans`}>
120
+ <StoreProvider locale={locale} initialStoreInfo={storeInfo} initialMessages={messages}>
121
+ <div className="min-h-screen flex flex-col">
122
+ <AnnouncementBar announcements={announcements} />
123
+ <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
124
+ <main className="flex-1">{children}</main>
125
+ <SiteFooter footer={siteFooter} storeName={<%- storeNameJs %>} locale={locale} />
126
+ </div>
127
+ <CartDrawer />
128
+ <BrainerceBotWidget />
129
+ </StoreProvider>
130
+ </body>
131
+ </html>
132
+ );
133
+ }
134
+ <% } else { %>
135
+ import type { Metadata } from 'next';
136
+ import { Assistant, Frank_Ruhl_Libre } from 'next/font/google';
137
+ import { StoreProvider } from '@/core/providers/store-provider';
138
+ import { AnnouncementBar } from '@/ui/layout/announcement-bar';
139
+ import { SiteHeader } from '@/ui/layout/site-header';
140
+ import { SiteFooter } from '@/ui/layout/site-footer';
141
+ import { CartDrawer } from '@/ui/cart/cart-drawer';
142
+ import { BrainerceBotWidget } from '@/components/brainerce-bot';
143
+ import { getServerClient, fetchStoreInfo } from '@/core/lib/brainerce';
144
+ import { getNonce } from '@/core/lib/nonce';
145
+ import './globals.css';
146
+
147
+ // Atelier: Assistant (clean Hebrew grotesque) for body, Frank Ruhl Libre
148
+ // (Hebrew serif the gallery voice) for display. Exposed as CSS variables
149
+ // consumed by tailwind's font-sans / font-serif and the h1-h3 base styles.
150
+ const sans = Assistant({
151
+ subsets: ['hebrew', 'latin'],
152
+ weight: ['400', '500', '600', '700'],
153
+ variable: '--font-sans',
154
+ });
155
+ const serif = Frank_Ruhl_Libre({
156
+ subsets: ['hebrew', 'latin'],
157
+ weight: ['400', '500', '700'],
158
+ variable: '--font-serif',
159
+ });
160
+
161
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';
162
+
163
+ export const metadata: Metadata = {
164
+ metadataBase: new URL(baseUrl),
165
+ title: {
166
+ default: <%- storeNameJs %>,
167
+ template: <%- titleTemplateJs %>,
168
+ },
169
+ description: <%- storeNameJs %>,
170
+ alternates: {
171
+ canonical: '/',
172
+ },
173
+ openGraph: {
174
+ siteName: <%- storeNameJs %>,
175
+ locale: '<%= ogLocale %>',
176
+ type: 'website',
177
+ },
178
+ robots: {
179
+ index: true,
180
+ follow: true,
181
+ },
182
+ };
183
+
184
+ const organizationJsonLd = {
185
+ '@context': 'https://schema.org',
186
+ '@type': 'Organization',
187
+ name: <%- storeNameJs %>,
188
+ url: baseUrl,
189
+ };
190
+
191
+ export default async function RootLayout({
192
+ children,
193
+ }: {
194
+ children: React.ReactNode;
195
+ }) {
196
+ const nonce = await getNonce();
197
+
198
+ // Merchant-driven layout chrome — fetched server-side. Each call falls back
199
+ // to null/[] on 404 so the layout never crashes when the merchant hasn't
200
+ // seeded a particular content type yet. New stores ship with default rows
201
+ // seeded by the backend (StoresService.seedDefaultContent).
202
+ const client = getServerClient();
203
+ const [announcements, siteHeader, siteFooter, storeInfo] = await Promise.all([
204
+ client.content.announcement.list().catch(() => []),
205
+ client.content.header.get('main').catch(() => null),
206
+ client.content.footer.get('main').catch(() => null),
207
+ // SSR-fetch store config so PriceDisplay / FreeShippingBar / upsell UI
208
+ // render with the real currency, feature flags, and i18n config at frame 0
209
+ // — without this, Googlebot sees USD/defaults baked into the HTML.
210
+ fetchStoreInfo(),
211
+ ]);
212
+
213
+ return (
214
+ <html lang="<%= language %>" dir="<%= direction %>">
215
+ <head>
216
+ <script
217
+ type="application/ld+json"
218
+ nonce={nonce}
219
+ suppressHydrationWarning
220
+ dangerouslySetInnerHTML={{
221
+ __html: JSON.stringify(organizationJsonLd)
222
+ .replace(/</g, '\\u003c')
223
+ .replace(/>/g, '\\u003e')
224
+ .replace(/&/g, '\\u0026'),
225
+ }}
226
+ />
227
+ {/* Brainerce cookieless traffic analytics — auto-tracks pageviews +
228
+ SPA route changes. Cookieless (no consent banner needed); the
229
+ merchant can toggle it per sales channel in the dashboard. */}
230
+ <script
231
+ defer
232
+ src={`${process.env.NEXT_PUBLIC_BRAINERCE_PIXEL_URL || 'https://api.brainerce.com'}/t.js`}
233
+ data-channel={
234
+ process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || '<%= connectionId %>'
235
+ }
236
+ nonce={nonce}
237
+ suppressHydrationWarning
238
+ />
239
+ </head>
240
+ <body className={`${sans.variable} ${serif.variable} font-sans`}>
241
+ <StoreProvider initialStoreInfo={storeInfo}>
242
+ <div className="min-h-screen flex flex-col">
243
+ <AnnouncementBar announcements={announcements} />
244
+ <SiteHeader header={siteHeader} storeName={<%- storeNameJs %>} />
245
+ <main className="flex-1">{children}</main>
246
+ <SiteFooter footer={siteFooter} storeName={<%- storeNameJs %>} />
247
+ </div>
248
+ <CartDrawer />
249
+ <BrainerceBotWidget />
250
+ </StoreProvider>
251
+ </body>
252
+ </html>
253
+ );
254
+ }
255
+ <% } %>