@fayz-ai/storefront 0.80.0 → 0.81.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/app/prerender.d.ts +1 -9
- package/dist/app/prerender.d.ts.map +1 -1
- package/dist/app/sitemap.d.ts +10 -0
- package/dist/app/sitemap.d.ts.map +1 -0
- package/dist/chunk-2KCORAVB.js +1183 -0
- package/dist/chunk-2KCORAVB.js.map +1 -0
- package/dist/{chunk-V7CGLOEN.js → chunk-CWHGBPRP.js} +162 -2664
- package/dist/chunk-CWHGBPRP.js.map +1 -0
- package/dist/chunk-OIPNINZQ.js +1336 -0
- package/dist/chunk-OIPNINZQ.js.map +1 -0
- package/dist/chunk-WCAQRATH.js +30 -0
- package/dist/chunk-WCAQRATH.js.map +1 -0
- package/dist/data/menu-needs.d.ts +30 -0
- package/dist/data/menu-needs.d.ts.map +1 -0
- package/dist/edge.d.ts +1 -0
- package/dist/edge.d.ts.map +1 -1
- package/dist/edge.js +80 -1
- package/dist/edge.js.map +1 -1
- package/dist/host/edge-handler.d.ts +38 -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 +131 -0
- package/dist/host/store-host.d.ts.map +1 -0
- package/dist/host.d.ts +7 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +223 -0
- package/dist/host.js.map +1 -0
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/render.js +6 -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 +115 -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
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
import React2, { createContext, useContext, useEffect, useSyncExternalStore, useRef, useMemo, useState } from 'react';
|
|
2
|
+
import { staticRouterAdapter, isLoaderParamRef, isLoaderPropRef, resolveLoader, errorMessage, dataNeedKey, getBlockMeta, defaultPropsFromSettings, getBlockErrorVisibility, isBlockEnabledOn, checkBlockConstraints, checkBlockContext, breadcrumbJsonLd, defaultRouterAdapter, seoOrigin } from '@fayz-ai/core';
|
|
3
|
+
import { getShopProvider } from '@fayz-ai/shop/runtime';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { imageVariantUrl } from '@fayz-ai/shop';
|
|
6
|
+
|
|
7
|
+
// src/locales/messages.ts
|
|
8
|
+
var ptBR = {
|
|
9
|
+
// Checkout shell
|
|
10
|
+
"checkout.title": "Checkout \u2014 {{store}}",
|
|
11
|
+
"checkout.backToStore": "Voltar \xE0 loja",
|
|
12
|
+
"checkout.secure": "Checkout seguro",
|
|
13
|
+
"checkout.help": "Ajuda",
|
|
14
|
+
"checkout.steps.label": "Etapas do checkout",
|
|
15
|
+
"checkout.steps.1": "Dados",
|
|
16
|
+
"checkout.steps.2": "Entrega",
|
|
17
|
+
"checkout.steps.3": "Pagamento",
|
|
18
|
+
"checkout.steps.4": "Pronto",
|
|
19
|
+
// Step 1 — contact
|
|
20
|
+
"checkout.contact.title": "Informa\xE7\xF5es de contato",
|
|
21
|
+
"checkout.contact.signedInAs": "Conectado como {{email}}",
|
|
22
|
+
"checkout.contact.edit": "Alterar",
|
|
23
|
+
"checkout.contact.notYou": "N\xE3o \xE9 voc\xEA?",
|
|
24
|
+
"checkout.contact.signOut": "Sair",
|
|
25
|
+
"checkout.contact.emailLocked": "O e-mail \xE9 o da sua conta.",
|
|
26
|
+
"checkout.contact.haveAccount": "J\xE1 tem conta? Entrar",
|
|
27
|
+
"checkout.contact.email": "E-mail",
|
|
28
|
+
"checkout.contact.name": "Nome completo",
|
|
29
|
+
"checkout.contact.phone": "Telefone",
|
|
30
|
+
"checkout.contact.phoneOptional": "Telefone (opcional)",
|
|
31
|
+
"checkout.contact.document": "CPF ou CNPJ",
|
|
32
|
+
"checkout.contact.documentOptional": "CPF ou CNPJ (opcional)",
|
|
33
|
+
"checkout.contact.requiresAccount": "Esta loja pede uma conta para finalizar a compra.",
|
|
34
|
+
"checkout.contact.signInOrCreate": "Entrar ou criar conta",
|
|
35
|
+
// Step 2 — address
|
|
36
|
+
"checkout.address.title": "Endere\xE7o de entrega",
|
|
37
|
+
"checkout.address.loadingSaved": "Buscando seus endere\xE7os salvos",
|
|
38
|
+
"checkout.address.selected": "Selecionado",
|
|
39
|
+
"checkout.address.addNew": "+ Adicionar novo endere\xE7o",
|
|
40
|
+
"checkout.address.enter": "Informe o endere\xE7o de entrega",
|
|
41
|
+
"checkout.address.enterHint": "Preencher outro endere\xE7o para esta compra",
|
|
42
|
+
"checkout.address.lookingUp": "Buscando o endere\xE7o deste CEP",
|
|
43
|
+
"checkout.address.notFound": "CEP n\xE3o encontrado \u2014 preencha o endere\xE7o \xE0 m\xE3o.",
|
|
44
|
+
"checkout.address.filled": "Preenchemos o endere\xE7o para voc\xEA.",
|
|
45
|
+
"checkout.address.regionFromPostal": "Prov\xEDncia preenchida pelo c\xF3digo postal.",
|
|
46
|
+
"checkout.address.optional": "{{label}} (opcional)",
|
|
47
|
+
"checkout.address.choose": "Selecione",
|
|
48
|
+
"checkout.address.shipTo": "Enviar para",
|
|
49
|
+
// Step 3 — payment
|
|
50
|
+
"checkout.confirming.title": "Confirmando seu pedido",
|
|
51
|
+
"checkout.confirming.label": "Criando seu pedido",
|
|
52
|
+
"checkout.confirming.keepOpen": "N\xE3o feche esta p\xE1gina \u2014 estamos registrando seu pedido na loja.",
|
|
53
|
+
"checkout.payment.title": "Pagamento",
|
|
54
|
+
"checkout.payment.label": "Forma de pagamento",
|
|
55
|
+
"checkout.payment.cardOnline": "Voc\xEA paga na p\xE1gina segura do provedor e volta para a loja",
|
|
56
|
+
"checkout.payment.placeOrder": "Fazer pedido - {{total}}",
|
|
57
|
+
"checkout.payment.pendingNote": "O pedido fica aguardando pagamento at\xE9 a loja confirmar.",
|
|
58
|
+
"checkout.payment.unavailableCountry": "Esta forma de pagamento n\xE3o est\xE1 dispon\xEDvel para {{country}}.",
|
|
59
|
+
"payment.pix.label": "Pix",
|
|
60
|
+
"payment.pix.hint": "Voc\xEA recebe a chave para pagar ap\xF3s confirmar o pedido",
|
|
61
|
+
"payment.credit_card.label": "Cart\xE3o de cr\xE9dito",
|
|
62
|
+
"payment.credit_card.hint": "Maquininha na entrega",
|
|
63
|
+
"payment.debit_card.label": "Cart\xE3o de d\xE9bito",
|
|
64
|
+
"payment.debit_card.hint": "Maquininha na entrega",
|
|
65
|
+
"payment.boleto.label": "Boleto",
|
|
66
|
+
"payment.boleto.hint": "Enviado por e-mail ap\xF3s a confirma\xE7\xE3o",
|
|
67
|
+
"payment.cash.label": "Dinheiro",
|
|
68
|
+
"payment.cash.hint": "Pagamento na entrega",
|
|
69
|
+
"payment.other.label": "Combinar com a loja",
|
|
70
|
+
"payment.other.hint": "A loja entra em contato para acertar o pagamento",
|
|
71
|
+
// Step 4
|
|
72
|
+
"checkout.done.title": "Pagamento confirmado!",
|
|
73
|
+
"checkout.done.text": "Seu pedido foi criado e a loja j\xE1 foi avisada.",
|
|
74
|
+
"checkout.done.viewOrder": "Ver meu pedido",
|
|
75
|
+
// Navigation
|
|
76
|
+
"checkout.continueToDelivery": "Continuar para a entrega",
|
|
77
|
+
"checkout.goToPayment": "Ir para o pagamento - {{total}}",
|
|
78
|
+
"checkout.backToContact": "Voltar aos dados",
|
|
79
|
+
"checkout.backToCart": "Voltar ao carrinho",
|
|
80
|
+
// Summary
|
|
81
|
+
"summary.title": "Resumo do pedido",
|
|
82
|
+
"summary.subtotal": "Subtotal",
|
|
83
|
+
"summary.discount": "Desconto",
|
|
84
|
+
"summary.delivery": "Entrega",
|
|
85
|
+
"summary.pickup": "Retirada na loja",
|
|
86
|
+
"summary.noFee": "Sem taxa",
|
|
87
|
+
"summary.free": "Gr\xE1tis",
|
|
88
|
+
"summary.total": "Total",
|
|
89
|
+
"summary.protected": "Compra protegida e dados criptografados.",
|
|
90
|
+
"summary.shipping": "Frete",
|
|
91
|
+
// Discount
|
|
92
|
+
"discount.placeholder": "Cupom de desconto",
|
|
93
|
+
"discount.apply": "Aplicar",
|
|
94
|
+
"discount.freeShipping": "frete gr\xE1tis",
|
|
95
|
+
"discount.percentOff": "{{percent}}% de desconto",
|
|
96
|
+
"discount.applied": "{{code}} aplicado: {{effect}}.",
|
|
97
|
+
"discount.appliedToast": "Cupom aplicado!",
|
|
98
|
+
"discount.invalid": "Cupom inv\xE1lido.",
|
|
99
|
+
"discount.invalidToast": "Cupom inv\xE1lido",
|
|
100
|
+
"discount.checkCode": "Verifique o c\xF3digo e tente novamente.",
|
|
101
|
+
"discount.cannotValidate": "N\xE3o conseguimos validar esse cupom agora.",
|
|
102
|
+
"discount.cannotValidateToast": "N\xE3o conseguimos validar o cupom",
|
|
103
|
+
"discount.tryLater": "Tente de novo em alguns instantes.",
|
|
104
|
+
// Legal
|
|
105
|
+
"legal.privacy": "Privacidade",
|
|
106
|
+
"legal.terms": "Termos",
|
|
107
|
+
"legal.refunds": "Trocas e devolu\xE7\xF5es",
|
|
108
|
+
// Validation
|
|
109
|
+
"error.requiresAccount": "Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.",
|
|
110
|
+
"error.email": "Informe um e-mail v\xE1lido.",
|
|
111
|
+
"error.name": "Informe seu nome completo.",
|
|
112
|
+
"error.phone": "Informe um telefone de contato.",
|
|
113
|
+
"error.documentInvalid": "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros.",
|
|
114
|
+
"error.documentMissing": "Informe o CPF ou CNPJ do comprador.",
|
|
115
|
+
"error.taxIdInvalid": "NIF/NIE inv\xE1lido \u2014 confira o n\xFAmero.",
|
|
116
|
+
"error.address": "Preencha ou selecione o endere\xE7o de entrega.",
|
|
117
|
+
"error.number": "Falta o n\xFAmero do endere\xE7o.",
|
|
118
|
+
"error.complement": "Informe o complemento (apto, bloco).",
|
|
119
|
+
"error.state": "Informe a UF com duas letras (ex.: RJ).",
|
|
120
|
+
"error.postalInvalid": "Confira o c\xF3digo postal (ex.: {{example}}).",
|
|
121
|
+
"error.region": "Selecione a {{label}}.",
|
|
122
|
+
"error.refusedZone": "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro.",
|
|
123
|
+
"error.unserved": "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
124
|
+
"error.sessionMismatch": "Sua sess\xE3o ficou presa a outro cadastro. Recarregue a p\xE1gina e tente de novo.",
|
|
125
|
+
"error.outOfStock": "Um item do carrinho ficou sem estoque. Revise o carrinho e tente de novo.",
|
|
126
|
+
"error.placeOrder": "N\xE3o foi poss\xEDvel concluir o pedido.",
|
|
127
|
+
// Delivery estimator
|
|
128
|
+
"delivery.title": "Calcular entrega",
|
|
129
|
+
"delivery.calculate": "Calcular",
|
|
130
|
+
"delivery.postalLabel": "CEP",
|
|
131
|
+
"delivery.change": "Trocar CEP",
|
|
132
|
+
"delivery.unserved": "Ainda n\xE3o entregamos nesse CEP.",
|
|
133
|
+
"delivery.refused": "Esta loja n\xE3o entrega nesse endere\xE7o.",
|
|
134
|
+
"delivery.review": "Entregamos nessa regi\xE3o, mas confirmamos com voc\xEA antes de sair.",
|
|
135
|
+
"delivery.etaSoon": "hoje ou amanh\xE3",
|
|
136
|
+
"delivery.etaRange": "{{min}} a {{max}} dias \xFAteis",
|
|
137
|
+
"delivery.etaDays": "{{days}} dia(s) \xFAtil(eis)",
|
|
138
|
+
"delivery.flatRate": "O frete \xE9 calculado no checkout.",
|
|
139
|
+
"delivery.error.digits": "Digite os 8 d\xEDgitos do CEP.",
|
|
140
|
+
"delivery.error.postal": "Confira o c\xF3digo postal (ex.: {{example}}).",
|
|
141
|
+
"delivery.error.quote": "N\xE3o foi poss\xEDvel calcular o frete agora. Tente de novo.",
|
|
142
|
+
"delivery.error.notFound": "CEP n\xE3o encontrado. Confira os n\xFAmeros.",
|
|
143
|
+
// Cart
|
|
144
|
+
"cart.title": "Carrinho",
|
|
145
|
+
"cart.close": "Fechar carrinho",
|
|
146
|
+
"cart.empty": "Seu carrinho est\xE1 vazio.",
|
|
147
|
+
"cart.remove": "Remover {{name}}",
|
|
148
|
+
"cart.checkout": "Finalizar compra",
|
|
149
|
+
"cart.missingForFree": "Faltam {{amount}} para frete gr\xE1tis",
|
|
150
|
+
"cart.wonFree": "Voc\xEA ganhou frete gr\xE1tis!",
|
|
151
|
+
"cart.minimumOrder": "Pedido m\xEDnimo para entrega: {{amount}}. Faltam {{missing}}.",
|
|
152
|
+
"delivery.eta": "Entrega em ~{{duration}}",
|
|
153
|
+
"delivery.etaUpTo": "Entrega em at\xE9 {{duration}}",
|
|
154
|
+
"delivery.paused": "Delivery fechado no momento",
|
|
155
|
+
"cart.deliveryPaused": "O delivery est\xE1 fechado no momento. Volte mais tarde para finalizar.",
|
|
156
|
+
// Deliver to
|
|
157
|
+
"country.deliverTo": "Enviar para",
|
|
158
|
+
"country.change": "Trocar pa\xEDs",
|
|
159
|
+
"country.label": "Pa\xEDs de entrega"
|
|
160
|
+
};
|
|
161
|
+
var en = {
|
|
162
|
+
"checkout.backToStore": "Back to store",
|
|
163
|
+
"checkout.secure": "Secure checkout",
|
|
164
|
+
"checkout.help": "Help",
|
|
165
|
+
"checkout.steps.label": "Checkout steps",
|
|
166
|
+
"checkout.steps.1": "Details",
|
|
167
|
+
"checkout.steps.2": "Delivery",
|
|
168
|
+
"checkout.steps.3": "Payment",
|
|
169
|
+
"checkout.steps.4": "Done",
|
|
170
|
+
"checkout.contact.title": "Contact information",
|
|
171
|
+
"checkout.contact.signedInAs": "Signed in as {{email}}",
|
|
172
|
+
"checkout.contact.edit": "Change",
|
|
173
|
+
"checkout.contact.notYou": "Not you?",
|
|
174
|
+
"checkout.contact.signOut": "Sign out",
|
|
175
|
+
"checkout.contact.emailLocked": "This is your account e-mail.",
|
|
176
|
+
"checkout.contact.haveAccount": "Have an account? Sign in",
|
|
177
|
+
"checkout.contact.email": "Email",
|
|
178
|
+
"checkout.contact.name": "Full name",
|
|
179
|
+
"checkout.contact.phone": "Phone",
|
|
180
|
+
"checkout.contact.phoneOptional": "Phone (optional)",
|
|
181
|
+
"checkout.contact.document": "Tax ID",
|
|
182
|
+
"checkout.contact.documentOptional": "Tax ID (optional)",
|
|
183
|
+
"checkout.contact.requiresAccount": "This store needs an account to complete the purchase.",
|
|
184
|
+
"checkout.contact.signInOrCreate": "Sign in or create an account",
|
|
185
|
+
"checkout.address.title": "Delivery address",
|
|
186
|
+
"checkout.address.loadingSaved": "Loading your saved addresses",
|
|
187
|
+
"checkout.address.selected": "Selected",
|
|
188
|
+
"checkout.address.addNew": "+ Add a new address",
|
|
189
|
+
"checkout.address.enter": "Enter the delivery address",
|
|
190
|
+
"checkout.address.enterHint": "Use a different address for this order",
|
|
191
|
+
"checkout.address.lookingUp": "Looking up this postal code",
|
|
192
|
+
"checkout.address.notFound": "Postal code not found \u2014 fill in the address by hand.",
|
|
193
|
+
"checkout.address.filled": "We filled in the address for you.",
|
|
194
|
+
"checkout.address.regionFromPostal": "Province filled in from the postcode.",
|
|
195
|
+
"checkout.address.optional": "{{label}} (optional)",
|
|
196
|
+
"checkout.address.choose": "Select",
|
|
197
|
+
"checkout.address.shipTo": "Ship to",
|
|
198
|
+
"checkout.confirming.title": "Confirming your order",
|
|
199
|
+
"checkout.confirming.label": "Creating your order",
|
|
200
|
+
"checkout.confirming.keepOpen": "Don\u2019t close this page \u2014 we\u2019re registering your order with the store.",
|
|
201
|
+
"checkout.payment.title": "Payment",
|
|
202
|
+
"checkout.payment.label": "Payment method",
|
|
203
|
+
"checkout.payment.cardOnline": "You pay on the provider\u2019s secure page and come back to the store",
|
|
204
|
+
"checkout.payment.placeOrder": "Place order - {{total}}",
|
|
205
|
+
"checkout.payment.pendingNote": "The order awaits payment until the store confirms it.",
|
|
206
|
+
"checkout.payment.unavailableCountry": "This payment method isn\u2019t available for {{country}}.",
|
|
207
|
+
"payment.pix.hint": "You get the key to pay after confirming the order",
|
|
208
|
+
"payment.credit_card.label": "Credit card",
|
|
209
|
+
"payment.credit_card.hint": "Card machine on delivery",
|
|
210
|
+
"payment.debit_card.label": "Debit card",
|
|
211
|
+
"payment.debit_card.hint": "Card machine on delivery",
|
|
212
|
+
"payment.boleto.hint": "Sent by email after confirmation",
|
|
213
|
+
"payment.cash.label": "Cash",
|
|
214
|
+
"payment.cash.hint": "Pay on delivery",
|
|
215
|
+
"payment.other.label": "Arrange with the store",
|
|
216
|
+
"payment.other.hint": "The store will contact you to arrange payment",
|
|
217
|
+
"checkout.done.title": "Payment confirmed!",
|
|
218
|
+
"checkout.done.text": "Your order was created and the store has been notified.",
|
|
219
|
+
"checkout.done.viewOrder": "View my order",
|
|
220
|
+
"checkout.continueToDelivery": "Continue to delivery",
|
|
221
|
+
"checkout.goToPayment": "Go to payment - {{total}}",
|
|
222
|
+
"checkout.backToContact": "Back to details",
|
|
223
|
+
"checkout.backToCart": "Back to cart",
|
|
224
|
+
"summary.title": "Order summary",
|
|
225
|
+
"summary.discount": "Discount",
|
|
226
|
+
"summary.delivery": "Delivery",
|
|
227
|
+
"summary.pickup": "Store pickup",
|
|
228
|
+
"summary.noFee": "No fee",
|
|
229
|
+
"summary.free": "Free",
|
|
230
|
+
"summary.protected": "Protected purchase, encrypted data.",
|
|
231
|
+
"summary.shipping": "Shipping",
|
|
232
|
+
"discount.placeholder": "Discount code",
|
|
233
|
+
"discount.apply": "Apply",
|
|
234
|
+
"discount.freeShipping": "free shipping",
|
|
235
|
+
"discount.percentOff": "{{percent}}% off",
|
|
236
|
+
"discount.applied": "{{code}} applied: {{effect}}.",
|
|
237
|
+
"discount.appliedToast": "Code applied!",
|
|
238
|
+
"discount.invalid": "Invalid code.",
|
|
239
|
+
"discount.invalidToast": "Invalid code",
|
|
240
|
+
"discount.checkCode": "Check the code and try again.",
|
|
241
|
+
"discount.cannotValidate": "We couldn\u2019t check this code right now.",
|
|
242
|
+
"discount.cannotValidateToast": "We couldn\u2019t check the code",
|
|
243
|
+
"discount.tryLater": "Try again in a moment.",
|
|
244
|
+
"legal.privacy": "Privacy",
|
|
245
|
+
"legal.terms": "Terms",
|
|
246
|
+
"legal.refunds": "Returns",
|
|
247
|
+
"error.requiresAccount": "This store needs an account to complete the purchase. Sign in or create one.",
|
|
248
|
+
"error.email": "Enter a valid email.",
|
|
249
|
+
"error.name": "Enter your full name.",
|
|
250
|
+
"error.phone": "Enter a contact phone number.",
|
|
251
|
+
"error.documentInvalid": "Invalid tax ID \u2014 check the numbers.",
|
|
252
|
+
"error.documentMissing": "Enter the buyer\u2019s tax ID.",
|
|
253
|
+
"error.taxIdInvalid": "Invalid NIF/NIE \u2014 check the number.",
|
|
254
|
+
"error.address": "Fill in or select the delivery address.",
|
|
255
|
+
"error.number": "The street number is missing.",
|
|
256
|
+
"error.complement": "Enter the apartment or unit.",
|
|
257
|
+
"error.state": "Enter the two-letter state code (e.g. RJ).",
|
|
258
|
+
"error.postalInvalid": "Check the postal code (e.g. {{example}}).",
|
|
259
|
+
"error.region": "Select the {{label}}.",
|
|
260
|
+
"error.refusedZone": "This store doesn\u2019t deliver to this address. Try another.",
|
|
261
|
+
"error.unserved": "We don\u2019t deliver to this postal code yet. Try another address.",
|
|
262
|
+
"error.sessionMismatch": "Your session got attached to another account. Reload the page and try again.",
|
|
263
|
+
"error.outOfStock": "An item in your cart is out of stock. Review the cart and try again.",
|
|
264
|
+
"error.placeOrder": "We couldn\u2019t complete the order.",
|
|
265
|
+
"delivery.title": "Estimate delivery",
|
|
266
|
+
"delivery.calculate": "Estimate",
|
|
267
|
+
"delivery.postalLabel": "Postal code",
|
|
268
|
+
"delivery.change": "Change postal code",
|
|
269
|
+
"delivery.unserved": "We don\u2019t deliver to this postal code yet.",
|
|
270
|
+
"delivery.refused": "This store doesn\u2019t deliver to this address.",
|
|
271
|
+
"delivery.review": "We deliver to this area, but we\u2019ll confirm with you before shipping.",
|
|
272
|
+
"delivery.etaSoon": "today or tomorrow",
|
|
273
|
+
"delivery.etaRange": "{{min}} to {{max}} business days",
|
|
274
|
+
"delivery.etaDays": "{{days}} business day(s)",
|
|
275
|
+
"delivery.flatRate": "Shipping is calculated at checkout.",
|
|
276
|
+
"delivery.error.digits": "Enter all 8 digits of the CEP.",
|
|
277
|
+
"delivery.error.postal": "Check the postal code (e.g. {{example}}).",
|
|
278
|
+
"delivery.error.quote": "We couldn\u2019t calculate shipping right now. Try again.",
|
|
279
|
+
"delivery.error.notFound": "Postal code not found. Check the numbers.",
|
|
280
|
+
"cart.title": "Cart",
|
|
281
|
+
"cart.close": "Close cart",
|
|
282
|
+
"cart.empty": "Your cart is empty.",
|
|
283
|
+
"cart.remove": "Remove {{name}}",
|
|
284
|
+
"cart.checkout": "Checkout",
|
|
285
|
+
"cart.missingForFree": "{{amount}} away from free shipping",
|
|
286
|
+
"cart.wonFree": "You got free shipping!",
|
|
287
|
+
"cart.minimumOrder": "Minimum order for delivery: {{amount}}. {{missing}} to go.",
|
|
288
|
+
"delivery.eta": "Delivery in ~{{duration}}",
|
|
289
|
+
"delivery.etaUpTo": "Delivery within {{duration}}",
|
|
290
|
+
"delivery.paused": "Delivery closed right now",
|
|
291
|
+
"cart.deliveryPaused": "Delivery is closed right now. Come back later to check out.",
|
|
292
|
+
"country.deliverTo": "Deliver to",
|
|
293
|
+
"country.change": "Change country",
|
|
294
|
+
"country.label": "Delivery country"
|
|
295
|
+
};
|
|
296
|
+
var es = {
|
|
297
|
+
"checkout.backToStore": "Volver a la tienda",
|
|
298
|
+
"checkout.secure": "Pago seguro",
|
|
299
|
+
"checkout.help": "Ayuda",
|
|
300
|
+
"checkout.steps.label": "Pasos del pago",
|
|
301
|
+
"checkout.steps.1": "Datos",
|
|
302
|
+
"checkout.steps.2": "Env\xEDo",
|
|
303
|
+
"checkout.steps.3": "Pago",
|
|
304
|
+
"checkout.steps.4": "Listo",
|
|
305
|
+
"checkout.contact.title": "Datos de contacto",
|
|
306
|
+
"checkout.contact.signedInAs": "Has iniciado sesi\xF3n como {{email}}",
|
|
307
|
+
"checkout.contact.edit": "Cambiar",
|
|
308
|
+
"checkout.contact.notYou": "\xBFNo eres t\xFA?",
|
|
309
|
+
"checkout.contact.signOut": "Salir",
|
|
310
|
+
"checkout.contact.emailLocked": "Es el correo de tu cuenta.",
|
|
311
|
+
"checkout.contact.haveAccount": "\xBFYa tienes cuenta? Entrar",
|
|
312
|
+
"checkout.contact.email": "Email",
|
|
313
|
+
"checkout.contact.name": "Nombre completo",
|
|
314
|
+
"checkout.contact.phone": "Tel\xE9fono",
|
|
315
|
+
"checkout.contact.phoneOptional": "Tel\xE9fono (opcional)",
|
|
316
|
+
"checkout.contact.document": "NIF/NIE",
|
|
317
|
+
"checkout.contact.documentOptional": "NIF/NIE (opcional)",
|
|
318
|
+
"checkout.contact.requiresAccount": "Esta tienda pide una cuenta para finalizar la compra.",
|
|
319
|
+
"checkout.contact.signInOrCreate": "Entrar o crear cuenta",
|
|
320
|
+
"checkout.address.title": "Direcci\xF3n de env\xEDo",
|
|
321
|
+
"checkout.address.loadingSaved": "Buscando tus direcciones guardadas",
|
|
322
|
+
"checkout.address.selected": "Seleccionada",
|
|
323
|
+
"checkout.address.addNew": "+ A\xF1adir una direcci\xF3n nueva",
|
|
324
|
+
"checkout.address.enter": "Indica la direcci\xF3n de env\xEDo",
|
|
325
|
+
"checkout.address.enterHint": "Usar otra direcci\xF3n para este pedido",
|
|
326
|
+
"checkout.address.lookingUp": "Buscando este c\xF3digo postal",
|
|
327
|
+
"checkout.address.notFound": "C\xF3digo postal no encontrado: rellena la direcci\xF3n a mano.",
|
|
328
|
+
"checkout.address.filled": "Hemos rellenado la direcci\xF3n por ti.",
|
|
329
|
+
"checkout.address.regionFromPostal": "Provincia rellenada a partir del c\xF3digo postal.",
|
|
330
|
+
"checkout.address.optional": "{{label}} (opcional)",
|
|
331
|
+
"checkout.address.choose": "Selecciona",
|
|
332
|
+
"checkout.address.shipTo": "Enviar a",
|
|
333
|
+
"checkout.confirming.title": "Confirmando tu pedido",
|
|
334
|
+
"checkout.confirming.label": "Creando tu pedido",
|
|
335
|
+
"checkout.confirming.keepOpen": "No cierres esta p\xE1gina: estamos registrando tu pedido en la tienda.",
|
|
336
|
+
"checkout.payment.title": "Pago",
|
|
337
|
+
"checkout.payment.label": "Forma de pago",
|
|
338
|
+
"checkout.payment.cardOnline": "Pagas en la p\xE1gina segura del proveedor y vuelves a la tienda",
|
|
339
|
+
"checkout.payment.placeOrder": "Hacer pedido - {{total}}",
|
|
340
|
+
"checkout.payment.pendingNote": "El pedido queda pendiente de pago hasta que la tienda lo confirme.",
|
|
341
|
+
"checkout.payment.unavailableCountry": "Esta forma de pago no est\xE1 disponible para {{country}}.",
|
|
342
|
+
"payment.pix.hint": "Recibes la clave para pagar al confirmar el pedido",
|
|
343
|
+
"payment.credit_card.label": "Tarjeta de cr\xE9dito",
|
|
344
|
+
"payment.credit_card.hint": "Dat\xE1fono en la entrega",
|
|
345
|
+
"payment.debit_card.label": "Tarjeta de d\xE9bito",
|
|
346
|
+
"payment.debit_card.hint": "Dat\xE1fono en la entrega",
|
|
347
|
+
"payment.boleto.hint": "Enviado por email tras la confirmaci\xF3n",
|
|
348
|
+
"payment.cash.label": "Efectivo",
|
|
349
|
+
"payment.cash.hint": "Pago en la entrega",
|
|
350
|
+
"payment.other.label": "Acordar con la tienda",
|
|
351
|
+
"payment.other.hint": "La tienda te contactar\xE1 para acordar el pago",
|
|
352
|
+
"checkout.done.title": "\xA1Pago confirmado!",
|
|
353
|
+
"checkout.done.text": "Tu pedido se ha creado y la tienda ya ha sido avisada.",
|
|
354
|
+
"checkout.done.viewOrder": "Ver mi pedido",
|
|
355
|
+
"checkout.continueToDelivery": "Continuar al env\xEDo",
|
|
356
|
+
"checkout.goToPayment": "Ir al pago - {{total}}",
|
|
357
|
+
"checkout.backToContact": "Volver a los datos",
|
|
358
|
+
"checkout.backToCart": "Volver al carrito",
|
|
359
|
+
"summary.title": "Resumen del pedido",
|
|
360
|
+
"summary.discount": "Descuento",
|
|
361
|
+
"summary.delivery": "Env\xEDo",
|
|
362
|
+
"summary.pickup": "Recogida en tienda",
|
|
363
|
+
"summary.noFee": "Sin coste",
|
|
364
|
+
"summary.free": "Gratis",
|
|
365
|
+
"summary.protected": "Compra protegida y datos cifrados.",
|
|
366
|
+
"summary.shipping": "Env\xEDo",
|
|
367
|
+
"discount.placeholder": "C\xF3digo de descuento",
|
|
368
|
+
"discount.apply": "Aplicar",
|
|
369
|
+
"discount.freeShipping": "env\xEDo gratis",
|
|
370
|
+
"discount.percentOff": "{{percent}}% de descuento",
|
|
371
|
+
"discount.applied": "{{code}} aplicado: {{effect}}.",
|
|
372
|
+
"discount.appliedToast": "\xA1C\xF3digo aplicado!",
|
|
373
|
+
"discount.invalid": "C\xF3digo no v\xE1lido.",
|
|
374
|
+
"discount.invalidToast": "C\xF3digo no v\xE1lido",
|
|
375
|
+
"discount.checkCode": "Revisa el c\xF3digo e int\xE9ntalo de nuevo.",
|
|
376
|
+
"discount.cannotValidate": "No hemos podido validar este c\xF3digo ahora.",
|
|
377
|
+
"discount.cannotValidateToast": "No hemos podido validar el c\xF3digo",
|
|
378
|
+
"discount.tryLater": "Int\xE9ntalo de nuevo en unos instantes.",
|
|
379
|
+
"legal.privacy": "Privacidad",
|
|
380
|
+
"legal.terms": "T\xE9rminos",
|
|
381
|
+
"legal.refunds": "Devoluciones",
|
|
382
|
+
"error.requiresAccount": "Esta tienda pide una cuenta para finalizar la compra. Entra o crea la tuya.",
|
|
383
|
+
"error.email": "Indica un email v\xE1lido.",
|
|
384
|
+
"error.name": "Indica tu nombre completo.",
|
|
385
|
+
"error.phone": "Indica un tel\xE9fono de contacto.",
|
|
386
|
+
"error.documentInvalid": "Documento no v\xE1lido: revisa los n\xFAmeros.",
|
|
387
|
+
"error.documentMissing": "Indica el documento del comprador.",
|
|
388
|
+
"error.taxIdInvalid": "NIF/NIE no v\xE1lido: revisa el n\xFAmero.",
|
|
389
|
+
"error.address": "Rellena o selecciona la direcci\xF3n de env\xEDo.",
|
|
390
|
+
"error.number": "Falta el n\xFAmero de la direcci\xF3n.",
|
|
391
|
+
"error.complement": "Indica el piso o la puerta.",
|
|
392
|
+
"error.state": "Indica el estado con dos letras (ej.: RJ).",
|
|
393
|
+
"error.postalInvalid": "Revisa el c\xF3digo postal (ej.: {{example}}).",
|
|
394
|
+
"error.region": "Selecciona la {{label}}.",
|
|
395
|
+
"error.refusedZone": "Esta tienda no env\xEDa a esta direcci\xF3n. Prueba con otra.",
|
|
396
|
+
"error.unserved": "Todav\xEDa no enviamos a este c\xF3digo postal. Prueba con otra direcci\xF3n.",
|
|
397
|
+
"error.sessionMismatch": "Tu sesi\xF3n qued\xF3 vinculada a otra cuenta. Recarga la p\xE1gina e int\xE9ntalo de nuevo.",
|
|
398
|
+
"error.outOfStock": "Un producto del carrito se ha agotado. Revisa el carrito e int\xE9ntalo de nuevo.",
|
|
399
|
+
"error.placeOrder": "No se ha podido completar el pedido.",
|
|
400
|
+
"delivery.title": "Calcular env\xEDo",
|
|
401
|
+
"delivery.calculate": "Calcular",
|
|
402
|
+
"delivery.postalLabel": "C\xF3digo postal",
|
|
403
|
+
"delivery.change": "Cambiar c\xF3digo postal",
|
|
404
|
+
"delivery.unserved": "Todav\xEDa no enviamos a este c\xF3digo postal.",
|
|
405
|
+
"delivery.refused": "Esta tienda no env\xEDa a esta direcci\xF3n.",
|
|
406
|
+
"delivery.review": "Enviamos a esta zona, pero lo confirmamos contigo antes de salir.",
|
|
407
|
+
"delivery.etaSoon": "hoy o ma\xF1ana",
|
|
408
|
+
"delivery.etaRange": "de {{min}} a {{max}} d\xEDas laborables",
|
|
409
|
+
"delivery.etaDays": "{{days}} d\xEDa(s) laborable(s)",
|
|
410
|
+
"delivery.flatRate": "El env\xEDo se calcula en el pago.",
|
|
411
|
+
"delivery.error.digits": "Escribe los 8 d\xEDgitos del CEP.",
|
|
412
|
+
"delivery.error.postal": "Revisa el c\xF3digo postal (ej.: {{example}}).",
|
|
413
|
+
"delivery.error.quote": "No hemos podido calcular el env\xEDo ahora. Int\xE9ntalo de nuevo.",
|
|
414
|
+
"delivery.error.notFound": "C\xF3digo postal no encontrado. Revisa los n\xFAmeros.",
|
|
415
|
+
"cart.title": "Carrito",
|
|
416
|
+
"cart.close": "Cerrar carrito",
|
|
417
|
+
"cart.empty": "Tu carrito est\xE1 vac\xEDo.",
|
|
418
|
+
"cart.remove": "Quitar {{name}}",
|
|
419
|
+
"cart.checkout": "Finalizar compra",
|
|
420
|
+
"cart.missingForFree": "Te faltan {{amount}} para el env\xEDo gratis",
|
|
421
|
+
"cart.wonFree": "\xA1Tienes env\xEDo gratis!",
|
|
422
|
+
"cart.minimumOrder": "Pedido m\xEDnimo para entrega: {{amount}}. Te faltan {{missing}}.",
|
|
423
|
+
"delivery.eta": "Entrega en ~{{minutes}} min",
|
|
424
|
+
"country.deliverTo": "Enviar a",
|
|
425
|
+
"country.change": "Cambiar pa\xEDs",
|
|
426
|
+
"country.label": "Pa\xEDs de env\xEDo"
|
|
427
|
+
};
|
|
428
|
+
var MESSAGES = { "pt-BR": ptBR, en, es };
|
|
429
|
+
function normalizeLanguage(value) {
|
|
430
|
+
const base = (value || "").toLowerCase().split(/[-_]/)[0];
|
|
431
|
+
if (base === "pt") return "pt-BR";
|
|
432
|
+
if (base === "en") return "en";
|
|
433
|
+
if (base === "es") return "es";
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
function translate(language, key, vars, overrides) {
|
|
437
|
+
let text = overrides?.[language]?.[key] ?? MESSAGES[language]?.[key] ?? overrides?.["pt-BR"]?.[key] ?? ptBR[key];
|
|
438
|
+
if (vars) for (const [name, value] of Object.entries(vars)) text = text.split(`{{${name}}}`).join(String(value));
|
|
439
|
+
return text;
|
|
440
|
+
}
|
|
441
|
+
function resolveCountries(config) {
|
|
442
|
+
const countries = [...new Set((config.countries ?? []).map((c) => c.trim().toUpperCase()).filter(Boolean))];
|
|
443
|
+
if (countries.length === 0) countries.push("BR");
|
|
444
|
+
const wanted = config.defaultCountry?.trim().toUpperCase();
|
|
445
|
+
return { countries, defaultCountry: wanted && countries.includes(wanted) ? wanted : countries[0] };
|
|
446
|
+
}
|
|
447
|
+
function resolveConfig(config) {
|
|
448
|
+
const homePage = config.pages?.home ?? (config.home ? { blocks: config.home.sections } : void 0);
|
|
449
|
+
const commerceMode = config.commerceMode ?? "checkout";
|
|
450
|
+
const catalogPath = commerceMode === "delivery" ? config.delivery?.menuPath ?? "/menu" : homePage ? "/catalog" : "/";
|
|
451
|
+
const checkoutEnabled = commerceMode === "checkout" || commerceMode === "delivery";
|
|
452
|
+
return {
|
|
453
|
+
...config,
|
|
454
|
+
pages: { ...config.pages, ...homePage ? { home: homePage } : {} },
|
|
455
|
+
currency: config.currency ?? "BRL",
|
|
456
|
+
locale: config.locale ?? "pt-BR",
|
|
457
|
+
language: normalizeLanguage(config.language) ?? "pt-BR",
|
|
458
|
+
...resolveCountries(config),
|
|
459
|
+
shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
|
|
460
|
+
payments: {
|
|
461
|
+
mode: config.payments?.mode ?? "mock",
|
|
462
|
+
methods: config.payments?.methods?.length ? config.payments.methods : ["pix", "credit_card", "cash"],
|
|
463
|
+
// Rebuilding `payments` key by key silently dropped `terms`, so the Pix
|
|
464
|
+
// price and the instalment line never reached a component even when the
|
|
465
|
+
// store had configured them.
|
|
466
|
+
terms: config.payments?.terms
|
|
467
|
+
},
|
|
468
|
+
commerceMode,
|
|
469
|
+
// Só existe no modo delivery. Fora dele o objeto inteiro é undefined, e é
|
|
470
|
+
// isso que a rota /menu consulta para decidir se existe.
|
|
471
|
+
delivery: commerceMode === "delivery" ? {
|
|
472
|
+
menuPath: config.delivery?.menuPath ?? "/menu",
|
|
473
|
+
coverImage: config.delivery?.coverImage,
|
|
474
|
+
logoImage: config.delivery?.logoImage,
|
|
475
|
+
locality: config.delivery?.locality,
|
|
476
|
+
highlightSlugs: config.delivery?.highlightSlugs,
|
|
477
|
+
// A ORDEM é a do seletor na tela. Um Set aqui perderia isso.
|
|
478
|
+
fulfillmentModes: config.delivery?.fulfillmentModes?.length ? config.delivery.fulfillmentModes : ["delivery"].slice(),
|
|
479
|
+
// Agendamento é opt-in duas vezes: aqui e na loja (0094 devolve zero
|
|
480
|
+
// slots quando scheduling_enabled é falso). A tela só oferece quando
|
|
481
|
+
// as duas concordam.
|
|
482
|
+
scheduling: config.delivery?.scheduling ?? false,
|
|
483
|
+
pickupAddress: config.delivery?.pickupAddress
|
|
484
|
+
} : void 0,
|
|
485
|
+
enquiry: {
|
|
486
|
+
label: config.enquiry?.label ?? "Contact me",
|
|
487
|
+
successMessage: config.enquiry?.successMessage ?? "Thanks. We received your enquiry.",
|
|
488
|
+
subjectPrefix: config.enquiry?.subjectPrefix ?? "Product enquiry",
|
|
489
|
+
email: config.enquiry?.email,
|
|
490
|
+
whatsappUrl: config.enquiry?.whatsappUrl,
|
|
491
|
+
requirePhone: config.enquiry?.requirePhone
|
|
492
|
+
},
|
|
493
|
+
features: {
|
|
494
|
+
// Auto-enable the coupon UI when the store actually ships discount codes
|
|
495
|
+
// (config.discounts, a seeded catalog, or a rule-engine promotion with a
|
|
496
|
+
// code — M6), unless explicitly overridden — otherwise a store can
|
|
497
|
+
// advertise a coupon with no field to enter it, which is exactly the
|
|
498
|
+
// figurinhas pilot failure this milestone exists to end.
|
|
499
|
+
discounts: config.features?.discounts ?? Boolean(
|
|
500
|
+
config.discounts?.length || config.catalog?.discounts?.length || config.promotions?.some((p) => p.code) || config.catalog?.promotions?.some((p) => p.code)
|
|
501
|
+
),
|
|
502
|
+
accounts: config.features?.accounts ?? checkoutEnabled,
|
|
503
|
+
cart: config.features?.cart ?? checkoutEnabled,
|
|
504
|
+
checkout: config.features?.checkout ?? checkoutEnabled
|
|
505
|
+
},
|
|
506
|
+
imageLoading: {
|
|
507
|
+
mode: config.imageLoading?.mode ?? "fade",
|
|
508
|
+
durationMs: config.imageLoading?.durationMs ?? 420,
|
|
509
|
+
easing: config.imageLoading?.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
510
|
+
blur: config.imageLoading?.blur ?? true,
|
|
511
|
+
transform: config.imageLoading?.transform ?? "none"
|
|
512
|
+
},
|
|
513
|
+
catalogPath,
|
|
514
|
+
search: {
|
|
515
|
+
mode: config.search?.mode ?? "inline",
|
|
516
|
+
placeholder: config.search?.placeholder ?? "Buscar produtos...",
|
|
517
|
+
resultsPath: config.search?.resultsPath ?? "/search",
|
|
518
|
+
suggestionsLimit: config.search?.suggestionsLimit ?? 4,
|
|
519
|
+
minChars: config.search?.minChars ?? 2,
|
|
520
|
+
ctaLabel: config.search?.ctaLabel ?? "Ver todos resultados",
|
|
521
|
+
emptyLabel: config.search?.emptyLabel ?? "Nada encontrado para",
|
|
522
|
+
renderSuggestion: config.search?.renderSuggestion
|
|
523
|
+
},
|
|
524
|
+
nav: config.nav ?? (homePage ? [
|
|
525
|
+
{ label: "In\xEDcio", to: "/" },
|
|
526
|
+
{ label: "Loja", to: "/catalog" }
|
|
527
|
+
] : [{ label: "Loja", to: "/" }])
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
var StorefrontConfigContext = createContext(null);
|
|
531
|
+
var StorefrontConfigProvider = StorefrontConfigContext.Provider;
|
|
532
|
+
function useStorefrontConfig() {
|
|
533
|
+
const ctx = useContext(StorefrontConfigContext);
|
|
534
|
+
if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
|
|
535
|
+
return ctx;
|
|
536
|
+
}
|
|
537
|
+
function useStorefrontConfigOptional() {
|
|
538
|
+
return useContext(StorefrontConfigContext);
|
|
539
|
+
}
|
|
540
|
+
function getStorefrontComponents(config) {
|
|
541
|
+
return config.components ?? {};
|
|
542
|
+
}
|
|
543
|
+
var RouterAdapterContext = createContext(null);
|
|
544
|
+
var StorefrontRouterProvider = RouterAdapterContext.Provider;
|
|
545
|
+
var _activeAdapter = null;
|
|
546
|
+
var _fallback = null;
|
|
547
|
+
function fallbackAdapter() {
|
|
548
|
+
if (!_fallback) _fallback = defaultRouterAdapter("hash");
|
|
549
|
+
return _fallback;
|
|
550
|
+
}
|
|
551
|
+
function useRouterAdapter() {
|
|
552
|
+
const injected = useContext(RouterAdapterContext);
|
|
553
|
+
const adapter = injected ?? fallbackAdapter();
|
|
554
|
+
useEffect(() => {
|
|
555
|
+
if (injected) _activeAdapter = injected;
|
|
556
|
+
}, [injected]);
|
|
557
|
+
return adapter;
|
|
558
|
+
}
|
|
559
|
+
function useRoutePath() {
|
|
560
|
+
const adapter = useRouterAdapter();
|
|
561
|
+
return useSyncExternalStore(
|
|
562
|
+
(onChange) => adapter.onPathChange(onChange),
|
|
563
|
+
() => adapter.getCurrentPath() || "/",
|
|
564
|
+
() => adapter.getCurrentPath() || "/"
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
function useScrollToTopOnNavigate(path) {
|
|
568
|
+
const mounted = useRef(false);
|
|
569
|
+
useEffect(() => {
|
|
570
|
+
if (!mounted.current) {
|
|
571
|
+
mounted.current = true;
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (typeof window !== "undefined") window.scrollTo(0, 0);
|
|
575
|
+
}, [path]);
|
|
576
|
+
}
|
|
577
|
+
function useNavigate() {
|
|
578
|
+
const adapter = useRouterAdapter();
|
|
579
|
+
return adapter.navigate.bind(adapter);
|
|
580
|
+
}
|
|
581
|
+
var useHashPath = useRoutePath;
|
|
582
|
+
function navigateTo(to) {
|
|
583
|
+
(_activeAdapter ?? fallbackAdapter()).navigate(to);
|
|
584
|
+
}
|
|
585
|
+
function shellRouterAdapter(pathOverride, live) {
|
|
586
|
+
if (pathOverride == null) return live;
|
|
587
|
+
return staticRouterAdapter(pathOverride, { href: (to) => live.href(to) });
|
|
588
|
+
}
|
|
589
|
+
function matchPath(pattern, path) {
|
|
590
|
+
const cleanPath = (path.split("?")[0] ?? "").replace(/\/+$/, "") || "/";
|
|
591
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
592
|
+
const pathParts = cleanPath.split("/").filter(Boolean);
|
|
593
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
594
|
+
const params = {};
|
|
595
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
596
|
+
const pat = patternParts[i];
|
|
597
|
+
const part = pathParts[i];
|
|
598
|
+
if (pat.startsWith(":")) params[pat.slice(1)] = decodeURIComponent(part);
|
|
599
|
+
else if (pat !== part) return null;
|
|
600
|
+
}
|
|
601
|
+
return params;
|
|
602
|
+
}
|
|
603
|
+
function Link({ to, children, onClick, target, ...rest }) {
|
|
604
|
+
const adapter = useRouterAdapter();
|
|
605
|
+
const handleClick = (event) => {
|
|
606
|
+
onClick?.(event);
|
|
607
|
+
if (!adapter.interceptsLinkClicks) return;
|
|
608
|
+
if (event.defaultPrevented) return;
|
|
609
|
+
if (target && target !== "_self") return;
|
|
610
|
+
if (event.button !== 0) return;
|
|
611
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
adapter.navigate(to);
|
|
614
|
+
};
|
|
615
|
+
return React2.createElement(
|
|
616
|
+
"a",
|
|
617
|
+
{ href: adapter.href(to), onClick: handleClick, target, ...rest },
|
|
618
|
+
children
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// src/app/static-render.ts
|
|
623
|
+
var active = null;
|
|
624
|
+
function currentStaticRenderPass() {
|
|
625
|
+
return active;
|
|
626
|
+
}
|
|
627
|
+
function withStaticRenderPass(pass, render) {
|
|
628
|
+
const previous = active;
|
|
629
|
+
active = pass;
|
|
630
|
+
try {
|
|
631
|
+
return render();
|
|
632
|
+
} finally {
|
|
633
|
+
active = previous;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function resolveDataParams(need, context) {
|
|
637
|
+
const out = {};
|
|
638
|
+
for (const [name, value] of Object.entries(need.params ?? {})) {
|
|
639
|
+
let resolved = value;
|
|
640
|
+
if (isLoaderParamRef(value)) resolved = context.params[value.$param];
|
|
641
|
+
else if (isLoaderPropRef(value)) resolved = context.props[value.$prop];
|
|
642
|
+
if (resolved !== void 0) out[name] = resolved;
|
|
643
|
+
}
|
|
644
|
+
return out;
|
|
645
|
+
}
|
|
646
|
+
function needsForNode(node) {
|
|
647
|
+
const meta = getBlockMeta(node.type);
|
|
648
|
+
return { ...meta?.data, ...node.data };
|
|
649
|
+
}
|
|
650
|
+
function collectDataNeeds(nodes, routeParams = {}) {
|
|
651
|
+
const found = /* @__PURE__ */ new Map();
|
|
652
|
+
const visit = (node) => {
|
|
653
|
+
const meta = getBlockMeta(node.type);
|
|
654
|
+
const needs = needsForNode(node);
|
|
655
|
+
if (Object.keys(needs).length > 0) {
|
|
656
|
+
const props = { ...defaultPropsFromSettings(meta?.settings), ...node.props };
|
|
657
|
+
for (const need of Object.values(needs)) {
|
|
658
|
+
if (!need || typeof need.loader !== "string" || !need.loader) continue;
|
|
659
|
+
const params = resolveDataParams(need, { params: routeParams, props });
|
|
660
|
+
const key = dataNeedKey(need.loader, params);
|
|
661
|
+
if (!found.has(key)) found.set(key, { key, loader: need.loader, params });
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
for (const child of node.children ?? []) visit(child);
|
|
665
|
+
};
|
|
666
|
+
for (const node of nodes) visit(node);
|
|
667
|
+
return [...found.values()];
|
|
668
|
+
}
|
|
669
|
+
async function resolvePageDataNeed(need) {
|
|
670
|
+
try {
|
|
671
|
+
const impl = resolveLoader(need.loader);
|
|
672
|
+
const data = await impl(need.params, { shop: getShopProvider() });
|
|
673
|
+
return { status: "ready", data };
|
|
674
|
+
} catch (err) {
|
|
675
|
+
const message = errorMessage(err);
|
|
676
|
+
console.error(`[storefront] loader "${need.loader}" failed:`, err);
|
|
677
|
+
return { status: "error", error: message };
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async function resolvePageData(needs) {
|
|
681
|
+
const settled = await Promise.all(
|
|
682
|
+
needs.map(async (need) => [need.key, await resolvePageDataNeed(need)])
|
|
683
|
+
);
|
|
684
|
+
return Object.fromEntries(settled);
|
|
685
|
+
}
|
|
686
|
+
var PageDataContext = createContext(null);
|
|
687
|
+
function PageDataScope({ nodes, params, initial, children }) {
|
|
688
|
+
const parent = useContext(PageDataContext);
|
|
689
|
+
const routeParams = useMemo(() => params ?? parent?.params ?? {}, [params, parent]);
|
|
690
|
+
const needs = useMemo(() => collectDataNeeds(nodes, routeParams), [nodes, routeParams]);
|
|
691
|
+
const own = useMemo(
|
|
692
|
+
() => needs.filter((need) => !parent || !(need.key in parent.entries)),
|
|
693
|
+
[needs, parent]
|
|
694
|
+
);
|
|
695
|
+
const staticPass = currentStaticRenderPass();
|
|
696
|
+
if (staticPass) {
|
|
697
|
+
staticPass.tree(nodes);
|
|
698
|
+
staticPass.needs(own);
|
|
699
|
+
}
|
|
700
|
+
const [entries, setEntries] = useState(() => ({
|
|
701
|
+
// Everything handed in is published, not only the needs THIS scope
|
|
702
|
+
// collected. That is what lets a host framework resolve a page's data in
|
|
703
|
+
// its own loader and wrap the shell in a seeding scope: the page's own
|
|
704
|
+
// scope then finds those keys already answered on its parent and fetches
|
|
705
|
+
// nothing. Without it, seeding only works when the seeding scope happens to
|
|
706
|
+
// render the same block tree.
|
|
707
|
+
...initial,
|
|
708
|
+
...Object.fromEntries(
|
|
709
|
+
own.map((need) => [
|
|
710
|
+
need.key,
|
|
711
|
+
initial?.[need.key] ?? staticPass?.entries[need.key] ?? { status: "loading" }
|
|
712
|
+
])
|
|
713
|
+
)
|
|
714
|
+
}));
|
|
715
|
+
const signature = own.map((need) => need.key).join("\n");
|
|
716
|
+
useEffect(() => {
|
|
717
|
+
const pending = own.filter((need) => initial?.[need.key]?.status !== "ready");
|
|
718
|
+
if (pending.length === 0) return;
|
|
719
|
+
let cancelled = false;
|
|
720
|
+
setEntries((prev) => {
|
|
721
|
+
const next = { ...prev };
|
|
722
|
+
for (const need of pending) {
|
|
723
|
+
if (next[need.key]?.status !== "ready") next[need.key] = { status: "loading" };
|
|
724
|
+
}
|
|
725
|
+
return next;
|
|
726
|
+
});
|
|
727
|
+
for (const need of pending) {
|
|
728
|
+
void resolvePageDataNeed(need).then((entry) => {
|
|
729
|
+
if (!cancelled) setEntries((prev) => ({ ...prev, [need.key]: entry }));
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return () => {
|
|
733
|
+
cancelled = true;
|
|
734
|
+
};
|
|
735
|
+
}, [signature]);
|
|
736
|
+
const value = useMemo(
|
|
737
|
+
() => ({ entries: { ...parent?.entries, ...entries, ...initial }, params: routeParams }),
|
|
738
|
+
[parent, entries, initial, routeParams]
|
|
739
|
+
);
|
|
740
|
+
return /* @__PURE__ */ jsx(PageDataContext.Provider, { value, children });
|
|
741
|
+
}
|
|
742
|
+
function useLoader(loader, params) {
|
|
743
|
+
const scope = useContext(PageDataContext);
|
|
744
|
+
const key = params === null ? null : dataNeedKey(loader, params);
|
|
745
|
+
const [nonce, setNonce] = useState(0);
|
|
746
|
+
const scoped = nonce === 0 && key !== null && scope ? scope.entries[key] : void 0;
|
|
747
|
+
const [local, setLocal] = useState(null);
|
|
748
|
+
const staticPass = currentStaticRenderPass();
|
|
749
|
+
const staticEntry = staticPass && key !== null && scoped === void 0 ? staticPass.entries[key] : void 0;
|
|
750
|
+
if (staticPass && key !== null && scoped === void 0 && staticEntry === void 0) {
|
|
751
|
+
staticPass.needs([{ key, loader, params }]);
|
|
752
|
+
}
|
|
753
|
+
const paramsKey = params === null ? null : JSON.stringify(params);
|
|
754
|
+
useEffect(() => {
|
|
755
|
+
if (key === null || scoped !== void 0) return;
|
|
756
|
+
let cancelled = false;
|
|
757
|
+
setLocal({ key, entry: { status: "loading" } });
|
|
758
|
+
void resolvePageDataNeed({ loader, params }).then((entry2) => {
|
|
759
|
+
if (!cancelled) setLocal({ key, entry: entry2 });
|
|
760
|
+
});
|
|
761
|
+
return () => {
|
|
762
|
+
cancelled = true;
|
|
763
|
+
};
|
|
764
|
+
}, [loader, paramsKey, nonce, scoped !== void 0]);
|
|
765
|
+
const entry = scoped ?? staticEntry ?? (local && local.key === key ? local.entry : key !== null ? { status: "loading" } : null);
|
|
766
|
+
return {
|
|
767
|
+
status: entry?.status ?? "idle",
|
|
768
|
+
data: entry?.status === "ready" ? entry.data : null,
|
|
769
|
+
error: entry?.status === "error" ? entry.error ?? "unknown error" : null,
|
|
770
|
+
reload: () => setNonce((n) => n + 1)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
function useBlockData(node, name, fallback) {
|
|
774
|
+
const scope = useContext(PageDataContext);
|
|
775
|
+
let loader = fallback.loader;
|
|
776
|
+
let params = fallback.params;
|
|
777
|
+
if (node) {
|
|
778
|
+
const need = needsForNode(node)[name];
|
|
779
|
+
if (need && typeof need.loader === "string" && need.loader) {
|
|
780
|
+
const meta = getBlockMeta(node.type);
|
|
781
|
+
loader = need.loader;
|
|
782
|
+
params = resolveDataParams(need, {
|
|
783
|
+
params: scope?.params ?? {},
|
|
784
|
+
props: { ...defaultPropsFromSettings(meta?.settings), ...node.props }
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
const result = useLoader(loader, params);
|
|
789
|
+
return { ...result, loader };
|
|
790
|
+
}
|
|
791
|
+
function BlockDataError({ loader, message }) {
|
|
792
|
+
if (getBlockErrorVisibility() === "silent") return null;
|
|
793
|
+
return /* @__PURE__ */ jsxs(
|
|
794
|
+
"div",
|
|
795
|
+
{
|
|
796
|
+
"data-block-error": `loader:${loader}`,
|
|
797
|
+
style: {
|
|
798
|
+
border: "1px dashed #dc2626",
|
|
799
|
+
color: "#b91c1c",
|
|
800
|
+
padding: "8px 12px",
|
|
801
|
+
font: "12px ui-monospace, monospace",
|
|
802
|
+
borderRadius: 6
|
|
803
|
+
},
|
|
804
|
+
children: [
|
|
805
|
+
`Data for this block failed (loader "${loader}")`,
|
|
806
|
+
message ? /* @__PURE__ */ jsx("div", { style: { opacity: 0.8, marginTop: 4 }, children: message }) : null
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
function mergePageTheme(base, overlay) {
|
|
812
|
+
if (!base && !overlay) return void 0;
|
|
813
|
+
if (!overlay) return base;
|
|
814
|
+
const name = overlay.name ?? (base ? `${base.name}-page` : "page");
|
|
815
|
+
const font = base?.font ? { ...base.font, ...overlay.font } : overlay.font;
|
|
816
|
+
return {
|
|
817
|
+
...base,
|
|
818
|
+
...overlay,
|
|
819
|
+
name,
|
|
820
|
+
colors: { ...base?.colors, ...overlay.colors },
|
|
821
|
+
font,
|
|
822
|
+
tokens: { ...base?.tokens, ...overlay.tokens },
|
|
823
|
+
header: { ...base?.header, ...overlay.header },
|
|
824
|
+
productCard: { ...base?.productCard, ...overlay.productCard }
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function pageThemeSelector(slug) {
|
|
828
|
+
return `[data-page="${slug}"]`;
|
|
829
|
+
}
|
|
830
|
+
function routePathParams(path) {
|
|
831
|
+
return path.split("/").filter((segment) => segment.startsWith(":")).map((segment) => segment.slice(1));
|
|
832
|
+
}
|
|
833
|
+
function contentPagePath(slug, page) {
|
|
834
|
+
return page.path ?? `/${slug}`;
|
|
835
|
+
}
|
|
836
|
+
function matchContentPage(pages, path) {
|
|
837
|
+
for (const [slug, page] of Object.entries(pages?.content ?? {})) {
|
|
838
|
+
const params = matchPath(contentPagePath(slug, page), path);
|
|
839
|
+
if (params) return { slug, page, params };
|
|
840
|
+
}
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
var CATALOG_BODY_BLOCK = "catalogBody";
|
|
844
|
+
var PRODUCT_BODY_BLOCK = "productBody";
|
|
845
|
+
var BODY_BLOCK_BY_KIND = {
|
|
846
|
+
catalog: CATALOG_BODY_BLOCK,
|
|
847
|
+
product: PRODUCT_BODY_BLOCK
|
|
848
|
+
};
|
|
849
|
+
var BLOCK_STRUCTURAL_KEYS = /* @__PURE__ */ new Set([
|
|
850
|
+
"type",
|
|
851
|
+
"id",
|
|
852
|
+
"props",
|
|
853
|
+
"children",
|
|
854
|
+
"data"
|
|
855
|
+
]);
|
|
856
|
+
var STRUCTURAL_KEYS = BLOCK_STRUCTURAL_KEYS;
|
|
857
|
+
function isNestedBlockRef(entry) {
|
|
858
|
+
return isNestedRef(entry);
|
|
859
|
+
}
|
|
860
|
+
function isNestedRef(entry) {
|
|
861
|
+
const record = entry;
|
|
862
|
+
if ("props" in record) return true;
|
|
863
|
+
return Object.keys(record).every((key) => STRUCTURAL_KEYS.has(key));
|
|
864
|
+
}
|
|
865
|
+
function blockInstanceId(entry, index) {
|
|
866
|
+
const record = entry;
|
|
867
|
+
if (record && typeof record.id === "string" && record.id) return record.id;
|
|
868
|
+
return `${String(record?.type)}-${index}`;
|
|
869
|
+
}
|
|
870
|
+
function toBlockNode(entry, index) {
|
|
871
|
+
const record = entry;
|
|
872
|
+
const type = String(record.type);
|
|
873
|
+
const id = blockInstanceId(entry, index);
|
|
874
|
+
const data = record.data;
|
|
875
|
+
if (isNestedRef(entry)) {
|
|
876
|
+
const children = record.children ?? [];
|
|
877
|
+
return {
|
|
878
|
+
type,
|
|
879
|
+
id,
|
|
880
|
+
props: record.props ?? {},
|
|
881
|
+
...children.length ? { children: children.map((child, i) => toBlockNode(child, i)) } : {},
|
|
882
|
+
...data ? { data } : {}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const props = {};
|
|
886
|
+
for (const [key, value] of Object.entries(record)) {
|
|
887
|
+
if (!STRUCTURAL_KEYS.has(key)) props[key] = value;
|
|
888
|
+
}
|
|
889
|
+
return { type, id, props, ...data ? { data } : {} };
|
|
890
|
+
}
|
|
891
|
+
function toBlockNodes(blocks) {
|
|
892
|
+
return blocks.map(toBlockNode);
|
|
893
|
+
}
|
|
894
|
+
function defaultPageBlocks(kind) {
|
|
895
|
+
const body = BODY_BLOCK_BY_KIND[kind];
|
|
896
|
+
return body ? [{ type: body, id: body, props: {} }] : [];
|
|
897
|
+
}
|
|
898
|
+
function checkPageBlocks(kind, nodes) {
|
|
899
|
+
const found = [];
|
|
900
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
901
|
+
const expectedBody = BODY_BLOCK_BY_KIND[kind];
|
|
902
|
+
let hasBody = false;
|
|
903
|
+
for (const node of nodes) {
|
|
904
|
+
if (node.type === expectedBody) hasBody = true;
|
|
905
|
+
if (node.id) {
|
|
906
|
+
if (seenIds.has(node.id)) {
|
|
907
|
+
found.push({
|
|
908
|
+
level: "warning",
|
|
909
|
+
rule: "duplicate-block-id",
|
|
910
|
+
detail: `two blocks on the ${kind} page share the id "${node.id}" \u2014 an editor or a generated diff cannot tell them apart`
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
seenIds.add(node.id);
|
|
914
|
+
}
|
|
915
|
+
const meta = getBlockMeta(node.type);
|
|
916
|
+
if (!meta) {
|
|
917
|
+
found.push({
|
|
918
|
+
level: "error",
|
|
919
|
+
rule: "unknown-block",
|
|
920
|
+
detail: `the ${kind} page references block "${node.type}", which is not registered \u2014 register it before the storefront mounts`
|
|
921
|
+
});
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
if (!isBlockEnabledOn({ enabledOn: meta.enabledOn ?? [] }, kind)) {
|
|
925
|
+
found.push({
|
|
926
|
+
level: "warning",
|
|
927
|
+
rule: "block-not-enabled-on-page",
|
|
928
|
+
detail: `block "${node.type}" declares enabledOn [${(meta.enabledOn ?? []).join(", ")}] and is placed on the ${kind} page`
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
if (expectedBody && !hasBody) {
|
|
933
|
+
found.push({
|
|
934
|
+
level: "warning",
|
|
935
|
+
rule: "missing-page-body",
|
|
936
|
+
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`
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
for (const violation of checkBlockConstraints(nodes)) {
|
|
940
|
+
found.push({
|
|
941
|
+
level: "error",
|
|
942
|
+
rule: violation.rule,
|
|
943
|
+
detail: `${kind} page ${violation.path}: ${violation.detail}`
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
for (const finding of checkBlockContext(nodes)) {
|
|
947
|
+
found.push({
|
|
948
|
+
level: "warning",
|
|
949
|
+
rule: "context-not-provided",
|
|
950
|
+
detail: `block "${finding.type}" at ${kind} page ${finding.path} uses context [${finding.missing.join(", ")}] that no ancestor provides`
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
return found;
|
|
954
|
+
}
|
|
955
|
+
function absolute(ctx, path = ctx.path) {
|
|
956
|
+
const origin = (ctx.origin ?? currentStaticRenderPass()?.origin ?? seoOrigin()).replace(/\/$/, "");
|
|
957
|
+
if (!origin) return void 0;
|
|
958
|
+
return `${origin}${path.startsWith("/") ? path : `/${path}`}`;
|
|
959
|
+
}
|
|
960
|
+
function primaryImageUrl(product) {
|
|
961
|
+
return (product.images?.find((i) => i.isPrimary) ?? product.images?.[0])?.url;
|
|
962
|
+
}
|
|
963
|
+
function shareImageUrl(product, ctx) {
|
|
964
|
+
const url = primaryImageUrl(product);
|
|
965
|
+
if (!url) return void 0;
|
|
966
|
+
return ctx.config.imageLoading.transform === "supabase" ? imageVariantUrl(url, { width: 1200, quality: 82 }) : url;
|
|
967
|
+
}
|
|
968
|
+
function productJsonLd(product, ctx) {
|
|
969
|
+
const url = absolute(ctx);
|
|
970
|
+
const image = shareImageUrl(product, ctx);
|
|
971
|
+
return {
|
|
972
|
+
"@context": "https://schema.org",
|
|
973
|
+
"@type": "Product",
|
|
974
|
+
name: product.name,
|
|
975
|
+
...product.description ? { description: product.description } : {},
|
|
976
|
+
...product.sku ? { sku: product.sku } : {},
|
|
977
|
+
...image ? { image: [image] } : {},
|
|
978
|
+
offers: {
|
|
979
|
+
"@type": "Offer",
|
|
980
|
+
...url ? { url } : {},
|
|
981
|
+
price: product.price,
|
|
982
|
+
priceCurrency: product.currency ?? ctx.config.currency,
|
|
983
|
+
availability: (product.inventoryCount ?? 0) > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock"
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function productSeo(product, ctx) {
|
|
988
|
+
const url = absolute(ctx);
|
|
989
|
+
const home = absolute(ctx, "/");
|
|
990
|
+
const catalog = absolute(ctx, ctx.config.catalogPath);
|
|
991
|
+
return {
|
|
992
|
+
title: `${product.name} \u2014 ${ctx.config.name}`,
|
|
993
|
+
description: product.description?.slice(0, 160),
|
|
994
|
+
canonical: url,
|
|
995
|
+
image: shareImageUrl(product, ctx),
|
|
996
|
+
type: "product",
|
|
997
|
+
jsonLd: [
|
|
998
|
+
productJsonLd(product, ctx),
|
|
999
|
+
...home && catalog && url ? [
|
|
1000
|
+
breadcrumbJsonLd([
|
|
1001
|
+
{ name: ctx.config.name, url: home },
|
|
1002
|
+
{ name: "Loja", url: catalog },
|
|
1003
|
+
{ name: product.name, url }
|
|
1004
|
+
])
|
|
1005
|
+
] : []
|
|
1006
|
+
]
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
function pageSeo(ctx, input) {
|
|
1010
|
+
return { ...input, canonical: absolute(ctx), type: "website" };
|
|
1011
|
+
}
|
|
1012
|
+
function menuSections(products, categories) {
|
|
1013
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
1014
|
+
const loose = [];
|
|
1015
|
+
for (const p of products) {
|
|
1016
|
+
const key = p.categoryId ?? p.categoryName ?? "";
|
|
1017
|
+
if (!key) {
|
|
1018
|
+
loose.push(p);
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
byCategory.set(key, [...byCategory.get(key) ?? [], p]);
|
|
1022
|
+
}
|
|
1023
|
+
const sections = categories.map((c) => ({ name: c.name, description: c.description, products: byCategory.get(c.id) ?? byCategory.get(c.name) ?? [] })).filter((s) => s.products.length > 0);
|
|
1024
|
+
if (loose.length) sections.push({ name: "Outros", description: null, products: loose });
|
|
1025
|
+
if (!sections.length && products.length) return [{ name: "Card\xE1pio", description: null, products }];
|
|
1026
|
+
return sections;
|
|
1027
|
+
}
|
|
1028
|
+
function menuJsonLd(input, ctx) {
|
|
1029
|
+
const url = absolute(ctx);
|
|
1030
|
+
const home = absolute(ctx, "/");
|
|
1031
|
+
const b = input.business ?? {};
|
|
1032
|
+
const currency = ctx.config.currency;
|
|
1033
|
+
const address = b.address ? {
|
|
1034
|
+
"@type": "PostalAddress",
|
|
1035
|
+
...b.address.streetAddress ? { streetAddress: b.address.streetAddress } : {},
|
|
1036
|
+
...b.address.locality ? { addressLocality: b.address.locality } : {},
|
|
1037
|
+
...b.address.region ? { addressRegion: b.address.region } : {},
|
|
1038
|
+
...b.address.postalCode ? { postalCode: b.address.postalCode } : {},
|
|
1039
|
+
addressCountry: b.address.country ?? "BR"
|
|
1040
|
+
} : void 0;
|
|
1041
|
+
return {
|
|
1042
|
+
"@context": "https://schema.org",
|
|
1043
|
+
"@type": b.type ?? "Restaurant",
|
|
1044
|
+
...home ? { "@id": `${home.replace(/\/$/, "")}/#business` } : {},
|
|
1045
|
+
name: ctx.config.name,
|
|
1046
|
+
...home ? { url: home } : {},
|
|
1047
|
+
...b.description ? { description: b.description } : {},
|
|
1048
|
+
...b.image ? { image: [b.image] } : {},
|
|
1049
|
+
...b.logo ? { logo: b.logo } : {},
|
|
1050
|
+
...b.telephone ? { telephone: b.telephone } : {},
|
|
1051
|
+
...b.email ? { email: b.email } : {},
|
|
1052
|
+
...b.servesCuisine ? { servesCuisine: b.servesCuisine } : {},
|
|
1053
|
+
...b.priceRange ? { priceRange: b.priceRange } : {},
|
|
1054
|
+
...address ? { address } : {},
|
|
1055
|
+
...b.areaServed?.length ? { areaServed: b.areaServed.map((name) => ({ "@type": "Place", name })) } : {},
|
|
1056
|
+
...b.openingHours?.length ? { openingHours: b.openingHours } : {},
|
|
1057
|
+
...b.sameAs?.length ? { sameAs: b.sameAs } : {},
|
|
1058
|
+
...url ? { hasMenu: url } : {},
|
|
1059
|
+
...url ? {
|
|
1060
|
+
potentialAction: {
|
|
1061
|
+
"@type": "OrderAction",
|
|
1062
|
+
target: { "@type": "EntryPoint", urlTemplate: url, actionPlatform: ["https://schema.org/DesktopWebPlatform", "https://schema.org/MobileWebPlatform"] },
|
|
1063
|
+
deliveryMethod: "http://purl.org/goodrelations/v1#DeliveryModeOwnFleet"
|
|
1064
|
+
}
|
|
1065
|
+
} : {},
|
|
1066
|
+
menu: {
|
|
1067
|
+
"@type": "Menu",
|
|
1068
|
+
name: `Card\xE1pio \u2014 ${ctx.config.name}`,
|
|
1069
|
+
...url ? { url } : {},
|
|
1070
|
+
inLanguage: "pt-BR",
|
|
1071
|
+
hasMenuSection: menuSections(input.products, input.categories).map((section) => ({
|
|
1072
|
+
"@type": "MenuSection",
|
|
1073
|
+
name: section.name,
|
|
1074
|
+
...section.description ? { description: section.description } : {},
|
|
1075
|
+
hasMenuItem: section.products.map((p) => {
|
|
1076
|
+
const image = shareImageUrl(p, ctx);
|
|
1077
|
+
const itemUrl = absolute(ctx, `/product/${p.slug}`);
|
|
1078
|
+
return {
|
|
1079
|
+
"@type": "MenuItem",
|
|
1080
|
+
name: p.name,
|
|
1081
|
+
...p.description ? { description: p.description.slice(0, 300) } : {},
|
|
1082
|
+
...image ? { image } : {},
|
|
1083
|
+
...itemUrl ? { url: itemUrl } : {},
|
|
1084
|
+
offers: {
|
|
1085
|
+
"@type": "Offer",
|
|
1086
|
+
price: p.price,
|
|
1087
|
+
priceCurrency: p.currency ?? currency,
|
|
1088
|
+
availability: (p.inventoryCount ?? 0) > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock"
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
})
|
|
1092
|
+
}))
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function menuSeo(input, ctx) {
|
|
1097
|
+
const url = absolute(ctx);
|
|
1098
|
+
const home = absolute(ctx, "/");
|
|
1099
|
+
const names = input.products.slice(0, 4).map((p) => p.name).join(", ");
|
|
1100
|
+
return {
|
|
1101
|
+
title: input.title ?? `Delivery ${ctx.config.name} \u2014 pe\xE7a online`,
|
|
1102
|
+
description: (input.description ?? (names ? `Pe\xE7a online: ${names} e mais. Card\xE1pio completo com pre\xE7os.` : void 0))?.slice(0, 160),
|
|
1103
|
+
canonical: url,
|
|
1104
|
+
image: input.business?.image,
|
|
1105
|
+
type: "website",
|
|
1106
|
+
jsonLd: [
|
|
1107
|
+
menuJsonLd(input, ctx),
|
|
1108
|
+
...home && url ? [breadcrumbJsonLd([{ name: ctx.config.name, url: home }, { name: "Delivery", url }])] : []
|
|
1109
|
+
]
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// src/app/serving.ts
|
|
1114
|
+
function resolveStorefrontRoute(config, rawPath) {
|
|
1115
|
+
const resolved = ensureResolved(config);
|
|
1116
|
+
const path = rawPath.split("?")[0] || "/";
|
|
1117
|
+
for (const route of resolved.routes ?? []) {
|
|
1118
|
+
const params = matchPath(route.path, path);
|
|
1119
|
+
if (params) return { kind: "custom", route, params };
|
|
1120
|
+
}
|
|
1121
|
+
const product = matchPath("/product/:slug", path);
|
|
1122
|
+
if (product?.slug) return { kind: "product", params: product };
|
|
1123
|
+
const order = matchPath("/order/:id", path);
|
|
1124
|
+
if (order?.id) return { kind: "order", params: order };
|
|
1125
|
+
if (matchPath("/checkout", path)) return { kind: "checkout" };
|
|
1126
|
+
if (matchPath("/account", path)) return { kind: "account" };
|
|
1127
|
+
if (matchPath("/catalog", path)) return { kind: "catalog" };
|
|
1128
|
+
if (resolved.delivery && matchPath(resolved.delivery.menuPath, path)) return { kind: "menu" };
|
|
1129
|
+
if (resolved.search.mode === "overlay" && matchPath(resolved.search.resultsPath, path)) {
|
|
1130
|
+
return { kind: "searchResults" };
|
|
1131
|
+
}
|
|
1132
|
+
if (matchPath("/privacy", path)) return { kind: "policy", policy: "privacy" };
|
|
1133
|
+
if (matchPath("/terms", path)) return { kind: "policy", policy: "terms" };
|
|
1134
|
+
if (matchPath("/refunds", path)) return { kind: "policy", policy: "returns" };
|
|
1135
|
+
const content = matchContentPage(resolved.pages, path);
|
|
1136
|
+
if (content) return { kind: "content", slug: content.slug, params: content.params };
|
|
1137
|
+
if (path === "/" || path === "") return { kind: "home" };
|
|
1138
|
+
return { kind: "notFound" };
|
|
1139
|
+
}
|
|
1140
|
+
function isKnownPath(config, path) {
|
|
1141
|
+
return resolveStorefrontRoute(config, path).kind !== "notFound";
|
|
1142
|
+
}
|
|
1143
|
+
var STATIC_PATTERNS = ["/", "/catalog", "/privacy", "/terms", "/refunds"];
|
|
1144
|
+
function ensureResolved(config) {
|
|
1145
|
+
return "features" in config && "pages" in config ? config : resolveConfig(config);
|
|
1146
|
+
}
|
|
1147
|
+
function isPublicPath(config, path) {
|
|
1148
|
+
const match = resolveStorefrontRoute(config, path);
|
|
1149
|
+
switch (match.kind) {
|
|
1150
|
+
case "checkout":
|
|
1151
|
+
case "account":
|
|
1152
|
+
case "order":
|
|
1153
|
+
return false;
|
|
1154
|
+
case "custom":
|
|
1155
|
+
return match.route.kind !== "checkout" && match.route.kind !== "account" && match.route.chrome !== "focused";
|
|
1156
|
+
default:
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async function storefrontPaths(input) {
|
|
1161
|
+
const resolved = ensureResolved(input.config);
|
|
1162
|
+
const paths = [...STATIC_PATTERNS];
|
|
1163
|
+
if (resolved.delivery) paths.push(resolved.delivery.menuPath);
|
|
1164
|
+
for (const route of resolved.routes ?? []) {
|
|
1165
|
+
if (!route.path.includes(":")) paths.push(route.path);
|
|
1166
|
+
}
|
|
1167
|
+
const products = await input.provider.listProducts({ status: "active" });
|
|
1168
|
+
for (const product of products) paths.push(`/product/${product.slug}`);
|
|
1169
|
+
const unique = Array.from(new Set(paths));
|
|
1170
|
+
return input.includePrivate ? unique : unique.filter((p) => isPublicPath(resolved, p));
|
|
1171
|
+
}
|
|
1172
|
+
var MENU_DATA_NEEDS = {
|
|
1173
|
+
products: { loader: "products.list", params: {} },
|
|
1174
|
+
categories: { loader: "categories.list", params: {} },
|
|
1175
|
+
featured: { loader: "products.featured", params: {} }
|
|
1176
|
+
};
|
|
1177
|
+
function menuDataNeeds() {
|
|
1178
|
+
return Object.values(MENU_DATA_NEEDS).map(({ loader, params }) => ({ key: dataNeedKey(loader, params), loader, params }));
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
export { BLOCK_STRUCTURAL_KEYS, BlockDataError, CATALOG_BODY_BLOCK, Link, MENU_DATA_NEEDS, MESSAGES, PRODUCT_BODY_BLOCK, PageDataScope, StorefrontConfigProvider, StorefrontRouterProvider, blockInstanceId, checkPageBlocks, collectDataNeeds, contentPagePath, currentStaticRenderPass, defaultPageBlocks, getStorefrontComponents, isKnownPath, isNestedBlockRef, isPublicPath, matchContentPage, matchPath, menuDataNeeds, menuJsonLd, menuSeo, mergePageTheme, navigateTo, normalizeLanguage, pageSeo, pageThemeSelector, primaryImageUrl, productJsonLd, productSeo, resolveConfig, resolveDataParams, resolvePageData, resolvePageDataNeed, resolveStorefrontRoute, routePathParams, shellRouterAdapter, storefrontPaths, toBlockNodes, translate, useBlockData, useHashPath, useLoader, useNavigate, useRoutePath, useRouterAdapter, useScrollToTopOnNavigate, useStorefrontConfig, useStorefrontConfigOptional, withStaticRenderPass };
|
|
1182
|
+
//# sourceMappingURL=chunk-2KCORAVB.js.map
|
|
1183
|
+
//# sourceMappingURL=chunk-2KCORAVB.js.map
|