create-brainerce-store 1.66.0 → 1.67.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.
Files changed (46) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/templates/nextjs/base/.env.local.ejs +45 -24
  4. package/templates/nextjs/base/.eslintrc.json +51 -57
  5. package/templates/nextjs/base/AGENTS.md.ejs +107 -81
  6. package/templates/nextjs/base/CLAUDE.md.ejs +118 -92
  7. package/templates/nextjs/base/next.config.ts +103 -86
  8. package/templates/nextjs/base/scripts/fetch-store-info.mjs +104 -97
  9. package/templates/nextjs/base/src/app/agents.md/route.ts +4 -3
  10. package/templates/nextjs/base/src/app/api/auth/me/route.ts +65 -59
  11. package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +255 -242
  12. package/templates/nextjs/base/src/app/blog/[slug]/page.tsx.ejs +311 -308
  13. package/templates/nextjs/base/src/app/blog/page.tsx.ejs +277 -276
  14. package/templates/nextjs/base/src/app/blog/rss.xml/route.ts +4 -3
  15. package/templates/nextjs/base/src/app/category/[slug]/page.tsx +6 -5
  16. package/templates/nextjs/base/src/app/faq/page.tsx.ejs +46 -46
  17. package/templates/nextjs/base/src/app/indexnow-key.txt/route.ts +25 -26
  18. package/templates/nextjs/base/src/app/layout.tsx.ejs +273 -257
  19. package/templates/nextjs/base/src/app/llms.txt/route.ts +4 -3
  20. package/templates/nextjs/base/src/app/opengraph-image.tsx +1 -1
  21. package/templates/nextjs/base/src/app/page.tsx +65 -64
  22. package/templates/nextjs/base/src/app/pages/[slug]/page.tsx.ejs +92 -92
  23. package/templates/nextjs/base/src/app/products/[slug]/page.tsx +6 -5
  24. package/templates/nextjs/base/src/app/robots.ts +3 -2
  25. package/templates/nextjs/base/src/app/sitemap.ts +4 -3
  26. package/templates/nextjs/base/src/components/checkout/custom-fields-step.tsx +294 -292
  27. package/templates/nextjs/base/src/components/seo/article-json-ld.tsx +60 -59
  28. package/templates/nextjs/base/src/components/seo/category-json-ld.tsx +60 -61
  29. package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +2 -1
  30. package/templates/nextjs/base/src/core/lib/brainerce.server.ts +81 -0
  31. package/templates/nextjs/base/src/core/lib/brainerce.ts.ejs +60 -110
  32. package/templates/nextjs/base/src/core/lib/site-url.ts +197 -0
  33. package/templates/nextjs/base/src/ui/product/review-form.tsx +294 -294
  34. package/templates/nextjs/base/src/ui/product/reviews-section.tsx.ejs +108 -108
  35. package/templates/nextjs/designs/atelier/app-overlay/layout.tsx.ejs +301 -285
  36. package/templates/nextjs/designs/atelier/ui/layout/faq-section.tsx +97 -96
  37. package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +142 -141
  38. package/templates/nextjs/designs/atelier/ui/layout/site-header.tsx.ejs +139 -138
  39. package/templates/nextjs/designs/atelier/ui/product/review-form.tsx +295 -267
  40. package/templates/nextjs/designs/atelier/ui/product/reviews-section.tsx.ejs +148 -148
  41. package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +72 -71
  42. package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +83 -82
  43. package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +120 -119
  44. package/templates/nextjs/ui-canvas/product/faq-section.tsx.ejs +54 -0
  45. package/templates/nextjs/ui-canvas/product/review-form.tsx +267 -266
  46. package/templates/nextjs/ui-canvas/product/reviews-section.tsx.ejs +96 -96
