@rovela-ai/sdk 0.19.4 → 0.19.6

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 (236) hide show
  1. package/dist/admin/components/AdminPageHeader.d.ts.map +1 -1
  2. package/dist/admin/components/AdminPageHeader.js +9 -2
  3. package/dist/admin/components/AdminPageHeader.js.map +1 -1
  4. package/dist/admin/components/AdminUserMenu.d.ts.map +1 -1
  5. package/dist/admin/components/AdminUserMenu.js +2 -1
  6. package/dist/admin/components/AdminUserMenu.js.map +1 -1
  7. package/dist/admin/components/CustomerDetails.d.ts.map +1 -1
  8. package/dist/admin/components/CustomerDetails.js +2 -1
  9. package/dist/admin/components/CustomerDetails.js.map +1 -1
  10. package/dist/admin/components/EmailsManager.d.ts.map +1 -1
  11. package/dist/admin/components/EmailsManager.js +2 -1
  12. package/dist/admin/components/EmailsManager.js.map +1 -1
  13. package/dist/admin/components/LowStockAlert.d.ts.map +1 -1
  14. package/dist/admin/components/LowStockAlert.js +3 -2
  15. package/dist/admin/components/LowStockAlert.js.map +1 -1
  16. package/dist/admin/components/OrderDetails.d.ts.map +1 -1
  17. package/dist/admin/components/OrderDetails.js +2 -1
  18. package/dist/admin/components/OrderDetails.js.map +1 -1
  19. package/dist/admin/components/ProductTable.d.ts.map +1 -1
  20. package/dist/admin/components/ProductTable.js +4 -3
  21. package/dist/admin/components/ProductTable.js.map +1 -1
  22. package/dist/admin/components/RecentOrders.d.ts.map +1 -1
  23. package/dist/admin/components/RecentOrders.js +2 -1
  24. package/dist/admin/components/RecentOrders.js.map +1 -1
  25. package/dist/admin/components/SEOPreview.d.ts +2 -8
  26. package/dist/admin/components/SEOPreview.d.ts.map +1 -1
  27. package/dist/admin/components/SEOPreview.js +32 -5
  28. package/dist/admin/components/SEOPreview.js.map +1 -1
  29. package/dist/admin/components/SecondaryMetricsRow.d.ts.map +1 -1
  30. package/dist/admin/components/SecondaryMetricsRow.js +3 -2
  31. package/dist/admin/components/SecondaryMetricsRow.js.map +1 -1
  32. package/dist/admin/components/StatsCards.d.ts.map +1 -1
  33. package/dist/admin/components/StatsCards.js +2 -1
  34. package/dist/admin/components/StatsCards.js.map +1 -1
  35. package/docs/changelog/README.md +53 -0
  36. package/docs/changelog/epoch-01-chrome-consent.md +63 -0
  37. package/docs/changelog/epoch-02-platform-commerce.md +108 -0
  38. package/docs/changelog/epoch-03-i18n-files.md +72 -0
  39. package/docs/changelog/epoch-04-emails.md +65 -0
  40. package/docs/changelog/epoch-05-admin-experience.md +98 -0
  41. package/docs/changelog/epoch-06-mobile-integrity.md +92 -0
  42. package/docs/design/admin-ledger-visual-universe.html +1961 -0
  43. package/docs/design/admin-sidebar-and-band.html +542 -0
  44. package/package.json +3 -1
  45. package/templates/store-template/.claude/skills/add-section/SKILL.md +438 -0
  46. package/templates/store-template/.claude/skills/build-feature/SKILL.md +201 -0
  47. package/templates/store-template/.claude/skills/customize-theme/SKILL.md +167 -0
  48. package/templates/store-template/.claude/skills/debug-errors/SKILL.md +68 -0
  49. package/templates/store-template/.claude/skills/frontend-design/SKILL.md +176 -0
  50. package/templates/store-template/.claude/skills/generate-images/SKILL.md +115 -0
  51. package/templates/store-template/.claude/skills/manage-products/SKILL.md +187 -0
  52. package/templates/store-template/.claude/skills/override-sdk-component/SKILL.md +154 -0
  53. package/templates/store-template/.env.example +50 -0
  54. package/templates/store-template/.mcp.json +8 -0
  55. package/templates/store-template/.rovela/blueprint.example.json +190 -0
  56. package/templates/store-template/CLAUDE.md +14 -0
  57. package/templates/store-template/app/LayoutContent.tsx +106 -0
  58. package/templates/store-template/app/[...path]/page.tsx +79 -0
  59. package/templates/store-template/app/account/addresses/page.tsx +45 -0
  60. package/templates/store-template/app/account/layout.tsx +17 -0
  61. package/templates/store-template/app/account/orders/[id]/invoice/page.tsx +277 -0
  62. package/templates/store-template/app/account/orders/[id]/page.tsx +725 -0
  63. package/templates/store-template/app/account/orders/page.tsx +153 -0
  64. package/templates/store-template/app/account/page.tsx +184 -0
  65. package/templates/store-template/app/account/profile/page.tsx +434 -0
  66. package/templates/store-template/app/admin/(dashboard)/account/page.tsx +16 -0
  67. package/templates/store-template/app/admin/(dashboard)/analytics/events/page.tsx +14 -0
  68. package/templates/store-template/app/admin/(dashboard)/analytics/layout.tsx +48 -0
  69. package/templates/store-template/app/admin/(dashboard)/analytics/page.tsx +15 -0
  70. package/templates/store-template/app/admin/(dashboard)/analytics/visitors/page.tsx +14 -0
  71. package/templates/store-template/app/admin/(dashboard)/categories/[id]/page.tsx +38 -0
  72. package/templates/store-template/app/admin/(dashboard)/categories/new/page.tsx +35 -0
  73. package/templates/store-template/app/admin/(dashboard)/categories/page.tsx +223 -0
  74. package/templates/store-template/app/admin/(dashboard)/customers/[id]/page.tsx +26 -0
  75. package/templates/store-template/app/admin/(dashboard)/customers/page.tsx +33 -0
  76. package/templates/store-template/app/admin/(dashboard)/discounts/page.tsx +16 -0
  77. package/templates/store-template/app/admin/(dashboard)/emails/page.tsx +16 -0
  78. package/templates/store-template/app/admin/(dashboard)/files/page.tsx +16 -0
  79. package/templates/store-template/app/admin/(dashboard)/layout.tsx +44 -0
  80. package/templates/store-template/app/admin/(dashboard)/loading.tsx +88 -0
  81. package/templates/store-template/app/admin/(dashboard)/locations/page.tsx +16 -0
  82. package/templates/store-template/app/admin/(dashboard)/orders/[id]/page.tsx +66 -0
  83. package/templates/store-template/app/admin/(dashboard)/orders/page.tsx +26 -0
  84. package/templates/store-template/app/admin/(dashboard)/page.tsx +146 -0
  85. package/templates/store-template/app/admin/(dashboard)/payments/page.tsx +15 -0
  86. package/templates/store-template/app/admin/(dashboard)/products/[id]/page.tsx +38 -0
  87. package/templates/store-template/app/admin/(dashboard)/products/new/page.tsx +35 -0
  88. package/templates/store-template/app/admin/(dashboard)/products/page.tsx +31 -0
  89. package/templates/store-template/app/admin/(dashboard)/settings/page.tsx +46 -0
  90. package/templates/store-template/app/admin/(dashboard)/shipping/page.tsx +15 -0
  91. package/templates/store-template/app/admin/(dashboard)/tax/page.tsx +15 -0
  92. package/templates/store-template/app/admin/(dashboard)/users/[id]/page.tsx +114 -0
  93. package/templates/store-template/app/admin/(dashboard)/users/page.tsx +16 -0
  94. package/templates/store-template/app/admin/accept-invite/page.tsx +36 -0
  95. package/templates/store-template/app/admin/forgot-password/page.tsx +13 -0
  96. package/templates/store-template/app/admin/layout.tsx +47 -0
  97. package/templates/store-template/app/admin/login/page.tsx +528 -0
  98. package/templates/store-template/app/admin/reset-password/page.tsx +36 -0
  99. package/templates/store-template/app/api/account/addresses/[id]/route.ts +10 -0
  100. package/templates/store-template/app/api/account/addresses/route.ts +10 -0
  101. package/templates/store-template/app/api/account/change-password/route.ts +107 -0
  102. package/templates/store-template/app/api/account/orders/[id]/refund/route.ts +11 -0
  103. package/templates/store-template/app/api/account/orders/[id]/return/route.ts +14 -0
  104. package/templates/store-template/app/api/account/orders/[id]/route.ts +42 -0
  105. package/templates/store-template/app/api/account/orders/route.ts +29 -0
  106. package/templates/store-template/app/api/account/profile/route.ts +165 -0
  107. package/templates/store-template/app/api/admin/accept-invite/route.ts +11 -0
  108. package/templates/store-template/app/api/admin/analytics/events/route.ts +9 -0
  109. package/templates/store-template/app/api/admin/analytics/route.ts +10 -0
  110. package/templates/store-template/app/api/admin/analytics/visitors/route.ts +9 -0
  111. package/templates/store-template/app/api/admin/branding/favicon/route.ts +9 -0
  112. package/templates/store-template/app/api/admin/branding/logo/route.ts +9 -0
  113. package/templates/store-template/app/api/admin/categories/[id]/route.ts +9 -0
  114. package/templates/store-template/app/api/admin/categories/route.ts +9 -0
  115. package/templates/store-template/app/api/admin/check/route.ts +10 -0
  116. package/templates/store-template/app/api/admin/customer-groups/[id]/route.ts +10 -0
  117. package/templates/store-template/app/api/admin/customer-groups/route.ts +10 -0
  118. package/templates/store-template/app/api/admin/customers/[id]/addresses/[addressId]/route.ts +15 -0
  119. package/templates/store-template/app/api/admin/customers/[id]/addresses/route.ts +15 -0
  120. package/templates/store-template/app/api/admin/customers/[id]/route.ts +11 -0
  121. package/templates/store-template/app/api/admin/customers/bulk/route.ts +8 -0
  122. package/templates/store-template/app/api/admin/customers/route.ts +9 -0
  123. package/templates/store-template/app/api/admin/discounts/[id]/route.ts +11 -0
  124. package/templates/store-template/app/api/admin/discounts/route.ts +10 -0
  125. package/templates/store-template/app/api/admin/emails/preview/route.ts +4 -0
  126. package/templates/store-template/app/api/admin/emails/route.ts +5 -0
  127. package/templates/store-template/app/api/admin/emails/test/route.ts +4 -0
  128. package/templates/store-template/app/api/admin/files/route.ts +8 -0
  129. package/templates/store-template/app/api/admin/forgot-password/route.ts +7 -0
  130. package/templates/store-template/app/api/admin/linked-customer/route.ts +11 -0
  131. package/templates/store-template/app/api/admin/locations/[id]/route.ts +10 -0
  132. package/templates/store-template/app/api/admin/locations/route.ts +10 -0
  133. package/templates/store-template/app/api/admin/me/password/route.ts +13 -0
  134. package/templates/store-template/app/api/admin/me/route.ts +11 -0
  135. package/templates/store-template/app/api/admin/orders/[id]/refund/route.ts +7 -0
  136. package/templates/store-template/app/api/admin/orders/[id]/return/approve/route.ts +7 -0
  137. package/templates/store-template/app/api/admin/orders/[id]/return/reject/route.ts +7 -0
  138. package/templates/store-template/app/api/admin/orders/[id]/route.ts +9 -0
  139. package/templates/store-template/app/api/admin/orders/[id]/ship-with-shippo/route.ts +6 -0
  140. package/templates/store-template/app/api/admin/orders/[id]/shippo-rates/route.ts +6 -0
  141. package/templates/store-template/app/api/admin/orders/bulk/route.ts +8 -0
  142. package/templates/store-template/app/api/admin/orders/route.ts +9 -0
  143. package/templates/store-template/app/api/admin/payment-methods/[id]/route.ts +10 -0
  144. package/templates/store-template/app/api/admin/payment-methods/route.ts +10 -0
  145. package/templates/store-template/app/api/admin/products/[id]/route.ts +9 -0
  146. package/templates/store-template/app/api/admin/products/bulk/route.ts +9 -0
  147. package/templates/store-template/app/api/admin/products/route.ts +9 -0
  148. package/templates/store-template/app/api/admin/products/stats/route.ts +10 -0
  149. package/templates/store-template/app/api/admin/reset-password/route.ts +10 -0
  150. package/templates/store-template/app/api/admin/settings/route.ts +11 -0
  151. package/templates/store-template/app/api/admin/setup/route.ts +10 -0
  152. package/templates/store-template/app/api/admin/setup-guide/manual-mark/route.ts +13 -0
  153. package/templates/store-template/app/api/admin/setup-guide/route.ts +10 -0
  154. package/templates/store-template/app/api/admin/shipping/carriers/[id]/route.ts +9 -0
  155. package/templates/store-template/app/api/admin/shipping/carriers/route.ts +9 -0
  156. package/templates/store-template/app/api/admin/shipping/rates/[id]/route.ts +9 -0
  157. package/templates/store-template/app/api/admin/shipping/rates/route.ts +9 -0
  158. package/templates/store-template/app/api/admin/shipping/shippo/api-key/route.ts +1 -0
  159. package/templates/store-template/app/api/admin/shipping/shippo/validate/route.ts +1 -0
  160. package/templates/store-template/app/api/admin/shipping/zones/[id]/route.ts +9 -0
  161. package/templates/store-template/app/api/admin/shipping/zones/route.ts +9 -0
  162. package/templates/store-template/app/api/admin/stats/route.ts +9 -0
  163. package/templates/store-template/app/api/admin/stripe-status/route.ts +1 -0
  164. package/templates/store-template/app/api/admin/tax-zones/[id]/route.ts +7 -0
  165. package/templates/store-template/app/api/admin/tax-zones/route.ts +7 -0
  166. package/templates/store-template/app/api/admin/users/[id]/route.ts +14 -0
  167. package/templates/store-template/app/api/admin/users/route.ts +12 -0
  168. package/templates/store-template/app/api/admin-auth/[...nextauth]/route.ts +17 -0
  169. package/templates/store-template/app/api/analytics/track/route.ts +12 -0
  170. package/templates/store-template/app/api/auth/[...nextauth]/route.ts +19 -0
  171. package/templates/store-template/app/api/auth/forgot-password/route.ts +9 -0
  172. package/templates/store-template/app/api/auth/register/route.ts +9 -0
  173. package/templates/store-template/app/api/auth/resend-verification/route.ts +9 -0
  174. package/templates/store-template/app/api/auth/reset-password/route.ts +10 -0
  175. package/templates/store-template/app/api/auth/verify-email/route.ts +10 -0
  176. package/templates/store-template/app/api/categories/route.ts +10 -0
  177. package/templates/store-template/app/api/checkout/countries/route.ts +7 -0
  178. package/templates/store-template/app/api/checkout/discount/route.ts +9 -0
  179. package/templates/store-template/app/api/checkout/manual/route.ts +10 -0
  180. package/templates/store-template/app/api/checkout/payment-methods/route.ts +8 -0
  181. package/templates/store-template/app/api/checkout/pickup-locations/route.ts +8 -0
  182. package/templates/store-template/app/api/checkout/route.ts +10 -0
  183. package/templates/store-template/app/api/checkout/shipping/route.ts +21 -0
  184. package/templates/store-template/app/api/checkout/status/route.ts +10 -0
  185. package/templates/store-template/app/api/contact/route.ts +164 -0
  186. package/templates/store-template/app/api/media/presign/route.ts +7 -0
  187. package/templates/store-template/app/api/media/route.ts +7 -0
  188. package/templates/store-template/app/api/products/[slug]/route.ts +10 -0
  189. package/templates/store-template/app/api/products/route.ts +10 -0
  190. package/templates/store-template/app/api/store/settings/route.ts +12 -0
  191. package/templates/store-template/app/api/webhooks/stripe/route.ts +63 -0
  192. package/templates/store-template/app/auth/forgot-password/page.tsx +38 -0
  193. package/templates/store-template/app/auth/layout.tsx +17 -0
  194. package/templates/store-template/app/auth/reset-password/page.tsx +81 -0
  195. package/templates/store-template/app/auth/signin/page.tsx +62 -0
  196. package/templates/store-template/app/auth/signup/page.tsx +38 -0
  197. package/templates/store-template/app/auth/verify-email/page.tsx +185 -0
  198. package/templates/store-template/app/cart/page.tsx +137 -0
  199. package/templates/store-template/app/checkout/page.tsx +88 -0
  200. package/templates/store-template/app/checkout/success/page.tsx +50 -0
  201. package/templates/store-template/app/contact/layout.tsx +24 -0
  202. package/templates/store-template/app/contact/page.tsx +321 -0
  203. package/templates/store-template/app/faq/page.tsx +153 -0
  204. package/templates/store-template/app/globals.css +99 -0
  205. package/templates/store-template/app/layout.tsx +269 -0
  206. package/templates/store-template/app/not-found.tsx +35 -0
  207. package/templates/store-template/app/page.tsx +23 -0
  208. package/templates/store-template/app/privacy/page.tsx +281 -0
  209. package/templates/store-template/app/products/[slug]/ProductDetailClient.tsx +179 -0
  210. package/templates/store-template/app/products/[slug]/page.tsx +149 -0
  211. package/templates/store-template/app/products/layout.tsx +26 -0
  212. package/templates/store-template/app/products/page.tsx +102 -0
  213. package/templates/store-template/app/robots.ts +25 -0
  214. package/templates/store-template/app/sitemap.ts +76 -0
  215. package/templates/store-template/app/terms/page.tsx +313 -0
  216. package/templates/store-template/components/PathSyncBridge.tsx +61 -0
  217. package/templates/store-template/components/Providers.tsx +37 -0
  218. package/templates/store-template/components/layout/Footer.tsx +150 -0
  219. package/templates/store-template/components/layout/Header.tsx +162 -0
  220. package/templates/store-template/components/sections/.gitkeep +7 -0
  221. package/templates/store-template/components/sections/CTA.tsx +51 -0
  222. package/templates/store-template/components/sections/Features.tsx +91 -0
  223. package/templates/store-template/components/sections/Hero.tsx +85 -0
  224. package/templates/store-template/eslint.config.mjs +18 -0
  225. package/templates/store-template/lib/store-config.ts +77 -0
  226. package/templates/store-template/lib/store-policy.ts +68 -0
  227. package/templates/store-template/lib/utils.ts +21 -0
  228. package/templates/store-template/next-env.d.ts +6 -0
  229. package/templates/store-template/next.config.js +105 -0
  230. package/templates/store-template/package.json +32 -0
  231. package/templates/store-template/postcss.config.js +6 -0
  232. package/templates/store-template/proxy.ts +150 -0
  233. package/templates/store-template/public/rovela-logo.png +0 -0
  234. package/templates/store-template/tailwind.config.ts +74 -0
  235. package/templates/store-template/tsconfig.json +41 -0
  236. package/templates/store-template/types/next-auth.d.ts +32 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Customer Refund Request API Route
