@fayz-ai/storefront 0.84.0 → 0.84.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CheckoutPage-DDY4WLFJ.js +12 -0
- package/dist/CheckoutPage-DDY4WLFJ.js.map +1 -0
- package/dist/MyPurchasesPage-IHCB5QRR.js +11 -0
- package/dist/MyPurchasesPage-IHCB5QRR.js.map +1 -0
- package/dist/OrderConfirmationPage-SAKAGOCC.js +10 -0
- package/dist/OrderConfirmationPage-SAKAGOCC.js.map +1 -0
- package/dist/analytics/meta-pixel.d.ts +13 -0
- package/dist/analytics/meta-pixel.d.ts.map +1 -1
- package/dist/app/createStorefrontApp.d.ts.map +1 -1
- package/dist/chunk-45R7CIEA.js +234 -0
- package/dist/chunk-45R7CIEA.js.map +1 -0
- package/dist/{chunk-QU5O4CKY.js → chunk-6NUBVJJY.js} +5 -4
- package/dist/chunk-6NUBVJJY.js.map +1 -0
- package/dist/chunk-BKO2AQ3Q.js +329 -0
- package/dist/chunk-BKO2AQ3Q.js.map +1 -0
- package/dist/chunk-CV6RYJIC.js +454 -0
- package/dist/chunk-CV6RYJIC.js.map +1 -0
- package/dist/chunk-D5C5U2SG.js +267 -0
- package/dist/chunk-D5C5U2SG.js.map +1 -0
- package/dist/chunk-DRMDBXMT.js +2123 -0
- package/dist/chunk-DRMDBXMT.js.map +1 -0
- package/dist/chunk-DVQHKNNX.js +29 -0
- package/dist/chunk-DVQHKNNX.js.map +1 -0
- package/dist/chunk-FCPOPWG3.js +31 -0
- package/dist/chunk-FCPOPWG3.js.map +1 -0
- package/dist/chunk-GQ3TCJVX.js +216 -0
- package/dist/chunk-GQ3TCJVX.js.map +1 -0
- package/dist/{chunk-7N5HKQBM.js → chunk-GVZ2SAOI.js} +5 -552
- package/dist/chunk-GVZ2SAOI.js.map +1 -0
- package/dist/{chunk-7FR6KOW2.js → chunk-JLCF7CPP.js} +1256 -6590
- package/dist/chunk-JLCF7CPP.js.map +1 -0
- package/dist/chunk-K27LYMSF.js +435 -0
- package/dist/chunk-K27LYMSF.js.map +1 -0
- package/dist/chunk-MFAU3AWK.js +817 -0
- package/dist/chunk-MFAU3AWK.js.map +1 -0
- package/dist/chunk-OHWX6BEY.js +436 -0
- package/dist/chunk-OHWX6BEY.js.map +1 -0
- package/dist/chunk-PATS4DIF.js +484 -0
- package/dist/chunk-PATS4DIF.js.map +1 -0
- package/dist/chunk-WDQFNHYO.js +41 -0
- package/dist/chunk-WDQFNHYO.js.map +1 -0
- package/dist/chunk-WPWNYBNG.js +68 -0
- package/dist/chunk-WPWNYBNG.js.map +1 -0
- package/dist/chunk-WXXYJZE6.js +99 -0
- package/dist/chunk-WXXYJZE6.js.map +1 -0
- package/dist/host.js +4 -2
- package/dist/host.js.map +1 -1
- package/dist/index.js +26 -6
- package/dist/index.js.map +1 -1
- package/dist/render.js +11 -3
- package/dist/render.js.map +1 -1
- package/dist/tanstack.js +10 -2
- package/dist/tanstack.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-7FR6KOW2.js.map +0 -1
- package/dist/chunk-7N5HKQBM.js.map +0 -1
- package/dist/chunk-QU5O4CKY.js.map +0 -1
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
import { useDeliveryStore, useCartStore, selectSubtotal } from './chunk-K27LYMSF.js';
|
|
2
|
+
import { formatMoney, establishCustomerSession, useSessionStore } from './chunk-PATS4DIF.js';
|
|
3
|
+
import { toBlockNodes, defaultPageBlocks, PageDataScope, checkPageBlocks } from './chunk-BKO2AQ3Q.js';
|
|
4
|
+
import { useRoutePath, useStorefrontConfigOptional, useStorefrontConfig, translate, Link } from './chunk-GVZ2SAOI.js';
|
|
5
|
+
import { createContext, useContext, useMemo, useState, useEffect, useCallback } from 'react';
|
|
6
|
+
import { repairBlockTree, renderBlocks, clearExtensionPoints, defineExtensionPoint, runExtensionPoint, runEscapeValve, getCountrySchema } from '@fayz-ai/core';
|
|
7
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
+
import { create } from 'zustand';
|
|
9
|
+
import { persist } from 'zustand/middleware';
|
|
10
|
+
import { validateDiscount } from '@fayz-ai/shop';
|
|
11
|
+
import { storefrontDiscountToPromotion, previewCouponPercent, discountToPromotion } from '@fayz-ai/shop/rules';
|
|
12
|
+
import { X, Lock } from 'lucide-react';
|
|
13
|
+
|
|
14
|
+
// src/analytics/bus.ts
|
|
15
|
+
var MAX_PENDING = 50;
|
|
16
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
17
|
+
var pending = [];
|
|
18
|
+
var identifier = null;
|
|
19
|
+
var pendingEmail = null;
|
|
20
|
+
var buyerListeners = /* @__PURE__ */ new Set();
|
|
21
|
+
var lastEmail = null;
|
|
22
|
+
var lastAt = /* @__PURE__ */ new Map();
|
|
23
|
+
function trackStorefront(name, properties = {}) {
|
|
24
|
+
if (typeof window === "undefined") return;
|
|
25
|
+
const event = { name, properties, at: Date.now() };
|
|
26
|
+
lastAt.set(name, event.at);
|
|
27
|
+
pending.push(event);
|
|
28
|
+
if (pending.length > MAX_PENDING) pending = pending.slice(-MAX_PENDING);
|
|
29
|
+
for (const listener of listeners) {
|
|
30
|
+
try {
|
|
31
|
+
listener(event);
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function onStorefrontEvent(listener) {
|
|
37
|
+
listeners.add(listener);
|
|
38
|
+
for (const event of pending) {
|
|
39
|
+
try {
|
|
40
|
+
listener(event);
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return () => {
|
|
45
|
+
listeners.delete(listener);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function identifyStorefrontBuyer(email) {
|
|
49
|
+
const clean = email.trim().toLowerCase();
|
|
50
|
+
if (!clean.includes("@")) return;
|
|
51
|
+
if (identifier) identifier(clean);
|
|
52
|
+
else pendingEmail = clean;
|
|
53
|
+
lastEmail = clean;
|
|
54
|
+
for (const listener of buyerListeners) {
|
|
55
|
+
try {
|
|
56
|
+
listener(clean);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function onStorefrontBuyer(listener) {
|
|
62
|
+
buyerListeners.add(listener);
|
|
63
|
+
if (lastEmail) {
|
|
64
|
+
try {
|
|
65
|
+
listener(lastEmail);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return () => {
|
|
70
|
+
buyerListeners.delete(listener);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function setStorefrontIdentifier(fn) {
|
|
74
|
+
identifier = fn;
|
|
75
|
+
if (fn && pendingEmail) {
|
|
76
|
+
fn(pendingEmail);
|
|
77
|
+
pendingEmail = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function lastStorefrontEventAt(name) {
|
|
81
|
+
return lastAt.get(name) ?? 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/app/hydration.ts
|
|
85
|
+
var PAGE_DATA_SCRIPT_ID = "__fayz_page_data__";
|
|
86
|
+
function escapeScriptJson(value) {
|
|
87
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
88
|
+
}
|
|
89
|
+
function serializePageData(payload) {
|
|
90
|
+
const ready = {};
|
|
91
|
+
for (const [key, entry] of Object.entries(payload.entries)) {
|
|
92
|
+
if (entry.status === "ready") ready[key] = entry;
|
|
93
|
+
}
|
|
94
|
+
const json = escapeScriptJson({ entries: ready, path: payload.path });
|
|
95
|
+
return `<script id="${PAGE_DATA_SCRIPT_ID}" type="application/json">${json}</script>`;
|
|
96
|
+
}
|
|
97
|
+
var _read = false;
|
|
98
|
+
var _cache = null;
|
|
99
|
+
function readHydratedPageData() {
|
|
100
|
+
if (_read) return _cache;
|
|
101
|
+
_read = true;
|
|
102
|
+
if (typeof document === "undefined") return _cache = null;
|
|
103
|
+
const el = document.getElementById(PAGE_DATA_SCRIPT_ID);
|
|
104
|
+
if (!el?.textContent) return _cache = null;
|
|
105
|
+
try {
|
|
106
|
+
_cache = JSON.parse(el.textContent);
|
|
107
|
+
} catch {
|
|
108
|
+
_cache = null;
|
|
109
|
+
}
|
|
110
|
+
return _cache;
|
|
111
|
+
}
|
|
112
|
+
function hydratedEntriesFor(path) {
|
|
113
|
+
const payload = readHydratedPageData();
|
|
114
|
+
if (!payload) return void 0;
|
|
115
|
+
const same = (a, b) => (a.split("?")[0] || "/") === (b.split("?")[0] || "/");
|
|
116
|
+
return same(payload.path, path) ? payload.entries : void 0;
|
|
117
|
+
}
|
|
118
|
+
var PageContext = createContext(null);
|
|
119
|
+
var StorefrontPageProvider = PageContext.Provider;
|
|
120
|
+
function useStorefrontPage() {
|
|
121
|
+
const ctx = useContext(PageContext);
|
|
122
|
+
if (!ctx) throw new Error("useStorefrontPage must be used inside a storefront page");
|
|
123
|
+
return ctx;
|
|
124
|
+
}
|
|
125
|
+
function useStorefrontPageOptional() {
|
|
126
|
+
return useContext(PageContext);
|
|
127
|
+
}
|
|
128
|
+
var reported = /* @__PURE__ */ new Set();
|
|
129
|
+
function reportDiagnostics(kind, nodes, repairs) {
|
|
130
|
+
const signature = `${kind}:${nodes.map((n) => `${n.type}#${n.id ?? ""}`).join(",")}`;
|
|
131
|
+
if (reported.has(signature)) return;
|
|
132
|
+
reported.add(signature);
|
|
133
|
+
for (const repair of repairs) {
|
|
134
|
+
console.warn(
|
|
135
|
+
`[storefront] forced-layout: re-inserted required block "${repair.inserted}" at ${repair.path}[${repair.index}] on the ${kind} page \u2014 add it to the composition`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
for (const finding of checkPageBlocks(kind, nodes)) {
|
|
139
|
+
const message = `[storefront] ${finding.rule}: ${finding.detail}`;
|
|
140
|
+
if (finding.level === "error") console.error(message);
|
|
141
|
+
else console.warn(message);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function resetPageDiagnostics() {
|
|
145
|
+
reported.clear();
|
|
146
|
+
}
|
|
147
|
+
function StorefrontPage({ kind, page, params = {} }) {
|
|
148
|
+
const routePath = useRoutePath();
|
|
149
|
+
const { nodes, repairs } = useMemo(() => {
|
|
150
|
+
const authored = page?.blocks ? toBlockNodes(page.blocks) : defaultPageBlocks(kind);
|
|
151
|
+
const repaired = repairBlockTree(authored);
|
|
152
|
+
return { nodes: repaired.nodes, repairs: repaired.repairs };
|
|
153
|
+
}, [page, kind]);
|
|
154
|
+
reportDiagnostics(kind, nodes, repairs);
|
|
155
|
+
const ctx = useMemo(() => ({ kind, params }), [kind, params]);
|
|
156
|
+
return /* @__PURE__ */ jsx(StorefrontPageProvider, { value: ctx, children: /* @__PURE__ */ jsx(PageDataScope, { nodes, params, initial: hydratedEntriesFor(routePath), children: /* @__PURE__ */ jsx("main", { children: renderBlocks(nodes, kind === "content" ? void 0 : `presentation.pages.${kind}.blocks`) }) }) });
|
|
157
|
+
}
|
|
158
|
+
var PRODUCT_DETAIL_CONTEXT = ["product", "soldOut", "lowStock", "discountPct"];
|
|
159
|
+
var PRODUCT_CARD_CONTEXT = ["product", "soldOut", "onSale", "hasOptions"];
|
|
160
|
+
var CATALOG_CONTEXT = ["products", "loading"];
|
|
161
|
+
var MENU_CONTEXT = ["coverSize", "highlights", "section", "openProduct", "quickAdd"];
|
|
162
|
+
var CART_CONTEXT = ["lines", "subtotal"];
|
|
163
|
+
var CHECKOUT_CONTEXT = ["subtotal", "total"];
|
|
164
|
+
var STOREFRONT_SLOTS = [
|
|
165
|
+
x("productDetail", "beforeTitle", "between the breadcrumb and the product name", PRODUCT_DETAIL_CONTEXT),
|
|
166
|
+
x("productDetail", "afterTitle", "under the product name, above the price", PRODUCT_DETAIL_CONTEXT),
|
|
167
|
+
x("productDetail", "afterPrice", "under the price and discount badge", PRODUCT_DETAIL_CONTEXT),
|
|
168
|
+
// The one PDP position with proof: this store put a Materials/Symbolism/
|
|
169
|
+
// Availability <dl> exactly here, and reimplementing the page to do it cost it
|
|
170
|
+
// the gallery, option selection, quantity, specs, related products and every
|
|
171
|
+
// head tag on every product page.
|
|
172
|
+
s(
|
|
173
|
+
"productDetail",
|
|
174
|
+
"afterDescription",
|
|
175
|
+
"under the description paragraph, above the actions",
|
|
176
|
+
PRODUCT_DETAIL_CONTEXT,
|
|
177
|
+
"cristinarotondaro-shop/src/storefront/components/product.tsx:71-86"
|
|
178
|
+
),
|
|
179
|
+
x("productDetail", "beforeAddToCart", "above the quantity and add-to-cart row", PRODUCT_DETAIL_CONTEXT),
|
|
180
|
+
x("productDetail", "afterAddToCart", "under the add-to-cart row", PRODUCT_DETAIL_CONTEXT),
|
|
181
|
+
x("productDetail", "afterGallery", "under the gallery, in the left column", PRODUCT_DETAIL_CONTEXT),
|
|
182
|
+
x("productDetail", "beforeSpecs", "above the specification table", PRODUCT_DETAIL_CONTEXT),
|
|
183
|
+
s(
|
|
184
|
+
"productDetail",
|
|
185
|
+
"afterDetail",
|
|
186
|
+
"the bottom of the page, after related products",
|
|
187
|
+
PRODUCT_DETAIL_CONTEXT,
|
|
188
|
+
"cristinarotondaro-shop/src/storefront/components/product.tsx:95-102 (enquiry panel)"
|
|
189
|
+
),
|
|
190
|
+
// The four card positions. Three are stable on the strength of the fleet: this
|
|
191
|
+
// is the component our stores forked most, and every fork lost something.
|
|
192
|
+
s(
|
|
193
|
+
"productCard",
|
|
194
|
+
"afterName",
|
|
195
|
+
"between the product name and the price row",
|
|
196
|
+
PRODUCT_CARD_CONTEXT,
|
|
197
|
+
"pulse-store PulseProductCard.tsx:70-76; artorious ArtoriusProductCard.tsx:107-118; compactor CompactorProductCard.tsx:99; cristinarotondaro product.tsx:34 \u2014 4 of 5"
|
|
198
|
+
),
|
|
199
|
+
s(
|
|
200
|
+
"productCard",
|
|
201
|
+
"imageOverlay",
|
|
202
|
+
"over the image; the container is relative, content positions itself",
|
|
203
|
+
PRODUCT_CARD_CONTEXT,
|
|
204
|
+
"pulse PulseProductCard.tsx:40-44; artorious ArtoriusProductCard.tsx:67-92 \u2014 3 of 5"
|
|
205
|
+
),
|
|
206
|
+
s(
|
|
207
|
+
"productCard",
|
|
208
|
+
"underPrice",
|
|
209
|
+
"under the price row",
|
|
210
|
+
PRODUCT_CARD_CONTEXT,
|
|
211
|
+
"compactor CompactorProductCard.tsx:104 (Pix + installment lines); config.payments.terms already exists for it"
|
|
212
|
+
),
|
|
213
|
+
x("productCard", "top", "above the image, full card width", PRODUCT_CARD_CONTEXT),
|
|
214
|
+
s(
|
|
215
|
+
"catalog",
|
|
216
|
+
"beforeHeading",
|
|
217
|
+
"above the catalog heading",
|
|
218
|
+
CATALOG_CONTEXT,
|
|
219
|
+
"cristinarotondaro-shop/src/storefront/customization.tsx:414-426 (category hero)"
|
|
220
|
+
),
|
|
221
|
+
s(
|
|
222
|
+
"catalog",
|
|
223
|
+
"beforeGrid",
|
|
224
|
+
"between the heading and the grid",
|
|
225
|
+
CATALOG_CONTEXT,
|
|
226
|
+
"cristinarotondaro-shop/src/storefront/customization.tsx:427-429 (statement band)"
|
|
227
|
+
),
|
|
228
|
+
x("catalog", "afterFilters", "inside the filter column, under the filters", CATALOG_CONTEXT),
|
|
229
|
+
s(
|
|
230
|
+
"catalog",
|
|
231
|
+
"afterGrid",
|
|
232
|
+
"under the product grid",
|
|
233
|
+
CATALOG_CONTEXT,
|
|
234
|
+
"cristinarotondaro-shop/src/storefront/customization.tsx:442-450 (bespoke empty state)"
|
|
235
|
+
),
|
|
236
|
+
// The three menu positions are stable on one store's proof: Artorius put
|
|
237
|
+
// its cover copy, a highlights carousel and per-category banners into the
|
|
238
|
+
// SDK menu with portals after mount, and paid for it with a 0.24 layout
|
|
239
|
+
// shift and a highlight image (its LCP) discovered only after hydration.
|
|
240
|
+
s("menu", "coverOverlay", "inside the menu cover, after its own content", MENU_CONTEXT, "artorious-shop src/custom/components/DeliveryBanners.tsx (hero copy portal)"),
|
|
241
|
+
s("menu", "highlights", "inside the highlights section, replacing the default rows", MENU_CONTEXT, "artorious-shop src/custom/components/DeliveryHighlightsCarousel.tsx (carousel portal + CSS hiding the rows)"),
|
|
242
|
+
s("menu", "sectionIntro", "the top of each menu section, above its heading", MENU_CONTEXT, "artorious-shop src/custom/components/DeliveryBanners.tsx (category banner portals)"),
|
|
243
|
+
x("cart", "beforeLines", "above the cart line items", CART_CONTEXT),
|
|
244
|
+
x("cart", "beforeSummary", "between the lines and the totals", CART_CONTEXT),
|
|
245
|
+
s(
|
|
246
|
+
"cart",
|
|
247
|
+
"afterSummary",
|
|
248
|
+
"under the totals, around the checkout button",
|
|
249
|
+
CART_CONTEXT,
|
|
250
|
+
"artorious-shop/docs/TODO.md:167-179; the CartDrawerFooterExtra contract exists for it"
|
|
251
|
+
),
|
|
252
|
+
x("checkout", "beforeForm", "above the checkout form", CHECKOUT_CONTEXT),
|
|
253
|
+
x("checkout", "afterPaymentMethods", "under the payment method selector", CHECKOUT_CONTEXT),
|
|
254
|
+
x("checkout", "afterForm", "under the form, around the place-order button", CHECKOUT_CONTEXT),
|
|
255
|
+
x("header", "beforeActions", "left of the account and cart icons", []),
|
|
256
|
+
x("header", "afterActions", "right of the cart icon", []),
|
|
257
|
+
x("header", "belowNav", "a full-width strip under the whole header", []),
|
|
258
|
+
x("footer", "before", "above the footer columns", []),
|
|
259
|
+
x("footer", "after", "under the footer columns, above the credit line", [])
|
|
260
|
+
];
|
|
261
|
+
function s(area, name, position, provides, evidence) {
|
|
262
|
+
return { area, name, id: `${area}.${name}`, position, provides, stability: "stable", evidence };
|
|
263
|
+
}
|
|
264
|
+
function x(area, name, position, provides) {
|
|
265
|
+
return { area, name, id: `${area}.${name}`, position, provides, stability: "experimental" };
|
|
266
|
+
}
|
|
267
|
+
function listSlots(areaOrOptions) {
|
|
268
|
+
const options = typeof areaOrOptions === "string" ? { area: areaOrOptions } : areaOrOptions ?? {};
|
|
269
|
+
return STOREFRONT_SLOTS.filter(
|
|
270
|
+
(slot) => (!options.area || slot.area === options.area) && (!options.stableOnly || slot.stability === "stable")
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
function usedSlots(slots) {
|
|
274
|
+
if (!slots) return [];
|
|
275
|
+
return STOREFRONT_SLOTS.filter((slot) => {
|
|
276
|
+
const area = slots[slot.area];
|
|
277
|
+
return Boolean(area?.[slot.name]?.length);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
var SlotContext = createContext(null);
|
|
281
|
+
function useSlotContext() {
|
|
282
|
+
return useContext(SlotContext);
|
|
283
|
+
}
|
|
284
|
+
function useSlotFilled(area, name) {
|
|
285
|
+
const config = useStorefrontConfigOptional();
|
|
286
|
+
return Boolean(config?.slots?.[area]?.[name]?.length);
|
|
287
|
+
}
|
|
288
|
+
function Slot({ area, name, context }) {
|
|
289
|
+
const config = useStorefrontConfigOptional();
|
|
290
|
+
const page = useStorefrontPageOptional();
|
|
291
|
+
const blocks = config?.slots?.[area]?.[name];
|
|
292
|
+
const nodes = useMemo(
|
|
293
|
+
() => blocks?.length ? repairBlockTree(toBlockNodes(blocks)).nodes : null,
|
|
294
|
+
[blocks]
|
|
295
|
+
);
|
|
296
|
+
const value = useMemo(() => context ?? {}, [context]);
|
|
297
|
+
if (!nodes) return null;
|
|
298
|
+
return /* @__PURE__ */ jsx(SlotContext.Provider, { value, children: /* @__PURE__ */ jsx(PageDataScope, { nodes, params: page?.params, children: renderBlocks(nodes, `presentation.slots.${area}.${name}`) }) });
|
|
299
|
+
}
|
|
300
|
+
var CART_LINE_ANNOTATION_POINT = "cart.lineAnnotation";
|
|
301
|
+
var ORDER_METADATA_POINT = "order.metadata";
|
|
302
|
+
var PRODUCT_CARD_BADGE_POINT = "product.cardBadge";
|
|
303
|
+
var CHECKOUT_FIELD_POINT = "checkout.field";
|
|
304
|
+
var BEFORE_PLACE_ORDER_VALVE = "checkout.beforePlaceOrder";
|
|
305
|
+
var registered = false;
|
|
306
|
+
function resetStorefrontExtensionPoints() {
|
|
307
|
+
clearExtensionPoints();
|
|
308
|
+
registered = false;
|
|
309
|
+
}
|
|
310
|
+
function registerStorefrontExtensionPoints() {
|
|
311
|
+
if (registered) return;
|
|
312
|
+
registered = true;
|
|
313
|
+
defineExtensionPoint({
|
|
314
|
+
id: CART_LINE_ANNOTATION_POINT,
|
|
315
|
+
description: "Annotate one cart line (gift wrap, engraving, availability note). Handlers see the line, return one annotation; annotations render under the line in the cart drawer.",
|
|
316
|
+
source: "sdk",
|
|
317
|
+
since: "0.10",
|
|
318
|
+
input: {
|
|
319
|
+
type: "object",
|
|
320
|
+
properties: {
|
|
321
|
+
productId: { type: "string", minLength: 1 },
|
|
322
|
+
name: { type: "string" },
|
|
323
|
+
quantity: { type: "integer", minimum: 1 },
|
|
324
|
+
unitPrice: { type: "number", minimum: 0 },
|
|
325
|
+
slug: { type: "string" },
|
|
326
|
+
optionsLabel: { type: "string" }
|
|
327
|
+
},
|
|
328
|
+
required: ["productId", "name", "quantity", "unitPrice"],
|
|
329
|
+
additionalProperties: false
|
|
330
|
+
},
|
|
331
|
+
output: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
key: { type: "string", minLength: 1 },
|
|
335
|
+
label: { type: "string", minLength: 1 },
|
|
336
|
+
value: { type: "string" }
|
|
337
|
+
},
|
|
338
|
+
required: ["key", "label"],
|
|
339
|
+
additionalProperties: false
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
defineExtensionPoint({
|
|
343
|
+
id: ORDER_METADATA_POINT,
|
|
344
|
+
description: "Contribute machine-readable metadata to an order at placement. Each handler returns an object; contributions land on the order namespaced by handler id, so two extensions can never collide.",
|
|
345
|
+
source: "sdk",
|
|
346
|
+
since: "0.10",
|
|
347
|
+
input: {
|
|
348
|
+
type: "object",
|
|
349
|
+
properties: {
|
|
350
|
+
items: {
|
|
351
|
+
type: "array",
|
|
352
|
+
items: {
|
|
353
|
+
type: "object",
|
|
354
|
+
properties: {
|
|
355
|
+
productId: { type: "string" },
|
|
356
|
+
quantity: { type: "integer", minimum: 1 },
|
|
357
|
+
unitPrice: { type: "number", minimum: 0 }
|
|
358
|
+
},
|
|
359
|
+
required: ["productId", "quantity", "unitPrice"]
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
subtotal: { type: "number", minimum: 0 },
|
|
363
|
+
customerEmail: { type: "string" }
|
|
364
|
+
},
|
|
365
|
+
required: ["items", "subtotal"],
|
|
366
|
+
additionalProperties: false
|
|
367
|
+
},
|
|
368
|
+
output: { type: "object" }
|
|
369
|
+
});
|
|
370
|
+
defineExtensionPoint({
|
|
371
|
+
id: PRODUCT_CARD_BADGE_POINT,
|
|
372
|
+
description: 'Badge a product card ("Novo", "\xDAltimas unidades", "Frete gr\xE1tis"). Handlers see a product summary and return a badge; badges render next to the built-in sale/sold-out chips.',
|
|
373
|
+
source: "sdk",
|
|
374
|
+
since: "0.10",
|
|
375
|
+
input: {
|
|
376
|
+
type: "object",
|
|
377
|
+
properties: {
|
|
378
|
+
id: { type: "string", minLength: 1 },
|
|
379
|
+
slug: { type: "string" },
|
|
380
|
+
name: { type: "string" },
|
|
381
|
+
price: { type: "number" },
|
|
382
|
+
inventoryCount: { type: "integer" },
|
|
383
|
+
compareAtPrice: { type: ["number", "null"] }
|
|
384
|
+
},
|
|
385
|
+
required: ["id", "name", "price"],
|
|
386
|
+
additionalProperties: false
|
|
387
|
+
},
|
|
388
|
+
output: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
text: { type: "string", minLength: 1, maxLength: 24 },
|
|
392
|
+
tone: { type: "string", enum: ["default", "accent", "warning"] }
|
|
393
|
+
},
|
|
394
|
+
required: ["text"],
|
|
395
|
+
additionalProperties: false
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
defineExtensionPoint({
|
|
399
|
+
id: CHECKOUT_FIELD_POINT,
|
|
400
|
+
description: "Contribute one extra field to the checkout form (CPF/CNPJ, gift message, marketing opt-in). The captured value is stored on the order metadata under the handler id.",
|
|
401
|
+
source: "sdk",
|
|
402
|
+
since: "0.10",
|
|
403
|
+
input: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: { customerEmail: { type: "string" } },
|
|
406
|
+
additionalProperties: false
|
|
407
|
+
},
|
|
408
|
+
output: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
id: { type: "string", minLength: 1 },
|
|
412
|
+
label: { type: "string", minLength: 1 },
|
|
413
|
+
type: { type: "string", enum: ["text", "checkbox"] },
|
|
414
|
+
required: { type: "boolean" },
|
|
415
|
+
placeholder: { type: "string" }
|
|
416
|
+
},
|
|
417
|
+
required: ["id", "label", "type"],
|
|
418
|
+
additionalProperties: false
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
defineExtensionPoint({
|
|
422
|
+
id: BEFORE_PLACE_ORDER_VALVE,
|
|
423
|
+
description: "THE escape valve for order creation. One registrant maximum. Return null to take the default placeOrder path; return an Order to replace order creation entirely (external OMS, quote-first flows).",
|
|
424
|
+
source: "sdk",
|
|
425
|
+
since: "0.10",
|
|
426
|
+
mode: "escape",
|
|
427
|
+
input: {
|
|
428
|
+
type: "object",
|
|
429
|
+
properties: {
|
|
430
|
+
items: {
|
|
431
|
+
type: "array",
|
|
432
|
+
minItems: 1,
|
|
433
|
+
items: {
|
|
434
|
+
type: "object",
|
|
435
|
+
properties: {
|
|
436
|
+
productId: { type: "string", minLength: 1 },
|
|
437
|
+
variantId: { type: "string" },
|
|
438
|
+
quantity: { type: "integer", minimum: 1 },
|
|
439
|
+
optionsLabel: { type: "string" }
|
|
440
|
+
},
|
|
441
|
+
required: ["productId", "quantity"]
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
customer: {
|
|
445
|
+
type: "object",
|
|
446
|
+
properties: { name: { type: "string" }, email: { type: "string", minLength: 3 } },
|
|
447
|
+
required: ["name", "email"]
|
|
448
|
+
},
|
|
449
|
+
currency: { type: "string" },
|
|
450
|
+
discountCode: { type: "string" },
|
|
451
|
+
shippingTotal: { type: "number" },
|
|
452
|
+
notes: { type: "string" },
|
|
453
|
+
customerId: { type: "string" }
|
|
454
|
+
},
|
|
455
|
+
required: ["items", "customer"],
|
|
456
|
+
additionalProperties: false
|
|
457
|
+
},
|
|
458
|
+
// An Order is a rich provider shape; the valve's contract pins what the
|
|
459
|
+
// storefront actually needs to continue (an id and totals) and lets the
|
|
460
|
+
// rest of the Order shape ride along.
|
|
461
|
+
output: {
|
|
462
|
+
type: "object",
|
|
463
|
+
properties: {
|
|
464
|
+
id: { type: "string", minLength: 1 },
|
|
465
|
+
total: { type: "number" }
|
|
466
|
+
},
|
|
467
|
+
required: ["id"]
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async function getCartLineAnnotations(line) {
|
|
472
|
+
registerStorefrontExtensionPoints();
|
|
473
|
+
const input = {
|
|
474
|
+
productId: line.productId,
|
|
475
|
+
name: line.name,
|
|
476
|
+
quantity: line.quantity,
|
|
477
|
+
unitPrice: line.unitPrice,
|
|
478
|
+
...line.slug ? { slug: line.slug } : {},
|
|
479
|
+
...line.optionsLabel ? { optionsLabel: line.optionsLabel } : {}
|
|
480
|
+
};
|
|
481
|
+
const contributions = await runExtensionPoint(
|
|
482
|
+
CART_LINE_ANNOTATION_POINT,
|
|
483
|
+
input
|
|
484
|
+
);
|
|
485
|
+
return contributions.map((c) => c.value);
|
|
486
|
+
}
|
|
487
|
+
async function getProductCardBadges(product) {
|
|
488
|
+
registerStorefrontExtensionPoints();
|
|
489
|
+
const input = {
|
|
490
|
+
id: product.id,
|
|
491
|
+
slug: product.slug,
|
|
492
|
+
name: product.name,
|
|
493
|
+
price: product.price,
|
|
494
|
+
inventoryCount: product.inventoryCount,
|
|
495
|
+
compareAtPrice: product.compareAtPrice
|
|
496
|
+
};
|
|
497
|
+
const contributions = await runExtensionPoint(
|
|
498
|
+
PRODUCT_CARD_BADGE_POINT,
|
|
499
|
+
input
|
|
500
|
+
);
|
|
501
|
+
return contributions.map((c) => c.value);
|
|
502
|
+
}
|
|
503
|
+
async function getCheckoutFieldContributions(input = {}) {
|
|
504
|
+
registerStorefrontExtensionPoints();
|
|
505
|
+
const contributions = await runExtensionPoint(
|
|
506
|
+
CHECKOUT_FIELD_POINT,
|
|
507
|
+
input
|
|
508
|
+
);
|
|
509
|
+
return contributions.map((c) => ({ ...c.value, handlerId: c.handlerId }));
|
|
510
|
+
}
|
|
511
|
+
async function collectOrderMetadata(input) {
|
|
512
|
+
registerStorefrontExtensionPoints();
|
|
513
|
+
const contributions = await runExtensionPoint(
|
|
514
|
+
ORDER_METADATA_POINT,
|
|
515
|
+
input
|
|
516
|
+
);
|
|
517
|
+
if (contributions.length === 0) return void 0;
|
|
518
|
+
return Object.fromEntries(contributions.map((c) => [c.handlerId, c.value]));
|
|
519
|
+
}
|
|
520
|
+
async function runBeforePlaceOrderValve(ctx) {
|
|
521
|
+
registerStorefrontExtensionPoints();
|
|
522
|
+
return runEscapeValve(BEFORE_PLACE_ORDER_VALVE, ctx);
|
|
523
|
+
}
|
|
524
|
+
var BUYER_COUNTRY_COOKIE = "fayz_country";
|
|
525
|
+
var useBuyerStore = create()(
|
|
526
|
+
persist(
|
|
527
|
+
(set) => ({
|
|
528
|
+
picked: null,
|
|
529
|
+
pick(country) {
|
|
530
|
+
const iso2 = country.trim().toUpperCase();
|
|
531
|
+
set({ picked: iso2 });
|
|
532
|
+
try {
|
|
533
|
+
document.cookie = `${BUYER_COUNTRY_COOKIE}=${iso2}; path=/; max-age=31536000; samesite=lax`;
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}),
|
|
538
|
+
{ name: "fayz.storefront.buyer.v1" }
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
function navigatorRegion() {
|
|
542
|
+
try {
|
|
543
|
+
for (const tag of navigator.languages ?? [navigator.language]) {
|
|
544
|
+
const region = tag?.split(/[-_]/)[1];
|
|
545
|
+
if (region && /^[A-Za-z]{2}$/.test(region)) return region.toUpperCase();
|
|
546
|
+
}
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
return void 0;
|
|
550
|
+
}
|
|
551
|
+
function useBuyerCountry() {
|
|
552
|
+
const config = useStorefrontConfig();
|
|
553
|
+
const allowed = config.countries;
|
|
554
|
+
const picked = useBuyerStore((s2) => s2.picked);
|
|
555
|
+
const pick = useBuyerStore((s2) => s2.pick);
|
|
556
|
+
const [mounted, setMounted] = useState(false);
|
|
557
|
+
useEffect(() => setMounted(true), []);
|
|
558
|
+
const ok = (c) => !!c && allowed.includes(c);
|
|
559
|
+
let country = ok(config.countryHint) ? config.countryHint : config.defaultCountry;
|
|
560
|
+
if (allowed.length > 1 && mounted) {
|
|
561
|
+
const region = navigatorRegion();
|
|
562
|
+
if (ok(picked)) country = picked;
|
|
563
|
+
else if (!ok(config.countryHint) && ok(region)) country = region;
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
country,
|
|
567
|
+
allowed,
|
|
568
|
+
schema: getCountrySchema(country),
|
|
569
|
+
setCountry(next) {
|
|
570
|
+
const iso2 = next.toUpperCase();
|
|
571
|
+
if (!allowed.includes(iso2) || iso2 === country) return;
|
|
572
|
+
pick(iso2);
|
|
573
|
+
useDeliveryStore.getState().clear();
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function useT() {
|
|
578
|
+
const config = useStorefrontConfig();
|
|
579
|
+
const language = config.language;
|
|
580
|
+
const overrides = config.messages;
|
|
581
|
+
return useCallback((key, vars) => translate(language, key, vars, overrides), [language, overrides]);
|
|
582
|
+
}
|
|
583
|
+
var REASON_MESSAGE = {
|
|
584
|
+
expired: "Cupom expirado.",
|
|
585
|
+
not_started: "Cupom ainda n\xE3o est\xE1 ativo.",
|
|
586
|
+
usage_limit: "Cupom esgotado.",
|
|
587
|
+
not_found: "Cupom inv\xE1lido ou expirado.",
|
|
588
|
+
inactive: "Cupom inv\xE1lido ou expirado.",
|
|
589
|
+
rules_not_matched: "Este cupom n\xE3o se aplica ao seu carrinho.",
|
|
590
|
+
unsupported: "Este cupom n\xE3o \xE9 suportado na loja."
|
|
591
|
+
};
|
|
592
|
+
function cartSubtotal() {
|
|
593
|
+
return selectSubtotal({ lines: useCartStore.getState().lines });
|
|
594
|
+
}
|
|
595
|
+
function cartSnapshot() {
|
|
596
|
+
const lines = useCartStore.getState().lines;
|
|
597
|
+
return {
|
|
598
|
+
items: lines.map((line) => ({
|
|
599
|
+
id: line.lineId ?? line.productId,
|
|
600
|
+
productId: line.productId,
|
|
601
|
+
quantity: line.quantity,
|
|
602
|
+
unitPrice: line.unitPrice
|
|
603
|
+
}))
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function useDiscountValidator() {
|
|
607
|
+
const config = useStorefrontConfig();
|
|
608
|
+
return useCallback(async (code) => {
|
|
609
|
+
const normalized = code.trim().toUpperCase();
|
|
610
|
+
if (!normalized) return { valid: false, percent: 0, message: "Informe um cupom." };
|
|
611
|
+
const local = [
|
|
612
|
+
...config.promotions ?? [],
|
|
613
|
+
...(config.discounts ?? []).map(storefrontDiscountToPromotion)
|
|
614
|
+
];
|
|
615
|
+
const localPreview = previewCouponPercent(local, normalized, cartSnapshot());
|
|
616
|
+
if (localPreview.valid) {
|
|
617
|
+
return { valid: true, percent: localPreview.percent, freeShipping: localPreview.freeShipping };
|
|
618
|
+
}
|
|
619
|
+
if (localPreview.reason !== "not_found") {
|
|
620
|
+
return { valid: false, percent: 0, message: REASON_MESSAGE[localPreview.reason ?? "not_found"] ?? "Cupom inv\xE1lido." };
|
|
621
|
+
}
|
|
622
|
+
const validation = await validateDiscount({ code: normalized, subtotal: cartSubtotal() });
|
|
623
|
+
if (!validation.valid) {
|
|
624
|
+
if (validation.reason === "min_subtotal" && validation.minSubtotal != null) {
|
|
625
|
+
const missing = formatMoney(validation.minSubtotal, config.currency, config.locale);
|
|
626
|
+
return { valid: false, percent: 0, message: `Este cupom vale em pedidos a partir de ${missing}.` };
|
|
627
|
+
}
|
|
628
|
+
return { valid: false, percent: 0, message: REASON_MESSAGE[validation.reason ?? "not_found"] ?? "Cupom inv\xE1lido ou expirado." };
|
|
629
|
+
}
|
|
630
|
+
if (validation.type !== "percentage" && validation.type !== "free_shipping") {
|
|
631
|
+
return { valid: false, percent: 0, message: REASON_MESSAGE.unsupported };
|
|
632
|
+
}
|
|
633
|
+
const promotion = discountToPromotion({
|
|
634
|
+
id: `provider:${normalized}`,
|
|
635
|
+
tenantId: "storefront",
|
|
636
|
+
title: normalized,
|
|
637
|
+
code: validation.code ?? normalized,
|
|
638
|
+
type: validation.type,
|
|
639
|
+
method: "code",
|
|
640
|
+
value: validation.value,
|
|
641
|
+
usageLimit: null,
|
|
642
|
+
oncePerCustomer: false,
|
|
643
|
+
minSubtotal: null,
|
|
644
|
+
appliesTo: "all",
|
|
645
|
+
productIds: [],
|
|
646
|
+
categoryIds: [],
|
|
647
|
+
startsAt: "1970-01-01T00:00:00Z",
|
|
648
|
+
endsAt: null,
|
|
649
|
+
status: "active",
|
|
650
|
+
timesUsed: 0,
|
|
651
|
+
createdAt: "1970-01-01T00:00:00Z",
|
|
652
|
+
updatedAt: "1970-01-01T00:00:00Z"
|
|
653
|
+
});
|
|
654
|
+
const preview = promotion ? previewCouponPercent([promotion], normalized, cartSnapshot()) : null;
|
|
655
|
+
if (!preview?.valid) {
|
|
656
|
+
return { valid: false, percent: 0, message: REASON_MESSAGE[preview?.reason ?? "not_found"] ?? "Cupom inv\xE1lido." };
|
|
657
|
+
}
|
|
658
|
+
return { valid: true, percent: preview.percent, freeShipping: preview.freeShipping };
|
|
659
|
+
}, [config.discounts, config.promotions]);
|
|
660
|
+
}
|
|
661
|
+
var seq = 0;
|
|
662
|
+
var useToastStore = create((set) => ({
|
|
663
|
+
toasts: [],
|
|
664
|
+
push: (input) => {
|
|
665
|
+
const id = `toast-${++seq}-${Date.now()}`;
|
|
666
|
+
const toast2 = {
|
|
667
|
+
id,
|
|
668
|
+
title: input.title,
|
|
669
|
+
description: input.description,
|
|
670
|
+
variant: input.variant ?? "info",
|
|
671
|
+
durationMs: input.durationMs ?? 3400,
|
|
672
|
+
action: input.action
|
|
673
|
+
};
|
|
674
|
+
set((s2) => ({ toasts: [...s2.toasts, toast2] }));
|
|
675
|
+
return id;
|
|
676
|
+
},
|
|
677
|
+
dismiss: (id) => set((s2) => ({ toasts: s2.toasts.filter((t) => t.id !== id) }))
|
|
678
|
+
}));
|
|
679
|
+
var toast = {
|
|
680
|
+
success: (title, description) => useToastStore.getState().push({ title, description, variant: "success" }),
|
|
681
|
+
error: (title, description) => useToastStore.getState().push({ title, description, variant: "error" }),
|
|
682
|
+
info: (title, description) => useToastStore.getState().push({ title, description, variant: "info" })
|
|
683
|
+
};
|
|
684
|
+
function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
685
|
+
const [email, setEmail] = useState(defaultEmail);
|
|
686
|
+
const [password, setPassword] = useState("");
|
|
687
|
+
const [busy, setBusy] = useState(false);
|
|
688
|
+
const [error, setError] = useState(null);
|
|
689
|
+
useEffect(() => {
|
|
690
|
+
const onKey = (event) => {
|
|
691
|
+
if (event.key === "Escape") onClose();
|
|
692
|
+
};
|
|
693
|
+
document.addEventListener("keydown", onKey);
|
|
694
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
695
|
+
}, [onClose]);
|
|
696
|
+
async function submit(event) {
|
|
697
|
+
event.preventDefault();
|
|
698
|
+
if (!email.trim() || !password) return;
|
|
699
|
+
setBusy(true);
|
|
700
|
+
setError(null);
|
|
701
|
+
try {
|
|
702
|
+
await establishCustomerSession(email, { password });
|
|
703
|
+
const session = useSessionStore.getState();
|
|
704
|
+
onSignedIn?.({ email: session.email, name: session.name });
|
|
705
|
+
onClose();
|
|
706
|
+
} catch {
|
|
707
|
+
setError("N\xE3o foi poss\xEDvel entrar. Verifique seu e-mail e senha.");
|
|
708
|
+
} finally {
|
|
709
|
+
setBusy(false);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return /* @__PURE__ */ jsxs(
|
|
713
|
+
"div",
|
|
714
|
+
{
|
|
715
|
+
className: "fixed inset-0 z-[60] flex items-center justify-center p-4",
|
|
716
|
+
role: "dialog",
|
|
717
|
+
"aria-modal": "true",
|
|
718
|
+
"aria-label": "Entrar na sua conta",
|
|
719
|
+
children: [
|
|
720
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 animate-fade-in bg-foreground/40 backdrop-blur-sm", onClick: onClose }),
|
|
721
|
+
/* @__PURE__ */ jsxs(
|
|
722
|
+
"div",
|
|
723
|
+
{
|
|
724
|
+
className: "relative z-10 w-full max-w-sm animate-fade-up border bg-card p-6 shadow-2xl",
|
|
725
|
+
style: { borderRadius: "var(--sf-radius-card)" },
|
|
726
|
+
children: [
|
|
727
|
+
/* @__PURE__ */ jsx(
|
|
728
|
+
"button",
|
|
729
|
+
{
|
|
730
|
+
type: "button",
|
|
731
|
+
onClick: onClose,
|
|
732
|
+
"aria-label": "Fechar",
|
|
733
|
+
className: "absolute right-4 top-4 rounded-full p-1 text-muted-foreground transition hover:bg-muted hover:text-foreground",
|
|
734
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
|
|
735
|
+
}
|
|
736
|
+
),
|
|
737
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
738
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(Lock, { className: "h-4 w-4" }) }),
|
|
739
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
740
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-lg font-semibold leading-tight", children: "Entrar na sua conta" }),
|
|
741
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Use seus dados salvos e acompanhe pedidos." })
|
|
742
|
+
] })
|
|
743
|
+
] }),
|
|
744
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "mt-5 space-y-3", children: [
|
|
745
|
+
/* @__PURE__ */ jsx(
|
|
746
|
+
"input",
|
|
747
|
+
{
|
|
748
|
+
type: "email",
|
|
749
|
+
required: true,
|
|
750
|
+
autoFocus: true,
|
|
751
|
+
placeholder: "voce@exemplo.com",
|
|
752
|
+
value: email,
|
|
753
|
+
onChange: (event) => setEmail(event.target.value),
|
|
754
|
+
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",
|
|
755
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
756
|
+
}
|
|
757
|
+
),
|
|
758
|
+
/* @__PURE__ */ jsx(
|
|
759
|
+
"input",
|
|
760
|
+
{
|
|
761
|
+
type: "password",
|
|
762
|
+
required: true,
|
|
763
|
+
placeholder: "Senha",
|
|
764
|
+
value: password,
|
|
765
|
+
onChange: (event) => setPassword(event.target.value),
|
|
766
|
+
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",
|
|
767
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
768
|
+
}
|
|
769
|
+
),
|
|
770
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
"button",
|
|
773
|
+
{
|
|
774
|
+
type: "submit",
|
|
775
|
+
disabled: busy,
|
|
776
|
+
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",
|
|
777
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
778
|
+
children: busy ? "Entrando\u2026" : "Entrar"
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
] }),
|
|
782
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-4 text-center text-sm text-muted-foreground", children: [
|
|
783
|
+
"N\xE3o tem conta?",
|
|
784
|
+
" ",
|
|
785
|
+
/* @__PURE__ */ jsx(Link, { to: "/account", onClick: onClose, className: "font-semibold text-primary hover:underline", children: "Criar conta" })
|
|
786
|
+
] })
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
)
|
|
790
|
+
]
|
|
791
|
+
}
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/app/return-path.ts
|
|
796
|
+
var KEY = "fayz:sf:return-path";
|
|
797
|
+
function rememberReturnPath(path) {
|
|
798
|
+
try {
|
|
799
|
+
if (typeof sessionStorage === "undefined") return;
|
|
800
|
+
sessionStorage.setItem(KEY, path);
|
|
801
|
+
} catch {
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function returnPath(config) {
|
|
805
|
+
try {
|
|
806
|
+
if (typeof sessionStorage !== "undefined") {
|
|
807
|
+
const remembered = sessionStorage.getItem(KEY);
|
|
808
|
+
if (remembered) return remembered;
|
|
809
|
+
}
|
|
810
|
+
} catch {
|
|
811
|
+
}
|
|
812
|
+
return config.delivery?.menuPath ?? config.catalogPath;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
export { BEFORE_PLACE_ORDER_VALVE, BUYER_COUNTRY_COOKIE, CART_LINE_ANNOTATION_POINT, CHECKOUT_FIELD_POINT, ORDER_METADATA_POINT, PAGE_DATA_SCRIPT_ID, PRODUCT_CARD_BADGE_POINT, STOREFRONT_SLOTS, SignInModal, Slot, StorefrontPage, collectOrderMetadata, getCartLineAnnotations, getCheckoutFieldContributions, getProductCardBadges, hydratedEntriesFor, identifyStorefrontBuyer, lastStorefrontEventAt, listSlots, onStorefrontBuyer, onStorefrontEvent, readHydratedPageData, registerStorefrontExtensionPoints, rememberReturnPath, resetPageDiagnostics, resetStorefrontExtensionPoints, returnPath, runBeforePlaceOrderValve, serializePageData, setStorefrontIdentifier, toast, trackStorefront, useBuyerCountry, useBuyerStore, useDiscountValidator, useSlotContext, useSlotFilled, useStorefrontPage, useStorefrontPageOptional, useT, useToastStore, usedSlots };
|
|
816
|
+
//# sourceMappingURL=chunk-MFAU3AWK.js.map
|
|
817
|
+
//# sourceMappingURL=chunk-MFAU3AWK.js.map
|