@rovela-ai/sdk 0.19.3 → 0.19.5

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 (211) hide show
  1. package/dist/admin/server/admin-session.d.ts.map +1 -1
  2. package/dist/admin/server/admin-session.js +7 -0
  3. package/dist/admin/server/admin-session.js.map +1 -1
  4. package/dist/auth/server/customer-session.d.ts.map +1 -1
  5. package/dist/auth/server/customer-session.js +5 -0
  6. package/dist/auth/server/customer-session.js.map +1 -1
  7. package/dist/checkout/server/checkout-customer.d.ts.map +1 -1
  8. package/dist/checkout/server/checkout-customer.js +5 -0
  9. package/dist/checkout/server/checkout-customer.js.map +1 -1
  10. package/docs/changelog/README.md +53 -0
  11. package/docs/changelog/epoch-01-chrome-consent.md +63 -0
  12. package/docs/changelog/epoch-02-platform-commerce.md +108 -0
  13. package/docs/changelog/epoch-03-i18n-files.md +72 -0
  14. package/docs/changelog/epoch-04-emails.md +65 -0
  15. package/docs/changelog/epoch-05-admin-experience.md +98 -0
  16. package/docs/changelog/epoch-06-mobile-integrity.md +72 -0
  17. package/docs/design/admin-ledger-visual-universe.html +1961 -0
  18. package/docs/design/admin-sidebar-and-band.html +542 -0
  19. package/package.json +3 -1
  20. package/templates/store-template/.claude/skills/add-section/SKILL.md +438 -0
  21. package/templates/store-template/.claude/skills/build-feature/SKILL.md +201 -0
  22. package/templates/store-template/.claude/skills/customize-theme/SKILL.md +167 -0
  23. package/templates/store-template/.claude/skills/debug-errors/SKILL.md +68 -0
  24. package/templates/store-template/.claude/skills/frontend-design/SKILL.md +176 -0
  25. package/templates/store-template/.claude/skills/generate-images/SKILL.md +115 -0
  26. package/templates/store-template/.claude/skills/manage-products/SKILL.md +187 -0
  27. package/templates/store-template/.claude/skills/override-sdk-component/SKILL.md +154 -0
  28. package/templates/store-template/.env.example +50 -0
  29. package/templates/store-template/.mcp.json +8 -0
  30. package/templates/store-template/.rovela/blueprint.example.json +190 -0
  31. package/templates/store-template/CLAUDE.md +14 -0
  32. package/templates/store-template/app/LayoutContent.tsx +106 -0
  33. package/templates/store-template/app/[...path]/page.tsx +79 -0
  34. package/templates/store-template/app/account/addresses/page.tsx +45 -0
  35. package/templates/store-template/app/account/layout.tsx +17 -0
  36. package/templates/store-template/app/account/orders/[id]/invoice/page.tsx +277 -0
  37. package/templates/store-template/app/account/orders/[id]/page.tsx +725 -0
  38. package/templates/store-template/app/account/orders/page.tsx +153 -0
  39. package/templates/store-template/app/account/page.tsx +184 -0
  40. package/templates/store-template/app/account/profile/page.tsx +434 -0
  41. package/templates/store-template/app/admin/(dashboard)/account/page.tsx +16 -0
  42. package/templates/store-template/app/admin/(dashboard)/analytics/events/page.tsx +14 -0
  43. package/templates/store-template/app/admin/(dashboard)/analytics/layout.tsx +48 -0
  44. package/templates/store-template/app/admin/(dashboard)/analytics/page.tsx +15 -0
  45. package/templates/store-template/app/admin/(dashboard)/analytics/visitors/page.tsx +14 -0
  46. package/templates/store-template/app/admin/(dashboard)/categories/[id]/page.tsx +38 -0
  47. package/templates/store-template/app/admin/(dashboard)/categories/new/page.tsx +35 -0
  48. package/templates/store-template/app/admin/(dashboard)/categories/page.tsx +223 -0
  49. package/templates/store-template/app/admin/(dashboard)/customers/[id]/page.tsx +26 -0
  50. package/templates/store-template/app/admin/(dashboard)/customers/page.tsx +33 -0
  51. package/templates/store-template/app/admin/(dashboard)/discounts/page.tsx +16 -0
  52. package/templates/store-template/app/admin/(dashboard)/emails/page.tsx +16 -0
  53. package/templates/store-template/app/admin/(dashboard)/files/page.tsx +16 -0
  54. package/templates/store-template/app/admin/(dashboard)/layout.tsx +44 -0
  55. package/templates/store-template/app/admin/(dashboard)/loading.tsx +88 -0
  56. package/templates/store-template/app/admin/(dashboard)/locations/page.tsx +16 -0
  57. package/templates/store-template/app/admin/(dashboard)/orders/[id]/page.tsx +66 -0
  58. package/templates/store-template/app/admin/(dashboard)/orders/page.tsx +26 -0
  59. package/templates/store-template/app/admin/(dashboard)/page.tsx +146 -0
  60. package/templates/store-template/app/admin/(dashboard)/payments/page.tsx +15 -0
  61. package/templates/store-template/app/admin/(dashboard)/products/[id]/page.tsx +38 -0
  62. package/templates/store-template/app/admin/(dashboard)/products/new/page.tsx +35 -0
  63. package/templates/store-template/app/admin/(dashboard)/products/page.tsx +31 -0
  64. package/templates/store-template/app/admin/(dashboard)/settings/page.tsx +46 -0
  65. package/templates/store-template/app/admin/(dashboard)/shipping/page.tsx +15 -0
  66. package/templates/store-template/app/admin/(dashboard)/tax/page.tsx +15 -0
  67. package/templates/store-template/app/admin/(dashboard)/users/[id]/page.tsx +114 -0
  68. package/templates/store-template/app/admin/(dashboard)/users/page.tsx +16 -0
  69. package/templates/store-template/app/admin/accept-invite/page.tsx +36 -0
  70. package/templates/store-template/app/admin/forgot-password/page.tsx +13 -0
  71. package/templates/store-template/app/admin/layout.tsx +47 -0
  72. package/templates/store-template/app/admin/login/page.tsx +528 -0
  73. package/templates/store-template/app/admin/reset-password/page.tsx +36 -0
  74. package/templates/store-template/app/api/account/addresses/[id]/route.ts +10 -0
  75. package/templates/store-template/app/api/account/addresses/route.ts +10 -0
  76. package/templates/store-template/app/api/account/change-password/route.ts +107 -0
  77. package/templates/store-template/app/api/account/orders/[id]/refund/route.ts +11 -0
  78. package/templates/store-template/app/api/account/orders/[id]/return/route.ts +14 -0
  79. package/templates/store-template/app/api/account/orders/[id]/route.ts +42 -0
  80. package/templates/store-template/app/api/account/orders/route.ts +29 -0
  81. package/templates/store-template/app/api/account/profile/route.ts +165 -0
  82. package/templates/store-template/app/api/admin/accept-invite/route.ts +11 -0
  83. package/templates/store-template/app/api/admin/analytics/events/route.ts +9 -0
  84. package/templates/store-template/app/api/admin/analytics/route.ts +10 -0
  85. package/templates/store-template/app/api/admin/analytics/visitors/route.ts +9 -0
  86. package/templates/store-template/app/api/admin/branding/favicon/route.ts +9 -0
  87. package/templates/store-template/app/api/admin/branding/logo/route.ts +9 -0
  88. package/templates/store-template/app/api/admin/categories/[id]/route.ts +9 -0
  89. package/templates/store-template/app/api/admin/categories/route.ts +9 -0
  90. package/templates/store-template/app/api/admin/check/route.ts +10 -0
  91. package/templates/store-template/app/api/admin/customer-groups/[id]/route.ts +10 -0
  92. package/templates/store-template/app/api/admin/customer-groups/route.ts +10 -0
  93. package/templates/store-template/app/api/admin/customers/[id]/addresses/[addressId]/route.ts +15 -0
  94. package/templates/store-template/app/api/admin/customers/[id]/addresses/route.ts +15 -0
  95. package/templates/store-template/app/api/admin/customers/[id]/route.ts +11 -0
  96. package/templates/store-template/app/api/admin/customers/bulk/route.ts +8 -0
  97. package/templates/store-template/app/api/admin/customers/route.ts +9 -0
  98. package/templates/store-template/app/api/admin/discounts/[id]/route.ts +11 -0
  99. package/templates/store-template/app/api/admin/discounts/route.ts +10 -0
  100. package/templates/store-template/app/api/admin/emails/preview/route.ts +4 -0
  101. package/templates/store-template/app/api/admin/emails/route.ts +5 -0
  102. package/templates/store-template/app/api/admin/emails/test/route.ts +4 -0
  103. package/templates/store-template/app/api/admin/files/route.ts +8 -0
  104. package/templates/store-template/app/api/admin/forgot-password/route.ts +7 -0
  105. package/templates/store-template/app/api/admin/linked-customer/route.ts +11 -0
  106. package/templates/store-template/app/api/admin/locations/[id]/route.ts +10 -0
  107. package/templates/store-template/app/api/admin/locations/route.ts +10 -0
  108. package/templates/store-template/app/api/admin/me/password/route.ts +13 -0
  109. package/templates/store-template/app/api/admin/me/route.ts +11 -0
  110. package/templates/store-template/app/api/admin/orders/[id]/refund/route.ts +7 -0
  111. package/templates/store-template/app/api/admin/orders/[id]/return/approve/route.ts +7 -0
  112. package/templates/store-template/app/api/admin/orders/[id]/return/reject/route.ts +7 -0
  113. package/templates/store-template/app/api/admin/orders/[id]/route.ts +9 -0
  114. package/templates/store-template/app/api/admin/orders/[id]/ship-with-shippo/route.ts +6 -0
  115. package/templates/store-template/app/api/admin/orders/[id]/shippo-rates/route.ts +6 -0
  116. package/templates/store-template/app/api/admin/orders/bulk/route.ts +8 -0
  117. package/templates/store-template/app/api/admin/orders/route.ts +9 -0
  118. package/templates/store-template/app/api/admin/payment-methods/[id]/route.ts +10 -0
  119. package/templates/store-template/app/api/admin/payment-methods/route.ts +10 -0
  120. package/templates/store-template/app/api/admin/products/[id]/route.ts +9 -0
  121. package/templates/store-template/app/api/admin/products/bulk/route.ts +9 -0
  122. package/templates/store-template/app/api/admin/products/route.ts +9 -0
  123. package/templates/store-template/app/api/admin/products/stats/route.ts +10 -0
  124. package/templates/store-template/app/api/admin/reset-password/route.ts +10 -0
  125. package/templates/store-template/app/api/admin/settings/route.ts +11 -0
  126. package/templates/store-template/app/api/admin/setup/route.ts +10 -0
  127. package/templates/store-template/app/api/admin/setup-guide/manual-mark/route.ts +13 -0
  128. package/templates/store-template/app/api/admin/setup-guide/route.ts +10 -0
  129. package/templates/store-template/app/api/admin/shipping/carriers/[id]/route.ts +9 -0
  130. package/templates/store-template/app/api/admin/shipping/carriers/route.ts +9 -0
  131. package/templates/store-template/app/api/admin/shipping/rates/[id]/route.ts +9 -0
  132. package/templates/store-template/app/api/admin/shipping/rates/route.ts +9 -0
  133. package/templates/store-template/app/api/admin/shipping/shippo/api-key/route.ts +1 -0
  134. package/templates/store-template/app/api/admin/shipping/shippo/validate/route.ts +1 -0
  135. package/templates/store-template/app/api/admin/shipping/zones/[id]/route.ts +9 -0
  136. package/templates/store-template/app/api/admin/shipping/zones/route.ts +9 -0
  137. package/templates/store-template/app/api/admin/stats/route.ts +9 -0
  138. package/templates/store-template/app/api/admin/stripe-status/route.ts +1 -0
  139. package/templates/store-template/app/api/admin/tax-zones/[id]/route.ts +7 -0
  140. package/templates/store-template/app/api/admin/tax-zones/route.ts +7 -0
  141. package/templates/store-template/app/api/admin/users/[id]/route.ts +14 -0
  142. package/templates/store-template/app/api/admin/users/route.ts +12 -0
  143. package/templates/store-template/app/api/admin-auth/[...nextauth]/route.ts +17 -0
  144. package/templates/store-template/app/api/analytics/track/route.ts +12 -0
  145. package/templates/store-template/app/api/auth/[...nextauth]/route.ts +19 -0
  146. package/templates/store-template/app/api/auth/forgot-password/route.ts +9 -0
  147. package/templates/store-template/app/api/auth/register/route.ts +9 -0
  148. package/templates/store-template/app/api/auth/resend-verification/route.ts +9 -0
  149. package/templates/store-template/app/api/auth/reset-password/route.ts +10 -0
  150. package/templates/store-template/app/api/auth/verify-email/route.ts +10 -0
  151. package/templates/store-template/app/api/categories/route.ts +10 -0
  152. package/templates/store-template/app/api/checkout/countries/route.ts +7 -0
  153. package/templates/store-template/app/api/checkout/discount/route.ts +9 -0
  154. package/templates/store-template/app/api/checkout/manual/route.ts +10 -0
  155. package/templates/store-template/app/api/checkout/payment-methods/route.ts +8 -0
  156. package/templates/store-template/app/api/checkout/pickup-locations/route.ts +8 -0
  157. package/templates/store-template/app/api/checkout/route.ts +10 -0
  158. package/templates/store-template/app/api/checkout/shipping/route.ts +21 -0
  159. package/templates/store-template/app/api/checkout/status/route.ts +10 -0
  160. package/templates/store-template/app/api/contact/route.ts +164 -0
  161. package/templates/store-template/app/api/media/presign/route.ts +7 -0
  162. package/templates/store-template/app/api/media/route.ts +7 -0
  163. package/templates/store-template/app/api/products/[slug]/route.ts +10 -0
  164. package/templates/store-template/app/api/products/route.ts +10 -0
  165. package/templates/store-template/app/api/store/settings/route.ts +12 -0
  166. package/templates/store-template/app/api/webhooks/stripe/route.ts +63 -0
  167. package/templates/store-template/app/auth/forgot-password/page.tsx +38 -0
  168. package/templates/store-template/app/auth/layout.tsx +17 -0
  169. package/templates/store-template/app/auth/reset-password/page.tsx +81 -0
  170. package/templates/store-template/app/auth/signin/page.tsx +62 -0
  171. package/templates/store-template/app/auth/signup/page.tsx +38 -0
  172. package/templates/store-template/app/auth/verify-email/page.tsx +185 -0
  173. package/templates/store-template/app/cart/page.tsx +137 -0
  174. package/templates/store-template/app/checkout/page.tsx +88 -0
  175. package/templates/store-template/app/checkout/success/page.tsx +50 -0
  176. package/templates/store-template/app/contact/layout.tsx +24 -0
  177. package/templates/store-template/app/contact/page.tsx +321 -0
  178. package/templates/store-template/app/faq/page.tsx +153 -0
  179. package/templates/store-template/app/globals.css +99 -0
  180. package/templates/store-template/app/layout.tsx +269 -0
  181. package/templates/store-template/app/not-found.tsx +35 -0
  182. package/templates/store-template/app/page.tsx +23 -0
  183. package/templates/store-template/app/privacy/page.tsx +281 -0
  184. package/templates/store-template/app/products/[slug]/ProductDetailClient.tsx +179 -0
  185. package/templates/store-template/app/products/[slug]/page.tsx +149 -0
  186. package/templates/store-template/app/products/layout.tsx +26 -0
  187. package/templates/store-template/app/products/page.tsx +102 -0
  188. package/templates/store-template/app/robots.ts +25 -0
  189. package/templates/store-template/app/sitemap.ts +76 -0
  190. package/templates/store-template/app/terms/page.tsx +313 -0
  191. package/templates/store-template/components/PathSyncBridge.tsx +61 -0
  192. package/templates/store-template/components/Providers.tsx +37 -0
  193. package/templates/store-template/components/layout/Footer.tsx +150 -0
  194. package/templates/store-template/components/layout/Header.tsx +162 -0
  195. package/templates/store-template/components/sections/.gitkeep +7 -0
  196. package/templates/store-template/components/sections/CTA.tsx +51 -0
  197. package/templates/store-template/components/sections/Features.tsx +91 -0
  198. package/templates/store-template/components/sections/Hero.tsx +85 -0
  199. package/templates/store-template/eslint.config.mjs +18 -0
  200. package/templates/store-template/lib/store-config.ts +77 -0
  201. package/templates/store-template/lib/store-policy.ts +68 -0
  202. package/templates/store-template/lib/utils.ts +21 -0
  203. package/templates/store-template/next-env.d.ts +6 -0
  204. package/templates/store-template/next.config.js +105 -0
  205. package/templates/store-template/package.json +32 -0
  206. package/templates/store-template/postcss.config.js +6 -0
  207. package/templates/store-template/proxy.ts +150 -0
  208. package/templates/store-template/public/rovela-logo.png +0 -0
  209. package/templates/store-template/tailwind.config.ts +74 -0
  210. package/templates/store-template/tsconfig.json +41 -0
  211. package/templates/store-template/types/next-auth.d.ts +32 -0
