@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,146 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Dashboard
5
+ *
6
+ * Overview of store metrics, charts, and recent activity.
7
+ *
8
+ * Metric layout:
9
+ * - Primary row: Revenue / Orders / AOV / Refund rate (with period deltas)
10
+ * - Secondary row: Best seller / Customers / Active products
11
+ * - Time-window selector in the header drives both rows
12
+ *
13
+ * Charts (RevenueChart + OrderStatusChart) and Activity (RecentOrders +
14
+ * LowStockAlert) preserve the existing behavior. RevenueChart now also
15
+ * scopes to the selected window.
16
+ */
17
+
18
+ import { useState } from 'react'
19
+ import Link from 'next/link'
20
+ import { Plus } from 'lucide-react'
21
+ import {
22
+ AdminPageHeader,
23
+ PrimaryMetricsRow,
24
+ SecondaryMetricsRow,
25
+ PeriodSelector,
26
+ RecentOrders,
27
+ LowStockAlert,
28
+ RevenueChart,
29
+ OrderStatusChart,
30
+ useAdminStats,
31
+ useAdminAuth,
32
+ } from '@rovela-ai/sdk/admin'
33
+ import type { DashboardPeriod } from '@rovela-ai/sdk/admin'
34
+ import { useStoreSettings } from '@rovela-ai/sdk/core'
35
+
36
+ // =============================================================================
37
+ // Helpers
38
+ // =============================================================================
39
+
40
+ function getGreeting(): string {
41
+ const hour = new Date().getHours()
42
+ if (hour < 12) return 'Good morning'
43
+ if (hour < 18) return 'Good afternoon'
44
+ return 'Good evening'
45
+ }
46
+
47
+ function getFormattedDate(): string {
48
+ return new Date().toLocaleDateString('en-US', {
49
+ weekday: 'long',
50
+ month: 'long',
51
+ day: 'numeric',
52
+ })
53
+ }
54
+
55
+ // =============================================================================
56
+ // Component
57
+ // =============================================================================
58
+
59
+ export default function AdminDashboardPage() {
60
+ const { admin } = useAdminAuth()
61
+ const { currency, locale } = useStoreSettings()
62
+ const [period, setPeriod] = useState<DashboardPeriod>('30d')
63
+
64
+ const {
65
+ stats,
66
+ summary,
67
+ revenueByDay,
68
+ ordersByStatus,
69
+ isLoading,
70
+ } = useAdminStats({
71
+ includeRevenue: true,
72
+ includeOrderStatus: true,
73
+ period,
74
+ })
75
+
76
+ const firstName = admin?.name?.split(' ')[0] || 'Admin'
77
+
78
+
79
+ return (
80
+ <div className="space-y-6">
81
+ {/* THE page header (AdminPageHeader = LEDGER page architecture):
82
+ date eyebrow + greeting title + actions, one primary button. */}
83
+ <AdminPageHeader
84
+ eyebrow={getFormattedDate()}
85
+ title={`${getGreeting()}, ${firstName}`}
86
+ actions={
87
+ /* View/Edit store moved to the AdminTopBar (2026-07 revamp) — the
88
+ page keeps only its own concerns: the period and the primary. */
89
+ <>
90
+ <PeriodSelector value={period} onChange={setPeriod} />
91
+ <Link href="/admin/products/new" className="admin-btn admin-btn-primary admin-btn-sm">
92
+ <Plus className="h-3.5 w-3.5" strokeWidth={2} />
93
+ Add product
94
+ </Link>
95
+ </>
96
+ }
97
+ />
98
+
99
+ {/* Primary metrics — Revenue / Orders / AOV / Refund rate */}
100
+ <section>
101
+ <PrimaryMetricsRow
102
+ summary={summary}
103
+ isLoading={isLoading}
104
+ currency={currency}
105
+ locale={locale}
106
+ />
107
+ </section>
108
+
109
+ {/* Secondary — Best seller / Customers / Products */}
110
+ <section>
111
+ <SecondaryMetricsRow
112
+ summary={summary}
113
+ stats={stats}
114
+ isLoading={isLoading}
115
+ currency={currency}
116
+ locale={locale}
117
+ />
118
+ </section>
119
+
120
+ {/* Charts Row */}
121
+ <section className="grid grid-cols-1 gap-6 xl:grid-cols-3">
122
+ <div className="xl:col-span-2">
123
+ <RevenueChart
124
+ data={revenueByDay}
125
+ isLoading={isLoading}
126
+ currency={currency}
127
+ />
128
+ </div>
129
+ <div>
130
+ <OrderStatusChart
131
+ data={ordersByStatus}
132
+ isLoading={isLoading}
133
+ />
134
+ </div>
135
+ </section>
136
+
137
+ {/* Activity Section */}
138
+ <section>
139
+ <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
140
+ <RecentOrders period={period} />
141
+ <LowStockAlert />
142
+ </div>
143
+ </section>
144
+ </div>
145
+ )
146
+ }
@@ -0,0 +1,15 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Payments Page
5
+ *
6
+ * Stripe connection, manual payment methods and currency. The
7
+ * PaymentSettings component renders the page header itself (LEDGER page
8
+ * architecture): this wrapper stays a bare mount.
9
+ */
10
+
11
+ import { PaymentSettings } from '@rovela-ai/sdk/admin'
12
+
13
+ export default function AdminPaymentsPage() {
14
+ return <PaymentSettings />
15
+ }
@@ -0,0 +1,38 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Edit Product Page
5
+ *
6
+ * Edit an existing product using the SDK ProductForm component.
7
+ * Uses useParams() hook for client-side dynamic route access.
8
+ */
9
+
10
+ import { useParams, useRouter } from 'next/navigation'
11
+ import { AdminPageHeader, ProductForm } from '@rovela-ai/sdk/admin'
12
+
13
+ export default function EditProductPage() {
14
+ const params = useParams()
15
+ const id = params.id as string
16
+ const router = useRouter()
17
+
18
+ return (
19
+ <div className="space-y-6">
20
+ <AdminPageHeader
21
+ backHref="/admin/products"
22
+ backLabel="Back to products"
23
+ eyebrow="Products"
24
+ title="Edit product"
25
+ />
26
+
27
+ <ProductForm
28
+ productId={id}
29
+ onSuccess={() => {
30
+ router.push('/admin/products')
31
+ }}
32
+ onCancel={() => {
33
+ router.push('/admin/products')
34
+ }}
35
+ />
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,35 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin New Product Page
5
+ *
6
+ * Create a new product using the SDK ProductForm component.
7
+ */
8
+
9
+ import { useRouter } from 'next/navigation'
10
+ import { AdminPageHeader, ProductForm } from '@rovela-ai/sdk/admin'
11
+
12
+ export default function NewProductPage() {
13
+ const router = useRouter()
14
+
15
+ return (
16
+ <div className="space-y-6">
17
+ <AdminPageHeader
18
+ backHref="/admin/products"
19
+ backLabel="Back to products"
20
+ eyebrow="Products"
21
+ title="Add product"
22
+ subtitle="Create a new product for your store."
23
+ />
24
+
25
+ <ProductForm
26
+ onSuccess={() => {
27
+ router.push('/admin/products')
28
+ }}
29
+ onCancel={() => {
30
+ router.push('/admin/products')
31
+ }}
32
+ />
33
+ </div>
34
+ )
35
+ }
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Products Page
5
+ *
6
+ * Product management with table and actions.
7
+ * Uses SDK admin ProductTable component.
8
+ */
9
+
10
+ import Link from 'next/link'
11
+ import { Plus } from 'lucide-react'
12
+ import { AdminPageHeader, ProductTable } from '@rovela-ai/sdk/admin'
13
+
14
+ export default function AdminProductsPage() {
15
+ return (
16
+ <div className="space-y-6">
17
+ <AdminPageHeader
18
+ title="Products"
19
+ subtitle="Manage your product catalog."
20
+ actions={
21
+ <Link href="/admin/products/new" className="admin-btn admin-btn-primary">
22
+ <Plus className="h-4 w-4" />
23
+ Add product
24
+ </Link>
25
+ }
26
+ />
27
+
28
+ <ProductTable />
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,46 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Settings Page
5
+ *
6
+ * Store settings management with database persistence.
7
+ * Uses self-contained SDK components:
8
+ * - StoreSettings: Fetches and saves store configuration
9
+ * - LogoUpload: Handles logo upload/delete
10
+ */
11
+
12
+ import { AdminPageHeader, StoreSettings, LogoUpload, FaviconUpload } from '@rovela-ai/sdk/admin'
13
+ import { useStoreSettings } from '@rovela-ai/sdk/core'
14
+
15
+ export default function AdminSettingsPage() {
16
+ // Get refetch from global store settings context to update sidebar/header after save
17
+ const { refetch: refetchGlobalSettings } = useStoreSettings()
18
+
19
+ return (
20
+ <div className="space-y-6">
21
+ <AdminPageHeader
22
+ title="Settings"
23
+ subtitle="Your store's identity, checkout and languages."
24
+ />
25
+
26
+ {/* Two-column layout */}
27
+ <div className="grid gap-6 lg:grid-cols-3">
28
+ {/* Left Column: Settings Form (2/3) */}
29
+ <div className="lg:col-span-2">
30
+ <StoreSettings onSave={() => refetchGlobalSettings()} />
31
+ </div>
32
+
33
+ {/* Right Column: Branding (1/3) */}
34
+ <div>
35
+ <div className="admin-card p-6">
36
+ <h3 className="admin-card-title mb-4">Store branding</h3>
37
+ <LogoUpload />
38
+ <div className="mt-6 border-t border-[hsl(var(--admin-border))] pt-6">
39
+ <FaviconUpload />
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ )
46
+ }
@@ -0,0 +1,15 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Shipping Page
5
+ *
6
+ * Carriers, zones and rates. The ShippingSettings component renders the
7
+ * page header itself (LEDGER page architecture): this wrapper stays a
8
+ * bare mount.
9
+ */
10
+
11
+ import { ShippingSettings } from '@rovela-ai/sdk/admin'
12
+
13
+ export default function AdminShippingPage() {
14
+ return <ShippingSettings />
15
+ }
@@ -0,0 +1,15 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Tax Page
5
+ *
6
+ * Tax configuration and zone-based rates. The TaxSettings component
7
+ * renders the page header itself (LEDGER page architecture): this wrapper
8
+ * stays a bare mount.
9
+ */
10
+
11
+ import { TaxSettings } from '@rovela-ai/sdk/admin'
12
+
13
+ export default function AdminTaxPage() {
14
+ return <TaxSettings />
15
+ }
@@ -0,0 +1,114 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin User Detail Page
5
+ *
6
+ * Minimal detail view for a single admin. Shows their core info (name,
7
+ * email, role, status, created/last-login dates) and a "Back to users"
8
+ * link. Phase 4 may extend this with activity history and audit info.
9
+ *
10
+ * For Phase 2 we keep it deliberately bare: the table at /admin/users is
11
+ * the main interaction surface and handles all lifecycle actions directly.
12
+ */
13
+
14
+ import { useEffect, useState } from 'react'
15
+ import { useParams } from 'next/navigation'
16
+ import Link from 'next/link'
17
+ import { AdminPageHeader, useAdminUsers, type AdminListItem } from '@rovela-ai/sdk/admin'
18
+
19
+ export default function AdminUserDetailPage() {
20
+ const params = useParams<{ id: string }>()
21
+ const { getUser } = useAdminUsers()
22
+ const [user, setUser] = useState<AdminListItem | null>(null)
23
+ const [loading, setLoading] = useState(true)
24
+
25
+ useEffect(() => {
26
+ if (!params?.id) return
27
+ let cancelled = false
28
+ ;(async () => {
29
+ const result = await getUser(params.id)
30
+ if (!cancelled) {
31
+ setUser(result)
32
+ setLoading(false)
33
+ }
34
+ })()
35
+ return () => {
36
+ cancelled = true
37
+ }
38
+ }, [params?.id, getUser])
39
+
40
+ if (loading) {
41
+ return (
42
+ <div className="space-y-4">
43
+ <div className="admin-skeleton admin-skeleton-lg w-64 rounded" />
44
+ <div className="admin-skeleton admin-skeleton-md w-96 rounded" />
45
+ </div>
46
+ )
47
+ }
48
+
49
+ if (!user) {
50
+ return (
51
+ <div className="space-y-4">
52
+ <h1 className="admin-page-title">Team member not found</h1>
53
+ <Link href="/admin/users" className="text-sm admin-text-muted hover:underline">
54
+ ← Back to users
55
+ </Link>
56
+ </div>
57
+ )
58
+ }
59
+
60
+ return (
61
+ <div className="space-y-6">
62
+ <AdminPageHeader
63
+ backHref="/admin/users"
64
+ backLabel="Back to team"
65
+ eyebrow="Team"
66
+ title={user.name || 'Unnamed admin'}
67
+ subtitle={user.email}
68
+ />
69
+
70
+ <div className="admin-card p-6">
71
+ <dl className="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
72
+ <div>
73
+ <dt className="admin-text-muted">Role</dt>
74
+ <dd className="font-medium">{user.role}</dd>
75
+ </div>
76
+ <div>
77
+ <dt className="admin-text-muted">Status</dt>
78
+ <dd className="font-medium">{user.status}</dd>
79
+ </div>
80
+ <div>
81
+ <dt className="admin-text-muted">Created</dt>
82
+ <dd className="font-medium">
83
+ {new Date(user.createdAt).toLocaleDateString()}
84
+ </dd>
85
+ </div>
86
+ <div>
87
+ <dt className="admin-text-muted">Last login</dt>
88
+ <dd className="font-medium">
89
+ {user.lastLoginAt
90
+ ? new Date(user.lastLoginAt).toLocaleDateString()
91
+ : 'Never'}
92
+ </dd>
93
+ </div>
94
+ {user.deactivatedAt && (
95
+ <>
96
+ <div>
97
+ <dt className="admin-text-muted">Deactivated</dt>
98
+ <dd className="font-medium">
99
+ {new Date(user.deactivatedAt).toLocaleDateString()}
100
+ </dd>
101
+ </div>
102
+ <div>
103
+ <dt className="admin-text-muted">Deactivated by</dt>
104
+ <dd className="font-medium">
105
+ {user.deactivatedBy || 'Unknown'}
106
+ </dd>
107
+ </div>
108
+ </>
109
+ )}
110
+ </dl>
111
+ </div>
112
+ </div>
113
+ )
114
+ }
@@ -0,0 +1,16 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Admin Team Page (route: /admin/users)
5
+ *
6
+ * The UsersTable renders the page header itself (title, subtitle and the
7
+ * Invite CTA) — LEDGER page architecture: this wrapper stays a bare mount
8
+ * so the title and primary action exist exactly once. All permission
9
+ * checks, invariants, and error handling happen at the service + API layer.
10
+ */
11
+
12
+ import { UsersTable } from '@rovela-ai/sdk/admin'
13
+
14
+ export default function AdminUsersPage() {
15
+ return <UsersTable pageSize={20} />
16
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Admin Accept Invite Page
3
+ *
4
+ * Public page (outside the dashboard route group). The SDK's
5
+ * AdminAcceptInviteForm renders its own AdminAuthShell (brand tile +
6
+ * store name + card + theme CSS), so this page is a bare mount.
7
+ *
8
+ * The form reads the `?token=X` query param via `useSearchParams`, so
9
+ * Next.js App Router requires a <Suspense> boundary here. The fallback
10
+ * reuses the shell so the suspense moment and the form's own validating
11
+ * state look identical.
12
+ */
13
+
14
+ import { Suspense } from 'react'
15
+ import {
16
+ AdminAuthShell,
17
+ AdminAcceptInviteForm,
18
+ } from '@rovela-ai/sdk/admin/components'
19
+
20
+ function LoadingFallback() {
21
+ return (
22
+ <AdminAuthShell>
23
+ <div className="flex items-center justify-center py-8">
24
+ <span className="admin-spinner admin-spinner-lg" />
25
+ </div>
26
+ </AdminAuthShell>
27
+ )
28
+ }
29
+
30
+ export default function AdminAcceptInvitePage() {
31
+ return (
32
+ <Suspense fallback={<LoadingFallback />}>
33
+ <AdminAcceptInviteForm />
34
+ </Suspense>
35
+ )
36
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Admin Forgot Password Page
3
+ *
4
+ * Public page (outside the dashboard route group). The SDK's
5
+ * AdminForgotPasswordForm renders its own AdminAuthShell (brand tile +
6
+ * store name + card + theme CSS), so this page is a bare mount.
7
+ */
8
+
9
+ import { AdminForgotPasswordForm } from '@rovela-ai/sdk/admin/components'
10
+
11
+ export default function AdminForgotPasswordPage() {
12
+ return <AdminForgotPasswordForm />
13
+ }
@@ -0,0 +1,47 @@
1
+ import { Plus_Jakarta_Sans } from 'next/font/google'
2
+ import type { Viewport } from 'next'
3
+
4
+ /**
5
+ * Admin segment layout — loads Plus Jakarta Sans via next/font and exposes it
6
+ * as `--admin-font-jakarta`, which admin-theme.css consumes through
7
+ * `--admin-font-sans`. This replaced a render-blocking Google Fonts @import
8
+ * inside the CSS (2026-07 admin redesign): next/font self-hosts the files,
9
+ * eliminates the extra origin round-trip, and covers EVERY /admin route
10
+ * (dashboard group + the auth pages) from one place.
11
+ *
12
+ * `display: contents` keeps the wrapper structurally invisible — it exists
13
+ * only to scope the font variable.
14
+ */
15
+ const jakarta = Plus_Jakarta_Sans({
16
+ subsets: ['latin'],
17
+ weight: ['400', '500', '600', '700'],
18
+ variable: '--admin-font-jakarta',
19
+ display: 'swap',
20
+ })
21
+
22
+ // Explicit viewport contract for the responsive admin (2026-07 mobile pass).
23
+ // Next.js would inject the same default, but the admin's mobile chrome now
24
+ // depends on it, so it is stated rather than assumed.
25
+ export const viewport: Viewport = {
26
+ width: 'device-width',
27
+ initialScale: 1,
28
+ }
29
+
30
+ // The admin is per-request and session-gated — never static. Declaring it
31
+ // (same pattern as the auth/ and account/ segments) stops the build from
32
+ // attempting to prerender ~21 admin pages, which threw Next's intentional
33
+ // DYNAMIC_SERVER_USAGE bailout through requireAdmin's catch and printed a
34
+ // scary-but-benign "Failed to read session" block per page in build logs.
35
+ export const dynamic = 'force-dynamic'
36
+
37
+ export default function AdminSegmentLayout({
38
+ children,
39
+ }: {
40
+ children: React.ReactNode
41
+ }) {
42
+ return (
43
+ <div className={jakarta.variable} style={{ display: 'contents' }}>
44
+ {children}
45
+ </div>
46
+ )
47
+ }