3
+ *
4
+ * POST /api/account/orders/[id]/refund - Request a refund for an order
5
+ *
6
+ * Only available for orders that:
7
+ * - Belong to the authenticated customer
8
+ * - Have status 'paid' (not yet shipped)
9
+ */
10
+
11
+ export { POST } from '@rovela-ai/sdk/auth/api/request-refund'
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Customer Return Request API Route
3
+ *
4
+ * POST /api/account/orders/[id]/return - Request a return for a delivered order
5
+ *
6
+ * Only available for orders that:
7
+ * - Belong to the authenticated customer
8
+ * - Have status 'delivered'
9
+ *
10
+ * This creates a return request that must be approved by an admin
11
+ * before a refund is processed.
12
+ */
13
+
14
+ export { POST } from '@rovela-ai/sdk/auth/api/request-return'
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Customer Single Order API Route
3
+ *
4
+ * GET /api/account/orders/[id] - Get single order for authenticated customer
5
+ *
6
+ * Protected by `requireCustomer()`. In addition, we verify the order's
7
+ * `customerId` matches the authenticated customer — cross-customer access
8
+ * returns 403 rather than leaking existence information.
9
+ */
10
+
11
+ import { requireCustomer } from '@rovela-ai/sdk/auth/server'
12
+ import { findOrderById } from '@rovela-ai/sdk/core/server'
13
+
14
+ export async function GET(
15
+ _request: Request,
16
+ { params }: { params: Promise<{ id: string }> }
17
+ ) {
18
+ const guard = await requireCustomer()
19
+ if (!guard.ok) return guard.response
20
+
21
+ try {
22
+ const { id } = await params
23
+ const order = await findOrderById(id)
24
+
25
+ if (!order) {
26
+ return Response.json({ error: 'Order not found' }, { status: 404 })
27
+ }
28
+
29
+ // Verify the order belongs to this customer
30
+ if (order.customerId !== guard.customer.id) {
31
+ return Response.json({ error: 'Forbidden' }, { status: 403 })
32
+ }
33
+
34
+ return Response.json({ order })
35
+ } catch (error) {
36
+ console.error('Failed to fetch order:', error)
37
+ return Response.json(
38
+ { error: 'Failed to fetch order' },
39
+ { status: 500 }
40
+ )
41
+ }
42
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Customer Orders API Route
3
+ *
4
+ * GET /api/account/orders - Get orders for authenticated customer
5
+ *
6
+ * Protected by `requireCustomer()` which:
7
+ * - Rejects unauthenticated requests (401)
8
+ * - Rejects admin sessions (403) — defense in depth
9
+ * - Verifies the customer row still exists in the store DB (401 if not)
10
+ */
11
+
12
+ import { requireCustomer } from '@rovela-ai/sdk/auth/server'
13
+ import { findOrdersWithItems } from '@rovela-ai/sdk/core/server'
14
+
15
+ export async function GET() {
16
+ const guard = await requireCustomer()
17
+ if (!guard.ok) return guard.response
18
+
19
+ try {
20
+ const orders = await findOrdersWithItems({ customerId: guard.customer.id })
21
+ return Response.json({ orders })
22
+ } catch (error) {
23
+ console.error('Failed to fetch orders:', error)
24
+ return Response.json(
25
+ { error: 'Failed to fetch orders' },
26
+ { status: 500 }
27
+ )
28
+ }
29
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Customer Profile API Route
3
+ *
4
+ * GET /api/account/profile - Get current customer profile
5
+ * PUT /api/account/profile - Update customer name and/or email
6
+ *
7
+ * Protected by `requireCustomer()` — admins get a 403, not a 401, because
8
+ * the endpoint is valid but the identity class isn't.
9
+ */
10
+
11
+ import { requireCustomer } from '@rovela-ai/sdk/auth/server'
12
+ import {
13
+ findCustomerById,
14
+ updateCustomer,
15
+ emailExists,
16
+ } from '@rovela-ai/sdk/auth/server'
17
+
18
+ /**
19
+ * GET /api/account/profile
20
+ *
21
+ * Returns the authenticated customer's profile information.
22
+ */
23
+ export async function GET() {
24
+ const guard = await requireCustomer()
25
+ if (!guard.ok) return guard.response
26
+
27
+ try {
28
+ const customer = await findCustomerById(guard.customer.id)
29
+
30
+ if (!customer) {
31
+ return Response.json({ error: 'Customer not found' }, { status: 404 })
32
+ }
33
+
34
+ // Return profile data (exclude sensitive fields)
35
+ return Response.json({
36
+ profile: {
37
+ id: customer.id,
38
+ email: customer.email,
39
+ name: customer.name,
40
+ phone: customer.phone ?? null,
41
+ emailVerified: !!customer.emailVerified,
42
+ createdAt: customer.createdAt,
43
+ },
44
+ })
45
+ } catch (error) {
46
+ console.error('[Profile API] Failed to fetch profile:', error)
47
+ return Response.json(
48
+ { error: 'Failed to fetch profile' },
49
+ { status: 500 }
50
+ )
51
+ }
52
+ }
53
+
54
+ /**
55
+ * PUT /api/account/profile
56
+ *
57
+ * Updates the authenticated customer's profile.
58
+ * Accepts: { name?: string, email?: string, phone?: string | null }
59
+ */
60
+ export async function PUT(request: Request) {
61
+ const guard = await requireCustomer()
62
+ if (!guard.ok) return guard.response
63
+
64
+ try {
65
+ const body = await request.json()
66
+ const { name, email, phone } = body as {
67
+ name?: string
68
+ email?: string
69
+ phone?: string | null
70
+ }
71
+
72
+ // Validate input
73
+ if (name !== undefined && typeof name !== 'string') {
74
+ return Response.json(
75
+ { error: 'Name must be a string' },
76
+ { status: 400 }
77
+ )
78
+ }
79
+
80
+ if (name !== undefined && name.trim().length > 100) {
81
+ return Response.json(
82
+ { error: 'Name must be 100 characters or less' },
83
+ { status: 400 }
84
+ )
85
+ }
86
+
87
+ if (phone !== undefined && phone !== null && typeof phone !== 'string') {
88
+ return Response.json(
89
+ { error: 'Phone must be a string' },
90
+ { status: 400 }
91
+ )
92
+ }
93
+
94
+ if (typeof phone === 'string' && phone.trim().length > 40) {
95
+ return Response.json(
96
+ { error: 'Phone must be 40 characters or less' },
97
+ { status: 400 }
98
+ )
99
+ }
100
+
101
+ if (email !== undefined) {
102
+ if (typeof email !== 'string') {
103
+ return Response.json(
104
+ { error: 'Email must be a string' },
105
+ { status: 400 }
106
+ )
107
+ }
108
+
109
+ // Simple email validation
110
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
111
+ if (!emailRegex.test(email)) {
112
+ return Response.json(
113
+ { error: 'Invalid email format' },
114
+ { status: 400 }
115
+ )
116
+ }
117
+
118
+ // Check if email is already in use by another customer
119
+ const normalizedEmail = email.toLowerCase().trim()
120
+ const currentCustomer = await findCustomerById(guard.customer.id)
121
+
122
+ if (currentCustomer && currentCustomer.email !== normalizedEmail) {
123
+ const emailTaken = await emailExists(normalizedEmail)
124
+ if (emailTaken) {
125
+ return Response.json(
126
+ { error: 'Email is already registered' },
127
+ { status: 400 }
128
+ )
129
+ }
130
+ }
131
+ }
132
+
133
+ // Update customer
134
+ const updatedCustomer = await updateCustomer(guard.customer.id, {
135
+ name: name?.trim(),
136
+ email: email?.toLowerCase().trim(),
137
+ ...(phone !== undefined ? { phone: phone === null ? null : phone.trim() } : {}),
138
+ })
139
+
140
+ if (!updatedCustomer) {
141
+ return Response.json(
142
+ { error: 'Customer not found' },
143
+ { status: 404 }
144
+ )
145
+ }
146
+
147
+ return Response.json({
148
+ success: true,
149
+ profile: {
150
+ id: updatedCustomer.id,
151
+ email: updatedCustomer.email,
152
+ name: updatedCustomer.name,
153
+ phone: updatedCustomer.phone ?? null,
154
+ emailVerified: !!updatedCustomer.emailVerified,
155
+ createdAt: updatedCustomer.createdAt,
156
+ },
157
+ })
158
+ } catch (error) {
159
+ console.error('[Profile API] Failed to update profile:', error)
160
+ return Response.json(
161
+ { error: 'Failed to update profile' },
162
+ { status: 500 }
163
+ )
164
+ }
165
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Accept Invite API Route
3
+ *
4
+ * Thin re-export of the SDK handlers:
5
+ * GET ?token=X — non-destructive token validation (returns invitee snippet)
6
+ * POST { token, password } — consume the token, activate the admin
7
+ *
8
+ * Unauthenticated — the token IS the auth.
9
+ */
10
+
11
+ export { GET, POST } from '@rovela-ai/sdk/admin/api/accept-invite'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * GET /api/admin/analytics/events
3
+ *
4
+ * Cursor-paginated raw event log. Admin-gated.
5
+ *
6
+ * See CLAUDE.md §46.
7
+ */
8
+
9
+ export { GET } from '@rovela-ai/sdk/analytics/api/events'
@@ -0,0 +1,10 @@
1
+ /**
2
+ * GET /api/admin/analytics?period=today|7d|30d|90d
3
+ *
4
+ * Admin-gated dashboard payload (visitors, sessions, pageviews, conversion,
5
+ * top pages, top products, traffic sources, devices). Gated on settings.read.
6
+ *
7
+ * See CLAUDE.md §46.
8
+ */
9
+
10
+ export { GET } from '@rovela-ai/sdk/analytics/api/dashboard'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * GET /api/admin/analytics/visitors
3
+ *
4
+ * Per-visitor aggregate (offset-paginated). Admin-gated.
5
+ *
6
+ * See CLAUDE.md §46.
7
+ */
8
+
9
+ export { GET } from '@rovela-ai/sdk/analytics/api/visitors'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Favicon API — SDK re-export (see @rovela-ai/sdk/admin/api/branding).
3
+ *
4
+ * Same architecture as the logo route: R2 object at the fixed key
5
+ * stores/{STORE_ID}/branding/favicon + store_settings.favicon_url mirror.
6
+ * The favicon surfaces in the browser tab (app/layout.tsx metadata) and the
7
+ * admin top bar tile; both fall back to the logo when no favicon is set.
8
+ */
9
+ export { faviconGET as GET, faviconPOST as POST, faviconDELETE as DELETE } from '@rovela-ai/sdk/admin/api/branding'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Logo API — SDK re-export (see @rovela-ai/sdk/admin/api/branding).
3
+ *
4
+ * Writes the R2 object at the fixed key stores/{STORE_ID}/branding/logo AND
5
+ * mirrors the URL into store_settings.logo_url (the store-side runtime
6
+ * mirror every settings-first surface reads: header, emails, admin top bar).
7
+ * GET reads the truth directly via an R2 HEAD on the fixed key.
8
+ */
9
+ export { logoGET as GET, logoPOST as POST, logoDELETE as DELETE } from '@rovela-ai/sdk/admin/api/branding'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Single Category API Route
3
+ *
4
+ * GET/PUT/DELETE /api/admin/categories/[id]
5
+ */
6
+
7
+ import { getCategory, updateCategory, deleteCategory } from '@rovela-ai/sdk/admin/api'
8
+
9
+ export { getCategory as GET, updateCategory as PUT, deleteCategory as DELETE }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Categories API Route
3
+ *
4
+ * GET/POST /api/admin/categories - List/Create categories
5
+ */
6
+
7
+ import { getCategories, createCategory } from '@rovela-ai/sdk/admin/api'
8
+
9
+ export { getCategories as GET, createCategory as POST }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Check API Route
3
+ *
4
+ * Check if any admin exists for the store.
5
+ * Used by login page to determine whether to show setup or login form.
6
+ */
7
+
8
+ import { GET as handler } from '@rovela-ai/sdk/admin/api/check'
9
+
10
+ export const GET = handler
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Single Customer Group API Route
3
+ *
4
+ * PUT /api/admin/customer-groups/[id] - Update a group
5
+ * DELETE /api/admin/customer-groups/[id] - Delete a group (members are ungrouped)
6
+ */
7
+
8
+ import { updateCustomerGroupHandler, deleteCustomerGroupHandler } from '@rovela-ai/sdk/admin/api'
9
+
10
+ export { updateCustomerGroupHandler as PUT, deleteCustomerGroupHandler as DELETE }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Customer Groups API Route
3
+ *
4
+ * GET /api/admin/customer-groups - List groups (with member counts)
5
+ * POST /api/admin/customer-groups - Create a group
6
+ */
7
+
8
+ import { getCustomerGroups, createCustomerGroupHandler } from '@rovela-ai/sdk/admin/api'
9
+
10
+ export { getCustomerGroups as GET, createCustomerGroupHandler as POST }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Admin Customer Address — update / set-default / archive one address.
3
+ *
4
+ * PATCH /api/admin/customers/[id]/addresses/[addressId] → update / set default
5
+ * DELETE /api/admin/customers/[id]/addresses/[addressId] → soft-delete (archive)
6
+ *
7
+ * Gated by `customers.write` inside the SDK handlers.
8
+ */
9
+
10
+ import {
11
+ updateCustomerAddressAdmin,
12
+ deleteCustomerAddressAdmin,
13
+ } from '@rovela-ai/sdk/admin/api'
14
+
15
+ export { updateCustomerAddressAdmin as PATCH, deleteCustomerAddressAdmin as DELETE }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Admin Customer Address Book — list + create.
3
+ *
4
+ * GET /api/admin/customers/[id]/addresses → list a customer's saved addresses
5
+ * POST /api/admin/customers/[id]/addresses → create an address for the customer
6
+ *
7
+ * Gated by `customers.read` / `customers.write` inside the SDK handlers.
8
+ */
9
+
10
+ import {
11
+ listCustomerAddressesAdmin,
12
+ createCustomerAddressAdmin,
13
+ } from '@rovela-ai/sdk/admin/api'
14
+
15
+ export { listCustomerAddressesAdmin as GET, createCustomerAddressAdmin as POST }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Single Customer API Route
3
+ *
4
+ * GET /api/admin/customers/[id] - Get customer details
5
+ * PUT /api/admin/customers/[id] - Update customer
6
+ * DELETE /api/admin/customers/[id] - Delete (anonymize) customer
7
+ */
8
+
9
+ import { getCustomer, updateCustomer, deleteCustomer } from '@rovela-ai/sdk/admin/api'
10
+
11
+ export { getCustomer as GET, updateCustomer as PUT, deleteCustomer as DELETE }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Admin Customers Bulk API Route
3
+ *
4
+ * POST /api/admin/customers/bulk - Bulk group assign / soft delete
5
+ * Body: { ids: string[], action: 'assign_group' | 'delete', groupId?: string | null }
6
+ */
7
+
8
+ export { bulkCustomers as POST } from '@rovela-ai/sdk/admin/api'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Customers API Route
3
+ *
4
+ * GET /api/admin/customers - List customers
5
+ */
6
+
7
+ import { getCustomers } from '@rovela-ai/sdk/admin/api'
8
+
9
+ export { getCustomers as GET }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Single Discount API Route
3
+ *
4
+ * PUT /api/admin/discounts/[id] - Update a discount
5
+ * DELETE /api/admin/discounts/[id] - Delete a discount (order history keeps
6
+ * its code + amount snapshots; redemption records survive)
7
+ */
8
+
9
+ import { updateDiscountHandler, deleteDiscountHandler } from '@rovela-ai/sdk/admin/api'
10
+
11
+ export { updateDiscountHandler as PUT, deleteDiscountHandler as DELETE }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Discounts API Route
3
+ *
4
+ * GET /api/admin/discounts - List all discounts
5
+ * POST /api/admin/discounts - Create a discount (code or automatic)
6
+ */
7
+
8
+ import { getDiscounts, createDiscountHandler } from '@rovela-ai/sdk/admin/api'
9
+
10
+ export { getDiscounts as GET, createDiscountHandler as POST }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Admin emails API — live HTML preview (sample data + drafts, nothing sent).
3
+ */
4
+ export { previewEmail as POST } from '@rovela-ai/sdk/admin/api/emails'
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Admin emails API — registry + overrides + theme state (GET), saves (PUT).
3
+ * SDK re-export (see @rovela-ai/sdk/admin/api/emails).
4
+ */
5
+ export { GET, PUT } from '@rovela-ai/sdk/admin/api/emails'
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Admin emails API — send the sample email to the signed-in admin.
3
+ */
4
+ export { sendTestEmail as POST } from '@rovela-ai/sdk/admin/api/emails'
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Admin Files API Route
3
+ *
4
+ * GET /api/admin/files - List the store's media library (R2 listing)
5
+ * DELETE /api/admin/files - Delete one file by key (system assets protected)
6
+ */
7
+
8
+ export { getStoreFilesHandler as GET, deleteStoreFileHandler as DELETE } from '@rovela-ai/sdk/media/api'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Admin Forgot Password API Route
3
+ *
4
+ * Thin re-export of the SDK handler.
5
+ */
6
+
7
+ export { POST } from '@rovela-ai/sdk/admin/api/forgot-password'
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Linked-Customer API Route
3
+ *
4
+ * Thin re-export of the SDK handler:
5
+ * GET — returns { hasLinkedCustomer } for the current admin.
6
+ *
7
+ * Powers the conditional "My customer account" entry in the admin user
8
+ * menu (R112). Gated on the SDK's `requireAdmin()`.
9
+ */
10
+
11
+ export { GET } from '@rovela-ai/sdk/admin/api/linked-customer'
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Location API Route (single)
3
+ *
4
+ * PUT /api/admin/locations/[id] - Update a location
5
+ * DELETE /api/admin/locations/[id] - Delete a location
6
+ */
7
+
8
+ import { updateLocationHandler, deleteLocationHandler } from '@rovela-ai/sdk/admin/api'
9
+
10
+ export { updateLocationHandler as PUT, deleteLocationHandler as DELETE }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Admin Locations API Route
3
+ *
4
+ * GET /api/admin/locations - List store locations
5
+ * POST /api/admin/locations - Create a location
6
+ */
7
+
8
+ import { getLocations, createLocationHandler } from '@rovela-ai/sdk/admin/api'
9
+
10
+ export { getLocations as GET, createLocationHandler as POST }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Admin Me Password API Route
3
+ *
4
+ * Thin re-export of the SDK handler. This route is mounted separately
5
+ * from `/api/admin/me` because Next.js uses the HTTP method name as the
6
+ * export binding — we have two PATCH handlers (one for profile, one for
7
+ * password), and they need distinct route files.
8
+ *
9
+ * PATCH /api/admin/me/password — change own password
10
+ * Body: { currentPassword, newPassword }
11
+ */
12
+
13
+ export { PATCH_PASSWORD as PATCH } from '@rovela-ai/sdk/admin/api/me'
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin Me API Route
3
+ *
4
+ * Thin re-export of the SDK handlers:
5
+ * GET — return the caller's own profile
6
+ * PATCH — update own name / email
7
+ *
8
+ * The password change endpoint lives at /api/admin/me/password.
9
+ */
10
+
11
+ export { GET, PATCH } from '@rovela-ai/sdk/admin/api/me'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Admin Order Refund API Route
3
+ *
4
+ * POST /api/admin/orders/[id]/refund - Process a refund via Stripe
5
+ */
6
+
7
+ export { POST } from '@rovela-ai/sdk/admin/api/refund'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Admin Return Approval API Route
3
+ *
4
+ * POST /api/admin/orders/[id]/return/approve - Approve a return request and process refund
5
+ */
6
+
7
+ export { APPROVE_POST as POST } from '@rovela-ai/sdk/admin/api/return'
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Admin Return Rejection API Route
3
+ *
4
+ * POST /api/admin/orders/[id]/return/reject - Reject a return request
5
+ */
6
+
7
+ export { REJECT_POST as POST } from '@rovela-ai/sdk/admin/api/return'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Single Order API Route
3
+ *
4
+ * GET/PUT /api/admin/orders/[id]
5
+ */
6
+
7
+ import { getOrder, updateOrder } from '@rovela-ai/sdk/admin/api'
8
+
9
+ export { getOrder as GET, updateOrder as PUT }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Purchase Shippo shipping label for an order
3
+ */
4
+ import { shipWithShippo } from '@rovela-ai/sdk/admin/api'
5
+
6
+ export { shipWithShippo as POST }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get Shippo shipping rates for an order
3
+ */
4
+ import { getShippoRatesForOrder } from '@rovela-ai/sdk/admin/api'
5
+
6
+ export { getShippoRatesForOrder as GET }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Admin Orders Bulk API Route
3
+ *
4
+ * POST /api/admin/orders/bulk - Bulk status update
5
+ * Body: { ids: string[], status: OrderStatus, notifyCustomer?: boolean }
6
+ */
7
+
8
+ export { bulkUpdateOrders as POST } from '@rovela-ai/sdk/admin/api'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Orders API Route
3
+ *
4
+ * GET /api/admin/orders - List orders
5
+ */
6
+
7
+ import { getOrders } from '@rovela-ai/sdk/admin/api'
8
+
9
+ export { getOrders as GET }