@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.
- package/dist/admin/components/AdminPageHeader.d.ts.map +1 -1
- package/dist/admin/components/AdminPageHeader.js +9 -2
- package/dist/admin/components/AdminPageHeader.js.map +1 -1
- package/dist/admin/components/AdminUserMenu.d.ts.map +1 -1
- package/dist/admin/components/AdminUserMenu.js +2 -1
- package/dist/admin/components/AdminUserMenu.js.map +1 -1
- package/dist/admin/components/CustomerDetails.d.ts.map +1 -1
- package/dist/admin/components/CustomerDetails.js +2 -1
- package/dist/admin/components/CustomerDetails.js.map +1 -1
- package/dist/admin/components/EmailsManager.d.ts.map +1 -1
- package/dist/admin/components/EmailsManager.js +2 -1
- package/dist/admin/components/EmailsManager.js.map +1 -1
- package/dist/admin/components/LowStockAlert.d.ts.map +1 -1
- package/dist/admin/components/LowStockAlert.js +3 -2
- package/dist/admin/components/LowStockAlert.js.map +1 -1
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +2 -1
- package/dist/admin/components/OrderDetails.js.map +1 -1
- package/dist/admin/components/ProductTable.d.ts.map +1 -1
- package/dist/admin/components/ProductTable.js +4 -3
- package/dist/admin/components/ProductTable.js.map +1 -1
- package/dist/admin/components/RecentOrders.d.ts.map +1 -1
- package/dist/admin/components/RecentOrders.js +2 -1
- package/dist/admin/components/RecentOrders.js.map +1 -1
- package/dist/admin/components/SEOPreview.d.ts +2 -8
- package/dist/admin/components/SEOPreview.d.ts.map +1 -1
- package/dist/admin/components/SEOPreview.js +32 -5
- package/dist/admin/components/SEOPreview.js.map +1 -1
- package/dist/admin/components/SecondaryMetricsRow.d.ts.map +1 -1
- package/dist/admin/components/SecondaryMetricsRow.js +3 -2
- package/dist/admin/components/SecondaryMetricsRow.js.map +1 -1
- package/dist/admin/components/StatsCards.d.ts.map +1 -1
- package/dist/admin/components/StatsCards.js +2 -1
- package/dist/admin/components/StatsCards.js.map +1 -1
- package/docs/changelog/README.md +53 -0
- package/docs/changelog/epoch-01-chrome-consent.md +63 -0
- package/docs/changelog/epoch-02-platform-commerce.md +108 -0
- package/docs/changelog/epoch-03-i18n-files.md +72 -0
- package/docs/changelog/epoch-04-emails.md +65 -0
- package/docs/changelog/epoch-05-admin-experience.md +98 -0
- package/docs/changelog/epoch-06-mobile-integrity.md +92 -0
- package/docs/design/admin-ledger-visual-universe.html +1961 -0
- package/docs/design/admin-sidebar-and-band.html +542 -0
- package/package.json +3 -1
- package/templates/store-template/.claude/skills/add-section/SKILL.md +438 -0
- package/templates/store-template/.claude/skills/build-feature/SKILL.md +201 -0
- package/templates/store-template/.claude/skills/customize-theme/SKILL.md +167 -0
- package/templates/store-template/.claude/skills/debug-errors/SKILL.md +68 -0
- package/templates/store-template/.claude/skills/frontend-design/SKILL.md +176 -0
- package/templates/store-template/.claude/skills/generate-images/SKILL.md +115 -0
- package/templates/store-template/.claude/skills/manage-products/SKILL.md +187 -0
- package/templates/store-template/.claude/skills/override-sdk-component/SKILL.md +154 -0
- package/templates/store-template/.env.example +50 -0
- package/templates/store-template/.mcp.json +8 -0
- package/templates/store-template/.rovela/blueprint.example.json +190 -0
- package/templates/store-template/CLAUDE.md +14 -0
- package/templates/store-template/app/LayoutContent.tsx +106 -0
- package/templates/store-template/app/[...path]/page.tsx +79 -0
- package/templates/store-template/app/account/addresses/page.tsx +45 -0
- package/templates/store-template/app/account/layout.tsx +17 -0
- package/templates/store-template/app/account/orders/[id]/invoice/page.tsx +277 -0
- package/templates/store-template/app/account/orders/[id]/page.tsx +725 -0
- package/templates/store-template/app/account/orders/page.tsx +153 -0
- package/templates/store-template/app/account/page.tsx +184 -0
- package/templates/store-template/app/account/profile/page.tsx +434 -0
- package/templates/store-template/app/admin/(dashboard)/account/page.tsx +16 -0
- package/templates/store-template/app/admin/(dashboard)/analytics/events/page.tsx +14 -0
- package/templates/store-template/app/admin/(dashboard)/analytics/layout.tsx +48 -0
- package/templates/store-template/app/admin/(dashboard)/analytics/page.tsx +15 -0
- package/templates/store-template/app/admin/(dashboard)/analytics/visitors/page.tsx +14 -0
- package/templates/store-template/app/admin/(dashboard)/categories/[id]/page.tsx +38 -0
- package/templates/store-template/app/admin/(dashboard)/categories/new/page.tsx +35 -0
- package/templates/store-template/app/admin/(dashboard)/categories/page.tsx +223 -0
- package/templates/store-template/app/admin/(dashboard)/customers/[id]/page.tsx +26 -0
- package/templates/store-template/app/admin/(dashboard)/customers/page.tsx +33 -0
- package/templates/store-template/app/admin/(dashboard)/discounts/page.tsx +16 -0
- package/templates/store-template/app/admin/(dashboard)/emails/page.tsx +16 -0
- package/templates/store-template/app/admin/(dashboard)/files/page.tsx +16 -0
- package/templates/store-template/app/admin/(dashboard)/layout.tsx +44 -0
- package/templates/store-template/app/admin/(dashboard)/loading.tsx +88 -0
- package/templates/store-template/app/admin/(dashboard)/locations/page.tsx +16 -0
- package/templates/store-template/app/admin/(dashboard)/orders/[id]/page.tsx +66 -0
- package/templates/store-template/app/admin/(dashboard)/orders/page.tsx +26 -0
- package/templates/store-template/app/admin/(dashboard)/page.tsx +146 -0
- package/templates/store-template/app/admin/(dashboard)/payments/page.tsx +15 -0
- package/templates/store-template/app/admin/(dashboard)/products/[id]/page.tsx +38 -0
- package/templates/store-template/app/admin/(dashboard)/products/new/page.tsx +35 -0
- package/templates/store-template/app/admin/(dashboard)/products/page.tsx +31 -0
- package/templates/store-template/app/admin/(dashboard)/settings/page.tsx +46 -0
- package/templates/store-template/app/admin/(dashboard)/shipping/page.tsx +15 -0
- package/templates/store-template/app/admin/(dashboard)/tax/page.tsx +15 -0
- package/templates/store-template/app/admin/(dashboard)/users/[id]/page.tsx +114 -0
- package/templates/store-template/app/admin/(dashboard)/users/page.tsx +16 -0
- package/templates/store-template/app/admin/accept-invite/page.tsx +36 -0
- package/templates/store-template/app/admin/forgot-password/page.tsx +13 -0
- package/templates/store-template/app/admin/layout.tsx +47 -0
- package/templates/store-template/app/admin/login/page.tsx +528 -0
- package/templates/store-template/app/admin/reset-password/page.tsx +36 -0
- package/templates/store-template/app/api/account/addresses/[id]/route.ts +10 -0
- package/templates/store-template/app/api/account/addresses/route.ts +10 -0
- package/templates/store-template/app/api/account/change-password/route.ts +107 -0
- package/templates/store-template/app/api/account/orders/[id]/refund/route.ts +11 -0
- package/templates/store-template/app/api/account/orders/[id]/return/route.ts +14 -0
- package/templates/store-template/app/api/account/orders/[id]/route.ts +42 -0
- package/templates/store-template/app/api/account/orders/route.ts +29 -0
- package/templates/store-template/app/api/account/profile/route.ts +165 -0
- package/templates/store-template/app/api/admin/accept-invite/route.ts +11 -0
- package/templates/store-template/app/api/admin/analytics/events/route.ts +9 -0
- package/templates/store-template/app/api/admin/analytics/route.ts +10 -0
- package/templates/store-template/app/api/admin/analytics/visitors/route.ts +9 -0
- package/templates/store-template/app/api/admin/branding/favicon/route.ts +9 -0
- package/templates/store-template/app/api/admin/branding/logo/route.ts +9 -0
- package/templates/store-template/app/api/admin/categories/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/categories/route.ts +9 -0
- package/templates/store-template/app/api/admin/check/route.ts +10 -0
- package/templates/store-template/app/api/admin/customer-groups/[id]/route.ts +10 -0
- package/templates/store-template/app/api/admin/customer-groups/route.ts +10 -0
- package/templates/store-template/app/api/admin/customers/[id]/addresses/[addressId]/route.ts +15 -0
- package/templates/store-template/app/api/admin/customers/[id]/addresses/route.ts +15 -0
- package/templates/store-template/app/api/admin/customers/[id]/route.ts +11 -0
- package/templates/store-template/app/api/admin/customers/bulk/route.ts +8 -0
- package/templates/store-template/app/api/admin/customers/route.ts +9 -0
- package/templates/store-template/app/api/admin/discounts/[id]/route.ts +11 -0
- package/templates/store-template/app/api/admin/discounts/route.ts +10 -0
- package/templates/store-template/app/api/admin/emails/preview/route.ts +4 -0
- package/templates/store-template/app/api/admin/emails/route.ts +5 -0
- package/templates/store-template/app/api/admin/emails/test/route.ts +4 -0
- package/templates/store-template/app/api/admin/files/route.ts +8 -0
- package/templates/store-template/app/api/admin/forgot-password/route.ts +7 -0
- package/templates/store-template/app/api/admin/linked-customer/route.ts +11 -0
- package/templates/store-template/app/api/admin/locations/[id]/route.ts +10 -0
- package/templates/store-template/app/api/admin/locations/route.ts +10 -0
- package/templates/store-template/app/api/admin/me/password/route.ts +13 -0
- package/templates/store-template/app/api/admin/me/route.ts +11 -0
- package/templates/store-template/app/api/admin/orders/[id]/refund/route.ts +7 -0
- package/templates/store-template/app/api/admin/orders/[id]/return/approve/route.ts +7 -0
- package/templates/store-template/app/api/admin/orders/[id]/return/reject/route.ts +7 -0
- package/templates/store-template/app/api/admin/orders/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/orders/[id]/ship-with-shippo/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/[id]/shippo-rates/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/bulk/route.ts +8 -0
- package/templates/store-template/app/api/admin/orders/route.ts +9 -0
- package/templates/store-template/app/api/admin/payment-methods/[id]/route.ts +10 -0
- package/templates/store-template/app/api/admin/payment-methods/route.ts +10 -0
- package/templates/store-template/app/api/admin/products/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/products/bulk/route.ts +9 -0
- package/templates/store-template/app/api/admin/products/route.ts +9 -0
- package/templates/store-template/app/api/admin/products/stats/route.ts +10 -0
- package/templates/store-template/app/api/admin/reset-password/route.ts +10 -0
- package/templates/store-template/app/api/admin/settings/route.ts +11 -0
- package/templates/store-template/app/api/admin/setup/route.ts +10 -0
- package/templates/store-template/app/api/admin/setup-guide/manual-mark/route.ts +13 -0
- package/templates/store-template/app/api/admin/setup-guide/route.ts +10 -0
- package/templates/store-template/app/api/admin/shipping/carriers/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/shipping/carriers/route.ts +9 -0
- package/templates/store-template/app/api/admin/shipping/rates/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/shipping/rates/route.ts +9 -0
- package/templates/store-template/app/api/admin/shipping/shippo/api-key/route.ts +1 -0
- package/templates/store-template/app/api/admin/shipping/shippo/validate/route.ts +1 -0
- package/templates/store-template/app/api/admin/shipping/zones/[id]/route.ts +9 -0
- package/templates/store-template/app/api/admin/shipping/zones/route.ts +9 -0
- package/templates/store-template/app/api/admin/stats/route.ts +9 -0
- package/templates/store-template/app/api/admin/stripe-status/route.ts +1 -0
- package/templates/store-template/app/api/admin/tax-zones/[id]/route.ts +7 -0
- package/templates/store-template/app/api/admin/tax-zones/route.ts +7 -0
- package/templates/store-template/app/api/admin/users/[id]/route.ts +14 -0
- package/templates/store-template/app/api/admin/users/route.ts +12 -0
- package/templates/store-template/app/api/admin-auth/[...nextauth]/route.ts +17 -0
- package/templates/store-template/app/api/analytics/track/route.ts +12 -0
- package/templates/store-template/app/api/auth/[...nextauth]/route.ts +19 -0
- package/templates/store-template/app/api/auth/forgot-password/route.ts +9 -0
- package/templates/store-template/app/api/auth/register/route.ts +9 -0
- package/templates/store-template/app/api/auth/resend-verification/route.ts +9 -0
- package/templates/store-template/app/api/auth/reset-password/route.ts +10 -0
- package/templates/store-template/app/api/auth/verify-email/route.ts +10 -0
- package/templates/store-template/app/api/categories/route.ts +10 -0
- package/templates/store-template/app/api/checkout/countries/route.ts +7 -0
- package/templates/store-template/app/api/checkout/discount/route.ts +9 -0
- package/templates/store-template/app/api/checkout/manual/route.ts +10 -0
- package/templates/store-template/app/api/checkout/payment-methods/route.ts +8 -0
- package/templates/store-template/app/api/checkout/pickup-locations/route.ts +8 -0
- package/templates/store-template/app/api/checkout/route.ts +10 -0
- package/templates/store-template/app/api/checkout/shipping/route.ts +21 -0
- package/templates/store-template/app/api/checkout/status/route.ts +10 -0
- package/templates/store-template/app/api/contact/route.ts +164 -0
- package/templates/store-template/app/api/media/presign/route.ts +7 -0
- package/templates/store-template/app/api/media/route.ts +7 -0
- package/templates/store-template/app/api/products/[slug]/route.ts +10 -0
- package/templates/store-template/app/api/products/route.ts +10 -0
- package/templates/store-template/app/api/store/settings/route.ts +12 -0
- package/templates/store-template/app/api/webhooks/stripe/route.ts +63 -0
- package/templates/store-template/app/auth/forgot-password/page.tsx +38 -0
- package/templates/store-template/app/auth/layout.tsx +17 -0
- package/templates/store-template/app/auth/reset-password/page.tsx +81 -0
- package/templates/store-template/app/auth/signin/page.tsx +62 -0
- package/templates/store-template/app/auth/signup/page.tsx +38 -0
- package/templates/store-template/app/auth/verify-email/page.tsx +185 -0
- package/templates/store-template/app/cart/page.tsx +137 -0
- package/templates/store-template/app/checkout/page.tsx +88 -0
- package/templates/store-template/app/checkout/success/page.tsx +50 -0
- package/templates/store-template/app/contact/layout.tsx +24 -0
- package/templates/store-template/app/contact/page.tsx +321 -0
- package/templates/store-template/app/faq/page.tsx +153 -0
- package/templates/store-template/app/globals.css +99 -0
- package/templates/store-template/app/layout.tsx +269 -0
- package/templates/store-template/app/not-found.tsx +35 -0
- package/templates/store-template/app/page.tsx +23 -0
- package/templates/store-template/app/privacy/page.tsx +281 -0
- package/templates/store-template/app/products/[slug]/ProductDetailClient.tsx +179 -0
- package/templates/store-template/app/products/[slug]/page.tsx +149 -0
- package/templates/store-template/app/products/layout.tsx +26 -0
- package/templates/store-template/app/products/page.tsx +102 -0
- package/templates/store-template/app/robots.ts +25 -0
- package/templates/store-template/app/sitemap.ts +76 -0
- package/templates/store-template/app/terms/page.tsx +313 -0
- package/templates/store-template/components/PathSyncBridge.tsx +61 -0
- package/templates/store-template/components/Providers.tsx +37 -0
- package/templates/store-template/components/layout/Footer.tsx +150 -0
- package/templates/store-template/components/layout/Header.tsx +162 -0
- package/templates/store-template/components/sections/.gitkeep +7 -0
- package/templates/store-template/components/sections/CTA.tsx +51 -0
- package/templates/store-template/components/sections/Features.tsx +91 -0
- package/templates/store-template/components/sections/Hero.tsx +85 -0
- package/templates/store-template/eslint.config.mjs +18 -0
- package/templates/store-template/lib/store-config.ts +77 -0
- package/templates/store-template/lib/store-policy.ts +68 -0
- package/templates/store-template/lib/utils.ts +21 -0
- package/templates/store-template/next-env.d.ts +6 -0
- package/templates/store-template/next.config.js +105 -0
- package/templates/store-template/package.json +32 -0
- package/templates/store-template/postcss.config.js +6 -0
- package/templates/store-template/proxy.ts +150 -0
- package/templates/store-template/public/rovela-logo.png +0 -0
- package/templates/store-template/tailwind.config.ts +74 -0
- package/templates/store-template/tsconfig.json +41 -0
- package/templates/store-template/types/next-auth.d.ts +32 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Products Listing Page
|
|
5
|
+
*
|
|
6
|
+
* Uses SDK components for product display:
|
|
7
|
+
* - ProductGrid: Responsive grid of product cards
|
|
8
|
+
* - ProductSearch: Search input with debouncing
|
|
9
|
+
* - ProductSort: Sort dropdown
|
|
10
|
+
* - useProducts: Data fetching hook
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useState } from 'react'
|
|
14
|
+
import {
|
|
15
|
+
ProductGrid,
|
|
16
|
+
ProductSearch,
|
|
17
|
+
ProductSort,
|
|
18
|
+
useProducts,
|
|
19
|
+
} from '@rovela-ai/sdk/products'
|
|
20
|
+
import type { SortOption } from '@rovela-ai/sdk/products'
|
|
21
|
+
import { useT } from '@rovela-ai/sdk/core'
|
|
22
|
+
|
|
23
|
+
export default function ProductsPage() {
|
|
24
|
+
const t = useT()
|
|
25
|
+
const [search, setSearch] = useState('')
|
|
26
|
+
const [sort, setSort] = useState<SortOption>('newest')
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
products,
|
|
30
|
+
isLoading,
|
|
31
|
+
error,
|
|
32
|
+
hasMore,
|
|
33
|
+
loadMore,
|
|
34
|
+
} = useProducts({
|
|
35
|
+
search,
|
|
36
|
+
sort,
|
|
37
|
+
limit: 12,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
42
|
+
{/* Page Header */}
|
|
43
|
+
<div className="mb-8">
|
|
44
|
+
<h1 className="text-3xl font-bold font-heading">{t('listing.title')}</h1>
|
|
45
|
+
<p className="mt-2 text-muted-foreground">
|
|
46
|
+
{t('listing.subtitle')}
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{/* Filters Bar */}
|
|
51
|
+
<div className="flex flex-col sm:flex-row gap-4 mb-8">
|
|
52
|
+
<div className="flex-1 max-w-md">
|
|
53
|
+
<ProductSearch
|
|
54
|
+
value={search}
|
|
55
|
+
onSearch={setSearch}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
<ProductSort
|
|
59
|
+
value={sort}
|
|
60
|
+
onChange={setSort}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{/* One message per state: on error, ONLY the error renders (the grid
|
|
65
|
+
is skipped — its built-in empty state would double the message);
|
|
66
|
+
when empty, the page's copy rides the grid's emptyState slot. */}
|
|
67
|
+
{error ? (
|
|
68
|
+
<div className="text-center py-12">
|
|
69
|
+
<p className="text-destructive">{t('listing.loadFailed')}</p>
|
|
70
|
+
</div>
|
|
71
|
+
) : (
|
|
72
|
+
<ProductGrid
|
|
73
|
+
products={products}
|
|
74
|
+
isLoading={isLoading}
|
|
75
|
+
columns={{ sm: 2, md: 3, lg: 4 }}
|
|
76
|
+
cardProps={{
|
|
77
|
+
showQuickAdd: true,
|
|
78
|
+
}}
|
|
79
|
+
emptyState={
|
|
80
|
+
<div className="text-center py-12">
|
|
81
|
+
<p className="text-muted-foreground">
|
|
82
|
+
{search ? t('listing.noneMatching') : t('listing.noneAvailable')}
|
|
83
|
+
</p>
|
|
84
|
+
</div>
|
|
85
|
+
}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
|
|
89
|
+
{/* Load More */}
|
|
90
|
+
{hasMore && !isLoading && !error && (
|
|
91
|
+
<div className="mt-12 text-center">
|
|
92
|
+
<button
|
|
93
|
+
onClick={loadMore}
|
|
94
|
+
className="rounded-md bg-primary px-8 py-3 text-sm font-semibold text-primary-foreground shadow-sm hover:bg-primary/90 transition-colors"
|
|
95
|
+
>
|
|
96
|
+
{t('listing.loadMore')}
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MetadataRoute } from 'next'
|
|
2
|
+
|
|
3
|
+
const STORE_URL = process.env.NEXT_PUBLIC_APP_URL || ''
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Storefront robots rules.
|
|
7
|
+
*
|
|
8
|
+
* Merchant stores default to MAXIMUM discoverability for every search + AI
|
|
9
|
+
* shopping crawler (Google, Bing, ChatGPT, Perplexity, Google AI Mode, etc.) —
|
|
10
|
+
* AI shopping visibility is the goal. Only private / transactional routes are
|
|
11
|
+
* disallowed.
|
|
12
|
+
*/
|
|
13
|
+
export default function robots(): MetadataRoute.Robots {
|
|
14
|
+
const base = STORE_URL.replace(/\/$/, '')
|
|
15
|
+
return {
|
|
16
|
+
rules: [
|
|
17
|
+
{
|
|
18
|
+
userAgent: '*',
|
|
19
|
+
allow: '/',
|
|
20
|
+
disallow: ['/admin', '/api/', '/account', '/cart', '/checkout'],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
...(base && { sitemap: `${base}/sitemap.xml` }),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { MetadataRoute } from 'next'
|
|
2
|
+
import { findProducts, findSettings } from '@rovela-ai/sdk/core/server'
|
|
3
|
+
import { resolveStoreLocaleConfig } from '@rovela-ai/sdk/core'
|
|
4
|
+
|
|
5
|
+
// ISR: the sitemap reads live data (products + locale config) but Next
|
|
6
|
+
// statically caches metadata routes at build — hourly revalidation keeps
|
|
7
|
+
// new products and language toggles from being frozen until the next deploy.
|
|
8
|
+
export const revalidate = 3600
|
|
9
|
+
|
|
10
|
+
const STORE_URL = process.env.NEXT_PUBLIC_APP_URL || ''
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Storefront sitemap: static public pages + every active product detail page.
|
|
14
|
+
* Gives search + AI shopping crawlers a discovery path to the catalog.
|
|
15
|
+
* Transactional / private routes (cart, checkout, account, admin) are omitted
|
|
16
|
+
* by design — they mirror the robots.ts disallow list.
|
|
17
|
+
*/
|
|
18
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
19
|
+
const base = STORE_URL.replace(/\/$/, '')
|
|
20
|
+
if (!base) return []
|
|
21
|
+
|
|
22
|
+
let products: Awaited<ReturnType<typeof findProducts>> = []
|
|
23
|
+
try {
|
|
24
|
+
products = await findProducts({ status: 'active', limit: 5000 })
|
|
25
|
+
} catch {
|
|
26
|
+
products = []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// hreflang via sitemap (i18n core): when the store serves more than one
|
|
30
|
+
// locale, every URL declares its alternates so crawlers index /fr pages as
|
|
31
|
+
// translations, not duplicates. Sitemap-level hreflang is fully supported
|
|
32
|
+
// by Google and avoids fragile per-page head injection.
|
|
33
|
+
let localeAlternates: ((path: string) => MetadataRoute.Sitemap[number]['alternates']) | null = null
|
|
34
|
+
try {
|
|
35
|
+
const settings = await findSettings()
|
|
36
|
+
const { defaultLocale, enabledLocales } = resolveStoreLocaleConfig(settings)
|
|
37
|
+
if (enabledLocales.length > 1) {
|
|
38
|
+
localeAlternates = (path: string) => ({
|
|
39
|
+
languages: Object.fromEntries(
|
|
40
|
+
enabledLocales.map((l) => [
|
|
41
|
+
l,
|
|
42
|
+
l === defaultLocale ? `${base}${path}` : `${base}/${l}${path === '/' ? '' : path}`,
|
|
43
|
+
])
|
|
44
|
+
),
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// settings unreadable → single-locale sitemap (safe default)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const staticDefs: Array<{ path: string; changeFrequency: 'weekly' | 'daily' | 'monthly' | 'yearly'; priority: number }> = [
|
|
52
|
+
{ path: '/', changeFrequency: 'weekly', priority: 1.0 },
|
|
53
|
+
{ path: '/products', changeFrequency: 'daily', priority: 0.9 },
|
|
54
|
+
{ path: '/faq', changeFrequency: 'monthly', priority: 0.5 },
|
|
55
|
+
{ path: '/contact', changeFrequency: 'monthly', priority: 0.4 },
|
|
56
|
+
{ path: '/privacy', changeFrequency: 'yearly', priority: 0.2 },
|
|
57
|
+
{ path: '/terms', changeFrequency: 'yearly', priority: 0.2 },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const staticPages: MetadataRoute.Sitemap = staticDefs.map((d) => ({
|
|
61
|
+
url: `${base}${d.path === '/' ? '/' : d.path}`,
|
|
62
|
+
changeFrequency: d.changeFrequency,
|
|
63
|
+
priority: d.priority,
|
|
64
|
+
...(localeAlternates && { alternates: localeAlternates(d.path) }),
|
|
65
|
+
}))
|
|
66
|
+
|
|
67
|
+
const productPages: MetadataRoute.Sitemap = products.map((p) => ({
|
|
68
|
+
url: `${base}/products/${p.slug}`,
|
|
69
|
+
lastModified: p.updatedAt ?? undefined,
|
|
70
|
+
changeFrequency: 'weekly',
|
|
71
|
+
priority: 0.8,
|
|
72
|
+
...(localeAlternates && { alternates: localeAlternates(`/products/${p.slug}`) }),
|
|
73
|
+
}))
|
|
74
|
+
|
|
75
|
+
return [...staticPages, ...productPages]
|
|
76
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import { LocaleLink as Link } from '@rovela-ai/sdk/core'
|
|
3
|
+
import { getBlueprint, localizedAlternates, findSettings } from '@rovela-ai/sdk/core/server'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Terms of Service Page
|
|
7
|
+
*
|
|
8
|
+
* Static terms of service page with standard e-commerce sections.
|
|
9
|
+
* Store name is pulled from blueprint for personalization.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Metadata
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
17
|
+
let storeName = 'Our Store'
|
|
18
|
+
try {
|
|
19
|
+
const blueprint = getBlueprint()
|
|
20
|
+
storeName = blueprint?.store?.name || process.env.STORE_NAME || storeName
|
|
21
|
+
} catch {
|
|
22
|
+
// Blueprint might not exist
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
title: 'Terms of Service',
|
|
27
|
+
description: `Terms of Service for ${storeName}. Read our terms and conditions for using our website and services.`,
|
|
28
|
+
alternates: await localizedAlternates('/terms'),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Page Component
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
export default async function TermsPage() {
|
|
37
|
+
// Get store name for display — LIVE settings first (store_settings is what
|
|
38
|
+
// the merchant edits in /admin; blueprint + env are build-time fallbacks
|
|
39
|
+
// that go stale after a rename). Fail-open on DB errors.
|
|
40
|
+
let storeName = 'Our Store'
|
|
41
|
+
let supportEmail = ''
|
|
42
|
+
const liveSettings = await findSettings().catch(() => null)
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const blueprint = getBlueprint()
|
|
46
|
+
storeName =
|
|
47
|
+
liveSettings?.storeName || blueprint?.store?.name || process.env.STORE_NAME || storeName
|
|
48
|
+
} catch {
|
|
49
|
+
storeName = liveSettings?.storeName || process.env.STORE_NAME || storeName
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Public contact email = the merchant's own address only. NEVER the platform
|
|
53
|
+
// transactional sender (RESEND_FROM_EMAIL): surfacing that put
|
|
54
|
+
// "contact@rovela.ai" on storefront legal pages and read as a template
|
|
55
|
+
// artifact. Falls back to support@<your-domain> derived from the live host;
|
|
56
|
+
// if only a *.rovela.app / *.vercel.app / localhost host is available it stays
|
|
57
|
+
// empty and the email line is hidden (the contact-page link still renders).
|
|
58
|
+
const storeHost = (process.env.NEXT_PUBLIC_APP_URL || '')
|
|
59
|
+
.replace(/^https?:\/\//, '')
|
|
60
|
+
.replace(/\/.*$/, '')
|
|
61
|
+
.trim()
|
|
62
|
+
const hasOwnDomain =
|
|
63
|
+
!!storeHost &&
|
|
64
|
+
!storeHost.endsWith('rovela.app') &&
|
|
65
|
+
!storeHost.endsWith('vercel.app') &&
|
|
66
|
+
!storeHost.startsWith('localhost')
|
|
67
|
+
// Live store email first — this is the address the merchant actually set
|
|
68
|
+
// (emails, contact page and JSON-LD already read it); SUPPORT_EMAIL env is
|
|
69
|
+
// the build-time fallback, support@<domain> the last resort.
|
|
70
|
+
supportEmail =
|
|
71
|
+
liveSettings?.storeEmail?.trim() ||
|
|
72
|
+
process.env.SUPPORT_EMAIL?.trim() ||
|
|
73
|
+
(hasOwnDomain ? `support@${storeHost}` : '')
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
77
|
+
<div className="max-w-3xl mx-auto">
|
|
78
|
+
{/* Header */}
|
|
79
|
+
<div className="mb-12">
|
|
80
|
+
<h1 className="text-3xl font-bold font-heading">Terms of Service</h1>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{/* Content */}
|
|
84
|
+
<div className="prose prose-neutral dark:prose-invert max-w-none">
|
|
85
|
+
{/* Introduction */}
|
|
86
|
+
<section className="mb-10">
|
|
87
|
+
<p className="text-muted-foreground leading-relaxed">
|
|
88
|
+
Welcome to {storeName}. By accessing or using our website, you agree to be bound
|
|
89
|
+
by these Terms of Service. Please read them carefully before making any purchases
|
|
90
|
+
or using our services.
|
|
91
|
+
</p>
|
|
92
|
+
</section>
|
|
93
|
+
|
|
94
|
+
{/* Acceptance of Terms */}
|
|
95
|
+
<section className="mb-10">
|
|
96
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
97
|
+
1. Acceptance of Terms
|
|
98
|
+
</h2>
|
|
99
|
+
<p className="text-muted-foreground">
|
|
100
|
+
By accessing and using this website, you accept and agree to be bound by these
|
|
101
|
+
Terms of Service and our Privacy Policy. If you do not agree to these terms,
|
|
102
|
+
please do not use our website or services.
|
|
103
|
+
</p>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
{/* Use of Website */}
|
|
107
|
+
<section className="mb-10">
|
|
108
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
109
|
+
2. Use of Website
|
|
110
|
+
</h2>
|
|
111
|
+
<p className="text-muted-foreground mb-4">
|
|
112
|
+
You agree to use this website only for lawful purposes and in accordance with
|
|
113
|
+
these Terms. You agree not to:
|
|
114
|
+
</p>
|
|
115
|
+
<ul className="list-disc pl-6 space-y-2 text-muted-foreground">
|
|
116
|
+
<li>Use the website in any way that violates applicable laws or regulations</li>
|
|
117
|
+
<li>Attempt to interfere with the proper functioning of the website</li>
|
|
118
|
+
<li>Use automated systems or software to extract data from the website</li>
|
|
119
|
+
<li>Impersonate any person or entity or misrepresent your affiliation</li>
|
|
120
|
+
<li>Transmit any viruses, malware, or other harmful code</li>
|
|
121
|
+
</ul>
|
|
122
|
+
</section>
|
|
123
|
+
|
|
124
|
+
{/* Account Registration */}
|
|
125
|
+
<section className="mb-10">
|
|
126
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
127
|
+
3. Account Registration
|
|
128
|
+
</h2>
|
|
129
|
+
<p className="text-muted-foreground">
|
|
130
|
+
If you create an account with us, you are responsible for maintaining the
|
|
131
|
+
confidentiality of your account credentials and for all activities that occur
|
|
132
|
+
under your account. You must provide accurate and complete information when
|
|
133
|
+
creating an account. You agree to notify us immediately of any unauthorized
|
|
134
|
+
use of your account.
|
|
135
|
+
</p>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
{/* Products and Pricing */}
|
|
139
|
+
<section className="mb-10">
|
|
140
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
141
|
+
4. Products and Pricing
|
|
142
|
+
</h2>
|
|
143
|
+
<p className="text-muted-foreground mb-4">
|
|
144
|
+
We strive to provide accurate product descriptions and pricing. However:
|
|
145
|
+
</p>
|
|
146
|
+
<ul className="list-disc pl-6 space-y-2 text-muted-foreground">
|
|
147
|
+
<li>Product images are for illustration purposes and may vary slightly from actual products</li>
|
|
148
|
+
<li>We reserve the right to correct pricing errors</li>
|
|
149
|
+
<li>Prices are subject to change without notice</li>
|
|
150
|
+
<li>Availability of products is not guaranteed and may change</li>
|
|
151
|
+
</ul>
|
|
152
|
+
</section>
|
|
153
|
+
|
|
154
|
+
{/* Orders and Payment */}
|
|
155
|
+
<section className="mb-10">
|
|
156
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
157
|
+
5. Orders and Payment
|
|
158
|
+
</h2>
|
|
159
|
+
<p className="text-muted-foreground mb-4">
|
|
160
|
+
When you place an order:
|
|
161
|
+
</p>
|
|
162
|
+
<ul className="list-disc pl-6 space-y-2 text-muted-foreground">
|
|
163
|
+
<li>You are making an offer to purchase products at the listed price</li>
|
|
164
|
+
<li>We reserve the right to accept or decline any order</li>
|
|
165
|
+
<li>Payment must be made at the time of purchase</li>
|
|
166
|
+
<li>All payments are processed securely through our payment provider</li>
|
|
167
|
+
<li>You are responsible for any applicable taxes and shipping fees</li>
|
|
168
|
+
</ul>
|
|
169
|
+
</section>
|
|
170
|
+
|
|
171
|
+
{/* Shipping and Delivery */}
|
|
172
|
+
<section className="mb-10">
|
|
173
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
174
|
+
6. Shipping and Delivery
|
|
175
|
+
</h2>
|
|
176
|
+
<p className="text-muted-foreground">
|
|
177
|
+
Shipping times and costs are estimates and may vary based on your location and
|
|
178
|
+
the shipping method selected. We are not responsible for delays caused by
|
|
179
|
+
shipping carriers or customs processing. Risk of loss and title for products
|
|
180
|
+
pass to you upon delivery to the carrier.
|
|
181
|
+
</p>
|
|
182
|
+
</section>
|
|
183
|
+
|
|
184
|
+
{/* Returns and Refunds */}
|
|
185
|
+
<section className="mb-10">
|
|
186
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
187
|
+
7. Returns and Refunds
|
|
188
|
+
</h2>
|
|
189
|
+
<p className="text-muted-foreground">
|
|
190
|
+
We want you to be satisfied with your purchase. If you are not happy with your
|
|
191
|
+
order, please contact us within 30 days of delivery. Return eligibility depends
|
|
192
|
+
on the product condition and type. Refunds will be processed to the original
|
|
193
|
+
payment method within 5-10 business days after we receive the returned item.
|
|
194
|
+
</p>
|
|
195
|
+
</section>
|
|
196
|
+
|
|
197
|
+
{/* Intellectual Property */}
|
|
198
|
+
<section className="mb-10">
|
|
199
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
200
|
+
8. Intellectual Property
|
|
201
|
+
</h2>
|
|
202
|
+
<p className="text-muted-foreground">
|
|
203
|
+
All content on this website, including text, graphics, logos, images, and software,
|
|
204
|
+
is the property of {storeName} or its content suppliers and is protected by
|
|
205
|
+
intellectual property laws. You may not use, reproduce, or distribute any content
|
|
206
|
+
without our prior written permission.
|
|
207
|
+
</p>
|
|
208
|
+
</section>
|
|
209
|
+
|
|
210
|
+
{/* Limitation of Liability */}
|
|
211
|
+
<section className="mb-10">
|
|
212
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
213
|
+
9. Limitation of Liability
|
|
214
|
+
</h2>
|
|
215
|
+
<p className="text-muted-foreground">
|
|
216
|
+
To the fullest extent permitted by law, {storeName} shall not be liable for any
|
|
217
|
+
indirect, incidental, special, consequential, or punitive damages arising from
|
|
218
|
+
your use of the website or purchase of products. Our total liability shall not
|
|
219
|
+
exceed the amount you paid for the products in question.
|
|
220
|
+
</p>
|
|
221
|
+
</section>
|
|
222
|
+
|
|
223
|
+
{/* Disclaimer of Warranties */}
|
|
224
|
+
<section className="mb-10">
|
|
225
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
226
|
+
10. Disclaimer of Warranties
|
|
227
|
+
</h2>
|
|
228
|
+
<p className="text-muted-foreground">
|
|
229
|
+
This website and all products are provided "as is" without warranties of any kind,
|
|
230
|
+
either express or implied. We do not warrant that the website will be
|
|
231
|
+
uninterrupted, error-free, or free of viruses or other harmful components.
|
|
232
|
+
</p>
|
|
233
|
+
</section>
|
|
234
|
+
|
|
235
|
+
{/* Indemnification */}
|
|
236
|
+
<section className="mb-10">
|
|
237
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
238
|
+
11. Indemnification
|
|
239
|
+
</h2>
|
|
240
|
+
<p className="text-muted-foreground">
|
|
241
|
+
You agree to indemnify and hold harmless {storeName} and its affiliates, officers,
|
|
242
|
+
directors, employees, and agents from any claims, damages, losses, or expenses
|
|
243
|
+
arising from your use of the website or violation of these Terms.
|
|
244
|
+
</p>
|
|
245
|
+
</section>
|
|
246
|
+
|
|
247
|
+
{/* Governing Law */}
|
|
248
|
+
<section className="mb-10">
|
|
249
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
250
|
+
12. Governing Law
|
|
251
|
+
</h2>
|
|
252
|
+
<p className="text-muted-foreground">
|
|
253
|
+
These Terms of Service shall be governed by and construed in accordance with
|
|
254
|
+
applicable laws, without regard to conflict of law principles. Any disputes
|
|
255
|
+
arising from these terms shall be resolved through binding arbitration or in
|
|
256
|
+
the courts of competent jurisdiction.
|
|
257
|
+
</p>
|
|
258
|
+
</section>
|
|
259
|
+
|
|
260
|
+
{/* Changes to Terms */}
|
|
261
|
+
<section className="mb-10">
|
|
262
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
263
|
+
13. Changes to Terms
|
|
264
|
+
</h2>
|
|
265
|
+
<p className="text-muted-foreground">
|
|
266
|
+
We reserve the right to modify these Terms of Service at any time. Changes will
|
|
267
|
+
be effective immediately upon posting on this page. Your continued use of the
|
|
268
|
+
website after any changes constitutes acceptance of the new terms.
|
|
269
|
+
</p>
|
|
270
|
+
</section>
|
|
271
|
+
|
|
272
|
+
{/* Contact Information */}
|
|
273
|
+
<section className="mb-10">
|
|
274
|
+
<h2 className="text-xl font-semibold font-heading mb-4">
|
|
275
|
+
14. Contact Information
|
|
276
|
+
</h2>
|
|
277
|
+
<p className="text-muted-foreground">
|
|
278
|
+
If you have any questions about these Terms of Service, please
|
|
279
|
+
{supportEmail ? (
|
|
280
|
+
<>
|
|
281
|
+
{' '}contact us at{' '}
|
|
282
|
+
<a
|
|
283
|
+
href={`mailto:${supportEmail}`}
|
|
284
|
+
className="text-primary hover:underline"
|
|
285
|
+
>
|
|
286
|
+
{supportEmail}
|
|
287
|
+
</a>{' '}
|
|
288
|
+
or visit our{' '}
|
|
289
|
+
</>
|
|
290
|
+
) : (
|
|
291
|
+
<>{' '}reach us through our{' '}</>
|
|
292
|
+
)}
|
|
293
|
+
<Link href="/contact" className="text-primary hover:underline">
|
|
294
|
+
contact page
|
|
295
|
+
</Link>
|
|
296
|
+
.
|
|
297
|
+
</p>
|
|
298
|
+
</section>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
{/* Back to Home */}
|
|
302
|
+
<div className="mt-12 pt-8 border-t">
|
|
303
|
+
<Link
|
|
304
|
+
href="/"
|
|
305
|
+
className="text-sm text-primary hover:underline"
|
|
306
|
+
>
|
|
307
|
+
← Back to Home
|
|
308
|
+
</Link>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Path-sync bridge — posts the storefront's current pathname (+ search)
|
|
5
|
+
* to its parent every time it changes. Powers the Rovela editor's path
|
|
6
|
+
* bar so it stays in lock-step with whatever URL the iframe is showing,
|
|
7
|
+
* including:
|
|
8
|
+
*
|
|
9
|
+
* - Direct link clicks inside the storefront (header / footer / cards)
|
|
10
|
+
* - Browser back / forward (popstate fires usePathname re-renders)
|
|
11
|
+
* - Dynamic-route renders (/products/[slug] etc.)
|
|
12
|
+
* - Programmatic router.push from any component
|
|
13
|
+
*
|
|
14
|
+
* Cross-origin safe:
|
|
15
|
+
* - postMessage with '*' target — the payload is a public pathname,
|
|
16
|
+
* no sensitive data
|
|
17
|
+
* - Parent validates `event.origin` against its known iframe origin
|
|
18
|
+
* before reading (anti-spoofing defense in depth)
|
|
19
|
+
*
|
|
20
|
+
* Renders nothing — pure side-effect component. Mount it once in the
|
|
21
|
+
* customer + admin branches of LayoutContent so coverage matches the
|
|
22
|
+
* actual route tree.
|
|
23
|
+
*
|
|
24
|
+
* No-op outside iframe context: when `window.parent === window` the
|
|
25
|
+
* component returns immediately. Safe to ship in production builds
|
|
26
|
+
* served on a domain (no postMessage spam to the same window).
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { usePathname } from 'next/navigation'
|
|
30
|
+
import { useEffect } from 'react'
|
|
31
|
+
|
|
32
|
+
/*
|
|
33
|
+
* IMPORTANT — DO NOT add `useSearchParams()` to this component.
|
|
34
|
+
*
|
|
35
|
+
* `useSearchParams` opts every page that contains it out of static
|
|
36
|
+
* prerendering unless wrapped in a Suspense boundary. Because this
|
|
37
|
+
* component is mounted in LayoutContent (which wraps EVERY route),
|
|
38
|
+
* adding it would force a Suspense wrap at every mount site AND
|
|
39
|
+
* dynamic-render every prerenderable page (/auth/*, /privacy, /terms,
|
|
40
|
+
* marketing pages, etc.) — Vercel hard-fails the build for any page
|
|
41
|
+
* that lacks the Suspense wrap.
|
|
42
|
+
*
|
|
43
|
+
* `usePathname` carries no such restriction. Reading `window.location.search`
|
|
44
|
+
* synchronously inside the effect gives us the current query string
|
|
45
|
+
* without the reactivity (which we don't need — we only sync on
|
|
46
|
+
* pathname change, which is the dominant navigation event).
|
|
47
|
+
*
|
|
48
|
+
* Trade-off: pure ?query-only changes without pathname change won't
|
|
49
|
+
* sync. Acceptable v1.
|
|
50
|
+
*/
|
|
51
|
+
export function PathSyncBridge() {
|
|
52
|
+
const pathname = usePathname()
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (typeof window === 'undefined') return
|
|
55
|
+
if (window.parent === window) return
|
|
56
|
+
const search = window.location.search
|
|
57
|
+
const path = pathname + (search || '')
|
|
58
|
+
window.parent.postMessage({ type: 'rovela:navigation', path }, '*')
|
|
59
|
+
}, [pathname])
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* App Providers
|
|
5
|
+
*
|
|
6
|
+
* Client component wrapper for all context providers.
|
|
7
|
+
* Required because SessionProvider and StoreSettingsProvider are client components.
|
|
8
|
+
*
|
|
9
|
+
* Provider hierarchy:
|
|
10
|
+
* - SessionProvider: NextAuth authentication state
|
|
11
|
+
* - StoreSettingsProvider: Dynamic store settings from database
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { SessionProvider } from 'next-auth/react'
|
|
15
|
+
import { StoreSettingsProvider } from '@rovela-ai/sdk/core'
|
|
16
|
+
import { storeConfig } from '@/lib/store-config'
|
|
17
|
+
|
|
18
|
+
interface ProvidersProps {
|
|
19
|
+
children: React.ReactNode
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function Providers({ children }: ProvidersProps) {
|
|
23
|
+
return (
|
|
24
|
+
<SessionProvider>
|
|
25
|
+
<StoreSettingsProvider
|
|
26
|
+
fallback={{
|
|
27
|
+
storeName: storeConfig.store.name,
|
|
28
|
+
currency: storeConfig.store.currency,
|
|
29
|
+
locale: storeConfig.store.locale,
|
|
30
|
+
supportEmail: storeConfig.store.supportEmail,
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</StoreSettingsProvider>
|
|
35
|
+
</SessionProvider>
|
|
36
|
+
)
|
|
37
|
+
}
|