@fayz-ai/storefront 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/auth.d.ts +29 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/blocks.d.ts +6 -0
  6. package/dist/blocks.d.ts.map +1 -0
  7. package/dist/components/CartDrawer.d.ts +3 -0
  8. package/dist/components/CartDrawer.d.ts.map +1 -0
  9. package/dist/components/CustomerAccountShell.d.ts +13 -0
  10. package/dist/components/CustomerAccountShell.d.ts.map +1 -0
  11. package/dist/components/FiltersPanel.d.ts +3 -0
  12. package/dist/components/FiltersPanel.d.ts.map +1 -0
  13. package/dist/components/OrderTrackingTimeline.d.ts +7 -0
  14. package/dist/components/OrderTrackingTimeline.d.ts.map +1 -0
  15. package/dist/components/Price.d.ts +11 -0
  16. package/dist/components/Price.d.ts.map +1 -0
  17. package/dist/components/ProductCard.d.ts +7 -0
  18. package/dist/components/ProductCard.d.ts.map +1 -0
  19. package/dist/components/ProductGrid.d.ts +7 -0
  20. package/dist/components/ProductGrid.d.ts.map +1 -0
  21. package/dist/components/ProductOptionSelector.d.ts +9 -0
  22. package/dist/components/ProductOptionSelector.d.ts.map +1 -0
  23. package/dist/components/ProductSpecs.d.ts +8 -0
  24. package/dist/components/ProductSpecs.d.ts.map +1 -0
  25. package/dist/components/QuantityInput.d.ts +12 -0
  26. package/dist/components/QuantityInput.d.ts.map +1 -0
  27. package/dist/components/RelatedProducts.d.ts +9 -0
  28. package/dist/components/RelatedProducts.d.ts.map +1 -0
  29. package/dist/components/SignInModal.d.ts +19 -0
  30. package/dist/components/SignInModal.d.ts.map +1 -0
  31. package/dist/components/StorefrontErrorBoundary.d.ts +20 -0
  32. package/dist/components/StorefrontErrorBoundary.d.ts.map +1 -0
  33. package/dist/components/StorefrontFooter.d.ts +3 -0
  34. package/dist/components/StorefrontFooter.d.ts.map +1 -0
  35. package/dist/components/StorefrontHeader.d.ts +3 -0
  36. package/dist/components/StorefrontHeader.d.ts.map +1 -0
  37. package/dist/components/Toaster.d.ts +3 -0
  38. package/dist/components/Toaster.d.ts.map +1 -0
  39. package/dist/components/sections/CategoryShowcase.d.ts +6 -0
  40. package/dist/components/sections/CategoryShowcase.d.ts.map +1 -0
  41. package/dist/components/sections/HeroSection.d.ts +9 -0
  42. package/dist/components/sections/HeroSection.d.ts.map +1 -0
  43. package/dist/components/sections/MiscSections.d.ts +33 -0
  44. package/dist/components/sections/MiscSections.d.ts.map +1 -0
  45. package/dist/components/sections/ProductRail.d.ts +9 -0
  46. package/dist/components/sections/ProductRail.d.ts.map +1 -0
  47. package/dist/config.d.ts +149 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/createStorefrontApp.d.ts +16 -0
  50. package/dist/createStorefrontApp.d.ts.map +1 -0
  51. package/dist/format.d.ts +3 -0
  52. package/dist/format.d.ts.map +1 -0
  53. package/dist/hooks/index.d.ts +6 -0
  54. package/dist/hooks/index.d.ts.map +1 -0
  55. package/dist/hooks/useCategories.d.ts +6 -0
  56. package/dist/hooks/useCategories.d.ts.map +1 -0
  57. package/dist/hooks/useDiscountValidator.d.ts +14 -0
  58. package/dist/hooks/useDiscountValidator.d.ts.map +1 -0
  59. package/dist/hooks/useMyOrders.d.ts +7 -0
  60. package/dist/hooks/useMyOrders.d.ts.map +1 -0
  61. package/dist/hooks/useProduct.d.ts +7 -0
  62. package/dist/hooks/useProduct.d.ts.map +1 -0
  63. package/dist/hooks/useProducts.d.ts +8 -0
  64. package/dist/hooks/useProducts.d.ts.map +1 -0
  65. package/dist/hooks/useStorefrontHead.d.ts +11 -0
  66. package/dist/hooks/useStorefrontHead.d.ts.map +1 -0
  67. package/dist/index.cjs +4186 -0
  68. package/dist/index.cjs.map +1 -0
  69. package/dist/index.d.ts +55 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +4085 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/motion.d.ts +19 -0
  74. package/dist/motion.d.ts.map +1 -0
  75. package/dist/order-status.d.ts +10 -0
  76. package/dist/order-status.d.ts.map +1 -0
  77. package/dist/pages/CatalogPage.d.ts +3 -0
  78. package/dist/pages/CatalogPage.d.ts.map +1 -0
  79. package/dist/pages/CheckoutPage.d.ts +3 -0
  80. package/dist/pages/CheckoutPage.d.ts.map +1 -0
  81. package/dist/pages/HomePage.d.ts +6 -0
  82. package/dist/pages/HomePage.d.ts.map +1 -0
  83. package/dist/pages/MyPurchasesPage.d.ts +3 -0
  84. package/dist/pages/MyPurchasesPage.d.ts.map +1 -0
  85. package/dist/pages/NotFoundPage.d.ts +4 -0
  86. package/dist/pages/NotFoundPage.d.ts.map +1 -0
  87. package/dist/pages/OrderConfirmationPage.d.ts +5 -0
  88. package/dist/pages/OrderConfirmationPage.d.ts.map +1 -0
  89. package/dist/pages/PolicyPage.d.ts +15 -0
  90. package/dist/pages/PolicyPage.d.ts.map +1 -0
  91. package/dist/pages/ProductDetailPage.d.ts +5 -0
  92. package/dist/pages/ProductDetailPage.d.ts.map +1 -0
  93. package/dist/presets.d.ts +33 -0
  94. package/dist/presets.d.ts.map +1 -0
  95. package/dist/product-options.d.ts +12 -0
  96. package/dist/product-options.d.ts.map +1 -0
  97. package/dist/router.d.ts +285 -0
  98. package/dist/router.d.ts.map +1 -0
  99. package/dist/scaffold.d.ts +14 -0
  100. package/dist/scaffold.d.ts.map +1 -0
  101. package/dist/sections.d.ts +84 -0
  102. package/dist/sections.d.ts.map +1 -0
  103. package/dist/slot-contracts.d.ts +34 -0
  104. package/dist/slot-contracts.d.ts.map +1 -0
  105. package/dist/stores/cart.store.d.ts +58 -0
  106. package/dist/stores/cart.store.d.ts.map +1 -0
  107. package/dist/stores/catalog.store.d.ts +18 -0
  108. package/dist/stores/catalog.store.d.ts.map +1 -0
  109. package/dist/stores/session.store.d.ts +23 -0
  110. package/dist/stores/session.store.d.ts.map +1 -0
  111. package/dist/stores/toast.store.d.ts +27 -0
  112. package/dist/stores/toast.store.d.ts.map +1 -0
  113. package/dist/testids.d.ts +93 -0
  114. package/dist/testids.d.ts.map +1 -0
  115. package/dist/theme.d.ts +59 -0
  116. package/dist/theme.d.ts.map +1 -0
  117. package/dist/workflows/checkout.d.ts +33 -0
  118. package/dist/workflows/checkout.d.ts.map +1 -0
  119. package/package.json +56 -0
  120. package/src/auth.ts +134 -0
  121. package/src/blocks.tsx +47 -0
  122. package/src/components/CartDrawer.tsx +250 -0
  123. package/src/components/CustomerAccountShell.tsx +102 -0
  124. package/src/components/FiltersPanel.tsx +109 -0
  125. package/src/components/OrderTrackingTimeline.tsx +135 -0
  126. package/src/components/Price.tsx +32 -0
  127. package/src/components/ProductCard.tsx +104 -0
  128. package/src/components/ProductGrid.tsx +34 -0
  129. package/src/components/ProductOptionSelector.tsx +50 -0
  130. package/src/components/ProductSpecs.tsx +75 -0
  131. package/src/components/QuantityInput.tsx +52 -0
  132. package/src/components/RelatedProducts.tsx +31 -0
  133. package/src/components/SignInModal.tsx +120 -0
  134. package/src/components/StorefrontErrorBoundary.tsx +51 -0
  135. package/src/components/StorefrontFooter.tsx +100 -0
  136. package/src/components/StorefrontHeader.tsx +342 -0
  137. package/src/components/Toaster.tsx +60 -0
  138. package/src/components/sections/CategoryShowcase.tsx +79 -0
  139. package/src/components/sections/HeroSection.tsx +177 -0
  140. package/src/components/sections/MiscSections.tsx +157 -0
  141. package/src/components/sections/ProductRail.tsx +73 -0
  142. package/src/config.ts +176 -0
  143. package/src/createStorefrontApp.tsx +171 -0
  144. package/src/format.ts +7 -0
  145. package/src/hooks/index.ts +5 -0
  146. package/src/hooks/useCategories.ts +26 -0
  147. package/src/hooks/useDiscountValidator.ts +43 -0
  148. package/src/hooks/useMyOrders.ts +41 -0
  149. package/src/hooks/useProduct.ts +36 -0
  150. package/src/hooks/useProducts.ts +39 -0
  151. package/src/hooks/useStorefrontHead.ts +25 -0
  152. package/src/index.ts +132 -0
  153. package/src/motion.tsx +94 -0
  154. package/src/order-status.ts +33 -0
  155. package/src/pages/CatalogPage.tsx +118 -0
  156. package/src/pages/CheckoutPage.tsx +578 -0
  157. package/src/pages/HomePage.tsx +12 -0
  158. package/src/pages/MyPurchasesPage.tsx +310 -0
  159. package/src/pages/NotFoundPage.tsx +25 -0
  160. package/src/pages/OrderConfirmationPage.tsx +189 -0
  161. package/src/pages/PolicyPage.tsx +47 -0
  162. package/src/pages/ProductDetailPage.tsx +184 -0
  163. package/src/presets.ts +258 -0
  164. package/src/product-options.ts +63 -0
  165. package/src/router.ts +49 -0
  166. package/src/scaffold.tsx +109 -0
  167. package/src/sections.ts +69 -0
  168. package/src/slot-contracts.ts +20 -0
  169. package/src/stores/cart.store.ts +149 -0
  170. package/src/stores/catalog.store.ts +39 -0
  171. package/src/stores/session.store.ts +28 -0
  172. package/src/stores/toast.store.ts +43 -0
  173. package/src/testids.ts +103 -0
  174. package/src/theme.ts +160 -0
  175. package/src/workflows/checkout.ts +91 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,4186 @@