@@ -1,138 +1,139 @@
1
- /**
2
- * Merchant-controlled site header.
3
- *
4
- * The merchant configures the header in the Brainerce dashboard under
5
- * Sell → Content → Header
6
- *
7
- * Sticky, translucent warm-white bar: brand (serif) at the start, nav in the
8
- * middle (desktop), actions at the end (language / account / cart / mobile
9
- * menu). Everything is generic — nav labels and logo come from the dashboard.
10
- * If `header` is null we render the same chrome with just brand + actions so
11
- * the layout never collapses.
12
- */
13
- import * as React from 'react';
14
- import type { Content } from 'brainerce';
15
- import { HeaderAccount } from './header-account';
16
- import { HeaderCart } from './header-cart';
17
- <% if (i18nEnabled) { %>
18
- import { LanguageSwitcher } from '@/ui/layout/language-switcher';
19
- <% } %>
20
- import { IconMenu } from '@/ui/shared/icons';
21
- import { resolveNavUrl } from '@/ui/layout/nav-url';
22
-
23
- interface SiteHeaderProps {
24
- /** Pre-fetched header payload (server-side). `null` triggers static fallback. */
25
- header: Content<'HEADER'> | null;
26
- /** Fallback brand label when the merchant hasn't uploaded a logo yet. */
27
- storeName?: string;
28
- }
29
-
30
- function Brand({
31
- logo,
32
- brandLabel,
33
- }: {
34
- logo?: { src: string; alt: string } | null;
35
- brandLabel: string;
36
- }) {
37
- return (
38
- <a
39
- href="/"
40
- aria-label={brandLabel}
41
- className="flex shrink-0 items-center gap-2 rounded-lg"
42
- >
43
- {logo ? (
44
- // eslint-disable-next-line @next/next/no-img-element -- merchant-supplied URL, dynamic optimization handled by CDN
45
- <img src={logo.src} alt={logo.alt} className="h-9 w-auto object-contain" />
46
- ) : (
47
- <span className="font-display text-xl font-semibold leading-none text-foreground sm:text-2xl">
48
- {brandLabel}
49
- </span>
50
- )}
51
- </a>
52
- );
53
- }
54
-
55
- export function SiteHeader({ header, storeName }: SiteHeaderProps) {
56
- const data = header?.data;
57
- const logo = data?.logo;
58
- const navItems = data?.navItems ?? [];
59
- const cta = data?.cta;
60
- const brandLabel = logo?.alt || storeName || 'Store';
61
-
62
- return (
63
- <header className="sticky top-0 z-40 border-b bg-background/90 backdrop-blur supports-[backdrop-filter]:bg-background/80">
64
- <div className="container-page flex h-16 items-center justify-between gap-4 sm:h-[4.5rem]">
65
- <Brand logo={logo ?? null} brandLabel={brandLabel} />
66
-
67
- {navItems.length > 0 && (
68
- <nav aria-label="Main" className="hidden md:block">
69
- <ul className="flex items-center gap-1">
70
- {navItems.map((item, idx) => (
71
- <li key={`${item.url}-${idx}`}>
72
- <a
73
- href={resolveNavUrl(item.url)}
74
- className="rounded-full px-4 py-2 text-sm font-medium text-foreground/80 transition-colors hover:bg-secondary hover:text-foreground"
75
- >
76
- {item.label}
77
- </a>
78
- </li>
79
- ))}
80
- </ul>
81
- </nav>
82
- )}
83
-
84
- <div className="flex items-center gap-1 sm:gap-2">
85
- {cta ? (
86
- <a href={resolveNavUrl(cta.url)} className="btn-primary btn-sm hidden lg:inline-flex">
87
- {cta.label}
88
- </a>
89
- ) : null}
90
- <% if (i18nEnabled) { %>
91
-
92
- <LanguageSwitcher />
93
- <% } %>
94
- <HeaderAccount />
95
- <HeaderCart />
96
-
97
- {navItems.length > 0 && (
98
- <details className="group relative md:hidden">
99
- <summary
100
- className="flex h-10 w-10 cursor-pointer list-none items-center justify-center rounded-full text-foreground transition-colors hover:bg-secondary [&::-webkit-details-marker]:hidden"
101
- aria-label="Menu"
102
- >
103
- <IconMenu size={20} />
104
- </summary>
105
- <nav
106
- aria-label="Mobile"
107
- className="absolute end-0 top-12 z-50 w-56 rounded-xl border bg-background p-2 shadow-lg"
108
- >
109
- <ul className="flex flex-col">
110
- {navItems.map((item, idx) => (
111
- <li key={`m-${item.url}-${idx}`}>
112
- <a
113
- href={resolveNavUrl(item.url)}
114
- className="block rounded-lg px-3 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-secondary"
115
- >
116
- {item.label}
117
- </a>
118
- </li>
119
- ))}
120
- {cta ? (
121
- <li className="mt-1 border-t pt-2">
122
- <a
123
- href={resolveNavUrl(cta.url)}
124
- className="block rounded-lg px-3 py-2.5 text-sm font-semibold text-primary transition-colors hover:bg-secondary"
125
- >
126
- {cta.label}
127
- </a>
128
- </li>
129
- ) : null}
130
- </ul>
131
- </nav>
132
- </details>
133
- )}
134
- </div>
135
- </div>
136
- </header>
137
- );
138
- }
1
+ /**
2
+ * Merchant-controlled site header.
3
+ *
4
+ * The merchant configures the header in the Brainerce dashboard under
5
+ * Sell → Content → Header
6
+ *
7
+ * Sticky, translucent warm-white bar: brand (serif) at the start, nav in the
8
+ * middle (desktop), actions at the end (language / account / cart / mobile
9
+ * menu). Everything is generic — nav labels and logo come from the dashboard.
10
+ * If `header` is null we render the same chrome with just brand + actions so
11
+ * the layout never collapses.
12
+ */
13
+ import * as React from 'react';
14
+ import { Link } from '@/core/lib/navigation';
15
+ import type { Content } from 'brainerce';
16
+ import { HeaderAccount } from './header-account';
17
+ import { HeaderCart } from './header-cart';
18
+ <% if (i18nEnabled) { %>
19
+ import { LanguageSwitcher } from '@/ui/layout/language-switcher';
20
+ <% } %>
21
+ import { IconMenu } from '@/ui/shared/icons';
22
+ import { resolveNavUrl } from '@/ui/layout/nav-url';
23
+
24
+ interface SiteHeaderProps {
25
+ /** Pre-fetched header payload (server-side). `null` triggers static fallback. */
26
+ header: Content<'HEADER'> | null;
27
+ /** Fallback brand label when the merchant hasn't uploaded a logo yet. */
28
+ storeName?: string;
29
+ }
30
+
31
+ function Brand({
32
+ logo,
33
+ brandLabel,
34
+ }: {
35
+ logo?: { src: string; alt: string } | null;
36
+ brandLabel: string;
37
+ }) {
38
+ return (
39
+ <Link
40
+ href="/"
41
+ aria-label={brandLabel}
42
+ className="flex shrink-0 items-center gap-2 rounded-lg"
43
+ >
44
+ {logo ? (
45
+ // eslint-disable-next-line @next/next/no-img-element -- merchant-supplied URL, dynamic optimization handled by CDN
46
+ <img src={logo.src} alt={logo.alt} className="h-9 w-auto object-contain" />
47
+ ) : (
48
+ <span className="font-display text-xl font-semibold leading-none text-foreground sm:text-2xl">
49
+ {brandLabel}
50
+ </span>
51
+ )}
52
+ </Link>
53
+ );
54
+ }
55
+
56
+ export function SiteHeader({ header, storeName }: SiteHeaderProps) {
57
+ const data = header?.data;
58
+ const logo = data?.logo;
59
+ const navItems = data?.navItems ?? [];
60
+ const cta = data?.cta;
61
+ const brandLabel = logo?.alt || storeName || 'Store';
62
+
63
+ return (
64
+ <header className="sticky top-0 z-40 border-b bg-background/90 backdrop-blur supports-[backdrop-filter]:bg-background/80">
65
+ <div className="container-page flex h-16 items-center justify-between gap-4 sm:h-[4.5rem]">
66
+ <Brand logo={logo ?? null} brandLabel={brandLabel} />
67
+
68
+ {navItems.length > 0 && (
69
+ <nav aria-label="Main" className="hidden md:block">
70
+ <ul className="flex items-center gap-1">
71
+ {navItems.map((item, idx) => (
72
+ <li key={`${item.url}-${idx}`}>
73
+ <a
74
+ href={resolveNavUrl(item.url)}
75
+ className="rounded-full px-4 py-2 text-sm font-medium text-foreground/80 transition-colors hover:bg-secondary hover:text-foreground"
76
+ >
77
+ {item.label}
78
+ </a>
79
+ </li>
80
+ ))}
81
+ </ul>
82
+ </nav>
83
+ )}
84
+
85
+ <div className="flex items-center gap-1 sm:gap-2">
86
+ {cta ? (
87
+ <a href={resolveNavUrl(cta.url)} className="btn-primary btn-sm hidden lg:inline-flex">
88
+ {cta.label}
89
+ </a>
90
+ ) : null}
91
+ <% if (i18nEnabled) { %>
92
+
93
+ <LanguageSwitcher />
94
+ <% } %>
95
+ <HeaderAccount />
96
+ <HeaderCart />
97
+
98
+ {navItems.length > 0 && (
99
+ <details className="group relative md:hidden">
100
+ <summary
101
+ className="flex h-10 w-10 cursor-pointer list-none items-center justify-center rounded-full text-foreground transition-colors hover:bg-secondary [&::-webkit-details-marker]:hidden"
102
+ aria-label="Menu"
103
+ >
104
+ <IconMenu size={20} />
105
+ </summary>
106
+ <nav
107
+ aria-label="Mobile"
108
+ className="absolute end-0 top-12 z-50 w-56 rounded-xl border bg-background p-2 shadow-lg"
109
+ >
110
+ <ul className="flex flex-col">
111
+ {navItems.map((item, idx) => (
112
+ <li key={`m-${item.url}-${idx}`}>
113
+ <a
114
+ href={resolveNavUrl(item.url)}
115
+ className="block rounded-lg px-3 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-secondary"
116
+ >
117
+ {item.label}
118
+ </a>
119
+ </li>
120
+ ))}
121
+ {cta ? (
122
+ <li className="mt-1 border-t pt-2">
123
+ <a
124
+ href={resolveNavUrl(cta.url)}
125
+ className="block rounded-lg px-3 py-2.5 text-sm font-semibold text-primary transition-colors hover:bg-secondary"
126
+ >
127
+ {cta.label}
128
+ </a>
129
+ </li>
130
+ ) : null}
131
+ </ul>
132
+ </nav>
133
+ </details>
134
+ )}
135
+ </div>
136
+ </div>
137
+ </header>
138
+ );
139
+ }