@@ -0,0 +1,150 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Footer Component
5
+ *
6
+ * This is a customizable footer component. Claude Agent will modify it
7
+ * based on the store's brand identity and visual preferences.
8
+ *
9
+ * Uses useStoreSettings for dynamic store name from database.
10
+ */
11
+
12
+ import { LocaleLink as Link } from '@rovela-ai/sdk/core'
13
+ import { useStoreSettings, useT } from '@rovela-ai/sdk/core'
14
+ import { CookiePreferencesLink } from '@rovela-ai/sdk/core/cookie-consent'
15
+
16
+ // Link labels come from the i18n catalog (footer.*) — resolved inside the
17
+ // component so they follow the active locale.
18
+ const footerLinks = {
19
+ shop: [
20
+ { key: 'footer.allProducts', href: '/products' },
21
+ { key: 'footer.newArrivals', href: '/products?sort=newest' },
22
+ { key: 'footer.bestSellers', href: '/products?sort=popular' },
23
+ ],
24
+ support: [
25
+ { key: 'footer.contactUs', href: '/contact' },
26
+ { key: 'footer.faq', href: '/faq' },
27
+ ],
28
+ company: [
29
+ { key: 'nav.about', href: '/#about' },
30
+ { key: 'footer.privacyPolicy', href: '/privacy' },
31
+ { key: 'footer.terms', href: '/terms' },
32
+ ],
33
+ }
34
+
35
+ export function Footer() {
36
+ const t = useT()
37
+ const currentYear = new Date().getFullYear()
38
+ const { storeName } = useStoreSettings()
39
+
40
+ return (
41
+ <footer className="border-t bg-muted/30">
42
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 py-12">
43
+ <div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
44
+ {/* Brand */}
45
+ <div className="space-y-4">
46
+ <Link href="/" className="inline-block">
47
+ <span className="text-xl font-bold font-heading">
48
+ {storeName}
49
+ </span>
50
+ </Link>
51
+ <p className="text-sm text-muted-foreground max-w-xs">
52
+ {t('footer.tagline')}
53
+ </p>
54
+ </div>
55
+
56
+ {/* Shop Links */}
57
+ <div>
58
+ <h3 className="text-sm font-semibold mb-4">{t('footer.shop')}</h3>
59
+ <ul className="space-y-3">
60
+ {footerLinks.shop.map((link) => (
61
+ <li key={link.key}>
62
+ <Link
63
+ href={link.href}
64
+ className="text-sm text-muted-foreground hover:text-foreground transition-colors"
65
+ >
66
+ {t(link.key)}
67
+ </Link>
68
+ </li>
69
+ ))}
70
+ </ul>
71
+ </div>
72
+
73
+ {/* Support Links */}
74
+ <div>
75
+ <h3 className="text-sm font-semibold mb-4">{t('footer.support')}</h3>
76
+ <ul className="space-y-3">
77
+ {footerLinks.support.map((link) => (
78
+ <li key={link.key}>
79
+ <Link
80
+ href={link.href}
81
+ className="text-sm text-muted-foreground hover:text-foreground transition-colors"
82
+ >
83
+ {t(link.key)}
84
+ </Link>
85
+ </li>
86
+ ))}
87
+ </ul>
88
+ </div>
89
+
90
+ {/* Company Links */}
91
+ <div>
92
+ <h3 className="text-sm font-semibold mb-4">{t('footer.company')}</h3>
93
+ <ul className="space-y-3">
94
+ {footerLinks.company.map((link) => (
95
+ <li key={link.key}>
96
+ <Link
97
+ href={link.href}
98
+ className="text-sm text-muted-foreground hover:text-foreground transition-colors"
99
+ >
100
+ {t(link.key)}
101
+ </Link>
102
+ </li>
103
+ ))}
104
+ </ul>
105
+ </div>
106
+ </div>
107
+
108
+ {/* Bottom Bar */}
109
+ <div className="mt-12 pt-8 border-t">
110
+ <div className="flex flex-col sm:flex-row justify-between items-center gap-4">
111
+ <p className="text-sm text-muted-foreground">
112
+ &copy; {currentYear} {storeName}. {t('footer.allRightsReserved')}
113
+ </p>
114
+ {/* R021: persistent Cookie preferences link — fulfills
115
+ "withdrawal as easy as consent" (GDPR Art. 7(3)). Renders
116
+ null when the admin has disabled the banner in Settings. */}
117
+ <CookiePreferencesLink className="text-sm text-muted-foreground hover:text-foreground transition-colors">
118
+ {t('consent.title')}
119
+ </CookiePreferencesLink>
120
+ {/* "Built with Rovela" badge — shown on free/basic storefronts,
121
+ hidden on Growth/Scale. The env var is set at deploy time from
122
+ the owner's tier; unset (older stores) → hidden. */}
123
+ {process.env.NEXT_PUBLIC_ROVELA_SHOW_BADGE === '1' && (
124
+ <a
125
+ href="https://rovela.ai"
126
+ target="_blank"
127
+ rel="noopener"
128
+ className="text-sm text-muted-foreground hover:text-foreground transition-colors"
129
+ >
130
+ Built with Rovela
131
+ </a>
132
+ )}
133
+ <div className="flex items-center space-x-4">
134
+ {/* Payment icons placeholder */}
135
+ <div className="flex items-center space-x-2 text-muted-foreground">
136
+ <span className="text-xs">{t('footer.securePaymentsWith')}</span>
137
+ <svg className="h-6 w-10" viewBox="0 0 50 20" fill="currentColor">
138
+ <rect x="0" y="2" width="50" height="16" rx="2" fillOpacity="0.2" />
139
+ <text x="25" y="13" textAnchor="middle" fontSize="8" fill="currentColor">
140
+ Stripe
141
+ </text>
142
+ </svg>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </footer>
149
+ )
150
+ }
@@ -0,0 +1,162 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Header Component
5
+ *
6
+ * This is a customizable header component. Claude Agent will modify it
7
+ * based on the store's brand identity and visual preferences.
8
+ *
9
+ * Uses SDK components:
10
+ * - CartIcon from @rovela-ai/sdk/cart
11
+ * - useStoreSettings from @rovela-ai/sdk/core for dynamic store name
12
+ * - UserMenu from @rovela-ai/sdk/auth (when customer accounts enabled)
13
+ */
14
+
15
+ import { useState } from 'react'
16
+ import { LocaleLink as Link } from '@rovela-ai/sdk/core'
17
+ import { Menu, X, Search } from 'lucide-react'
18
+ import { CartIcon } from '@rovela-ai/sdk/cart'
19
+ import { LanguageSwitcher } from '@rovela-ai/sdk/core'
20
+ import { UserMenu } from '@rovela-ai/sdk/auth'
21
+ import { useStoreSettings, useT } from '@rovela-ai/sdk/core'
22
+ import { cn } from '@/lib/utils'
23
+ import { storeConfig } from '@/lib/store-config'
24
+
25
+ // Nav labels come from the i18n catalog (nav.*) — resolved inside the
26
+ // component so they follow the active locale.
27
+ const navigation = [
28
+ { key: 'nav.shop', href: '/products' },
29
+ { key: 'nav.about', href: '/#about' },
30
+ { key: 'nav.contact', href: '/contact' },
31
+ ]
32
+
33
+ export function Header() {
34
+ const t = useT()
35
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
36
+ const { storeName, settings } = useStoreSettings()
37
+ // Live admin setting first; the static store-config flag is only the
38
+ // pre-hydration/legacy fallback.
39
+ const showAccounts = settings?.customerAccounts ?? storeConfig.auth.customerAccounts
40
+
41
+ // Logo: live store setting first (store_settings.logo_url — updated
42
+ // instantly by both the /admin branding page and the Rovela platform);
43
+ // the build-time env var covers the pre-hydration frame and legacy
44
+ // stores whose settings row predates the mirror.
45
+ const envLogoUrl = process.env.NEXT_PUBLIC_STORE_LOGO_URL || null
46
+ const logoUrl = settings?.logoUrl || envLogoUrl
47
+ // A stale pointer (asset deleted while an env var still carries the fixed
48
+ // URL) must degrade to the store name, never a broken image icon.
49
+ const [logoBroken, setLogoBroken] = useState(false)
50
+
51
+ return (
52
+ <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
53
+ <nav className="container mx-auto px-4 sm:px-6 lg:px-8">
54
+ <div className="flex h-16 items-center justify-between">
55
+ {/* Logo */}
56
+ <div className="flex items-center">
57
+ <Link href="/" className="flex items-center space-x-2">
58
+ {logoUrl && !logoBroken ? (
59
+ // 32px header logo; merchant-configured URL (any host) so remotePatterns can't pre-allowlist it
60
+ // eslint-disable-next-line @next/next/no-img-element
61
+ <img
62
+ src={logoUrl}
63
+ alt={storeName}
64
+ className="h-8 w-auto object-contain"
65
+ onError={() => setLogoBroken(true)}
66
+ />
67
+ ) : (
68
+ <span className="text-xl font-bold font-heading">
69
+ {storeName}
70
+ </span>
71
+ )}
72
+ </Link>
73
+ </div>
74
+
75
+ {/* Desktop Navigation */}
76
+ <div className="hidden md:flex md:items-center md:space-x-8">
77
+ {navigation.map((item) => (
78
+ <Link
79
+ key={item.key}
80
+ href={item.href}
81
+ className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
82
+ >
83
+ {t(item.key)}
84
+ </Link>
85
+ ))}
86
+ </div>
87
+
88
+ {/* Right side actions */}
89
+ <div className="flex items-center space-x-4">
90
+ {/* Language switcher — renders nothing on single-language stores */}
91
+ <LanguageSwitcher className="hidden sm:flex" />
92
+
93
+ {/* Search */}
94
+ <button
95
+ type="button"
96
+ className="hidden sm:flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent hover:text-foreground"
97
+ aria-label={t('nav.searchAria')}
98
+ >
99
+ <Search className="h-5 w-5" />
100
+ </button>
101
+
102
+ {/* Account — auth-aware (avatar + menu when signed in, "Sign in" when out) */}
103
+ {showAccounts && (
104
+ <UserMenu className="hidden sm:block" />
105
+ )}
106
+
107
+ {/* Cart */}
108
+ <CartIcon showCount className="text-muted-foreground hover:text-foreground" />
109
+
110
+ {/* Mobile menu button */}
111
+ <button
112
+ type="button"
113
+ className="md:hidden inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent hover:text-foreground"
114
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
115
+ aria-expanded={mobileMenuOpen}
116
+ aria-label={t('nav.toggleMenuAria')}
117
+ >
118
+ {mobileMenuOpen ? (
119
+ <X className="h-6 w-6" />
120
+ ) : (
121
+ <Menu className="h-6 w-6" />
122
+ )}
123
+ </button>
124
+ </div>
125
+ </div>
126
+
127
+ {/* Mobile Navigation */}
128
+ <div
129
+ className={cn(
130
+ 'md:hidden',
131
+ mobileMenuOpen ? 'block' : 'hidden'
132
+ )}
133
+ >
134
+ <div className="space-y-1 pb-3 pt-2">
135
+ {navigation.map((item) => (
136
+ <Link
137
+ key={item.key}
138
+ href={item.href}
139
+ className="block rounded-md px-3 py-2 text-base font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
140
+ onClick={() => setMobileMenuOpen(false)}
141
+ >
142
+ {t(item.key)}
143
+ </Link>
144
+ ))}
145
+ {showAccounts && (
146
+ <Link
147
+ href="/account"
148
+ className="block rounded-md px-3 py-2 text-base font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
149
+ onClick={() => setMobileMenuOpen(false)}
150
+ >
151
+ {t('nav.account')}
152
+ </Link>
153
+ )}
154
+ {/* Language switcher — mobile placement (the header rail's copy is
155
+ hidden below sm; renders nothing on single-language stores). */}
156
+ <LanguageSwitcher variant="full" className="px-3 py-2.5" />
157
+ </div>
158
+ </div>
159
+ </nav>
160
+ </header>
161
+ )
162
+ }
@@ -0,0 +1,7 @@
1
+ # Placeholder for section components
2
+ # Claude Agent will create section components here based on blueprint.homepage:
3
+ # - Hero.tsx
4
+ # - Features.tsx
5
+ # - Testimonials.tsx (if applicable)
6
+ # - CTA.tsx
7
+ # - etc.
@@ -0,0 +1,51 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { useStoreSettings } from '@rovela-ai/sdk/core'
5
+
6
+ /**
7
+ * Pre-baked CTA Section
8
+ *
9
+ * Renders a branded call-to-action using theme CSS variables and store settings.
10
+ * Claude Code will replace this entire file with a custom CTA section.
11
+ */
12
+ export default function CTA() {
13
+ const { storeName } = useStoreSettings()
14
+
15
+ return (
16
+ <section className="py-20 sm:py-28">
17
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8">
18
+ <div className="relative overflow-hidden rounded-2xl bg-primary px-6 py-16 sm:px-16 sm:py-20">
19
+ {/* Decorative background elements */}
20
+ <div className="absolute inset-0 bg-gradient-to-br from-primary via-primary to-primary/80" />
21
+ <div className="absolute -top-24 -right-24 h-64 w-64 rounded-full bg-primary-foreground/5 blur-3xl" />
22
+ <div className="absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-primary-foreground/5 blur-3xl" />
23
+
24
+ {/* Content */}
25
+ <div className="relative text-center max-w-2xl mx-auto">
26
+ <h2 className="text-3xl sm:text-4xl font-bold font-heading tracking-tight text-primary-foreground">
27
+ Your store is almost here
28
+ </h2>
29
+ <p className="mt-4 text-lg text-primary-foreground/80 leading-relaxed">
30
+ Sit tight while Rovela finishes building {storeName}. Everything you see here is a starting sketch.
31
+ </p>
32
+ <div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
33
+ <Link
34
+ href="/products"
35
+ className="w-full sm:w-auto rounded-lg bg-primary-foreground px-8 py-3.5 text-base font-semibold text-primary shadow-lg hover:bg-primary-foreground/90 transition-all duration-200 hover:-translate-y-0.5"
36
+ >
37
+ Browse Collection
38
+ </Link>
39
+ <Link
40
+ href="/auth/signup"
41
+ className="w-full sm:w-auto rounded-lg px-8 py-3.5 text-base font-semibold text-primary-foreground border-2 border-primary-foreground/20 hover:bg-primary-foreground/10 transition-all duration-200"
42
+ >
43
+ Create Account
44
+ </Link>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </section>
50
+ )
51
+ }
@@ -0,0 +1,91 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Pre-baked Features Section
5
+ *
6
+ * Renders professional e-commerce features using theme CSS variables.
7
+ * Claude Code will replace this entire file with custom, business-specific features.
8
+ */
9
+
10
+ const features = [
11
+ {
12
+ title: 'Free Shipping',
13
+ description: 'Enjoy complimentary shipping on all orders. No minimum purchase required.',
14
+ icon: (
15
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
16
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
17
+ </svg>
18
+ ),
19
+ },
20
+ {
21
+ title: 'Secure Payments',
22
+ description: 'Your transactions are protected with industry-leading encryption and security.',
23
+ icon: (
24
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
25
+ <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
26
+ </svg>
27
+ ),
28
+ },
29
+ {
30
+ title: 'Easy Returns',
31
+ description: 'Not satisfied? Return any item within 30 days for a full refund, no questions asked.',
32
+ icon: (
33
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
34
+ <path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
35
+ </svg>
36
+ ),
37
+ },
38
+ {
39
+ title: 'Premium Quality',
40
+ description: 'Every product is carefully selected and quality-checked before reaching you.',
41
+ icon: (
42
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor">
43
+ <path strokeLinecap="round" strokeLinejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
44
+ </svg>
45
+ ),
46
+ },
47
+ ]
48
+
49
+ export default function Features() {
50
+ return (
51
+ <section className="py-20 sm:py-28 bg-muted/30">
52
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8">
53
+ {/* Section header */}
54
+ <div className="text-center max-w-2xl mx-auto mb-16">
55
+ <p className="text-sm font-semibold text-primary uppercase tracking-wider mb-3">
56
+ Placeholder section
57
+ </p>
58
+ <h2 className="text-3xl sm:text-4xl font-bold font-heading tracking-tight">
59
+ Your sections are on the way
60
+ </h2>
61
+ <p className="mt-4 text-lg text-muted-foreground">
62
+ Rovela is generating sections tailored to your business. This placeholder gets replaced automatically as your store is built.
63
+ </p>
64
+ </div>
65
+
66
+ {/* Feature grid */}
67
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-8">
68
+ {features.map((feature) => (
69
+ <div
70
+ key={feature.title}
71
+ className="group relative rounded-xl bg-card border border-border/50 p-6 sm:p-8 transition-all duration-300 hover:border-primary/20 hover:shadow-lg hover:shadow-primary/5 hover:-translate-y-1"
72
+ >
73
+ {/* Icon */}
74
+ <div className="inline-flex items-center justify-center rounded-lg bg-primary/10 text-primary p-3 mb-5">
75
+ {feature.icon}
76
+ </div>
77
+
78
+ {/* Content */}
79
+ <h3 className="text-lg font-semibold font-heading mb-2">
80
+ {feature.title}
81
+ </h3>
82
+ <p className="text-sm text-muted-foreground leading-relaxed">
83
+ {feature.description}
84
+ </p>
85
+ </div>
86
+ ))}
87
+ </div>
88
+ </div>
89
+ </section>
90
+ )
91
+ }
@@ -0,0 +1,85 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { useStoreSettings } from '@rovela-ai/sdk/core'
5
+
6
+ /**
7
+ * Pre-baked Hero Section
8
+ *
9
+ * Renders a branded hero using theme CSS variables and store settings.
10
+ * This provides the instant "aha moment" when theme is injected.
11
+ * Claude Code will replace this entire file with a custom hero section.
12
+ */
13
+ export default function Hero() {
14
+ const { storeName } = useStoreSettings()
15
+
16
+ return (
17
+ <section className="relative overflow-hidden">
18
+ {/* Gradient background using theme primary color */}
19
+ <div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-background to-accent/5" />
20
+ <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,hsl(var(--primary)/0.08),transparent_70%)]" />
21
+
22
+ <div className="relative container mx-auto px-4 sm:px-6 lg:px-8 py-24 sm:py-32 lg:py-40">
23
+ <div className="text-center max-w-4xl mx-auto">
24
+ {/* Eyebrow badge */}
25
+ <div className="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/5 px-4 py-1.5 text-sm font-medium text-primary mb-8">
26
+ <span className="relative flex h-2 w-2">
27
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
28
+ <span className="relative inline-flex rounded-full h-2 w-2 bg-primary" />
29
+ </span>
30
+ Building your store
31
+ </div>
32
+
33
+ {/* Main heading */}
34
+ <h1 className="text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl xl:text-7xl font-heading leading-[1.1]">
35
+ Building{' '}
36
+ <span className="text-primary">{storeName}</span>
37
+ </h1>
38
+
39
+ {/* Subheadline */}
40
+ <p className="mt-6 text-lg sm:text-xl text-muted-foreground max-w-2xl mx-auto leading-relaxed">
41
+ This is a starting sketch. Rovela is building your real storefront right now, and every section here gets replaced with your own in a few moments. Hang tight and watch it come together.
42
+ </p>
43
+
44
+ {/* CTA buttons */}
45
+ <div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
46
+ <Link
47
+ href="/products"
48
+ className="w-full sm:w-auto rounded-lg bg-primary px-8 py-3.5 text-base font-semibold text-primary-foreground shadow-lg shadow-primary/25 hover:bg-primary/90 transition-all duration-200 hover:shadow-xl hover:shadow-primary/30 hover:-translate-y-0.5"
49
+ >
50
+ Shop Now
51
+ </Link>
52
+ <Link
53
+ href="/products"
54
+ className="w-full sm:w-auto rounded-lg px-8 py-3.5 text-base font-semibold text-foreground border-2 border-border hover:bg-accent hover:border-accent transition-all duration-200"
55
+ >
56
+ Browse Collection
57
+ </Link>
58
+ </div>
59
+
60
+ {/* Trust indicators */}
61
+ <div className="mt-16 flex flex-wrap items-center justify-center gap-x-8 gap-y-4 text-sm text-muted-foreground">
62
+ <div className="flex items-center gap-2">
63
+ <svg className="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
64
+ <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
65
+ </svg>
66
+ Free Shipping
67
+ </div>
68
+ <div className="flex items-center gap-2">
69
+ <svg className="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
70
+ <path strokeLinecap="round" strokeLinejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z" />
71
+ </svg>
72
+ Secure Checkout
73
+ </div>
74
+ <div className="flex items-center gap-2">
75
+ <svg className="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
76
+ <path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
77
+ </svg>
78
+ Easy Returns
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </section>
84
+ )
85
+ }
@@ -0,0 +1,18 @@
1
+ import next from 'eslint-config-next/core-web-vitals';
2
+
3
+ const config = [
4
+ {
5
+ ignores: [
6
+ '**/node_modules/**',
7
+ '**/.next/**',
8
+ '**/out/**',
9
+ '**/build/**',
10
+ '**/dist/**',
11
+ 'next-env.d.ts',
12
+ 'public/**',
13
+ ],
14
+ },
15
+ ...next,
16
+ ];
17
+
18
+ export default config;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Store Configuration
3
+ *
4
+ * This file exports the store's module configuration.
5
+ * Values are derived from the blueprint at build time.
6
+ *
7
+ * NOTE: Claude Agent will populate this based on blueprint.modules
8
+ */
9
+
10
+ const config = {
11
+ // Store identity
12
+ store: {
13
+ name: 'Store',
14
+ slug: 'store',
15
+ currency: 'USD',
16
+ locale: 'en-US',
17
+ supportEmail: 'support@store.com',
18
+ },
19
+
20
+ // Authentication settings
21
+ auth: {
22
+ enabled: true,
23
+ guestCheckout: true,
24
+ customerAccounts: true,
25
+ },
26
+
27
+ // Product settings
28
+ products: {
29
+ hasVariants: true,
30
+ trackInventory: true,
31
+ },
32
+
33
+ // Order settings
34
+ orders: {
35
+ processing: 'instant' as const,
36
+ returnPolicy: '30-days' as const,
37
+ refundType: 'full' as const,
38
+ },
39
+
40
+ // Shipping settings (physical products only)
41
+ shipping: {
42
+ enabled: true,
43
+ method: 'carrier' as const,
44
+ rates: 'flat' as const,
45
+ processingTime: '1-2-days' as const,
46
+ },
47
+
48
+ // Payment settings
49
+ payments: {
50
+ provider: 'stripe' as const,
51
+ currency: 'USD',
52
+ taxHandling: 'checkout' as const,
53
+ },
54
+
55
+ // Email templates
56
+ emails: {
57
+ templates: [
58
+ 'order-confirmation',
59
+ 'shipping-update',
60
+ 'welcome',
61
+ 'password-reset',
62
+ ] as const,
63
+ },
64
+ }
65
+
66
+ // Flat export for convenience
67
+ export const storeConfig = {
68
+ ...config,
69
+ // Convenience accessors
70
+ name: config.store.name,
71
+ slug: config.store.slug,
72
+ currency: config.store.currency,
73
+ locale: config.store.locale,
74
+ supportEmail: config.store.supportEmail,
75
+ }
76
+
77
+ export type StoreConfig = typeof storeConfig