1
+ 'use strict';
2
+
3
+ var runtime = require('@fayz-ai/shop/runtime');
4
+ var mock = require('@fayz-ai/shop/mock');
5
+ var auth = require('@fayz-ai/auth');
6
+ var zustand = require('zustand');
7
+ var middleware = require('zustand/middleware');
8
+ var React18 = require('react');
9
+ var core = require('@fayz-ai/core');
10
+ var LucideIcons = require('lucide-react');
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ var shop = require('@fayz-ai/shop');
13
+
14
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
+
16
+ function _interopNamespace(e) {
17
+ if (e && e.__esModule) return e;
18
+ var n = Object.create(null);
19
+ if (e) {
20
+ Object.keys(e).forEach(function (k) {
21
+ if (k !== 'default') {
22
+ var d = Object.getOwnPropertyDescriptor(e, k);
23
+ Object.defineProperty(n, k, d.get ? d : {
24
+ enumerable: true,
25
+ get: function () { return e[k]; }
26
+ });
27
+ }
28
+ });
29
+ }
30
+ n.default = e;
31
+ return Object.freeze(n);
32
+ }
33
+
34
+ var React18__default = /*#__PURE__*/_interopDefault(React18);
35
+ var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
36
+
37
+ // src/createStorefrontApp.tsx
38
+ var useSessionStore = zustand.create()(
39
+ middleware.persist(
40
+ (set) => ({
41
+ customerId: null,
42
+ email: null,
43
+ name: null,
44
+ setSession: ({ customerId, email, name }) => set({ customerId, email, name: name ?? null }),
45
+ signOut: () => set({ customerId: null, email: null, name: null })
46
+ }),
47
+ { name: "fayz.storefront.session.v1" }
48
+ )
49
+ );
50
+
51
+ // src/auth.ts
52
+ var _adapter = null;
53
+ function resolveAuthAdapter(configured) {
54
+ if (configured && configured !== "mock" && configured !== "supabase") return configured;
55
+ if (configured === "supabase") {
56
+ console.warn(
57
+ '@fayz-ai/shop: auth.adapter="supabase" is legacy. Pass an explicit AuthAdapter or use the Fayz SDK broker path.'
58
+ );
59
+ }
60
+ return auth.createMockAuthAdapter();
61
+ }
62
+ function initCustomerAuth(adapter2) {
63
+ _adapter = adapter2;
64
+ adapter2.getSession().then((existing) => {
65
+ if (!existing) return;
66
+ const store = useSessionStore.getState();
67
+ if (store.email !== existing.user.email) {
68
+ void linkCustomer(existing.user);
69
+ }
70
+ }).catch(() => {
71
+ });
72
+ adapter2.onAuthStateChange((user) => {
73
+ if (!user) useSessionStore.getState().signOut();
74
+ });
75
+ }
76
+ function getCustomerAuthAdapter() {
77
+ if (!_adapter) _adapter = auth.createMockAuthAdapter();
78
+ return _adapter;
79
+ }
80
+ async function resolveShopCustomer(email, name) {
81
+ const provider = runtime.getShopProvider();
82
+ if (provider.resolveCustomer) return provider.resolveCustomer({ email, name });
83
+ const matches = await provider.listCustomers({ search: email, limit: 10 });
84
+ const existing = matches.find((c) => (c.email ?? "").toLowerCase() === email);
85
+ if (existing) return existing;
86
+ const parts = name.trim().split(/\s+/).filter(Boolean);
87
+ return provider.createCustomer({
88
+ firstName: parts[0] ?? email.split("@")[0] ?? "Cliente",
89
+ lastName: parts.slice(1).join(" "),
90
+ email
91
+ });
92
+ }
93
+ async function linkCustomer(user, fullName) {
94
+ const email = (user.email ?? "").trim().toLowerCase();
95
+ const name = fullName ?? user.fullName ?? "";
96
+ const customer = await resolveShopCustomer(email, name);
97
+ useSessionStore.getState().setSession({
98
+ customerId: customer.id,
99
+ email,
100
+ name: name || `${customer.firstName} ${customer.lastName}`.trim()
101
+ });
102
+ return { customerId: customer.id };
103
+ }
104
+ async function establishCustomerSession(email, opts) {
105
+ const normalized = email.trim().toLowerCase();
106
+ if (opts?.password) {
107
+ const adapter2 = getCustomerAuthAdapter();
108
+ const session = await adapter2.signIn(normalized, opts.password);
109
+ return linkCustomer(session.user.email ? session.user : { ...session.user, email: normalized }, opts.name);
110
+ }
111
+ return linkCustomer({ email: normalized }, opts?.name);
112
+ }
113
+ function signInByEmail(email, name) {
114
+ return establishCustomerSession(email, { name });
115
+ }
116
+ async function signUpCustomer(email, password, name) {
117
+ const adapter2 = getCustomerAuthAdapter();
118
+ const normalized = email.trim().toLowerCase();
119
+ const session = await adapter2.signUp(normalized, password, name);
120
+ return linkCustomer(session.user.email ? session.user : { ...session.user, email: normalized }, name);
121
+ }
122
+ async function signOutCustomer() {
123
+ try {
124
+ await getCustomerAuthAdapter().signOut();
125
+ } finally {
126
+ useSessionStore.getState().signOut();
127
+ }
128
+ }
129
+ function resolveConfig(config) {
130
+ const catalogPath = config.home ? "/catalog" : "/";
131
+ return {
132
+ ...config,
133
+ currency: config.currency ?? "BRL",
134
+ locale: config.locale ?? "pt-BR",
135
+ shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
136
+ payments: { mode: config.payments?.mode ?? "mock" },
137
+ features: {
138
+ // Auto-enable the coupon UI when the store actually ships discount codes
139
+ // (via config.discounts or a seeded catalog), unless explicitly overridden —
140
+ // otherwise a store can advertise a coupon with no field to enter it.
141
+ discounts: config.features?.discounts ?? Boolean(config.discounts?.length || config.catalog?.discounts?.length),
142
+ accounts: config.features?.accounts ?? true
143
+ },
144
+ catalogPath,
145
+ nav: config.nav ?? (config.home ? [
146
+ { label: "In\xEDcio", to: "/" },
147
+ { label: "Loja", to: "/catalog" }
148
+ ] : [{ label: "Loja", to: "/" }])
149
+ };
150
+ }
151
+ var StorefrontConfigContext = React18.createContext(null);
152
+ var StorefrontConfigProvider = StorefrontConfigContext.Provider;
153
+ function useStorefrontConfig() {
154
+ const ctx = React18.useContext(StorefrontConfigContext);
155
+ if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
156
+ return ctx;
157
+ }
158
+ var adapter = core.hashRouterAdapter();
159
+ function navigateTo(to) {
160
+ adapter.navigate(to);
161
+ }
162
+ function useHashPath() {
163
+ const [path, setPath] = React18.useState(() => adapter.getCurrentPath() || "/");
164
+ React18.useEffect(() => {
165
+ const unsub = adapter.onPathChange((p) => {
166
+ setPath(p || "/");
167
+ window.scrollTo(0, 0);
168
+ });
169
+ return unsub;
170
+ }, []);
171
+ return path;
172
+ }
173
+ function matchPath(pattern, path) {
174
+ const cleanPath = (path.split("?")[0] ?? "").replace(/\/+$/, "") || "/";
175
+ const patternParts = pattern.split("/").filter(Boolean);
176
+ const pathParts = cleanPath.split("/").filter(Boolean);
177
+ if (patternParts.length !== pathParts.length) return null;
178
+ const params = {};
179
+ for (let i = 0; i < patternParts.length; i++) {
180
+ const pat = patternParts[i];
181
+ const part = pathParts[i];
182
+ if (pat.startsWith(":")) params[pat.slice(1)] = decodeURIComponent(part);
183
+ else if (pat !== part) return null;
184
+ }
185
+ return params;
186
+ }
187
+ function Link({ to, children, ...rest }) {
188
+ return React18__default.default.createElement("a", { href: `#${to}`, ...rest }, children);
189
+ }
190
+ var RADIUS_MAP = {
191
+ none: { button: "0px", card: "0px", input: "0px" },
192
+ soft: { button: "6px", card: "10px", input: "6px" },
193
+ round: { button: "10px", card: "16px", input: "10px" },
194
+ pill: { button: "999px", card: "20px", input: "999px" }
195
+ };
196
+ var COLOR_VAR_MAP = {
197
+ background: "--background",
198
+ foreground: "--foreground",
199
+ primary: "--primary",
200
+ primaryForeground: "--primary-foreground",
201
+ card: "--card",
202
+ cardForeground: "--card-foreground",
203
+ muted: "--muted",
204
+ mutedForeground: "--muted-foreground",
205
+ border: "--border",
206
+ headerBackground: "--sf-header-bg",
207
+ headerForeground: "--sf-header-fg",
208
+ announcementBackground: "--sf-announcement-bg",
209
+ announcementForeground: "--sf-announcement-fg"
210
+ };
211
+ function themeToCss(theme) {
212
+ const lines = [];
213
+ for (const [key, cssVar] of Object.entries(COLOR_VAR_MAP)) {
214
+ const value = theme.colors?.[key];
215
+ if (value) lines.push(`${cssVar}: ${value};`);
216
+ }
217
+ if (!theme.colors?.headerBackground && theme.colors?.background) {
218
+ lines.push(`--sf-header-bg: ${theme.colors.background};`);
219
+ }
220
+ if (!theme.colors?.headerForeground && theme.colors?.foreground) {
221
+ lines.push(`--sf-header-fg: ${theme.colors.foreground};`);
222
+ }
223
+ if (theme.font) {
224
+ const fallback = theme.font.fallback ?? "sans-serif";
225
+ lines.push(`--font-family: '${theme.font.body}', ${fallback};`);
226
+ lines.push(`--sf-font-heading: '${theme.font.heading}', ${fallback};`);
227
+ }
228
+ const radius = RADIUS_MAP[theme.radius ?? "soft"];
229
+ lines.push(`--button-radius: ${radius.button};`);
230
+ lines.push(`--card-radius: ${radius.card};`);
231
+ lines.push(`--input-radius: ${radius.input};`);
232
+ lines.push(`--sf-radius-button: ${radius.button};`);
233
+ lines.push(`--sf-radius-card: ${radius.card};`);
234
+ lines.push(`--sf-radius-input: ${radius.input};`);
235
+ let css = `:root {
236
+ ${lines.join("\n ")}
237
+ }
238
+ `;
239
+ css += `body { font-family: var(--font-family); }
240
+ `;
241
+ css += `h1, h2, h3, h4, .sf-heading { font-family: var(--sf-font-heading, var(--font-family)); }
242
+ `;
243
+ if (theme.uppercaseButtons) {
244
+ css += `.sf-cta { text-transform: uppercase; letter-spacing: 0.08em; }
245
+ `;
246
+ }
247
+ if (theme.header?.uppercaseNav) {
248
+ css += `.sf-nav-link { text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.8rem; }
249
+ `;
250
+ }
251
+ return css;
252
+ }
253
+ function googleFontsHref(theme) {
254
+ if (!theme.font) return null;
255
+ const families = theme.font.googleFonts ?? [theme.font.heading, theme.font.body];
256
+ const unique = [...new Set(families)];
257
+ const params = unique.map((f) => `family=${encodeURIComponent(f).replace(/%20/g, "+")}:wght@300;400;500;600;700`).join("&");
258
+ return `https://fonts.googleapis.com/css2?${params}&display=swap`;
259
+ }
260
+ function StorefrontThemeStyle({ theme }) {
261
+ const fontsHref = googleFontsHref(theme);
262
+ React18.useEffect(() => {
263
+ if (!fontsHref) return;
264
+ const id = "sf-theme-fonts";
265
+ let link = document.getElementById(id);
266
+ if (!link) {
267
+ link = document.createElement("link");
268
+ link.id = id;
269
+ link.rel = "stylesheet";
270
+ document.head.appendChild(link);
271
+ }
272
+ link.href = fontsHref;
273
+ }, [fontsHref]);
274
+ return React18__default.default.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
275
+ }
276
+
277
+ // src/format.ts
278
+ function formatMoney(value, currency = "BRL", locale = "pt-BR") {
279
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(value);
280
+ }
281
+ function roundCents(value) {
282
+ return Math.round(value * 100) / 100;
283
+ }
284
+
285
+ // src/product-options.ts
286
+ function cleanValues(value) {
287
+ if (!Array.isArray(value)) return [];
288
+ return Array.from(new Set(value.filter((item) => typeof item === "string" && item.trim().length > 0)));
289
+ }
290
+ function cleanOptionGroup(value) {
291
+ if (!value || typeof value !== "object") return null;
292
+ const record = value;
293
+ const id = typeof record.id === "string" ? record.id : typeof record.name === "string" ? record.name : null;
294
+ const label = typeof record.label === "string" ? record.label : typeof record.name === "string" ? record.name : id;
295
+ const values = cleanValues(record.values);
296
+ if (!id || !label || values.length === 0) return null;
297
+ return { id, label, values };
298
+ }
299
+ function getProductOptionGroups(product) {
300
+ const options = product.metadata.options;
301
+ const explicit = Array.isArray(options) ? options.map(cleanOptionGroup).filter((group) => Boolean(group)) : [];
302
+ if (explicit.length > 0) return explicit;
303
+ const groups = [];
304
+ const sizes = cleanValues(product.metadata.sizes);
305
+ const colors = cleanValues(product.metadata.colors);
306
+ if (sizes.length > 0) groups.push({ id: "Tamanho", label: "Tamanho", values: sizes });
307
+ if (colors.length > 0) groups.push({ id: "Cor", label: "Cor", values: colors });
308
+ return groups;
309
+ }
310
+ function normalizeProductOptionSelection(selection) {
311
+ if (!selection) return {};
312
+ return Object.fromEntries(
313
+ Object.entries(selection).filter((entry) => entry[0].trim().length > 0 && entry[1].trim().length > 0).sort(([a], [b]) => a.localeCompare(b))
314
+ );
315
+ }
316
+ function formatProductOptionSelection(selection) {
317
+ return Object.entries(normalizeProductOptionSelection(selection)).map(([label, value]) => `${label}: ${value}`).join(" \xB7 ");
318
+ }
319
+ function productOptionSelectionKey(selection) {
320
+ const normalized = normalizeProductOptionSelection(selection);
321
+ const entries = Object.entries(normalized);
322
+ return entries.length > 0 ? JSON.stringify(entries) : "";
323
+ }
324
+
325
+ // src/stores/cart.store.ts
326
+ var useCartStore = zustand.create()(
327
+ middleware.persist(
328
+ (set) => ({
329
+ lines: [],
330
+ discountCode: null,
331
+ discountPercent: 0,
332
+ isOpen: false,
333
+ justAddedLineId: null,
334
+ addItem: (product, qty = 1, options) => set((state) => {
335
+ const normalizedOptions = normalizeProductOptionSelection(options);
336
+ const optionKey = productOptionSelectionKey(normalizedOptions);
337
+ const lineId = optionKey ? `${product.id}:${optionKey}` : product.id;
338
+ const existing = state.lines.find((l) => (l.lineId ?? l.productId) === lineId);
339
+ if (existing) {
340
+ const nextQty = Math.min(existing.quantity + qty, existing.maxQuantity || Infinity);
341
+ return {
342
+ // Auto-open the cart on add — the standard premium "slide-in cart"
343
+ // signal — and flag the line so the drawer can highlight it.
344
+ isOpen: true,
345
+ justAddedLineId: lineId,
346
+ lines: state.lines.map(
347
+ (l) => (l.lineId ?? l.productId) === lineId ? { ...l, lineId, quantity: nextQty } : l
348
+ )
349
+ };
350
+ }
351
+ const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
352
+ const line = {
353
+ lineId,
354
+ productId: product.id,
355
+ slug: product.slug,
356
+ name: product.name,
357
+ sku: product.sku,
358
+ options: normalizedOptions,
359
+ optionsLabel: formatProductOptionSelection(normalizedOptions),
360
+ unitPrice: product.price,
361
+ quantity: Math.min(qty, product.inventoryCount || Infinity),
362
+ imageUrl: image?.url ?? null,
363
+ maxQuantity: product.inventoryCount
364
+ };
365
+ return { isOpen: true, justAddedLineId: lineId, lines: [...state.lines, line] };
366
+ }),
367
+ setQuantity: (lineId, qty) => set((state) => {
368
+ if (qty <= 0) return { lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) };
369
+ return {
370
+ lines: state.lines.map(
371
+ (l) => (l.lineId ?? l.productId) === lineId ? { ...l, quantity: Math.min(qty, l.maxQuantity || Infinity) } : l
372
+ )
373
+ };
374
+ }),
375
+ removeItem: (lineId) => set((state) => ({ lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) })),
376
+ applyDiscount: (code, percent) => set({ discountCode: code, discountPercent: percent }),
377
+ clearDiscount: () => set({ discountCode: null, discountPercent: 0 }),
378
+ clear: () => set({ lines: [], discountCode: null, discountPercent: 0 }),
379
+ openDrawer: () => set({ isOpen: true }),
380
+ closeDrawer: () => set({ isOpen: false, justAddedLineId: null })
381
+ }),
382
+ {
383
+ name: "fayz.storefront.cart.v1",
384
+ partialize: (state) => ({
385
+ lines: state.lines,
386
+ discountCode: state.discountCode,
387
+ discountPercent: state.discountPercent
388
+ })
389
+ }
390
+ )
391
+ );
392
+ var selectCount = (s) => s.lines.reduce((n, l) => n + l.quantity, 0);
393
+ var selectSubtotal = (s) => roundCents(s.lines.reduce((sum, l) => sum + l.unitPrice * l.quantity, 0));
394
+ var selectDiscountTotal = (s) => roundCents(selectSubtotal(s) * (s.discountPercent / 100));
395
+ var selectShipping = (s, cfg) => {
396
+ if (s.lines.length === 0) return 0;
397
+ const subtotal = selectSubtotal(s);
398
+ if (cfg.shipping.freeAbove != null && subtotal >= cfg.shipping.freeAbove) return 0;
399
+ return cfg.shipping.flatRate;
400
+ };
401
+ var selectTotal = (s, cfg) => roundCents(selectSubtotal(s) - selectDiscountTotal(s) + selectShipping(s, cfg));
402
+ var initial = {
403
+ search: "",
404
+ categoryId: null,
405
+ priceMin: null,
406
+ priceMax: null,
407
+ inStockOnly: false,
408
+ sort: "newest"
409
+ };
410
+ var useCatalogStore = zustand.create()((set) => ({
411
+ ...initial,
412
+ setSearch: (search) => set({ search }),
413
+ setCategoryId: (categoryId) => set({ categoryId }),
414
+ setPriceMin: (priceMin) => set({ priceMin }),
415
+ setPriceMax: (priceMax) => set({ priceMax }),
416
+ setInStockOnly: (inStockOnly) => set({ inStockOnly }),
417
+ setSort: (sort) => set({ sort }),
418
+ reset: () => set(initial)
419
+ }));
420
+ function useCategories() {
421
+ const [categories, setCategories] = React18.useState([]);
422
+ const [loading, setLoading] = React18.useState(true);
423
+ React18.useEffect(() => {
424
+ let cancelled = false;
425
+ runtime.getShopProvider().listCategories().then((data) => {
426
+ if (!cancelled) setCategories(data);
427
+ }).catch(() => {
428
+ }).finally(() => {
429
+ if (!cancelled) setLoading(false);
430
+ });
431
+ return () => {
432
+ cancelled = true;
433
+ };
434
+ }, []);
435
+ return { categories, loading };
436
+ }
437
+ function prefersReducedMotion() {
438
+ if (typeof window === "undefined" || !window.matchMedia) return false;
439
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
440
+ }
441
+ function useInView(margin = "0px 0px -8% 0px") {
442
+ const ref = React18.useRef(null);
443
+ const [inView, setInView] = React18.useState(() => prefersReducedMotion());
444
+ React18.useEffect(() => {
445
+ if (inView) return;
446
+ const el = ref.current;
447
+ if (!el || typeof IntersectionObserver === "undefined") {
448
+ setInView(true);
449
+ return;
450
+ }
451
+ const observer = new IntersectionObserver(
452
+ (entries) => {
453
+ if (entries.some((e) => e.isIntersecting)) {
454
+ setInView(true);
455
+ observer.disconnect();
456
+ }
457
+ },
458
+ { rootMargin: margin }
459
+ );
460
+ observer.observe(el);
461
+ return () => observer.disconnect();
462
+ }, [inView, margin]);
463
+ return [ref, inView];
464
+ }
465
+ function Reveal({ delay = 0, children, className, style, ...rest }) {
466
+ const [ref, inView] = useInView();
467
+ return /* @__PURE__ */ jsxRuntime.jsx(
468
+ "div",
469
+ {
470
+ ref,
471
+ className: `${inView ? "animate-fade-up" : ""} ${className ?? ""}`,
472
+ style: { ...style, animationDelay: delay ? `${delay}ms` : void 0 },
473
+ ...rest,
474
+ children
475
+ }
476
+ );
477
+ }
478
+ function useScrolled(threshold = 8) {
479
+ const [scrolled, setScrolled] = React18.useState(false);
480
+ React18.useEffect(() => {
481
+ const onScroll = () => setScrolled(window.scrollY > threshold);
482
+ onScroll();
483
+ window.addEventListener("scroll", onScroll, { passive: true });
484
+ return () => window.removeEventListener("scroll", onScroll);
485
+ }, [threshold]);
486
+ return scrolled;
487
+ }
488
+ function usePopOnChange(value) {
489
+ const [pop, setPop] = React18.useState(false);
490
+ const prev = React18.useRef(value);
491
+ React18.useEffect(() => {
492
+ if (prev.current !== value && value > 0) {
493
+ setPop(true);
494
+ const t = setTimeout(() => setPop(false), 400);
495
+ return () => clearTimeout(t);
496
+ }
497
+ prev.current = value;
498
+ return void 0;
499
+ }, [value]);
500
+ React18.useEffect(() => {
501
+ prev.current = value;
502
+ });
503
+ return pop;
504
+ }
505
+
506
+ // src/testids.ts
507
+ var TID = {
508
+ // header
509
+ headerSearch: "header-search",
510
+ cartButton: "cart-button",
511
+ cartCount: "cart-count",
512
+ accountLink: "account-link",
513
+ accountDropdown: "account-dropdown",
514
+ announcementBar: "announcement-bar",
515
+ navLink: (label) => `nav-${label.toLowerCase().replace(/\s+/g, "-")}`,
516
+ navCategory: (slug2) => `nav-cat-${slug2}`,
517
+ accountName: "account-name",
518
+ // home sections
519
+ hero: "home-hero",
520
+ heroPrev: "hero-prev",
521
+ heroNext: "hero-next",
522
+ categoryShowcase: "category-showcase",
523
+ productRail: "product-rail",
524
+ benefits: "benefits-row",
525
+ promoBanner: "promo-banner",
526
+ manifesto: "manifesto",
527
+ testimonials: "testimonials",
528
+ newsletter: "newsletter",
529
+ newsletterEmail: "newsletter-email",
530
+ newsletterSubmit: "newsletter-submit",
531
+ newsletterDone: "newsletter-done",
532
+ // catalog
533
+ productGrid: "product-grid",
534
+ productCard: "product-card",
535
+ productCardName: "product-card-name",
536
+ productCardPrice: "product-card-price",
537
+ productCardAdd: "product-card-add",
538
+ badgeSale: "badge-sale",
539
+ badgeSoldout: "badge-soldout",
540
+ // filters
541
+ filterCategory: (slug2) => `filter-category-${slug2}`,
542
+ filterPriceMin: "filter-price-min",
543
+ filterPriceMax: "filter-price-max",
544
+ filterInstock: "filter-instock",
545
+ sortSelect: "sort-select",
546
+ filtersClear: "filters-clear",
547
+ // product detail
548
+ pdpName: "pdp-name",
549
+ pdpPrice: "pdp-price",
550
+ pdpComparePrice: "pdp-compare-price",
551
+ pdpDescription: "pdp-description",
552
+ productOptionGroup: (id) => `product-option-${id.toLowerCase().replace(/\s+/g, "-")}`,
553
+ productOptionValue: (id, value) => `product-option-${id.toLowerCase().replace(/\s+/g, "-")}-${value.toLowerCase().replace(/\s+/g, "-")}`,
554
+ pdpQtyInput: "pdp-qty-input",
555
+ pdpAddToCart: "pdp-add-to-cart",
556
+ // cart
557
+ cartDrawer: "cart-drawer",
558
+ cartLine: "cart-line",
559
+ lineQty: "line-qty",
560
+ lineInc: "line-inc",
561
+ lineDec: "line-dec",
562
+ lineRemove: "line-remove",
563
+ cartSubtotal: "cart-subtotal",
564
+ discountInput: "discount-input",
565
+ discountApply: "discount-apply",
566
+ discountRow: "discount-row",
567
+ discountError: "discount-error",
568
+ cartShipping: "cart-shipping",
569
+ shippingProgress: "shipping-progress",
570
+ cartTotal: "cart-total",
571
+ goCheckout: "go-checkout",
572
+ cartEmpty: "cart-empty",
573
+ // checkout
574
+ checkoutEmail: "checkout-email",
575
+ checkoutName: "checkout-name",
576
+ checkoutStreet: "checkout-street",
577
+ checkoutCity: "checkout-city",
578
+ checkoutZip: "checkout-zip",
579
+ checkoutCard: "checkout-card",
580
+ checkoutExpiry: "checkout-expiry",
581
+ checkoutCvc: "checkout-cvc",
582
+ placeOrder: "place-order",
583
+ checkoutError: "checkout-error",
584
+ checkoutProcessing: "checkout-processing",
585
+ // confirmation
586
+ orderNumber: "order-number",
587
+ orderStatus: "order-status",
588
+ orderTracking: "order-tracking",
589
+ orderTrackingStep: (step) => `order-tracking-step-${step}`,
590
+ orderTotal: "order-total",
591
+ viewPurchases: "view-purchases",
592
+ // account
593
+ customerAccountShell: "customer-account-shell",
594
+ customerAccountMenu: "customer-account-menu",
595
+ orderDetail: "order-detail",
596
+ authTabSignin: "auth-tab-signin",
597
+ authTabSignup: "auth-tab-signup",
598
+ signinEmail: "signin-email",
599
+ signinPassword: "signin-password",
600
+ signinName: "signin-name",
601
+ signinSubmit: "signin-submit",
602
+ authError: "auth-error",
603
+ purchasesList: "purchases-list",
604
+ purchaseItem: "purchase-item",
605
+ purchasesEmpty: "purchases-empty",
606
+ signout: "signout"
607
+ };
608
+ var headerStyle = {
609
+ backgroundColor: "hsl(var(--sf-header-bg, var(--background)))",
610
+ color: "hsl(var(--sf-header-fg, var(--foreground)))",
611
+ borderColor: "hsl(var(--sf-header-fg, var(--foreground)) / 0.14)"
612
+ };
613
+ var headerDivider = {
614
+ borderColor: "hsl(var(--sf-header-fg, var(--foreground)) / 0.14)"
615
+ };
616
+ var ANNOUNCEMENT_KEY = "fayz.storefront.announcement.dismissed";
617
+ function AnnouncementBar() {
618
+ const config = useStorefrontConfig();
619
+ const [dismissed, setDismissed] = React18.useState(() => {
620
+ try {
621
+ return localStorage.getItem(ANNOUNCEMENT_KEY) === config.announcement;
622
+ } catch {
623
+ return false;
624
+ }
625
+ });
626
+ if (!config.announcement || dismissed) return null;
627
+ const dismiss = () => {
628
+ setDismissed(true);
629
+ try {
630
+ localStorage.setItem(ANNOUNCEMENT_KEY, config.announcement ?? "");
631
+ } catch {
632
+ }
633
+ };
634
+ return /* @__PURE__ */ jsxRuntime.jsxs(
635
+ "div",
636
+ {
637
+ "data-testid": TID.announcementBar,
638
+ className: "relative px-4 py-2 text-center text-xs font-semibold tracking-wide",
639
+ style: {
640
+ backgroundColor: "hsl(var(--sf-announcement-bg, var(--primary)))",
641
+ color: "hsl(var(--sf-announcement-fg, var(--primary-foreground)))"
642
+ },
643
+ children: [
644
+ config.announcement,
645
+ /* @__PURE__ */ jsxRuntime.jsx(
646
+ "button",
647
+ {
648
+ type: "button",
649
+ "aria-label": "Fechar aviso",
650
+ onClick: dismiss,
651
+ className: "absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-1 opacity-70 transition-opacity hover:opacity-100",
652
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-3.5 w-3.5" })
653
+ }
654
+ )
655
+ ]
656
+ }
657
+ );
658
+ }
659
+ function UtilityBar() {
660
+ const config = useStorefrontConfig();
661
+ const contact = config.footer?.contact;
662
+ const social = config.footer?.social;
663
+ if (!contact?.phone && !contact?.email && !social?.length) return null;
664
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden border-b text-xs opacity-80 md:block", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex h-8 max-w-7xl items-center gap-5 px-4 sm:px-6", children: [
665
+ contact?.phone && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
666
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Phone, { className: "h-3 w-3" }),
667
+ " ",
668
+ contact.phone
669
+ ] }),
670
+ contact?.email && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
671
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Mail, { className: "h-3 w-3" }),
672
+ " ",
673
+ contact.email
674
+ ] }),
675
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto flex items-center gap-4", children: social?.map((s) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: s.href, target: "_blank", rel: "noreferrer", className: "hover:underline", children: s.label }, s.href)) })
676
+ ] }) });
677
+ }
678
+ function SearchInput({ className }) {
679
+ const config = useStorefrontConfig();
680
+ const search = useCatalogStore((s) => s.search);
681
+ const setSearch = useCatalogStore((s) => s.setSearch);
682
+ const path = useHashPath();
683
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className ?? ""}`, children: [
684
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 opacity-60" }),
685
+ /* @__PURE__ */ jsxRuntime.jsx(
686
+ "input",
687
+ {
688
+ "data-testid": TID.headerSearch,
689
+ type: "search",
690
+ placeholder: "Buscar produtos...",
691
+ value: search,
692
+ onChange: (e) => {
693
+ setSearch(e.target.value);
694
+ if (path !== config.catalogPath) navigateTo(config.catalogPath);
695
+ },
696
+ className: "w-full border bg-white/10 py-2 pl-9 pr-4 text-sm outline-none transition-colors placeholder:opacity-60 focus:border-primary focus:bg-white focus:text-gray-900",
697
+ style: {
698
+ borderRadius: "var(--sf-radius-input)",
699
+ borderColor: "hsl(var(--sf-header-fg, var(--foreground)) / 0.25)"
700
+ }
701
+ }
702
+ )
703
+ ] });
704
+ }
705
+ function HeaderActions() {
706
+ const config = useStorefrontConfig();
707
+ const count = useCartStore(selectCount);
708
+ const openDrawer = useCartStore((s) => s.openDrawer);
709
+ const session = useSessionStore();
710
+ const [accountOpen, setAccountOpen] = React18.useState(false);
711
+ const pop = usePopOnChange(count);
712
+ const firstName = session.name?.split(/\s+/)[0];
713
+ const initial2 = (firstName ?? session.email ?? "C").slice(0, 1).toUpperCase();
714
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex items-center gap-1", children: [
715
+ config.features.accounts && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
716
+ session.email ? /* @__PURE__ */ jsxRuntime.jsxs(
717
+ "button",
718
+ {
719
+ type: "button",
720
+ "data-testid": TID.accountLink,
721
+ "aria-label": "Abrir menu da conta",
722
+ "aria-expanded": accountOpen,
723
+ onClick: () => setAccountOpen((open) => !open),
724
+ className: "flex items-center gap-1.5 rounded-full p-1.5 transition-opacity hover:opacity-80",
725
+ children: [
726
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-primary text-sm font-bold text-primary-foreground", children: initial2 }),
727
+ firstName && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": TID.accountName, className: "hidden max-w-24 truncate text-sm font-medium lg:inline", children: firstName })
728
+ ]
729
+ }
730
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
731
+ Link,
732
+ {
733
+ to: "/account",
734
+ "data-testid": TID.accountLink,
735
+ "aria-label": "Minhas compras",
736
+ className: "flex items-center gap-1.5 rounded-full p-2.5 transition-opacity hover:opacity-70",
737
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.User, { className: "h-5 w-5" })
738
+ }
739
+ ),
740
+ accountOpen && session.email && /* @__PURE__ */ jsxRuntime.jsxs(
741
+ "div",
742
+ {
743
+ "data-testid": TID.accountDropdown,
744
+ className: "absolute right-0 top-full z-50 mt-3 w-72 overflow-hidden rounded-xl border bg-background text-foreground shadow-xl",
745
+ children: [
746
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b p-4", children: [
747
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold", children: session.name || firstName || "Cliente" }),
748
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm text-muted-foreground", children: session.email })
749
+ ] }),
750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2", children: [
751
+ /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: "/account", className: "flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted", children: [
752
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.UserCircle, { className: "h-4 w-4" }),
753
+ "Perfil"
754
+ ] }),
755
+ /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: "/account", className: "flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted", children: [
756
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Package, { className: "h-4 w-4" }),
757
+ "Meus pedidos"
758
+ ] }),
759
+ /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: "/account", className: "flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted", children: [
760
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-4 w-4" }),
761
+ "Pagamentos"
762
+ ] }),
763
+ /* @__PURE__ */ jsxRuntime.jsxs(
764
+ "button",
765
+ {
766
+ type: "button",
767
+ onClick: () => {
768
+ setAccountOpen(false);
769
+ void signOutCustomer();
770
+ },
771
+ className: "flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold hover:bg-muted",
772
+ children: [
773
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.LogOut, { className: "h-4 w-4" }),
774
+ "Sair"
775
+ ]
776
+ }
777
+ )
778
+ ] })
779
+ ]
780
+ }
781
+ )
782
+ ] }),
783
+ /* @__PURE__ */ jsxRuntime.jsxs(
784
+ "button",
785
+ {
786
+ type: "button",
787
+ "data-testid": TID.cartButton,
788
+ "aria-label": "Carrinho",
789
+ onClick: openDrawer,
790
+ className: "relative rounded-full p-2.5 transition-opacity hover:opacity-70",
791
+ children: [
792
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShoppingBag, { className: "h-5 w-5" }),
793
+ count > 0 && /* @__PURE__ */ jsxRuntime.jsx(
794
+ "span",
795
+ {
796
+ "data-testid": TID.cartCount,
797
+ className: `absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-semibold text-primary-foreground ${pop ? "animate-badge-pop" : ""}`,
798
+ children: count
799
+ }
800
+ )
801
+ ]
802
+ }
803
+ )
804
+ ] });
805
+ }
806
+ function NavLinks({ className }) {
807
+ const config = useStorefrontConfig();
808
+ const { categories } = useCategories();
809
+ const setCategoryId = useCatalogStore((s) => s.setCategoryId);
810
+ const goCategory = (categoryId) => {
811
+ useCatalogStore.getState().reset();
812
+ setCategoryId(categoryId);
813
+ navigateTo(config.catalogPath);
814
+ };
815
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: `flex items-center gap-6 ${className ?? ""}`, children: [
816
+ config.nav.map((link) => /* @__PURE__ */ jsxRuntime.jsx(
817
+ Link,
818
+ {
819
+ to: link.to,
820
+ "data-testid": TID.navLink(link.label),
821
+ className: "sf-nav-link text-sm font-medium transition-opacity hover:opacity-70",
822
+ children: link.label
823
+ },
824
+ link.to
825
+ )),
826
+ categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
827
+ "button",
828
+ {
829
+ type: "button",
830
+ "data-testid": TID.navCategory(c.slug),
831
+ onClick: () => goCategory(c.id),
832
+ className: "sf-nav-link group relative hidden text-sm font-medium transition-opacity hover:opacity-80 lg:inline",
833
+ children: [
834
+ c.name,
835
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -bottom-1 left-0 h-0.5 w-0 bg-primary transition-all duration-300 group-hover:w-full" })
836
+ ]
837
+ },
838
+ c.id
839
+ ))
840
+ ] });
841
+ }
842
+ function StorefrontHeader() {
843
+ const config = useStorefrontConfig();
844
+ const variant = config.theme?.header?.variant ?? "classic";
845
+ const scrolled = useScrolled();
846
+ const logo = /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "sf-heading shrink-0 text-xl font-bold tracking-tight", children: config.logo ?? config.name });
847
+ return /* @__PURE__ */ jsxRuntime.jsxs(
848
+ "header",
849
+ {
850
+ className: `sticky top-0 z-40 border-b transition-shadow duration-300 ${scrolled ? "shadow-md" : ""}`,
851
+ style: headerStyle,
852
+ children: [
853
+ /* @__PURE__ */ jsxRuntime.jsx(AnnouncementBar, {}),
854
+ /* @__PURE__ */ jsxRuntime.jsx(UtilityBar, {}),
855
+ variant === "centered" ? (
856
+ // Rio/Flex pattern: centered logo row, nav row below
857
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6", children: [
859
+ /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-xs sm:block" }),
860
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
861
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {}) })
862
+ ] }),
863
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden justify-center border-t py-2.5 sm:flex", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) })
864
+ ] })
865
+ ) : variant === "search" ? (
866
+ // Brasília pattern: prominent search left, logo center, actions right; nav row below
867
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center gap-6 px-4 sm:px-6", children: [
869
+ /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-sm sm:block" }),
870
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
871
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {}) })
872
+ ] }),
873
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden border-t sm:block", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) }) })
874
+ ] })
875
+ ) : variant === "minimal" ? (
876
+ // Uyuni pattern: nav left, centered logo, actions right — single compact row
877
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6", children: [
878
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-5", children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, { className: "hidden sm:flex" }) }),
879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
880
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-3", children: [
881
+ /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-44 lg:block" }),
882
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {})
883
+ ] })
884
+ ] })
885
+ ) : (
886
+ // classic: logo left, search center-right, actions right (original layout)
887
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex h-16 max-w-7xl items-center gap-6 px-4 sm:px-6", children: [
888
+ logo,
889
+ /* @__PURE__ */ jsxRuntime.jsx(NavLinks, { className: "hidden md:flex" }),
890
+ /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "ml-auto hidden w-full max-w-sm sm:block" }),
891
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {})
892
+ ] })
893
+ )
894
+ ]
895
+ }
896
+ );
897
+ }
898
+ function StorefrontFooter() {
899
+ const config = useStorefrontConfig();
900
+ const { categories } = useCategories();
901
+ const visibleCategories = categories.slice(0, 8);
902
+ const hiddenCategoryCount = Math.max(categories.length - visibleCategories.length, 0);
903
+ const goCategory = (categoryId) => {
904
+ useCatalogStore.getState().reset();
905
+ useCatalogStore.getState().setCategoryId(categoryId);
906
+ navigateTo(config.catalogPath);
907
+ };
908
+ return /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "mt-16 border-t bg-card", children: [
909
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid max-w-7xl gap-10 px-4 py-12 sm:grid-cols-2 sm:px-6 lg:grid-cols-4", children: [
910
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
911
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sf-heading text-lg font-bold", children: config.name }),
912
+ config.footer?.about && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm leading-relaxed text-muted-foreground", children: config.footer.about })
913
+ ] }),
914
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
915
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Departamentos" }),
916
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm", children: [
917
+ visibleCategories.map((c) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
918
+ "button",
919
+ {
920
+ type: "button",
921
+ onClick: () => goCategory(c.id),
922
+ className: "max-w-full truncate text-left transition-opacity hover:opacity-70",
923
+ children: c.name
924
+ }
925
+ ) }, c.id)),
926
+ hiddenCategoryCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: config.catalogPath, className: "font-medium underline-offset-2 transition-opacity hover:underline", children: [
927
+ "Ver todos (",
928
+ categories.length,
929
+ ")"
930
+ ] }) })
931
+ ] })
932
+ ] }),
933
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
934
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Navega\xE7\xE3o" }),
935
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm", children: [
936
+ config.nav.map((link) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Link, { to: link.to, className: "transition-opacity hover:opacity-70", children: link.label }) }, link.to)),
937
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children: "Minhas compras" }) })
938
+ ] })
939
+ ] }),
940
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
941
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Contato" }),
942
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm text-muted-foreground", children: [
943
+ config.footer?.contact?.phone && /* @__PURE__ */ jsxRuntime.jsx("li", { children: config.footer.contact.phone }),
944
+ config.footer?.contact?.email && /* @__PURE__ */ jsxRuntime.jsx("li", { children: config.footer.contact.email }),
945
+ config.footer?.contact?.address && /* @__PURE__ */ jsxRuntime.jsx("li", { children: config.footer.contact.address }),
946
+ config.footer?.social?.map((s) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: s.href, target: "_blank", rel: "noreferrer", className: "transition-opacity hover:opacity-70", children: s.label }) }, s.href))
947
+ ] })
948
+ ] })
949
+ ] }),
950
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex max-w-7xl flex-col items-center gap-3 px-4 py-5 text-xs text-muted-foreground sm:flex-row sm:justify-between sm:px-6", children: [
951
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-wrap items-center justify-center gap-x-5 gap-y-2", children: [
952
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children: "Privacidade" }),
953
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children: "Termos" }),
954
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children: "Trocas e devolu\xE7\xF5es" })
955
+ ] }),
956
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-center", children: [
957
+ "\xA9 ",
958
+ config.name,
959
+ " \u2014 powered by Fayz \xB7 Fotos:",
960
+ " ",
961
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "https://unsplash.com", target: "_blank", rel: "noreferrer", className: "underline-offset-2 hover:underline", children: "Unsplash" })
962
+ ] })
963
+ ] }) })
964
+ ] });
965
+ }
966
+ var REASON_MESSAGE = {
967
+ expired: "Cupom expirado.",
968
+ not_started: "Cupom ainda n\xE3o est\xE1 ativo.",
969
+ usage_limit: "Cupom esgotado.",
970
+ not_found: "Cupom inv\xE1lido ou expirado."
971
+ };
972
+ function useDiscountValidator() {
973
+ const config = useStorefrontConfig();
974
+ return React18.useCallback(async (code) => {
975
+ const normalized = code.trim().toUpperCase();
976
+ if (!normalized) return { valid: false, percent: 0, message: "Informe um cupom." };
977
+ const configDiscount = config.discounts?.find((d) => d.code.trim().toUpperCase() === normalized);
978
+ if (configDiscount) return { valid: true, percent: configDiscount.percent };
979
+ const validation = await shop.validateDiscount({ code: normalized });
980
+ if (!validation.valid) {
981
+ return { valid: false, percent: 0, message: REASON_MESSAGE[validation.reason ?? "not_found"] ?? "Cupom inv\xE1lido ou expirado." };
982
+ }
983
+ if (validation.type !== "percentage") {
984
+ return { valid: false, percent: 0, message: "Este cupom n\xE3o \xE9 suportado na loja." };
985
+ }
986
+ return { valid: true, percent: validation.value };
987
+ }, [config.discounts]);
988
+ }
989
+ var seq = 0;
990
+ var useToastStore = zustand.create((set) => ({
991
+ toasts: [],
992
+ push: (input) => {
993
+ const id = `toast-${++seq}-${Date.now()}`;
994
+ const toast2 = {
995
+ id,
996
+ title: input.title,
997
+ description: input.description,
998
+ variant: input.variant ?? "info",
999
+ durationMs: input.durationMs ?? 3400
1000
+ };
1001
+ set((s) => ({ toasts: [...s.toasts, toast2] }));
1002
+ return id;
1003
+ },
1004
+ dismiss: (id) => set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }))
1005
+ }));
1006
+ var toast = {
1007
+ success: (title, description) => useToastStore.getState().push({ title, description, variant: "success" }),
1008
+ error: (title, description) => useToastStore.getState().push({ title, description, variant: "error" }),
1009
+ info: (title, description) => useToastStore.getState().push({ title, description, variant: "info" })
1010
+ };
1011
+ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTestId }) {
1012
+ const clamp = (n) => Math.max(min, max != null ? Math.min(n, max) : n);
1013
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center rounded-lg border", children: [
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ "button",
1016
+ {
1017
+ type: "button",
1018
+ "data-testid": decTestId,
1019
+ "aria-label": "Diminuir quantidade",
1020
+ onClick: () => onChange(clamp(value - 1)),
1021
+ className: "px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40",
1022
+ disabled: value <= min,
1023
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Minus, { className: "h-3.5 w-3.5" })
1024
+ }
1025
+ ),
1026
+ /* @__PURE__ */ jsxRuntime.jsx(
1027
+ "input",
1028
+ {
1029
+ "data-testid": testId,
1030
+ type: "number",
1031
+ value,
1032
+ min,
1033
+ max,
1034
+ onChange: (e) => {
1035
+ const n = parseInt(e.target.value, 10);
1036
+ if (!isNaN(n)) onChange(clamp(n));
1037
+ },
1038
+ className: "w-12 border-x bg-transparent py-1.5 text-center text-sm [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsxRuntime.jsx(
1042
+ "button",
1043
+ {
1044
+ type: "button",
1045
+ "data-testid": incTestId,
1046
+ "aria-label": "Aumentar quantidade",
1047
+ onClick: () => onChange(clamp(value + 1)),
1048
+ className: "px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40",
1049
+ disabled: max != null && value >= max,
1050
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Plus, { className: "h-3.5 w-3.5" })
1051
+ }
1052
+ )
1053
+ ] });
1054
+ }
1055
+ function CartDrawer() {
1056
+ const config = useStorefrontConfig();
1057
+ const cart = useCartStore();
1058
+ const validate = useDiscountValidator();
1059
+ const [code, setCode] = React18.useState("");
1060
+ const [discountError, setDiscountError] = React18.useState(null);
1061
+ const [applying, setApplying] = React18.useState(false);
1062
+ React18.useEffect(() => {
1063
+ if (!cart.isOpen) return;
1064
+ const onKey = (e) => {
1065
+ if (e.key === "Escape") cart.closeDrawer();
1066
+ };
1067
+ document.addEventListener("keydown", onKey);
1068
+ return () => document.removeEventListener("keydown", onKey);
1069
+ }, [cart.isOpen, cart.closeDrawer]);
1070
+ const subtotal = selectSubtotal(cart);
1071
+ const discountTotal = selectDiscountTotal(cart);
1072
+ const shipping = selectShipping(cart, config);
1073
+ const total = selectTotal(cart, config);
1074
+ const money = (v) => formatMoney(v, config.currency, config.locale);
1075
+ async function applyCode() {
1076
+ setApplying(true);
1077
+ setDiscountError(null);
1078
+ try {
1079
+ const result = await validate(code);
1080
+ if (result.valid) {
1081
+ const applied = code.trim().toUpperCase();
1082
+ cart.applyDiscount(applied, result.percent);
1083
+ setCode("");
1084
+ toast.success("Cupom aplicado!", `${applied} \u2022 ${result.percent}% de desconto`);
1085
+ } else {
1086
+ setDiscountError(result.message ?? "Cupom inv\xE1lido.");
1087
+ toast.error("Cupom inv\xE1lido", result.message ?? "Verifique o c\xF3digo e tente novamente.");
1088
+ }
1089
+ } finally {
1090
+ setApplying(false);
1091
+ }
1092
+ }
1093
+ if (!cart.isOpen) return null;
1094
+ const freeAbove = config.shipping.freeAbove;
1095
+ const missingForFree = freeAbove != null && subtotal < freeAbove ? freeAbove - subtotal : 0;
1096
+ const progress = freeAbove != null ? Math.min(100, subtotal / freeAbove * 100) : 0;
1097
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50", children: [
1098
+ /* @__PURE__ */ jsxRuntime.jsx(
1099
+ "div",
1100
+ {
1101
+ className: "absolute inset-0 animate-fade-in bg-black/40",
1102
+ "aria-hidden": true,
1103
+ onClick: cart.closeDrawer
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsxRuntime.jsxs(
1107
+ "div",
1108
+ {
1109
+ "data-testid": TID.cartDrawer,
1110
+ role: "dialog",
1111
+ "aria-label": "Carrinho",
1112
+ className: "absolute right-0 top-0 flex h-full w-full max-w-md animate-slide-in-from-right flex-col bg-background shadow-2xl",
1113
+ children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b px-5 py-4", children: [
1115
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: "Carrinho" }),
1116
+ /* @__PURE__ */ jsxRuntime.jsx(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ "aria-label": "Fechar carrinho",
1121
+ onClick: cart.closeDrawer,
1122
+ className: "rounded-full p-1.5 hover:bg-muted",
1123
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-5 w-5" })
1124
+ }
1125
+ )
1126
+ ] }),
1127
+ cart.lines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.cartEmpty, className: "flex flex-1 items-center justify-center p-8 text-center text-muted-foreground", children: "Seu carrinho est\xE1 vazio." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1128
+ freeAbove != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": TID.shippingProgress, className: "border-b bg-muted/40 px-5 py-3", children: [
1129
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium", children: missingForFree > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1130
+ "Faltam ",
1131
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: money(missingForFree) }),
1132
+ " para frete gr\xE1tis \u{1F69A}"
1133
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1134
+ "\u{1F389} Voc\xEA ganhou ",
1135
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "frete gr\xE1tis" }),
1136
+ "!"
1137
+ ] }) }),
1138
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 h-1.5 overflow-hidden rounded-full bg-border", children: /* @__PURE__ */ jsxRuntime.jsx(
1139
+ "div",
1140
+ {
1141
+ className: "h-full rounded-full bg-primary transition-all duration-500 ease-out",
1142
+ style: { width: `${progress}%` }
1143
+ }
1144
+ ) })
1145
+ ] }),
1146
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 space-y-4 overflow-y-auto px-5 py-4", children: cart.lines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(
1147
+ "div",
1148
+ {
1149
+ "data-testid": TID.cartLine,
1150
+ "data-product-id": line.productId,
1151
+ "data-line-id": line.lineId ?? line.productId,
1152
+ className: `flex animate-fade-up gap-3 rounded-lg p-1.5 transition-all hover:bg-muted/40 ${(line.lineId ?? line.productId) === cart.justAddedLineId ? "bg-primary/5 ring-1 ring-primary/30" : ""}`,
1153
+ children: [
1154
+ line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: line.imageUrl, alt: line.name, className: "h-20 w-20 shrink-0 rounded-lg border object-cover" }),
1155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col", children: [
1156
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1157
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium leading-snug", children: line.name }),
1158
+ /* @__PURE__ */ jsxRuntime.jsx(
1159
+ "button",
1160
+ {
1161
+ type: "button",
1162
+ "data-testid": TID.lineRemove,
1163
+ "aria-label": `Remover ${line.name}`,
1164
+ onClick: () => cart.removeItem(line.lineId ?? line.productId),
1165
+ className: "text-muted-foreground hover:text-destructive",
1166
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-4 w-4" })
1167
+ }
1168
+ )
1169
+ ] }),
1170
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: line.sku }),
1171
+ line.optionsLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: line.optionsLabel }),
1172
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-2", children: [
1173
+ /* @__PURE__ */ jsxRuntime.jsx(
1174
+ QuantityInput,
1175
+ {
1176
+ value: line.quantity,
1177
+ max: line.maxQuantity || void 0,
1178
+ onChange: (n) => cart.setQuantity(line.lineId ?? line.productId, n),
1179
+ testId: TID.lineQty,
1180
+ incTestId: TID.lineInc,
1181
+ decTestId: TID.lineDec
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ jsxRuntime.jsx(
1185
+ "span",
1186
+ {
1187
+ className: "text-sm font-semibold",
1188
+ "data-price": (line.unitPrice * line.quantity).toFixed(2),
1189
+ children: money(line.unitPrice * line.quantity)
1190
+ }
1191
+ )
1192
+ ] })
1193
+ ] })
1194
+ ]
1195
+ },
1196
+ line.lineId ?? line.productId
1197
+ )) }),
1198
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 border-t px-5 py-4", children: [
1199
+ config.features.discounts && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
1200
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1201
+ /* @__PURE__ */ jsxRuntime.jsx(
1202
+ "input",
1203
+ {
1204
+ "data-testid": TID.discountInput,
1205
+ type: "text",
1206
+ placeholder: "Cupom de desconto",
1207
+ value: code,
1208
+ onChange: (e) => setCode(e.target.value),
1209
+ className: "flex-1 rounded-lg border bg-background px-3 py-2 text-sm uppercase"
1210
+ }
1211
+ ),
1212
+ /* @__PURE__ */ jsxRuntime.jsx(
1213
+ "button",
1214
+ {
1215
+ type: "button",
1216
+ "data-testid": TID.discountApply,
1217
+ onClick: applyCode,
1218
+ disabled: applying || !code.trim(),
1219
+ className: "rounded-lg border px-3 py-2 text-sm font-medium hover:bg-muted disabled:opacity-50",
1220
+ children: "Aplicar"
1221
+ }
1222
+ )
1223
+ ] }),
1224
+ discountError && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.discountError, className: "text-xs text-destructive", children: discountError })
1225
+ ] }),
1226
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "space-y-1.5 text-sm", children: [
1227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
1228
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
1229
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": TID.cartSubtotal, "data-price": subtotal.toFixed(2), children: money(subtotal) })
1230
+ ] }),
1231
+ discountTotal > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-emerald-700", "data-testid": TID.discountRow, children: [
1232
+ /* @__PURE__ */ jsxRuntime.jsxs("dt", { children: [
1233
+ "Desconto",
1234
+ " ",
1235
+ /* @__PURE__ */ jsxRuntime.jsxs(
1236
+ "button",
1237
+ {
1238
+ type: "button",
1239
+ onClick: cart.clearDiscount,
1240
+ className: "text-xs text-muted-foreground underline",
1241
+ children: [
1242
+ "(",
1243
+ cart.discountCode,
1244
+ " \u2715)"
1245
+ ]
1246
+ }
1247
+ )
1248
+ ] }),
1249
+ /* @__PURE__ */ jsxRuntime.jsxs("dd", { "data-price": discountTotal.toFixed(2), children: [
1250
+ "\u2212",
1251
+ money(discountTotal)
1252
+ ] })
1253
+ ] }),
1254
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
1255
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Frete" }),
1256
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": TID.cartShipping, "data-price": shipping.toFixed(2), children: shipping === 0 ? "Gr\xE1tis" : money(shipping) })
1257
+ ] }),
1258
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between border-t pt-2 text-base font-semibold", children: [
1259
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
1260
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": TID.cartTotal, "data-price": total.toFixed(2), children: money(total) })
1261
+ ] })
1262
+ ] }),
1263
+ /* @__PURE__ */ jsxRuntime.jsx(
1264
+ "button",
1265
+ {
1266
+ type: "button",
1267
+ "data-testid": TID.goCheckout,
1268
+ onClick: () => {
1269
+ cart.closeDrawer();
1270
+ navigateTo("/checkout");
1271
+ },
1272
+ className: "sf-cta w-full bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg",
1273
+ style: { borderRadius: "var(--sf-radius-button)" },
1274
+ children: "Finalizar compra"
1275
+ }
1276
+ )
1277
+ ] })
1278
+ ] })
1279
+ ]
1280
+ }
1281
+ )
1282
+ ] });
1283
+ }
1284
+ function useProducts(opts) {
1285
+ const [products, setProducts] = React18.useState([]);
1286
+ const [loading, setLoading] = React18.useState(true);
1287
+ const [error, setError] = React18.useState(null);
1288
+ const [nonce, setNonce] = React18.useState(0);
1289
+ const key = JSON.stringify(opts);
1290
+ React18.useEffect(() => {
1291
+ let cancelled = false;
1292
+ setLoading(true);
1293
+ runtime.getShopProvider().listProducts(JSON.parse(key)).then((data) => {
1294
+ if (cancelled) return;
1295
+ setProducts(data);
1296
+ setError(null);
1297
+ }).catch((e) => {
1298
+ if (!cancelled) setError(e instanceof Error ? e.message : String(e));
1299
+ }).finally(() => {
1300
+ if (!cancelled) setLoading(false);
1301
+ });
1302
+ return () => {
1303
+ cancelled = true;
1304
+ };
1305
+ }, [key, nonce]);
1306
+ return { products, loading, error, reload: () => setNonce((n) => n + 1) };
1307
+ }
1308
+ function Price({ value, compareAt, testId, compareTestId, className }) {
1309
+ const { currency, locale } = useStorefrontConfig();
1310
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-baseline gap-2 ${className ?? ""}`, children: [
1311
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": testId, "data-price": value.toFixed(2), className: "font-semibold", children: formatMoney(value, currency, locale) }),
1312
+ compareAt != null && compareAt > value && /* @__PURE__ */ jsxRuntime.jsx(
1313
+ "span",
1314
+ {
1315
+ "data-testid": compareTestId,
1316
+ "data-price": compareAt.toFixed(2),
1317
+ className: "text-sm text-muted-foreground line-through",
1318
+ children: formatMoney(compareAt, currency, locale)
1319
+ }
1320
+ )
1321
+ ] });
1322
+ }
1323
+
1324
+ // src/slot-contracts.ts
1325
+ var productCardSlotContract = {
1326
+ root: { "data-testid": TID.productCard },
1327
+ name: { "data-testid": TID.productCardName },
1328
+ priceTestId: TID.productCardPrice,
1329
+ addButton: { "data-testid": TID.productCardAdd }
1330
+ };
1331
+ var storefrontSlotContracts = {
1332
+ productCard: productCardSlotContract
1333
+ };
1334
+ function ProductCard({ product }) {
1335
+ const config = useStorefrontConfig();
1336
+ const addItem = useCartStore((s) => s.addItem);
1337
+ const hasOptions = getProductOptionGroups(product).length > 0;
1338
+ const soldOut = product.inventoryCount <= 0;
1339
+ const onSale = product.compareAtPrice != null && product.compareAtPrice > product.price;
1340
+ const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
1341
+ const cardStyle = config.theme?.productCard?.style ?? "card";
1342
+ const aspect = config.theme?.productCard?.imageAspect === "portrait" ? "aspect-[3/4]" : "aspect-square";
1343
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1344
+ "div",
1345
+ {
1346
+ ...productCardSlotContract.root,
1347
+ "data-slug": product.slug,
1348
+ className: `group flex flex-col overflow-hidden transition-all duration-300 ${cardStyle === "editorial" ? "hover:-translate-y-1" : "border bg-card hover:-translate-y-1 hover:shadow-lg"}`,
1349
+ style: { borderRadius: "var(--sf-radius-card)" },
1350
+ children: [
1351
+ /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: `/product/${product.slug}`, className: `relative block ${aspect} overflow-hidden bg-muted`, style: { borderRadius: cardStyle === "editorial" ? "var(--sf-radius-card)" : void 0 }, children: [
1352
+ image && /* @__PURE__ */ jsxRuntime.jsx(
1353
+ "img",
1354
+ {
1355
+ src: image.url,
1356
+ alt: image.altText ?? product.name,
1357
+ className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105",
1358
+ loading: "lazy"
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-3 top-3 flex gap-2", children: [
1362
+ onSale && !soldOut && /* @__PURE__ */ jsxRuntime.jsx(
1363
+ "span",
1364
+ {
1365
+ "data-testid": TID.badgeSale,
1366
+ className: "rounded-full bg-red-600 px-2.5 py-0.5 text-xs font-semibold text-white",
1367
+ children: "Oferta"
1368
+ }
1369
+ ),
1370
+ soldOut && /* @__PURE__ */ jsxRuntime.jsx(
1371
+ "span",
1372
+ {
1373
+ "data-testid": TID.badgeSoldout,
1374
+ className: "rounded-full bg-gray-900/80 px-2.5 py-0.5 text-xs font-semibold text-white",
1375
+ children: "Esgotado"
1376
+ }
1377
+ )
1378
+ ] })
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-1 flex-col gap-1 ${cardStyle === "editorial" ? "px-1 py-3" : "p-4"}`, children: [
1381
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: product.categoryName }),
1382
+ /* @__PURE__ */ jsxRuntime.jsx(
1383
+ Link,
1384
+ {
1385
+ to: `/product/${product.slug}`,
1386
+ ...productCardSlotContract.name,
1387
+ className: "font-medium leading-snug hover:underline",
1388
+ children: product.name
1389
+ }
1390
+ ),
1391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-2", children: [
1392
+ /* @__PURE__ */ jsxRuntime.jsx(
1393
+ Price,
1394
+ {
1395
+ value: product.price,
1396
+ compareAt: product.compareAtPrice,
1397
+ testId: productCardSlotContract.priceTestId
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsxRuntime.jsx(
1401
+ "button",
1402
+ {
1403
+ type: "button",
1404
+ ...productCardSlotContract.addButton,
1405
+ disabled: soldOut,
1406
+ "aria-label": hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho`,
1407
+ onClick: () => {
1408
+ if (hasOptions) {
1409
+ navigateTo(`/product/${product.slug}`);
1410
+ return;
1411
+ }
1412
+ addItem(product);
1413
+ },
1414
+ className: "rounded-full border bg-background/90 p-2.5 text-foreground shadow-sm transition-all duration-200 hover:scale-110 hover:bg-primary hover:text-primary-foreground hover:shadow disabled:cursor-not-allowed disabled:opacity-40 lg:translate-y-1 lg:opacity-0 lg:group-hover:translate-y-0 lg:group-hover:opacity-100 lg:disabled:opacity-0 lg:group-hover:disabled:opacity-40",
1415
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShoppingBag, { className: "h-4 w-4" })
1416
+ }
1417
+ )
1418
+ ] })
1419
+ ] })
1420
+ ]
1421
+ }
1422
+ );
1423
+ }
1424
+ function ProductGrid({ products, loading }) {
1425
+ const config = useStorefrontConfig();
1426
+ const ProductCardComponent = config.slots?.ProductCard ?? ProductCard;
1427
+ if (loading) {
1428
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[3/4] animate-pulse rounded-xl bg-muted" }, i)) });
1429
+ }
1430
+ if (products.length === 0) {
1431
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "py-20 text-center text-muted-foreground", children: "Nenhum produto encontrado." });
1432
+ }
1433
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: products.map((p) => /* @__PURE__ */ jsxRuntime.jsx(ProductCardComponent, { product: p }, p.id)) });
1434
+ }
1435
+ function FiltersPanel() {
1436
+ const { categories } = useCategories();
1437
+ const catalog = useCatalogStore();
1438
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "w-full shrink-0 space-y-6 lg:w-56", children: [
1439
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1440
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold", children: "Ordenar por" }),
1441
+ /* @__PURE__ */ jsxRuntime.jsxs(
1442
+ "select",
1443
+ {
1444
+ "data-testid": TID.sortSelect,
1445
+ value: catalog.sort,
1446
+ onChange: (e) => catalog.setSort(e.target.value),
1447
+ className: "w-full rounded-lg border bg-background px-3 py-2 text-sm",
1448
+ children: [
1449
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "newest", children: "Mais recentes" }),
1450
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "price-asc", children: "Menor pre\xE7o" }),
1451
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "price-desc", children: "Maior pre\xE7o" }),
1452
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "name", children: "Nome (A\u2013Z)" })
1453
+ ]
1454
+ }
1455
+ )
1456
+ ] }),
1457
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1458
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold", children: "Categorias" }),
1459
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-1", children: [
1460
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1461
+ "button",
1462
+ {
1463
+ type: "button",
1464
+ onClick: () => catalog.setCategoryId(null),
1465
+ className: `w-full rounded-md px-2 py-1.5 text-left text-sm transition-colors ${catalog.categoryId === null ? "bg-primary/10 font-medium text-primary" : "hover:bg-muted"}`,
1466
+ children: "Todas"
1467
+ }
1468
+ ) }),
1469
+ categories.map((c) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1470
+ "button",
1471
+ {
1472
+ type: "button",
1473
+ "data-testid": TID.filterCategory(c.slug),
1474
+ onClick: () => catalog.setCategoryId(catalog.categoryId === c.id ? null : c.id),
1475
+ className: `w-full rounded-md px-2 py-1.5 text-left text-sm transition-colors ${catalog.categoryId === c.id ? "bg-primary/10 font-medium text-primary" : "hover:bg-muted"}`,
1476
+ children: c.name
1477
+ }
1478
+ ) }, c.id))
1479
+ ] })
1480
+ ] }),
1481
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1482
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold", children: "Pre\xE7o" }),
1483
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1484
+ /* @__PURE__ */ jsxRuntime.jsx(
1485
+ "input",
1486
+ {
1487
+ "data-testid": TID.filterPriceMin,
1488
+ type: "number",
1489
+ placeholder: "M\xEDn",
1490
+ min: 0,
1491
+ value: catalog.priceMin ?? "",
1492
+ onChange: (e) => catalog.setPriceMin(e.target.value === "" ? null : Number(e.target.value)),
1493
+ className: "w-full rounded-lg border bg-background px-2.5 py-1.5 text-sm"
1494
+ }
1495
+ ),
1496
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
1497
+ /* @__PURE__ */ jsxRuntime.jsx(
1498
+ "input",
1499
+ {
1500
+ "data-testid": TID.filterPriceMax,
1501
+ type: "number",
1502
+ placeholder: "M\xE1x",
1503
+ min: 0,
1504
+ value: catalog.priceMax ?? "",
1505
+ onChange: (e) => catalog.setPriceMax(e.target.value === "" ? null : Number(e.target.value)),
1506
+ className: "w-full rounded-lg border bg-background px-2.5 py-1.5 text-sm"
1507
+ }
1508
+ )
1509
+ ] })
1510
+ ] }),
1511
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
1512
+ /* @__PURE__ */ jsxRuntime.jsx(
1513
+ "input",
1514
+ {
1515
+ "data-testid": TID.filterInstock,
1516
+ type: "checkbox",
1517
+ checked: catalog.inStockOnly,
1518
+ onChange: (e) => catalog.setInStockOnly(e.target.checked),
1519
+ className: "h-4 w-4 rounded border"
1520
+ }
1521
+ ),
1522
+ "Somente em estoque"
1523
+ ] }),
1524
+ /* @__PURE__ */ jsxRuntime.jsx(
1525
+ "button",
1526
+ {
1527
+ type: "button",
1528
+ "data-testid": TID.filtersClear,
1529
+ onClick: () => catalog.reset(),
1530
+ className: "text-sm text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
1531
+ children: "Limpar filtros"
1532
+ }
1533
+ )
1534
+ ] });
1535
+ }
1536
+ function useStorefrontHead(input) {
1537
+ const { title, description } = input;
1538
+ React18.useEffect(() => {
1539
+ if (title) document.title = title;
1540
+ }, [title]);
1541
+ React18.useEffect(() => {
1542
+ if (!description) return;
1543
+ let tag = document.head.querySelector('meta[name="description"]');
1544
+ if (!tag) {
1545
+ tag = document.createElement("meta");
1546
+ tag.setAttribute("name", "description");
1547
+ document.head.appendChild(tag);
1548
+ }
1549
+ tag.setAttribute("content", description.slice(0, 160));
1550
+ }, [description]);
1551
+ }
1552
+ var SORT_MAP = {
1553
+ newest: { orderBy: "created_at", order: "desc" },
1554
+ "price-asc": { orderBy: "price", order: "asc" },
1555
+ "price-desc": { orderBy: "price", order: "desc" },
1556
+ name: { orderBy: "name", order: "asc" }
1557
+ };
1558
+ var SORTS = ["newest", "price-asc", "price-desc", "name"];
1559
+ function useCatalogUrlSync() {
1560
+ const hydrated = React18.useRef(false);
1561
+ const catalog = useCatalogStore();
1562
+ React18.useEffect(() => {
1563
+ if (hydrated.current) return;
1564
+ hydrated.current = true;
1565
+ const q = window.location.hash.split("?")[1];
1566
+ if (!q) return;
1567
+ const p = new URLSearchParams(q);
1568
+ const s = useCatalogStore.getState();
1569
+ if (p.has("search")) s.setSearch(p.get("search") ?? "");
1570
+ if (p.get("category")) s.setCategoryId(p.get("category"));
1571
+ const sort = p.get("sort");
1572
+ if (sort && SORTS.includes(sort)) s.setSort(sort);
1573
+ if (p.get("stock") === "1") s.setInStockOnly(true);
1574
+ if (p.has("min")) s.setPriceMin(Number(p.get("min")) || null);
1575
+ if (p.has("max")) s.setPriceMax(Number(p.get("max")) || null);
1576
+ }, []);
1577
+ React18.useEffect(() => {
1578
+ const p = new URLSearchParams();
1579
+ if (catalog.search) p.set("search", catalog.search);
1580
+ if (catalog.categoryId) p.set("category", catalog.categoryId);
1581
+ if (catalog.sort !== "newest") p.set("sort", catalog.sort);
1582
+ if (catalog.inStockOnly) p.set("stock", "1");
1583
+ if (catalog.priceMin != null) p.set("min", String(catalog.priceMin));
1584
+ if (catalog.priceMax != null) p.set("max", String(catalog.priceMax));
1585
+ const base = window.location.hash.split("?")[0] || "#/catalog";
1586
+ const qs = p.toString();
1587
+ const next = qs ? `${base}?${qs}` : base;
1588
+ if (next !== window.location.hash) window.history.replaceState(null, "", next);
1589
+ }, [catalog.search, catalog.categoryId, catalog.sort, catalog.inStockOnly, catalog.priceMin, catalog.priceMax]);
1590
+ }
1591
+ function CatalogPage() {
1592
+ const config = useStorefrontConfig();
1593
+ const catalog = useCatalogStore();
1594
+ useCatalogUrlSync();
1595
+ useStorefrontHead({
1596
+ title: catalog.search ? `\u201C${catalog.search}\u201D \u2014 ${config.name}` : `${config.name} \u2014 Loja`
1597
+ });
1598
+ const providerOpts = {
1599
+ status: "active",
1600
+ categoryId: catalog.categoryId ?? void 0,
1601
+ search: catalog.search || void 0,
1602
+ ...SORT_MAP[catalog.sort]
1603
+ };
1604
+ const { products, loading, error, reload } = useProducts(providerOpts);
1605
+ const filtered = React18.useMemo(
1606
+ () => products.filter((p) => {
1607
+ if (catalog.priceMin != null && p.price < catalog.priceMin) return false;
1608
+ if (catalog.priceMax != null && p.price > catalog.priceMax) return false;
1609
+ if (catalog.inStockOnly && p.inventoryCount <= 0) return false;
1610
+ return true;
1611
+ }),
1612
+ [products, catalog.priceMin, catalog.priceMax, catalog.inStockOnly]
1613
+ );
1614
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-7xl px-4 py-8 sm:px-6", children: [
1615
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8", children: [
1616
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold tracking-tight", children: config.name }),
1617
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-muted-foreground", children: catalog.search ? `Resultados para \u201C${catalog.search}\u201D` : "Produtos selecionados com qualidade e entrega r\xE1pida." })
1618
+ ] }),
1619
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-8 lg:flex-row", children: [
1620
+ /* @__PURE__ */ jsxRuntime.jsx(FiltersPanel, {}),
1621
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border bg-card py-16 text-center", children: [
1622
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "N\xE3o foi poss\xEDvel carregar os produtos." }),
1623
+ /* @__PURE__ */ jsxRuntime.jsx(
1624
+ "button",
1625
+ {
1626
+ type: "button",
1627
+ onClick: reload,
1628
+ className: "mt-4 rounded-lg border px-4 py-2 text-sm font-semibold transition hover:bg-muted",
1629
+ children: "Tentar novamente"
1630
+ }
1631
+ )
1632
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ProductGrid, { products: filtered, loading }) })
1633
+ ] })
1634
+ ] });
1635
+ }
1636
+
1637
+ // src/sections.ts
1638
+ function bannerPlaceholder(label, hueA, hueB, w = 1600, h = 700) {
1639
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="hsl(${hueA},40%,78%)"/><stop offset="1" stop-color="hsl(${hueB},45%,55%)"/></linearGradient></defs><rect width="${w}" height="${h}" fill="url(#g)"/><circle cx="${w * 0.8}" cy="${h * 0.3}" r="${h * 0.45}" fill="hsla(${hueB},50%,90%,0.25)"/><circle cx="${w * 0.15}" cy="${h * 0.85}" r="${h * 0.35}" fill="hsla(${hueA},45%,30%,0.12)"/></svg>`;
1640
+ return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
1641
+ }
1642
+ function slideImage(slide, w = 1600, h = 700) {
1643
+ return slide.image ?? bannerPlaceholder(slide.title, slide.hue ?? 200, (slide.hue ?? 200) + 40, w, h);
1644
+ }
1645
+ function SlideCopy({ slide, active }) {
1646
+ const stagger = (i) => active ? { className: "animate-fade-up", style: { animationDelay: `${i * 120}ms` } } : { className: "opacity-0" };
1647
+ const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1648
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: `sf-heading text-3xl font-bold leading-tight tracking-tight sm:text-5xl ${stagger(0).className}`, style: stagger(0).style, children: slide.title }),
1649
+ slide.subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `mt-3 max-w-md text-base sm:text-lg ${stagger(1).className}`, style: stagger(1).style, children: slide.subtitle }),
1650
+ slide.cta && /* @__PURE__ */ jsxRuntime.jsx("div", { className: stagger(2).className, style: stagger(2).style, children: /* @__PURE__ */ jsxRuntime.jsx(
1651
+ Link,
1652
+ {
1653
+ to: slide.href ?? "/catalog",
1654
+ className: "sf-cta mt-6 inline-block bg-primary px-8 py-3.5 font-semibold text-primary-foreground shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl",
1655
+ style: { borderRadius: "var(--sf-radius-button)" },
1656
+ children: slide.cta
1657
+ }
1658
+ ) })
1659
+ ] });
1660
+ if (slide.boxed) {
1661
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border border-foreground/30 bg-background/90 p-8 backdrop-blur-sm sm:p-10", children: inner });
1662
+ }
1663
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-white", children: inner });
1664
+ }
1665
+ function HeroSection({
1666
+ variant,
1667
+ slides,
1668
+ height = "tall"
1669
+ }) {
1670
+ const [index, setIndex] = React18.useState(0);
1671
+ const interacted = React18.useRef(false);
1672
+ const hClass = height === "tall" ? "h-[72vh] min-h-[440px]" : "h-[48vh] min-h-[340px]";
1673
+ React18.useEffect(() => {
1674
+ if (variant !== "slider" || slides.length < 2 || prefersReducedMotion()) return;
1675
+ const timer = setInterval(() => {
1676
+ if (!interacted.current) setIndex((i) => (i + 1) % slides.length);
1677
+ }, 7e3);
1678
+ return () => clearInterval(timer);
1679
+ }, [variant, slides.length]);
1680
+ const goTo = (i) => {
1681
+ interacted.current = true;
1682
+ setIndex((i + slides.length) % slides.length);
1683
+ };
1684
+ if (slides.length === 0) return null;
1685
+ if (variant === "split") {
1686
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.hero, className: "grid gap-px sm:grid-cols-2", children: slides.slice(0, 2).map((slide, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1687
+ Link,
1688
+ {
1689
+ to: slide.href ?? "/catalog",
1690
+ className: "group relative block h-[42vh] min-h-[320px] overflow-hidden",
1691
+ children: [
1692
+ /* @__PURE__ */ jsxRuntime.jsx(
1693
+ "img",
1694
+ {
1695
+ src: slideImage(slide, 900, 700),
1696
+ alt: slide.title,
1697
+ className: "h-full w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
1698
+ }
1699
+ ),
1700
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-t from-black/65 via-black/25 to-black/10 p-6 text-center text-white", children: [
1701
+ /* @__PURE__ */ jsxRuntime.jsx(
1702
+ "h2",
1703
+ {
1704
+ className: "sf-heading animate-fade-up text-3xl font-bold uppercase tracking-wide drop-shadow-md sm:text-4xl",
1705
+ style: { animationDelay: `${i * 150}ms` },
1706
+ children: slide.title
1707
+ }
1708
+ ),
1709
+ slide.subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "animate-fade-up mt-2 max-w-sm drop-shadow", style: { animationDelay: `${i * 150 + 120}ms` }, children: slide.subtitle }),
1710
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-4 inline-block border-b-2 border-white/0 pb-0.5 text-sm font-semibold uppercase tracking-wider opacity-0 transition-all duration-300 group-hover:border-white group-hover:opacity-100", children: "Explorar \u2192" })
1711
+ ] })
1712
+ ]
1713
+ },
1714
+ i
1715
+ )) });
1716
+ }
1717
+ const active = slides[Math.min(index, slides.length - 1)];
1718
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.hero, className: `relative w-full overflow-hidden ${hClass}`, children: [
1719
+ slides.map((slide, i) => /* @__PURE__ */ jsxRuntime.jsx(
1720
+ "img",
1721
+ {
1722
+ src: slideImage(slide),
1723
+ alt: slide.title,
1724
+ "aria-hidden": i !== index,
1725
+ className: `absolute inset-0 h-full w-full object-cover transition-opacity duration-700 ${i === index ? "animate-kenburns opacity-100" : "opacity-0"}`
1726
+ },
1727
+ i
1728
+ )),
1729
+ /* @__PURE__ */ jsxRuntime.jsx(
1730
+ "div",
1731
+ {
1732
+ className: `absolute inset-0 flex items-end p-6 sm:p-12 ${active.boxed ? "" : "bg-gradient-to-t from-black/60 via-black/20 to-transparent"}`,
1733
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto w-full max-w-7xl", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsxRuntime.jsx(SlideCopy, { slide: active, active: true }, index) }) })
1734
+ }
1735
+ ),
1736
+ variant === "slider" && slides.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1737
+ /* @__PURE__ */ jsxRuntime.jsx(
1738
+ "button",
1739
+ {
1740
+ type: "button",
1741
+ "aria-label": "Slide anterior",
1742
+ "data-testid": TID.heroPrev,
1743
+ onClick: () => goTo(index - 1),
1744
+ className: "absolute left-4 top-1/2 -translate-y-1/2 rounded-full bg-white/85 p-2.5 shadow transition-transform hover:scale-110 hover:bg-white",
1745
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronLeft, { className: "h-5 w-5 text-gray-900" })
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxRuntime.jsx(
1749
+ "button",
1750
+ {
1751
+ type: "button",
1752
+ "aria-label": "Pr\xF3ximo slide",
1753
+ "data-testid": TID.heroNext,
1754
+ onClick: () => goTo(index + 1),
1755
+ className: "absolute right-4 top-1/2 -translate-y-1/2 rounded-full bg-white/85 p-2.5 shadow transition-transform hover:scale-110 hover:bg-white",
1756
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronRight, { className: "h-5 w-5 text-gray-900" })
1757
+ }
1758
+ ),
1759
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2", children: slides.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
1760
+ "button",
1761
+ {
1762
+ type: "button",
1763
+ "aria-label": `Ir para o slide ${i + 1}`,
1764
+ onClick: () => goTo(i),
1765
+ className: `h-2 rounded-full transition-all duration-300 ${i === index ? "w-7 bg-white" : "w-2 bg-white/60 hover:bg-white/80"}`
1766
+ },
1767
+ i
1768
+ )) })
1769
+ ] })
1770
+ ] });
1771
+ }
1772
+ function CategoryShowcase({ style, title }) {
1773
+ const { categories } = useCategories();
1774
+ const config = useStorefrontConfig();
1775
+ const setCategoryId = useCatalogStore((s) => s.setCategoryId);
1776
+ if (categories.length === 0) return null;
1777
+ const go = (categoryId) => {
1778
+ useCatalogStore.getState().reset();
1779
+ setCategoryId(categoryId);
1780
+ navigateTo(config.catalogPath);
1781
+ };
1782
+ if (style === "bubbles") {
1783
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.categoryShowcase, className: "mx-auto max-w-7xl px-4 py-12 sm:px-6", children: [
1784
+ title && /* @__PURE__ */ jsxRuntime.jsx(Reveal, { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
1785
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-start justify-center gap-8", children: categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 80, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => go(c.id), className: "group flex w-28 flex-col items-center gap-3", children: [
1786
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block h-24 w-24 overflow-hidden rounded-full border-2 border-transparent shadow-sm transition-all duration-300 group-hover:scale-105 group-hover:border-primary group-hover:shadow-md", children: /* @__PURE__ */ jsxRuntime.jsx(
1787
+ "img",
1788
+ {
1789
+ src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 200, 200),
1790
+ alt: c.name,
1791
+ className: "h-full w-full object-cover"
1792
+ }
1793
+ ) }),
1794
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-center text-xs font-medium leading-tight", children: c.name })
1795
+ ] }) }, c.id)) })
1796
+ ] });
1797
+ }
1798
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.categoryShowcase, className: "mx-auto max-w-7xl px-4 py-12 sm:px-6", children: [
1799
+ title && /* @__PURE__ */ jsxRuntime.jsx(Reveal, { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
1800
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-4", children: categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => go(c.id), className: "group relative block aspect-[4/5] w-full overflow-hidden", style: { borderRadius: "var(--sf-radius-card)" }, children: [
1801
+ /* @__PURE__ */ jsxRuntime.jsx(
1802
+ "img",
1803
+ {
1804
+ src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 480, 600),
1805
+ alt: c.name,
1806
+ className: "h-full w-full object-cover transition-transform duration-700 ease-out group-hover:scale-110"
1807
+ }
1808
+ ),
1809
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent p-5 pt-12 text-left", children: [
1810
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sf-heading block text-lg font-semibold text-white", children: c.name }),
1811
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 block text-xs text-white/80 opacity-0 transition-opacity duration-300 group-hover:opacity-100", children: "Explorar \u2192" })
1812
+ ] })
1813
+ ] }) }, c.id)) })
1814
+ ] });
1815
+ }
1816
+ function ProductRail({
1817
+ title,
1818
+ subtitle,
1819
+ eyebrow,
1820
+ filter = "all",
1821
+ limit = 4
1822
+ }) {
1823
+ const config = useStorefrontConfig();
1824
+ const ProductCardComponent = config.slots?.ProductCard ?? ProductCard;
1825
+ const { products, loading } = useProducts(
1826
+ filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
1827
+ );
1828
+ const visible = React18.useMemo(() => {
1829
+ let list = products;
1830
+ if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
1831
+ return list.slice(0, limit);
1832
+ }, [products, filter, limit]);
1833
+ if (!loading && visible.length === 0) return null;
1834
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.productRail, className: "mx-auto max-w-7xl px-4 py-14 sm:px-6", children: [
1835
+ /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { className: "mb-10 text-center", children: [
1836
+ eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
1837
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
1838
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-muted-foreground", children: subtitle })
1839
+ ] }),
1840
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: Array.from({ length: limit }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[3/4] animate-pulse bg-muted", style: { borderRadius: "var(--sf-radius-card)" } }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: visible.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxRuntime.jsx(ProductCardComponent, { product: p }) }, p.id)) }),
1841
+ /* @__PURE__ */ jsxRuntime.jsx(Reveal, { className: "mt-10 text-center", delay: 200, children: /* @__PURE__ */ jsxRuntime.jsx(
1842
+ Link,
1843
+ {
1844
+ to: config.catalogPath,
1845
+ className: "sf-cta inline-block border border-foreground/30 px-7 py-2.5 text-sm font-semibold transition-all hover:-translate-y-0.5 hover:bg-foreground hover:text-background hover:shadow",
1846
+ style: { borderRadius: "var(--sf-radius-button)" },
1847
+ children: "Ver tudo"
1848
+ }
1849
+ ) })
1850
+ ] });
1851
+ }
1852
+ function Icon({ name, className }) {
1853
+ const C = LucideIcons__namespace[name];
1854
+ return C ? /* @__PURE__ */ jsxRuntime.jsx(C, { className }) : null;
1855
+ }
1856
+ function BenefitsRow({ items }) {
1857
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.benefits, className: "border-y bg-card", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto grid max-w-7xl grid-cols-1 gap-6 px-4 py-9 sm:grid-cols-3 sm:px-6", children: items.slice(0, 4).map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { delay: i * 120, className: "flex items-center justify-center gap-3 text-center sm:text-left", children: [
1858
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, className: "h-6 w-6 text-primary" }) }),
1859
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1860
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold", children: item.title }),
1861
+ item.text && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: item.text })
1862
+ ] })
1863
+ ] }, i)) }) });
1864
+ }
1865
+ function PromoBanner({
1866
+ title,
1867
+ subtitle,
1868
+ eyebrow,
1869
+ cta,
1870
+ href,
1871
+ image,
1872
+ hue = 160
1873
+ }) {
1874
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.promoBanner, className: "group relative my-12 overflow-hidden", children: [
1875
+ /* @__PURE__ */ jsxRuntime.jsx(
1876
+ "img",
1877
+ {
1878
+ src: image ?? bannerPlaceholder(title, hue, hue + 50, 1600, 420),
1879
+ alt: title,
1880
+ className: "h-80 w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
1881
+ }
1882
+ ),
1883
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-t from-black/65 via-black/35 to-black/20 p-6 text-center text-white", children: /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { children: [
1884
+ eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-[0.25em] text-white/80", children: eyebrow }),
1885
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold drop-shadow sm:text-5xl", children: title }),
1886
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 max-w-lg text-white/90", children: subtitle }),
1887
+ cta && /* @__PURE__ */ jsxRuntime.jsx(
1888
+ Link,
1889
+ {
1890
+ to: href ?? "/catalog",
1891
+ className: "sf-cta mt-6 inline-block bg-white px-8 py-3 font-semibold text-gray-900 shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl",
1892
+ style: { borderRadius: "var(--sf-radius-button)" },
1893
+ children: cta
1894
+ }
1895
+ )
1896
+ ] }) })
1897
+ ] });
1898
+ }
1899
+ function ManifestoBlock({ title, text }) {
1900
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.manifesto, className: "mx-auto max-w-3xl px-4 py-14 text-center sm:px-6", children: [
1901
+ title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-5 text-2xl font-bold", children: title }),
1902
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sf-heading text-xl leading-relaxed text-muted-foreground sm:text-2xl", children: text })
1903
+ ] });
1904
+ }
1905
+ function Testimonials({ title, items }) {
1906
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.testimonials, className: "bg-muted/50 py-14", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-7xl px-4 sm:px-6", children: [
1907
+ title && /* @__PURE__ */ jsxRuntime.jsx(Reveal, { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-10 text-center text-3xl font-bold", children: title }) }),
1908
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-6 md:grid-cols-3", children: items.slice(0, 3).map((t, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 130, children: /* @__PURE__ */ jsxRuntime.jsxs(
1909
+ "figure",
1910
+ {
1911
+ className: "h-full bg-card p-7 shadow-sm transition-shadow hover:shadow-md",
1912
+ style: { borderRadius: "var(--sf-radius-card)" },
1913
+ children: [
1914
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 text-primary", "aria-hidden": true, children: "\u2605\u2605\u2605\u2605\u2605" }),
1915
+ /* @__PURE__ */ jsxRuntime.jsxs("blockquote", { className: "leading-relaxed", children: [
1916
+ "\u201C",
1917
+ t.quote,
1918
+ "\u201D"
1919
+ ] }),
1920
+ /* @__PURE__ */ jsxRuntime.jsxs("figcaption", { className: "mt-4 text-sm font-semibold text-muted-foreground", children: [
1921
+ "\u2014 ",
1922
+ t.author
1923
+ ] })
1924
+ ]
1925
+ }
1926
+ ) }, i)) })
1927
+ ] }) });
1928
+ }
1929
+ function NewsletterBand({ title, subtitle }) {
1930
+ const [email, setEmail] = React18.useState("");
1931
+ const [done, setDone] = React18.useState(false);
1932
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.newsletter, className: "bg-primary py-16 text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { className: "mx-auto max-w-xl px-4 text-center sm:px-6", children: [
1933
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold", children: title ?? "Receba nossas novidades" }),
1934
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 opacity-90", children: subtitle }),
1935
+ done ? /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.newsletterDone, className: "mt-6 font-medium", children: "Inscri\xE7\xE3o confirmada! \u{1F389}" }) : /* @__PURE__ */ jsxRuntime.jsxs(
1936
+ "form",
1937
+ {
1938
+ className: "mt-6 flex gap-2",
1939
+ onSubmit: (e) => {
1940
+ e.preventDefault();
1941
+ if (email.trim()) setDone(true);
1942
+ },
1943
+ children: [
1944
+ /* @__PURE__ */ jsxRuntime.jsx(
1945
+ "input",
1946
+ {
1947
+ "data-testid": TID.newsletterEmail,
1948
+ type: "email",
1949
+ required: true,
1950
+ placeholder: "Seu e-mail",
1951
+ value: email,
1952
+ onChange: (e) => setEmail(e.target.value),
1953
+ className: "flex-1 border-0 bg-white/95 px-4 py-3 text-sm text-gray-900 outline-none",
1954
+ style: { borderRadius: "var(--sf-radius-input)" }
1955
+ }
1956
+ ),
1957
+ /* @__PURE__ */ jsxRuntime.jsx(
1958
+ "button",
1959
+ {
1960
+ type: "submit",
1961
+ "data-testid": TID.newsletterSubmit,
1962
+ className: "sf-cta bg-foreground px-6 py-3 text-sm font-semibold text-background",
1963
+ style: { borderRadius: "var(--sf-radius-button)" },
1964
+ children: "Assinar"
1965
+ }
1966
+ )
1967
+ ]
1968
+ }
1969
+ )
1970
+ ] }) });
1971
+ }
1972
+
1973
+ // src/blocks.tsx
1974
+ var registered = false;
1975
+ function registerStorefrontBlocks() {
1976
+ if (registered) return;
1977
+ registered = true;
1978
+ core.registerBlock("hero", HeroSection, { source: "sdk", label: "Hero" });
1979
+ core.registerBlock("categories", CategoryShowcase, { source: "sdk", label: "Categories" });
1980
+ core.registerBlock("products", ProductRail, { source: "sdk", label: "Product rail" });
1981
+ core.registerBlock("benefits", BenefitsRow, { source: "sdk", label: "Benefits row" });
1982
+ core.registerBlock("banner", PromoBanner, { source: "sdk", label: "Promo banner" });
1983
+ core.registerBlock("manifesto", ManifestoBlock, { source: "sdk", label: "Manifesto" });
1984
+ core.registerBlock("testimonials", Testimonials, { source: "sdk", label: "Testimonials" });
1985
+ core.registerBlock("newsletter", NewsletterBand, { source: "sdk", label: "Newsletter band" });
1986
+ }
1987
+ registerStorefrontBlocks();
1988
+ function sectionsToBlocks(sections) {
1989
+ return sections.map((section, i) => {
1990
+ const { type, ...props } = section;
1991
+ return { type, id: `${type}-${i}`, props };
1992
+ });
1993
+ }
1994
+ function HomePage({ sections }) {
1995
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { children: core.renderBlocks(sectionsToBlocks(sections)) });
1996
+ }
1997
+ function useProduct(slug2) {
1998
+ const [product, setProduct] = React18.useState(null);
1999
+ const [loading, setLoading] = React18.useState(true);
2000
+ const [error, setError] = React18.useState(null);
2001
+ React18.useEffect(() => {
2002
+ let cancelled = false;
2003
+ setLoading(true);
2004
+ runtime.getShopProvider().listProducts({ slug: slug2, status: "active", limit: 1 }).then((data) => {
2005
+ if (cancelled) return;
2006
+ setProduct(data[0] ?? null);
2007
+ setError(null);
2008
+ }).catch((e) => {
2009
+ if (!cancelled) setError(e instanceof Error ? e.message : String(e));
2010
+ }).finally(() => {
2011
+ if (!cancelled) setLoading(false);
2012
+ });
2013
+ return () => {
2014
+ cancelled = true;
2015
+ };
2016
+ }, [slug2]);
2017
+ return { product, loading, error };
2018
+ }
2019
+ function ProductOptionSelector({ groups, value, onChange }) {
2020
+ if (groups.length === 0) return null;
2021
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 space-y-4", children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": TID.productOptionGroup(group.id), children: [
2022
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
2023
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: group.label }),
2024
+ value[group.label] && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-muted-foreground", children: value[group.label] })
2025
+ ] }),
2026
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: group.values.map((option) => {
2027
+ const selected = value[group.label] === option;
2028
+ return /* @__PURE__ */ jsxRuntime.jsx(
2029
+ "button",
2030
+ {
2031
+ type: "button",
2032
+ "data-testid": TID.productOptionValue(group.id, option),
2033
+ "aria-pressed": selected,
2034
+ onClick: () => onChange({ ...value, [group.label]: option }),
2035
+ className: [
2036
+ "min-h-10 rounded-full border px-4 text-sm font-medium transition-colors",
2037
+ selected ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-foreground hover:border-primary/60"
2038
+ ].join(" "),
2039
+ children: option
2040
+ },
2041
+ option
2042
+ );
2043
+ }) })
2044
+ ] }, group.id)) });
2045
+ }
2046
+ var EXCLUDED_KEYS = /* @__PURE__ */ new Set([
2047
+ "options",
2048
+ "sizes",
2049
+ "colors",
2050
+ "fayzStoreCategory",
2051
+ "source",
2052
+ "storeKey",
2053
+ "storeName",
2054
+ "tenantId",
2055
+ "slug"
2056
+ ]);
2057
+ function isInternalKey(key) {
2058
+ return EXCLUDED_KEYS.has(key) || key.startsWith("_") || key.startsWith("fayz");
2059
+ }
2060
+ var LABELS = {
2061
+ colorway: "Cor",
2062
+ color: "Cor",
2063
+ fit: "Caimento",
2064
+ material: "Material",
2065
+ vintage: "Safra",
2066
+ region: "Regi\xE3o",
2067
+ grape: "Uva",
2068
+ pairing: "Harmoniza\xE7\xE3o",
2069
+ abv: "Teor alco\xF3lico",
2070
+ volume: "Volume",
2071
+ brand: "Marca",
2072
+ origin: "Origem",
2073
+ warranty: "Garantia",
2074
+ dimensions: "Dimens\xF5es"
2075
+ };
2076
+ function humanize(key) {
2077
+ if (LABELS[key]) return LABELS[key];
2078
+ const spaced = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
2079
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
2080
+ }
2081
+ function scalarToString(value) {
2082
+ if (typeof value === "string") return value.trim() || null;
2083
+ if (typeof value === "number") return String(value);
2084
+ if (typeof value === "boolean") return value ? "Sim" : "N\xE3o";
2085
+ return null;
2086
+ }
2087
+ function ProductSpecs({ product }) {
2088
+ const rows = [];
2089
+ if (product.sku) rows.push(["SKU", product.sku]);
2090
+ if (product.categoryName) rows.push(["Categoria", product.categoryName]);
2091
+ if (product.weight != null) rows.push(["Peso", `${product.weight} ${product.weightUnit}`]);
2092
+ for (const [key, value] of Object.entries(product.metadata ?? {})) {
2093
+ if (isInternalKey(key)) continue;
2094
+ const str = scalarToString(value);
2095
+ if (str) rows.push([humanize(key), str]);
2096
+ }
2097
+ if (rows.length === 0) return null;
2098
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mt-14", children: [
2099
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-xl font-semibold tracking-tight", children: "Especifica\xE7\xF5es" }),
2100
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "mt-5 grid gap-x-12 sm:grid-cols-2", children: rows.map(([label, value]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4 border-b py-3 text-sm", children: [
2101
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: label }),
2102
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "text-right font-medium", children: value })
2103
+ ] }, label)) })
2104
+ ] });
2105
+ }
2106
+ function RelatedProducts({ product }) {
2107
+ const config = useStorefrontConfig();
2108
+ const Card = config.slots?.ProductCard ?? ProductCard;
2109
+ const opts = product.categoryId ? { categoryId: product.categoryId, status: "active", limit: 5 } : { status: "active", limit: 5 };
2110
+ const { products, loading } = useProducts(opts);
2111
+ const related = products.filter((p) => p.id !== product.id).slice(0, 4);
2112
+ if (loading || related.length === 0) return null;
2113
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mt-16", children: [
2114
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-xl font-semibold tracking-tight", children: "Voc\xEA tamb\xE9m pode gostar" }),
2115
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsxRuntime.jsx(Card, { product: p }, p.id)) })
2116
+ ] });
2117
+ }
2118
+ function ProductDetailPage({ slug: slug2 }) {
2119
+ const config = useStorefrontConfig();
2120
+ const { product, loading } = useProduct(slug2);
2121
+ const addItem = useCartStore((s) => s.addItem);
2122
+ const [qty, setQty] = React18.useState(1);
2123
+ const [selectedOptions, setSelectedOptions] = React18.useState({});
2124
+ const optionGroups = product ? getProductOptionGroups(product) : [];
2125
+ useStorefrontHead({
2126
+ title: product ? `${product.name} \u2014 ${config.name}` : config.name,
2127
+ description: product?.description
2128
+ });
2129
+ React18.useEffect(() => {
2130
+ if (!product) return;
2131
+ const defaults = Object.fromEntries(
2132
+ getProductOptionGroups(product).map((group) => [group.label, group.values[0] ?? ""])
2133
+ );
2134
+ setSelectedOptions(defaults);
2135
+ }, [product?.id]);
2136
+ if (loading) {
2137
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid animate-pulse gap-10 md:grid-cols-2", children: [
2138
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square rounded-2xl bg-muted" }),
2139
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 py-4", children: [
2140
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-3/4 rounded bg-muted" }),
2141
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-1/3 rounded bg-muted" }),
2142
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-24 rounded bg-muted" })
2143
+ ] })
2144
+ ] }) });
2145
+ }
2146
+ if (!product) {
2147
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-5xl px-4 py-20 text-center sm:px-6", children: [
2148
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground", children: "Produto n\xE3o encontrado." }),
2149
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "mt-4 inline-block text-primary underline", children: "Voltar \xE0 loja" })
2150
+ ] });
2151
+ }
2152
+ const soldOut = product.inventoryCount <= 0;
2153
+ const lowStock = !soldOut && product.inventoryCount <= 5;
2154
+ const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
2155
+ const discountPct = product.compareAtPrice && product.compareAtPrice > product.price ? Math.round((1 - product.price / product.compareAtPrice) * 100) : 0;
2156
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: [
2157
+ /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: "/", className: "mb-6 inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [
2158
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronLeft, { className: "h-4 w-4" }),
2159
+ " Continuar comprando"
2160
+ ] }),
2161
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid animate-fade-up gap-10 md:grid-cols-2", children: [
2162
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "group overflow-hidden border bg-muted", style: { borderRadius: "var(--sf-radius-card)" }, children: image && /* @__PURE__ */ jsxRuntime.jsx(
2163
+ "img",
2164
+ {
2165
+ src: image.url,
2166
+ alt: image.altText ?? product.name,
2167
+ className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
2168
+ }
2169
+ ) }),
2170
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col py-2 lg:sticky lg:top-24 lg:self-start", children: [
2171
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "mb-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
2172
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "transition-colors hover:text-foreground", children: "In\xEDcio" }),
2173
+ product.categoryName && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "/" }),
2175
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: product.categoryName })
2176
+ ] }),
2177
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "/" }),
2178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: product.name })
2179
+ ] }),
2180
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { "data-testid": TID.pdpName, className: "sf-heading text-3xl font-bold tracking-tight", children: product.name }),
2181
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
2182
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
2183
+ Price,
2184
+ {
2185
+ value: product.price,
2186
+ compareAt: product.compareAtPrice,
2187
+ testId: TID.pdpPrice,
2188
+ compareTestId: TID.pdpComparePrice
2189
+ }
2190
+ ) }),
2191
+ discountPct > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "rounded-full bg-primary/10 px-2.5 py-1 text-xs font-bold text-primary", children: [
2192
+ "\u2212",
2193
+ discountPct,
2194
+ "%"
2195
+ ] })
2196
+ ] }),
2197
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.pdpDescription, className: "mt-5 leading-relaxed text-muted-foreground", children: product.description }),
2198
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
2199
+ soldOut ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 font-medium text-muted-foreground", children: [
2200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-muted-foreground" }),
2201
+ " Sem estoque"
2202
+ ] }) : lowStock ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 font-semibold text-amber-600", children: [
2203
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 animate-pulse rounded-full bg-amber-500" }),
2204
+ "\xDAltimas unidades \u2014 s\xF3 ",
2205
+ product.inventoryCount,
2206
+ " restantes"
2207
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 font-medium text-emerald-600", children: [
2208
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-emerald-500" }),
2209
+ " Em estoque"
2210
+ ] }),
2211
+ product.sku && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
2212
+ "SKU: ",
2213
+ product.sku
2214
+ ] })
2215
+ ] }),
2216
+ /* @__PURE__ */ jsxRuntime.jsx(
2217
+ ProductOptionSelector,
2218
+ {
2219
+ groups: optionGroups,
2220
+ value: selectedOptions,
2221
+ onChange: setSelectedOptions
2222
+ }
2223
+ ),
2224
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex items-center gap-4", children: [
2225
+ /* @__PURE__ */ jsxRuntime.jsx(
2226
+ QuantityInput,
2227
+ {
2228
+ value: qty,
2229
+ onChange: setQty,
2230
+ max: soldOut ? 1 : product.inventoryCount,
2231
+ testId: TID.pdpQtyInput
2232
+ }
2233
+ ),
2234
+ /* @__PURE__ */ jsxRuntime.jsx(
2235
+ "button",
2236
+ {
2237
+ type: "button",
2238
+ "data-testid": TID.pdpAddToCart,
2239
+ disabled: soldOut,
2240
+ onClick: () => addItem(product, qty, selectedOptions),
2241
+ className: "sf-cta flex-1 bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg disabled:cursor-not-allowed disabled:opacity-40 disabled:shadow-none disabled:hover:translate-y-0",
2242
+ style: { borderRadius: "var(--sf-radius-button)" },
2243
+ children: soldOut ? "Esgotado" : "Adicionar ao carrinho"
2244
+ }
2245
+ )
2246
+ ] }),
2247
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
2248
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
2249
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Truck, { className: "h-5 w-5 text-primary" }),
2250
+ " Envio r\xE1pido"
2251
+ ] }),
2252
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
2253
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.RefreshCcw, { className: "h-5 w-5 text-primary" }),
2254
+ " Troca f\xE1cil"
2255
+ ] }),
2256
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
2257
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShieldCheck, { className: "h-5 w-5 text-primary" }),
2258
+ " Pagamento seguro"
2259
+ ] })
2260
+ ] })
2261
+ ] })
2262
+ ] }),
2263
+ /* @__PURE__ */ jsxRuntime.jsx(ProductSpecs, { product }),
2264
+ /* @__PURE__ */ jsxRuntime.jsx(RelatedProducts, { product })
2265
+ ] });
2266
+ }
2267
+ function formatDeliveryNotes(address) {
2268
+ if (!address) return void 0;
2269
+ if (address.notes?.trim()) return address.notes.trim();
2270
+ const parts = [address.street, address.city, address.zip].map((part) => part?.trim()).filter(Boolean);
2271
+ return parts.length > 0 ? `Entrega: ${parts.join(", ")}` : void 0;
2272
+ }
2273
+ async function placeStorefrontOrder({
2274
+ config,
2275
+ cart,
2276
+ session,
2277
+ customer,
2278
+ address,
2279
+ markPaid = false
2280
+ }) {
2281
+ const email = customer.email.trim().toLowerCase();
2282
+ const name = customer.name.trim();
2283
+ const provider = runtime.getShopProvider();
2284
+ let customerId = session?.customerId ?? null;
2285
+ if (!customerId || session?.email !== email) {
2286
+ const established = await establishCustomerSession(email, { name });
2287
+ customerId = established.customerId;
2288
+ }
2289
+ const order = await provider.placeOrder({
2290
+ customerId: customerId ?? void 0,
2291
+ customer: { name, email },
2292
+ currency: config.currency,
2293
+ notes: formatDeliveryNotes(address),
2294
+ discountCode: cart.discountCode ?? void 0,
2295
+ shippingTotal: selectShipping(cart, config),
2296
+ items: cart.lines.map((line) => ({
2297
+ productId: line.productId,
2298
+ quantity: line.quantity,
2299
+ optionsLabel: line.optionsLabel || void 0
2300
+ }))
2301
+ });
2302
+ if (markPaid) {
2303
+ await provider.updateOrder(order.id, { financialStatus: "paid" });
2304
+ }
2305
+ return { order, customerId: customerId ?? order.customerId ?? "" };
2306
+ }
2307
+ function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
2308
+ const [email, setEmail] = React18.useState(defaultEmail);
2309
+ const [password, setPassword] = React18.useState("");
2310
+ const [busy, setBusy] = React18.useState(false);
2311
+ const [error, setError] = React18.useState(null);
2312
+ React18.useEffect(() => {
2313
+ const onKey = (event) => {
2314
+ if (event.key === "Escape") onClose();
2315
+ };
2316
+ document.addEventListener("keydown", onKey);
2317
+ return () => document.removeEventListener("keydown", onKey);
2318
+ }, [onClose]);
2319
+ async function submit(event) {
2320
+ event.preventDefault();
2321
+ if (!email.trim() || !password) return;
2322
+ setBusy(true);
2323
+ setError(null);
2324
+ try {
2325
+ await establishCustomerSession(email, { password });
2326
+ const session = useSessionStore.getState();
2327
+ onSignedIn?.({ email: session.email, name: session.name });
2328
+ onClose();
2329
+ } catch {
2330
+ setError("N\xE3o foi poss\xEDvel entrar. Verifique seu e-mail e senha.");
2331
+ } finally {
2332
+ setBusy(false);
2333
+ }
2334
+ }
2335
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2336
+ "div",
2337
+ {
2338
+ className: "fixed inset-0 z-[60] flex items-center justify-center p-4",
2339
+ role: "dialog",
2340
+ "aria-modal": "true",
2341
+ "aria-label": "Entrar na sua conta",
2342
+ children: [
2343
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 animate-fade-in bg-foreground/40 backdrop-blur-sm", onClick: onClose }),
2344
+ /* @__PURE__ */ jsxRuntime.jsxs(
2345
+ "div",
2346
+ {
2347
+ className: "relative z-10 w-full max-w-sm animate-fade-up border bg-card p-6 shadow-2xl",
2348
+ style: { borderRadius: "var(--sf-radius-card)" },
2349
+ children: [
2350
+ /* @__PURE__ */ jsxRuntime.jsx(
2351
+ "button",
2352
+ {
2353
+ type: "button",
2354
+ onClick: onClose,
2355
+ "aria-label": "Fechar",
2356
+ className: "absolute right-4 top-4 rounded-full p-1 text-muted-foreground transition hover:bg-muted hover:text-foreground",
2357
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-5 w-5" })
2358
+ }
2359
+ ),
2360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2361
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Lock, { className: "h-4 w-4" }) }),
2362
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2363
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-lg font-semibold leading-tight", children: "Entrar na sua conta" }),
2364
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Use seus dados salvos e acompanhe pedidos." })
2365
+ ] })
2366
+ ] }),
2367
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: submit, className: "mt-5 space-y-3", children: [
2368
+ /* @__PURE__ */ jsxRuntime.jsx(
2369
+ "input",
2370
+ {
2371
+ type: "email",
2372
+ required: true,
2373
+ autoFocus: true,
2374
+ placeholder: "voce@exemplo.com",
2375
+ value: email,
2376
+ onChange: (event) => setEmail(event.target.value),
2377
+ className: "w-full border bg-background px-3 py-2.5 text-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10",
2378
+ style: { borderRadius: "var(--sf-radius-input)" }
2379
+ }
2380
+ ),
2381
+ /* @__PURE__ */ jsxRuntime.jsx(
2382
+ "input",
2383
+ {
2384
+ type: "password",
2385
+ required: true,
2386
+ placeholder: "Senha",
2387
+ value: password,
2388
+ onChange: (event) => setPassword(event.target.value),
2389
+ className: "w-full border bg-background px-3 py-2.5 text-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10",
2390
+ style: { borderRadius: "var(--sf-radius-input)" }
2391
+ }
2392
+ ),
2393
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
2394
+ /* @__PURE__ */ jsxRuntime.jsx(
2395
+ "button",
2396
+ {
2397
+ type: "submit",
2398
+ disabled: busy,
2399
+ className: "sf-cta w-full bg-primary py-3 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg disabled:opacity-60",
2400
+ style: { borderRadius: "var(--sf-radius-button)" },
2401
+ children: busy ? "Entrando\u2026" : "Entrar"
2402
+ }
2403
+ )
2404
+ ] }),
2405
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-4 text-center text-sm text-muted-foreground", children: [
2406
+ "N\xE3o tem conta?",
2407
+ " ",
2408
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/account", onClick: onClose, className: "font-semibold text-primary hover:underline", children: "Criar conta" })
2409
+ ] })
2410
+ ]
2411
+ }
2412
+ )
2413
+ ]
2414
+ }
2415
+ );
2416
+ }
2417
+ var SAVED_ADDRESSES = [
2418
+ { id: "home", label: "Casa", street: "Rua das Flores, 123", city: "S\xE3o Paulo", zip: "01310-100" }
2419
+ ];
2420
+ var SAVED_PAYMENT_METHODS = [
2421
+ { id: "visa", label: "Visa final 4242", card: "4242 4242 4242 4242", expiry: "12/29", cvc: "123" }
2422
+ ];
2423
+ var PROCESSING_STEPS = [
2424
+ { icon: LucideIcons.Lock, label: "Validando seus dados..." },
2425
+ { icon: LucideIcons.CreditCard, label: "Processando pagamento..." },
2426
+ { icon: LucideIcons.PackageCheck, label: "Confirmando seu pedido..." }
2427
+ ];
2428
+ var DEFAULT_ADDRESS = SAVED_ADDRESSES[0];
2429
+ var DEFAULT_PAYMENT = SAVED_PAYMENT_METHODS[0];
2430
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2431
+ function ProcessingOverlay({ step }) {
2432
+ const current = PROCESSING_STEPS[Math.min(step, PROCESSING_STEPS.length - 1)];
2433
+ const Icon2 = current.icon;
2434
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2435
+ "div",
2436
+ {
2437
+ "data-testid": TID.checkoutProcessing,
2438
+ className: "fixed inset-0 z-50 flex animate-fade-in flex-col items-center justify-center bg-background/95 backdrop-blur-sm",
2439
+ role: "status",
2440
+ "aria-live": "polite",
2441
+ children: [
2442
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-24 w-24 items-center justify-center", children: [
2443
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 animate-spin rounded-full border-[3px] border-muted border-t-primary", style: { animationDuration: "1.1s" } }),
2444
+ /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-9 w-9 animate-pop-in text-primary" }, step)
2445
+ ] }),
2446
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-6 animate-fade-up font-medium", children: current.label }, `label-${step}`),
2447
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-8 text-xs text-muted-foreground", children: "Pagamento seguro - n\xE3o feche esta janela" })
2448
+ ]
2449
+ }
2450
+ );
2451
+ }
2452
+ function field(label, testId, value, onChange, props) {
2453
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
2454
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: label }),
2455
+ /* @__PURE__ */ jsxRuntime.jsx(
2456
+ "input",
2457
+ {
2458
+ "data-testid": testId,
2459
+ "aria-label": label,
2460
+ value,
2461
+ onChange: (event) => onChange(event.target.value),
2462
+ className: "w-full rounded-lg border bg-background px-3.5 py-3 text-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10",
2463
+ ...props
2464
+ }
2465
+ )
2466
+ ] });
2467
+ }
2468
+ function CheckoutPage() {
2469
+ const config = useStorefrontConfig();
2470
+ const cart = useCartStore();
2471
+ const session = useSessionStore();
2472
+ const validateDiscount2 = useDiscountValidator();
2473
+ useStorefrontHead({ title: `Checkout \u2014 ${config.name}` });
2474
+ const [selectedAddressId, setSelectedAddressId] = React18.useState(DEFAULT_ADDRESS.id);
2475
+ const [selectedPaymentId, setSelectedPaymentId] = React18.useState(DEFAULT_PAYMENT.id);
2476
+ const [savedAddresses, setSavedAddresses] = React18.useState(SAVED_ADDRESSES);
2477
+ const [savedPaymentMethods, setSavedPaymentMethods] = React18.useState(SAVED_PAYMENT_METHODS);
2478
+ const [addressMode, setAddressMode] = React18.useState("saved");
2479
+ const [paymentMode, setPaymentMode] = React18.useState("saved");
2480
+ const [form, setForm] = React18.useState({
2481
+ email: session.email ?? "",
2482
+ name: session.name ?? "",
2483
+ street: DEFAULT_ADDRESS.street,
2484
+ city: DEFAULT_ADDRESS.city,
2485
+ zip: DEFAULT_ADDRESS.zip,
2486
+ card: DEFAULT_PAYMENT.card,
2487
+ expiry: DEFAULT_PAYMENT.expiry,
2488
+ cvc: DEFAULT_PAYMENT.cvc
2489
+ });
2490
+ const [error, setError] = React18.useState(null);
2491
+ const [discountCode, setDiscountCode] = React18.useState("");
2492
+ const [discountError, setDiscountError] = React18.useState(null);
2493
+ const [discountSuccess, setDiscountSuccess] = React18.useState(null);
2494
+ const [applyingDiscount, setApplyingDiscount] = React18.useState(false);
2495
+ const [placing, setPlacing] = React18.useState(false);
2496
+ const [processingStep, setProcessingStep] = React18.useState(0);
2497
+ const [showSignin, setShowSignin] = React18.useState(false);
2498
+ const subtotal = selectSubtotal(cart);
2499
+ const discountTotal = selectDiscountTotal(cart);
2500
+ const shipping = selectShipping(cart, config);
2501
+ const total = selectTotal(cart, config);
2502
+ const money = (value) => formatMoney(value, config.currency, config.locale);
2503
+ React18.useEffect(() => {
2504
+ if (cart.lines.length === 0 && !placing) navigateTo(config.catalogPath);
2505
+ }, [cart.lines.length, config.catalogPath, placing]);
2506
+ const set = (key) => (value) => setForm((current) => ({ ...current, [key]: value }));
2507
+ async function applyDiscountCode() {
2508
+ setApplyingDiscount(true);
2509
+ setDiscountError(null);
2510
+ setDiscountSuccess(null);
2511
+ try {
2512
+ const appliedCode = discountCode.trim().toUpperCase();
2513
+ const result = await validateDiscount2(discountCode);
2514
+ if (result.valid) {
2515
+ cart.applyDiscount(appliedCode, result.percent);
2516
+ setDiscountCode(appliedCode);
2517
+ setDiscountSuccess(`${appliedCode} aplicado: ${result.percent}% de desconto.`);
2518
+ toast.success("Cupom aplicado!", `${appliedCode} \u2022 ${result.percent}% de desconto`);
2519
+ } else {
2520
+ setDiscountError(result.message ?? "Cupom inv\xE1lido.");
2521
+ toast.error("Cupom inv\xE1lido", result.message ?? "Verifique o c\xF3digo e tente novamente.");
2522
+ }
2523
+ } finally {
2524
+ setApplyingDiscount(false);
2525
+ }
2526
+ }
2527
+ function clearDiscountCode() {
2528
+ cart.clearDiscount();
2529
+ setDiscountCode("");
2530
+ setDiscountSuccess(null);
2531
+ setDiscountError(null);
2532
+ }
2533
+ function applySavedAddress(address) {
2534
+ setAddressMode("saved");
2535
+ setSelectedAddressId(address.id);
2536
+ setForm((current) => ({ ...current, street: address.street, city: address.city, zip: address.zip }));
2537
+ }
2538
+ function selectAddress(addressId) {
2539
+ const address = savedAddresses.find((item) => item.id === addressId);
2540
+ if (address) applySavedAddress(address);
2541
+ }
2542
+ function addNewAddress() {
2543
+ setAddressMode("new");
2544
+ setSelectedAddressId("new");
2545
+ setForm((current) => ({ ...current, street: "", city: "", zip: "" }));
2546
+ }
2547
+ function removeAddress(addressId) {
2548
+ const nextAddresses = savedAddresses.filter((item) => item.id !== addressId);
2549
+ setSavedAddresses(nextAddresses);
2550
+ if (selectedAddressId !== addressId) return;
2551
+ const fallback = nextAddresses[0];
2552
+ if (fallback) applySavedAddress(fallback);
2553
+ else addNewAddress();
2554
+ }
2555
+ function applySavedPayment(method) {
2556
+ setPaymentMode("saved");
2557
+ setSelectedPaymentId(method.id);
2558
+ setForm((current) => ({ ...current, card: method.card, expiry: method.expiry, cvc: method.cvc }));
2559
+ }
2560
+ function selectPayment(paymentId) {
2561
+ const method = savedPaymentMethods.find((item) => item.id === paymentId);
2562
+ if (method) applySavedPayment(method);
2563
+ }
2564
+ function addNewPayment() {
2565
+ setPaymentMode("new");
2566
+ setSelectedPaymentId("new");
2567
+ setForm((current) => ({ ...current, card: "", expiry: "", cvc: "" }));
2568
+ }
2569
+ function removePayment(paymentId) {
2570
+ const nextMethods = savedPaymentMethods.filter((item) => item.id !== paymentId);
2571
+ setSavedPaymentMethods(nextMethods);
2572
+ if (selectedPaymentId !== paymentId) return;
2573
+ const fallback = nextMethods[0];
2574
+ if (fallback) applySavedPayment(fallback);
2575
+ else addNewPayment();
2576
+ }
2577
+ function validate() {
2578
+ setError(null);
2579
+ if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) {
2580
+ setError("Informe um e-mail v\xE1lido.");
2581
+ return false;
2582
+ }
2583
+ if (!form.name.trim()) {
2584
+ setError("Informe seu nome completo.");
2585
+ return false;
2586
+ }
2587
+ if (!form.street.trim() || !form.city.trim() || !form.zip.trim()) {
2588
+ setError("Preencha ou selecione o endere\xE7o de entrega.");
2589
+ return false;
2590
+ }
2591
+ if (!/^\d{16}$/.test(form.card.replace(/\s+/g, "")) || !form.expiry.trim() || !form.cvc.trim()) {
2592
+ setError("Selecione ou preencha um m\xE9todo de pagamento v\xE1lido.");
2593
+ return false;
2594
+ }
2595
+ return true;
2596
+ }
2597
+ async function placeOrder() {
2598
+ if (!validate()) return;
2599
+ setPlacing(true);
2600
+ setProcessingStep(0);
2601
+ const stepMs = prefersReducedMotion() ? 120 : 600;
2602
+ const stepTimers = [
2603
+ setTimeout(() => setProcessingStep(1), stepMs),
2604
+ setTimeout(() => setProcessingStep(2), stepMs * 2)
2605
+ ];
2606
+ const minDuration = sleep(stepMs * 2);
2607
+ try {
2608
+ const { order } = await placeStorefrontOrder({
2609
+ config,
2610
+ cart,
2611
+ session,
2612
+ customer: { email: form.email, name: form.name },
2613
+ address: { street: form.street, city: form.city, zip: form.zip },
2614
+ // Demo only: mock mode instantly approves. Real Pix/Mercado Pago (M4)
2615
+ // leaves the order pending until the payment webhook confirms.
2616
+ markPaid: config.payments.mode === "mock"
2617
+ });
2618
+ await minDuration;
2619
+ cart.clear();
2620
+ navigateTo(`/order/${order.id}`);
2621
+ } catch (error2) {
2622
+ setError(error2 instanceof Error ? error2.message : "N\xE3o foi poss\xEDvel concluir o pedido.");
2623
+ setPlacing(false);
2624
+ } finally {
2625
+ stepTimers.forEach(clearTimeout);
2626
+ }
2627
+ }
2628
+ const primaryCta = placing ? "Processando..." : `Comprar agora - ${money(total)}`;
2629
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "min-h-screen bg-background", children: [
2630
+ placing && /* @__PURE__ */ jsxRuntime.jsx(ProcessingOverlay, { step: processingStep }),
2631
+ showSignin && !session.email && /* @__PURE__ */ jsxRuntime.jsx(
2632
+ SignInModal,
2633
+ {
2634
+ defaultEmail: form.email,
2635
+ onClose: () => setShowSignin(false),
2636
+ onSignedIn: (signed) => setForm((current) => ({ ...current, email: signed.email ?? current.email, name: signed.name || current.name }))
2637
+ }
2638
+ ),
2639
+ /* @__PURE__ */ jsxRuntime.jsx("header", { className: "border-b bg-background", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-6 sm:px-6", children: [
2640
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: config.catalogPath, className: "text-sm text-muted-foreground hover:text-foreground", children: "Voltar \xE0 loja" }),
2641
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2642
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sf-heading text-2xl font-bold tracking-tight", children: config.logo ?? config.name }),
2643
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-center gap-2 text-xs text-muted-foreground", children: [
2644
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Lock, { className: "h-3.5 w-3.5" }),
2645
+ "Checkout seguro"
2646
+ ] })
2647
+ ] }),
2648
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "mailto:suporte@example.com", className: "text-sm text-muted-foreground hover:text-foreground", children: "Ajuda" })
2649
+ ] }) }),
2650
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid max-w-6xl gap-8 px-4 py-8 sm:px-6 lg:grid-cols-5 lg:px-6", children: [
2651
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "lg:col-span-3 lg:pr-10", children: [
2652
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "mb-8 flex flex-wrap items-center gap-2 text-sm text-muted-foreground", "aria-label": "Checkout steps", children: [
2653
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Informa\xE7\xF5es" }),
2654
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "/" }),
2655
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Entrega" }),
2656
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "/" }),
2657
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Pagamento" })
2658
+ ] }),
2659
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-8", children: [
2660
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
2661
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 flex items-center justify-between gap-4", children: [
2662
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold tracking-tight", children: "Informa\xE7\xF5es de contato" }),
2663
+ session.email ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-muted-foreground", children: [
2664
+ "Conectado como ",
2665
+ session.email
2666
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2667
+ "button",
2668
+ {
2669
+ type: "button",
2670
+ onClick: () => setShowSignin((value) => !value),
2671
+ className: "text-sm font-medium text-primary hover:underline",
2672
+ children: "J\xE1 tem conta? Entrar"
2673
+ }
2674
+ )
2675
+ ] }),
2676
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
2677
+ field("E-mail", TID.checkoutEmail, form.email, set("email"), {
2678
+ type: "email",
2679
+ placeholder: "E-mail ou telefone"
2680
+ }),
2681
+ field("Nome completo", TID.checkoutName, form.name, set("name"), {
2682
+ placeholder: "Nome completo"
2683
+ })
2684
+ ] })
2685
+ ] }),
2686
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
2687
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Endere\xE7o de entrega" }),
2688
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 grid gap-3", children: [
2689
+ savedAddresses.map((address) => /* @__PURE__ */ jsxRuntime.jsxs(
2690
+ "div",
2691
+ {
2692
+ className: `group relative rounded-lg border p-3 pr-11 text-left text-sm transition hover:bg-muted/40 ${selectedAddressId === address.id ? "border-primary bg-primary/5" : "border-border bg-background"}`,
2693
+ children: [
2694
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => selectAddress(address.id), className: "block w-full text-left", children: [
2695
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
2696
+ address.label,
2697
+ addressMode === "saved" && selectedAddressId === address.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
2698
+ ] }),
2699
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: address.street }),
2700
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block text-muted-foreground", children: [
2701
+ address.city,
2702
+ " - ",
2703
+ address.zip
2704
+ ] })
2705
+ ] }),
2706
+ /* @__PURE__ */ jsxRuntime.jsx(
2707
+ "button",
2708
+ {
2709
+ type: "button",
2710
+ onClick: () => removeAddress(address.id),
2711
+ className: "absolute right-3 top-3 inline-flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground opacity-0 transition hover:bg-destructive/10 hover:text-destructive focus:opacity-100 group-hover:opacity-100",
2712
+ "aria-label": `Remover endere\xE7o ${address.label}`,
2713
+ title: "Remover endere\xE7o",
2714
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-4 w-4" })
2715
+ }
2716
+ )
2717
+ ]
2718
+ },
2719
+ address.id
2720
+ )),
2721
+ /* @__PURE__ */ jsxRuntime.jsxs(
2722
+ "button",
2723
+ {
2724
+ type: "button",
2725
+ onClick: addNewAddress,
2726
+ className: `rounded-lg border border-dashed p-3 text-left text-sm transition hover:bg-muted/40 ${addressMode === "new" ? "border-primary bg-primary/5" : "border-border bg-muted/20"}`,
2727
+ children: [
2728
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-semibold text-primary", children: "+ Adicionar novo endere\xE7o" }),
2729
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: "Preencher outro endere\xE7o para esta compra" })
2730
+ ]
2731
+ }
2732
+ )
2733
+ ] }),
2734
+ addressMode === "new" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
2735
+ field("Endere\xE7o", TID.checkoutStreet, form.street, set("street"), { placeholder: "Endere\xE7o" }),
2736
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-[1fr_160px]", children: [
2737
+ field("Cidade", TID.checkoutCity, form.city, set("city"), { placeholder: "Cidade" }),
2738
+ field("CEP", TID.checkoutZip, form.zip, set("zip"), { placeholder: "CEP" })
2739
+ ] })
2740
+ ] })
2741
+ ] }),
2742
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
2743
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Pagamento" }),
2744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 grid gap-3", children: [
2745
+ savedPaymentMethods.map((method) => /* @__PURE__ */ jsxRuntime.jsxs(
2746
+ "div",
2747
+ {
2748
+ className: `group relative rounded-lg border p-3 pr-11 text-left text-sm transition hover:bg-muted/40 ${selectedPaymentId === method.id ? "border-primary bg-primary/5" : "border-border bg-background"}`,
2749
+ children: [
2750
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => selectPayment(method.id), className: "block w-full text-left", children: [
2751
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
2752
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
2753
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-4 w-4" }),
2754
+ method.label
2755
+ ] }),
2756
+ paymentMode === "saved" && selectedPaymentId === method.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
2757
+ ] }),
2758
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1 block text-muted-foreground", children: [
2759
+ "Expira em ",
2760
+ method.expiry
2761
+ ] })
2762
+ ] }),
2763
+ /* @__PURE__ */ jsxRuntime.jsx(
2764
+ "button",
2765
+ {
2766
+ type: "button",
2767
+ onClick: () => removePayment(method.id),
2768
+ className: "absolute right-3 top-3 inline-flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground opacity-0 transition hover:bg-destructive/10 hover:text-destructive focus:opacity-100 group-hover:opacity-100",
2769
+ "aria-label": `Remover cart\xE3o ${method.label}`,
2770
+ title: "Remover cart\xE3o",
2771
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-4 w-4" })
2772
+ }
2773
+ )
2774
+ ]
2775
+ },
2776
+ method.id
2777
+ )),
2778
+ /* @__PURE__ */ jsxRuntime.jsxs(
2779
+ "button",
2780
+ {
2781
+ type: "button",
2782
+ onClick: addNewPayment,
2783
+ className: `rounded-lg border border-dashed p-3 text-left text-sm transition hover:bg-muted/40 ${paymentMode === "new" ? "border-primary bg-primary/5" : "border-border bg-muted/20"}`,
2784
+ children: [
2785
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2 font-semibold text-primary", children: [
2786
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-4 w-4" }),
2787
+ "+ Adicionar novo cart\xE3o"
2788
+ ] }),
2789
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: "Usar outro cart\xE3o nesta compra" })
2790
+ ]
2791
+ }
2792
+ )
2793
+ ] }),
2794
+ paymentMode === "new" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
2795
+ field("N\xFAmero do cart\xE3o", TID.checkoutCard, form.card, set("card"), {
2796
+ placeholder: "N\xFAmero do cart\xE3o",
2797
+ inputMode: "numeric"
2798
+ }),
2799
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
2800
+ field("Validade", TID.checkoutExpiry, form.expiry, set("expiry"), { placeholder: "MM/AA" }),
2801
+ field("CVC", TID.checkoutCvc, form.cvc, set("cvc"), { placeholder: "CVC", inputMode: "numeric" })
2802
+ ] })
2803
+ ] }),
2804
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Pagamento de demonstra\xE7\xE3o - nenhuma cobran\xE7a \xE9 feita." })
2805
+ ] }),
2806
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.checkoutError, className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: error })
2807
+ ] }),
2808
+ /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "mt-10 flex flex-wrap gap-4 border-t pt-5 text-xs text-muted-foreground", children: [
2809
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#/privacy", className: "hover:text-foreground", children: "Privacidade" }),
2810
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#/terms", className: "hover:text-foreground", children: "Termos" }),
2811
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#/refunds", className: "hover:text-foreground", children: "Trocas e devolu\xE7\xF5es" })
2812
+ ] })
2813
+ ] }),
2814
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "rounded-xl border bg-muted/20 p-5 lg:sticky lg:top-6 lg:col-span-2 lg:self-start", children: [
2815
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Resumo do pedido" }),
2816
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-4", children: cart.lines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex gap-3 text-sm", children: [
2817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-16 w-16 flex-none overflow-hidden rounded-lg border bg-background", children: [
2818
+ line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: line.imageUrl, alt: line.name, className: "h-full w-full object-cover" }),
2819
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-muted-foreground px-1.5 text-[10px] font-bold text-background", children: line.quantity })
2820
+ ] }),
2821
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
2822
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate font-medium", children: line.name }),
2823
+ line.sku && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: line.sku }),
2824
+ line.optionsLabel && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: line.optionsLabel })
2825
+ ] }),
2826
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-price": (line.unitPrice * line.quantity).toFixed(2), children: money(line.unitPrice * line.quantity) })
2827
+ ] }, line.lineId ?? line.productId)) }),
2828
+ config.features.discounts && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 space-y-2 border-t pt-5", children: [
2829
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2830
+ /* @__PURE__ */ jsxRuntime.jsx(
2831
+ "input",
2832
+ {
2833
+ "data-testid": TID.discountInput,
2834
+ type: "text",
2835
+ placeholder: "Cupom de desconto",
2836
+ value: discountCode,
2837
+ onChange: (event) => {
2838
+ setDiscountCode(event.target.value);
2839
+ setDiscountSuccess(null);
2840
+ setDiscountError(null);
2841
+ },
2842
+ className: "min-w-0 flex-1 rounded-lg border bg-background px-3 py-2 text-sm uppercase"
2843
+ }
2844
+ ),
2845
+ /* @__PURE__ */ jsxRuntime.jsx(
2846
+ "button",
2847
+ {
2848
+ type: "button",
2849
+ "data-testid": TID.discountApply,
2850
+ onClick: applyDiscountCode,
2851
+ disabled: applyingDiscount || !discountCode.trim(),
2852
+ className: "rounded-lg border px-3 py-2 text-sm font-semibold hover:bg-muted disabled:opacity-50",
2853
+ children: "Aplicar"
2854
+ }
2855
+ )
2856
+ ] }),
2857
+ discountError && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.discountError, className: "text-xs text-destructive", children: discountError }),
2858
+ discountSuccess && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold text-emerald-700", children: discountSuccess })
2859
+ ] }),
2860
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "mt-8 space-y-3 border-t pt-6 text-sm", children: [
2861
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2862
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
2863
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-price": subtotal.toFixed(2), children: money(subtotal) })
2864
+ ] }),
2865
+ discountTotal > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-emerald-700", "data-testid": TID.discountRow, children: [
2866
+ /* @__PURE__ */ jsxRuntime.jsxs("dt", { children: [
2867
+ "Desconto",
2868
+ " ",
2869
+ /* @__PURE__ */ jsxRuntime.jsxs(
2870
+ "button",
2871
+ {
2872
+ type: "button",
2873
+ onClick: clearDiscountCode,
2874
+ className: "text-xs text-muted-foreground underline",
2875
+ children: [
2876
+ "(",
2877
+ cart.discountCode,
2878
+ " \xD7)"
2879
+ ]
2880
+ }
2881
+ )
2882
+ ] }),
2883
+ /* @__PURE__ */ jsxRuntime.jsxs("dd", { "data-price": discountTotal.toFixed(2), children: [
2884
+ "-",
2885
+ money(discountTotal)
2886
+ ] })
2887
+ ] }),
2888
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
2889
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Entrega" }),
2890
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-price": shipping.toFixed(2), children: shipping === 0 ? "Gr\xE1tis" : money(shipping) })
2891
+ ] }),
2892
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between border-t pt-5 text-lg font-bold", children: [
2893
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
2894
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-price": total.toFixed(2), children: money(total) })
2895
+ ] })
2896
+ ] }),
2897
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex items-center gap-2 text-xs text-muted-foreground", children: [
2898
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShieldCheck, { className: "h-4 w-4 text-primary" }),
2899
+ "Compra protegida e dados criptografados."
2900
+ ] }),
2901
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid gap-3 border-t pt-5", children: [
2902
+ /* @__PURE__ */ jsxRuntime.jsx(
2903
+ "button",
2904
+ {
2905
+ type: "button",
2906
+ "data-testid": TID.placeOrder,
2907
+ onClick: placeOrder,
2908
+ disabled: placing,
2909
+ className: "rounded-lg bg-primary px-6 py-4 text-sm font-bold text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
2910
+ children: primaryCta
2911
+ }
2912
+ ),
2913
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: config.catalogPath, className: "text-center text-sm text-muted-foreground hover:text-foreground", children: "Voltar ao carrinho" })
2914
+ ] })
2915
+ ] })
2916
+ ] })
2917
+ ] });
2918
+ }
2919
+ var menuItems = [
2920
+ { id: "profile", label: "Perfil", icon: LucideIcons.User },
2921
+ { id: "orders", label: "Meus pedidos", icon: LucideIcons.Package },
2922
+ { id: "addresses", label: "Endere\xE7os", icon: LucideIcons.MapPin },
2923
+ { id: "payments", label: "Pagamentos", icon: LucideIcons.CreditCard }
2924
+ ];
2925
+ function CustomerAccountShell({ title, subtitle, active, onSelect, children }) {
2926
+ const config = useStorefrontConfig();
2927
+ const session = useSessionStore();
2928
+ const displayName = session.name || session.email?.split("@")[0] || "Cliente";
2929
+ const displayEmail = session.email || "Conta criada no checkout";
2930
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { "data-testid": TID.customerAccountShell, className: "mx-auto max-w-6xl px-4 py-8 sm:px-6", children: [
2931
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-4", children: [
2932
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2933
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-muted-foreground", children: config.name }),
2934
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
2935
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: subtitle })
2936
+ ] }),
2937
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: config.catalogPath, className: "rounded-lg border px-4 py-2 text-sm font-semibold hover:bg-muted", children: "Voltar \xE0 loja" })
2938
+ ] }),
2939
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6 lg:grid-cols-[260px_minmax(0,1fr)]", children: [
2940
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "h-fit rounded-xl border bg-card p-4", children: [
2941
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 border-b pb-4", children: [
2942
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 font-bold text-primary", children: displayName.slice(0, 1).toUpperCase() }),
2943
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2944
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate font-semibold", children: displayName }),
2945
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-muted-foreground", children: displayEmail })
2946
+ ] })
2947
+ ] }),
2948
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { "data-testid": TID.customerAccountMenu, className: "mt-4 space-y-1", "aria-label": "\xC1rea do cliente", children: menuItems.map((item) => {
2949
+ const Icon2 = item.icon;
2950
+ const selected = item.id === active;
2951
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2952
+ "button",
2953
+ {
2954
+ type: "button",
2955
+ "aria-current": selected ? "page" : void 0,
2956
+ onClick: () => onSelect?.(item.id),
2957
+ className: `flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold transition ${selected ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
2958
+ children: [
2959
+ /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-4 w-4" }),
2960
+ item.label
2961
+ ]
2962
+ },
2963
+ item.id
2964
+ );
2965
+ }) }),
2966
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 rounded-lg bg-muted/40 p-3 text-xs text-muted-foreground", children: [
2967
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 font-semibold text-foreground", children: [
2968
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShieldCheck, { className: "h-4 w-4 text-primary" }),
2969
+ "Conta protegida"
2970
+ ] }),
2971
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1", children: "Pedidos, pagamentos e endere\xE7os ficam organizados para pr\xF3ximas compras." })
2972
+ ] }),
2973
+ /* @__PURE__ */ jsxRuntime.jsxs(
2974
+ "button",
2975
+ {
2976
+ type: "button",
2977
+ "data-testid": TID.signout,
2978
+ onClick: () => void signOutCustomer(),
2979
+ className: "mt-4 flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-muted-foreground transition hover:bg-muted hover:text-foreground",
2980
+ children: [
2981
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.LogOut, { className: "h-4 w-4" }),
2982
+ "Sair"
2983
+ ]
2984
+ }
2985
+ )
2986
+ ] }),
2987
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "min-w-0", children })
2988
+ ] })
2989
+ ] });
2990
+ }
2991
+
2992
+ // src/order-status.ts
2993
+ var FINANCIAL = {
2994
+ pending: { label: "Pendente", tone: "pending" },
2995
+ paid: { label: "Pago", tone: "positive" },
2996
+ partially_paid: { label: "Parcialmente pago", tone: "pending" },
2997
+ partially_refunded: { label: "Parcialmente reembolsado", tone: "negative" },
2998
+ refunded: { label: "Reembolsado", tone: "negative" },
2999
+ voided: { label: "Cancelado", tone: "negative" }
3000
+ };
3001
+ var TONE_CLASS = {
3002
+ positive: "bg-emerald-100 text-emerald-800",
3003
+ pending: "bg-amber-100 text-amber-800",
3004
+ negative: "bg-rose-100 text-rose-800"
3005
+ };
3006
+ function financialStatusBadge(status) {
3007
+ const entry = FINANCIAL[status] ?? { label: status, tone: "pending" };
3008
+ return { label: entry.label, className: TONE_CLASS[entry.tone] };
3009
+ }
3010
+ function isPaid(status) {
3011
+ return status === "paid" || status === "partially_paid";
3012
+ }
3013
+ function isRefunded(status) {
3014
+ return status === "refunded" || status === "partially_refunded";
3015
+ }
3016
+ function orderTrackingSteps(order) {
3017
+ const received = {
3018
+ id: "received",
3019
+ label: "Pedido recebido",
3020
+ description: `#${order.orderNumber}`,
3021
+ state: "complete",
3022
+ icon: LucideIcons.Check
3023
+ };
3024
+ if (order.status === "cancelled" || order.financialStatus === "voided") {
3025
+ return [
3026
+ received,
3027
+ { id: "payment", label: "Pedido cancelado", description: "Este pedido foi cancelado", state: "cancelled", icon: LucideIcons.XCircle }
3028
+ ];
3029
+ }
3030
+ if (isRefunded(order.financialStatus)) {
3031
+ return [
3032
+ received,
3033
+ { id: "payment", label: "Pagamento aprovado", description: "Compra confirmada", state: "complete", icon: LucideIcons.Check },
3034
+ {
3035
+ id: "refund",
3036
+ label: order.financialStatus === "refunded" ? "Reembolsado" : "Reembolso parcial",
3037
+ description: "Valor devolvido ao cliente",
3038
+ state: "cancelled",
3039
+ icon: LucideIcons.RotateCcw
3040
+ }
3041
+ ];
3042
+ }
3043
+ const paid = isPaid(order.financialStatus);
3044
+ const partiallyFulfilled = order.fulfillmentStatus === "partially_fulfilled";
3045
+ const fulfilled = order.fulfillmentStatus === "fulfilled";
3046
+ return [
3047
+ received,
3048
+ {
3049
+ id: "payment",
3050
+ label: paid ? "Pagamento aprovado" : "Aguardando pagamento",
3051
+ description: paid ? "Compra confirmada" : "Processando",
3052
+ state: paid ? "complete" : "current",
3053
+ icon: LucideIcons.Check
3054
+ },
3055
+ {
3056
+ id: "preparing",
3057
+ label: "Preparando envio",
3058
+ description: paid ? "Separando produtos" : "Come\xE7a ap\xF3s o pagamento",
3059
+ state: fulfilled || partiallyFulfilled ? "complete" : paid ? "current" : "pending",
3060
+ icon: LucideIcons.PackageCheck
3061
+ },
3062
+ {
3063
+ id: "delivery",
3064
+ label: fulfilled ? "Pedido enviado" : partiallyFulfilled ? "Envio parcial" : "Entrega",
3065
+ description: fulfilled ? "A caminho do cliente" : partiallyFulfilled ? "Parte do pedido saiu" : "Pr\xF3xima etapa",
3066
+ state: fulfilled ? "complete" : partiallyFulfilled ? "current" : "pending",
3067
+ icon: fulfilled || partiallyFulfilled ? LucideIcons.Truck : LucideIcons.Clock
3068
+ }
3069
+ ];
3070
+ }
3071
+ var GRID_COLS = {
3072
+ 2: "sm:grid-cols-2",
3073
+ 3: "sm:grid-cols-3",
3074
+ 4: "sm:grid-cols-4"
3075
+ };
3076
+ function OrderTrackingTimeline({ order, compact = false }) {
3077
+ const steps = orderTrackingSteps(order);
3078
+ return /* @__PURE__ */ jsxRuntime.jsx(
3079
+ "ol",
3080
+ {
3081
+ "data-testid": TID.orderTracking,
3082
+ className: `grid ${compact ? "gap-2" : "gap-3"} ${GRID_COLS[steps.length] ?? "sm:grid-cols-4"}`,
3083
+ "aria-label": "Acompanhamento do pedido",
3084
+ children: steps.map((step) => {
3085
+ const Icon2 = step.icon;
3086
+ const cancelled = step.state === "cancelled";
3087
+ const active = step.state !== "pending";
3088
+ return /* @__PURE__ */ jsxRuntime.jsx(
3089
+ "li",
3090
+ {
3091
+ "data-testid": TID.orderTrackingStep(step.id),
3092
+ "data-state": step.state,
3093
+ className: [
3094
+ "relative min-w-0 rounded-xl border p-3",
3095
+ cancelled ? "border-rose-200 bg-rose-50 text-rose-700" : active ? "border-primary/35 bg-primary/5" : "bg-muted/20 text-muted-foreground",
3096
+ compact ? "text-xs" : "text-sm"
3097
+ ].join(" "),
3098
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
3099
+ /* @__PURE__ */ jsxRuntime.jsx(
3100
+ "span",
3101
+ {
3102
+ className: [
3103
+ "grid h-7 w-7 shrink-0 place-items-center rounded-full border",
3104
+ cancelled ? "border-rose-300 bg-rose-500 text-white" : active ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/30 bg-background"
3105
+ ].join(" "),
3106
+ "aria-hidden": true,
3107
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-3.5 w-3.5" })
3108
+ }
3109
+ ),
3110
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
3111
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate font-semibold text-foreground", children: step.label }),
3112
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 block truncate text-muted-foreground", children: step.description })
3113
+ ] })
3114
+ ] })
3115
+ },
3116
+ step.id
3117
+ );
3118
+ })
3119
+ }
3120
+ );
3121
+ }
3122
+ var CONFETTI_COLORS = ["hsl(var(--primary))", "#f59e0b", "#10b981", "#3b82f6", "#ec4899"];
3123
+ function SuccessMark() {
3124
+ const reduced = prefersReducedMotion();
3125
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mx-auto h-24 w-24", children: [
3126
+ !reduced && Array.from({ length: 14 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(
3127
+ "span",
3128
+ {
3129
+ "aria-hidden": true,
3130
+ className: "absolute h-2 w-2 animate-confetti-fall rounded-sm",
3131
+ style: {
3132
+ left: `${i * 7.3 % 100}%`,
3133
+ top: "-4px",
3134
+ backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
3135
+ animationDelay: `${200 + i % 7 * 90}ms`
3136
+ }
3137
+ },
3138
+ i
3139
+ )),
3140
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `absolute inset-0 rounded-full bg-emerald-100 ${reduced ? "" : "animate-pop-in"}` }),
3141
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
3142
+ "path",
3143
+ {
3144
+ d: "M10 25 L20 35 L38 14",
3145
+ fill: "none",
3146
+ stroke: "rgb(5 150 105)",
3147
+ strokeWidth: "5",
3148
+ strokeLinecap: "round",
3149
+ strokeLinejoin: "round",
3150
+ strokeDasharray: "48",
3151
+ className: reduced ? "" : "animate-draw-check"
3152
+ }
3153
+ ) })
3154
+ ] });
3155
+ }
3156
+ function OrderConfirmationPage({ orderId }) {
3157
+ const config = useStorefrontConfig();
3158
+ const [order, setOrder] = React18.useState(null);
3159
+ const [loading, setLoading] = React18.useState(true);
3160
+ const money = (v) => formatMoney(v, config.currency, config.locale);
3161
+ useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
3162
+ React18.useEffect(() => {
3163
+ let cancelled = false;
3164
+ runtime.getShopProvider().getOrder(orderId).then((o) => {
3165
+ if (!cancelled) setOrder(o);
3166
+ }).finally(() => {
3167
+ if (!cancelled) setLoading(false);
3168
+ });
3169
+ return () => {
3170
+ cancelled = true;
3171
+ };
3172
+ }, [orderId]);
3173
+ if (loading) {
3174
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { className: "mx-auto max-w-2xl px-4 py-20 text-center text-muted-foreground", children: "Carregando\u2026" });
3175
+ }
3176
+ if (!order) {
3177
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-2xl px-4 py-20 text-center", children: [
3178
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Pedido n\xE3o encontrado." }),
3179
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "mt-4 inline-block text-primary underline", children: "Voltar \xE0 loja" })
3180
+ ] });
3181
+ }
3182
+ return /* @__PURE__ */ jsxRuntime.jsx(
3183
+ CustomerAccountShell,
3184
+ {
3185
+ title: "Detalhe do pedido",
3186
+ subtitle: "Compra confirmada e vinculada \xE0 sua conta.",
3187
+ active: "orders",
3188
+ children: /* @__PURE__ */ jsxRuntime.jsxs("article", { "data-testid": TID.orderDetail, className: "animate-fade-up rounded-xl border bg-card p-6 sm:p-8", children: [
3189
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
3190
+ /* @__PURE__ */ jsxRuntime.jsx(SuccessMark, {}),
3191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center sm:text-left", children: [
3192
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: "Pedido confirmado!" }),
3193
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-muted-foreground", children: [
3194
+ "Obrigado pela compra, ",
3195
+ order.customerName ?? "cliente",
3196
+ ". Enviamos a confirma\xE7\xE3o para ",
3197
+ order.customerEmail,
3198
+ "."
3199
+ ] }),
3200
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-4 text-lg", children: [
3201
+ "Pedido ",
3202
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-testid": TID.orderNumber, className: "font-semibold", children: [
3203
+ "#",
3204
+ order.orderNumber
3205
+ ] }),
3206
+ " ",
3207
+ /* @__PURE__ */ jsxRuntime.jsx(
3208
+ "span",
3209
+ {
3210
+ "data-testid": TID.orderStatus,
3211
+ className: `ml-2 inline-flex rounded-full px-2.5 py-0.5 text-sm font-medium ${financialStatusBadge(order.financialStatus).className}`,
3212
+ children: financialStatusBadge(order.financialStatus).label
3213
+ }
3214
+ )
3215
+ ] })
3216
+ ] })
3217
+ ] }),
3218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid gap-4 rounded-xl border bg-muted/20 p-4 text-sm sm:grid-cols-3", children: [
3219
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3220
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Cliente" }),
3221
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-semibold", children: order.customerName ?? "Cliente" }),
3222
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: order.customerEmail })
3223
+ ] }),
3224
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3225
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Entrega" }),
3226
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-semibold", children: order.fulfillmentStatus === "fulfilled" ? "Enviado" : "Preparando envio" }),
3227
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: order.notes ?? "Endere\xE7o salvo no pedido" })
3228
+ ] }),
3229
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3230
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Pagamento" }),
3231
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-semibold", children: financialStatusBadge(order.financialStatus).label }),
3232
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: "Processado no checkout" })
3233
+ ] })
3234
+ ] }),
3235
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6", children: [
3236
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase text-muted-foreground", children: "Acompanhamento" }),
3237
+ /* @__PURE__ */ jsxRuntime.jsx(OrderTrackingTimeline, { order })
3238
+ ] }),
3239
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-8 space-y-3 text-left", children: order.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-3 text-sm", children: [
3240
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
3241
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
3242
+ item.name,
3243
+ " ",
3244
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
3245
+ "\xD7 ",
3246
+ item.quantity
3247
+ ] })
3248
+ ] }),
3249
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-price": item.total.toFixed(2), children: money(item.total) })
3250
+ ] }, item.id)) }),
3251
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "mt-4 space-y-1.5 border-t pt-4 text-left text-sm", children: [
3252
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3253
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
3254
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-price": order.subtotal.toFixed(2), children: money(order.subtotal) })
3255
+ ] }),
3256
+ order.discountTotal > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-emerald-700", children: [
3257
+ /* @__PURE__ */ jsxRuntime.jsxs("dt", { children: [
3258
+ "Desconto ",
3259
+ order.discountCode ? `(${order.discountCode})` : ""
3260
+ ] }),
3261
+ /* @__PURE__ */ jsxRuntime.jsxs("dd", { "data-price": order.discountTotal.toFixed(2), children: [
3262
+ "\u2212",
3263
+ money(order.discountTotal)
3264
+ ] })
3265
+ ] }),
3266
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3267
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Frete" }),
3268
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-price": order.shippingTotal.toFixed(2), children: order.shippingTotal === 0 ? "Gr\xE1tis" : money(order.shippingTotal) })
3269
+ ] }),
3270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between border-t pt-2 text-base font-semibold", children: [
3271
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Total" }),
3272
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": TID.orderTotal, "data-price": order.total.toFixed(2), children: money(order.total) })
3273
+ ] })
3274
+ ] }),
3275
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex flex-wrap gap-3", children: [
3276
+ /* @__PURE__ */ jsxRuntime.jsx(
3277
+ Link,
3278
+ {
3279
+ to: "/account",
3280
+ "data-testid": TID.viewPurchases,
3281
+ className: "rounded-xl bg-primary px-5 py-2.5 font-semibold text-primary-foreground hover:opacity-90",
3282
+ children: "Minhas compras"
3283
+ }
3284
+ ),
3285
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "rounded-xl border px-5 py-2.5 font-semibold hover:bg-muted", children: "Continuar comprando" })
3286
+ ] })
3287
+ ] })
3288
+ }
3289
+ );
3290
+ }
3291
+ function useMyOrders() {
3292
+ const customerId = useSessionStore((s) => s.customerId);
3293
+ const email = useSessionStore((s) => s.email);
3294
+ const [orders, setOrders] = React18.useState([]);
3295
+ const [loading, setLoading] = React18.useState(true);
3296
+ const [tick, setTick] = React18.useState(0);
3297
+ const refresh = React18.useCallback(() => setTick((t) => t + 1), []);
3298
+ React18.useEffect(() => {
3299
+ let cancelled = false;
3300
+ if (!customerId && !email) {
3301
+ setOrders([]);
3302
+ setLoading(false);
3303
+ return;
3304
+ }
3305
+ setLoading(true);
3306
+ const query = customerId ? { customerId, limit: 50 } : { customerEmail: email, limit: 50 };
3307
+ runtime.getShopProvider().listOrders(query).then((data) => {
3308
+ if (!cancelled) setOrders(data);
3309
+ }).catch(() => {
3310
+ }).finally(() => {
3311
+ if (!cancelled) setLoading(false);
3312
+ });
3313
+ return () => {
3314
+ cancelled = true;
3315
+ };
3316
+ }, [customerId, email, tick]);
3317
+ return { orders, loading, refresh };
3318
+ }
3319
+ function AuthForm() {
3320
+ const [mode, setMode] = React18.useState("signin");
3321
+ const [email, setEmail] = React18.useState("");
3322
+ const [password, setPassword] = React18.useState("");
3323
+ const [name, setName] = React18.useState("");
3324
+ const [busy, setBusy] = React18.useState(false);
3325
+ const [error, setError] = React18.useState(null);
3326
+ async function submit(e) {
3327
+ e.preventDefault();
3328
+ setError(null);
3329
+ if (!email.trim() || !password) return;
3330
+ if (mode === "signup" && !name.trim()) {
3331
+ setError("Informe seu nome.");
3332
+ return;
3333
+ }
3334
+ setBusy(true);
3335
+ try {
3336
+ if (mode === "signup") await signUpCustomer(email, password, name.trim());
3337
+ else await establishCustomerSession(email, { password });
3338
+ } catch (err) {
3339
+ setError(err instanceof Error ? err.message : "N\xE3o foi poss\xEDvel entrar. Verifique seus dados.");
3340
+ } finally {
3341
+ setBusy(false);
3342
+ }
3343
+ }
3344
+ const tab = (id, label, testId) => /* @__PURE__ */ jsxRuntime.jsx(
3345
+ "button",
3346
+ {
3347
+ type: "button",
3348
+ "data-testid": testId,
3349
+ onClick: () => {
3350
+ setMode(id);
3351
+ setError(null);
3352
+ },
3353
+ className: `flex-1 border-b-2 pb-2.5 text-sm font-semibold transition-colors ${mode === id ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
3354
+ children: label
3355
+ }
3356
+ );
3357
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-sm animate-fade-up border bg-card p-7", style: { borderRadius: "var(--sf-radius-card)" }, children: [
3358
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-xl font-semibold", children: "Minha conta" }),
3359
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Acompanhe pedidos e acelere seus pr\xF3ximos checkouts." }),
3360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 flex gap-2", children: [
3361
+ tab("signin", "Entrar", TID.authTabSignin),
3362
+ tab("signup", "Criar conta", TID.authTabSignup)
3363
+ ] }),
3364
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "mt-5 space-y-3", onSubmit: submit, children: [
3365
+ mode === "signup" && /* @__PURE__ */ jsxRuntime.jsx(
3366
+ "input",
3367
+ {
3368
+ "data-testid": TID.signinName,
3369
+ type: "text",
3370
+ required: true,
3371
+ placeholder: "Nome completo",
3372
+ value: name,
3373
+ onChange: (e) => setName(e.target.value),
3374
+ className: "w-full border bg-background px-3 py-2.5 text-sm",
3375
+ style: { borderRadius: "var(--sf-radius-input)" }
3376
+ }
3377
+ ),
3378
+ /* @__PURE__ */ jsxRuntime.jsx(
3379
+ "input",
3380
+ {
3381
+ "data-testid": TID.signinEmail,
3382
+ type: "email",
3383
+ required: true,
3384
+ placeholder: "voce@exemplo.com",
3385
+ value: email,
3386
+ onChange: (e) => setEmail(e.target.value),
3387
+ className: "w-full border bg-background px-3 py-2.5 text-sm",
3388
+ style: { borderRadius: "var(--sf-radius-input)" }
3389
+ }
3390
+ ),
3391
+ /* @__PURE__ */ jsxRuntime.jsx(
3392
+ "input",
3393
+ {
3394
+ "data-testid": TID.signinPassword,
3395
+ type: "password",
3396
+ required: true,
3397
+ placeholder: "Senha",
3398
+ value: password,
3399
+ onChange: (e) => setPassword(e.target.value),
3400
+ className: "w-full border bg-background px-3 py-2.5 text-sm",
3401
+ style: { borderRadius: "var(--sf-radius-input)" }
3402
+ }
3403
+ ),
3404
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.authError, className: "text-sm text-destructive", children: error }),
3405
+ /* @__PURE__ */ jsxRuntime.jsx(
3406
+ "button",
3407
+ {
3408
+ type: "submit",
3409
+ "data-testid": TID.signinSubmit,
3410
+ disabled: busy,
3411
+ className: "sf-cta w-full bg-primary py-3 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg disabled:opacity-60",
3412
+ style: { borderRadius: "var(--sf-radius-button)" },
3413
+ children: busy ? "Entrando\u2026" : mode === "signup" ? "Criar conta" : "Entrar"
3414
+ }
3415
+ )
3416
+ ] })
3417
+ ] });
3418
+ }
3419
+ function OrdersPanel() {
3420
+ const config = useStorefrontConfig();
3421
+ const { orders, loading } = useMyOrders();
3422
+ const money = (v) => formatMoney(v, config.currency, config.locale);
3423
+ if (loading) return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-16 text-center text-muted-foreground", children: "Carregando\u2026" });
3424
+ if (orders.length === 0) {
3425
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": TID.purchasesEmpty, className: "rounded-2xl border bg-card py-16 text-center", children: [
3426
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Package, { className: "mx-auto h-10 w-10 text-muted-foreground" }),
3427
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-muted-foreground", children: "Voc\xEA ainda n\xE3o tem pedidos." }),
3428
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "mt-4 inline-block text-primary underline", children: "Explorar a loja" })
3429
+ ] });
3430
+ }
3431
+ return /* @__PURE__ */ jsxRuntime.jsx("ul", { "data-testid": TID.purchasesList, className: "space-y-4", children: orders.map((order) => /* @__PURE__ */ jsxRuntime.jsxs(
3432
+ "li",
3433
+ {
3434
+ "data-testid": TID.purchaseItem,
3435
+ "data-order-number": order.orderNumber,
3436
+ className: "rounded-2xl border bg-card p-5",
3437
+ children: [
3438
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
3439
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold", children: [
3440
+ "Pedido #",
3441
+ order.orderNumber
3442
+ ] }),
3443
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex rounded-full px-2.5 py-0.5 text-xs font-medium ${financialStatusBadge(order.financialStatus).className}`, children: financialStatusBadge(order.financialStatus).label })
3444
+ ] }),
3445
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: new Date(order.createdAt).toLocaleDateString(config.locale) }),
3446
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(OrderTrackingTimeline, { order, compact: true }) }),
3447
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-3 flex flex-wrap gap-2", children: order.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2 rounded-lg border px-2 py-1 text-xs", children: [
3448
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.imageUrl, alt: item.name, className: "h-6 w-6 rounded object-cover" }),
3449
+ item.name,
3450
+ " \xD7 ",
3451
+ item.quantity
3452
+ ] }, item.id)) }),
3453
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center justify-between border-t pt-3 text-sm", children: [
3454
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: `/order/${order.id}`, className: "font-medium text-primary hover:underline", children: "Ver detalhes" }),
3455
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3456
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-2 text-muted-foreground", children: "Total" }),
3457
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", "data-price": order.total.toFixed(2), children: money(order.total) })
3458
+ ] })
3459
+ ] })
3460
+ ]
3461
+ },
3462
+ order.id
3463
+ )) });
3464
+ }
3465
+ function ProfilePanel() {
3466
+ const session = useSessionStore();
3467
+ const [name, setName] = React18.useState(session.name ?? "");
3468
+ const [saving, setSaving] = React18.useState(false);
3469
+ const [saved, setSaved] = React18.useState(false);
3470
+ async function save(e) {
3471
+ e.preventDefault();
3472
+ const trimmed = name.trim();
3473
+ if (!trimmed) return;
3474
+ setSaving(true);
3475
+ setSaved(false);
3476
+ try {
3477
+ if (session.customerId) {
3478
+ const parts = trimmed.split(/\s+/).filter(Boolean);
3479
+ await runtime.getShopProvider().updateCustomer(session.customerId, {
3480
+ firstName: parts[0] ?? trimmed,
3481
+ lastName: parts.slice(1).join(" ")
3482
+ });
3483
+ }
3484
+ useSessionStore.setState({ name: trimmed });
3485
+ setSaved(true);
3486
+ } finally {
3487
+ setSaving(false);
3488
+ }
3489
+ }
3490
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: save, className: "max-w-md rounded-2xl border bg-card p-6", children: [
3491
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-lg font-semibold", children: "Perfil" }),
3492
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Seus dados para os pr\xF3ximos checkouts." }),
3493
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-5 block text-sm font-medium", children: [
3494
+ "Nome completo",
3495
+ /* @__PURE__ */ jsxRuntime.jsx(
3496
+ "input",
3497
+ {
3498
+ value: name,
3499
+ onChange: (e) => {
3500
+ setName(e.target.value);
3501
+ setSaved(false);
3502
+ },
3503
+ className: "mt-1 w-full rounded-lg border bg-background px-3 py-2.5 text-sm",
3504
+ placeholder: "Seu nome"
3505
+ }
3506
+ )
3507
+ ] }),
3508
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-4 block text-sm font-medium", children: [
3509
+ "E-mail",
3510
+ /* @__PURE__ */ jsxRuntime.jsx(
3511
+ "input",
3512
+ {
3513
+ value: session.email ?? "",
3514
+ readOnly: true,
3515
+ className: "mt-1 w-full cursor-not-allowed rounded-lg border bg-muted/40 px-3 py-2.5 text-sm text-muted-foreground"
3516
+ }
3517
+ )
3518
+ ] }),
3519
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 flex items-center gap-3", children: [
3520
+ /* @__PURE__ */ jsxRuntime.jsx(
3521
+ "button",
3522
+ {
3523
+ type: "submit",
3524
+ disabled: saving,
3525
+ className: "sf-cta rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground disabled:opacity-60",
3526
+ children: saving ? "Salvando\u2026" : "Salvar"
3527
+ }
3528
+ ),
3529
+ saved && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-emerald-700", children: "Salvo" })
3530
+ ] })
3531
+ ] });
3532
+ }
3533
+ function AddressesPanel() {
3534
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border bg-card p-6", children: [
3535
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3536
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.MapPin, { className: "h-5 w-5 text-primary" }),
3537
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-lg font-semibold", children: "Endere\xE7os" })
3538
+ ] }),
3539
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Voc\xEA informa o endere\xE7o de entrega no checkout. Em breve ser\xE1 poss\xEDvel salvar endere\xE7os para reutilizar nas pr\xF3ximas compras." }),
3540
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "mt-4 inline-block text-sm font-semibold text-primary underline", children: "Continuar comprando" })
3541
+ ] });
3542
+ }
3543
+ function PaymentsPanel() {
3544
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl border bg-card p-6", children: [
3545
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3546
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-5 w-5 text-primary" }),
3547
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-lg font-semibold", children: "Pagamentos" })
3548
+ ] }),
3549
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Os pagamentos s\xE3o processados com seguran\xE7a no checkout \u2014 nenhum dado de pagamento fica salvo nesta conta." }),
3550
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex items-center gap-2 rounded-lg bg-muted/40 px-3 py-2.5 text-sm", children: [
3551
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.QrCode, { className: "h-4 w-4 text-primary" }),
3552
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Pix e cart\xE3o via Mercado Pago no momento da compra." })
3553
+ ] })
3554
+ ] });
3555
+ }
3556
+ var SECTION_SUBTITLE = {
3557
+ profile: "Seus dados de contato.",
3558
+ orders: "Acompanhe seus pedidos e entregas.",
3559
+ addresses: "Endere\xE7os de entrega.",
3560
+ payments: "Como voc\xEA paga na loja."
3561
+ };
3562
+ function MyPurchasesPage() {
3563
+ const config = useStorefrontConfig();
3564
+ const session = useSessionStore();
3565
+ const [section, setSection] = React18.useState("orders");
3566
+ useStorefrontHead({ title: `Minha conta \u2014 ${config.name}` });
3567
+ if (!session.email) {
3568
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(AuthForm, {}) });
3569
+ }
3570
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3571
+ CustomerAccountShell,
3572
+ {
3573
+ title: "Minha conta",
3574
+ subtitle: SECTION_SUBTITLE[section],
3575
+ active: section,
3576
+ onSelect: setSection,
3577
+ children: [
3578
+ section === "profile" && /* @__PURE__ */ jsxRuntime.jsx(ProfilePanel, {}),
3579
+ section === "orders" && /* @__PURE__ */ jsxRuntime.jsx(OrdersPanel, {}),
3580
+ section === "addresses" && /* @__PURE__ */ jsxRuntime.jsx(AddressesPanel, {}),
3581
+ section === "payments" && /* @__PURE__ */ jsxRuntime.jsx(PaymentsPanel, {})
3582
+ ]
3583
+ }
3584
+ );
3585
+ }
3586
+ function NotFoundPage() {
3587
+ const config = useStorefrontConfig();
3588
+ useStorefrontHead({ title: `P\xE1gina n\xE3o encontrada \u2014 ${config.name}` });
3589
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto flex min-h-[60vh] max-w-2xl flex-col items-center justify-center px-4 py-16 text-center", children: [
3590
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sf-heading text-5xl font-bold tracking-tight text-primary", children: "404" }),
3591
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "mt-3 text-xl font-semibold", children: "P\xE1gina n\xE3o encontrada" }),
3592
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "O endere\xE7o que voc\xEA tentou acessar n\xE3o existe ou foi movido." }),
3593
+ /* @__PURE__ */ jsxRuntime.jsx(
3594
+ Link,
3595
+ {
3596
+ to: config.catalogPath,
3597
+ className: "mt-6 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground transition hover:opacity-90",
3598
+ children: "Ir para a loja"
3599
+ }
3600
+ )
3601
+ ] });
3602
+ }
3603
+ var TITLES = {
3604
+ privacy: "Pol\xEDtica de Privacidade",
3605
+ terms: "Termos de Uso",
3606
+ returns: "Trocas e Devolu\xE7\xF5es"
3607
+ };
3608
+ function PolicyPage({ kind }) {
3609
+ const config = useStorefrontConfig();
3610
+ const title = TITLES[kind];
3611
+ const content = config.legal?.[kind];
3612
+ const email = config.footer?.contact?.email;
3613
+ useStorefrontHead({ title: `${title} \u2014 ${config.name}` });
3614
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: [
3615
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: config.catalogPath, className: "text-sm text-muted-foreground transition-colors hover:text-foreground", children: "\u2190 Voltar \xE0 loja" }),
3616
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "sf-heading mt-4 text-3xl font-bold tracking-tight", children: title }),
3617
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 space-y-4 leading-relaxed text-muted-foreground", children: content ? content.split("\n").filter((line) => line.trim()).map((paragraph, i) => /* @__PURE__ */ jsxRuntime.jsx("p", { children: paragraph }, i)) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3618
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "O conte\xFAdo desta pol\xEDtica ainda ser\xE1 publicado pela loja." }),
3619
+ email && /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
3620
+ "Em caso de d\xFAvidas, fale com a gente em",
3621
+ " ",
3622
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${email}`, className: "text-primary underline", children: email }),
3623
+ "."
3624
+ ] })
3625
+ ] }) })
3626
+ ] });
3627
+ }
3628
+ var StorefrontErrorBoundary = class extends React18__default.default.Component {
3629
+ constructor() {
3630
+ super(...arguments);
3631
+ this.state = { error: null };
3632
+ }
3633
+ static getDerivedStateFromError(error) {
3634
+ return { error };
3635
+ }
3636
+ componentDidCatch(error, info) {
3637
+ console.error("[storefront] render error:", error, info.componentStack);
3638
+ }
3639
+ render() {
3640
+ if (!this.state.error) return this.props.children;
3641
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[60vh] flex-col items-center justify-center gap-4 px-4 py-16 text-center", children: [
3642
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "sf-heading text-xl font-semibold", children: "Algo deu errado" }),
3643
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-md text-sm text-muted-foreground", children: "Ocorreu um erro ao carregar esta parte da loja. Tente recarregar a p\xE1gina." }),
3644
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-center gap-3", children: [
3645
+ /* @__PURE__ */ jsxRuntime.jsx(
3646
+ "button",
3647
+ {
3648
+ type: "button",
3649
+ onClick: () => window.location.reload(),
3650
+ className: "rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground transition hover:opacity-90",
3651
+ children: "Recarregar"
3652
+ }
3653
+ ),
3654
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "#/", className: "rounded-lg border px-5 py-2.5 text-sm font-semibold transition hover:bg-muted", children: "Voltar ao in\xEDcio" })
3655
+ ] })
3656
+ ] });
3657
+ }
3658
+ };
3659
+ var ICON = {
3660
+ success: LucideIcons.Check,
3661
+ error: LucideIcons.AlertCircle,
3662
+ info: LucideIcons.Info
3663
+ };
3664
+ var ACCENT = {
3665
+ success: "bg-emerald-500/10 text-emerald-600",
3666
+ error: "bg-destructive/10 text-destructive",
3667
+ info: "bg-primary/10 text-primary"
3668
+ };
3669
+ function ToastCard({ toast: toast2 }) {
3670
+ const dismiss = useToastStore((s) => s.dismiss);
3671
+ const Icon2 = ICON[toast2.variant];
3672
+ React18.useEffect(() => {
3673
+ const timer = window.setTimeout(() => dismiss(toast2.id), toast2.durationMs);
3674
+ return () => window.clearTimeout(timer);
3675
+ }, [toast2.id, toast2.durationMs, dismiss]);
3676
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3677
+ "div",
3678
+ {
3679
+ role: "status",
3680
+ className: "pointer-events-auto flex w-80 max-w-[calc(100vw-2rem)] animate-slide-in-from-right items-start gap-3 rounded-xl border bg-card p-3.5 shadow-lg",
3681
+ children: [
3682
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `grid h-8 w-8 shrink-0 place-items-center rounded-full ${ACCENT[toast2.variant]}`, children: /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-4 w-4" }) }),
3683
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
3684
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold leading-tight", children: toast2.title }),
3685
+ toast2.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: toast2.description })
3686
+ ] }),
3687
+ /* @__PURE__ */ jsxRuntime.jsx(
3688
+ "button",
3689
+ {
3690
+ type: "button",
3691
+ "aria-label": "Fechar",
3692
+ onClick: () => dismiss(toast2.id),
3693
+ className: "text-muted-foreground transition-colors hover:text-foreground",
3694
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.X, { className: "h-4 w-4" })
3695
+ }
3696
+ )
3697
+ ]
3698
+ }
3699
+ );
3700
+ }
3701
+ function Toaster() {
3702
+ const toasts = useToastStore((s) => s.toasts);
3703
+ if (toasts.length === 0) return null;
3704
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none fixed bottom-4 right-4 z-[70] flex flex-col gap-2", children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { toast: t }, t.id)) });
3705
+ }
3706
+ function getCustomRouteMatch(config, path) {
3707
+ for (const route of config.routes ?? []) {
3708
+ const params = matchPath(route.path, path);
3709
+ if (params) return { route, params };
3710
+ }
3711
+ return null;
3712
+ }
3713
+ function isFocusedRoute(config, path) {
3714
+ const custom = getCustomRouteMatch(config, path);
3715
+ if (custom) return custom.route.chrome === "focused" || custom.route.kind === "checkout";
3716
+ return Boolean(matchPath("/checkout", path));
3717
+ }
3718
+ function RouteSwitch({ path }) {
3719
+ const config = useStorefrontConfig();
3720
+ const custom = getCustomRouteMatch(config, path);
3721
+ if (custom) {
3722
+ const CustomRoute = custom.route.component;
3723
+ return /* @__PURE__ */ jsxRuntime.jsx(CustomRoute, { path, params: custom.params, config });
3724
+ }
3725
+ const product = matchPath("/product/:slug", path);
3726
+ if (product?.slug) return /* @__PURE__ */ jsxRuntime.jsx(ProductDetailPage, { slug: product.slug });
3727
+ const order = matchPath("/order/:id", path);
3728
+ if (order?.id) return /* @__PURE__ */ jsxRuntime.jsx(OrderConfirmationPage, { orderId: order.id });
3729
+ if (matchPath("/checkout", path)) return /* @__PURE__ */ jsxRuntime.jsx(CheckoutPage, {});
3730
+ if (matchPath("/account", path)) return /* @__PURE__ */ jsxRuntime.jsx(MyPurchasesPage, {});
3731
+ if (matchPath("/catalog", path)) return /* @__PURE__ */ jsxRuntime.jsx(CatalogPage, {});
3732
+ if (matchPath("/privacy", path)) return /* @__PURE__ */ jsxRuntime.jsx(PolicyPage, { kind: "privacy" });
3733
+ if (matchPath("/terms", path)) return /* @__PURE__ */ jsxRuntime.jsx(PolicyPage, { kind: "terms" });
3734
+ if (matchPath("/refunds", path)) return /* @__PURE__ */ jsxRuntime.jsx(PolicyPage, { kind: "returns" });
3735
+ if (path === "/" || path === "") {
3736
+ return config.home ? /* @__PURE__ */ jsxRuntime.jsx(HomePage, { sections: config.home.sections }) : /* @__PURE__ */ jsxRuntime.jsx(CatalogPage, {});
3737
+ }
3738
+ return /* @__PURE__ */ jsxRuntime.jsx(NotFoundPage, {});
3739
+ }
3740
+ function storefrontDiscountToSeed(discount) {
3741
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
3742
+ return {
3743
+ id: `cfg-discount-${discount.code.trim().toLowerCase()}`,
3744
+ tenantId: "mock",
3745
+ title: discount.title ?? `${discount.percent}% off`,
3746
+ code: discount.code.trim().toUpperCase(),
3747
+ type: "percentage",
3748
+ method: "code",
3749
+ value: discount.percent,
3750
+ usageLimit: null,
3751
+ oncePerCustomer: false,
3752
+ startsAt: ts,
3753
+ endsAt: null,
3754
+ status: "active",
3755
+ timesUsed: 0,
3756
+ createdAt: ts,
3757
+ updatedAt: ts
3758
+ };
3759
+ }
3760
+ function buildMockSeed(config) {
3761
+ const configDiscounts = (config.discounts ?? []).map(storefrontDiscountToSeed);
3762
+ if (!config.catalog && configDiscounts.length === 0) return void 0;
3763
+ const catalogDiscounts = (config.catalog?.discounts ?? []).filter(
3764
+ (cd) => !configDiscounts.some((cfg) => (cd.code ?? "").toUpperCase() === cfg.code)
3765
+ );
3766
+ return {
3767
+ ...config.catalog,
3768
+ discounts: [...catalogDiscounts, ...configDiscounts]
3769
+ };
3770
+ }
3771
+ function initStorefrontRuntime(config) {
3772
+ initCustomerAuth(resolveAuthAdapter(config.auth?.adapter));
3773
+ if (config.provider) {
3774
+ runtime.setShopProvider(config.provider);
3775
+ } else {
3776
+ runtime.setShopProvider(mock.createMockShopProvider(buildMockSeed(config)));
3777
+ }
3778
+ if (config.supabaseUrl || config.supabaseAnonKey) {
3779
+ console.warn(
3780
+ "@fayz-ai/shop: supabaseUrl/supabaseAnonKey are legacy fields. Pass an explicit provider/adapter or use the Fayz SDK broker path."
3781
+ );
3782
+ }
3783
+ }
3784
+ function StorefrontShell() {
3785
+ const config = useStorefrontConfig();
3786
+ const path = useHashPath();
3787
+ const focused = isFocusedRoute(config, path);
3788
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-background text-foreground", children: [
3789
+ config.theme && /* @__PURE__ */ jsxRuntime.jsx(StorefrontThemeStyle, { theme: config.theme }),
3790
+ !focused && /* @__PURE__ */ jsxRuntime.jsx(StorefrontHeader, {}),
3791
+ /* @__PURE__ */ jsxRuntime.jsx(StorefrontErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteSwitch, { path }) }, path),
3792
+ !focused && /* @__PURE__ */ jsxRuntime.jsx(CartDrawer, {}),
3793
+ !focused && /* @__PURE__ */ jsxRuntime.jsx(StorefrontFooter, {}),
3794
+ /* @__PURE__ */ jsxRuntime.jsx(Toaster, {})
3795
+ ] });
3796
+ }
3797
+ function createStorefrontApp(config) {
3798
+ const resolved = resolveConfig(config);
3799
+ initStorefrontRuntime(config);
3800
+ function StorefrontApp() {
3801
+ return /* @__PURE__ */ jsxRuntime.jsx(StorefrontConfigProvider, { value: resolved, children: /* @__PURE__ */ jsxRuntime.jsx(StorefrontShell, {}) });
3802
+ }
3803
+ StorefrontApp.displayName = "StorefrontApp";
3804
+ return StorefrontApp;
3805
+ }
3806
+ var runtimeConfigRegistry = /* @__PURE__ */ new Map();
3807
+ function slug(name) {
3808
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
3809
+ }
3810
+ function defineStorefront(config) {
3811
+ const id = slug(config.name);
3812
+ runtimeConfigRegistry.set(id, config);
3813
+ const backendProvider = config.backend?.provider ?? (config.provider ? "custom" : "mock");
3814
+ return core.defineApp({
3815
+ id,
3816
+ name: config.name,
3817
+ backend: {
3818
+ provider: backendProvider,
3819
+ url: config.backend?.url
3820
+ },
3821
+ locale: {
3822
+ default: config.locale ?? "pt-BR",
3823
+ supported: [config.locale ?? "pt-BR"],
3824
+ currency: config.currency ?? "BRL"
3825
+ },
3826
+ theme: config.theme,
3827
+ surfaces: {
3828
+ storefront: {
3829
+ scaffold: "storefront",
3830
+ options: {
3831
+ announcement: config.announcement,
3832
+ home: config.home,
3833
+ nav: config.nav,
3834
+ footer: config.footer,
3835
+ shipping: config.shipping,
3836
+ features: config.features,
3837
+ catalog: config.catalog
3838
+ }
3839
+ }
3840
+ }
3841
+ });
3842
+ }
3843
+ function manifestToStorefrontConfig(manifest, surfaceName) {
3844
+ const runtimeConfig = runtimeConfigRegistry.get(manifest.id);
3845
+ const surface = manifest.surfaces[surfaceName];
3846
+ const o = surface?.options ?? {};
3847
+ return {
3848
+ ...runtimeConfig,
3849
+ name: manifest.name,
3850
+ currency: manifest.locale?.currency,
3851
+ locale: manifest.locale?.default,
3852
+ theme: manifest.theme,
3853
+ announcement: o.announcement,
3854
+ home: o.home,
3855
+ nav: o.nav,
3856
+ footer: o.footer,
3857
+ shipping: o.shipping,
3858
+ features: o.features,
3859
+ catalog: o.catalog,
3860
+ backend: {
3861
+ provider: manifest.backend?.provider,
3862
+ url: manifest.backend?.url
3863
+ },
3864
+ slots: runtimeConfig?.slots,
3865
+ routes: runtimeConfig?.routes,
3866
+ provider: runtimeConfig?.provider,
3867
+ auth: runtimeConfig?.auth,
3868
+ logo: runtimeConfig?.logo
3869
+ };
3870
+ }
3871
+ function StorefrontScaffold({ manifest, surface }) {
3872
+ const config = React18__default.default.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
3873
+ const resolved = React18__default.default.useMemo(() => resolveConfig(config), [config]);
3874
+ const inited = React18__default.default.useRef(false);
3875
+ if (!inited.current) {
3876
+ initStorefrontRuntime(config);
3877
+ inited.current = true;
3878
+ }
3879
+ return /* @__PURE__ */ jsxRuntime.jsx(StorefrontConfigProvider, { value: resolved, children: /* @__PURE__ */ jsxRuntime.jsx(StorefrontShell, {}) });
3880
+ }
3881
+ StorefrontScaffold.displayName = "StorefrontScaffold";
3882
+ core.registerScaffold("storefront", StorefrontScaffold, { source: "sdk", label: "Storefront" });
3883
+
3884
+ // src/presets.ts
3885
+ var mareTemplate = {
3886
+ id: "mare",
3887
+ inspiration: "Nuvemshop Rio",
3888
+ announcement: "FRETE GR\xC1TIS EM COMPRAS ACIMA DE R$ 300",
3889
+ theme: {
3890
+ name: "mare",
3891
+ colors: {
3892
+ background: "40 14% 93%",
3893
+ foreground: "90 8% 38%",
3894
+ primary: "42 32% 61%",
3895
+ primaryForeground: "40 30% 12%",
3896
+ card: "40 20% 97%",
3897
+ cardForeground: "90 10% 25%",
3898
+ muted: "40 12% 88%",
3899
+ mutedForeground: "90 6% 50%",
3900
+ border: "40 10% 82%",
3901
+ announcementBackground: "90 10% 30%",
3902
+ announcementForeground: "40 20% 95%"
3903
+ },
3904
+ font: { heading: "Plus Jakarta Sans", body: "Plus Jakarta Sans" },
3905
+ radius: "soft",
3906
+ header: { variant: "centered" },
3907
+ productCard: { style: "editorial", imageAspect: "portrait" }
3908
+ },
3909
+ home: (storeName, images) => ({
3910
+ sections: [
3911
+ {
3912
+ type: "hero",
3913
+ variant: "banner",
3914
+ height: "tall",
3915
+ slides: [
3916
+ { title: "Feito para durar", subtitle: `A nova cole\xE7\xE3o ${storeName} chegou \u2014 pe\xE7as atemporais em materiais honestos.`, cta: "Ver cole\xE7\xE3o", hue: 75, image: images?.hero?.[0] }
3917
+ ]
3918
+ },
3919
+ { type: "categories", style: "bubbles" },
3920
+ { type: "products", title: "Destaques", filter: "all", limit: 4 },
3921
+ { type: "banner", title: "At\xE9 30% off", eyebrow: "por tempo limitado", subtitle: "Pe\xE7as selecionadas da esta\xE7\xE3o", cta: "Aproveitar", hue: 40, image: images?.banners?.[0] },
3922
+ { type: "products", title: "Promo\xE7\xF5es", filter: "sale", limit: 4 },
3923
+ { type: "products", title: "Lan\xE7amentos", filter: "new", limit: 4 },
3924
+ { type: "newsletter", title: "Assine nossa newsletter", subtitle: "Novidades e ofertas exclusivas, sem spam." }
3925
+ ]
3926
+ })
3927
+ };
3928
+ var sertaoTemplate = {
3929
+ id: "sertao",
3930
+ inspiration: "Nuvemshop Uyuni",
3931
+ theme: {
3932
+ name: "sertao",
3933
+ colors: {
3934
+ background: "30 100% 98%",
3935
+ foreground: "28 42% 32%",
3936
+ primary: "28 42% 32%",
3937
+ primaryForeground: "30 100% 97%",
3938
+ card: "32 60% 96%",
3939
+ cardForeground: "28 40% 26%",
3940
+ muted: "32 40% 92%",
3941
+ mutedForeground: "28 22% 48%",
3942
+ border: "28 30% 80%"
3943
+ },
3944
+ font: { heading: "Marcellus", body: "Marcellus", fallback: "serif" },
3945
+ radius: "none",
3946
+ header: { variant: "minimal", uppercaseNav: true },
3947
+ productCard: { style: "editorial", imageAspect: "portrait" },
3948
+ uppercaseButtons: true
3949
+ },
3950
+ home: (storeName, images) => ({
3951
+ sections: [
3952
+ {
3953
+ type: "hero",
3954
+ variant: "banner",
3955
+ height: "tall",
3956
+ slides: [
3957
+ { title: "Essenciais naturais", subtitle: "Conhe\xE7a nossa cole\xE7\xE3o", cta: "Ver mais", hue: 28, boxed: true, image: images?.hero?.[0] }
3958
+ ]
3959
+ },
3960
+ {
3961
+ type: "manifesto",
3962
+ text: `Num mundo de pressa e excesso, ${storeName} escolhe o caminho lento: ingredientes honestos, produ\xE7\xE3o pequena e cuidado em cada detalhe.`
3963
+ },
3964
+ { type: "categories", style: "tiles" },
3965
+ { type: "products", title: "ESSENCIAIS", filter: "all", limit: 4 },
3966
+ {
3967
+ type: "testimonials",
3968
+ title: "Nossos clientes",
3969
+ items: [
3970
+ { quote: "Qualidade impec\xE1vel e um cheiro que abra\xE7a.", author: "Helena M." },
3971
+ { quote: "Compro h\xE1 dois anos e nunca me decepcionou.", author: "Rafael T." },
3972
+ { quote: "Entrega r\xE1pida e embalagem linda, d\xE1 gosto de presentear.", author: "Cl\xE1udia P." }
3973
+ ]
3974
+ },
3975
+ { type: "products", title: "NOVIDADES", filter: "new", limit: 4 },
3976
+ {
3977
+ type: "benefits",
3978
+ items: [
3979
+ { icon: "Truck", title: "Frete para todo o Brasil", text: "Envio em at\xE9 48h" },
3980
+ { icon: "Leaf", title: "100% vegano", text: "Sem testes em animais" },
3981
+ { icon: "RotateCcw", title: "Troca f\xE1cil", text: "30 dias para trocar" }
3982
+ ]
3983
+ },
3984
+ { type: "newsletter", title: "NEWSLETTER", subtitle: "Hist\xF3rias, lan\xE7amentos e cuidado \u2014 direto no seu e-mail." }
3985
+ ]
3986
+ })
3987
+ };
3988
+ var voltTemplate = {
3989
+ id: "volt",
3990
+ inspiration: "Nuvemshop Bras\xEDlia",
3991
+ announcement: "CUPOM BEMVINDO10 \u2014 10% DE DESCONTO NA PRIMEIRA COMPRA",
3992
+ theme: {
3993
+ name: "volt",
3994
+ colors: {
3995
+ background: "0 0% 100%",
3996
+ foreground: "0 0% 7%",
3997
+ primary: "341 100% 45%",
3998
+ primaryForeground: "0 0% 100%",
3999
+ card: "0 0% 98%",
4000
+ cardForeground: "0 0% 10%",
4001
+ muted: "0 0% 94%",
4002
+ mutedForeground: "0 0% 40%",
4003
+ border: "0 0% 88%",
4004
+ headerBackground: "0 0% 6%",
4005
+ headerForeground: "0 0% 98%",
4006
+ announcementBackground: "341 100% 45%",
4007
+ announcementForeground: "0 0% 100%"
4008
+ },
4009
+ font: { heading: "Chakra Petch", body: "Rubik" },
4010
+ radius: "soft",
4011
+ header: { variant: "search", uppercaseNav: true },
4012
+ productCard: { style: "card", imageAspect: "square" }
4013
+ },
4014
+ home: (storeName, images) => ({
4015
+ sections: [
4016
+ {
4017
+ type: "hero",
4018
+ variant: "split",
4019
+ slides: [
4020
+ { title: "Tecnologia", subtitle: "O melhor do hardware e dos acess\xF3rios", hue: 250, image: images?.hero?.[0] },
4021
+ { title: "Ofertas", subtitle: `Descontos semanais ${storeName}`, hue: 341, href: "/catalog", image: images?.hero?.[1] }
4022
+ ]
4023
+ },
4024
+ {
4025
+ type: "benefits",
4026
+ items: [
4027
+ { icon: "CreditCard", title: "3x sem juros", text: "Em todo o site" },
4028
+ { icon: "Truck", title: "Envio em 24h", text: "Para todo o Brasil" },
4029
+ { icon: "ShieldCheck", title: "Compra segura", text: "Seus dados protegidos" }
4030
+ ]
4031
+ },
4032
+ { type: "products", title: "NOVIDADES", filter: "new", limit: 4 },
4033
+ { type: "banner", title: "DESCONTOS SEMANAIS", eyebrow: "toda sexta", subtitle: "Ofertas novas, estoque limitado", cta: "Conferir", hue: 341, image: images?.banners?.[0] },
4034
+ { type: "products", title: "PROMO\xC7\xD5ES", filter: "sale", limit: 4 },
4035
+ { type: "newsletter", title: "Entre para o clube", subtitle: "Cupons e lan\xE7amentos antes de todo mundo." }
4036
+ ]
4037
+ })
4038
+ };
4039
+ var atelierTemplate = {
4040
+ id: "atelier",
4041
+ inspiration: "Nuvemshop Flex",
4042
+ theme: {
4043
+ name: "atelier",
4044
+ colors: {
4045
+ background: "0 0% 100%",
4046
+ foreground: "0 0% 9%",
4047
+ primary: "40 45% 48%",
4048
+ primaryForeground: "0 0% 100%",
4049
+ card: "0 0% 99%",
4050
+ cardForeground: "0 0% 9%",
4051
+ muted: "40 10% 95%",
4052
+ mutedForeground: "0 0% 42%",
4053
+ border: "0 0% 88%",
4054
+ announcementBackground: "0 0% 9%",
4055
+ announcementForeground: "0 0% 98%"
4056
+ },
4057
+ font: { heading: "Outfit", body: "Outfit" },
4058
+ radius: "none",
4059
+ header: { variant: "centered", uppercaseNav: true },
4060
+ productCard: { style: "card", imageAspect: "square" },
4061
+ uppercaseButtons: true
4062
+ },
4063
+ announcement: "PARCELE EM AT\xC9 10X \xB7 5% OFF NO PIX",
4064
+ home: (storeName, images) => ({
4065
+ sections: [
4066
+ {
4067
+ type: "hero",
4068
+ variant: "slider",
4069
+ height: "medium",
4070
+ slides: [
4071
+ { title: "Feito para quem valoriza detalhes", subtitle: `Cole\xE7\xE3o exclusiva ${storeName}`, cta: "APROVEITE", hue: 40, image: images?.hero?.[0] },
4072
+ { title: "Cl\xE1ssicos que n\xE3o saem de moda", subtitle: "Pe\xE7as autorais, produ\xE7\xE3o limitada", cta: "VER COLE\xC7\xC3O", hue: 210, image: images?.hero?.[1] }
4073
+ ]
4074
+ },
4075
+ {
4076
+ type: "benefits",
4077
+ items: [
4078
+ { icon: "CreditCard", title: "Parcele em at\xE9 10x", text: "Ou 5% off no PIX" },
4079
+ { icon: "Lock", title: "Site 100% seguro", text: "Seus dados protegidos" },
4080
+ { icon: "Award", title: "Pe\xE7as exclusivas", text: "Produtos 100% autorais" }
4081
+ ]
4082
+ },
4083
+ { type: "categories", style: "tiles" },
4084
+ { type: "products", title: "Novidades", filter: "new", limit: 4 },
4085
+ { type: "banner", title: "Promo\xE7\xF5es", eyebrow: "sele\xE7\xE3o especial", subtitle: "Selecionados com at\xE9 25% off", cta: "Ver ofertas", hue: 40, image: images?.banners?.[0] },
4086
+ { type: "products", title: "Promo\xE7\xF5es", filter: "sale", limit: 4 },
4087
+ {
4088
+ type: "testimonials",
4089
+ title: "O que dizem sobre n\xF3s",
4090
+ items: [
4091
+ { quote: "Acabamento perfeito, superou a expectativa.", author: "Mariana C." },
4092
+ { quote: "Atendimento impec\xE1vel do pedido \xE0 entrega.", author: "Jorge L." },
4093
+ { quote: "Virou minha loja favorita para presentes.", author: "Paula R." }
4094
+ ]
4095
+ },
4096
+ { type: "newsletter" }
4097
+ ]
4098
+ })
4099
+ };
4100
+ var storefrontTemplates = {
4101
+ mare: mareTemplate,
4102
+ sertao: sertaoTemplate,
4103
+ volt: voltTemplate,
4104
+ atelier: atelierTemplate
4105
+ };
4106
+
4107
+ exports.BenefitsRow = BenefitsRow;
4108
+ exports.CartDrawer = CartDrawer;
4109
+ exports.CatalogPage = CatalogPage;
4110
+ exports.CategoryShowcase = CategoryShowcase;
4111
+ exports.CheckoutPage = CheckoutPage;
4112
+ exports.FiltersPanel = FiltersPanel;
4113
+ exports.HeroSection = HeroSection;
4114
+ exports.HomePage = HomePage;
4115
+ exports.Link = Link;
4116
+ exports.ManifestoBlock = ManifestoBlock;
4117
+ exports.MyPurchasesPage = MyPurchasesPage;
4118
+ exports.NewsletterBand = NewsletterBand;
4119
+ exports.OrderConfirmationPage = OrderConfirmationPage;
4120
+ exports.OrderTrackingTimeline = OrderTrackingTimeline;
4121
+ exports.Price = Price;
4122
+ exports.ProductCard = ProductCard;
4123
+ exports.ProductDetailPage = ProductDetailPage;
4124
+ exports.ProductGrid = ProductGrid;
4125
+ exports.ProductOptionSelector = ProductOptionSelector;
4126
+ exports.ProductRail = ProductRail;
4127
+ exports.PromoBanner = PromoBanner;
4128
+ exports.QuantityInput = QuantityInput;
4129
+ exports.Reveal = Reveal;
4130
+ exports.StorefrontFooter = StorefrontFooter;
4131
+ exports.StorefrontHeader = StorefrontHeader;
4132
+ exports.StorefrontScaffold = StorefrontScaffold;
4133
+ exports.StorefrontShell = StorefrontShell;
4134
+ exports.StorefrontThemeStyle = StorefrontThemeStyle;
4135
+ exports.TID = TID;
4136
+ exports.Testimonials = Testimonials;
4137
+ exports.atelierTemplate = atelierTemplate;
4138
+ exports.bannerPlaceholder = bannerPlaceholder;
4139
+ exports.createStorefrontApp = createStorefrontApp;
4140
+ exports.defineStorefront = defineStorefront;
4141
+ exports.establishCustomerSession = establishCustomerSession;
4142
+ exports.formatMoney = formatMoney;
4143
+ exports.formatProductOptionSelection = formatProductOptionSelection;
4144
+ exports.getCustomerAuthAdapter = getCustomerAuthAdapter;
4145
+ exports.getProductOptionGroups = getProductOptionGroups;
4146
+ exports.initStorefrontRuntime = initStorefrontRuntime;
4147
+ exports.mareTemplate = mareTemplate;
4148
+ exports.matchPath = matchPath;
4149
+ exports.navigateTo = navigateTo;
4150
+ exports.normalizeProductOptionSelection = normalizeProductOptionSelection;
4151
+ exports.placeStorefrontOrder = placeStorefrontOrder;
4152
+ exports.prefersReducedMotion = prefersReducedMotion;
4153
+ exports.productCardSlotContract = productCardSlotContract;
4154
+ exports.productOptionSelectionKey = productOptionSelectionKey;
4155
+ exports.registerStorefrontBlocks = registerStorefrontBlocks;
4156
+ exports.resolveAuthAdapter = resolveAuthAdapter;
4157
+ exports.roundCents = roundCents;
4158
+ exports.sectionsToBlocks = sectionsToBlocks;
4159
+ exports.selectCount = selectCount;
4160
+ exports.selectDiscountTotal = selectDiscountTotal;
4161
+ exports.selectShipping = selectShipping;
4162
+ exports.selectSubtotal = selectSubtotal;
4163
+ exports.selectTotal = selectTotal;
4164
+ exports.sertaoTemplate = sertaoTemplate;
4165
+ exports.signInByEmail = signInByEmail;
4166
+ exports.signOutCustomer = signOutCustomer;
4167
+ exports.signUpCustomer = signUpCustomer;
4168
+ exports.storefrontSlotContracts = storefrontSlotContracts;
4169
+ exports.storefrontTemplates = storefrontTemplates;
4170
+ exports.themeToCss = themeToCss;
4171
+ exports.useCartStore = useCartStore;
4172
+ exports.useCatalogStore = useCatalogStore;
4173
+ exports.useCategories = useCategories;
4174
+ exports.useDiscountValidator = useDiscountValidator;
4175
+ exports.useHashPath = useHashPath;
4176
+ exports.useInView = useInView;
4177
+ exports.useMyOrders = useMyOrders;
4178
+ exports.usePopOnChange = usePopOnChange;
4179
+ exports.useProduct = useProduct;
4180
+ exports.useProducts = useProducts;
4181
+ exports.useScrolled = useScrolled;
4182
+ exports.useSessionStore = useSessionStore;
4183
+ exports.useStorefrontConfig = useStorefrontConfig;
4184
+ exports.voltTemplate = voltTemplate;
4185
+ //# sourceMappingURL=index.cjs.map
4186
+ //# sourceMappingURL=index.cjs.map