@fayz-ai/storefront 0.80.0 → 0.82.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.
- package/dist/analytics/bus.d.ts +3 -1
- package/dist/analytics/bus.d.ts.map +1 -1
- package/dist/analytics/posthog.d.ts.map +1 -1
- package/dist/app/prerender.d.ts +1 -9
- package/dist/app/prerender.d.ts.map +1 -1
- package/dist/app/router.d.ts +1 -1
- package/dist/app/sitemap.d.ts +10 -0
- package/dist/app/sitemap.d.ts.map +1 -0
- package/dist/blocks/registry.d.ts.map +1 -1
- package/dist/chunk-746BEU7H.js +160 -0
- package/dist/chunk-746BEU7H.js.map +1 -0
- package/dist/chunk-GI5BTJK6.js +1336 -0
- package/dist/chunk-GI5BTJK6.js.map +1 -0
- package/dist/{chunk-V7CGLOEN.js → chunk-RYWQGQ4M.js} +367 -2740
- package/dist/chunk-RYWQGQ4M.js.map +1 -0
- package/dist/chunk-UJHCVRK3.js +1184 -0
- package/dist/chunk-UJHCVRK3.js.map +1 -0
- package/dist/chunk-WCAQRATH.js +30 -0
- package/dist/chunk-WCAQRATH.js.map +1 -0
- package/dist/components/delivery/StoreCover.d.ts.map +1 -1
- package/dist/data/menu-needs.d.ts +30 -0
- package/dist/data/menu-needs.d.ts.map +1 -0
- package/dist/document/config.d.ts +22 -0
- package/dist/document/config.d.ts.map +1 -1
- package/dist/edge.d.ts +2 -0
- package/dist/edge.d.ts.map +1 -1
- package/dist/edge.js +137 -1
- package/dist/edge.js.map +1 -1
- package/dist/experiments/assign.d.ts +113 -0
- package/dist/experiments/assign.d.ts.map +1 -0
- package/dist/experiments/react.d.ts +98 -0
- package/dist/experiments/react.d.ts.map +1 -0
- package/dist/host/edge-handler.d.ts +65 -0
- package/dist/host/edge-handler.d.ts.map +1 -0
- package/dist/host/image-origin.d.ts +16 -0
- package/dist/host/image-origin.d.ts.map +1 -0
- package/dist/host/store-host.d.ts +141 -0
- package/dist/host/store-host.d.ts.map +1 -0
- package/dist/host.d.ts +9 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +229 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/render.js +7 -27
- package/dist/render.js.map +1 -1
- package/dist/tanstack.d.ts +35 -0
- package/dist/tanstack.d.ts.map +1 -0
- package/dist/tanstack.js +116 -0
- package/dist/tanstack.js.map +1 -0
- package/dist/views/MenuPage.d.ts +21 -6
- package/dist/views/MenuPage.d.ts.map +1 -1
- package/package.json +17 -2
- package/dist/chunk-V7CGLOEN.js.map +0 -1
|
@@ -1,26 +1,27 @@
|
|
|
1
|
+
import { Link, useStorefrontConfig, useStorefrontConfigOptional, useBlockData, BlockDataError, navigateTo, useRoutePath, toBlockNodes, defaultPageBlocks, PageDataScope, getStorefrontComponents, useLoader, currentStaticRenderPass, matchPath, translate, productSeo, StorefrontConfigProvider, mergePageTheme, pageThemeSelector, MENU_DATA_NEEDS, resolveConfig, useRouterAdapter, shellRouterAdapter, StorefrontRouterProvider, useScrollToTopOnNavigate, checkPageBlocks, normalizeLanguage, useHashPath, resolveStorefrontRoute, matchContentPage } from './chunk-UJHCVRK3.js';
|
|
2
|
+
import { CONTROL, readVisitorId } from './chunk-746BEU7H.js';
|
|
1
3
|
import { create } from 'zustand';
|
|
2
4
|
import { persist } from 'zustand/middleware';
|
|
3
|
-
import { normalizePostalCode, getCountrySchema, lookupPostalCode,
|
|
5
|
+
import { normalizePostalCode, getCountrySchema, lookupPostalCode, useBlockPath, BlockRenderer, BlockChildren, defineLoader, repairBlockTree, renderBlocks, clearExtensionPoints, defineExtensionPoint, runExtensionPoint, runEscapeValve, errorMessage, hasEntityDeclaration, resolveEntityDeclaration, defineBlock, formatPostalCode, countryName, defineEntity, defaultRouterAdapter, describeError, validateAddress } from '@fayz-ai/core';
|
|
4
6
|
import { getShopProvider, setShopProvider } from '@fayz-ai/shop/runtime';
|
|
5
|
-
import * as
|
|
6
|
-
import
|
|
7
|
+
import * as React37 from 'react';
|
|
8
|
+
import React37__default, { createContext, useRef, useState, useEffect, useMemo, useContext, useCallback } from 'react';
|
|
9
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
10
|
import { createMockAuthAdapter } from '@fayz-ai/auth';
|
|
8
11
|
import { createAuthRuntime } from '@fayz-ai/auth/lib/runtime';
|
|
9
12
|
import { setShopCarrierQuotesResolver, setShopAccessTokenResolver, setShopShippingItemsResolver } from '@fayz-ai/sdk/shop';
|
|
10
|
-
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
13
|
import { isStorageImageUrl, imageVariantUrl, imageSrcSet, IMAGE_SRCSET_WIDTHS, validateDiscount, documentDigits, isChargeableProvider, supportsCard, formatDocument, isValidDocument, gatewayLabel, isValidCpf } from '@fayz-ai/shop';
|
|
12
14
|
import { ChevronLeft, ChevronRight, ShoppingBag, X, Minus, Plus, Send, MessageCircle, Mail, Truck, RefreshCcw, ShieldCheck, ChevronDown, Check, Search, Menu, Trash2, MapPin, Lock, CreditCard, UserRound, Store, Bike, Clock, Pencil, Star, Phone, User, UserCircle, Package, LogOut, QrCode, Copy, XCircle, RotateCcw, PackageCheck, ChefHat, Info, AlertCircle } from 'lucide-react';
|
|
13
15
|
import { getLucideIcon } from '@fayz-ai/ui/icons';
|
|
14
|
-
import { storefrontDiscountToPromotion, previewCouponPercent, discountToPromotion
|
|
16
|
+
import { storefrontDiscountToPromotion, previewCouponPercent, discountToPromotion } from '@fayz-ai/shop/rules';
|
|
15
17
|
import { createMockShopProvider } from '@fayz-ai/shop/mock';
|
|
16
18
|
|
|
17
|
-
// src/stores/market.store.ts
|
|
18
19
|
var useMarketStore = create()((set) => ({
|
|
19
20
|
markets: [],
|
|
20
21
|
active: null,
|
|
21
22
|
prices: {},
|
|
22
23
|
setMarkets: (markets) => set({ markets }),
|
|
23
|
-
setActive: (
|
|
24
|
+
setActive: (active) => set({ active }),
|
|
24
25
|
setPrices: (code, list) => set((state) => ({
|
|
25
26
|
prices: {
|
|
26
27
|
...state.prices,
|
|
@@ -531,6 +532,95 @@ async function storeBuyerId(storeId, email) {
|
|
|
531
532
|
const hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
532
533
|
return `store:${storeId}:${hex}`;
|
|
533
534
|
}
|
|
535
|
+
function isExperimentBinding(value) {
|
|
536
|
+
return typeof value === "object" && value !== null && typeof value.experiment === "string" && typeof value.variants === "object";
|
|
537
|
+
}
|
|
538
|
+
function resolveExperimental(value, variant) {
|
|
539
|
+
if (!isExperimentBinding(value)) return value;
|
|
540
|
+
return value.variants[variant] ?? value.variants.control;
|
|
541
|
+
}
|
|
542
|
+
var bootstrap = null;
|
|
543
|
+
function getExperimentBootstrap() {
|
|
544
|
+
return bootstrap;
|
|
545
|
+
}
|
|
546
|
+
var ExperimentContext = React37__default.createContext(null);
|
|
547
|
+
function ExperimentProvider({ assignments, path, children }) {
|
|
548
|
+
const config = useStorefrontConfig();
|
|
549
|
+
const kept = React37__default.useRef(null);
|
|
550
|
+
if (!kept.current && assignments) kept.current = assignments;
|
|
551
|
+
const current = kept.current ?? { flags: {} };
|
|
552
|
+
if (typeof window !== "undefined" && kept.current && !bootstrap) bootstrap = kept.current;
|
|
553
|
+
const declaredKeys = Object.keys(config.experiments ?? {}).sort().join("\n");
|
|
554
|
+
const declared = React37__default.useMemo(() => new Set(declaredKeys ? declaredKeys.split("\n") : []), [declaredKeys]);
|
|
555
|
+
const exposed = React37__default.useMemo(() => /* @__PURE__ */ new Set(), [path]);
|
|
556
|
+
const value = React37__default.useMemo(() => ({ assignments: current, declared, exposed }), [current, declared, exposed]);
|
|
557
|
+
return /* @__PURE__ */ jsx(ExperimentContext.Provider, { value, children });
|
|
558
|
+
}
|
|
559
|
+
function useExperiment(key) {
|
|
560
|
+
const state = React37__default.useContext(ExperimentContext);
|
|
561
|
+
const assigned = state?.declared.has(key) ? state.assignments.flags[key] : void 0;
|
|
562
|
+
const result = assigned ?? { variant: CONTROL };
|
|
563
|
+
const variant = result.variant;
|
|
564
|
+
const inExperiment = Boolean(assigned);
|
|
565
|
+
React37__default.useEffect(() => {
|
|
566
|
+
if (!state || !inExperiment || state.exposed.has(key)) return;
|
|
567
|
+
state.exposed.add(key);
|
|
568
|
+
trackStorefront("experiment_exposed", { experiment: key, variant });
|
|
569
|
+
}, [state, key, variant, inExperiment]);
|
|
570
|
+
return result;
|
|
571
|
+
}
|
|
572
|
+
function useExperimentValue(value) {
|
|
573
|
+
const bound = isExperimentBinding(value);
|
|
574
|
+
if (!bound) return value;
|
|
575
|
+
const { variant } = useExperiment(value.experiment);
|
|
576
|
+
return resolveExperimental(value, variant);
|
|
577
|
+
}
|
|
578
|
+
function ExperimentBlock({ experiment, node }) {
|
|
579
|
+
const { variant } = useExperiment(experiment ?? "");
|
|
580
|
+
const path = useBlockPath();
|
|
581
|
+
const variants = node?.children ?? [];
|
|
582
|
+
let index = variants.findIndex((child) => child.props?.variant === variant);
|
|
583
|
+
if (index < 0) index = variants.findIndex((child) => child.props?.variant === CONTROL);
|
|
584
|
+
if (index < 0) return null;
|
|
585
|
+
return /* @__PURE__ */ jsx(BlockRenderer, { node: variants[index], path: path ? `${path}.children[${index}]` : void 0 });
|
|
586
|
+
}
|
|
587
|
+
function ExperimentVariantBlock({ node }) {
|
|
588
|
+
return /* @__PURE__ */ jsx(BlockChildren, { node });
|
|
589
|
+
}
|
|
590
|
+
var experimentBlockMeta = {
|
|
591
|
+
label: "Experiment",
|
|
592
|
+
description: 'A/B test: shows one of its variants to each visitor, as decided by the PostHog flag of the same key. The variant named "control" is the fallback.',
|
|
593
|
+
settings: [
|
|
594
|
+
{
|
|
595
|
+
id: "experiment",
|
|
596
|
+
type: "text",
|
|
597
|
+
label: "Experiment key",
|
|
598
|
+
required: true,
|
|
599
|
+
help: "The PostHog flag key, declared in the store config under `experiments`."
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
presets: [{ name: "A/B test", props: { experiment: "my-experiment" } }],
|
|
603
|
+
enabledOn: ["home", "catalog", "product", "collection", "content"]
|
|
604
|
+
};
|
|
605
|
+
var experimentVariantBlockMeta = {
|
|
606
|
+
label: "Experiment variant",
|
|
607
|
+
description: "The blocks one variant of an experiment shows.",
|
|
608
|
+
parent: ["experiment"],
|
|
609
|
+
settings: [
|
|
610
|
+
{
|
|
611
|
+
id: "variant",
|
|
612
|
+
type: "text",
|
|
613
|
+
label: "Variant key",
|
|
614
|
+
required: true,
|
|
615
|
+
help: 'Exactly as on the PostHog flag, e.g. "control" or "test".'
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
presets: [
|
|
619
|
+
{ name: "Control", props: { variant: "control" } },
|
|
620
|
+
{ name: "Test", props: { variant: "test" } }
|
|
621
|
+
],
|
|
622
|
+
enabledOn: ["home", "catalog", "product", "collection", "content"]
|
|
623
|
+
};
|
|
534
624
|
|
|
535
625
|
// src/analytics/posthog.ts
|
|
536
626
|
var started = null;
|
|
@@ -540,7 +630,24 @@ function startStorefrontPostHog(config, context) {
|
|
|
540
630
|
started = (async () => {
|
|
541
631
|
const { default: posthog } = await import('posthog-js');
|
|
542
632
|
const store = { store_id: config.storeId, store_name: context.storeName, commerce_mode: context.commerceMode };
|
|
633
|
+
const experiments = getExperimentBootstrap();
|
|
634
|
+
const hasPostHogCookie = document.cookie.includes(`ph_${config.key}_posthog=`);
|
|
635
|
+
const visitorId = hasPostHogCookie ? void 0 : readVisitorId(document.cookie, config.key);
|
|
636
|
+
const flags = experiments?.flags ?? {};
|
|
637
|
+
const bootstrap2 = Object.keys(flags).length || visitorId ? {
|
|
638
|
+
...visitorId ? { distinctID: visitorId } : {},
|
|
639
|
+
featureFlags: Object.fromEntries(Object.entries(flags).map(([k, a]) => [k, a.variant])),
|
|
640
|
+
featureFlagPayloads: Object.fromEntries(
|
|
641
|
+
Object.entries(flags).filter(([, a]) => a.payload !== void 0).map(([k, a]) => [k, a.payload])
|
|
642
|
+
)
|
|
643
|
+
} : void 0;
|
|
543
644
|
posthog.init(config.key, {
|
|
645
|
+
...bootstrap2 ? { bootstrap: bootstrap2 } : {},
|
|
646
|
+
// With variants bootstrapped, the browser does not re-ask /flags on
|
|
647
|
+
// load: an answer that differed from the rendered page (a server-side
|
|
648
|
+
// timeout, a rollout change between the two calls) would tag this
|
|
649
|
+
// visitor's events with a variant they never saw.
|
|
650
|
+
...Object.keys(flags).length ? { advanced_disable_feature_flags_on_first_load: true } : {},
|
|
544
651
|
api_host: config.apiHost ?? "/ingest",
|
|
545
652
|
ui_host: config.uiHost ?? "https://us.posthog.com",
|
|
546
653
|
defaults: "2026-08-30",
|
|
@@ -559,6 +666,16 @@ function startStorefrontPostHog(config, context) {
|
|
|
559
666
|
});
|
|
560
667
|
posthog.register(store);
|
|
561
668
|
onStorefrontEvent((event) => {
|
|
669
|
+
if (event.name === "experiment_exposed") {
|
|
670
|
+
const { experiment, variant } = event.properties;
|
|
671
|
+
if (typeof experiment !== "string" || typeof variant !== "string") return;
|
|
672
|
+
posthog.capture("$feature_flag_called", {
|
|
673
|
+
$feature_flag: experiment,
|
|
674
|
+
$feature_flag_response: variant,
|
|
675
|
+
[`$feature/${experiment}`]: variant
|
|
676
|
+
});
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
562
679
|
posthog.capture(event.name, event.properties, { timestamp: new Date(event.at) });
|
|
563
680
|
});
|
|
564
681
|
setStorefrontIdentifier((email) => {
|
|
@@ -698,19 +815,19 @@ function StorefrontAnalytics({ config, path }) {
|
|
|
698
815
|
const metaPixel = pixelId && META_PIXEL_ID.test(pixelId) ? pixelId : null;
|
|
699
816
|
const reporting = Boolean(posthog || metaPixel);
|
|
700
817
|
const menuPath = config.delivery?.menuPath;
|
|
701
|
-
const previous =
|
|
702
|
-
|
|
818
|
+
const previous = React37.useRef(null);
|
|
819
|
+
React37.useEffect(() => {
|
|
703
820
|
if (!posthog) return;
|
|
704
821
|
startStorefrontPostHog(posthog, { storeName: config.name, commerceMode: config.commerceMode });
|
|
705
822
|
}, [posthog, config.name, config.commerceMode]);
|
|
706
|
-
|
|
823
|
+
React37.useEffect(() => {
|
|
707
824
|
if (!metaPixel) return;
|
|
708
825
|
startStorefrontMetaPixel({ pixelId: metaPixel }, { currency: config.currency ?? "BRL" });
|
|
709
826
|
}, [metaPixel, config.currency]);
|
|
710
|
-
|
|
827
|
+
React37.useEffect(() => {
|
|
711
828
|
if (metaPixel) trackMetaPageView();
|
|
712
829
|
}, [metaPixel, path]);
|
|
713
|
-
|
|
830
|
+
React37.useEffect(() => {
|
|
714
831
|
if (!reporting) return;
|
|
715
832
|
let ready = useCartStore.persist?.hasHydrated?.() ?? true;
|
|
716
833
|
const unhydrated = useCartStore.persist?.onFinishHydration?.(() => {
|
|
@@ -741,7 +858,7 @@ function StorefrontAnalytics({ config, path }) {
|
|
|
741
858
|
unhydrated?.();
|
|
742
859
|
};
|
|
743
860
|
}, [reporting]);
|
|
744
|
-
|
|
861
|
+
React37.useEffect(() => {
|
|
745
862
|
if (!reporting) return;
|
|
746
863
|
const from = previous.current;
|
|
747
864
|
previous.current = path;
|
|
@@ -1230,543 +1347,6 @@ function productPlaceholder(label, w = PLACEHOLDER_ASPECTS.product[0], h = PLACE
|
|
|
1230
1347
|
function escapeXml(text) {
|
|
1231
1348
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1232
1349
|
}
|
|
1233
|
-
|
|
1234
|
-
// src/locales/messages.ts
|
|
1235
|
-
var ptBR = {
|
|
1236
|
-
// Checkout shell
|
|
1237
|
-
"checkout.title": "Checkout \u2014 {{store}}",
|
|
1238
|
-
"checkout.backToStore": "Voltar \xE0 loja",
|
|
1239
|
-
"checkout.secure": "Checkout seguro",
|
|
1240
|
-
"checkout.help": "Ajuda",
|
|
1241
|
-
"checkout.steps.label": "Etapas do checkout",
|
|
1242
|
-
"checkout.steps.1": "Dados",
|
|
1243
|
-
"checkout.steps.2": "Entrega",
|
|
1244
|
-
"checkout.steps.3": "Pagamento",
|
|
1245
|
-
"checkout.steps.4": "Pronto",
|
|
1246
|
-
// Step 1 — contact
|
|
1247
|
-
"checkout.contact.title": "Informa\xE7\xF5es de contato",
|
|
1248
|
-
"checkout.contact.signedInAs": "Conectado como {{email}}",
|
|
1249
|
-
"checkout.contact.edit": "Alterar",
|
|
1250
|
-
"checkout.contact.notYou": "N\xE3o \xE9 voc\xEA?",
|
|
1251
|
-
"checkout.contact.signOut": "Sair",
|
|
1252
|
-
"checkout.contact.emailLocked": "O e-mail \xE9 o da sua conta.",
|
|
1253
|
-
"checkout.contact.haveAccount": "J\xE1 tem conta? Entrar",
|
|
1254
|
-
"checkout.contact.email": "E-mail",
|
|
1255
|
-
"checkout.contact.name": "Nome completo",
|
|
1256
|
-
"checkout.contact.phone": "Telefone",
|
|
1257
|
-
"checkout.contact.phoneOptional": "Telefone (opcional)",
|
|
1258
|
-
"checkout.contact.document": "CPF ou CNPJ",
|
|
1259
|
-
"checkout.contact.documentOptional": "CPF ou CNPJ (opcional)",
|
|
1260
|
-
"checkout.contact.requiresAccount": "Esta loja pede uma conta para finalizar a compra.",
|
|
1261
|
-
"checkout.contact.signInOrCreate": "Entrar ou criar conta",
|
|
1262
|
-
// Step 2 — address
|
|
1263
|
-
"checkout.address.title": "Endere\xE7o de entrega",
|
|
1264
|
-
"checkout.address.loadingSaved": "Buscando seus endere\xE7os salvos",
|
|
1265
|
-
"checkout.address.selected": "Selecionado",
|
|
1266
|
-
"checkout.address.addNew": "+ Adicionar novo endere\xE7o",
|
|
1267
|
-
"checkout.address.enter": "Informe o endere\xE7o de entrega",
|
|
1268
|
-
"checkout.address.enterHint": "Preencher outro endere\xE7o para esta compra",
|
|
1269
|
-
"checkout.address.lookingUp": "Buscando o endere\xE7o deste CEP",
|
|
1270
|
-
"checkout.address.notFound": "CEP n\xE3o encontrado \u2014 preencha o endere\xE7o \xE0 m\xE3o.",
|
|
1271
|
-
"checkout.address.filled": "Preenchemos o endere\xE7o para voc\xEA.",
|
|
1272
|
-
"checkout.address.regionFromPostal": "Prov\xEDncia preenchida pelo c\xF3digo postal.",
|
|
1273
|
-
"checkout.address.optional": "{{label}} (opcional)",
|
|
1274
|
-
"checkout.address.choose": "Selecione",
|
|
1275
|
-
"checkout.address.shipTo": "Enviar para",
|
|
1276
|
-
// Step 3 — payment
|
|
1277
|
-
"checkout.confirming.title": "Confirmando seu pedido",
|
|
1278
|
-
"checkout.confirming.label": "Criando seu pedido",
|
|
1279
|
-
"checkout.confirming.keepOpen": "N\xE3o feche esta p\xE1gina \u2014 estamos registrando seu pedido na loja.",
|
|
1280
|
-
"checkout.payment.title": "Pagamento",
|
|
1281
|
-
"checkout.payment.label": "Forma de pagamento",
|
|
1282
|
-
"checkout.payment.cardOnline": "Voc\xEA paga na p\xE1gina segura do provedor e volta para a loja",
|
|
1283
|
-
"checkout.payment.placeOrder": "Fazer pedido - {{total}}",
|
|
1284
|
-
"checkout.payment.pendingNote": "O pedido fica aguardando pagamento at\xE9 a loja confirmar.",
|
|
1285
|
-
"checkout.payment.unavailableCountry": "Esta forma de pagamento n\xE3o est\xE1 dispon\xEDvel para {{country}}.",
|
|
1286
|
-
"payment.pix.label": "Pix",
|
|
1287
|
-
"payment.pix.hint": "Voc\xEA recebe a chave para pagar ap\xF3s confirmar o pedido",
|
|
1288
|
-
"payment.credit_card.label": "Cart\xE3o de cr\xE9dito",
|
|
1289
|
-
"payment.credit_card.hint": "Maquininha na entrega",
|
|
1290
|
-
"payment.debit_card.label": "Cart\xE3o de d\xE9bito",
|
|
1291
|
-
"payment.debit_card.hint": "Maquininha na entrega",
|
|
1292
|
-
"payment.boleto.label": "Boleto",
|
|
1293
|
-
"payment.boleto.hint": "Enviado por e-mail ap\xF3s a confirma\xE7\xE3o",
|
|
1294
|
-
"payment.cash.label": "Dinheiro",
|
|
1295
|
-
"payment.cash.hint": "Pagamento na entrega",
|
|
1296
|
-
"payment.other.label": "Combinar com a loja",
|
|
1297
|
-
"payment.other.hint": "A loja entra em contato para acertar o pagamento",
|
|
1298
|
-
// Step 4
|
|
1299
|
-
"checkout.done.title": "Pagamento confirmado!",
|
|
1300
|
-
"checkout.done.text": "Seu pedido foi criado e a loja j\xE1 foi avisada.",
|
|
1301
|
-
"checkout.done.viewOrder": "Ver meu pedido",
|
|
1302
|
-
// Navigation
|
|
1303
|
-
"checkout.continueToDelivery": "Continuar para a entrega",
|
|
1304
|
-
"checkout.goToPayment": "Ir para o pagamento - {{total}}",
|
|
1305
|
-
"checkout.backToContact": "Voltar aos dados",
|
|
1306
|
-
"checkout.backToCart": "Voltar ao carrinho",
|
|
1307
|
-
// Summary
|
|
1308
|
-
"summary.title": "Resumo do pedido",
|
|
1309
|
-
"summary.subtotal": "Subtotal",
|
|
1310
|
-
"summary.discount": "Desconto",
|
|
1311
|
-
"summary.delivery": "Entrega",
|
|
1312
|
-
"summary.pickup": "Retirada na loja",
|
|
1313
|
-
"summary.noFee": "Sem taxa",
|
|
1314
|
-
"summary.free": "Gr\xE1tis",
|
|
1315
|
-
"summary.total": "Total",
|
|
1316
|
-
"summary.protected": "Compra protegida e dados criptografados.",
|
|
1317
|
-
"summary.shipping": "Frete",
|
|
1318
|
-
// Discount
|
|
1319
|
-
"discount.placeholder": "Cupom de desconto",
|
|
1320
|
-
"discount.apply": "Aplicar",
|
|
1321
|
-
"discount.freeShipping": "frete gr\xE1tis",
|
|
1322
|
-
"discount.percentOff": "{{percent}}% de desconto",
|
|
1323
|
-
"discount.applied": "{{code}} aplicado: {{effect}}.",
|
|
1324
|
-
"discount.appliedToast": "Cupom aplicado!",
|
|
1325
|
-
"discount.invalid": "Cupom inv\xE1lido.",
|
|
1326
|
-
"discount.invalidToast": "Cupom inv\xE1lido",
|
|
1327
|
-
"discount.checkCode": "Verifique o c\xF3digo e tente novamente.",
|
|
1328
|
-
"discount.cannotValidate": "N\xE3o conseguimos validar esse cupom agora.",
|
|
1329
|
-
"discount.cannotValidateToast": "N\xE3o conseguimos validar o cupom",
|
|
1330
|
-
"discount.tryLater": "Tente de novo em alguns instantes.",
|
|
1331
|
-
// Legal
|
|
1332
|
-
"legal.privacy": "Privacidade",
|
|
1333
|
-
"legal.terms": "Termos",
|
|
1334
|
-
"legal.refunds": "Trocas e devolu\xE7\xF5es",
|
|
1335
|
-
// Validation
|
|
1336
|
-
"error.requiresAccount": "Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.",
|
|
1337
|
-
"error.email": "Informe um e-mail v\xE1lido.",
|
|
1338
|
-
"error.name": "Informe seu nome completo.",
|
|
1339
|
-
"error.phone": "Informe um telefone de contato.",
|
|
1340
|
-
"error.documentInvalid": "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros.",
|
|
1341
|
-
"error.documentMissing": "Informe o CPF ou CNPJ do comprador.",
|
|
1342
|
-
"error.taxIdInvalid": "NIF/NIE inv\xE1lido \u2014 confira o n\xFAmero.",
|
|
1343
|
-
"error.address": "Preencha ou selecione o endere\xE7o de entrega.",
|
|
1344
|
-
"error.number": "Falta o n\xFAmero do endere\xE7o.",
|
|
1345
|
-
"error.complement": "Informe o complemento (apto, bloco).",
|
|
1346
|
-
"error.state": "Informe a UF com duas letras (ex.: RJ).",
|
|
1347
|
-
"error.postalInvalid": "Confira o c\xF3digo postal (ex.: {{example}}).",
|
|
1348
|
-
"error.region": "Selecione a {{label}}.",
|
|
1349
|
-
"error.refusedZone": "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro.",
|
|
1350
|
-
"error.unserved": "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
1351
|
-
"error.sessionMismatch": "Sua sess\xE3o ficou presa a outro cadastro. Recarregue a p\xE1gina e tente de novo.",
|
|
1352
|
-
"error.outOfStock": "Um item do carrinho ficou sem estoque. Revise o carrinho e tente de novo.",
|
|
1353
|
-
"error.placeOrder": "N\xE3o foi poss\xEDvel concluir o pedido.",
|
|
1354
|
-
// Delivery estimator
|
|
1355
|
-
"delivery.title": "Calcular entrega",
|
|
1356
|
-
"delivery.calculate": "Calcular",
|
|
1357
|
-
"delivery.postalLabel": "CEP",
|
|
1358
|
-
"delivery.change": "Trocar CEP",
|
|
1359
|
-
"delivery.unserved": "Ainda n\xE3o entregamos nesse CEP.",
|
|
1360
|
-
"delivery.refused": "Esta loja n\xE3o entrega nesse endere\xE7o.",
|
|
1361
|
-
"delivery.review": "Entregamos nessa regi\xE3o, mas confirmamos com voc\xEA antes de sair.",
|
|
1362
|
-
"delivery.etaSoon": "hoje ou amanh\xE3",
|
|
1363
|
-
"delivery.etaRange": "{{min}} a {{max}} dias \xFAteis",
|
|
1364
|
-
"delivery.etaDays": "{{days}} dia(s) \xFAtil(eis)",
|
|
1365
|
-
"delivery.flatRate": "O frete \xE9 calculado no checkout.",
|
|
1366
|
-
"delivery.error.digits": "Digite os 8 d\xEDgitos do CEP.",
|
|
1367
|
-
"delivery.error.postal": "Confira o c\xF3digo postal (ex.: {{example}}).",
|
|
1368
|
-
"delivery.error.quote": "N\xE3o foi poss\xEDvel calcular o frete agora. Tente de novo.",
|
|
1369
|
-
"delivery.error.notFound": "CEP n\xE3o encontrado. Confira os n\xFAmeros.",
|
|
1370
|
-
// Cart
|
|
1371
|
-
"cart.title": "Carrinho",
|
|
1372
|
-
"cart.close": "Fechar carrinho",
|
|
1373
|
-
"cart.empty": "Seu carrinho est\xE1 vazio.",
|
|
1374
|
-
"cart.remove": "Remover {{name}}",
|
|
1375
|
-
"cart.checkout": "Finalizar compra",
|
|
1376
|
-
"cart.missingForFree": "Faltam {{amount}} para frete gr\xE1tis",
|
|
1377
|
-
"cart.wonFree": "Voc\xEA ganhou frete gr\xE1tis!",
|
|
1378
|
-
"cart.minimumOrder": "Pedido m\xEDnimo para entrega: {{amount}}. Faltam {{missing}}.",
|
|
1379
|
-
"delivery.eta": "Entrega em ~{{duration}}",
|
|
1380
|
-
"delivery.etaUpTo": "Entrega em at\xE9 {{duration}}",
|
|
1381
|
-
"delivery.paused": "Delivery fechado no momento",
|
|
1382
|
-
"cart.deliveryPaused": "O delivery est\xE1 fechado no momento. Volte mais tarde para finalizar.",
|
|
1383
|
-
// Deliver to
|
|
1384
|
-
"country.deliverTo": "Enviar para",
|
|
1385
|
-
"country.change": "Trocar pa\xEDs",
|
|
1386
|
-
"country.label": "Pa\xEDs de entrega"
|
|
1387
|
-
};
|
|
1388
|
-
var en = {
|
|
1389
|
-
"checkout.backToStore": "Back to store",
|
|
1390
|
-
"checkout.secure": "Secure checkout",
|
|
1391
|
-
"checkout.help": "Help",
|
|
1392
|
-
"checkout.steps.label": "Checkout steps",
|
|
1393
|
-
"checkout.steps.1": "Details",
|
|
1394
|
-
"checkout.steps.2": "Delivery",
|
|
1395
|
-
"checkout.steps.3": "Payment",
|
|
1396
|
-
"checkout.steps.4": "Done",
|
|
1397
|
-
"checkout.contact.title": "Contact information",
|
|
1398
|
-
"checkout.contact.signedInAs": "Signed in as {{email}}",
|
|
1399
|
-
"checkout.contact.edit": "Change",
|
|
1400
|
-
"checkout.contact.notYou": "Not you?",
|
|
1401
|
-
"checkout.contact.signOut": "Sign out",
|
|
1402
|
-
"checkout.contact.emailLocked": "This is your account e-mail.",
|
|
1403
|
-
"checkout.contact.haveAccount": "Have an account? Sign in",
|
|
1404
|
-
"checkout.contact.email": "Email",
|
|
1405
|
-
"checkout.contact.name": "Full name",
|
|
1406
|
-
"checkout.contact.phone": "Phone",
|
|
1407
|
-
"checkout.contact.phoneOptional": "Phone (optional)",
|
|
1408
|
-
"checkout.contact.document": "Tax ID",
|
|
1409
|
-
"checkout.contact.documentOptional": "Tax ID (optional)",
|
|
1410
|
-
"checkout.contact.requiresAccount": "This store needs an account to complete the purchase.",
|
|
1411
|
-
"checkout.contact.signInOrCreate": "Sign in or create an account",
|
|
1412
|
-
"checkout.address.title": "Delivery address",
|
|
1413
|
-
"checkout.address.loadingSaved": "Loading your saved addresses",
|
|
1414
|
-
"checkout.address.selected": "Selected",
|
|
1415
|
-
"checkout.address.addNew": "+ Add a new address",
|
|
1416
|
-
"checkout.address.enter": "Enter the delivery address",
|
|
1417
|
-
"checkout.address.enterHint": "Use a different address for this order",
|
|
1418
|
-
"checkout.address.lookingUp": "Looking up this postal code",
|
|
1419
|
-
"checkout.address.notFound": "Postal code not found \u2014 fill in the address by hand.",
|
|
1420
|
-
"checkout.address.filled": "We filled in the address for you.",
|
|
1421
|
-
"checkout.address.regionFromPostal": "Province filled in from the postcode.",
|
|
1422
|
-
"checkout.address.optional": "{{label}} (optional)",
|
|
1423
|
-
"checkout.address.choose": "Select",
|
|
1424
|
-
"checkout.address.shipTo": "Ship to",
|
|
1425
|
-
"checkout.confirming.title": "Confirming your order",
|
|
1426
|
-
"checkout.confirming.label": "Creating your order",
|
|
1427
|
-
"checkout.confirming.keepOpen": "Don\u2019t close this page \u2014 we\u2019re registering your order with the store.",
|
|
1428
|
-
"checkout.payment.title": "Payment",
|
|
1429
|
-
"checkout.payment.label": "Payment method",
|
|
1430
|
-
"checkout.payment.cardOnline": "You pay on the provider\u2019s secure page and come back to the store",
|
|
1431
|
-
"checkout.payment.placeOrder": "Place order - {{total}}",
|
|
1432
|
-
"checkout.payment.pendingNote": "The order awaits payment until the store confirms it.",
|
|
1433
|
-
"checkout.payment.unavailableCountry": "This payment method isn\u2019t available for {{country}}.",
|
|
1434
|
-
"payment.pix.hint": "You get the key to pay after confirming the order",
|
|
1435
|
-
"payment.credit_card.label": "Credit card",
|
|
1436
|
-
"payment.credit_card.hint": "Card machine on delivery",
|
|
1437
|
-
"payment.debit_card.label": "Debit card",
|
|
1438
|
-
"payment.debit_card.hint": "Card machine on delivery",
|
|
1439
|
-
"payment.boleto.hint": "Sent by email after confirmation",
|
|
1440
|
-
"payment.cash.label": "Cash",
|
|
1441
|
-
"payment.cash.hint": "Pay on delivery",
|
|
1442
|
-
"payment.other.label": "Arrange with the store",
|
|
1443
|
-
"payment.other.hint": "The store will contact you to arrange payment",
|
|
1444
|
-
"checkout.done.title": "Payment confirmed!",
|
|
1445
|
-
"checkout.done.text": "Your order was created and the store has been notified.",
|
|
1446
|
-
"checkout.done.viewOrder": "View my order",
|
|
1447
|
-
"checkout.continueToDelivery": "Continue to delivery",
|
|
1448
|
-
"checkout.goToPayment": "Go to payment - {{total}}",
|
|
1449
|
-
"checkout.backToContact": "Back to details",
|
|
1450
|
-
"checkout.backToCart": "Back to cart",
|
|
1451
|
-
"summary.title": "Order summary",
|
|
1452
|
-
"summary.discount": "Discount",
|
|
1453
|
-
"summary.delivery": "Delivery",
|
|
1454
|
-
"summary.pickup": "Store pickup",
|
|
1455
|
-
"summary.noFee": "No fee",
|
|
1456
|
-
"summary.free": "Free",
|
|
1457
|
-
"summary.protected": "Protected purchase, encrypted data.",
|
|
1458
|
-
"summary.shipping": "Shipping",
|
|
1459
|
-
"discount.placeholder": "Discount code",
|
|
1460
|
-
"discount.apply": "Apply",
|
|
1461
|
-
"discount.freeShipping": "free shipping",
|
|
1462
|
-
"discount.percentOff": "{{percent}}% off",
|
|
1463
|
-
"discount.applied": "{{code}} applied: {{effect}}.",
|
|
1464
|
-
"discount.appliedToast": "Code applied!",
|
|
1465
|
-
"discount.invalid": "Invalid code.",
|
|
1466
|
-
"discount.invalidToast": "Invalid code",
|
|
1467
|
-
"discount.checkCode": "Check the code and try again.",
|
|
1468
|
-
"discount.cannotValidate": "We couldn\u2019t check this code right now.",
|
|
1469
|
-
"discount.cannotValidateToast": "We couldn\u2019t check the code",
|
|
1470
|
-
"discount.tryLater": "Try again in a moment.",
|
|
1471
|
-
"legal.privacy": "Privacy",
|
|
1472
|
-
"legal.terms": "Terms",
|
|
1473
|
-
"legal.refunds": "Returns",
|
|
1474
|
-
"error.requiresAccount": "This store needs an account to complete the purchase. Sign in or create one.",
|
|
1475
|
-
"error.email": "Enter a valid email.",
|
|
1476
|
-
"error.name": "Enter your full name.",
|
|
1477
|
-
"error.phone": "Enter a contact phone number.",
|
|
1478
|
-
"error.documentInvalid": "Invalid tax ID \u2014 check the numbers.",
|
|
1479
|
-
"error.documentMissing": "Enter the buyer\u2019s tax ID.",
|
|
1480
|
-
"error.taxIdInvalid": "Invalid NIF/NIE \u2014 check the number.",
|
|
1481
|
-
"error.address": "Fill in or select the delivery address.",
|
|
1482
|
-
"error.number": "The street number is missing.",
|
|
1483
|
-
"error.complement": "Enter the apartment or unit.",
|
|
1484
|
-
"error.state": "Enter the two-letter state code (e.g. RJ).",
|
|
1485
|
-
"error.postalInvalid": "Check the postal code (e.g. {{example}}).",
|
|
1486
|
-
"error.region": "Select the {{label}}.",
|
|
1487
|
-
"error.refusedZone": "This store doesn\u2019t deliver to this address. Try another.",
|
|
1488
|
-
"error.unserved": "We don\u2019t deliver to this postal code yet. Try another address.",
|
|
1489
|
-
"error.sessionMismatch": "Your session got attached to another account. Reload the page and try again.",
|
|
1490
|
-
"error.outOfStock": "An item in your cart is out of stock. Review the cart and try again.",
|
|
1491
|
-
"error.placeOrder": "We couldn\u2019t complete the order.",
|
|
1492
|
-
"delivery.title": "Estimate delivery",
|
|
1493
|
-
"delivery.calculate": "Estimate",
|
|
1494
|
-
"delivery.postalLabel": "Postal code",
|
|
1495
|
-
"delivery.change": "Change postal code",
|
|
1496
|
-
"delivery.unserved": "We don\u2019t deliver to this postal code yet.",
|
|
1497
|
-
"delivery.refused": "This store doesn\u2019t deliver to this address.",
|
|
1498
|
-
"delivery.review": "We deliver to this area, but we\u2019ll confirm with you before shipping.",
|
|
1499
|
-
"delivery.etaSoon": "today or tomorrow",
|
|
1500
|
-
"delivery.etaRange": "{{min}} to {{max}} business days",
|
|
1501
|
-
"delivery.etaDays": "{{days}} business day(s)",
|
|
1502
|
-
"delivery.flatRate": "Shipping is calculated at checkout.",
|
|
1503
|
-
"delivery.error.digits": "Enter all 8 digits of the CEP.",
|
|
1504
|
-
"delivery.error.postal": "Check the postal code (e.g. {{example}}).",
|
|
1505
|
-
"delivery.error.quote": "We couldn\u2019t calculate shipping right now. Try again.",
|
|
1506
|
-
"delivery.error.notFound": "Postal code not found. Check the numbers.",
|
|
1507
|
-
"cart.title": "Cart",
|
|
1508
|
-
"cart.close": "Close cart",
|
|
1509
|
-
"cart.empty": "Your cart is empty.",
|
|
1510
|
-
"cart.remove": "Remove {{name}}",
|
|
1511
|
-
"cart.checkout": "Checkout",
|
|
1512
|
-
"cart.missingForFree": "{{amount}} away from free shipping",
|
|
1513
|
-
"cart.wonFree": "You got free shipping!",
|
|
1514
|
-
"cart.minimumOrder": "Minimum order for delivery: {{amount}}. {{missing}} to go.",
|
|
1515
|
-
"delivery.eta": "Delivery in ~{{duration}}",
|
|
1516
|
-
"delivery.etaUpTo": "Delivery within {{duration}}",
|
|
1517
|
-
"delivery.paused": "Delivery closed right now",
|
|
1518
|
-
"cart.deliveryPaused": "Delivery is closed right now. Come back later to check out.",
|
|
1519
|
-
"country.deliverTo": "Deliver to",
|
|
1520
|
-
"country.change": "Change country",
|
|
1521
|
-
"country.label": "Delivery country"
|
|
1522
|
-
};
|
|
1523
|
-
var es = {
|
|
1524
|
-
"checkout.backToStore": "Volver a la tienda",
|
|
1525
|
-
"checkout.secure": "Pago seguro",
|
|
1526
|
-
"checkout.help": "Ayuda",
|
|
1527
|
-
"checkout.steps.label": "Pasos del pago",
|
|
1528
|
-
"checkout.steps.1": "Datos",
|
|
1529
|
-
"checkout.steps.2": "Env\xEDo",
|
|
1530
|
-
"checkout.steps.3": "Pago",
|
|
1531
|
-
"checkout.steps.4": "Listo",
|
|
1532
|
-
"checkout.contact.title": "Datos de contacto",
|
|
1533
|
-
"checkout.contact.signedInAs": "Has iniciado sesi\xF3n como {{email}}",
|
|
1534
|
-
"checkout.contact.edit": "Cambiar",
|
|
1535
|
-
"checkout.contact.notYou": "\xBFNo eres t\xFA?",
|
|
1536
|
-
"checkout.contact.signOut": "Salir",
|
|
1537
|
-
"checkout.contact.emailLocked": "Es el correo de tu cuenta.",
|
|
1538
|
-
"checkout.contact.haveAccount": "\xBFYa tienes cuenta? Entrar",
|
|
1539
|
-
"checkout.contact.email": "Email",
|
|
1540
|
-
"checkout.contact.name": "Nombre completo",
|
|
1541
|
-
"checkout.contact.phone": "Tel\xE9fono",
|
|
1542
|
-
"checkout.contact.phoneOptional": "Tel\xE9fono (opcional)",
|
|
1543
|
-
"checkout.contact.document": "NIF/NIE",
|
|
1544
|
-
"checkout.contact.documentOptional": "NIF/NIE (opcional)",
|
|
1545
|
-
"checkout.contact.requiresAccount": "Esta tienda pide una cuenta para finalizar la compra.",
|
|
1546
|
-
"checkout.contact.signInOrCreate": "Entrar o crear cuenta",
|
|
1547
|
-
"checkout.address.title": "Direcci\xF3n de env\xEDo",
|
|
1548
|
-
"checkout.address.loadingSaved": "Buscando tus direcciones guardadas",
|
|
1549
|
-
"checkout.address.selected": "Seleccionada",
|
|
1550
|
-
"checkout.address.addNew": "+ A\xF1adir una direcci\xF3n nueva",
|
|
1551
|
-
"checkout.address.enter": "Indica la direcci\xF3n de env\xEDo",
|
|
1552
|
-
"checkout.address.enterHint": "Usar otra direcci\xF3n para este pedido",
|
|
1553
|
-
"checkout.address.lookingUp": "Buscando este c\xF3digo postal",
|
|
1554
|
-
"checkout.address.notFound": "C\xF3digo postal no encontrado: rellena la direcci\xF3n a mano.",
|
|
1555
|
-
"checkout.address.filled": "Hemos rellenado la direcci\xF3n por ti.",
|
|
1556
|
-
"checkout.address.regionFromPostal": "Provincia rellenada a partir del c\xF3digo postal.",
|
|
1557
|
-
"checkout.address.optional": "{{label}} (opcional)",
|
|
1558
|
-
"checkout.address.choose": "Selecciona",
|
|
1559
|
-
"checkout.address.shipTo": "Enviar a",
|
|
1560
|
-
"checkout.confirming.title": "Confirmando tu pedido",
|
|
1561
|
-
"checkout.confirming.label": "Creando tu pedido",
|
|
1562
|
-
"checkout.confirming.keepOpen": "No cierres esta p\xE1gina: estamos registrando tu pedido en la tienda.",
|
|
1563
|
-
"checkout.payment.title": "Pago",
|
|
1564
|
-
"checkout.payment.label": "Forma de pago",
|
|
1565
|
-
"checkout.payment.cardOnline": "Pagas en la p\xE1gina segura del proveedor y vuelves a la tienda",
|
|
1566
|
-
"checkout.payment.placeOrder": "Hacer pedido - {{total}}",
|
|
1567
|
-
"checkout.payment.pendingNote": "El pedido queda pendiente de pago hasta que la tienda lo confirme.",
|
|
1568
|
-
"checkout.payment.unavailableCountry": "Esta forma de pago no est\xE1 disponible para {{country}}.",
|
|
1569
|
-
"payment.pix.hint": "Recibes la clave para pagar al confirmar el pedido",
|
|
1570
|
-
"payment.credit_card.label": "Tarjeta de cr\xE9dito",
|
|
1571
|
-
"payment.credit_card.hint": "Dat\xE1fono en la entrega",
|
|
1572
|
-
"payment.debit_card.label": "Tarjeta de d\xE9bito",
|
|
1573
|
-
"payment.debit_card.hint": "Dat\xE1fono en la entrega",
|
|
1574
|
-
"payment.boleto.hint": "Enviado por email tras la confirmaci\xF3n",
|
|
1575
|
-
"payment.cash.label": "Efectivo",
|
|
1576
|
-
"payment.cash.hint": "Pago en la entrega",
|
|
1577
|
-
"payment.other.label": "Acordar con la tienda",
|
|
1578
|
-
"payment.other.hint": "La tienda te contactar\xE1 para acordar el pago",
|
|
1579
|
-
"checkout.done.title": "\xA1Pago confirmado!",
|
|
1580
|
-
"checkout.done.text": "Tu pedido se ha creado y la tienda ya ha sido avisada.",
|
|
1581
|
-
"checkout.done.viewOrder": "Ver mi pedido",
|
|
1582
|
-
"checkout.continueToDelivery": "Continuar al env\xEDo",
|
|
1583
|
-
"checkout.goToPayment": "Ir al pago - {{total}}",
|
|
1584
|
-
"checkout.backToContact": "Volver a los datos",
|
|
1585
|
-
"checkout.backToCart": "Volver al carrito",
|
|
1586
|
-
"summary.title": "Resumen del pedido",
|
|
1587
|
-
"summary.discount": "Descuento",
|
|
1588
|
-
"summary.delivery": "Env\xEDo",
|
|
1589
|
-
"summary.pickup": "Recogida en tienda",
|
|
1590
|
-
"summary.noFee": "Sin coste",
|
|
1591
|
-
"summary.free": "Gratis",
|
|
1592
|
-
"summary.protected": "Compra protegida y datos cifrados.",
|
|
1593
|
-
"summary.shipping": "Env\xEDo",
|
|
1594
|
-
"discount.placeholder": "C\xF3digo de descuento",
|
|
1595
|
-
"discount.apply": "Aplicar",
|
|
1596
|
-
"discount.freeShipping": "env\xEDo gratis",
|
|
1597
|
-
"discount.percentOff": "{{percent}}% de descuento",
|
|
1598
|
-
"discount.applied": "{{code}} aplicado: {{effect}}.",
|
|
1599
|
-
"discount.appliedToast": "\xA1C\xF3digo aplicado!",
|
|
1600
|
-
"discount.invalid": "C\xF3digo no v\xE1lido.",
|
|
1601
|
-
"discount.invalidToast": "C\xF3digo no v\xE1lido",
|
|
1602
|
-
"discount.checkCode": "Revisa el c\xF3digo e int\xE9ntalo de nuevo.",
|
|
1603
|
-
"discount.cannotValidate": "No hemos podido validar este c\xF3digo ahora.",
|
|
1604
|
-
"discount.cannotValidateToast": "No hemos podido validar el c\xF3digo",
|
|
1605
|
-
"discount.tryLater": "Int\xE9ntalo de nuevo en unos instantes.",
|
|
1606
|
-
"legal.privacy": "Privacidad",
|
|
1607
|
-
"legal.terms": "T\xE9rminos",
|
|
1608
|
-
"legal.refunds": "Devoluciones",
|
|
1609
|
-
"error.requiresAccount": "Esta tienda pide una cuenta para finalizar la compra. Entra o crea la tuya.",
|
|
1610
|
-
"error.email": "Indica un email v\xE1lido.",
|
|
1611
|
-
"error.name": "Indica tu nombre completo.",
|
|
1612
|
-
"error.phone": "Indica un tel\xE9fono de contacto.",
|
|
1613
|
-
"error.documentInvalid": "Documento no v\xE1lido: revisa los n\xFAmeros.",
|
|
1614
|
-
"error.documentMissing": "Indica el documento del comprador.",
|
|
1615
|
-
"error.taxIdInvalid": "NIF/NIE no v\xE1lido: revisa el n\xFAmero.",
|
|
1616
|
-
"error.address": "Rellena o selecciona la direcci\xF3n de env\xEDo.",
|
|
1617
|
-
"error.number": "Falta el n\xFAmero de la direcci\xF3n.",
|
|
1618
|
-
"error.complement": "Indica el piso o la puerta.",
|
|
1619
|
-
"error.state": "Indica el estado con dos letras (ej.: RJ).",
|
|
1620
|
-
"error.postalInvalid": "Revisa el c\xF3digo postal (ej.: {{example}}).",
|
|
1621
|
-
"error.region": "Selecciona la {{label}}.",
|
|
1622
|
-
"error.refusedZone": "Esta tienda no env\xEDa a esta direcci\xF3n. Prueba con otra.",
|
|
1623
|
-
"error.unserved": "Todav\xEDa no enviamos a este c\xF3digo postal. Prueba con otra direcci\xF3n.",
|
|
1624
|
-
"error.sessionMismatch": "Tu sesi\xF3n qued\xF3 vinculada a otra cuenta. Recarga la p\xE1gina e int\xE9ntalo de nuevo.",
|
|
1625
|
-
"error.outOfStock": "Un producto del carrito se ha agotado. Revisa el carrito e int\xE9ntalo de nuevo.",
|
|
1626
|
-
"error.placeOrder": "No se ha podido completar el pedido.",
|
|
1627
|
-
"delivery.title": "Calcular env\xEDo",
|
|
1628
|
-
"delivery.calculate": "Calcular",
|
|
1629
|
-
"delivery.postalLabel": "C\xF3digo postal",
|
|
1630
|
-
"delivery.change": "Cambiar c\xF3digo postal",
|
|
1631
|
-
"delivery.unserved": "Todav\xEDa no enviamos a este c\xF3digo postal.",
|
|
1632
|
-
"delivery.refused": "Esta tienda no env\xEDa a esta direcci\xF3n.",
|
|
1633
|
-
"delivery.review": "Enviamos a esta zona, pero lo confirmamos contigo antes de salir.",
|
|
1634
|
-
"delivery.etaSoon": "hoy o ma\xF1ana",
|
|
1635
|
-
"delivery.etaRange": "de {{min}} a {{max}} d\xEDas laborables",
|
|
1636
|
-
"delivery.etaDays": "{{days}} d\xEDa(s) laborable(s)",
|
|
1637
|
-
"delivery.flatRate": "El env\xEDo se calcula en el pago.",
|
|
1638
|
-
"delivery.error.digits": "Escribe los 8 d\xEDgitos del CEP.",
|
|
1639
|
-
"delivery.error.postal": "Revisa el c\xF3digo postal (ej.: {{example}}).",
|
|
1640
|
-
"delivery.error.quote": "No hemos podido calcular el env\xEDo ahora. Int\xE9ntalo de nuevo.",
|
|
1641
|
-
"delivery.error.notFound": "C\xF3digo postal no encontrado. Revisa los n\xFAmeros.",
|
|
1642
|
-
"cart.title": "Carrito",
|
|
1643
|
-
"cart.close": "Cerrar carrito",
|
|
1644
|
-
"cart.empty": "Tu carrito est\xE1 vac\xEDo.",
|
|
1645
|
-
"cart.remove": "Quitar {{name}}",
|
|
1646
|
-
"cart.checkout": "Finalizar compra",
|
|
1647
|
-
"cart.missingForFree": "Te faltan {{amount}} para el env\xEDo gratis",
|
|
1648
|
-
"cart.wonFree": "\xA1Tienes env\xEDo gratis!",
|
|
1649
|
-
"cart.minimumOrder": "Pedido m\xEDnimo para entrega: {{amount}}. Te faltan {{missing}}.",
|
|
1650
|
-
"delivery.eta": "Entrega en ~{{minutes}} min",
|
|
1651
|
-
"country.deliverTo": "Enviar a",
|
|
1652
|
-
"country.change": "Cambiar pa\xEDs",
|
|
1653
|
-
"country.label": "Pa\xEDs de env\xEDo"
|
|
1654
|
-
};
|
|
1655
|
-
var MESSAGES = { "pt-BR": ptBR, en, es };
|
|
1656
|
-
function normalizeLanguage(value) {
|
|
1657
|
-
const base = (value || "").toLowerCase().split(/[-_]/)[0];
|
|
1658
|
-
if (base === "pt") return "pt-BR";
|
|
1659
|
-
if (base === "en") return "en";
|
|
1660
|
-
if (base === "es") return "es";
|
|
1661
|
-
return null;
|
|
1662
|
-
}
|
|
1663
|
-
function translate(language, key, vars, overrides) {
|
|
1664
|
-
let text = overrides?.[language]?.[key] ?? MESSAGES[language]?.[key] ?? overrides?.["pt-BR"]?.[key] ?? ptBR[key];
|
|
1665
|
-
if (vars) for (const [name, value] of Object.entries(vars)) text = text.split(`{{${name}}}`).join(String(value));
|
|
1666
|
-
return text;
|
|
1667
|
-
}
|
|
1668
|
-
function resolveCountries(config) {
|
|
1669
|
-
const countries = [...new Set((config.countries ?? []).map((c) => c.trim().toUpperCase()).filter(Boolean))];
|
|
1670
|
-
if (countries.length === 0) countries.push("BR");
|
|
1671
|
-
const wanted = config.defaultCountry?.trim().toUpperCase();
|
|
1672
|
-
return { countries, defaultCountry: wanted && countries.includes(wanted) ? wanted : countries[0] };
|
|
1673
|
-
}
|
|
1674
|
-
function resolveConfig(config) {
|
|
1675
|
-
const homePage = config.pages?.home ?? (config.home ? { blocks: config.home.sections } : void 0);
|
|
1676
|
-
const commerceMode = config.commerceMode ?? "checkout";
|
|
1677
|
-
const catalogPath = commerceMode === "delivery" ? config.delivery?.menuPath ?? "/menu" : homePage ? "/catalog" : "/";
|
|
1678
|
-
const checkoutEnabled = commerceMode === "checkout" || commerceMode === "delivery";
|
|
1679
|
-
return {
|
|
1680
|
-
...config,
|
|
1681
|
-
pages: { ...config.pages, ...homePage ? { home: homePage } : {} },
|
|
1682
|
-
currency: config.currency ?? "BRL",
|
|
1683
|
-
locale: config.locale ?? "pt-BR",
|
|
1684
|
-
language: normalizeLanguage(config.language) ?? "pt-BR",
|
|
1685
|
-
...resolveCountries(config),
|
|
1686
|
-
shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
|
|
1687
|
-
payments: {
|
|
1688
|
-
mode: config.payments?.mode ?? "mock",
|
|
1689
|
-
methods: config.payments?.methods?.length ? config.payments.methods : ["pix", "credit_card", "cash"],
|
|
1690
|
-
// Rebuilding `payments` key by key silently dropped `terms`, so the Pix
|
|
1691
|
-
// price and the instalment line never reached a component even when the
|
|
1692
|
-
// store had configured them.
|
|
1693
|
-
terms: config.payments?.terms
|
|
1694
|
-
},
|
|
1695
|
-
commerceMode,
|
|
1696
|
-
// Só existe no modo delivery. Fora dele o objeto inteiro é undefined, e é
|
|
1697
|
-
// isso que a rota /menu consulta para decidir se existe.
|
|
1698
|
-
delivery: commerceMode === "delivery" ? {
|
|
1699
|
-
menuPath: config.delivery?.menuPath ?? "/menu",
|
|
1700
|
-
coverImage: config.delivery?.coverImage,
|
|
1701
|
-
logoImage: config.delivery?.logoImage,
|
|
1702
|
-
locality: config.delivery?.locality,
|
|
1703
|
-
highlightSlugs: config.delivery?.highlightSlugs,
|
|
1704
|
-
// A ORDEM é a do seletor na tela. Um Set aqui perderia isso.
|
|
1705
|
-
fulfillmentModes: config.delivery?.fulfillmentModes?.length ? config.delivery.fulfillmentModes : ["delivery"].slice(),
|
|
1706
|
-
// Agendamento é opt-in duas vezes: aqui e na loja (0094 devolve zero
|
|
1707
|
-
// slots quando scheduling_enabled é falso). A tela só oferece quando
|
|
1708
|
-
// as duas concordam.
|
|
1709
|
-
scheduling: config.delivery?.scheduling ?? false,
|
|
1710
|
-
pickupAddress: config.delivery?.pickupAddress
|
|
1711
|
-
} : void 0,
|
|
1712
|
-
enquiry: {
|
|
1713
|
-
label: config.enquiry?.label ?? "Contact me",
|
|
1714
|
-
successMessage: config.enquiry?.successMessage ?? "Thanks. We received your enquiry.",
|
|
1715
|
-
subjectPrefix: config.enquiry?.subjectPrefix ?? "Product enquiry",
|
|
1716
|
-
email: config.enquiry?.email,
|
|
1717
|
-
whatsappUrl: config.enquiry?.whatsappUrl,
|
|
1718
|
-
requirePhone: config.enquiry?.requirePhone
|
|
1719
|
-
},
|
|
1720
|
-
features: {
|
|
1721
|
-
// Auto-enable the coupon UI when the store actually ships discount codes
|
|
1722
|
-
// (config.discounts, a seeded catalog, or a rule-engine promotion with a
|
|
1723
|
-
// code — M6), unless explicitly overridden — otherwise a store can
|
|
1724
|
-
// advertise a coupon with no field to enter it, which is exactly the
|
|
1725
|
-
// figurinhas pilot failure this milestone exists to end.
|
|
1726
|
-
discounts: config.features?.discounts ?? Boolean(
|
|
1727
|
-
config.discounts?.length || config.catalog?.discounts?.length || config.promotions?.some((p) => p.code) || config.catalog?.promotions?.some((p) => p.code)
|
|
1728
|
-
),
|
|
1729
|
-
accounts: config.features?.accounts ?? checkoutEnabled,
|
|
1730
|
-
cart: config.features?.cart ?? checkoutEnabled,
|
|
1731
|
-
checkout: config.features?.checkout ?? checkoutEnabled
|
|
1732
|
-
},
|
|
1733
|
-
imageLoading: {
|
|
1734
|
-
mode: config.imageLoading?.mode ?? "fade",
|
|
1735
|
-
durationMs: config.imageLoading?.durationMs ?? 420,
|
|
1736
|
-
easing: config.imageLoading?.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
1737
|
-
blur: config.imageLoading?.blur ?? true,
|
|
1738
|
-
transform: config.imageLoading?.transform ?? "none"
|
|
1739
|
-
},
|
|
1740
|
-
catalogPath,
|
|
1741
|
-
search: {
|
|
1742
|
-
mode: config.search?.mode ?? "inline",
|
|
1743
|
-
placeholder: config.search?.placeholder ?? "Buscar produtos...",
|
|
1744
|
-
resultsPath: config.search?.resultsPath ?? "/search",
|
|
1745
|
-
suggestionsLimit: config.search?.suggestionsLimit ?? 4,
|
|
1746
|
-
minChars: config.search?.minChars ?? 2,
|
|
1747
|
-
ctaLabel: config.search?.ctaLabel ?? "Ver todos resultados",
|
|
1748
|
-
emptyLabel: config.search?.emptyLabel ?? "Nada encontrado para",
|
|
1749
|
-
renderSuggestion: config.search?.renderSuggestion
|
|
1750
|
-
},
|
|
1751
|
-
nav: config.nav ?? (homePage ? [
|
|
1752
|
-
{ label: "In\xEDcio", to: "/" },
|
|
1753
|
-
{ label: "Loja", to: "/catalog" }
|
|
1754
|
-
] : [{ label: "Loja", to: "/" }])
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
var StorefrontConfigContext = createContext(null);
|
|
1758
|
-
var StorefrontConfigProvider = StorefrontConfigContext.Provider;
|
|
1759
|
-
function useStorefrontConfig() {
|
|
1760
|
-
const ctx = useContext(StorefrontConfigContext);
|
|
1761
|
-
if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
|
|
1762
|
-
return ctx;
|
|
1763
|
-
}
|
|
1764
|
-
function useStorefrontConfigOptional() {
|
|
1765
|
-
return useContext(StorefrontConfigContext);
|
|
1766
|
-
}
|
|
1767
|
-
function getStorefrontComponents(config) {
|
|
1768
|
-
return config.components ?? {};
|
|
1769
|
-
}
|
|
1770
1350
|
function prefersReducedMotion() {
|
|
1771
1351
|
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
1772
1352
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
@@ -1858,84 +1438,6 @@ function usePopOnChange(value) {
|
|
|
1858
1438
|
});
|
|
1859
1439
|
return pop;
|
|
1860
1440
|
}
|
|
1861
|
-
var RouterAdapterContext = createContext(null);
|
|
1862
|
-
var StorefrontRouterProvider = RouterAdapterContext.Provider;
|
|
1863
|
-
var _activeAdapter = null;
|
|
1864
|
-
var _fallback = null;
|
|
1865
|
-
function fallbackAdapter() {
|
|
1866
|
-
if (!_fallback) _fallback = defaultRouterAdapter("hash");
|
|
1867
|
-
return _fallback;
|
|
1868
|
-
}
|
|
1869
|
-
function useRouterAdapter() {
|
|
1870
|
-
const injected = useContext(RouterAdapterContext);
|
|
1871
|
-
const adapter = injected ?? fallbackAdapter();
|
|
1872
|
-
useEffect(() => {
|
|
1873
|
-
if (injected) _activeAdapter = injected;
|
|
1874
|
-
}, [injected]);
|
|
1875
|
-
return adapter;
|
|
1876
|
-
}
|
|
1877
|
-
function useRoutePath() {
|
|
1878
|
-
const adapter = useRouterAdapter();
|
|
1879
|
-
return useSyncExternalStore(
|
|
1880
|
-
(onChange) => adapter.onPathChange(onChange),
|
|
1881
|
-
() => adapter.getCurrentPath() || "/",
|
|
1882
|
-
() => adapter.getCurrentPath() || "/"
|
|
1883
|
-
);
|
|
1884
|
-
}
|
|
1885
|
-
function useScrollToTopOnNavigate(path) {
|
|
1886
|
-
const mounted = useRef(false);
|
|
1887
|
-
useEffect(() => {
|
|
1888
|
-
if (!mounted.current) {
|
|
1889
|
-
mounted.current = true;
|
|
1890
|
-
return;
|
|
1891
|
-
}
|
|
1892
|
-
if (typeof window !== "undefined") window.scrollTo(0, 0);
|
|
1893
|
-
}, [path]);
|
|
1894
|
-
}
|
|
1895
|
-
function useNavigate() {
|
|
1896
|
-
const adapter = useRouterAdapter();
|
|
1897
|
-
return adapter.navigate.bind(adapter);
|
|
1898
|
-
}
|
|
1899
|
-
var useHashPath = useRoutePath;
|
|
1900
|
-
function navigateTo(to) {
|
|
1901
|
-
(_activeAdapter ?? fallbackAdapter()).navigate(to);
|
|
1902
|
-
}
|
|
1903
|
-
function shellRouterAdapter(pathOverride, live) {
|
|
1904
|
-
if (pathOverride == null) return live;
|
|
1905
|
-
return staticRouterAdapter(pathOverride, { href: (to) => live.href(to) });
|
|
1906
|
-
}
|
|
1907
|
-
function matchPath(pattern, path) {
|
|
1908
|
-
const cleanPath = (path.split("?")[0] ?? "").replace(/\/+$/, "") || "/";
|
|
1909
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
1910
|
-
const pathParts = cleanPath.split("/").filter(Boolean);
|
|
1911
|
-
if (patternParts.length !== pathParts.length) return null;
|
|
1912
|
-
const params = {};
|
|
1913
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
1914
|
-
const pat = patternParts[i];
|
|
1915
|
-
const part = pathParts[i];
|
|
1916
|
-
if (pat.startsWith(":")) params[pat.slice(1)] = decodeURIComponent(part);
|
|
1917
|
-
else if (pat !== part) return null;
|
|
1918
|
-
}
|
|
1919
|
-
return params;
|
|
1920
|
-
}
|
|
1921
|
-
function Link({ to, children, onClick, target, ...rest }) {
|
|
1922
|
-
const adapter = useRouterAdapter();
|
|
1923
|
-
const handleClick = (event) => {
|
|
1924
|
-
onClick?.(event);
|
|
1925
|
-
if (!adapter.interceptsLinkClicks) return;
|
|
1926
|
-
if (event.defaultPrevented) return;
|
|
1927
|
-
if (target && target !== "_self") return;
|
|
1928
|
-
if (event.button !== 0) return;
|
|
1929
|
-
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
1930
|
-
event.preventDefault();
|
|
1931
|
-
adapter.navigate(to);
|
|
1932
|
-
};
|
|
1933
|
-
return React39__default.createElement(
|
|
1934
|
-
"a",
|
|
1935
|
-
{ href: adapter.href(to), onClick: handleClick, target, ...rest },
|
|
1936
|
-
children
|
|
1937
|
-
);
|
|
1938
|
-
}
|
|
1939
1441
|
|
|
1940
1442
|
// src/lib/testids.ts
|
|
1941
1443
|
var TID = {
|
|
@@ -2170,9 +1672,9 @@ function SmoothImage({
|
|
|
2170
1672
|
const easing = resolved.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
2171
1673
|
const blur = resolved.blur ?? true;
|
|
2172
1674
|
const transform = resolved.transform ?? "none";
|
|
2173
|
-
const ref =
|
|
2174
|
-
const [loaded, setLoaded] =
|
|
2175
|
-
const delivered =
|
|
1675
|
+
const ref = React37__default.useRef(null);
|
|
1676
|
+
const [loaded, setLoaded] = React37__default.useState(() => mode === "none" || !clientMounted);
|
|
1677
|
+
const delivered = React37__default.useMemo(() => {
|
|
2176
1678
|
if (transform !== "supabase" || !src || srcSet || !isStorageImageUrl(src)) return { src, srcSet, sizes };
|
|
2177
1679
|
if (variant?.width) {
|
|
2178
1680
|
const w = variant.width;
|
|
@@ -2190,10 +1692,10 @@ function SmoothImage({
|
|
|
2190
1692
|
sizes: variant?.sizes ?? sizes ?? "100vw"
|
|
2191
1693
|
};
|
|
2192
1694
|
}, [transform, src, srcSet, sizes, variant?.width, variant?.sizes, variant?.widths, variant?.quality]);
|
|
2193
|
-
|
|
1695
|
+
React37__default.useEffect(() => {
|
|
2194
1696
|
clientMounted = true;
|
|
2195
1697
|
}, []);
|
|
2196
|
-
|
|
1698
|
+
React37__default.useEffect(() => {
|
|
2197
1699
|
if (mode === "none") {
|
|
2198
1700
|
setLoaded(true);
|
|
2199
1701
|
return;
|
|
@@ -2227,8 +1729,8 @@ function SmoothImage({
|
|
|
2227
1729
|
function slideImage(slide, w = 1600, h = 700) {
|
|
2228
1730
|
return slide.image ?? bannerPlaceholder(slide.title, slide.hue ?? 200, (slide.hue ?? 200) + 40, w, h);
|
|
2229
1731
|
}
|
|
2230
|
-
function SlideCopy({ slide, active
|
|
2231
|
-
const stagger = (i) =>
|
|
1732
|
+
function SlideCopy({ slide, active }) {
|
|
1733
|
+
const stagger = (i) => active ? { className: "animate-fade-up", style: { animationDelay: `${i * 120}ms` } } : { className: "opacity-0" };
|
|
2232
1734
|
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2233
1735
|
/* @__PURE__ */ 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 }),
|
|
2234
1736
|
slide.subtitle && /* @__PURE__ */ jsx("p", { className: `mt-3 max-w-md text-base sm:text-lg ${stagger(1).className}`, style: stagger(1).style, children: slide.subtitle }),
|
|
@@ -2309,7 +1811,7 @@ function HeroSection({
|
|
|
2309
1811
|
i
|
|
2310
1812
|
)) });
|
|
2311
1813
|
}
|
|
2312
|
-
const
|
|
1814
|
+
const active = slides[Math.min(index, slides.length - 1)];
|
|
2313
1815
|
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.hero, className: `relative w-full overflow-hidden ${hClass}`, children: [
|
|
2314
1816
|
slides.map((slide, i) => /* @__PURE__ */ jsx(
|
|
2315
1817
|
"img",
|
|
@@ -2328,8 +1830,8 @@ function HeroSection({
|
|
|
2328
1830
|
/* @__PURE__ */ jsx(
|
|
2329
1831
|
"div",
|
|
2330
1832
|
{
|
|
2331
|
-
className: `absolute inset-0 flex items-end p-6 sm:p-12 ${
|
|
2332
|
-
children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-7xl", children: /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsx(SlideCopy, { slide:
|
|
1833
|
+
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"}`,
|
|
1834
|
+
children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-7xl", children: /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsx(SlideCopy, { slide: active, active: true }, index) }) })
|
|
2333
1835
|
}
|
|
2334
1836
|
),
|
|
2335
1837
|
variant === "slider" && slides.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -2495,196 +1997,6 @@ var useCatalogStore = create()((set) => ({
|
|
|
2495
1997
|
setSort: (sort) => set({ sort }),
|
|
2496
1998
|
reset: () => set(initial)
|
|
2497
1999
|
}));
|
|
2498
|
-
|
|
2499
|
-
// src/app/static-render.ts
|
|
2500
|
-
var active = null;
|
|
2501
|
-
function currentStaticRenderPass() {
|
|
2502
|
-
return active;
|
|
2503
|
-
}
|
|
2504
|
-
function withStaticRenderPass(pass, render) {
|
|
2505
|
-
const previous = active;
|
|
2506
|
-
active = pass;
|
|
2507
|
-
try {
|
|
2508
|
-
return render();
|
|
2509
|
-
} finally {
|
|
2510
|
-
active = previous;
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
function resolveDataParams(need, context) {
|
|
2514
|
-
const out = {};
|
|
2515
|
-
for (const [name, value] of Object.entries(need.params ?? {})) {
|
|
2516
|
-
let resolved = value;
|
|
2517
|
-
if (isLoaderParamRef(value)) resolved = context.params[value.$param];
|
|
2518
|
-
else if (isLoaderPropRef(value)) resolved = context.props[value.$prop];
|
|
2519
|
-
if (resolved !== void 0) out[name] = resolved;
|
|
2520
|
-
}
|
|
2521
|
-
return out;
|
|
2522
|
-
}
|
|
2523
|
-
function needsForNode(node) {
|
|
2524
|
-
const meta = getBlockMeta(node.type);
|
|
2525
|
-
return { ...meta?.data, ...node.data };
|
|
2526
|
-
}
|
|
2527
|
-
function collectDataNeeds(nodes, routeParams = {}) {
|
|
2528
|
-
const found = /* @__PURE__ */ new Map();
|
|
2529
|
-
const visit = (node) => {
|
|
2530
|
-
const meta = getBlockMeta(node.type);
|
|
2531
|
-
const needs = needsForNode(node);
|
|
2532
|
-
if (Object.keys(needs).length > 0) {
|
|
2533
|
-
const props = { ...defaultPropsFromSettings(meta?.settings), ...node.props };
|
|
2534
|
-
for (const need of Object.values(needs)) {
|
|
2535
|
-
if (!need || typeof need.loader !== "string" || !need.loader) continue;
|
|
2536
|
-
const params = resolveDataParams(need, { params: routeParams, props });
|
|
2537
|
-
const key = dataNeedKey(need.loader, params);
|
|
2538
|
-
if (!found.has(key)) found.set(key, { key, loader: need.loader, params });
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
for (const child of node.children ?? []) visit(child);
|
|
2542
|
-
};
|
|
2543
|
-
for (const node of nodes) visit(node);
|
|
2544
|
-
return [...found.values()];
|
|
2545
|
-
}
|
|
2546
|
-
async function resolvePageDataNeed(need) {
|
|
2547
|
-
try {
|
|
2548
|
-
const impl = resolveLoader(need.loader);
|
|
2549
|
-
const data = await impl(need.params, { shop: getShopProvider() });
|
|
2550
|
-
return { status: "ready", data };
|
|
2551
|
-
} catch (err) {
|
|
2552
|
-
const message = errorMessage(err);
|
|
2553
|
-
console.error(`[storefront] loader "${need.loader}" failed:`, err);
|
|
2554
|
-
return { status: "error", error: message };
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
async function resolvePageData(needs) {
|
|
2558
|
-
const settled = await Promise.all(
|
|
2559
|
-
needs.map(async (need) => [need.key, await resolvePageDataNeed(need)])
|
|
2560
|
-
);
|
|
2561
|
-
return Object.fromEntries(settled);
|
|
2562
|
-
}
|
|
2563
|
-
var PageDataContext = createContext(null);
|
|
2564
|
-
function PageDataScope({ nodes, params, initial: initial2, children }) {
|
|
2565
|
-
const parent = useContext(PageDataContext);
|
|
2566
|
-
const routeParams = useMemo(() => params ?? parent?.params ?? {}, [params, parent]);
|
|
2567
|
-
const needs = useMemo(() => collectDataNeeds(nodes, routeParams), [nodes, routeParams]);
|
|
2568
|
-
const own = useMemo(
|
|
2569
|
-
() => needs.filter((need) => !parent || !(need.key in parent.entries)),
|
|
2570
|
-
[needs, parent]
|
|
2571
|
-
);
|
|
2572
|
-
const staticPass = currentStaticRenderPass();
|
|
2573
|
-
if (staticPass) {
|
|
2574
|
-
staticPass.tree(nodes);
|
|
2575
|
-
staticPass.needs(own);
|
|
2576
|
-
}
|
|
2577
|
-
const [entries, setEntries] = useState(() => ({
|
|
2578
|
-
// Everything handed in is published, not only the needs THIS scope
|
|
2579
|
-
// collected. That is what lets a host framework resolve a page's data in
|
|
2580
|
-
// its own loader and wrap the shell in a seeding scope: the page's own
|
|
2581
|
-
// scope then finds those keys already answered on its parent and fetches
|
|
2582
|
-
// nothing. Without it, seeding only works when the seeding scope happens to
|
|
2583
|
-
// render the same block tree.
|
|
2584
|
-
...initial2,
|
|
2585
|
-
...Object.fromEntries(
|
|
2586
|
-
own.map((need) => [
|
|
2587
|
-
need.key,
|
|
2588
|
-
initial2?.[need.key] ?? staticPass?.entries[need.key] ?? { status: "loading" }
|
|
2589
|
-
])
|
|
2590
|
-
)
|
|
2591
|
-
}));
|
|
2592
|
-
const signature = own.map((need) => need.key).join("\n");
|
|
2593
|
-
useEffect(() => {
|
|
2594
|
-
const pending2 = own.filter((need) => initial2?.[need.key]?.status !== "ready");
|
|
2595
|
-
if (pending2.length === 0) return;
|
|
2596
|
-
let cancelled = false;
|
|
2597
|
-
setEntries((prev) => {
|
|
2598
|
-
const next = { ...prev };
|
|
2599
|
-
for (const need of pending2) {
|
|
2600
|
-
if (next[need.key]?.status !== "ready") next[need.key] = { status: "loading" };
|
|
2601
|
-
}
|
|
2602
|
-
return next;
|
|
2603
|
-
});
|
|
2604
|
-
for (const need of pending2) {
|
|
2605
|
-
void resolvePageDataNeed(need).then((entry) => {
|
|
2606
|
-
if (!cancelled) setEntries((prev) => ({ ...prev, [need.key]: entry }));
|
|
2607
|
-
});
|
|
2608
|
-
}
|
|
2609
|
-
return () => {
|
|
2610
|
-
cancelled = true;
|
|
2611
|
-
};
|
|
2612
|
-
}, [signature]);
|
|
2613
|
-
const value = useMemo(
|
|
2614
|
-
() => ({ entries: { ...parent?.entries, ...entries, ...initial2 }, params: routeParams }),
|
|
2615
|
-
[parent, entries, initial2, routeParams]
|
|
2616
|
-
);
|
|
2617
|
-
return /* @__PURE__ */ jsx(PageDataContext.Provider, { value, children });
|
|
2618
|
-
}
|
|
2619
|
-
function useLoader(loader, params) {
|
|
2620
|
-
const scope = useContext(PageDataContext);
|
|
2621
|
-
const key = params === null ? null : dataNeedKey(loader, params);
|
|
2622
|
-
const [nonce, setNonce] = useState(0);
|
|
2623
|
-
const scoped = nonce === 0 && key !== null && scope ? scope.entries[key] : void 0;
|
|
2624
|
-
const [local, setLocal] = useState(null);
|
|
2625
|
-
const staticPass = currentStaticRenderPass();
|
|
2626
|
-
const staticEntry = staticPass && key !== null && scoped === void 0 ? staticPass.entries[key] : void 0;
|
|
2627
|
-
if (staticPass && key !== null && scoped === void 0 && staticEntry === void 0) {
|
|
2628
|
-
staticPass.needs([{ key, loader, params }]);
|
|
2629
|
-
}
|
|
2630
|
-
const paramsKey = params === null ? null : JSON.stringify(params);
|
|
2631
|
-
useEffect(() => {
|
|
2632
|
-
if (key === null || scoped !== void 0) return;
|
|
2633
|
-
let cancelled = false;
|
|
2634
|
-
setLocal({ key, entry: { status: "loading" } });
|
|
2635
|
-
void resolvePageDataNeed({ loader, params }).then((entry2) => {
|
|
2636
|
-
if (!cancelled) setLocal({ key, entry: entry2 });
|
|
2637
|
-
});
|
|
2638
|
-
return () => {
|
|
2639
|
-
cancelled = true;
|
|
2640
|
-
};
|
|
2641
|
-
}, [loader, paramsKey, nonce, scoped !== void 0]);
|
|
2642
|
-
const entry = scoped ?? staticEntry ?? (local && local.key === key ? local.entry : key !== null ? { status: "loading" } : null);
|
|
2643
|
-
return {
|
|
2644
|
-
status: entry?.status ?? "idle",
|
|
2645
|
-
data: entry?.status === "ready" ? entry.data : null,
|
|
2646
|
-
error: entry?.status === "error" ? entry.error ?? "unknown error" : null,
|
|
2647
|
-
reload: () => setNonce((n) => n + 1)
|
|
2648
|
-
};
|
|
2649
|
-
}
|
|
2650
|
-
function useBlockData(node, name, fallback) {
|
|
2651
|
-
const scope = useContext(PageDataContext);
|
|
2652
|
-
let loader = fallback.loader;
|
|
2653
|
-
let params = fallback.params;
|
|
2654
|
-
if (node) {
|
|
2655
|
-
const need = needsForNode(node)[name];
|
|
2656
|
-
if (need && typeof need.loader === "string" && need.loader) {
|
|
2657
|
-
const meta = getBlockMeta(node.type);
|
|
2658
|
-
loader = need.loader;
|
|
2659
|
-
params = resolveDataParams(need, {
|
|
2660
|
-
params: scope?.params ?? {},
|
|
2661
|
-
props: { ...defaultPropsFromSettings(meta?.settings), ...node.props }
|
|
2662
|
-
});
|
|
2663
|
-
}
|
|
2664
|
-
}
|
|
2665
|
-
const result = useLoader(loader, params);
|
|
2666
|
-
return { ...result, loader };
|
|
2667
|
-
}
|
|
2668
|
-
function BlockDataError({ loader, message }) {
|
|
2669
|
-
if (getBlockErrorVisibility() === "silent") return null;
|
|
2670
|
-
return /* @__PURE__ */ jsxs(
|
|
2671
|
-
"div",
|
|
2672
|
-
{
|
|
2673
|
-
"data-block-error": `loader:${loader}`,
|
|
2674
|
-
style: {
|
|
2675
|
-
border: "1px dashed #dc2626",
|
|
2676
|
-
color: "#b91c1c",
|
|
2677
|
-
padding: "8px 12px",
|
|
2678
|
-
font: "12px ui-monospace, monospace",
|
|
2679
|
-
borderRadius: 6
|
|
2680
|
-
},
|
|
2681
|
-
children: [
|
|
2682
|
-
`Data for this block failed (loader "${loader}")`,
|
|
2683
|
-
message ? /* @__PURE__ */ jsx("div", { style: { opacity: 0.8, marginTop: 4 }, children: message }) : null
|
|
2684
|
-
]
|
|
2685
|
-
}
|
|
2686
|
-
);
|
|
2687
|
-
}
|
|
2688
2000
|
function CategoryShowcase({
|
|
2689
2001
|
style,
|
|
2690
2002
|
title,
|
|
@@ -2801,11 +2113,11 @@ function useStorefrontActions() {
|
|
|
2801
2113
|
};
|
|
2802
2114
|
}
|
|
2803
2115
|
function useMarketProduct(product) {
|
|
2804
|
-
const
|
|
2116
|
+
const active = useMarketStore((s2) => s2.active);
|
|
2805
2117
|
const prices = useMarketStore((s2) => s2.prices);
|
|
2806
2118
|
return useMemo(
|
|
2807
|
-
() => product ? withMarketPrice(product, { active
|
|
2808
|
-
[product,
|
|
2119
|
+
() => product ? withMarketPrice(product, { active, prices }) : product,
|
|
2120
|
+
[product, active, prices]
|
|
2809
2121
|
);
|
|
2810
2122
|
}
|
|
2811
2123
|
function Price({ value, compareAt, testId, compareTestId, className }) {
|
|
@@ -2845,12 +2157,12 @@ function PaymentTerms({ price, metadata, variant = "card", className = "" }) {
|
|
|
2845
2157
|
if (terms.pixPrice == null && terms.installments === 0) return null;
|
|
2846
2158
|
const currency = config.currency;
|
|
2847
2159
|
const locale = config.locale;
|
|
2848
|
-
const
|
|
2160
|
+
const compact = variant === "card";
|
|
2849
2161
|
return /* @__PURE__ */ jsxs(
|
|
2850
2162
|
"div",
|
|
2851
2163
|
{
|
|
2852
2164
|
"data-testid": "payment-terms",
|
|
2853
|
-
className: `${
|
|
2165
|
+
className: `${compact ? "mt-0.5 space-y-0.5 text-[11px]" : "mt-3 space-y-1 text-sm"} leading-tight text-muted-foreground ${className}`,
|
|
2854
2166
|
children: [
|
|
2855
2167
|
terms.pixPrice != null && /* @__PURE__ */ jsxs("p", { "data-testid": "payment-terms-pix", children: [
|
|
2856
2168
|
/* @__PURE__ */ jsx("strong", { className: "font-semibold text-foreground", children: formatMoney(terms.pixPrice, currency, locale) }),
|
|
@@ -2890,150 +2202,6 @@ var storefrontComponentContracts = {
|
|
|
2890
2202
|
enquiryForm: { "data-testid": TID.enquiryForm }
|
|
2891
2203
|
}
|
|
2892
2204
|
};
|
|
2893
|
-
function mergePageTheme(base, overlay) {
|
|
2894
|
-
if (!base && !overlay) return void 0;
|
|
2895
|
-
if (!overlay) return base;
|
|
2896
|
-
const name = overlay.name ?? (base ? `${base.name}-page` : "page");
|
|
2897
|
-
const font = base?.font ? { ...base.font, ...overlay.font } : overlay.font;
|
|
2898
|
-
return {
|
|
2899
|
-
...base,
|
|
2900
|
-
...overlay,
|
|
2901
|
-
name,
|
|
2902
|
-
colors: { ...base?.colors, ...overlay.colors },
|
|
2903
|
-
font,
|
|
2904
|
-
tokens: { ...base?.tokens, ...overlay.tokens },
|
|
2905
|
-
header: { ...base?.header, ...overlay.header },
|
|
2906
|
-
productCard: { ...base?.productCard, ...overlay.productCard }
|
|
2907
|
-
};
|
|
2908
|
-
}
|
|
2909
|
-
function pageThemeSelector(slug) {
|
|
2910
|
-
return `[data-page="${slug}"]`;
|
|
2911
|
-
}
|
|
2912
|
-
function routePathParams(path) {
|
|
2913
|
-
return path.split("/").filter((segment) => segment.startsWith(":")).map((segment) => segment.slice(1));
|
|
2914
|
-
}
|
|
2915
|
-
function contentPagePath(slug, page) {
|
|
2916
|
-
return page.path ?? `/${slug}`;
|
|
2917
|
-
}
|
|
2918
|
-
function matchContentPage(pages, path) {
|
|
2919
|
-
for (const [slug, page] of Object.entries(pages?.content ?? {})) {
|
|
2920
|
-
const params = matchPath(contentPagePath(slug, page), path);
|
|
2921
|
-
if (params) return { slug, page, params };
|
|
2922
|
-
}
|
|
2923
|
-
return null;
|
|
2924
|
-
}
|
|
2925
|
-
var CATALOG_BODY_BLOCK = "catalogBody";
|
|
2926
|
-
var PRODUCT_BODY_BLOCK = "productBody";
|
|
2927
|
-
var BODY_BLOCK_BY_KIND = {
|
|
2928
|
-
catalog: CATALOG_BODY_BLOCK,
|
|
2929
|
-
product: PRODUCT_BODY_BLOCK
|
|
2930
|
-
};
|
|
2931
|
-
var BLOCK_STRUCTURAL_KEYS = /* @__PURE__ */ new Set([
|
|
2932
|
-
"type",
|
|
2933
|
-
"id",
|
|
2934
|
-
"props",
|
|
2935
|
-
"children",
|
|
2936
|
-
"data"
|
|
2937
|
-
]);
|
|
2938
|
-
var STRUCTURAL_KEYS = BLOCK_STRUCTURAL_KEYS;
|
|
2939
|
-
function isNestedBlockRef(entry) {
|
|
2940
|
-
return isNestedRef(entry);
|
|
2941
|
-
}
|
|
2942
|
-
function isNestedRef(entry) {
|
|
2943
|
-
const record = entry;
|
|
2944
|
-
if ("props" in record) return true;
|
|
2945
|
-
return Object.keys(record).every((key) => STRUCTURAL_KEYS.has(key));
|
|
2946
|
-
}
|
|
2947
|
-
function blockInstanceId(entry, index) {
|
|
2948
|
-
const record = entry;
|
|
2949
|
-
if (record && typeof record.id === "string" && record.id) return record.id;
|
|
2950
|
-
return `${String(record?.type)}-${index}`;
|
|
2951
|
-
}
|
|
2952
|
-
function toBlockNode(entry, index) {
|
|
2953
|
-
const record = entry;
|
|
2954
|
-
const type = String(record.type);
|
|
2955
|
-
const id = blockInstanceId(entry, index);
|
|
2956
|
-
const data = record.data;
|
|
2957
|
-
if (isNestedRef(entry)) {
|
|
2958
|
-
const children = record.children ?? [];
|
|
2959
|
-
return {
|
|
2960
|
-
type,
|
|
2961
|
-
id,
|
|
2962
|
-
props: record.props ?? {},
|
|
2963
|
-
...children.length ? { children: children.map((child, i) => toBlockNode(child, i)) } : {},
|
|
2964
|
-
...data ? { data } : {}
|
|
2965
|
-
};
|
|
2966
|
-
}
|
|
2967
|
-
const props = {};
|
|
2968
|
-
for (const [key, value] of Object.entries(record)) {
|
|
2969
|
-
if (!STRUCTURAL_KEYS.has(key)) props[key] = value;
|
|
2970
|
-
}
|
|
2971
|
-
return { type, id, props, ...data ? { data } : {} };
|
|
2972
|
-
}
|
|
2973
|
-
function toBlockNodes(blocks) {
|
|
2974
|
-
return blocks.map(toBlockNode);
|
|
2975
|
-
}
|
|
2976
|
-
function defaultPageBlocks(kind) {
|
|
2977
|
-
const body = BODY_BLOCK_BY_KIND[kind];
|
|
2978
|
-
return body ? [{ type: body, id: body, props: {} }] : [];
|
|
2979
|
-
}
|
|
2980
|
-
function checkPageBlocks(kind, nodes) {
|
|
2981
|
-
const found = [];
|
|
2982
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
2983
|
-
const expectedBody = BODY_BLOCK_BY_KIND[kind];
|
|
2984
|
-
let hasBody = false;
|
|
2985
|
-
for (const node of nodes) {
|
|
2986
|
-
if (node.type === expectedBody) hasBody = true;
|
|
2987
|
-
if (node.id) {
|
|
2988
|
-
if (seenIds.has(node.id)) {
|
|
2989
|
-
found.push({
|
|
2990
|
-
level: "warning",
|
|
2991
|
-
rule: "duplicate-block-id",
|
|
2992
|
-
detail: `two blocks on the ${kind} page share the id "${node.id}" \u2014 an editor or a generated diff cannot tell them apart`
|
|
2993
|
-
});
|
|
2994
|
-
}
|
|
2995
|
-
seenIds.add(node.id);
|
|
2996
|
-
}
|
|
2997
|
-
const meta = getBlockMeta(node.type);
|
|
2998
|
-
if (!meta) {
|
|
2999
|
-
found.push({
|
|
3000
|
-
level: "error",
|
|
3001
|
-
rule: "unknown-block",
|
|
3002
|
-
detail: `the ${kind} page references block "${node.type}", which is not registered \u2014 register it before the storefront mounts`
|
|
3003
|
-
});
|
|
3004
|
-
continue;
|
|
3005
|
-
}
|
|
3006
|
-
if (!isBlockEnabledOn({ enabledOn: meta.enabledOn ?? [] }, kind)) {
|
|
3007
|
-
found.push({
|
|
3008
|
-
level: "warning",
|
|
3009
|
-
rule: "block-not-enabled-on-page",
|
|
3010
|
-
detail: `block "${node.type}" declares enabledOn [${(meta.enabledOn ?? []).join(", ")}] and is placed on the ${kind} page`
|
|
3011
|
-
});
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
if (expectedBody && !hasBody) {
|
|
3015
|
-
found.push({
|
|
3016
|
-
level: "warning",
|
|
3017
|
-
rule: "missing-page-body",
|
|
3018
|
-
detail: `the ${kind} page tree omits "${expectedBody}", so the page renders no ${kind === "product" ? "product detail" : "product list"} \u2014 add it back unless the replacement is deliberate`
|
|
3019
|
-
});
|
|
3020
|
-
}
|
|
3021
|
-
for (const violation of checkBlockConstraints(nodes)) {
|
|
3022
|
-
found.push({
|
|
3023
|
-
level: "error",
|
|
3024
|
-
rule: violation.rule,
|
|
3025
|
-
detail: `${kind} page ${violation.path}: ${violation.detail}`
|
|
3026
|
-
});
|
|
3027
|
-
}
|
|
3028
|
-
for (const finding of checkBlockContext(nodes)) {
|
|
3029
|
-
found.push({
|
|
3030
|
-
level: "warning",
|
|
3031
|
-
rule: "context-not-provided",
|
|
3032
|
-
detail: `block "${finding.type}" at ${kind} page ${finding.path} uses context [${finding.missing.join(", ")}] that no ancestor provides`
|
|
3033
|
-
});
|
|
3034
|
-
}
|
|
3035
|
-
return found;
|
|
3036
|
-
}
|
|
3037
2205
|
|
|
3038
2206
|
// src/app/hydration.ts
|
|
3039
2207
|
var PAGE_DATA_SCRIPT_ID = "__fayz_page_data__";
|
|
@@ -3469,8 +2637,8 @@ var BADGE_TONE_CLASSES = {
|
|
|
3469
2637
|
warning: "bg-amber-500 text-white"
|
|
3470
2638
|
};
|
|
3471
2639
|
function useExtensionBadges(product) {
|
|
3472
|
-
const [badges, setBadges] =
|
|
3473
|
-
|
|
2640
|
+
const [badges, setBadges] = React37__default.useState([]);
|
|
2641
|
+
React37__default.useEffect(() => {
|
|
3474
2642
|
let cancelled = false;
|
|
3475
2643
|
getProductCardBadges(product).then((next) => {
|
|
3476
2644
|
if (!cancelled && next.length > 0) setBadges(next);
|
|
@@ -3792,7 +2960,7 @@ function MediaCarousel({
|
|
|
3792
2960
|
return () => clearInterval(timer);
|
|
3793
2961
|
}, [autoplayMs, items.length]);
|
|
3794
2962
|
if (items.length === 0) return null;
|
|
3795
|
-
const
|
|
2963
|
+
const active = items[Math.min(index, items.length - 1)];
|
|
3796
2964
|
const goTo = (next) => {
|
|
3797
2965
|
interacted.current = true;
|
|
3798
2966
|
setIndex((next + items.length) % items.length);
|
|
@@ -3814,16 +2982,16 @@ function MediaCarousel({
|
|
|
3814
2982
|
`${item.title}-${i}`
|
|
3815
2983
|
)),
|
|
3816
2984
|
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 -z-10 ${overlayClass}` }),
|
|
3817
|
-
/* @__PURE__ */ jsx("div", { className: `flex h-full px-5 py-16 sm:px-10 lg:px-16 ${alignClass[
|
|
3818
|
-
|
|
3819
|
-
/* @__PURE__ */ jsx("h1", { className: "sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl", children:
|
|
3820
|
-
|
|
3821
|
-
|
|
2985
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-full px-5 py-16 sm:px-10 lg:px-16 ${alignClass[active.align ?? "start"]}`, children: /* @__PURE__ */ jsxs("div", { className: "my-auto max-w-2xl text-white drop-shadow-md", children: [
|
|
2986
|
+
active.eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-4 text-xs font-semibold uppercase tracking-[0.24em] text-white/80", children: active.eyebrow }),
|
|
2987
|
+
/* @__PURE__ */ jsx("h1", { className: "sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl", children: active.title }),
|
|
2988
|
+
active.subtitle && /* @__PURE__ */ jsx("p", { className: "mt-5 max-w-xl text-base leading-8 text-white/88 sm:text-lg", children: active.subtitle }),
|
|
2989
|
+
active.cta && /* @__PURE__ */ jsx(
|
|
3822
2990
|
Link,
|
|
3823
2991
|
{
|
|
3824
|
-
to:
|
|
2992
|
+
to: active.href ?? "/catalog",
|
|
3825
2993
|
className: "mt-8 inline-flex border border-white/75 px-6 py-3 text-xs font-semibold uppercase tracking-[0.18em] text-white transition hover:bg-white hover:text-black",
|
|
3826
|
-
children:
|
|
2994
|
+
children: active.cta
|
|
3827
2995
|
}
|
|
3828
2996
|
)
|
|
3829
2997
|
] }) }),
|
|
@@ -4232,12 +3400,12 @@ function FiltersPanel() {
|
|
|
4232
3400
|
] })
|
|
4233
3401
|
] }),
|
|
4234
3402
|
facets.map((facet) => {
|
|
4235
|
-
const
|
|
3403
|
+
const active = catalog.facets[facet.key];
|
|
4236
3404
|
const swatched = facet.values.some((v) => v.swatch);
|
|
4237
3405
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4238
3406
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: facet.label }),
|
|
4239
3407
|
/* @__PURE__ */ jsx("div", { className: swatched ? "flex flex-wrap gap-2" : "space-y-1", children: facet.values.map((v) => {
|
|
4240
|
-
const on =
|
|
3408
|
+
const on = active === v.value;
|
|
4241
3409
|
const label = v.label ?? v.value;
|
|
4242
3410
|
const toggle = () => catalog.setFacet(facet.key, on ? null : v.value);
|
|
4243
3411
|
if (v.swatch) {
|
|
@@ -4486,163 +3654,6 @@ function useProduct(slug) {
|
|
|
4486
3654
|
error
|
|
4487
3655
|
};
|
|
4488
3656
|
}
|
|
4489
|
-
function absolute(ctx, path = ctx.path) {
|
|
4490
|
-
const origin = (ctx.origin ?? currentStaticRenderPass()?.origin ?? seoOrigin()).replace(/\/$/, "");
|
|
4491
|
-
if (!origin) return void 0;
|
|
4492
|
-
return `${origin}${path.startsWith("/") ? path : `/${path}`}`;
|
|
4493
|
-
}
|
|
4494
|
-
function primaryImageUrl(product) {
|
|
4495
|
-
return (product.images?.find((i) => i.isPrimary) ?? product.images?.[0])?.url;
|
|
4496
|
-
}
|
|
4497
|
-
function shareImageUrl(product, ctx) {
|
|
4498
|
-
const url = primaryImageUrl(product);
|
|
4499
|
-
if (!url) return void 0;
|
|
4500
|
-
return ctx.config.imageLoading.transform === "supabase" ? imageVariantUrl(url, { width: 1200, quality: 82 }) : url;
|
|
4501
|
-
}
|
|
4502
|
-
function productJsonLd(product, ctx) {
|
|
4503
|
-
const url = absolute(ctx);
|
|
4504
|
-
const image = shareImageUrl(product, ctx);
|
|
4505
|
-
return {
|
|
4506
|
-
"@context": "https://schema.org",
|
|
4507
|
-
"@type": "Product",
|
|
4508
|
-
name: product.name,
|
|
4509
|
-
...product.description ? { description: product.description } : {},
|
|
4510
|
-
...product.sku ? { sku: product.sku } : {},
|
|
4511
|
-
...image ? { image: [image] } : {},
|
|
4512
|
-
offers: {
|
|
4513
|
-
"@type": "Offer",
|
|
4514
|
-
...url ? { url } : {},
|
|
4515
|
-
price: product.price,
|
|
4516
|
-
priceCurrency: product.currency ?? ctx.config.currency,
|
|
4517
|
-
availability: (product.inventoryCount ?? 0) > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock"
|
|
4518
|
-
}
|
|
4519
|
-
};
|
|
4520
|
-
}
|
|
4521
|
-
function productSeo(product, ctx) {
|
|
4522
|
-
const url = absolute(ctx);
|
|
4523
|
-
const home = absolute(ctx, "/");
|
|
4524
|
-
const catalog = absolute(ctx, ctx.config.catalogPath);
|
|
4525
|
-
return {
|
|
4526
|
-
title: `${product.name} \u2014 ${ctx.config.name}`,
|
|
4527
|
-
description: product.description?.slice(0, 160),
|
|
4528
|
-
canonical: url,
|
|
4529
|
-
image: shareImageUrl(product, ctx),
|
|
4530
|
-
type: "product",
|
|
4531
|
-
jsonLd: [
|
|
4532
|
-
productJsonLd(product, ctx),
|
|
4533
|
-
...home && catalog && url ? [
|
|
4534
|
-
breadcrumbJsonLd([
|
|
4535
|
-
{ name: ctx.config.name, url: home },
|
|
4536
|
-
{ name: "Loja", url: catalog },
|
|
4537
|
-
{ name: product.name, url }
|
|
4538
|
-
])
|
|
4539
|
-
] : []
|
|
4540
|
-
]
|
|
4541
|
-
};
|
|
4542
|
-
}
|
|
4543
|
-
function pageSeo(ctx, input) {
|
|
4544
|
-
return { ...input, canonical: absolute(ctx), type: "website" };
|
|
4545
|
-
}
|
|
4546
|
-
function menuSections(products, categories) {
|
|
4547
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
4548
|
-
const loose = [];
|
|
4549
|
-
for (const p of products) {
|
|
4550
|
-
const key = p.categoryId ?? p.categoryName ?? "";
|
|
4551
|
-
if (!key) {
|
|
4552
|
-
loose.push(p);
|
|
4553
|
-
continue;
|
|
4554
|
-
}
|
|
4555
|
-
byCategory.set(key, [...byCategory.get(key) ?? [], p]);
|
|
4556
|
-
}
|
|
4557
|
-
const sections = categories.map((c) => ({ name: c.name, description: c.description, products: byCategory.get(c.id) ?? byCategory.get(c.name) ?? [] })).filter((s2) => s2.products.length > 0);
|
|
4558
|
-
if (loose.length) sections.push({ name: "Outros", description: null, products: loose });
|
|
4559
|
-
if (!sections.length && products.length) return [{ name: "Card\xE1pio", description: null, products }];
|
|
4560
|
-
return sections;
|
|
4561
|
-
}
|
|
4562
|
-
function menuJsonLd(input, ctx) {
|
|
4563
|
-
const url = absolute(ctx);
|
|
4564
|
-
const home = absolute(ctx, "/");
|
|
4565
|
-
const b = input.business ?? {};
|
|
4566
|
-
const currency = ctx.config.currency;
|
|
4567
|
-
const address = b.address ? {
|
|
4568
|
-
"@type": "PostalAddress",
|
|
4569
|
-
...b.address.streetAddress ? { streetAddress: b.address.streetAddress } : {},
|
|
4570
|
-
...b.address.locality ? { addressLocality: b.address.locality } : {},
|
|
4571
|
-
...b.address.region ? { addressRegion: b.address.region } : {},
|
|
4572
|
-
...b.address.postalCode ? { postalCode: b.address.postalCode } : {},
|
|
4573
|
-
addressCountry: b.address.country ?? "BR"
|
|
4574
|
-
} : void 0;
|
|
4575
|
-
return {
|
|
4576
|
-
"@context": "https://schema.org",
|
|
4577
|
-
"@type": b.type ?? "Restaurant",
|
|
4578
|
-
...home ? { "@id": `${home.replace(/\/$/, "")}/#business` } : {},
|
|
4579
|
-
name: ctx.config.name,
|
|
4580
|
-
...home ? { url: home } : {},
|
|
4581
|
-
...b.description ? { description: b.description } : {},
|
|
4582
|
-
...b.image ? { image: [b.image] } : {},
|
|
4583
|
-
...b.logo ? { logo: b.logo } : {},
|
|
4584
|
-
...b.telephone ? { telephone: b.telephone } : {},
|
|
4585
|
-
...b.email ? { email: b.email } : {},
|
|
4586
|
-
...b.servesCuisine ? { servesCuisine: b.servesCuisine } : {},
|
|
4587
|
-
...b.priceRange ? { priceRange: b.priceRange } : {},
|
|
4588
|
-
...address ? { address } : {},
|
|
4589
|
-
...b.areaServed?.length ? { areaServed: b.areaServed.map((name) => ({ "@type": "Place", name })) } : {},
|
|
4590
|
-
...b.openingHours?.length ? { openingHours: b.openingHours } : {},
|
|
4591
|
-
...b.sameAs?.length ? { sameAs: b.sameAs } : {},
|
|
4592
|
-
...url ? { hasMenu: url } : {},
|
|
4593
|
-
...url ? {
|
|
4594
|
-
potentialAction: {
|
|
4595
|
-
"@type": "OrderAction",
|
|
4596
|
-
target: { "@type": "EntryPoint", urlTemplate: url, actionPlatform: ["https://schema.org/DesktopWebPlatform", "https://schema.org/MobileWebPlatform"] },
|
|
4597
|
-
deliveryMethod: "http://purl.org/goodrelations/v1#DeliveryModeOwnFleet"
|
|
4598
|
-
}
|
|
4599
|
-
} : {},
|
|
4600
|
-
menu: {
|
|
4601
|
-
"@type": "Menu",
|
|
4602
|
-
name: `Card\xE1pio \u2014 ${ctx.config.name}`,
|
|
4603
|
-
...url ? { url } : {},
|
|
4604
|
-
inLanguage: "pt-BR",
|
|
4605
|
-
hasMenuSection: menuSections(input.products, input.categories).map((section) => ({
|
|
4606
|
-
"@type": "MenuSection",
|
|
4607
|
-
name: section.name,
|
|
4608
|
-
...section.description ? { description: section.description } : {},
|
|
4609
|
-
hasMenuItem: section.products.map((p) => {
|
|
4610
|
-
const image = shareImageUrl(p, ctx);
|
|
4611
|
-
const itemUrl = absolute(ctx, `/product/${p.slug}`);
|
|
4612
|
-
return {
|
|
4613
|
-
"@type": "MenuItem",
|
|
4614
|
-
name: p.name,
|
|
4615
|
-
...p.description ? { description: p.description.slice(0, 300) } : {},
|
|
4616
|
-
...image ? { image } : {},
|
|
4617
|
-
...itemUrl ? { url: itemUrl } : {},
|
|
4618
|
-
offers: {
|
|
4619
|
-
"@type": "Offer",
|
|
4620
|
-
price: p.price,
|
|
4621
|
-
priceCurrency: p.currency ?? currency,
|
|
4622
|
-
availability: (p.inventoryCount ?? 0) > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock"
|
|
4623
|
-
}
|
|
4624
|
-
};
|
|
4625
|
-
})
|
|
4626
|
-
}))
|
|
4627
|
-
}
|
|
4628
|
-
};
|
|
4629
|
-
}
|
|
4630
|
-
function menuSeo(input, ctx) {
|
|
4631
|
-
const url = absolute(ctx);
|
|
4632
|
-
const home = absolute(ctx, "/");
|
|
4633
|
-
const names = input.products.slice(0, 4).map((p) => p.name).join(", ");
|
|
4634
|
-
return {
|
|
4635
|
-
title: input.title ?? `Delivery ${ctx.config.name} \u2014 pe\xE7a online`,
|
|
4636
|
-
description: (input.description ?? (names ? `Pe\xE7a online: ${names} e mais. Card\xE1pio completo com pre\xE7os.` : void 0))?.slice(0, 160),
|
|
4637
|
-
canonical: url,
|
|
4638
|
-
image: input.business?.image,
|
|
4639
|
-
type: "website",
|
|
4640
|
-
jsonLd: [
|
|
4641
|
-
menuJsonLd(input, ctx),
|
|
4642
|
-
...home && url ? [breadcrumbJsonLd([{ name: ctx.config.name, url: home }, { name: "Delivery", url }])] : []
|
|
4643
|
-
]
|
|
4644
|
-
};
|
|
4645
|
-
}
|
|
4646
3657
|
function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTestId }) {
|
|
4647
3658
|
const clamp = (n) => Math.max(min, max != null ? Math.min(n, max) : n);
|
|
4648
3659
|
return /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center rounded-lg border", children: [
|
|
@@ -4767,7 +3778,7 @@ function scalarToString(value) {
|
|
|
4767
3778
|
}
|
|
4768
3779
|
function ProductSpecs({ product }) {
|
|
4769
3780
|
const config = useStorefrontConfig();
|
|
4770
|
-
const extraExcluded =
|
|
3781
|
+
const extraExcluded = React37__default.useMemo(
|
|
4771
3782
|
() => new Set(config.productSpecs?.exclude ?? []),
|
|
4772
3783
|
[config.productSpecs?.exclude]
|
|
4773
3784
|
);
|
|
@@ -5270,7 +4281,7 @@ function RelatedProducts({ product }) {
|
|
|
5270
4281
|
)) })
|
|
5271
4282
|
] });
|
|
5272
4283
|
}
|
|
5273
|
-
function DeliveryEstimator({ compact
|
|
4284
|
+
function DeliveryEstimator({ compact = false }) {
|
|
5274
4285
|
const config = useStorefrontConfig();
|
|
5275
4286
|
const cart = useCartStore();
|
|
5276
4287
|
const delivery = useDeliveryStore();
|
|
@@ -5297,7 +4308,7 @@ function DeliveryEstimator({ compact: compact2 = false }) {
|
|
|
5297
4308
|
"section",
|
|
5298
4309
|
{
|
|
5299
4310
|
"data-testid": TID.deliveryEstimator,
|
|
5300
|
-
className:
|
|
4311
|
+
className: compact ? "rounded-lg border p-3" : "mt-6 rounded-lg border p-4",
|
|
5301
4312
|
children: [
|
|
5302
4313
|
!resolved && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5303
4314
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm font-semibold", children: [
|
|
@@ -5633,11 +4644,11 @@ function ProductDetailPage({ slug }) {
|
|
|
5633
4644
|
] });
|
|
5634
4645
|
}
|
|
5635
4646
|
function buildEnquiryFromForm(fields, values, subject, sourceUrl) {
|
|
5636
|
-
const emailField = fields.find((
|
|
5637
|
-
const phoneField = fields.find((
|
|
5638
|
-
const nameField = fields.find((
|
|
4647
|
+
const emailField = fields.find((f) => f.type === "email");
|
|
4648
|
+
const phoneField = fields.find((f) => f.type === "phone");
|
|
4649
|
+
const nameField = fields.find((f) => f.id === "name") ?? fields.find((f) => (f.type ?? "text") === "text" && f !== emailField && f !== phoneField);
|
|
5639
4650
|
const identity = /* @__PURE__ */ new Set([emailField?.id, phoneField?.id, nameField?.id]);
|
|
5640
|
-
const lines = fields.filter((
|
|
4651
|
+
const lines = fields.filter((f) => !identity.has(f.id)).map((f) => ({ field: f, value: (values[f.id] ?? "").trim() })).filter((entry) => entry.value).map(({ field: field2, value }) => field2.type === "textarea" ? value : `${field2.label}: ${value}`);
|
|
5641
4652
|
return {
|
|
5642
4653
|
productName: subject,
|
|
5643
4654
|
customerName: nameField && values[nameField.id]?.trim() || "Visitante",
|
|
@@ -6492,7 +5503,7 @@ function CollectionHero({
|
|
|
6492
5503
|
{
|
|
6493
5504
|
className: `absolute inset-0 flex flex-col justify-center p-6 text-white sm:p-12 ${OVERLAY_CLASS[overlay]} ${centered ? "items-center text-center" : "items-start text-left"}`,
|
|
6494
5505
|
children: /* @__PURE__ */ jsxs(Reveal, { className: centered ? "max-w-3xl" : "max-w-2xl", children: [
|
|
6495
|
-
logos && logos.length > 0 && /* @__PURE__ */ jsx("div", { className: `mb-6 flex items-center gap-5 ${centered ? "justify-center" : ""}`, children: logos.map((logo, i) => /* @__PURE__ */ jsxs(
|
|
5506
|
+
logos && logos.length > 0 && /* @__PURE__ */ jsx("div", { className: `mb-6 flex items-center gap-5 ${centered ? "justify-center" : ""}`, children: logos.map((logo, i) => /* @__PURE__ */ jsxs(React37__default.Fragment, { children: [
|
|
6496
5507
|
i > 0 && /* @__PURE__ */ jsx("span", { className: "text-2xl font-light opacity-60", "aria-hidden": true, children: "\xD7" }),
|
|
6497
5508
|
/* @__PURE__ */ jsx("img", { src: logo.src, alt: logo.alt, className: "h-10 w-auto object-contain sm:h-14" })
|
|
6498
5509
|
] }, logo.src)) }),
|
|
@@ -7222,6 +6233,8 @@ function registerStorefrontBlocks() {
|
|
|
7222
6233
|
defineBlock("collectionCta", CollectionCta, { source: "sdk", ...collectionCtaBlockMeta });
|
|
7223
6234
|
defineBlock("catalogBody", CatalogBodyBlock, { source: "sdk", ...catalogBodyBlockMeta });
|
|
7224
6235
|
defineBlock("productBody", ProductBodyBlock, { source: "sdk", ...productBodyBlockMeta });
|
|
6236
|
+
defineBlock("experiment", ExperimentBlock, { source: "sdk", ...experimentBlockMeta });
|
|
6237
|
+
defineBlock("experimentVariant", ExperimentVariantBlock, { source: "sdk", ...experimentVariantBlockMeta });
|
|
7225
6238
|
}
|
|
7226
6239
|
function sectionsToBlocks(sections) {
|
|
7227
6240
|
return sections.map((section, i) => {
|
|
@@ -7274,10 +6287,10 @@ function StorefrontConfigHost({
|
|
|
7274
6287
|
buyer,
|
|
7275
6288
|
children
|
|
7276
6289
|
}) {
|
|
7277
|
-
const [policy, setPolicy] =
|
|
7278
|
-
const [shipping, setShipping] =
|
|
7279
|
-
const [delivery, setDelivery] =
|
|
7280
|
-
|
|
6290
|
+
const [policy, setPolicy] = React37__default.useState(null);
|
|
6291
|
+
const [shipping, setShipping] = React37__default.useState(null);
|
|
6292
|
+
const [delivery, setDelivery] = React37__default.useState(null);
|
|
6293
|
+
React37__default.useEffect(() => {
|
|
7281
6294
|
let cancelled = false;
|
|
7282
6295
|
let provider;
|
|
7283
6296
|
try {
|
|
@@ -7314,7 +6327,7 @@ function StorefrontConfigHost({
|
|
|
7314
6327
|
cancelled = true;
|
|
7315
6328
|
};
|
|
7316
6329
|
}, [config]);
|
|
7317
|
-
const resolved =
|
|
6330
|
+
const resolved = React37__default.useMemo(
|
|
7318
6331
|
() => applyBuyer(applyDeliveryPolicy(applyShippingSettings(applyStoreSettings(config, policy), shipping), delivery), buyer),
|
|
7319
6332
|
[config, policy, shipping, delivery, buyer?.language, buyer?.country]
|
|
7320
6333
|
);
|
|
@@ -7326,7 +6339,7 @@ function MarketScope({ children }) {
|
|
|
7326
6339
|
const markets = useMarketStore((s2) => s2.markets);
|
|
7327
6340
|
const market = markets.length ? marketForCountry(markets, country) : null;
|
|
7328
6341
|
const prices = useMarketStore((s2) => market ? s2.prices[market.code] : void 0);
|
|
7329
|
-
|
|
6342
|
+
React37__default.useEffect(() => {
|
|
7330
6343
|
useMarketStore.getState().setActive(market);
|
|
7331
6344
|
if (!market || market.isDefault || prices) return;
|
|
7332
6345
|
let provider;
|
|
@@ -7338,11 +6351,11 @@ function MarketScope({ children }) {
|
|
|
7338
6351
|
void provider.getMarketPrices?.(market.code).then((list) => useMarketStore.getState().setPrices(market.code, list)).catch(() => {
|
|
7339
6352
|
});
|
|
7340
6353
|
}, [market?.code, prices]);
|
|
7341
|
-
|
|
6354
|
+
React37__default.useEffect(() => {
|
|
7342
6355
|
if (!market || !market.isDefault && !prices) return;
|
|
7343
6356
|
useCartStore.getState().repriceForMarket();
|
|
7344
6357
|
}, [market?.code, prices]);
|
|
7345
|
-
const value =
|
|
6358
|
+
const value = React37__default.useMemo(() => {
|
|
7346
6359
|
if (!market) return base;
|
|
7347
6360
|
return {
|
|
7348
6361
|
...base,
|
|
@@ -7445,10 +6458,10 @@ function themeToCss(theme, options = {}) {
|
|
|
7445
6458
|
function googleFontsHref(theme) {
|
|
7446
6459
|
if (!theme.font) return null;
|
|
7447
6460
|
const families = theme.font.googleFonts ?? [theme.font.heading, theme.font.body, theme.font.display, theme.font.serif].filter(
|
|
7448
|
-
(
|
|
6461
|
+
(f) => Boolean(f)
|
|
7449
6462
|
);
|
|
7450
6463
|
const unique = [...new Set(families)];
|
|
7451
|
-
const params = unique.map((
|
|
6464
|
+
const params = unique.map((f) => `family=${encodeURIComponent(f).replace(/%20/g, "+")}:wght@300;400;500;600;700`).join("&");
|
|
7452
6465
|
return `https://fonts.googleapis.com/css2?${params}&display=swap`;
|
|
7453
6466
|
}
|
|
7454
6467
|
function StorefrontThemeStyle({
|
|
@@ -7472,7 +6485,7 @@ function StorefrontThemeStyle({
|
|
|
7472
6485
|
document.getElementById(linkId)?.remove();
|
|
7473
6486
|
};
|
|
7474
6487
|
}, [fontsHref, linkId, selector]);
|
|
7475
|
-
return
|
|
6488
|
+
return React37__default.createElement(
|
|
7476
6489
|
"style",
|
|
7477
6490
|
{ "data-sf-theme": theme.name },
|
|
7478
6491
|
themeToCss(theme, { selector })
|
|
@@ -7500,13 +6513,13 @@ function SearchOverlay({ onClose }) {
|
|
|
7500
6513
|
const id = setTimeout(() => setQuery(term.trim()), 220);
|
|
7501
6514
|
return () => clearTimeout(id);
|
|
7502
6515
|
}, [term]);
|
|
7503
|
-
const
|
|
6516
|
+
const active = query.length >= searchConfig.minChars;
|
|
7504
6517
|
const { products, loading } = useProducts(
|
|
7505
|
-
|
|
6518
|
+
active ? { status: "active", search: query, limit: searchConfig.suggestionsLimit } : { status: "active", limit: 0 }
|
|
7506
6519
|
);
|
|
7507
6520
|
const suggestions = useMemo(
|
|
7508
|
-
() =>
|
|
7509
|
-
[
|
|
6521
|
+
() => active ? products.slice(0, searchConfig.suggestionsLimit) : [],
|
|
6522
|
+
[active, products, searchConfig.suggestionsLimit]
|
|
7510
6523
|
);
|
|
7511
6524
|
const submit = (value) => {
|
|
7512
6525
|
const q = value.trim();
|
|
@@ -7564,14 +6577,14 @@ function SearchOverlay({ onClose }) {
|
|
|
7564
6577
|
}
|
|
7565
6578
|
)
|
|
7566
6579
|
] }),
|
|
7567
|
-
|
|
6580
|
+
active && /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-5xl px-4 pb-4 sm:px-6", children: loading && suggestions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid gap-4 pb-2 sm:grid-cols-2 lg:grid-cols-4", children: Array.from({ length: Math.min(searchConfig.suggestionsLimit, 4) }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-40 animate-pulse rounded-xl bg-muted" }, i)) }) : suggestions.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "py-6 text-center text-sm text-muted-foreground", children: [
|
|
7568
6581
|
searchConfig.emptyLabel,
|
|
7569
6582
|
" \u201C",
|
|
7570
6583
|
query,
|
|
7571
6584
|
"\u201D."
|
|
7572
6585
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7573
6586
|
/* @__PURE__ */ jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: suggestions.map(
|
|
7574
|
-
(product) => searchConfig.renderSuggestion ? /* @__PURE__ */ jsx(
|
|
6587
|
+
(product) => searchConfig.renderSuggestion ? /* @__PURE__ */ jsx(React37__default.Fragment, { children: searchConfig.renderSuggestion(product) }, product.id) : /* @__PURE__ */ jsx(SuggestionCard, { product, onNavigate: onClose }, product.id)
|
|
7575
6588
|
) }),
|
|
7576
6589
|
/* @__PURE__ */ jsx(
|
|
7577
6590
|
"button",
|
|
@@ -7778,11 +6791,11 @@ function HeaderActions({ onMenuClick }) {
|
|
|
7778
6791
|
const openDrawer = useCartStore((s2) => s2.openDrawer);
|
|
7779
6792
|
const session = useSessionStore();
|
|
7780
6793
|
const [accountOpen, setAccountOpen] = useState(false);
|
|
7781
|
-
const accountRef =
|
|
6794
|
+
const accountRef = React37__default.useRef(null);
|
|
7782
6795
|
const pop = usePopOnChange(count);
|
|
7783
6796
|
const firstName = session.name?.split(/\s+/)[0];
|
|
7784
6797
|
const initial2 = (firstName ?? session.email ?? "C").slice(0, 1).toUpperCase();
|
|
7785
|
-
|
|
6798
|
+
React37__default.useEffect(() => {
|
|
7786
6799
|
if (!accountOpen) return;
|
|
7787
6800
|
const onPointerDown = (event) => {
|
|
7788
6801
|
if (!accountRef.current?.contains(event.target)) setAccountOpen(false);
|
|
@@ -7949,7 +6962,7 @@ function MobileMenuDrawer({ open, onClose }) {
|
|
|
7949
6962
|
navigateTo(config.catalogPath);
|
|
7950
6963
|
onClose();
|
|
7951
6964
|
};
|
|
7952
|
-
|
|
6965
|
+
React37__default.useEffect(() => {
|
|
7953
6966
|
if (!open) return;
|
|
7954
6967
|
const onKey = (e) => {
|
|
7955
6968
|
if (e.key === "Escape") onClose();
|
|
@@ -8021,7 +7034,7 @@ function MobileMenuDrawer({ open, onClose }) {
|
|
|
8021
7034
|
}
|
|
8022
7035
|
function StorefrontHeader({ chrome } = {}) {
|
|
8023
7036
|
const config = useStorefrontConfig();
|
|
8024
|
-
const
|
|
7037
|
+
const compact = chrome === "minimal";
|
|
8025
7038
|
const variant = config.theme?.header?.variant ?? "classic";
|
|
8026
7039
|
const scrolled = useScrolled();
|
|
8027
7040
|
const showSearch = config.theme?.header?.showSearch !== false;
|
|
@@ -8035,8 +7048,8 @@ function StorefrontHeader({ chrome } = {}) {
|
|
|
8035
7048
|
style: headerStyle,
|
|
8036
7049
|
children: [
|
|
8037
7050
|
/* @__PURE__ */ jsx(AnnouncementBar, {}),
|
|
8038
|
-
!
|
|
8039
|
-
|
|
7051
|
+
!compact && /* @__PURE__ */ jsx(UtilityBar, {}),
|
|
7052
|
+
compact ? (
|
|
8040
7053
|
// O cabeçalho de um app de pedido: marca, busca, carrinho — e o resto da
|
|
8041
7054
|
// navegação atrás do menu, alcançável e fora do caminho.
|
|
8042
7055
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto flex h-14 max-w-7xl items-center gap-3 px-4 sm:px-6", children: [
|
|
@@ -8618,66 +7631,6 @@ function ContentPage({ slug, page, params }) {
|
|
|
8618
7631
|
] });
|
|
8619
7632
|
}
|
|
8620
7633
|
|
|
8621
|
-
// src/app/serving.ts
|
|
8622
|
-
function resolveStorefrontRoute(config, rawPath) {
|
|
8623
|
-
const resolved = ensureResolved(config);
|
|
8624
|
-
const path = rawPath.split("?")[0] || "/";
|
|
8625
|
-
for (const route of resolved.routes ?? []) {
|
|
8626
|
-
const params = matchPath(route.path, path);
|
|
8627
|
-
if (params) return { kind: "custom", route, params };
|
|
8628
|
-
}
|
|
8629
|
-
const product = matchPath("/product/:slug", path);
|
|
8630
|
-
if (product?.slug) return { kind: "product", params: product };
|
|
8631
|
-
const order = matchPath("/order/:id", path);
|
|
8632
|
-
if (order?.id) return { kind: "order", params: order };
|
|
8633
|
-
if (matchPath("/checkout", path)) return { kind: "checkout" };
|
|
8634
|
-
if (matchPath("/account", path)) return { kind: "account" };
|
|
8635
|
-
if (matchPath("/catalog", path)) return { kind: "catalog" };
|
|
8636
|
-
if (resolved.delivery && matchPath(resolved.delivery.menuPath, path)) return { kind: "menu" };
|
|
8637
|
-
if (resolved.search.mode === "overlay" && matchPath(resolved.search.resultsPath, path)) {
|
|
8638
|
-
return { kind: "searchResults" };
|
|
8639
|
-
}
|
|
8640
|
-
if (matchPath("/privacy", path)) return { kind: "policy", policy: "privacy" };
|
|
8641
|
-
if (matchPath("/terms", path)) return { kind: "policy", policy: "terms" };
|
|
8642
|
-
if (matchPath("/refunds", path)) return { kind: "policy", policy: "returns" };
|
|
8643
|
-
const content = matchContentPage(resolved.pages, path);
|
|
8644
|
-
if (content) return { kind: "content", slug: content.slug, params: content.params };
|
|
8645
|
-
if (path === "/" || path === "") return { kind: "home" };
|
|
8646
|
-
return { kind: "notFound" };
|
|
8647
|
-
}
|
|
8648
|
-
function isKnownPath(config, path) {
|
|
8649
|
-
return resolveStorefrontRoute(config, path).kind !== "notFound";
|
|
8650
|
-
}
|
|
8651
|
-
var STATIC_PATTERNS = ["/", "/catalog", "/privacy", "/terms", "/refunds"];
|
|
8652
|
-
function ensureResolved(config) {
|
|
8653
|
-
return "features" in config && "pages" in config ? config : resolveConfig(config);
|
|
8654
|
-
}
|
|
8655
|
-
function isPublicPath(config, path) {
|
|
8656
|
-
const match = resolveStorefrontRoute(config, path);
|
|
8657
|
-
switch (match.kind) {
|
|
8658
|
-
case "checkout":
|
|
8659
|
-
case "account":
|
|
8660
|
-
case "order":
|
|
8661
|
-
return false;
|
|
8662
|
-
case "custom":
|
|
8663
|
-
return match.route.kind !== "checkout" && match.route.kind !== "account" && match.route.chrome !== "focused";
|
|
8664
|
-
default:
|
|
8665
|
-
return true;
|
|
8666
|
-
}
|
|
8667
|
-
}
|
|
8668
|
-
async function storefrontPaths(input) {
|
|
8669
|
-
const resolved = ensureResolved(input.config);
|
|
8670
|
-
const paths = [...STATIC_PATTERNS];
|
|
8671
|
-
if (resolved.delivery) paths.push(resolved.delivery.menuPath);
|
|
8672
|
-
for (const route of resolved.routes ?? []) {
|
|
8673
|
-
if (!route.path.includes(":")) paths.push(route.path);
|
|
8674
|
-
}
|
|
8675
|
-
const products = await input.provider.listProducts({ status: "active" });
|
|
8676
|
-
for (const product of products) paths.push(`/product/${product.slug}`);
|
|
8677
|
-
const unique = Array.from(new Set(paths));
|
|
8678
|
-
return input.includePrivate ? unique : unique.filter((p) => isPublicPath(resolved, p));
|
|
8679
|
-
}
|
|
8680
|
-
|
|
8681
7634
|
// src/data/local-orders.ts
|
|
8682
7635
|
var KEY = "fayz.storefront.orders.v1";
|
|
8683
7636
|
var MAX = 25;
|
|
@@ -8703,7 +7656,7 @@ function listLocalOrderIds() {
|
|
|
8703
7656
|
}
|
|
8704
7657
|
|
|
8705
7658
|
// src/lib/errors.ts
|
|
8706
|
-
function
|
|
7659
|
+
function errorMessage4(err) {
|
|
8707
7660
|
if (err instanceof Error) return err.message;
|
|
8708
7661
|
if (err && typeof err === "object" && typeof err.message === "string") {
|
|
8709
7662
|
return err.message;
|
|
@@ -8720,7 +7673,7 @@ function formatDeliveryNotes(address, shippingAddress) {
|
|
|
8720
7673
|
return parts.length > 0 ? `Entrega: ${parts.join(", ")}` : void 0;
|
|
8721
7674
|
}
|
|
8722
7675
|
function isStaleCustomerError(err) {
|
|
8723
|
-
const message =
|
|
7676
|
+
const message = errorMessage4(err).toLowerCase();
|
|
8724
7677
|
return message.includes("customer email mismatch") || message.includes("customer does not belong to this store") || message.includes("customer belongs to another account");
|
|
8725
7678
|
}
|
|
8726
7679
|
async function placeStorefrontOrder({
|
|
@@ -8983,8 +7936,8 @@ function AddressForm({ country, language, value, onChange, onPostalCode, lookup,
|
|
|
8983
7936
|
postalHint()
|
|
8984
7937
|
] }, "postalCode");
|
|
8985
7938
|
}
|
|
8986
|
-
if (row.length === 1) return /* @__PURE__ */ jsx(
|
|
8987
|
-
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: cells.map((key) => /* @__PURE__ */ jsx(
|
|
7939
|
+
if (row.length === 1) return /* @__PURE__ */ jsx(React37__default.Fragment, { children: input(specs.get(cells[0])) }, cells[0]);
|
|
7940
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: cells.map((key) => /* @__PURE__ */ jsx(React37__default.Fragment, { children: input(specs.get(key)) }, key)) }, cells.join("-"));
|
|
8988
7941
|
}) });
|
|
8989
7942
|
}
|
|
8990
7943
|
function flag(iso2) {
|
|
@@ -9160,7 +8113,7 @@ function CheckoutSteps({ current, onGoBackTo, omit }) {
|
|
|
9160
8113
|
return /* @__PURE__ */ jsxs("nav", { "data-testid": TID.checkoutSteps, "aria-label": t("checkout.steps.label"), className: "mb-8", children: [
|
|
9161
8114
|
/* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 sm:gap-2", children: steps.map((step, index) => {
|
|
9162
8115
|
const done = step < current;
|
|
9163
|
-
const
|
|
8116
|
+
const active = step === current;
|
|
9164
8117
|
const canGoBack = done && !!onGoBackTo;
|
|
9165
8118
|
const Tag = canGoBack ? "button" : "div";
|
|
9166
8119
|
return (
|
|
@@ -9179,12 +8132,12 @@ function CheckoutSteps({ current, onGoBackTo, omit }) {
|
|
|
9179
8132
|
{
|
|
9180
8133
|
...canGoBack ? { type: "button", onClick: () => onGoBackTo(step) } : {},
|
|
9181
8134
|
"data-testid": TID.checkoutStep(step),
|
|
9182
|
-
"data-state": done ? "done" :
|
|
9183
|
-
"aria-current":
|
|
8135
|
+
"data-state": done ? "done" : active ? "current" : "pending",
|
|
8136
|
+
"aria-current": active ? "step" : void 0,
|
|
9184
8137
|
className: [
|
|
9185
8138
|
"flex min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition",
|
|
9186
8139
|
canGoBack ? "hover:bg-muted" : "",
|
|
9187
|
-
|
|
8140
|
+
active ? "text-foreground" : done ? "text-foreground" : "text-muted-foreground"
|
|
9188
8141
|
].join(" "),
|
|
9189
8142
|
children: [
|
|
9190
8143
|
/* @__PURE__ */ jsx(
|
|
@@ -9192,12 +8145,12 @@ function CheckoutSteps({ current, onGoBackTo, omit }) {
|
|
|
9192
8145
|
{
|
|
9193
8146
|
className: [
|
|
9194
8147
|
"grid h-7 w-7 shrink-0 place-items-center rounded-full border text-xs font-semibold",
|
|
9195
|
-
done ? "border-primary bg-primary text-primary-foreground" :
|
|
8148
|
+
done ? "border-primary bg-primary text-primary-foreground" : active ? "border-primary bg-background text-primary" : "border-muted-foreground/30 bg-background"
|
|
9196
8149
|
].join(" "),
|
|
9197
8150
|
children: done ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : index + 1
|
|
9198
8151
|
}
|
|
9199
8152
|
),
|
|
9200
|
-
/* @__PURE__ */ jsx("span", { className: `hidden truncate text-sm sm:inline ${
|
|
8153
|
+
/* @__PURE__ */ jsx("span", { className: `hidden truncate text-sm sm:inline ${active ? "font-semibold" : ""}`, children: t(LABELS2[step]) })
|
|
9201
8154
|
]
|
|
9202
8155
|
}
|
|
9203
8156
|
),
|
|
@@ -9340,13 +8293,13 @@ function CardBrick({ publicKey, amount, email, onSubmit, onError }) {
|
|
|
9340
8293
|
var POLL_MS = 3e3;
|
|
9341
8294
|
var RESUME_KEY = "fz.checkout.resume";
|
|
9342
8295
|
function useExpiry(expiresAt) {
|
|
9343
|
-
const target =
|
|
8296
|
+
const target = React37__default.useMemo(() => {
|
|
9344
8297
|
if (!expiresAt) return null;
|
|
9345
8298
|
const t = new Date(expiresAt).getTime();
|
|
9346
8299
|
return Number.isFinite(t) ? t : null;
|
|
9347
8300
|
}, [expiresAt]);
|
|
9348
|
-
const [now, setNow] =
|
|
9349
|
-
|
|
8301
|
+
const [now, setNow] = React37__default.useState(() => Date.now());
|
|
8302
|
+
React37__default.useEffect(() => {
|
|
9350
8303
|
if (target === null) return;
|
|
9351
8304
|
const id = setInterval(() => setNow(Date.now()), 1e3);
|
|
9352
8305
|
return () => clearInterval(id);
|
|
@@ -9936,7 +8889,7 @@ function ContactSummary({ name, email, phone, document: document2, onEdit, onSig
|
|
|
9936
8889
|
function toShippingAddress(form, country) {
|
|
9937
8890
|
const schema = getCountrySchema(country);
|
|
9938
8891
|
const isBrazil = schema.iso2 === "BR";
|
|
9939
|
-
const has = (key) => schema.fields.some((
|
|
8892
|
+
const has = (key) => schema.fields.some((f) => f.key === key);
|
|
9940
8893
|
return {
|
|
9941
8894
|
postalCode: isBrazil ? form.zip.trim() : normalizePostalCode(form.zip, country),
|
|
9942
8895
|
street: form.street.trim(),
|
|
@@ -9965,7 +8918,7 @@ var PAYMENT_LABELS = {
|
|
|
9965
8918
|
};
|
|
9966
8919
|
var BRAZIL_ONLY = ["pix", "boleto"];
|
|
9967
8920
|
function placeOrderMessage(err, t) {
|
|
9968
|
-
const message =
|
|
8921
|
+
const message = errorMessage4(err);
|
|
9969
8922
|
if (/customer (email mismatch|does not belong|belongs to another)/i.test(message)) {
|
|
9970
8923
|
return t("error.sessionMismatch");
|
|
9971
8924
|
}
|
|
@@ -10042,7 +8995,7 @@ function CheckoutPage() {
|
|
|
10042
8995
|
district: "",
|
|
10043
8996
|
state: ""
|
|
10044
8997
|
});
|
|
10045
|
-
|
|
8998
|
+
React37__default.useEffect(() => {
|
|
10046
8999
|
if (!known.ready) return;
|
|
10047
9000
|
setForm((current) => ({
|
|
10048
9001
|
...current,
|
|
@@ -10058,7 +9011,7 @@ function CheckoutPage() {
|
|
|
10058
9011
|
}));
|
|
10059
9012
|
}, [known.ready, known.phone, known.document, known.address]);
|
|
10060
9013
|
const [step, setStep] = useState(resume ? 3 : 1);
|
|
10061
|
-
const reportedStart =
|
|
9014
|
+
const reportedStart = React37__default.useRef(false);
|
|
10062
9015
|
useEffect(() => {
|
|
10063
9016
|
if (reportedStart.current) return;
|
|
10064
9017
|
reportedStart.current = true;
|
|
@@ -10071,7 +9024,7 @@ function CheckoutPage() {
|
|
|
10071
9024
|
});
|
|
10072
9025
|
}
|
|
10073
9026
|
}, []);
|
|
10074
|
-
const reportedStep =
|
|
9027
|
+
const reportedStep = React37__default.useRef(null);
|
|
10075
9028
|
useEffect(() => {
|
|
10076
9029
|
if (reportedStep.current === step) return;
|
|
10077
9030
|
reportedStep.current = step;
|
|
@@ -10080,7 +9033,7 @@ function CheckoutPage() {
|
|
|
10080
9033
|
}, [step]);
|
|
10081
9034
|
const [contactKnown, setContactKnown] = useState(false);
|
|
10082
9035
|
const [editingContact, setEditingContact] = useState(false);
|
|
10083
|
-
const skippedContact =
|
|
9036
|
+
const skippedContact = React37__default.useRef(false);
|
|
10084
9037
|
const fulfillmentType = useDeliveryStore((s2) => s2.fulfillmentType);
|
|
10085
9038
|
const scheduledFor = useDeliveryStore((s2) => s2.scheduledFor);
|
|
10086
9039
|
const deliveryReference = useDeliveryStore((s2) => {
|
|
@@ -10109,7 +9062,7 @@ function CheckoutPage() {
|
|
|
10109
9062
|
const [applyingDiscount, setApplyingDiscount] = useState(false);
|
|
10110
9063
|
const [showSignin, setShowSignin] = useState(false);
|
|
10111
9064
|
const [placing, setPlacing] = useState(false);
|
|
10112
|
-
const leaving =
|
|
9065
|
+
const leaving = React37__default.useRef(false);
|
|
10113
9066
|
const [zipLookup, setZipLookup] = useState("idle");
|
|
10114
9067
|
const subtotal = selectSubtotal(cart);
|
|
10115
9068
|
const discountTotal = selectDiscountTotal(cart);
|
|
@@ -10117,7 +9070,7 @@ function CheckoutPage() {
|
|
|
10117
9070
|
const shipping = isPickup ? 0 : shippingRaw;
|
|
10118
9071
|
const total = selectTotal(cart, config) - (isPickup ? shippingRaw : 0);
|
|
10119
9072
|
const checkoutSlotContext = { subtotal, total };
|
|
10120
|
-
const checkoutSessionInput =
|
|
9073
|
+
const checkoutSessionInput = React37__default.useMemo(() => ({
|
|
10121
9074
|
items: cart.lines.map((line) => ({
|
|
10122
9075
|
productId: line.productId,
|
|
10123
9076
|
variantId: line.variantId ?? null,
|
|
@@ -10294,7 +9247,7 @@ function CheckoutPage() {
|
|
|
10294
9247
|
cancelled = true;
|
|
10295
9248
|
};
|
|
10296
9249
|
}, [session.customerId]);
|
|
10297
|
-
const shownCountry =
|
|
9250
|
+
const shownCountry = React37__default.useRef(country);
|
|
10298
9251
|
useEffect(() => {
|
|
10299
9252
|
if (shownCountry.current === country) return;
|
|
10300
9253
|
shownCountry.current = country;
|
|
@@ -10445,7 +9398,7 @@ function CheckoutPage() {
|
|
|
10445
9398
|
if (errors.postalCode || errors.street || errors.city) return fail(t("error.address"), errors.postalCode ? TID.checkoutZip : TID.checkoutStreet);
|
|
10446
9399
|
if (errors.complement) return fail(t("error.complement"), "checkout-complement");
|
|
10447
9400
|
if (errors.region) {
|
|
10448
|
-
const label = countrySchema.fields.find((
|
|
9401
|
+
const label = countrySchema.fields.find((f) => f.key === "region")?.label[config.language] ?? "";
|
|
10449
9402
|
return fail(t("error.region", { label: label.toLowerCase() }), "checkout-state");
|
|
10450
9403
|
}
|
|
10451
9404
|
return true;
|
|
@@ -11083,6 +10036,8 @@ function StoreCover({ onShowHours, onOpenAccount }) {
|
|
|
11083
10036
|
const cover = config.delivery?.coverImage;
|
|
11084
10037
|
const logo = config.delivery?.logoImage;
|
|
11085
10038
|
const locality = config.delivery?.locality;
|
|
10039
|
+
const size = useExperimentValue(config.delivery?.coverSize) ?? "full";
|
|
10040
|
+
const compact = size === "compact";
|
|
11086
10041
|
const modes = config.delivery?.fulfillmentModes ?? ["delivery"];
|
|
11087
10042
|
const type = useDeliveryStore((s2) => s2.fulfillmentType);
|
|
11088
10043
|
const setFulfillmentType = useDeliveryStore((s2) => s2.setFulfillmentType);
|
|
@@ -11094,89 +10049,97 @@ function StoreCover({ onShowHours, onOpenAccount }) {
|
|
|
11094
10049
|
setFulfillmentType(next);
|
|
11095
10050
|
void refreshService(next);
|
|
11096
10051
|
}
|
|
11097
|
-
return /* @__PURE__ */ jsxs(
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
className: "absolute right-4 top-3 grid h-10 w-10 place-items-center rounded-full bg-black/40 text-sm font-bold ring-1 ring-white/25 backdrop-blur",
|
|
11123
|
-
children: sessionName ? sessionName.slice(0, 1).toUpperCase() : /* @__PURE__ */ jsx(UserRound, { className: "h-5 w-5" })
|
|
11124
|
-
}
|
|
11125
|
-
),
|
|
11126
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 [text-shadow:0_1px_3px_rgb(0_0_0_/_0.7)]", children: [
|
|
11127
|
-
/* @__PURE__ */ jsx("div", { className: "grid h-14 w-14 shrink-0 place-items-center overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/20", children: logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "text-xl font-bold", children: config.name.slice(0, 1) }) }),
|
|
11128
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
11129
|
-
/* @__PURE__ */ jsx("h1", { className: "truncate text-xl font-bold leading-tight", children: config.name }),
|
|
11130
|
-
locality && /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-white/70", children: locality }),
|
|
11131
|
-
config.delivery?.paused ? /* @__PURE__ */ jsxs("p", { "data-testid": "store-delivery-paused", className: "mt-0.5 inline-flex items-center gap-1.5 truncate text-sm font-semibold text-amber-300", children: [
|
|
11132
|
-
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400", "aria-hidden": true }),
|
|
11133
|
-
t("delivery.paused")
|
|
11134
|
-
] }) : (() => {
|
|
11135
|
-
const eta = type !== "pickup" && config.delivery?.etaMinutes ? etaParts(config.delivery.etaMinutes) : null;
|
|
11136
|
-
return eta ? /* @__PURE__ */ jsx("p", { "data-testid": "store-eta", className: "truncate text-sm text-white/80", children: t(eta.approx ? "delivery.eta" : "delivery.etaUpTo", { duration: eta.text }) }) : null;
|
|
11137
|
-
})(),
|
|
11138
|
-
showsHours && service.service && // O estado é o botão que abre a placa da porta. Um selo que diz
|
|
11139
|
-
// "Fechado" e não leva a lugar nenhum deixa a pessoa sem a única
|
|
11140
|
-
// pergunta seguinte que ela tem: então quando abre?
|
|
11141
|
-
/* @__PURE__ */ jsxs(
|
|
10052
|
+
return /* @__PURE__ */ jsxs(
|
|
10053
|
+
"header",
|
|
10054
|
+
{
|
|
10055
|
+
"data-testid": TID.storeCover,
|
|
10056
|
+
"data-cover-size": size,
|
|
10057
|
+
className: `relative isolate bg-neutral-900 text-white${compact ? " max-h-40 overflow-hidden" : ""}`,
|
|
10058
|
+
children: [
|
|
10059
|
+
cover && /* @__PURE__ */ jsx(
|
|
10060
|
+
"img",
|
|
10061
|
+
{
|
|
10062
|
+
src: cover,
|
|
10063
|
+
alt: "",
|
|
10064
|
+
"aria-hidden": true,
|
|
10065
|
+
className: "absolute inset-0 -z-10 h-full w-full object-cover"
|
|
10066
|
+
}
|
|
10067
|
+
),
|
|
10068
|
+
/* @__PURE__ */ jsx(
|
|
10069
|
+
"div",
|
|
10070
|
+
{
|
|
10071
|
+
"aria-hidden": true,
|
|
10072
|
+
className: "absolute inset-0 -z-10 bg-gradient-to-b from-black/30 via-black/45 to-black/75"
|
|
10073
|
+
}
|
|
10074
|
+
),
|
|
10075
|
+
/* @__PURE__ */ jsxs("div", { className: `relative mx-auto w-full max-w-3xl px-4 ${compact ? "pb-3 pt-3" : "pb-4 pt-8"}`, children: [
|
|
10076
|
+
onOpenAccount && /* @__PURE__ */ jsx(
|
|
11142
10077
|
"button",
|
|
11143
10078
|
{
|
|
11144
10079
|
type: "button",
|
|
11145
|
-
|
|
11146
|
-
"data-
|
|
11147
|
-
|
|
11148
|
-
className: "
|
|
11149
|
-
children:
|
|
11150
|
-
/* @__PURE__ */ jsx(
|
|
11151
|
-
"span",
|
|
11152
|
-
{
|
|
11153
|
-
"aria-hidden": true,
|
|
11154
|
-
className: `h-2 w-2 rounded-full ${open ? "bg-emerald-400" : "bg-red-400"}`
|
|
11155
|
-
}
|
|
11156
|
-
),
|
|
11157
|
-
open ? /* @__PURE__ */ jsx(OpenUntil, {}) : /* @__PURE__ */ jsx(ClosedUntil, {})
|
|
11158
|
-
]
|
|
10080
|
+
onClick: onOpenAccount,
|
|
10081
|
+
"data-testid": "store-account",
|
|
10082
|
+
"aria-label": sessionName ? "Sua conta" : "Entrar ou cadastrar",
|
|
10083
|
+
className: "absolute right-4 top-3 grid h-10 w-10 place-items-center rounded-full bg-black/40 text-sm font-bold ring-1 ring-white/25 backdrop-blur",
|
|
10084
|
+
children: sessionName ? sessionName.slice(0, 1).toUpperCase() : /* @__PURE__ */ jsx(UserRound, { className: "h-5 w-5" })
|
|
11159
10085
|
}
|
|
11160
|
-
)
|
|
10086
|
+
),
|
|
10087
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 [text-shadow:0_1px_3px_rgb(0_0_0_/_0.7)]", children: [
|
|
10088
|
+
/* @__PURE__ */ jsx("div", { className: `grid ${compact ? "h-11 w-11" : "h-14 w-14"} shrink-0 place-items-center overflow-hidden rounded-xl bg-white/10 ring-1 ring-white/20`, children: logo ? /* @__PURE__ */ jsx("img", { src: logo, alt: "", "aria-hidden": true, className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "text-xl font-bold", children: config.name.slice(0, 1) }) }),
|
|
10089
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
10090
|
+
/* @__PURE__ */ jsx("h1", { className: "truncate text-xl font-bold leading-tight", children: config.name }),
|
|
10091
|
+
locality && /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-white/70", children: locality }),
|
|
10092
|
+
config.delivery?.paused ? /* @__PURE__ */ jsxs("p", { "data-testid": "store-delivery-paused", className: "mt-0.5 inline-flex items-center gap-1.5 truncate text-sm font-semibold text-amber-300", children: [
|
|
10093
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400", "aria-hidden": true }),
|
|
10094
|
+
t("delivery.paused")
|
|
10095
|
+
] }) : (() => {
|
|
10096
|
+
const eta = type !== "pickup" && config.delivery?.etaMinutes ? etaParts(config.delivery.etaMinutes) : null;
|
|
10097
|
+
return eta ? /* @__PURE__ */ jsx("p", { "data-testid": "store-eta", className: "truncate text-sm text-white/80", children: t(eta.approx ? "delivery.eta" : "delivery.etaUpTo", { duration: eta.text }) }) : null;
|
|
10098
|
+
})(),
|
|
10099
|
+
showsHours && service.service && // O estado é o botão que abre a placa da porta. Um selo que diz
|
|
10100
|
+
// "Fechado" e não leva a lugar nenhum deixa a pessoa sem a única
|
|
10101
|
+
// pergunta seguinte que ela tem: então quando abre?
|
|
10102
|
+
/* @__PURE__ */ jsxs(
|
|
10103
|
+
"button",
|
|
10104
|
+
{
|
|
10105
|
+
type: "button",
|
|
10106
|
+
"data-testid": TID.serviceStatus,
|
|
10107
|
+
"data-open": open ? "true" : "false",
|
|
10108
|
+
onClick: onShowHours,
|
|
10109
|
+
className: "mt-0.5 flex items-center gap-1.5 text-sm font-semibold underline-offset-2 hover:underline",
|
|
10110
|
+
children: [
|
|
10111
|
+
/* @__PURE__ */ jsx(
|
|
10112
|
+
"span",
|
|
10113
|
+
{
|
|
10114
|
+
"aria-hidden": true,
|
|
10115
|
+
className: `h-2 w-2 rounded-full ${open ? "bg-emerald-400" : "bg-red-400"}`
|
|
10116
|
+
}
|
|
10117
|
+
),
|
|
10118
|
+
open ? /* @__PURE__ */ jsx(OpenUntil, {}) : /* @__PURE__ */ jsx(ClosedUntil, {})
|
|
10119
|
+
]
|
|
10120
|
+
}
|
|
10121
|
+
)
|
|
10122
|
+
] })
|
|
10123
|
+
] }),
|
|
10124
|
+
modes.length > 1 && /* @__PURE__ */ jsx("div", { className: "mt-4 flex gap-1", children: modes.map((mode) => {
|
|
10125
|
+
const active = type === mode;
|
|
10126
|
+
return /* @__PURE__ */ jsx(
|
|
10127
|
+
"button",
|
|
10128
|
+
{
|
|
10129
|
+
type: "button",
|
|
10130
|
+
"data-testid": TID.fulfillmentMode(mode),
|
|
10131
|
+
"aria-pressed": active,
|
|
10132
|
+
onClick: () => choose(mode),
|
|
10133
|
+
className: `rounded-full px-5 py-2 text-sm font-semibold transition ${active ? "bg-white text-neutral-900" : "text-white/80"}`,
|
|
10134
|
+
children: mode === "pickup" ? "Retirada" : "Entrega"
|
|
10135
|
+
},
|
|
10136
|
+
mode
|
|
10137
|
+
);
|
|
10138
|
+
}) })
|
|
11161
10139
|
] })
|
|
11162
|
-
]
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
return /* @__PURE__ */ jsx(
|
|
11166
|
-
"button",
|
|
11167
|
-
{
|
|
11168
|
-
type: "button",
|
|
11169
|
-
"data-testid": TID.fulfillmentMode(mode),
|
|
11170
|
-
"aria-pressed": active2,
|
|
11171
|
-
onClick: () => choose(mode),
|
|
11172
|
-
className: `rounded-full px-5 py-2 text-sm font-semibold transition ${active2 ? "bg-white text-neutral-900" : "text-white/80"}`,
|
|
11173
|
-
children: mode === "pickup" ? "Retirada" : "Entrega"
|
|
11174
|
-
},
|
|
11175
|
-
mode
|
|
11176
|
-
);
|
|
11177
|
-
}) })
|
|
11178
|
-
] })
|
|
11179
|
-
] });
|
|
10140
|
+
]
|
|
10141
|
+
}
|
|
10142
|
+
);
|
|
11180
10143
|
}
|
|
11181
10144
|
function OpenUntil() {
|
|
11182
10145
|
const service = useServiceStore((s2) => s2.service);
|
|
@@ -11207,8 +10170,8 @@ function AddressStrip({
|
|
|
11207
10170
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
11208
10171
|
const subtotal = selectSubtotal(useCartStore());
|
|
11209
10172
|
const resolve = useDeliveryStore((s2) => s2.resolve);
|
|
11210
|
-
const quotedOnce =
|
|
11211
|
-
|
|
10173
|
+
const quotedOnce = React37__default.useRef(false);
|
|
10174
|
+
React37__default.useEffect(() => {
|
|
11212
10175
|
if (quotedOnce.current || isPickup) return;
|
|
11213
10176
|
if (delivery.status !== "idle" || delivery.options.length > 0 || !delivery.postalCode) return;
|
|
11214
10177
|
quotedOnce.current = true;
|
|
@@ -11270,17 +10233,17 @@ function MenuToolbar({
|
|
|
11270
10233
|
onQuery,
|
|
11271
10234
|
autoFocusSearch
|
|
11272
10235
|
}) {
|
|
11273
|
-
const inputRef =
|
|
11274
|
-
const [
|
|
11275
|
-
|
|
10236
|
+
const inputRef = React37__default.useRef(null);
|
|
10237
|
+
const [active, setActive] = React37__default.useState(null);
|
|
10238
|
+
React37__default.useEffect(() => {
|
|
11276
10239
|
setActive(
|
|
11277
10240
|
(current) => current && sections.some((s2) => s2.slug === current) ? current : sections[0]?.slug ?? null
|
|
11278
10241
|
);
|
|
11279
10242
|
}, [sections]);
|
|
11280
|
-
|
|
10243
|
+
React37__default.useEffect(() => {
|
|
11281
10244
|
if (autoFocusSearch) inputRef.current?.focus();
|
|
11282
10245
|
}, [autoFocusSearch]);
|
|
11283
|
-
|
|
10246
|
+
React37__default.useEffect(() => {
|
|
11284
10247
|
if (typeof IntersectionObserver === "undefined") return;
|
|
11285
10248
|
const seen = /* @__PURE__ */ new Map();
|
|
11286
10249
|
const observer = new IntersectionObserver(
|
|
@@ -11348,7 +10311,7 @@ function MenuToolbar({
|
|
|
11348
10311
|
"aria-label": "Categorias",
|
|
11349
10312
|
className: "mx-auto w-full max-w-3xl overflow-x-auto pb-3 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
11350
10313
|
children: /* @__PURE__ */ jsx("ul", { className: "flex w-max gap-2 px-4", children: sections.map((section) => {
|
|
11351
|
-
const on =
|
|
10314
|
+
const on = active === section.slug;
|
|
11352
10315
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
11353
10316
|
"button",
|
|
11354
10317
|
{
|
|
@@ -11467,10 +10430,10 @@ function ItemSheet({
|
|
|
11467
10430
|
}) {
|
|
11468
10431
|
const config = useStorefrontConfig();
|
|
11469
10432
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
11470
|
-
const groups =
|
|
11471
|
-
const [options, setOptions] =
|
|
11472
|
-
const [notes, setNotes] =
|
|
11473
|
-
const [quantity, setQuantity] =
|
|
10433
|
+
const groups = React37__default.useMemo(() => getProductOptionGroups(product), [product]);
|
|
10434
|
+
const [options, setOptions] = React37__default.useState({});
|
|
10435
|
+
const [notes, setNotes] = React37__default.useState("");
|
|
10436
|
+
const [quantity, setQuantity] = React37__default.useState(1);
|
|
11474
10437
|
const image = product.images?.find((i) => i.isPrimary) ?? product.images?.[0];
|
|
11475
10438
|
const stock = product.inventoryCount;
|
|
11476
10439
|
const missing = groups.filter((g) => !options[g.label]);
|
|
@@ -11664,8 +10627,8 @@ function ScheduleSheet({ onClose }) {
|
|
|
11664
10627
|
const scheduledFor = useDeliveryStore((s2) => s2.scheduledFor);
|
|
11665
10628
|
const setScheduledFor = useDeliveryStore((s2) => s2.setScheduledFor);
|
|
11666
10629
|
const timezone = useServiceStore((s2) => s2.service?.timezone) ?? "America/Sao_Paulo";
|
|
11667
|
-
const [slots, setSlots] =
|
|
11668
|
-
|
|
10630
|
+
const [slots, setSlots] = React37__default.useState(null);
|
|
10631
|
+
React37__default.useEffect(() => {
|
|
11669
10632
|
let alive = true;
|
|
11670
10633
|
void (async () => {
|
|
11671
10634
|
const rows = await getShopProvider()?.listScheduleSlots?.(fulfillmentType) ?? [];
|
|
@@ -11675,7 +10638,7 @@ function ScheduleSheet({ onClose }) {
|
|
|
11675
10638
|
alive = false;
|
|
11676
10639
|
};
|
|
11677
10640
|
}, [fulfillmentType]);
|
|
11678
|
-
const days =
|
|
10641
|
+
const days = React37__default.useMemo(() => {
|
|
11679
10642
|
const fmtDay = new Intl.DateTimeFormat("pt-BR", {
|
|
11680
10643
|
weekday: "long",
|
|
11681
10644
|
day: "2-digit",
|
|
@@ -11728,18 +10691,18 @@ function ScheduleSheet({ onClose }) {
|
|
|
11728
10691
|
days.map(([day, daySlots]) => /* @__PURE__ */ jsxs("div", { className: "mb-5", children: [
|
|
11729
10692
|
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-sm font-semibold capitalize", children: day }),
|
|
11730
10693
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: daySlots.map((slot) => {
|
|
11731
|
-
const
|
|
10694
|
+
const active = scheduledFor === slot.startsAt;
|
|
11732
10695
|
return /* @__PURE__ */ jsxs(
|
|
11733
10696
|
"button",
|
|
11734
10697
|
{
|
|
11735
10698
|
type: "button",
|
|
11736
10699
|
"data-testid": TID.scheduleSlot(slot.startsAt),
|
|
11737
|
-
"aria-pressed":
|
|
10700
|
+
"aria-pressed": active,
|
|
11738
10701
|
onClick: () => {
|
|
11739
10702
|
setScheduledFor(slot.startsAt);
|
|
11740
10703
|
onClose();
|
|
11741
10704
|
},
|
|
11742
|
-
className: `rounded-lg border px-3 py-2 text-sm ${
|
|
10705
|
+
className: `rounded-lg border px-3 py-2 text-sm ${active ? "border-primary font-medium" : "border-border"}`,
|
|
11743
10706
|
children: [
|
|
11744
10707
|
fmtTime.format(new Date(slot.startsAt)),
|
|
11745
10708
|
" \u2013 ",
|
|
@@ -11761,8 +10724,8 @@ function hhmm(time) {
|
|
|
11761
10724
|
}
|
|
11762
10725
|
function ServiceHoursModal({ onClose }) {
|
|
11763
10726
|
const fulfillmentType = useServiceStore((s2) => s2.fulfillmentType);
|
|
11764
|
-
const [hours, setHours] =
|
|
11765
|
-
|
|
10727
|
+
const [hours, setHours] = React37__default.useState(null);
|
|
10728
|
+
React37__default.useEffect(() => {
|
|
11766
10729
|
let alive = true;
|
|
11767
10730
|
void (async () => {
|
|
11768
10731
|
const rows = await getShopProvider()?.listServiceHours?.(fulfillmentType) ?? [];
|
|
@@ -11772,7 +10735,7 @@ function ServiceHoursModal({ onClose }) {
|
|
|
11772
10735
|
alive = false;
|
|
11773
10736
|
};
|
|
11774
10737
|
}, [fulfillmentType]);
|
|
11775
|
-
const byDay =
|
|
10738
|
+
const byDay = React37__default.useMemo(() => {
|
|
11776
10739
|
const map = /* @__PURE__ */ new Map();
|
|
11777
10740
|
for (const h of hours ?? []) {
|
|
11778
10741
|
const list = map.get(h.weekday) ?? [];
|
|
@@ -11857,7 +10820,7 @@ function AddressSheet({ onClose, onSignIn }) {
|
|
|
11857
10820
|
const unsavedCurrent = current && !saved.some((a) => a.id === selectedId) ? current : null;
|
|
11858
10821
|
const fromAccount = accountBook.filter((a) => !saved.some((b) => normalizePostalCode(b.postalCode) === normalizePostalCode(a.postalCode) && b.number.trim().toLowerCase() === (a.number ?? "").trim().toLowerCase()));
|
|
11859
10822
|
const nothingToList = saved.length === 0 && !unsavedCurrent && fromAccount.length === 0 && !customerId;
|
|
11860
|
-
const [editing, setEditing] =
|
|
10823
|
+
const [editing, setEditing] = React37__default.useState(
|
|
11861
10824
|
nothingToList && !onSignIn ? {} : null
|
|
11862
10825
|
);
|
|
11863
10826
|
function pickAccountAddress(a) {
|
|
@@ -11876,13 +10839,13 @@ function AddressSheet({ onClose, onSignIn }) {
|
|
|
11876
10839
|
void selectAddress(id, subtotal);
|
|
11877
10840
|
onClose();
|
|
11878
10841
|
}
|
|
11879
|
-
const closeRef =
|
|
10842
|
+
const closeRef = React37__default.useRef(() => {
|
|
11880
10843
|
});
|
|
11881
10844
|
closeRef.current = () => {
|
|
11882
10845
|
if (editing && !nothingToList) setEditing(null);
|
|
11883
10846
|
else onClose();
|
|
11884
10847
|
};
|
|
11885
|
-
|
|
10848
|
+
React37__default.useEffect(() => {
|
|
11886
10849
|
const onKey = (e) => {
|
|
11887
10850
|
if (e.key === "Escape") closeRef.current();
|
|
11888
10851
|
};
|
|
@@ -12063,8 +11026,8 @@ function AddressLines({ address }) {
|
|
|
12063
11026
|
] });
|
|
12064
11027
|
}
|
|
12065
11028
|
function useAccountAddresses(customerId) {
|
|
12066
|
-
const [rows, setRows] =
|
|
12067
|
-
|
|
11029
|
+
const [rows, setRows] = React37__default.useState([]);
|
|
11030
|
+
React37__default.useEffect(() => {
|
|
12068
11031
|
if (!customerId) {
|
|
12069
11032
|
setRows([]);
|
|
12070
11033
|
return;
|
|
@@ -12092,24 +11055,24 @@ function AddressForm2({ onDone, initial: initial2, replaces }) {
|
|
|
12092
11055
|
const storeError = useDeliveryStore((s2) => s2.error);
|
|
12093
11056
|
const refusedZone = useDeliveryStore((s2) => s2.refusedZone);
|
|
12094
11057
|
const subtotal = selectSubtotal(useCartStore());
|
|
12095
|
-
const [cep, setCep] =
|
|
12096
|
-
const [number, setNumber] =
|
|
12097
|
-
const [complement, setComplement] =
|
|
12098
|
-
const [reference, setReference] =
|
|
12099
|
-
const [label, setLabel] =
|
|
12100
|
-
const [touched, setTouched] =
|
|
12101
|
-
const numberRef =
|
|
11058
|
+
const [cep, setCep] = React37__default.useState(initial2?.postalCode ? formatPostalCode(initial2.postalCode) : "");
|
|
11059
|
+
const [number, setNumber] = React37__default.useState(initial2?.number ?? "");
|
|
11060
|
+
const [complement, setComplement] = React37__default.useState(initial2?.complement ?? "");
|
|
11061
|
+
const [reference, setReference] = React37__default.useState(initial2?.reference ?? "");
|
|
11062
|
+
const [label, setLabel] = React37__default.useState(initial2?.label ?? "");
|
|
11063
|
+
const [touched, setTouched] = React37__default.useState(false);
|
|
11064
|
+
const numberRef = React37__default.useRef(null);
|
|
12102
11065
|
const digits = normalizePostalCode(cep);
|
|
12103
11066
|
const found = Boolean(looked) && normalizePostalCode(looked.postalCode) === digits;
|
|
12104
|
-
|
|
11067
|
+
React37__default.useEffect(() => {
|
|
12105
11068
|
if (found) numberRef.current?.focus();
|
|
12106
11069
|
}, [found]);
|
|
12107
11070
|
async function lookUp() {
|
|
12108
11071
|
if (digits.length !== 8) return;
|
|
12109
11072
|
await resolve(digits, subtotal);
|
|
12110
11073
|
}
|
|
12111
|
-
const prefilled =
|
|
12112
|
-
|
|
11074
|
+
const prefilled = React37__default.useRef(false);
|
|
11075
|
+
React37__default.useEffect(() => {
|
|
12113
11076
|
if (prefilled.current || !initial2?.postalCode) return;
|
|
12114
11077
|
prefilled.current = true;
|
|
12115
11078
|
if (!found) void lookUp();
|
|
@@ -12325,7 +11288,7 @@ function AccountDrawer({ onClose, onSignIn, onAddresses, onShowHours }) {
|
|
|
12325
11288
|
const session = useSessionStore();
|
|
12326
11289
|
const signedIn = Boolean(session.email);
|
|
12327
11290
|
const name = session.name || session.email?.split("@")[0] || "";
|
|
12328
|
-
|
|
11291
|
+
React37__default.useEffect(() => {
|
|
12329
11292
|
const onKey = (event) => {
|
|
12330
11293
|
if (event.key === "Escape") onClose();
|
|
12331
11294
|
};
|
|
@@ -12406,19 +11369,11 @@ function AccountDrawer({ onClose, onSignIn, onAddresses, onShowHours }) {
|
|
|
12406
11369
|
] })
|
|
12407
11370
|
] });
|
|
12408
11371
|
}
|
|
12409
|
-
var MENU_DATA_NEEDS = {
|
|
12410
|
-
products: { loader: "products.list", params: {} },
|
|
12411
|
-
categories: { loader: "categories.list", params: {} },
|
|
12412
|
-
featured: { loader: "products.featured", params: {} }
|
|
12413
|
-
};
|
|
12414
|
-
function menuDataNeeds() {
|
|
12415
|
-
return Object.values(MENU_DATA_NEEDS).map(({ loader, params }) => ({ key: dataNeedKey(loader, params), loader, params }));
|
|
12416
|
-
}
|
|
12417
11372
|
function MenuPage() {
|
|
12418
11373
|
const config = useStorefrontConfig();
|
|
12419
11374
|
const addItem = useCartStore((s2) => s2.addItem);
|
|
12420
|
-
const [openProduct, setOpenProduct] =
|
|
12421
|
-
|
|
11375
|
+
const [openProduct, setOpenProduct] = React37__default.useState(null);
|
|
11376
|
+
React37__default.useEffect(() => {
|
|
12422
11377
|
if (!openProduct) return;
|
|
12423
11378
|
trackStorefront("product_viewed", {
|
|
12424
11379
|
product_id: openProduct.id,
|
|
@@ -12429,18 +11384,18 @@ function MenuPage() {
|
|
|
12429
11384
|
surface: "menu_sheet"
|
|
12430
11385
|
});
|
|
12431
11386
|
}, [openProduct?.id]);
|
|
12432
|
-
const [showHours, setShowHours] =
|
|
12433
|
-
const [showClosed, setShowClosed] =
|
|
12434
|
-
const [showAddress, setShowAddress] =
|
|
12435
|
-
const [showAccount, setShowAccount] =
|
|
12436
|
-
const [showSignIn, setShowSignIn] =
|
|
12437
|
-
const [showSchedule, setShowSchedule] =
|
|
12438
|
-
const [query, setQuery] =
|
|
11387
|
+
const [showHours, setShowHours] = React37__default.useState(false);
|
|
11388
|
+
const [showClosed, setShowClosed] = React37__default.useState(false);
|
|
11389
|
+
const [showAddress, setShowAddress] = React37__default.useState(false);
|
|
11390
|
+
const [showAccount, setShowAccount] = React37__default.useState(false);
|
|
11391
|
+
const [showSignIn, setShowSignIn] = React37__default.useState(false);
|
|
11392
|
+
const [showSchedule, setShowSchedule] = React37__default.useState(false);
|
|
11393
|
+
const [query, setQuery] = React37__default.useState("");
|
|
12439
11394
|
const serviceState = useServiceStore();
|
|
12440
11395
|
const refreshService = useServiceStore((s2) => s2.refresh);
|
|
12441
11396
|
const paused = Boolean(config.delivery?.paused);
|
|
12442
11397
|
const isOpen = selectIsOpen(serviceState) && !paused;
|
|
12443
|
-
|
|
11398
|
+
React37__default.useEffect(() => {
|
|
12444
11399
|
void refreshService();
|
|
12445
11400
|
}, [refreshService]);
|
|
12446
11401
|
const cart = useCartStore();
|
|
@@ -12462,7 +11417,7 @@ function MenuPage() {
|
|
|
12462
11417
|
const loading = products.status === "loading" || products.status === "idle";
|
|
12463
11418
|
const items = products.data ?? [];
|
|
12464
11419
|
const cats = categories.data ?? [];
|
|
12465
|
-
const allSections =
|
|
11420
|
+
const allSections = React37__default.useMemo(() => {
|
|
12466
11421
|
const byCategory = /* @__PURE__ */ new Map();
|
|
12467
11422
|
const loose = [];
|
|
12468
11423
|
for (const p of items) {
|
|
@@ -12490,7 +11445,7 @@ function MenuPage() {
|
|
|
12490
11445
|
}
|
|
12491
11446
|
return ordered;
|
|
12492
11447
|
}, [items, cats]);
|
|
12493
|
-
const highlights =
|
|
11448
|
+
const highlights = React37__default.useMemo(
|
|
12494
11449
|
() => pickHighlights(items, {
|
|
12495
11450
|
listingFeaturedIds: featured.data?.listing,
|
|
12496
11451
|
menuFeaturedIds: featured.data?.menu,
|
|
@@ -12499,7 +11454,7 @@ function MenuPage() {
|
|
|
12499
11454
|
[items, featured.data, config.delivery?.highlightSlugs]
|
|
12500
11455
|
);
|
|
12501
11456
|
const normalized = query.trim().toLowerCase();
|
|
12502
|
-
const sections =
|
|
11457
|
+
const sections = React37__default.useMemo(() => {
|
|
12503
11458
|
if (!normalized) return allSections;
|
|
12504
11459
|
return allSections.map((section) => ({
|
|
12505
11460
|
...section,
|
|
@@ -12672,7 +11627,7 @@ function MenuPage() {
|
|
|
12672
11627
|
);
|
|
12673
11628
|
}
|
|
12674
11629
|
function BottomBarSpace() {
|
|
12675
|
-
|
|
11630
|
+
React37__default.useEffect(() => {
|
|
12676
11631
|
const root = document.documentElement;
|
|
12677
11632
|
root.style.setProperty("--fayz-bottom-bar", "4.5rem");
|
|
12678
11633
|
return () => {
|
|
@@ -12813,20 +11768,20 @@ var GRID_COLS = {
|
|
|
12813
11768
|
3: "sm:grid-cols-3",
|
|
12814
11769
|
4: "sm:grid-cols-4"
|
|
12815
11770
|
};
|
|
12816
|
-
function OrderTrackingTimeline({ order, compact
|
|
11771
|
+
function OrderTrackingTimeline({ order, compact = false }) {
|
|
12817
11772
|
const steps = orderTrackingSteps(order);
|
|
12818
11773
|
return /* @__PURE__ */ jsx(
|
|
12819
11774
|
"ol",
|
|
12820
11775
|
{
|
|
12821
11776
|
"data-testid": TID.orderTracking,
|
|
12822
|
-
className: `grid ${
|
|
11777
|
+
className: `grid ${compact ? "gap-2" : "gap-3"} ${GRID_COLS[steps.length] ?? "sm:grid-cols-4"}`,
|
|
12823
11778
|
"aria-label": "Acompanhamento do pedido",
|
|
12824
11779
|
children: steps.map((step) => {
|
|
12825
11780
|
const Icon2 = step.icon;
|
|
12826
11781
|
const cancelled = step.state === "cancelled";
|
|
12827
11782
|
const done = step.state === "complete";
|
|
12828
11783
|
const current = step.state === "current";
|
|
12829
|
-
const
|
|
11784
|
+
const active = done || current;
|
|
12830
11785
|
return /* @__PURE__ */ jsx(
|
|
12831
11786
|
"li",
|
|
12832
11787
|
{
|
|
@@ -12834,8 +11789,8 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
12834
11789
|
"data-state": step.state,
|
|
12835
11790
|
className: [
|
|
12836
11791
|
"relative min-w-0 rounded-xl border p-3",
|
|
12837
|
-
cancelled ? "border-destructive/40 bg-destructive/10" :
|
|
12838
|
-
|
|
11792
|
+
cancelled ? "border-destructive/40 bg-destructive/10" : active ? "border-primary/35 bg-primary/5" : "bg-muted/20 text-muted-foreground",
|
|
11793
|
+
compact ? "text-xs" : "text-sm"
|
|
12839
11794
|
].join(" "),
|
|
12840
11795
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
12841
11796
|
/* @__PURE__ */ jsx(
|
|
@@ -12867,7 +11822,7 @@ var menuItems = [
|
|
|
12867
11822
|
{ id: "addresses", label: "Endere\xE7os", icon: MapPin },
|
|
12868
11823
|
{ id: "payments", label: "Pagamentos", icon: CreditCard }
|
|
12869
11824
|
];
|
|
12870
|
-
function CustomerAccountShell({ title, subtitle, active
|
|
11825
|
+
function CustomerAccountShell({ title, subtitle, active, onSelect, children }) {
|
|
12871
11826
|
const config = useStorefrontConfig();
|
|
12872
11827
|
const session = useSessionStore();
|
|
12873
11828
|
const displayName = session.name || session.email?.split("@")[0] || "Cliente";
|
|
@@ -12892,7 +11847,7 @@ function CustomerAccountShell({ title, subtitle, active: active2, onSelect, chil
|
|
|
12892
11847
|
] }),
|
|
12893
11848
|
/* @__PURE__ */ jsx("nav", { "data-testid": TID.customerAccountMenu, className: "mt-4 space-y-1", "aria-label": "\xC1rea do cliente", children: menuItems.map((item) => {
|
|
12894
11849
|
const Icon2 = item.icon;
|
|
12895
|
-
const selected = item.id ===
|
|
11850
|
+
const selected = item.id === active;
|
|
12896
11851
|
return /* @__PURE__ */ jsxs(
|
|
12897
11852
|
Link,
|
|
12898
11853
|
{
|
|
@@ -12963,7 +11918,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
12963
11918
|
const [loading, setLoading] = useState(true);
|
|
12964
11919
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
12965
11920
|
useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
|
|
12966
|
-
const reload =
|
|
11921
|
+
const reload = React37__default.useCallback(() => {
|
|
12967
11922
|
let cancelled = false;
|
|
12968
11923
|
getShopProvider().getOrder(orderId).then((o) => {
|
|
12969
11924
|
if (!cancelled) setOrder(o);
|
|
@@ -12992,7 +11947,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
12992
11947
|
] }, column)) }),
|
|
12993
11948
|
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
12994
11949
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-32" }),
|
|
12995
|
-
/* @__PURE__ */ jsx("div", { className: "mt-3 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(
|
|
11950
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React37__default.Fragment, { children: [
|
|
12996
11951
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-7 shrink-0 rounded-full" }),
|
|
12997
11952
|
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
12998
11953
|
] }, node)) })
|
|
@@ -13279,7 +12234,7 @@ function OrdersPanel() {
|
|
|
13279
12234
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-24 rounded-full" })
|
|
13280
12235
|
] }),
|
|
13281
12236
|
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3 w-20" }),
|
|
13282
|
-
/* @__PURE__ */ jsx("div", { className: "mt-4 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(
|
|
12237
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React37__default.Fragment, { children: [
|
|
13283
12238
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-6 shrink-0 rounded-full" }),
|
|
13284
12239
|
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
13285
12240
|
] }, node)) }),
|
|
@@ -13358,7 +12313,7 @@ function ProfilePanel() {
|
|
|
13358
12313
|
const [saving, setSaving] = useState(false);
|
|
13359
12314
|
const [saved, setSaved] = useState(false);
|
|
13360
12315
|
const [error, setError] = useState(null);
|
|
13361
|
-
|
|
12316
|
+
React37__default.useEffect(() => {
|
|
13362
12317
|
if (!supported) return;
|
|
13363
12318
|
let cancelled = false;
|
|
13364
12319
|
provider.getMyProfile().then((p) => {
|
|
@@ -13651,7 +12606,7 @@ function PolicyPage({ kind }) {
|
|
|
13651
12606
|
] }) })
|
|
13652
12607
|
] });
|
|
13653
12608
|
}
|
|
13654
|
-
var StorefrontErrorBoundary = class extends
|
|
12609
|
+
var StorefrontErrorBoundary = class extends React37__default.Component {
|
|
13655
12610
|
constructor() {
|
|
13656
12611
|
super(...arguments);
|
|
13657
12612
|
this.state = { error: null };
|
|
@@ -13940,1334 +12895,6 @@ function createStorefront(options) {
|
|
|
13940
12895
|
return createStorefrontApp(config);
|
|
13941
12896
|
}
|
|
13942
12897
|
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
var STORE_PAYMENT_MODES = ["mock", "pix-mercadopago", "gateway"];
|
|
13947
|
-
var STORE_PAYMENT_METHOD_KINDS = [
|
|
13948
|
-
"pix",
|
|
13949
|
-
"credit_card",
|
|
13950
|
-
"debit_card",
|
|
13951
|
-
"boleto",
|
|
13952
|
-
"cash",
|
|
13953
|
-
"other"
|
|
13954
|
-
];
|
|
13955
|
-
var STORE_BACKEND_PROVIDERS = ["mock", "fayz-api", "fayz-shop", "custom"];
|
|
13956
|
-
var STORE_ROUTE_KINDS = [
|
|
13957
|
-
"home",
|
|
13958
|
-
"catalog",
|
|
13959
|
-
"product",
|
|
13960
|
-
"checkout",
|
|
13961
|
-
"account",
|
|
13962
|
-
"order",
|
|
13963
|
-
"custom"
|
|
13964
|
-
];
|
|
13965
|
-
var STORE_ROUTE_CHROMES = ["default", "minimal", "focused"];
|
|
13966
|
-
var STORE_THEME_RADII = ["none", "soft", "round", "pill"];
|
|
13967
|
-
var f = (type, description, extra = {}) => ({ type, description, ...extra });
|
|
13968
|
-
var STORE_SECTIONS = {
|
|
13969
|
-
store: {
|
|
13970
|
-
description: "Store identity: name, currency, locale.",
|
|
13971
|
-
versions: [1],
|
|
13972
|
-
fields: {
|
|
13973
|
-
name: f("string", "Store display name (header, page title).", { required: true }),
|
|
13974
|
-
currency: f("string", "ISO 4217 currency code \u2014 default 'BRL'."),
|
|
13975
|
-
locale: f("string", "Intl locale for money/date formatting \u2014 default 'pt-BR'.")
|
|
13976
|
-
}
|
|
13977
|
-
},
|
|
13978
|
-
theme: {
|
|
13979
|
-
description: "Visual identity: colors, fonts, radius, header/card personality.",
|
|
13980
|
-
versions: [1],
|
|
13981
|
-
fields: {
|
|
13982
|
-
name: f("string", "Theme name.", { required: true }),
|
|
13983
|
-
colors: f("object", "HSL token triplets (background, primary, \u2026)."),
|
|
13984
|
-
font: f("object", "Google Fonts pairing (heading, body, display, serif)."),
|
|
13985
|
-
radius: f("string", "Corner radius scale.", { enum: STORE_THEME_RADII }),
|
|
13986
|
-
header: f("object", "Header treatment (variant, uppercaseNav, search)."),
|
|
13987
|
-
productCard: f("object", "Card style and image aspect."),
|
|
13988
|
-
uppercaseButtons: f("boolean", "Uppercase CTA buttons with letter-spacing."),
|
|
13989
|
-
spacing: f("object", "Spacing scale (key \u2192 CSS length), emitted as --sf-space-<key>. e.g. {sm:'8px', md:'16px'}."),
|
|
13990
|
-
typeScale: f("object", "Type scale (key \u2192 CSS font-size), emitted as --sf-text-<key>. e.g. {body:'1rem', h1:'clamp(40px,6vw,66px)'}."),
|
|
13991
|
-
breakpoints: f("object", "Layout breakpoints (key \u2192 CSS length), emitted as --sf-breakpoint-<key> for JS/generated-style consumers."),
|
|
13992
|
-
motion: f("object", "Motion tokens (key \u2192 duration or easing), emitted as --sf-motion-<key>. e.g. {fast:'150ms', reveal:'cubic-bezier(0.22,1,0.36,1)'}."),
|
|
13993
|
-
tokens: f("object", "Free-form brand-token passthrough, emitted verbatim as CSS custom properties.")
|
|
13994
|
-
}
|
|
13995
|
-
},
|
|
13996
|
-
commerce: {
|
|
13997
|
-
description: "Commercial behavior: mode, shipping, payments, discounts, enquiry, feature toggles.",
|
|
13998
|
-
// v2 = the legacy `discounts` coupon list has been migrated onto the rule
|
|
13999
|
-
// engine (codemod commerce.discounts-to-promotions). Both versions are
|
|
14000
|
-
// understood, which is the whole point: the fleet migrates store by store.
|
|
14001
|
-
versions: [1, 2],
|
|
14002
|
-
fields: {
|
|
14003
|
-
commerceMode: f("string", "Checkout-first, catalog-only browsing, or product enquiry.", {
|
|
14004
|
-
enum: STORE_COMMERCE_MODES
|
|
14005
|
-
}),
|
|
14006
|
-
shipping: f("object", "Flat rate and free-above threshold."),
|
|
14007
|
-
payments: f("object", "Payment mode and offered methods."),
|
|
14008
|
-
discounts: f("array", "DEPRECATED \u2014 store-level discount codes ({code, percent, title?}). Use promotions.promotions.", {
|
|
14009
|
-
deprecated: {
|
|
14010
|
-
id: "storefront.commerce.discounts",
|
|
14011
|
-
removedAtVersion: 2,
|
|
14012
|
-
replacement: "promotions.promotions"
|
|
14013
|
-
}
|
|
14014
|
-
}),
|
|
14015
|
-
enquiry: f("object", "Product enquiry behavior for catalog/enquiry stores."),
|
|
14016
|
-
features: f("object", "Feature toggles (discounts, accounts, cart, checkout).")
|
|
14017
|
-
}
|
|
14018
|
-
},
|
|
14019
|
-
presentation: {
|
|
14020
|
-
description: "What the shopper sees: composed pages, slots, nav, footer, chrome.",
|
|
14021
|
-
versions: [1],
|
|
14022
|
-
fields: {
|
|
14023
|
-
announcement: f("string", "Promo message above the header."),
|
|
14024
|
-
logo: f("string", "Text logo. A JSX logo is code and lives at code.logo."),
|
|
14025
|
-
home: f("object", "Home page blueprint (legacy short form of pages.home)."),
|
|
14026
|
-
pages: f("object", "Pages composed from blocks, keyed by page kind."),
|
|
14027
|
-
slots: f("object", "Blocks grafted into named positions inside SDK pages."),
|
|
14028
|
-
nav: f("array", "Header nav links ({label, to})."),
|
|
14029
|
-
footer: f("object", "Footer content (about, contact, social, labels)."),
|
|
14030
|
-
imageLoading: f("object", "Global image reveal behavior."),
|
|
14031
|
-
catalogFacets: f(
|
|
14032
|
-
"array",
|
|
14033
|
-
"Catalog refinements over product.metadata ({key, label, values[]}), rendered in the filters panel and mirrored into ?<key>=<value>."
|
|
14034
|
-
),
|
|
14035
|
-
productSpecs: f(
|
|
14036
|
-
"object",
|
|
14037
|
-
"PDP specification table rules: `exclude` hides metadata keys, `labels` renames the rest."
|
|
14038
|
-
),
|
|
14039
|
-
search: f(
|
|
14040
|
-
"object",
|
|
14041
|
-
"Header search: mode ('inline' live filter, the default, or 'overlay'), plus placeholder, resultsPath, suggestionsLimit, minChars and labels. A custom suggestion renderer is code and lives at code.searchRenderSuggestion."
|
|
14042
|
-
)
|
|
14043
|
-
}
|
|
14044
|
-
},
|
|
14045
|
-
content: {
|
|
14046
|
-
description: "Long-form content: legal/institutional pages, product reviews.",
|
|
14047
|
-
versions: [1],
|
|
14048
|
-
fields: {
|
|
14049
|
-
legal: f("object", "Privacy / terms / returns page text."),
|
|
14050
|
-
reviews: f(
|
|
14051
|
-
"object",
|
|
14052
|
-
"Store-supplied product reviews ({items, enabled, emptyText}). Nothing is invented \u2014 with no items the PDP renders an honest empty state."
|
|
14053
|
-
)
|
|
14054
|
-
}
|
|
14055
|
-
},
|
|
14056
|
-
catalog: {
|
|
14057
|
-
description: "Mock catalog seed for stores without a live backend.",
|
|
14058
|
-
versions: [1],
|
|
14059
|
-
fields: {
|
|
14060
|
-
products: f("array", "Seed products."),
|
|
14061
|
-
categories: f("array", "Seed categories."),
|
|
14062
|
-
discounts: f("array", "Seed discounts."),
|
|
14063
|
-
promotions: f("array", "Seed rule-engine promotions for the mock provider. Prefer the top-level promotions section.")
|
|
14064
|
-
}
|
|
14065
|
-
},
|
|
14066
|
-
entities: {
|
|
14067
|
-
description: "Custom entity declarations (defineEntity inputs), keyed by entity key.",
|
|
14068
|
-
versions: [1],
|
|
14069
|
-
fields: {
|
|
14070
|
-
definitions: f("object", "Entity key \u2192 declaration ({fields, scope, access, indexes, \u2026}).")
|
|
14071
|
-
}
|
|
14072
|
-
},
|
|
14073
|
-
promotions: {
|
|
14074
|
-
description: "Rule-engine promotions: eligibility rules + application method, as pure data (spine M6).",
|
|
14075
|
-
versions: [1],
|
|
14076
|
-
fields: {
|
|
14077
|
-
promotions: f("array", "Promotions ({id, code?, isAutomatic?, rules?, applicationMethod}). Rule attributes come from the rule_attribute vocabulary.")
|
|
14078
|
-
}
|
|
14079
|
-
},
|
|
14080
|
-
backend: {
|
|
14081
|
-
description: "Backend descriptor: which provider serves the store.",
|
|
14082
|
-
// v2 = the legacy supabaseUrl/supabaseAnonKey pair has moved to
|
|
14083
|
-
// auth.supabase (codemod backend.supabase-to-auth).
|
|
14084
|
-
versions: [1, 2],
|
|
14085
|
-
fields: {
|
|
14086
|
-
provider: f("string", "Backend provider family.", { enum: STORE_BACKEND_PROVIDERS }),
|
|
14087
|
-
url: f("string", "Backend URL, when the provider needs one."),
|
|
14088
|
-
supabaseUrl: f("string", "DEPRECATED legacy field, kept for lossless round-trip. Use auth.supabase.url.", {
|
|
14089
|
-
deprecated: {
|
|
14090
|
-
id: "storefront.backend.supabaseUrl",
|
|
14091
|
-
removedAtVersion: 2,
|
|
14092
|
-
replacement: "auth.supabase.url"
|
|
14093
|
-
}
|
|
14094
|
-
}),
|
|
14095
|
-
supabaseAnonKey: f("string", "DEPRECATED legacy field, kept for lossless round-trip. Use auth.supabase.anonKey.", {
|
|
14096
|
-
deprecated: {
|
|
14097
|
-
id: "storefront.backend.supabaseAnonKey",
|
|
14098
|
-
removedAtVersion: 2,
|
|
14099
|
-
replacement: "auth.supabase.anonKey"
|
|
14100
|
-
}
|
|
14101
|
-
})
|
|
14102
|
-
}
|
|
14103
|
-
},
|
|
14104
|
-
auth: {
|
|
14105
|
-
description: "Customer auth, when expressible as data. Code-bearing auth lives at code.auth.",
|
|
14106
|
-
versions: [1],
|
|
14107
|
-
fields: {
|
|
14108
|
-
adapter: f("string", "Adapter id ('mock' | 'supabase')."),
|
|
14109
|
-
provider: f("string", "Provider id ('mock' | 'supabase')."),
|
|
14110
|
-
routes: f("object", "Auth route overrides."),
|
|
14111
|
-
supabase: f("object", "Supabase connection (url, anonKey, callback urls).")
|
|
14112
|
-
}
|
|
14113
|
-
},
|
|
14114
|
-
code: {
|
|
14115
|
-
description: "The code sidecar: config fields that hold code, referenced by registered handle name.",
|
|
14116
|
-
versions: [1],
|
|
14117
|
-
fields: {
|
|
14118
|
-
logo: f("string", "Handle of the JSX logo node."),
|
|
14119
|
-
provider: f("string", "Handle of the explicit ShopProvider."),
|
|
14120
|
-
auth: f("string", "Handle of a code-bearing auth config."),
|
|
14121
|
-
footerCredit: f("string", "Handle of the footer credit node."),
|
|
14122
|
-
searchRenderSuggestion: f("string", "Handle of the custom search-suggestion renderer."),
|
|
14123
|
-
components: f("object", "Component-slot name \u2192 handle of its override."),
|
|
14124
|
-
routes: f("array", "Custom routes; each component is a handle name.")
|
|
14125
|
-
}
|
|
14126
|
-
}
|
|
14127
|
-
};
|
|
14128
|
-
var STORE_SECTION_NAMES = Object.keys(STORE_SECTIONS);
|
|
14129
|
-
function sectionToJsonSchema(name) {
|
|
14130
|
-
const spec = STORE_SECTIONS[name];
|
|
14131
|
-
const properties = {
|
|
14132
|
-
version: {
|
|
14133
|
-
type: "integer",
|
|
14134
|
-
description: `Schema version of the ${name} section. Supported: ${spec.versions.join(", ")}.`,
|
|
14135
|
-
enum: [...spec.versions]
|
|
14136
|
-
}
|
|
14137
|
-
};
|
|
14138
|
-
const required = ["version"];
|
|
14139
|
-
for (const [field2, fieldSpec] of Object.entries(spec.fields)) {
|
|
14140
|
-
const node = { type: fieldSpec.type, description: fieldSpec.description };
|
|
14141
|
-
if (fieldSpec.enum) node.enum = [...fieldSpec.enum];
|
|
14142
|
-
if (fieldSpec.deprecated) {
|
|
14143
|
-
node.deprecated = true;
|
|
14144
|
-
node["x-fayz-deprecation"] = fieldSpec.deprecated.id;
|
|
14145
|
-
node["x-fayz-replacement"] = fieldSpec.deprecated.replacement;
|
|
14146
|
-
node["x-fayz-removed-at-section-version"] = fieldSpec.deprecated.removedAtVersion;
|
|
14147
|
-
}
|
|
14148
|
-
properties[field2] = node;
|
|
14149
|
-
if (fieldSpec.required) required.push(field2);
|
|
14150
|
-
}
|
|
14151
|
-
return {
|
|
14152
|
-
type: "object",
|
|
14153
|
-
description: spec.description,
|
|
14154
|
-
properties,
|
|
14155
|
-
required
|
|
14156
|
-
};
|
|
14157
|
-
}
|
|
14158
|
-
function isJsonValue(value) {
|
|
14159
|
-
if (value === null) return true;
|
|
14160
|
-
const t = typeof value;
|
|
14161
|
-
if (t === "string" || t === "number" || t === "boolean") return true;
|
|
14162
|
-
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
14163
|
-
if (t === "object") {
|
|
14164
|
-
const record = value;
|
|
14165
|
-
if ("$$typeof" in record) return false;
|
|
14166
|
-
const proto = Object.getPrototypeOf(record);
|
|
14167
|
-
if (proto !== Object.prototype && proto !== null) return false;
|
|
14168
|
-
return Object.values(record).every((v) => v === void 0 || isJsonValue(v));
|
|
14169
|
-
}
|
|
14170
|
-
return false;
|
|
14171
|
-
}
|
|
14172
|
-
function isRecord(value) {
|
|
14173
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14174
|
-
}
|
|
14175
|
-
function compact(obj) {
|
|
14176
|
-
const out = {};
|
|
14177
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
14178
|
-
if (value !== void 0) out[key] = value;
|
|
14179
|
-
}
|
|
14180
|
-
return out;
|
|
14181
|
-
}
|
|
14182
|
-
|
|
14183
|
-
// src/extend/component-contracts.ts
|
|
14184
|
-
var STOREFRONT_COMPONENT_KEYS = [
|
|
14185
|
-
"ProductCard",
|
|
14186
|
-
"ProductDetail",
|
|
14187
|
-
"ProductGallery",
|
|
14188
|
-
"ProductPrice",
|
|
14189
|
-
"ProductActions",
|
|
14190
|
-
"ProductEnquiryForm",
|
|
14191
|
-
"CategoryCard",
|
|
14192
|
-
"CollectionSection",
|
|
14193
|
-
"HeroSection",
|
|
14194
|
-
"MediaCarousel",
|
|
14195
|
-
"ProductSlider",
|
|
14196
|
-
"Header",
|
|
14197
|
-
"Footer",
|
|
14198
|
-
"Shell",
|
|
14199
|
-
"EmptyState",
|
|
14200
|
-
"LoadingState",
|
|
14201
|
-
"ErrorState",
|
|
14202
|
-
"CartDrawerFooterExtra"
|
|
14203
|
-
];
|
|
14204
|
-
var STOREFRONT_PAGE_KINDS = ["home", "catalog", "product", "collection", "content"];
|
|
14205
|
-
var STRUCTURAL_BLOCK_KEYS = /* @__PURE__ */ new Set(["type", "id", "props", "children", "data"]);
|
|
14206
|
-
var DATA_NEED_KEYS = ["loader", "params"];
|
|
14207
|
-
var CODE_ROUTE_KEYS = ["id", "path", "kind", "chrome", "component"];
|
|
14208
|
-
var SLOT_AREAS = [...new Set(STOREFRONT_SLOTS.map((slot) => slot.area))];
|
|
14209
|
-
function slotNamesFor(area) {
|
|
14210
|
-
return STOREFRONT_SLOTS.filter((slot) => slot.area === area).map((slot) => slot.name);
|
|
14211
|
-
}
|
|
14212
|
-
var PAGE_KIND_PARAMS = {
|
|
14213
|
-
home: [],
|
|
14214
|
-
catalog: [],
|
|
14215
|
-
product: ["slug"],
|
|
14216
|
-
collection: ["slug"]
|
|
14217
|
-
};
|
|
14218
|
-
var SLOT_AREA_PARAMS = {
|
|
14219
|
-
productDetail: ["slug"]
|
|
14220
|
-
};
|
|
14221
|
-
var knownEntities = /* @__PURE__ */ new Set();
|
|
14222
|
-
function checkEntityRef(errors, path, value) {
|
|
14223
|
-
if (typeof value !== "string" || !value) return;
|
|
14224
|
-
if (!knownEntities.has(value)) {
|
|
14225
|
-
push(
|
|
14226
|
-
errors,
|
|
14227
|
-
path,
|
|
14228
|
-
"unknown-entity",
|
|
14229
|
-
`entity "${value}" is not declared (defineEntity or the entities section)`,
|
|
14230
|
-
[...knownEntities].sort()
|
|
14231
|
-
);
|
|
14232
|
-
}
|
|
14233
|
-
}
|
|
14234
|
-
function push(errors, path, code, message, valid) {
|
|
14235
|
-
errors.push(valid ? { path, code, message, valid: [...valid] } : { path, code, message });
|
|
14236
|
-
}
|
|
14237
|
-
function blockPropKeys(entry) {
|
|
14238
|
-
if ("props" in entry) {
|
|
14239
|
-
const props = entry.props;
|
|
14240
|
-
return isRecord(props) ? Object.keys(props) : [];
|
|
14241
|
-
}
|
|
14242
|
-
return Object.keys(entry).filter((key) => !STRUCTURAL_BLOCK_KEYS.has(key));
|
|
14243
|
-
}
|
|
14244
|
-
function validateSettingValues(errors, valuesPath, settings, values, owner, labelPrefix = "") {
|
|
14245
|
-
for (const setting of settings) {
|
|
14246
|
-
const value = values[setting.id];
|
|
14247
|
-
if (value === void 0) continue;
|
|
14248
|
-
const valuePath = `${valuesPath}.${setting.id}`;
|
|
14249
|
-
const label = `${labelPrefix}${setting.id}`;
|
|
14250
|
-
if (setting.type === "entity") {
|
|
14251
|
-
checkEntityRef(errors, valuePath, value);
|
|
14252
|
-
continue;
|
|
14253
|
-
}
|
|
14254
|
-
if (setting.type !== "list" || !setting.of?.length || !Array.isArray(value)) continue;
|
|
14255
|
-
const of = setting.of;
|
|
14256
|
-
const declared = collectSettingIds(of);
|
|
14257
|
-
value.forEach((item, index) => {
|
|
14258
|
-
if (!isRecord(item)) return;
|
|
14259
|
-
const itemPath = `${valuePath}[${index}]`;
|
|
14260
|
-
for (const key of Object.keys(item)) {
|
|
14261
|
-
if (!declared.has(key)) {
|
|
14262
|
-
push(
|
|
14263
|
-
errors,
|
|
14264
|
-
`${itemPath}.${key}`,
|
|
14265
|
-
owner.code,
|
|
14266
|
-
`${owner.subject} declares no ${owner.noun} "${label}[].${key}"`,
|
|
14267
|
-
[...declared].sort()
|
|
14268
|
-
);
|
|
14269
|
-
}
|
|
14270
|
-
}
|
|
14271
|
-
validateSettingValues(errors, itemPath, of, item, owner, `${label}[].`);
|
|
14272
|
-
});
|
|
14273
|
-
}
|
|
14274
|
-
}
|
|
14275
|
-
function checkLiteralParam(errors, path, containerPath, loaderId, setting, value) {
|
|
14276
|
-
const expected = setting.type === "number" ? "number" : setting.type === "boolean" ? "boolean" : setting.type === "list" ? "array" : "string";
|
|
14277
|
-
const actual = Array.isArray(value) ? "array" : typeof value;
|
|
14278
|
-
if (actual !== expected) {
|
|
14279
|
-
push(
|
|
14280
|
-
errors,
|
|
14281
|
-
path,
|
|
14282
|
-
"invalid-value",
|
|
14283
|
-
`loader "${loaderId}" param "${setting.id}" expects ${expected}, got ${actual}`
|
|
14284
|
-
);
|
|
14285
|
-
return;
|
|
14286
|
-
}
|
|
14287
|
-
if (setting.type === "select" && setting.options?.length && typeof value === "string") {
|
|
14288
|
-
const allowed = setting.options.map((option) => option.value);
|
|
14289
|
-
if (!allowed.includes(value)) {
|
|
14290
|
-
push(errors, path, "invalid-value", `"${value}" is not a valid ${setting.id}`, allowed);
|
|
14291
|
-
}
|
|
14292
|
-
}
|
|
14293
|
-
if (setting.type === "entity") checkEntityRef(errors, path, value);
|
|
14294
|
-
if (setting.type === "list") {
|
|
14295
|
-
validateSettingValues(
|
|
14296
|
-
errors,
|
|
14297
|
-
containerPath,
|
|
14298
|
-
[setting],
|
|
14299
|
-
{ [setting.id]: value },
|
|
14300
|
-
{ subject: `loader "${loaderId}"`, noun: "param", code: "unknown-loader-param" }
|
|
14301
|
-
);
|
|
14302
|
-
}
|
|
14303
|
-
}
|
|
14304
|
-
function validateDataNeeds(errors, entryPath, blockType, value, declaredParams) {
|
|
14305
|
-
if (!isRecord(value)) {
|
|
14306
|
-
push(errors, `${entryPath}.data`, "invalid-value", "`data` maps need names to {loader, params?}");
|
|
14307
|
-
return;
|
|
14308
|
-
}
|
|
14309
|
-
const blockSettings = getBlockMeta(blockType)?.settings;
|
|
14310
|
-
for (const [name, need] of Object.entries(value)) {
|
|
14311
|
-
const needPath = `${entryPath}.data.${name}`;
|
|
14312
|
-
if (!isRecord(need) || typeof need.loader !== "string" || !need.loader) {
|
|
14313
|
-
push(errors, needPath, "invalid-value", "a data need is {loader: string, params?: object}");
|
|
14314
|
-
continue;
|
|
14315
|
-
}
|
|
14316
|
-
if (!loaderRegistry.has(need.loader)) {
|
|
14317
|
-
push(errors, `${needPath}.loader`, "unknown-loader", `loader "${need.loader}" is not registered`, listLoaders());
|
|
14318
|
-
continue;
|
|
14319
|
-
}
|
|
14320
|
-
for (const key of Object.keys(need)) {
|
|
14321
|
-
if (!DATA_NEED_KEYS.includes(key)) {
|
|
14322
|
-
push(errors, `${needPath}.${key}`, "unknown-field", `a data need has no field "${key}"`, DATA_NEED_KEYS);
|
|
14323
|
-
}
|
|
14324
|
-
}
|
|
14325
|
-
if (need.params === void 0) continue;
|
|
14326
|
-
if (!isRecord(need.params)) {
|
|
14327
|
-
push(errors, `${needPath}.params`, "invalid-value", "loader params are an object");
|
|
14328
|
-
continue;
|
|
14329
|
-
}
|
|
14330
|
-
const loaderSettings = loaderRegistry.getEntry(need.loader)?.meta.settings;
|
|
14331
|
-
const declaredLoaderParams = loaderSettings ? collectSettingIds(loaderSettings) : null;
|
|
14332
|
-
for (const [param, paramValue] of Object.entries(need.params)) {
|
|
14333
|
-
const paramPath = `${needPath}.params.${param}`;
|
|
14334
|
-
const setting = loaderSettings?.find((s2) => s2.id === param);
|
|
14335
|
-
if (declaredLoaderParams && !declaredLoaderParams.has(param)) {
|
|
14336
|
-
push(
|
|
14337
|
-
errors,
|
|
14338
|
-
paramPath,
|
|
14339
|
-
"unknown-loader-param",
|
|
14340
|
-
`loader "${need.loader}" declares no param "${param}"`,
|
|
14341
|
-
[...declaredLoaderParams].sort()
|
|
14342
|
-
);
|
|
14343
|
-
continue;
|
|
14344
|
-
}
|
|
14345
|
-
if (isLoaderParamRef(paramValue)) {
|
|
14346
|
-
if (!declaredParams.includes(paramValue.$param)) {
|
|
14347
|
-
push(
|
|
14348
|
-
errors,
|
|
14349
|
-
paramPath,
|
|
14350
|
-
"unbound-route-param",
|
|
14351
|
-
declaredParams.length ? `$param "${paramValue.$param}" is not a route param of this page` : `$param "${paramValue.$param}" cannot bind \u2014 this page declares no route params`,
|
|
14352
|
-
declaredParams
|
|
14353
|
-
);
|
|
14354
|
-
}
|
|
14355
|
-
continue;
|
|
14356
|
-
}
|
|
14357
|
-
if (isLoaderPropRef(paramValue)) {
|
|
14358
|
-
const settingIds = blockSettings ? collectSettingIds(blockSettings) : null;
|
|
14359
|
-
if (settingIds && !settingIds.has(paramValue.$prop)) {
|
|
14360
|
-
push(
|
|
14361
|
-
errors,
|
|
14362
|
-
paramPath,
|
|
14363
|
-
"unknown-prop-binding",
|
|
14364
|
-
`$prop "${paramValue.$prop}" is not a setting of block "${blockType}"`,
|
|
14365
|
-
[...settingIds].sort()
|
|
14366
|
-
);
|
|
14367
|
-
}
|
|
14368
|
-
continue;
|
|
14369
|
-
}
|
|
14370
|
-
if (setting) {
|
|
14371
|
-
checkLiteralParam(errors, paramPath, `${needPath}.params`, need.loader, setting, paramValue);
|
|
14372
|
-
}
|
|
14373
|
-
}
|
|
14374
|
-
}
|
|
14375
|
-
}
|
|
14376
|
-
function validateBlockList(errors, path, value, declaredParams) {
|
|
14377
|
-
if (!Array.isArray(value)) {
|
|
14378
|
-
push(errors, path, "invalid-value", `expected an array of blocks, got ${typeof value}`);
|
|
14379
|
-
return;
|
|
14380
|
-
}
|
|
14381
|
-
value.forEach((entry, index) => {
|
|
14382
|
-
const entryPath = `${path}[${index}]`;
|
|
14383
|
-
if (!isRecord(entry)) {
|
|
14384
|
-
push(errors, entryPath, "invalid-value", "a block entry must be an object with a `type`");
|
|
14385
|
-
return;
|
|
14386
|
-
}
|
|
14387
|
-
const type = entry.type;
|
|
14388
|
-
if (typeof type !== "string" || !type) {
|
|
14389
|
-
push(errors, `${entryPath}.type`, "invalid-value", "a block entry must declare a string `type`");
|
|
14390
|
-
return;
|
|
14391
|
-
}
|
|
14392
|
-
if (!blockRegistry.has(type)) {
|
|
14393
|
-
push(
|
|
14394
|
-
errors,
|
|
14395
|
-
`${entryPath}.type`,
|
|
14396
|
-
"unknown-block",
|
|
14397
|
-
`block "${type}" is not registered`,
|
|
14398
|
-
blockRegistry.ids().sort()
|
|
14399
|
-
);
|
|
14400
|
-
return;
|
|
14401
|
-
}
|
|
14402
|
-
const settings = getBlockMeta(type)?.settings;
|
|
14403
|
-
if (settings) {
|
|
14404
|
-
const declared = collectSettingIds(settings);
|
|
14405
|
-
for (const key of blockPropKeys(entry)) {
|
|
14406
|
-
if (!declared.has(key)) {
|
|
14407
|
-
push(
|
|
14408
|
-
errors,
|
|
14409
|
-
`${entryPath}.props.${key}`,
|
|
14410
|
-
"unknown-setting",
|
|
14411
|
-
`block "${type}" declares no setting "${key}"`,
|
|
14412
|
-
[...declared].sort()
|
|
14413
|
-
);
|
|
14414
|
-
}
|
|
14415
|
-
}
|
|
14416
|
-
const props = isRecord(entry.props) ? entry.props : entry;
|
|
14417
|
-
validateSettingValues(errors, `${entryPath}.props`, settings, props, {
|
|
14418
|
-
subject: `block "${type}"`,
|
|
14419
|
-
noun: "setting",
|
|
14420
|
-
code: "unknown-setting"
|
|
14421
|
-
});
|
|
14422
|
-
}
|
|
14423
|
-
if ("data" in entry && entry.data !== void 0) {
|
|
14424
|
-
validateDataNeeds(errors, entryPath, type, entry.data, declaredParams);
|
|
14425
|
-
}
|
|
14426
|
-
if ("children" in entry && entry.children !== void 0) {
|
|
14427
|
-
validateBlockList(errors, `${entryPath}.children`, entry.children, declaredParams);
|
|
14428
|
-
}
|
|
14429
|
-
});
|
|
14430
|
-
}
|
|
14431
|
-
function validateBlockTree(errors, path, value, declaredParams = []) {
|
|
14432
|
-
validateBlockList(errors, path, value, declaredParams);
|
|
14433
|
-
if (!Array.isArray(value)) return;
|
|
14434
|
-
const nodes = toBlockNodes(value);
|
|
14435
|
-
for (const violation of checkBlockConstraints(nodes)) {
|
|
14436
|
-
push(errors, `${path}${violation.path}`, violation.rule, violation.detail, violation.valid);
|
|
14437
|
-
}
|
|
14438
|
-
}
|
|
14439
|
-
function collectSettingIds(settings) {
|
|
14440
|
-
return new Set(settings.map((setting) => setting.id));
|
|
14441
|
-
}
|
|
14442
|
-
var TOKEN_KEY = /^[a-zA-Z][a-zA-Z0-9-]*$/;
|
|
14443
|
-
function validateTheme(errors, section) {
|
|
14444
|
-
for (const field2 of THEME_SCALE_FIELDS) {
|
|
14445
|
-
const scale = section[field2];
|
|
14446
|
-
if (scale === void 0) continue;
|
|
14447
|
-
if (!isRecord(scale)) continue;
|
|
14448
|
-
for (const [key, value] of Object.entries(scale)) {
|
|
14449
|
-
const path = `theme.${field2}.${key}`;
|
|
14450
|
-
if (!TOKEN_KEY.test(key)) {
|
|
14451
|
-
push(
|
|
14452
|
-
errors,
|
|
14453
|
-
path,
|
|
14454
|
-
"invalid-token-key",
|
|
14455
|
-
`"${key}" cannot become the CSS custom property ${THEME_SCALE_VAR_PREFIX[field2]}-${key} \u2014 token keys match ${TOKEN_KEY}`
|
|
14456
|
-
);
|
|
14457
|
-
}
|
|
14458
|
-
if (typeof value !== "string") {
|
|
14459
|
-
push(errors, path, "invalid-value", `a ${field2} token is a CSS value string, got ${typeof value}`);
|
|
14460
|
-
}
|
|
14461
|
-
}
|
|
14462
|
-
}
|
|
14463
|
-
}
|
|
14464
|
-
function validateSectionEnvelope(errors, name, section) {
|
|
14465
|
-
const spec = STORE_SECTIONS[name];
|
|
14466
|
-
const version = section.version;
|
|
14467
|
-
if (typeof version !== "number" || !spec.versions.includes(version)) {
|
|
14468
|
-
push(
|
|
14469
|
-
errors,
|
|
14470
|
-
`${name}.version`,
|
|
14471
|
-
"unsupported-section-version",
|
|
14472
|
-
`section "${name}" declares version ${JSON.stringify(version)}, which this SDK does not understand`,
|
|
14473
|
-
spec.versions.map(String)
|
|
14474
|
-
);
|
|
14475
|
-
return false;
|
|
14476
|
-
}
|
|
14477
|
-
const fieldNames = Object.keys(spec.fields);
|
|
14478
|
-
for (const key of Object.keys(section)) {
|
|
14479
|
-
if (key === "version") continue;
|
|
14480
|
-
const fieldSpec = spec.fields[key];
|
|
14481
|
-
if (!fieldSpec) {
|
|
14482
|
-
push(errors, `${name}.${key}`, "unknown-field", `section "${name}" has no field "${key}"`, fieldNames);
|
|
14483
|
-
continue;
|
|
14484
|
-
}
|
|
14485
|
-
const deprecated = fieldSpec.deprecated;
|
|
14486
|
-
if (deprecated && version >= deprecated.removedAtVersion) {
|
|
14487
|
-
push(
|
|
14488
|
-
errors,
|
|
14489
|
-
`${name}.${key}`,
|
|
14490
|
-
"removed-field",
|
|
14491
|
-
`field "${name}.${key}" was removed at ${name} version ${deprecated.removedAtVersion} (deprecation ${deprecated.id}); this section declares version ${version}`,
|
|
14492
|
-
[deprecated.replacement]
|
|
14493
|
-
);
|
|
14494
|
-
continue;
|
|
14495
|
-
}
|
|
14496
|
-
const value = section[key];
|
|
14497
|
-
const actual = Array.isArray(value) ? "array" : typeof value;
|
|
14498
|
-
if (actual !== fieldSpec.type) {
|
|
14499
|
-
push(
|
|
14500
|
-
errors,
|
|
14501
|
-
`${name}.${key}`,
|
|
14502
|
-
"invalid-value",
|
|
14503
|
-
`field "${name}.${key}" expects ${fieldSpec.type}, got ${actual}`
|
|
14504
|
-
);
|
|
14505
|
-
continue;
|
|
14506
|
-
}
|
|
14507
|
-
if (fieldSpec.enum && typeof value === "string" && !fieldSpec.enum.includes(value)) {
|
|
14508
|
-
push(
|
|
14509
|
-
errors,
|
|
14510
|
-
`${name}.${key}`,
|
|
14511
|
-
"invalid-value",
|
|
14512
|
-
`"${value}" is not a valid ${name}.${key}`,
|
|
14513
|
-
fieldSpec.enum
|
|
14514
|
-
);
|
|
14515
|
-
}
|
|
14516
|
-
}
|
|
14517
|
-
for (const [field2, fieldSpec] of Object.entries(spec.fields)) {
|
|
14518
|
-
if (fieldSpec.required && section[field2] === void 0) {
|
|
14519
|
-
push(errors, `${name}.${field2}`, "missing-field", `section "${name}" requires "${field2}"`);
|
|
14520
|
-
}
|
|
14521
|
-
}
|
|
14522
|
-
return true;
|
|
14523
|
-
}
|
|
14524
|
-
function validateCommerce(errors, section) {
|
|
14525
|
-
const payments = section.payments;
|
|
14526
|
-
if (isRecord(payments)) {
|
|
14527
|
-
if (payments.mode !== void 0) {
|
|
14528
|
-
if (typeof payments.mode !== "string" || !STORE_PAYMENT_MODES.includes(payments.mode)) {
|
|
14529
|
-
push(
|
|
14530
|
-
errors,
|
|
14531
|
-
"commerce.payments.mode",
|
|
14532
|
-
"invalid-value",
|
|
14533
|
-
`"${String(payments.mode)}" is not a payment mode`,
|
|
14534
|
-
STORE_PAYMENT_MODES
|
|
14535
|
-
);
|
|
14536
|
-
}
|
|
14537
|
-
}
|
|
14538
|
-
if (payments.methods !== void 0) {
|
|
14539
|
-
if (!Array.isArray(payments.methods)) {
|
|
14540
|
-
push(errors, "commerce.payments.methods", "invalid-value", "payments.methods must be an array");
|
|
14541
|
-
} else {
|
|
14542
|
-
payments.methods.forEach((method, index) => {
|
|
14543
|
-
if (typeof method !== "string" || !STORE_PAYMENT_METHOD_KINDS.includes(method)) {
|
|
14544
|
-
push(
|
|
14545
|
-
errors,
|
|
14546
|
-
`commerce.payments.methods[${index}]`,
|
|
14547
|
-
"invalid-value",
|
|
14548
|
-
`"${String(method)}" is not a payment method`,
|
|
14549
|
-
STORE_PAYMENT_METHOD_KINDS
|
|
14550
|
-
);
|
|
14551
|
-
}
|
|
14552
|
-
});
|
|
14553
|
-
}
|
|
14554
|
-
}
|
|
14555
|
-
}
|
|
14556
|
-
const discounts = section.discounts;
|
|
14557
|
-
if (Array.isArray(discounts)) {
|
|
14558
|
-
discounts.forEach((discount, index) => {
|
|
14559
|
-
if (!isRecord(discount) || typeof discount.code !== "string" || typeof discount.percent !== "number") {
|
|
14560
|
-
push(
|
|
14561
|
-
errors,
|
|
14562
|
-
`commerce.discounts[${index}]`,
|
|
14563
|
-
"invalid-value",
|
|
14564
|
-
"a discount is {code: string, percent: number, title?: string}"
|
|
14565
|
-
);
|
|
14566
|
-
}
|
|
14567
|
-
});
|
|
14568
|
-
}
|
|
14569
|
-
}
|
|
14570
|
-
var CONTENT_SLUG = /^[a-z0-9][a-z0-9-]*$/;
|
|
14571
|
-
function validateContentPages(errors, basePath, value) {
|
|
14572
|
-
if (!isRecord(value)) {
|
|
14573
|
-
push(errors, basePath, "invalid-value", "pages.content maps slugs to {title?, path?, blocks: [...]}");
|
|
14574
|
-
return;
|
|
14575
|
-
}
|
|
14576
|
-
const pathOwners = /* @__PURE__ */ new Map();
|
|
14577
|
-
for (const [slug, page] of Object.entries(value)) {
|
|
14578
|
-
const pagePath = `${basePath}.${slug}`;
|
|
14579
|
-
if (!CONTENT_SLUG.test(slug)) {
|
|
14580
|
-
push(
|
|
14581
|
-
errors,
|
|
14582
|
-
pagePath,
|
|
14583
|
-
"invalid-value",
|
|
14584
|
-
`"${slug}" cannot become the route "/${slug}" \u2014 content slugs match ${CONTENT_SLUG}`
|
|
14585
|
-
);
|
|
14586
|
-
}
|
|
14587
|
-
if (!isRecord(page) || page.blocks === void 0) {
|
|
14588
|
-
push(errors, pagePath, "invalid-value", "a content page is {title?, path?, blocks: [...]}");
|
|
14589
|
-
continue;
|
|
14590
|
-
}
|
|
14591
|
-
if (page.title !== void 0 && typeof page.title !== "string") {
|
|
14592
|
-
push(errors, `${pagePath}.title`, "invalid-value", `title expects string, got ${typeof page.title}`);
|
|
14593
|
-
}
|
|
14594
|
-
let routePath = `/${slug}`;
|
|
14595
|
-
if (page.path !== void 0) {
|
|
14596
|
-
if (typeof page.path !== "string" || !page.path.startsWith("/")) {
|
|
14597
|
-
push(errors, `${pagePath}.path`, "invalid-value", `path is an absolute route ('/${slug}'), got ${JSON.stringify(page.path)}`);
|
|
14598
|
-
} else {
|
|
14599
|
-
routePath = page.path;
|
|
14600
|
-
}
|
|
14601
|
-
}
|
|
14602
|
-
const owner = pathOwners.get(routePath);
|
|
14603
|
-
if (owner) {
|
|
14604
|
-
push(
|
|
14605
|
-
errors,
|
|
14606
|
-
`${pagePath}.path`,
|
|
14607
|
-
"duplicate-content-path",
|
|
14608
|
-
`content pages "${owner}" and "${slug}" both resolve to "${routePath}" \u2014 one of them can never render`
|
|
14609
|
-
);
|
|
14610
|
-
} else {
|
|
14611
|
-
pathOwners.set(routePath, slug);
|
|
14612
|
-
}
|
|
14613
|
-
validateBlockTree(errors, `${pagePath}.blocks`, page.blocks, routePathParams(routePath));
|
|
14614
|
-
}
|
|
14615
|
-
}
|
|
14616
|
-
function validatePresentation(errors, section) {
|
|
14617
|
-
const home = section.home;
|
|
14618
|
-
if (isRecord(home) && home.sections !== void 0) {
|
|
14619
|
-
validateBlockTree(errors, "presentation.home.sections", home.sections);
|
|
14620
|
-
}
|
|
14621
|
-
const pages = section.pages;
|
|
14622
|
-
if (isRecord(pages)) {
|
|
14623
|
-
const validKinds = [...STOREFRONT_PAGE_KINDS];
|
|
14624
|
-
for (const [kind, page] of Object.entries(pages)) {
|
|
14625
|
-
const pagePath = `presentation.pages.${kind}`;
|
|
14626
|
-
if (!validKinds.includes(kind)) {
|
|
14627
|
-
push(errors, pagePath, "unknown-page-kind", `"${kind}" is not a composable page kind`, validKinds);
|
|
14628
|
-
continue;
|
|
14629
|
-
}
|
|
14630
|
-
if (kind === "content") {
|
|
14631
|
-
validateContentPages(errors, pagePath, page);
|
|
14632
|
-
continue;
|
|
14633
|
-
}
|
|
14634
|
-
if (!isRecord(page) || page.blocks === void 0) {
|
|
14635
|
-
push(errors, pagePath, "invalid-value", "a composed page is {blocks: [...]}");
|
|
14636
|
-
continue;
|
|
14637
|
-
}
|
|
14638
|
-
validateBlockTree(errors, `${pagePath}.blocks`, page.blocks, PAGE_KIND_PARAMS[kind] ?? []);
|
|
14639
|
-
}
|
|
14640
|
-
}
|
|
14641
|
-
const slots = section.slots;
|
|
14642
|
-
if (isRecord(slots)) {
|
|
14643
|
-
for (const [area, areaSlots] of Object.entries(slots)) {
|
|
14644
|
-
const areaPath = `presentation.slots.${area}`;
|
|
14645
|
-
if (!SLOT_AREAS.includes(area)) {
|
|
14646
|
-
push(errors, areaPath, "unknown-slot-area", `"${area}" is not a slot area`, SLOT_AREAS);
|
|
14647
|
-
continue;
|
|
14648
|
-
}
|
|
14649
|
-
if (!isRecord(areaSlots)) {
|
|
14650
|
-
push(errors, areaPath, "invalid-value", "a slot area maps slot names to block lists");
|
|
14651
|
-
continue;
|
|
14652
|
-
}
|
|
14653
|
-
const validNames = slotNamesFor(area);
|
|
14654
|
-
for (const [slotName, blocks] of Object.entries(areaSlots)) {
|
|
14655
|
-
const slotPath = `${areaPath}.${slotName}`;
|
|
14656
|
-
if (!validNames.includes(slotName)) {
|
|
14657
|
-
push(errors, slotPath, "unknown-slot", `"${area}.${slotName}" is not a slot`, validNames);
|
|
14658
|
-
continue;
|
|
14659
|
-
}
|
|
14660
|
-
validateBlockTree(errors, slotPath, blocks, SLOT_AREA_PARAMS[area] ?? []);
|
|
14661
|
-
}
|
|
14662
|
-
}
|
|
14663
|
-
}
|
|
14664
|
-
}
|
|
14665
|
-
var ENTITY_KEY_RE = /^[a-z][a-z0-9_]*$/;
|
|
14666
|
-
var ENTITY_FIELD_RE = /^[a-z][a-z0-9_]*$/;
|
|
14667
|
-
function validateEntitiesSection(errors, section) {
|
|
14668
|
-
const definitions = section.definitions;
|
|
14669
|
-
if (definitions === void 0 || !isRecord(definitions)) return;
|
|
14670
|
-
for (const [key, declaration] of Object.entries(definitions)) {
|
|
14671
|
-
const path = `entities.definitions.${key}`;
|
|
14672
|
-
if (!ENTITY_KEY_RE.test(key)) {
|
|
14673
|
-
push(errors, path, "invalid-value", `"${key}" is not a valid entity key \u2014 keys are snake_case`);
|
|
14674
|
-
}
|
|
14675
|
-
if (!isRecord(declaration) || !isRecord(declaration.fields) || Object.keys(declaration.fields).length === 0) {
|
|
14676
|
-
push(errors, path, "invalid-value", "an entity declaration is {fields: {<name>: {type, \u2026}}, scope?, access?, indexes?}");
|
|
14677
|
-
continue;
|
|
14678
|
-
}
|
|
14679
|
-
const fields = declaration.fields;
|
|
14680
|
-
const fieldNames = Object.keys(fields);
|
|
14681
|
-
for (const [name, field2] of Object.entries(fields)) {
|
|
14682
|
-
const fieldPath = `${path}.fields.${name}`;
|
|
14683
|
-
if (!ENTITY_FIELD_RE.test(name)) {
|
|
14684
|
-
push(errors, fieldPath, "invalid-value", `"${name}" is not a valid field name \u2014 fields are snake_case`);
|
|
14685
|
-
}
|
|
14686
|
-
if (RESERVED_ENTITY_FIELDS.includes(name)) {
|
|
14687
|
-
push(errors, fieldPath, "invalid-value", `"${name}" is a structural record column`, RESERVED_ENTITY_FIELDS);
|
|
14688
|
-
continue;
|
|
14689
|
-
}
|
|
14690
|
-
if (!isRecord(field2) || typeof field2.type !== "string") {
|
|
14691
|
-
push(errors, fieldPath, "invalid-value", "a field is {type: <entity field type>, label?, required?, \u2026}");
|
|
14692
|
-
continue;
|
|
14693
|
-
}
|
|
14694
|
-
if (!ENTITY_FIELD_TYPES.includes(field2.type)) {
|
|
14695
|
-
push(errors, `${fieldPath}.type`, "unknown-entity-field-type", `"${field2.type}" is not an entity field type`, ENTITY_FIELD_TYPES);
|
|
14696
|
-
continue;
|
|
14697
|
-
}
|
|
14698
|
-
if (field2.type === "ref") {
|
|
14699
|
-
if (typeof field2.entity !== "string" || !field2.entity) {
|
|
14700
|
-
push(errors, fieldPath, "invalid-value", `a ref field names its target: {type: 'ref', entity: '<key>'}`);
|
|
14701
|
-
} else {
|
|
14702
|
-
checkEntityRef(errors, `${fieldPath}.entity`, field2.entity);
|
|
14703
|
-
}
|
|
14704
|
-
}
|
|
14705
|
-
}
|
|
14706
|
-
const scope = declaration.scope;
|
|
14707
|
-
if (scope !== void 0 && scope !== "tenant") {
|
|
14708
|
-
push(errors, `${path}.scope`, "invalid-value", `"${String(scope)}" is not an entity scope`, ["tenant"]);
|
|
14709
|
-
}
|
|
14710
|
-
const access = declaration.access;
|
|
14711
|
-
if (access !== void 0) {
|
|
14712
|
-
if (!isRecord(access)) {
|
|
14713
|
-
push(errors, `${path}.access`, "invalid-value", "access is {read: 'public'|'staff', write: 'staff'}");
|
|
14714
|
-
} else {
|
|
14715
|
-
if (access.read !== void 0 && access.read !== "public" && access.read !== "staff") {
|
|
14716
|
-
push(errors, `${path}.access.read`, "invalid-value", `"${String(access.read)}" is not a read access level`, ["public", "staff"]);
|
|
14717
|
-
}
|
|
14718
|
-
if (access.write !== void 0 && access.write !== "staff") {
|
|
14719
|
-
push(errors, `${path}.access.write`, "invalid-value", `"${String(access.write)}" is not a write access level \u2014 shopper writes go through actions (M7), not RLS`, ["staff"]);
|
|
14720
|
-
}
|
|
14721
|
-
}
|
|
14722
|
-
}
|
|
14723
|
-
const references = (where, name) => {
|
|
14724
|
-
if (typeof name !== "string" || !fieldNames.includes(name)) {
|
|
14725
|
-
push(errors, `${path}.${where}`, "invalid-value", `${where} references undeclared field "${String(name)}"`, fieldNames);
|
|
14726
|
-
}
|
|
14727
|
-
};
|
|
14728
|
-
if (Array.isArray(declaration.indexes)) {
|
|
14729
|
-
declaration.indexes.forEach((tuple, i) => {
|
|
14730
|
-
if (!Array.isArray(tuple)) {
|
|
14731
|
-
push(errors, `${path}.indexes[${i}]`, "invalid-value", "an index is an array of declared field names");
|
|
14732
|
-
return;
|
|
14733
|
-
}
|
|
14734
|
-
for (const name of tuple) references(`indexes[${i}]`, name);
|
|
14735
|
-
});
|
|
14736
|
-
}
|
|
14737
|
-
if (declaration.titleField !== void 0) references("titleField", declaration.titleField);
|
|
14738
|
-
if (Array.isArray(declaration.displayFields)) {
|
|
14739
|
-
for (const name of declaration.displayFields) references("displayFields", name);
|
|
14740
|
-
}
|
|
14741
|
-
}
|
|
14742
|
-
}
|
|
14743
|
-
function validatePromotionRule(errors, path, rule) {
|
|
14744
|
-
if (!isRecord(rule) || typeof rule.attribute !== "string" || typeof rule.operator !== "string") {
|
|
14745
|
-
push(errors, path, "invalid-value", "a rule is {attribute, operator, values: [...]}");
|
|
14746
|
-
return;
|
|
14747
|
-
}
|
|
14748
|
-
if (!hasRuleAttribute(rule.attribute)) {
|
|
14749
|
-
push(
|
|
14750
|
-
errors,
|
|
14751
|
-
`${path}.attribute`,
|
|
14752
|
-
"unknown-rule-attribute",
|
|
14753
|
-
`"${rule.attribute}" is not in the rule attribute vocabulary`,
|
|
14754
|
-
listRuleAttributeKeys()
|
|
14755
|
-
);
|
|
14756
|
-
return;
|
|
14757
|
-
}
|
|
14758
|
-
if (!RULE_OPERATORS.includes(rule.operator)) {
|
|
14759
|
-
push(errors, `${path}.operator`, "unknown-rule-operator", `"${rule.operator}" is not a rule operator`, RULE_OPERATORS);
|
|
14760
|
-
return;
|
|
14761
|
-
}
|
|
14762
|
-
const attribute = resolveRuleAttribute(rule.attribute);
|
|
14763
|
-
if (!attribute.operators.includes(rule.operator)) {
|
|
14764
|
-
push(
|
|
14765
|
-
errors,
|
|
14766
|
-
`${path}.operator`,
|
|
14767
|
-
"unsupported-rule-operator",
|
|
14768
|
-
`attribute "${rule.attribute}" does not support operator "${rule.operator}"`,
|
|
14769
|
-
attribute.operators
|
|
14770
|
-
);
|
|
14771
|
-
}
|
|
14772
|
-
if (!Array.isArray(rule.values) || rule.values.length === 0) {
|
|
14773
|
-
push(errors, `${path}.values`, "invalid-value", "rule values are a non-empty array of scalars");
|
|
14774
|
-
return;
|
|
14775
|
-
}
|
|
14776
|
-
rule.values.forEach((value, index) => {
|
|
14777
|
-
const t = typeof value;
|
|
14778
|
-
if (t !== "string" && t !== "number" && t !== "boolean") {
|
|
14779
|
-
push(errors, `${path}.values[${index}]`, "invalid-value", `a rule value is a JSON scalar, got ${t}`);
|
|
14780
|
-
}
|
|
14781
|
-
});
|
|
14782
|
-
}
|
|
14783
|
-
function validateRuleList(errors, path, value) {
|
|
14784
|
-
if (value === void 0) return;
|
|
14785
|
-
if (!Array.isArray(value)) {
|
|
14786
|
-
push(errors, path, "invalid-value", `expected an array of rules, got ${typeof value}`);
|
|
14787
|
-
return;
|
|
14788
|
-
}
|
|
14789
|
-
value.forEach((rule, index) => validatePromotionRule(errors, `${path}[${index}]`, rule));
|
|
14790
|
-
}
|
|
14791
|
-
function validatePromotionsSection(errors, section) {
|
|
14792
|
-
const promotions = section.promotions;
|
|
14793
|
-
if (promotions === void 0 || !Array.isArray(promotions)) return;
|
|
14794
|
-
promotions.forEach((promotion, index) => {
|
|
14795
|
-
const path = `promotions.promotions[${index}]`;
|
|
14796
|
-
if (!isRecord(promotion) || typeof promotion.id !== "string" || !promotion.id) {
|
|
14797
|
-
push(errors, path, "invalid-value", "a promotion is {id, code?, isAutomatic?, rules?, applicationMethod}");
|
|
14798
|
-
return;
|
|
14799
|
-
}
|
|
14800
|
-
if (promotion.type !== void 0 && !PROMOTION_TYPES.includes(promotion.type)) {
|
|
14801
|
-
push(errors, `${path}.type`, "invalid-value", `"${String(promotion.type)}" is not a promotion type`, PROMOTION_TYPES);
|
|
14802
|
-
}
|
|
14803
|
-
if (promotion.status !== void 0 && !PROMOTION_STATUSES.includes(promotion.status)) {
|
|
14804
|
-
push(errors, `${path}.status`, "invalid-value", `"${String(promotion.status)}" is not a promotion status`, PROMOTION_STATUSES);
|
|
14805
|
-
}
|
|
14806
|
-
if (promotion.code === void 0 && promotion.isAutomatic !== true) {
|
|
14807
|
-
push(
|
|
14808
|
-
errors,
|
|
14809
|
-
`${path}.code`,
|
|
14810
|
-
"invalid-value",
|
|
14811
|
-
"a promotion needs a code or isAutomatic: true \u2014 otherwise nothing can ever trigger it"
|
|
14812
|
-
);
|
|
14813
|
-
}
|
|
14814
|
-
validateRuleList(errors, `${path}.rules`, promotion.rules);
|
|
14815
|
-
const method = promotion.applicationMethod;
|
|
14816
|
-
if (!isRecord(method)) {
|
|
14817
|
-
push(errors, `${path}.applicationMethod`, "invalid-value", "a promotion carries an applicationMethod ({type, value, targetType, \u2026})");
|
|
14818
|
-
return;
|
|
14819
|
-
}
|
|
14820
|
-
const methodPath = `${path}.applicationMethod`;
|
|
14821
|
-
if (typeof method.type !== "string" || !APPLICATION_METHOD_TYPES.includes(method.type)) {
|
|
14822
|
-
push(errors, `${methodPath}.type`, "invalid-value", `"${String(method.type)}" is not an application method type`, APPLICATION_METHOD_TYPES);
|
|
14823
|
-
}
|
|
14824
|
-
if (typeof method.value !== "number" || method.value < 0) {
|
|
14825
|
-
push(errors, `${methodPath}.value`, "invalid-value", "applicationMethod.value is a non-negative number");
|
|
14826
|
-
}
|
|
14827
|
-
if (typeof method.targetType !== "string" || !APPLICATION_METHOD_TARGET_TYPES.includes(method.targetType)) {
|
|
14828
|
-
push(errors, `${methodPath}.targetType`, "invalid-value", `"${String(method.targetType)}" is not a target type`, APPLICATION_METHOD_TARGET_TYPES);
|
|
14829
|
-
}
|
|
14830
|
-
if (method.allocation !== void 0 && !APPLICATION_METHOD_ALLOCATIONS.includes(method.allocation)) {
|
|
14831
|
-
push(errors, `${methodPath}.allocation`, "invalid-value", `"${String(method.allocation)}" is not an allocation`, APPLICATION_METHOD_ALLOCATIONS);
|
|
14832
|
-
}
|
|
14833
|
-
for (const field2 of ["maxQuantity", "applyToQuantity", "buyRulesMinQuantity"]) {
|
|
14834
|
-
const value = method[field2];
|
|
14835
|
-
if (value !== void 0 && value !== null && (typeof value !== "number" || value < 1)) {
|
|
14836
|
-
push(errors, `${methodPath}.${field2}`, "invalid-value", `${field2} is a positive integer when set`);
|
|
14837
|
-
}
|
|
14838
|
-
}
|
|
14839
|
-
validateRuleList(errors, `${methodPath}.targetRules`, method.targetRules);
|
|
14840
|
-
validateRuleList(errors, `${methodPath}.buyRules`, method.buyRules);
|
|
14841
|
-
});
|
|
14842
|
-
}
|
|
14843
|
-
function validateHandleRef(errors, path, name) {
|
|
14844
|
-
if (typeof name !== "string" || !name) {
|
|
14845
|
-
push(errors, path, "invalid-value", "a code reference is a registered handle name (string)");
|
|
14846
|
-
return;
|
|
14847
|
-
}
|
|
14848
|
-
if (!hasHandle(name)) {
|
|
14849
|
-
push(errors, path, "unknown-handle", `handle "${name}" is not registered`, listHandles());
|
|
14850
|
-
}
|
|
14851
|
-
}
|
|
14852
|
-
function validateCode(errors, section) {
|
|
14853
|
-
for (const field2 of ["logo", "provider", "auth", "footerCredit"]) {
|
|
14854
|
-
if (section[field2] !== void 0) validateHandleRef(errors, `code.${field2}`, section[field2]);
|
|
14855
|
-
}
|
|
14856
|
-
const components = section.components;
|
|
14857
|
-
if (isRecord(components)) {
|
|
14858
|
-
for (const [slot, handle] of Object.entries(components)) {
|
|
14859
|
-
const slotPath = `code.components.${slot}`;
|
|
14860
|
-
if (!STOREFRONT_COMPONENT_KEYS.includes(slot)) {
|
|
14861
|
-
push(
|
|
14862
|
-
errors,
|
|
14863
|
-
slotPath,
|
|
14864
|
-
"unknown-component-slot",
|
|
14865
|
-
`"${slot}" is not an overridable storefront component`,
|
|
14866
|
-
STOREFRONT_COMPONENT_KEYS
|
|
14867
|
-
);
|
|
14868
|
-
continue;
|
|
14869
|
-
}
|
|
14870
|
-
validateHandleRef(errors, slotPath, handle);
|
|
14871
|
-
}
|
|
14872
|
-
}
|
|
14873
|
-
const routes = section.routes;
|
|
14874
|
-
if (Array.isArray(routes)) {
|
|
14875
|
-
routes.forEach((route, index) => {
|
|
14876
|
-
const routePath = `code.routes[${index}]`;
|
|
14877
|
-
if (!isRecord(route)) {
|
|
14878
|
-
push(errors, routePath, "invalid-value", "a route is {id, path, kind?, chrome?, component}");
|
|
14879
|
-
return;
|
|
14880
|
-
}
|
|
14881
|
-
for (const required of ["id", "path"]) {
|
|
14882
|
-
if (typeof route[required] !== "string" || !route[required]) {
|
|
14883
|
-
push(errors, `${routePath}.${required}`, "missing-field", `route requires a string "${required}"`);
|
|
14884
|
-
}
|
|
14885
|
-
}
|
|
14886
|
-
for (const key of Object.keys(route)) {
|
|
14887
|
-
if (!CODE_ROUTE_KEYS.includes(key)) {
|
|
14888
|
-
push(errors, `${routePath}.${key}`, "unknown-field", `a route has no field "${key}"`, CODE_ROUTE_KEYS);
|
|
14889
|
-
}
|
|
14890
|
-
}
|
|
14891
|
-
if (route.kind !== void 0 && !STORE_ROUTE_KINDS.includes(route.kind)) {
|
|
14892
|
-
push(
|
|
14893
|
-
errors,
|
|
14894
|
-
`${routePath}.kind`,
|
|
14895
|
-
"invalid-value",
|
|
14896
|
-
`"${String(route.kind)}" is not a route kind`,
|
|
14897
|
-
STORE_ROUTE_KINDS
|
|
14898
|
-
);
|
|
14899
|
-
}
|
|
14900
|
-
if (route.chrome !== void 0 && !STORE_ROUTE_CHROMES.includes(route.chrome)) {
|
|
14901
|
-
push(
|
|
14902
|
-
errors,
|
|
14903
|
-
`${routePath}.chrome`,
|
|
14904
|
-
"invalid-value",
|
|
14905
|
-
`"${String(route.chrome)}" is not a route chrome`,
|
|
14906
|
-
STORE_ROUTE_CHROMES
|
|
14907
|
-
);
|
|
14908
|
-
}
|
|
14909
|
-
validateHandleRef(errors, `${routePath}.component`, route.component);
|
|
14910
|
-
});
|
|
14911
|
-
}
|
|
14912
|
-
}
|
|
14913
|
-
function validateStore(doc) {
|
|
14914
|
-
registerStorefrontBlocks();
|
|
14915
|
-
const errors = [];
|
|
14916
|
-
knownEntities = new Set(listEntityDeclarations());
|
|
14917
|
-
if (isRecord(doc)) {
|
|
14918
|
-
const entitiesSection = doc.entities;
|
|
14919
|
-
if (isRecord(entitiesSection) && isRecord(entitiesSection.definitions)) {
|
|
14920
|
-
for (const key of Object.keys(entitiesSection.definitions)) knownEntities.add(key);
|
|
14921
|
-
}
|
|
14922
|
-
}
|
|
14923
|
-
if (!isRecord(doc)) {
|
|
14924
|
-
push(errors, "", "invalid-document", `a store document is an object, got ${doc === null ? "null" : typeof doc}`);
|
|
14925
|
-
return { ok: false, errors };
|
|
14926
|
-
}
|
|
14927
|
-
if (doc.$kind !== STORE_DOCUMENT_KIND) {
|
|
14928
|
-
push(
|
|
14929
|
-
errors,
|
|
14930
|
-
"$kind",
|
|
14931
|
-
"invalid-document",
|
|
14932
|
-
`expected $kind "${STORE_DOCUMENT_KIND}", got ${JSON.stringify(doc.$kind)}`,
|
|
14933
|
-
[STORE_DOCUMENT_KIND]
|
|
14934
|
-
);
|
|
14935
|
-
}
|
|
14936
|
-
if (doc.store === void 0) {
|
|
14937
|
-
push(errors, "store", "missing-section", 'the "store" section (name/currency/locale) is required', [
|
|
14938
|
-
...STORE_SECTION_NAMES
|
|
14939
|
-
]);
|
|
14940
|
-
}
|
|
14941
|
-
for (const [name, section] of Object.entries(doc)) {
|
|
14942
|
-
if (name === "$kind") continue;
|
|
14943
|
-
if (!STORE_SECTION_NAMES.includes(name)) {
|
|
14944
|
-
push(errors, name, "unknown-section", `"${name}" is not a document section`, STORE_SECTION_NAMES);
|
|
14945
|
-
continue;
|
|
14946
|
-
}
|
|
14947
|
-
if (!isRecord(section)) {
|
|
14948
|
-
push(errors, name, "invalid-value", `section "${name}" must be an object with a version`);
|
|
14949
|
-
continue;
|
|
14950
|
-
}
|
|
14951
|
-
const sectionName = name;
|
|
14952
|
-
if (!validateSectionEnvelope(errors, sectionName, section)) continue;
|
|
14953
|
-
if (sectionName === "theme") validateTheme(errors, section);
|
|
14954
|
-
if (sectionName === "entities") validateEntitiesSection(errors, section);
|
|
14955
|
-
if (sectionName === "promotions") validatePromotionsSection(errors, section);
|
|
14956
|
-
if (sectionName === "commerce") validateCommerce(errors, section);
|
|
14957
|
-
if (sectionName === "presentation") validatePresentation(errors, section);
|
|
14958
|
-
if (sectionName === "code") validateCode(errors, section);
|
|
14959
|
-
}
|
|
14960
|
-
return { ok: errors.length === 0, errors };
|
|
14961
|
-
}
|
|
14962
|
-
function slugifyStoreName(name) {
|
|
14963
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
|
|
14964
|
-
}
|
|
14965
|
-
function codeRef(value, configPath, handleSuffix, ctx) {
|
|
14966
|
-
const existing = handleNameFor(value);
|
|
14967
|
-
if (existing) return existing;
|
|
14968
|
-
const name = `${ctx.appId}.${handleSuffix}`;
|
|
14969
|
-
if (!ctx.autoRegister) {
|
|
14970
|
-
throw new Error(
|
|
14971
|
-
`exportStore: config.${configPath} holds code that is not a registered handle. Call registerHandle('${name}', value) before exporting, or export with { registerMissingHandles: true } (defineStorefront does).`
|
|
14972
|
-
);
|
|
14973
|
-
}
|
|
14974
|
-
registerHandle(
|
|
14975
|
-
name,
|
|
14976
|
-
value,
|
|
14977
|
-
{ description: `Auto-registered from config.${configPath}`, source: "app" },
|
|
14978
|
-
// The app re-declaring its config (module re-evaluation, HMR) re-derives
|
|
14979
|
-
// the same name for a fresh value; replacing is the correct semantics.
|
|
14980
|
-
{ replace: true }
|
|
14981
|
-
);
|
|
14982
|
-
return name;
|
|
14983
|
-
}
|
|
14984
|
-
function findNonJsonPath(value, path) {
|
|
14985
|
-
if (value === null || value === void 0) return null;
|
|
14986
|
-
const t = typeof value;
|
|
14987
|
-
if (t === "string" || t === "number" || t === "boolean") return null;
|
|
14988
|
-
if (Array.isArray(value)) {
|
|
14989
|
-
for (let i = 0; i < value.length; i++) {
|
|
14990
|
-
const found = findNonJsonPath(value[i], `${path}[${i}]`);
|
|
14991
|
-
if (found) return found;
|
|
14992
|
-
}
|
|
14993
|
-
return null;
|
|
14994
|
-
}
|
|
14995
|
-
if (t === "object") {
|
|
14996
|
-
const record = value;
|
|
14997
|
-
if ("$$typeof" in record) return path;
|
|
14998
|
-
const proto = Object.getPrototypeOf(record);
|
|
14999
|
-
if (proto !== Object.prototype && proto !== null) return path;
|
|
15000
|
-
for (const [key, child] of Object.entries(record)) {
|
|
15001
|
-
const found = findNonJsonPath(child, `${path}.${key}`);
|
|
15002
|
-
if (found) return found;
|
|
15003
|
-
}
|
|
15004
|
-
return null;
|
|
15005
|
-
}
|
|
15006
|
-
return path;
|
|
15007
|
-
}
|
|
15008
|
-
function hasContent(section) {
|
|
15009
|
-
return Object.keys(section).some((key) => key !== "version");
|
|
15010
|
-
}
|
|
15011
|
-
function exportStore(config, options = {}) {
|
|
15012
|
-
const ctx = {
|
|
15013
|
-
appId: options.appId ?? slugifyStoreName(config.name),
|
|
15014
|
-
autoRegister: options.registerMissingHandles ?? false
|
|
15015
|
-
};
|
|
15016
|
-
const code = { version: 1 };
|
|
15017
|
-
let presentationLogo;
|
|
15018
|
-
if (config.logo !== void 0) {
|
|
15019
|
-
if (typeof config.logo === "string") presentationLogo = config.logo;
|
|
15020
|
-
else code.logo = codeRef(config.logo, "logo", "logo", ctx);
|
|
15021
|
-
}
|
|
15022
|
-
let footerData;
|
|
15023
|
-
if (config.footer !== void 0) {
|
|
15024
|
-
const { credit, ...rest } = config.footer;
|
|
15025
|
-
if (credit === void 0 || typeof credit === "string") {
|
|
15026
|
-
footerData = config.footer;
|
|
15027
|
-
} else {
|
|
15028
|
-
footerData = rest;
|
|
15029
|
-
code.footerCredit = codeRef(credit, "footer.credit", "footer-credit", ctx);
|
|
15030
|
-
}
|
|
15031
|
-
}
|
|
15032
|
-
let searchData;
|
|
15033
|
-
if (config.search !== void 0) {
|
|
15034
|
-
const { renderSuggestion, ...rest } = config.search;
|
|
15035
|
-
searchData = rest;
|
|
15036
|
-
if (renderSuggestion !== void 0) {
|
|
15037
|
-
code.searchRenderSuggestion = codeRef(
|
|
15038
|
-
renderSuggestion,
|
|
15039
|
-
"search.renderSuggestion",
|
|
15040
|
-
"search-render-suggestion",
|
|
15041
|
-
ctx
|
|
15042
|
-
);
|
|
15043
|
-
}
|
|
15044
|
-
}
|
|
15045
|
-
let authData;
|
|
15046
|
-
if (config.auth !== void 0) {
|
|
15047
|
-
if (isJsonValue(config.auth)) authData = config.auth;
|
|
15048
|
-
else code.auth = codeRef(config.auth, "auth", "auth", ctx);
|
|
15049
|
-
}
|
|
15050
|
-
if (config.provider !== void 0) {
|
|
15051
|
-
code.provider = codeRef(config.provider, "provider", "provider", ctx);
|
|
15052
|
-
}
|
|
15053
|
-
if (config.components !== void 0) {
|
|
15054
|
-
const componentRefs = {};
|
|
15055
|
-
for (const [key, override] of Object.entries(config.components)) {
|
|
15056
|
-
if (override === void 0) continue;
|
|
15057
|
-
componentRefs[key] = codeRef(override, `components.${key}`, `components.${key}`, ctx);
|
|
15058
|
-
}
|
|
15059
|
-
if (Object.keys(componentRefs).length > 0) code.components = componentRefs;
|
|
15060
|
-
}
|
|
15061
|
-
if (config.routes !== void 0 && config.routes.length > 0) {
|
|
15062
|
-
code.routes = config.routes.map(
|
|
15063
|
-
(route) => compact({
|
|
15064
|
-
id: route.id,
|
|
15065
|
-
path: route.path,
|
|
15066
|
-
kind: route.kind,
|
|
15067
|
-
chrome: route.chrome,
|
|
15068
|
-
component: codeRef(route.component, `routes[${route.id}].component`, `routes.${route.id}`, ctx)
|
|
15069
|
-
})
|
|
15070
|
-
);
|
|
15071
|
-
}
|
|
15072
|
-
const sections = {
|
|
15073
|
-
store: compact({
|
|
15074
|
-
version: 1,
|
|
15075
|
-
name: config.name,
|
|
15076
|
-
currency: config.currency,
|
|
15077
|
-
locale: config.locale
|
|
15078
|
-
}),
|
|
15079
|
-
theme: config.theme ? { version: 1, ...compact(config.theme) } : { version: 1 },
|
|
15080
|
-
commerce: compact({
|
|
15081
|
-
version: 1,
|
|
15082
|
-
commerceMode: config.commerceMode,
|
|
15083
|
-
shipping: config.shipping,
|
|
15084
|
-
payments: config.payments,
|
|
15085
|
-
discounts: config.discounts,
|
|
15086
|
-
enquiry: config.enquiry,
|
|
15087
|
-
features: config.features
|
|
15088
|
-
}),
|
|
15089
|
-
presentation: compact({
|
|
15090
|
-
version: 1,
|
|
15091
|
-
announcement: config.announcement,
|
|
15092
|
-
logo: presentationLogo,
|
|
15093
|
-
home: config.home,
|
|
15094
|
-
pages: config.pages,
|
|
15095
|
-
slots: config.slots,
|
|
15096
|
-
nav: config.nav,
|
|
15097
|
-
footer: footerData,
|
|
15098
|
-
imageLoading: config.imageLoading,
|
|
15099
|
-
catalogFacets: config.catalogFacets,
|
|
15100
|
-
productSpecs: config.productSpecs,
|
|
15101
|
-
search: searchData
|
|
15102
|
-
}),
|
|
15103
|
-
content: compact({ version: 1, legal: config.legal, reviews: config.reviews }),
|
|
15104
|
-
catalog: config.catalog ? { version: 1, ...compact(config.catalog) } : { version: 1 },
|
|
15105
|
-
entities: config.entities ? { version: 1, definitions: config.entities } : { version: 1 },
|
|
15106
|
-
promotions: config.promotions ? { version: 1, promotions: config.promotions } : { version: 1 },
|
|
15107
|
-
backend: compact({
|
|
15108
|
-
version: 1,
|
|
15109
|
-
provider: config.backend?.provider,
|
|
15110
|
-
url: config.backend?.url,
|
|
15111
|
-
supabaseUrl: config.supabaseUrl,
|
|
15112
|
-
supabaseAnonKey: config.supabaseAnonKey
|
|
15113
|
-
}),
|
|
15114
|
-
auth: authData ? { version: 1, ...compact(authData) } : { version: 1 },
|
|
15115
|
-
code: compact(code)
|
|
15116
|
-
};
|
|
15117
|
-
const include = {
|
|
15118
|
-
theme: config.theme !== void 0,
|
|
15119
|
-
catalog: config.catalog !== void 0,
|
|
15120
|
-
entities: config.entities !== void 0,
|
|
15121
|
-
promotions: config.promotions !== void 0,
|
|
15122
|
-
auth: authData !== void 0,
|
|
15123
|
-
commerce: hasContent(sections.commerce),
|
|
15124
|
-
presentation: hasContent(sections.presentation),
|
|
15125
|
-
content: hasContent(sections.content),
|
|
15126
|
-
backend: hasContent(sections.backend),
|
|
15127
|
-
code: hasContent(sections.code)
|
|
15128
|
-
};
|
|
15129
|
-
const doc = { $kind: STORE_DOCUMENT_KIND, store: sections.store };
|
|
15130
|
-
for (const name of ["theme", "commerce", "presentation", "content", "catalog", "entities", "promotions", "backend", "auth", "code"]) {
|
|
15131
|
-
if (include[name]) doc[name] = sections[name];
|
|
15132
|
-
}
|
|
15133
|
-
for (const [name, section] of Object.entries(doc)) {
|
|
15134
|
-
if (name === "$kind") continue;
|
|
15135
|
-
const offending = findNonJsonPath(section, name);
|
|
15136
|
-
if (offending) {
|
|
15137
|
-
throw new Error(
|
|
15138
|
-
`exportStore: ${offending} holds a non-serializable value. Code belongs in a registered handle (registerHandle) referenced from the document's code section \u2014 data sections must be pure JSON.`
|
|
15139
|
-
);
|
|
15140
|
-
}
|
|
15141
|
-
}
|
|
15142
|
-
return doc;
|
|
15143
|
-
}
|
|
15144
|
-
function formatErrors(errors) {
|
|
15145
|
-
return errors.map((e) => ` - ${e.path || "(document)"}: ${e.message}${e.valid ? ` (valid: ${e.valid.join(", ")})` : ""}`).join("\n");
|
|
15146
|
-
}
|
|
15147
|
-
function hydrateStoreDocument(input, options = {}) {
|
|
15148
|
-
const onError = options.onError ?? "throw";
|
|
15149
|
-
const report = validateStore(input);
|
|
15150
|
-
if (!report.ok) {
|
|
15151
|
-
const message = `Store document failed validation:
|
|
15152
|
-
${formatErrors(report.errors)}`;
|
|
15153
|
-
if (onError === "throw") throw new Error(message);
|
|
15154
|
-
console.error(`[storefront] ${message}`);
|
|
15155
|
-
}
|
|
15156
|
-
const doc = input;
|
|
15157
|
-
const resolve = (name, path) => {
|
|
15158
|
-
try {
|
|
15159
|
-
return resolveHandle(name);
|
|
15160
|
-
} catch (err) {
|
|
15161
|
-
if (onError === "throw") throw err;
|
|
15162
|
-
console.error(`[storefront] dropping ${path}: ${errorMessage(err)}`);
|
|
15163
|
-
return void 0;
|
|
15164
|
-
}
|
|
15165
|
-
};
|
|
15166
|
-
const config = { name: doc.store?.name ?? "" };
|
|
15167
|
-
const set = (key, value) => {
|
|
15168
|
-
if (value !== void 0) config[key] = value;
|
|
15169
|
-
};
|
|
15170
|
-
set("currency", doc.store?.currency);
|
|
15171
|
-
set("locale", doc.store?.locale);
|
|
15172
|
-
if (doc.theme) {
|
|
15173
|
-
const { version: _v, ...theme } = doc.theme;
|
|
15174
|
-
set("theme", theme);
|
|
15175
|
-
}
|
|
15176
|
-
if (doc.commerce) {
|
|
15177
|
-
set("commerceMode", doc.commerce.commerceMode);
|
|
15178
|
-
set("shipping", doc.commerce.shipping);
|
|
15179
|
-
set("payments", doc.commerce.payments);
|
|
15180
|
-
set("discounts", doc.commerce.discounts);
|
|
15181
|
-
set("enquiry", doc.commerce.enquiry);
|
|
15182
|
-
set("features", doc.commerce.features);
|
|
15183
|
-
}
|
|
15184
|
-
if (doc.presentation) {
|
|
15185
|
-
set("announcement", doc.presentation.announcement);
|
|
15186
|
-
set("home", doc.presentation.home);
|
|
15187
|
-
set("pages", doc.presentation.pages);
|
|
15188
|
-
set("slots", doc.presentation.slots);
|
|
15189
|
-
set("nav", doc.presentation.nav);
|
|
15190
|
-
set("imageLoading", doc.presentation.imageLoading);
|
|
15191
|
-
set("catalogFacets", doc.presentation.catalogFacets);
|
|
15192
|
-
set("productSpecs", doc.presentation.productSpecs);
|
|
15193
|
-
set("search", doc.presentation.search);
|
|
15194
|
-
if (doc.presentation.logo !== void 0) set("logo", doc.presentation.logo);
|
|
15195
|
-
}
|
|
15196
|
-
if (doc.content) {
|
|
15197
|
-
set("legal", doc.content.legal);
|
|
15198
|
-
set("reviews", doc.content.reviews);
|
|
15199
|
-
}
|
|
15200
|
-
if (doc.catalog) {
|
|
15201
|
-
const { version: _v, ...seed } = doc.catalog;
|
|
15202
|
-
set("catalog", seed);
|
|
15203
|
-
}
|
|
15204
|
-
if (doc.entities?.definitions) set("entities", doc.entities.definitions);
|
|
15205
|
-
if (doc.promotions?.promotions) set("promotions", doc.promotions.promotions);
|
|
15206
|
-
if (doc.backend) {
|
|
15207
|
-
const { provider, url, supabaseUrl, supabaseAnonKey } = doc.backend;
|
|
15208
|
-
if (provider !== void 0 || url !== void 0) set("backend", compact({ provider, url }));
|
|
15209
|
-
set("supabaseUrl", supabaseUrl);
|
|
15210
|
-
set("supabaseAnonKey", supabaseAnonKey);
|
|
15211
|
-
}
|
|
15212
|
-
if (doc.auth) {
|
|
15213
|
-
const { version: _v, ...auth } = doc.auth;
|
|
15214
|
-
set("auth", auth);
|
|
15215
|
-
}
|
|
15216
|
-
const code = doc.code;
|
|
15217
|
-
if (code) {
|
|
15218
|
-
if (code.logo) set("logo", resolve(code.logo, "code.logo"));
|
|
15219
|
-
if (code.provider) set("provider", resolve(code.provider, "code.provider"));
|
|
15220
|
-
if (code.auth) set("auth", resolve(code.auth, "code.auth"));
|
|
15221
|
-
if (code.components) {
|
|
15222
|
-
const components = {};
|
|
15223
|
-
for (const [key, handle] of Object.entries(code.components)) {
|
|
15224
|
-
const value = resolve(handle, `code.components.${key}`);
|
|
15225
|
-
if (value !== void 0) components[key] = value;
|
|
15226
|
-
}
|
|
15227
|
-
if (Object.keys(components).length > 0) set("components", components);
|
|
15228
|
-
}
|
|
15229
|
-
if (code.routes) {
|
|
15230
|
-
const routes = [];
|
|
15231
|
-
for (const route of code.routes) {
|
|
15232
|
-
const component = resolve(
|
|
15233
|
-
route.component,
|
|
15234
|
-
`code.routes[${route.id}].component`
|
|
15235
|
-
);
|
|
15236
|
-
if (component === void 0) continue;
|
|
15237
|
-
routes.push(
|
|
15238
|
-
compact({
|
|
15239
|
-
id: route.id,
|
|
15240
|
-
path: route.path,
|
|
15241
|
-
kind: route.kind,
|
|
15242
|
-
chrome: route.chrome,
|
|
15243
|
-
component
|
|
15244
|
-
})
|
|
15245
|
-
);
|
|
15246
|
-
}
|
|
15247
|
-
if (routes.length > 0) set("routes", routes);
|
|
15248
|
-
}
|
|
15249
|
-
if (code.searchRenderSuggestion) {
|
|
15250
|
-
const renderSuggestion = resolve(
|
|
15251
|
-
code.searchRenderSuggestion,
|
|
15252
|
-
"code.searchRenderSuggestion"
|
|
15253
|
-
);
|
|
15254
|
-
if (renderSuggestion !== void 0) {
|
|
15255
|
-
set("search", { ...doc.presentation?.search ?? {}, renderSuggestion });
|
|
15256
|
-
}
|
|
15257
|
-
}
|
|
15258
|
-
if (code.footerCredit) {
|
|
15259
|
-
const credit = resolve(code.footerCredit, "code.footerCredit");
|
|
15260
|
-
if (credit !== void 0) {
|
|
15261
|
-
set("footer", { ...doc.presentation?.footer ?? {}, credit });
|
|
15262
|
-
}
|
|
15263
|
-
}
|
|
15264
|
-
}
|
|
15265
|
-
if (config.footer === void 0 && doc.presentation?.footer !== void 0) {
|
|
15266
|
-
set("footer", doc.presentation.footer);
|
|
15267
|
-
}
|
|
15268
|
-
return config;
|
|
15269
|
-
}
|
|
15270
|
-
|
|
15271
|
-
export { AddressForm, AddressSheet, AddressStrip, BEFORE_PLACE_ORDER_VALVE, BLOCK_STRUCTURAL_KEYS, BUYER_COUNTRY_COOKIE, BenefitsRow, BlockDataError, CART_LINE_ANNOTATION_POINT, CATALOG_BODY_BLOCK, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, CountrySelect, DeliverToPicker, EmailConfirmationRequiredError, EntityListBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, Link, MESSAGES, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_BODY_BLOCK, PRODUCT_CARD_BADGE_POINT, PageDataScope, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STOREFRONT_SLOTS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, Slot, SmoothImage, StepsSection, StoreCover, StorefrontAnalytics, StorefrontConfigHost, StorefrontConfigProvider, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontRouterProvider, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, blockInstanceId, buildEnquiryFromForm, checkPageBlocks, collectDataNeeds, collectOrderMetadata, contentPagePath, createStorefront, createStorefrontApp, currentStaticRenderPass, defaultPageBlocks, entityListBlockMeta, establishCustomerSession, exportStore, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydrateStoreDocument, hydratedEntriesFor, identifyStorefrontBuyer, initStorefrontRuntime, isJsonValue, isKnownPath, isNestedBlockRef, isPublicPath, isRecord, listSlots, marketForCountry, marketUnitPrice, matchContentPage, matchPath, matchesFacets, menuDataNeeds, menuJsonLd, menuSeo, mergeCollectionFacet, mergePageTheme, navigateTo, nextMilestone, normalizeLanguage, normalizeProductOptionSelection, onStorefrontEvent, orderGalleryImages, overrideComponent, overrideProps, pageSeo, pageThemeSelector, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, primaryImageUrl, productCardComponentContract, productJsonLd, productOptionSelectionKey, productPlaceholder, productSeo, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveConfig, resolveDataParams, resolveOverride, resolvePageData, resolvePageDataNeed, resolvePaymentTerms, resolveStorefrontProvider, resolveStorefrontRoute, resolveVariantForSelection, roundCents, routePathParams, runBeforePlaceOrderValve, sectionToJsonSchema, sectionsToBlocks, selectCount, selectDiscountTotal, selectIsOpen, selectRequiresShipping, selectShipping, selectShowsHours, selectSubtotal, selectTotal, selectionPrice, serializePageData, shellRouterAdapter, signInByEmail, signOutCustomer, signUpCustomer, slugifyStoreName, startStorefrontPostHog, storeBuyerId, storefrontComponentContracts, storefrontPaths, themeToCss, toBlockNodes, toFacetMap, trackStorefront, translate, useBlockData, useBuyerCountry, useBuyerStore, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useHashPath, useInView, useLoader, useMarketProduct, useMarketStore, useMyOrders, useNavigate, usePopOnChange, useProduct, useProducts, useRoutePath, useRouterAdapter, useScrollToTopOnNavigate, useScrolled, useServiceStore, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontConfig, useStorefrontConfigOptional, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, useT, usedSlots, validateStore, withFacet, withMarketPrice, withStaticRenderPass };
|
|
15272
|
-
//# sourceMappingURL=chunk-V7CGLOEN.js.map
|
|
15273
|
-
//# sourceMappingURL=chunk-V7CGLOEN.js.map
|
|
12898
|
+
export { AddressForm, AddressSheet, AddressStrip, BEFORE_PLACE_ORDER_VALVE, BUYER_COUNTRY_COOKIE, BenefitsRow, CART_LINE_ANNOTATION_POINT, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, CountrySelect, DeliverToPicker, EmailConfirmationRequiredError, EntityListBlock, ExperimentBlock, ExperimentProvider, ExperimentVariantBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_CARD_BADGE_POINT, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_SLOTS, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, Slot, SmoothImage, StepsSection, StoreCover, StorefrontAnalytics, StorefrontConfigHost, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, buildEnquiryFromForm, collectOrderMetadata, createStorefront, createStorefrontApp, entityListBlockMeta, establishCustomerSession, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydratedEntriesFor, identifyStorefrontBuyer, initStorefrontRuntime, isExperimentBinding, listSlots, marketForCountry, marketUnitPrice, matchesFacets, mergeCollectionFacet, nextMilestone, normalizeProductOptionSelection, onStorefrontEvent, orderGalleryImages, overrideComponent, overrideProps, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, productCardComponentContract, productOptionSelectionKey, productPlaceholder, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveExperimental, resolveOverride, resolvePaymentTerms, resolveStorefrontProvider, resolveVariantForSelection, roundCents, runBeforePlaceOrderValve, sectionsToBlocks, selectCount, selectDiscountTotal, selectIsOpen, selectRequiresShipping, selectShipping, selectShowsHours, selectSubtotal, selectTotal, selectionPrice, serializePageData, signInByEmail, signOutCustomer, signUpCustomer, startStorefrontPostHog, storeBuyerId, storefrontComponentContracts, themeToCss, toFacetMap, trackStorefront, useBuyerCountry, useBuyerStore, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useExperiment, useExperimentValue, useInView, useMarketProduct, useMarketStore, useMyOrders, usePopOnChange, useProduct, useProducts, useScrolled, useServiceStore, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, useT, usedSlots, withFacet, withMarketPrice };
|
|
12899
|
+
//# sourceMappingURL=chunk-RYWQGQ4M.js.map
|
|
12900
|
+
//# sourceMappingURL=chunk-RYWQGQ4M.js.map
|