@fayz-ai/plugin-shop 0.24.4 → 0.24.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/CheckoutSettings-GQRYZF6I.js +128 -0
  2. package/dist/CheckoutSettings-GQRYZF6I.js.map +1 -0
  3. package/dist/ContentPage-2PHOZSFZ.js +510 -0
  4. package/dist/ContentPage-2PHOZSFZ.js.map +1 -0
  5. package/dist/DeliveryStoreSettings-L2RXOVB6.js +172 -0
  6. package/dist/DeliveryStoreSettings-L2RXOVB6.js.map +1 -0
  7. package/dist/FulfillmentSettings-PW233UOK.js +61 -0
  8. package/dist/FulfillmentSettings-PW233UOK.js.map +1 -0
  9. package/dist/MarketsSettings-YUEVDLIR.js +19 -0
  10. package/dist/MarketsSettings-YUEVDLIR.js.map +1 -0
  11. package/dist/PaymentsSettings-5CI74TY2.js +318 -0
  12. package/dist/PaymentsSettings-5CI74TY2.js.map +1 -0
  13. package/dist/ProductContentTab-PSNDS7JZ.js +4 -0
  14. package/dist/ProductContentTab-PSNDS7JZ.js.map +1 -0
  15. package/dist/ProductListingTab-55Y5RXU7.js +4 -0
  16. package/dist/ProductListingTab-55Y5RXU7.js.map +1 -0
  17. package/dist/ProductMarketPricesTab-QD2ZXEWL.js +4 -0
  18. package/dist/ProductMarketPricesTab-QD2ZXEWL.js.map +1 -0
  19. package/dist/ShippingSettings-UBTNRBJ4.js +9 -0
  20. package/dist/ShippingSettings-UBTNRBJ4.js.map +1 -0
  21. package/dist/{ShopPage-NUIIWBCB.js → ShopPage-NARKHXOO.js} +6 -3
  22. package/dist/ShopPage-NARKHXOO.js.map +1 -0
  23. package/dist/StoreGeneralSettings-DEZ2CMVG.js +97 -0
  24. package/dist/StoreGeneralSettings-DEZ2CMVG.js.map +1 -0
  25. package/dist/chunk-2MXOKJQO.js +23 -0
  26. package/dist/chunk-2MXOKJQO.js.map +1 -0
  27. package/dist/chunk-4JM32Z7J.js +51 -0
  28. package/dist/chunk-4JM32Z7J.js.map +1 -0
  29. package/dist/chunk-53Y6VLAW.js +132 -0
  30. package/dist/chunk-53Y6VLAW.js.map +1 -0
  31. package/dist/chunk-6V2VAUWP.js +24 -0
  32. package/dist/chunk-6V2VAUWP.js.map +1 -0
  33. package/dist/chunk-FQQMEM26.js +59 -0
  34. package/dist/chunk-FQQMEM26.js.map +1 -0
  35. package/dist/chunk-JYT6BTRE.js +298 -0
  36. package/dist/chunk-JYT6BTRE.js.map +1 -0
  37. package/dist/chunk-MFEOIJI7.js +127 -0
  38. package/dist/chunk-MFEOIJI7.js.map +1 -0
  39. package/dist/chunk-R5CUDFOI.js +61 -0
  40. package/dist/chunk-R5CUDFOI.js.map +1 -0
  41. package/dist/chunk-STHGZWBQ.js +1455 -0
  42. package/dist/chunk-STHGZWBQ.js.map +1 -0
  43. package/dist/{chunk-XV3KZRQX.js → chunk-STZYH32S.js} +9 -140
  44. package/dist/chunk-STZYH32S.js.map +1 -0
  45. package/dist/chunk-TDV3SFXT.js +75 -0
  46. package/dist/chunk-TDV3SFXT.js.map +1 -0
  47. package/dist/chunk-YUG6XQHJ.js +55 -0
  48. package/dist/chunk-YUG6XQHJ.js.map +1 -0
  49. package/dist/data/registry-routes.d.ts.map +1 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +129 -3456
  52. package/dist/index.js.map +1 -1
  53. package/dist/settings/ShippingSettings.d.ts.map +1 -1
  54. package/dist/views/EmailTemplatePage.d.ts.map +1 -1
  55. package/dist/views/EmailsPage.d.ts.map +1 -1
  56. package/dist/views/ProductContentTab.d.ts.map +1 -1
  57. package/dist/views/ProductMarketPricesTab.d.ts.map +1 -1
  58. package/dist/views/media-links.d.ts.map +1 -1
  59. package/package.json +7 -7
  60. package/dist/ShopPage-NUIIWBCB.js.map +0 -1
  61. package/dist/chunk-XV3KZRQX.js.map +0 -1
@@ -0,0 +1,1455 @@
1
+ import { ChoiceCards } from './chunk-4JM32Z7J.js';
2
+ import { shippingRegistryIdFromHash, shippingRegistryPath, shippingRecordPath } from './chunk-6V2VAUWP.js';
3
+ import { buildShopRegistries, locationSummary, LOCATIONS_REGISTRY, SHIPPING_ZONES_REGISTRY, packagesRegistry, PACKAGES_REGISTRY, SHIPPING_PROFILES_REGISTRY } from './chunk-STZYH32S.js';
4
+ import { useStoreSettings } from './chunk-R5CUDFOI.js';
5
+ import { resolve } from './chunk-YUG6XQHJ.js';
6
+ import { errorMessage } from './chunk-2MXOKJQO.js';
7
+ import * as React6 from 'react';
8
+ import React6__default from 'react';
9
+ import { useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, RegistryCrudView, adminNavigateTo, SettingsGroup, TextRow, ConnectorRow, ConnectorDetail, useAgentSurface } from '@fayz-ai/admin';
10
+ import { getShopProvider } from '@fayz-ai/shop';
11
+ import { ArrowRight, AlertTriangle, Warehouse, MapPin, Car, Truck, Loader2, Sparkles, Ban, ListChecks, Check, X, Layers, Search, Building2, Plus, Trash2 } from 'lucide-react';
12
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
+ import { Button, Input, DataTable, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@fayz-ai/ui';
14
+ import { onAppPathChange, navigateApp } from '@fayz-ai/core';
15
+
16
+ function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }) {
17
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
18
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pt-4", children: [
19
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Embalagens" }),
20
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: packagesRegistry.description })
21
+ ] }),
22
+ /* @__PURE__ */ jsx(RegistryCrudView, { registry: packagesRegistry, basePath, embedded: true })
23
+ ] });
24
+ }
25
+ function HandlingSettings({ settings, provider }) {
26
+ const shop = React6__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
27
+ const { value, set, loading, supported } = settings;
28
+ const [noteDraft, setNoteDraft] = React6__default.useState(null);
29
+ const [profiles, setProfiles] = React6__default.useState(null);
30
+ React6__default.useEffect(() => {
31
+ let cancelled = false;
32
+ void shop.listShippingProfiles?.().then((list) => {
33
+ if (!cancelled) setProfiles(list);
34
+ }).catch(() => {
35
+ if (!cancelled) setProfiles([]);
36
+ });
37
+ return () => {
38
+ cancelled = true;
39
+ };
40
+ }, [shop]);
41
+ if (!supported) return null;
42
+ const storedNote = value.handlingNote ?? "";
43
+ const note = noteDraft ?? storedNote;
44
+ const custom = (profiles ?? []).filter((p) => !p.isDefault);
45
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
46
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
47
+ /* @__PURE__ */ jsxs("h3", { className: "flex items-center gap-2 text-sm font-semibold", children: [
48
+ /* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
49
+ "Produtos que v\xE3o de outro jeito"
50
+ ] }),
51
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Um perfil \xE9 um grupo de produtos com zonas pr\xF3prias \u2014 refrigerados, fr\xE1geis, volumosos, sob encomenda. Um perfil com s\xF3 as suas zonas e o gateway desligado \xE9 como se diz \xABisto n\xE3o vai de transportadora\xBB. Produto sem perfil usa o geral, que \xE9 o de todos hoje." })
52
+ ] }),
53
+ /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
54
+ Button,
55
+ {
56
+ variant: "bare",
57
+ size: "bare",
58
+ type: "button",
59
+ onClick: () => adminNavigateTo(shippingRegistryPath(SHIPPING_PROFILES_REGISTRY)),
60
+ className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
61
+ children: [
62
+ /* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
63
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
64
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: profiles == null ? "Carregando perfis\u2026" : custom.length === 0 ? "S\xF3 o perfil geral" : `${custom.length} ${custom.length === 1 ? "perfil" : "perfis"} al\xE9m do geral` }),
65
+ /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
66
+ custom.length === 0 ? "Todo produto entrega pelas mesmas zonas. Crie um perfil quando um grupo precisar de zonas diferentes." : custom.slice(0, 3).map((p) => p.name).join(" \xB7 "),
67
+ custom.length > 3 && ` e ${custom.length - 3} mais`
68
+ ] })
69
+ ] }),
70
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
71
+ ]
72
+ }
73
+ ) }),
74
+ /* @__PURE__ */ jsx(
75
+ SettingsGroup,
76
+ {
77
+ title: "Recado para quem entrega",
78
+ description: "Vai junto com o pedido \u2014 para voc\xEA, para o motoboy, e na observa\xE7\xE3o da etiqueta quando houver gateway. N\xE3o aparece para o cliente, e n\xE3o decide quem pode levar.",
79
+ children: /* @__PURE__ */ jsx(
80
+ TextRow,
81
+ {
82
+ label: "Instru\xE7\xE3o de manuseio",
83
+ description: "Ex.: caixa t\xE9rmica com gelo, entregar em at\xE9 24h.",
84
+ value: note,
85
+ onChange: setNoteDraft,
86
+ onBlurCommit: () => {
87
+ const next = note.trim();
88
+ if (next !== storedNote) set({ handlingNote: next });
89
+ setNoteDraft(null);
90
+ },
91
+ placeholder: "Sem instru\xE7\xE3o",
92
+ disabled: loading
93
+ }
94
+ )
95
+ }
96
+ )
97
+ ] });
98
+ }
99
+ var EMPTY = { rules: [], gaps: [] };
100
+ var draft = EMPTY;
101
+ var listeners = /* @__PURE__ */ new Set();
102
+ var seq = 0;
103
+ var nextId = () => `rule-${++seq}`;
104
+ var identity = (rule) => [rule.matchKind, rule.cityCode ?? "", rule.districtKey ?? "", rule.postalFrom ?? "", rule.postalTo ?? ""].join("|");
105
+ function emit() {
106
+ for (const listener of listeners) listener();
107
+ }
108
+ function getDeliveryDraft() {
109
+ return draft;
110
+ }
111
+ function setDeliveryDraft(next) {
112
+ draft = { ...next, rules: next.rules.map((rule) => ({ ...rule, id: rule.id || nextId() })) };
113
+ emit();
114
+ }
115
+ function addDeliveryDraftRules(incoming) {
116
+ const seen = new Set(draft.rules.map(identity));
117
+ const added = [];
118
+ for (const rule of incoming) {
119
+ const key = identity(rule);
120
+ if (seen.has(key)) continue;
121
+ seen.add(key);
122
+ added.push({ ...rule, id: nextId() });
123
+ }
124
+ if (added.length === 0) return 0;
125
+ draft = { ...draft, rules: [...draft.rules, ...added] };
126
+ emit();
127
+ return added.length;
128
+ }
129
+ function isInDeliveryDraft(rule) {
130
+ const key = identity(rule);
131
+ return draft.rules.some((existing) => identity(existing) === key);
132
+ }
133
+ function setAllDeliveryDraftRates(patch) {
134
+ let touched = 0;
135
+ const rules = draft.rules.map((rule) => {
136
+ if (rule.outcome === "refuse") return rule;
137
+ touched++;
138
+ return { ...rule, ...patch };
139
+ });
140
+ if (touched === 0) return 0;
141
+ draft = { ...draft, rules };
142
+ emit();
143
+ return touched;
144
+ }
145
+ function updateDeliveryDraftRule(id, patch) {
146
+ const index = draft.rules.findIndex((rule) => rule.id === id);
147
+ if (index < 0) return false;
148
+ const rules = draft.rules.slice();
149
+ rules[index] = { ...rules[index], ...patch, id };
150
+ draft = { ...draft, rules };
151
+ emit();
152
+ return true;
153
+ }
154
+ function removeDeliveryDraftRule(id) {
155
+ const rules = draft.rules.filter((rule) => rule.id !== id);
156
+ if (rules.length === draft.rules.length) return false;
157
+ draft = { ...draft, rules };
158
+ emit();
159
+ return true;
160
+ }
161
+ function clearDeliveryDraft() {
162
+ draft = EMPTY;
163
+ emit();
164
+ }
165
+ function subscribeDeliveryDraft(listener) {
166
+ listeners.add(listener);
167
+ return () => {
168
+ listeners.delete(listener);
169
+ };
170
+ }
171
+ function useDeliveryDraft() {
172
+ return React6.useSyncExternalStore(subscribeDeliveryDraft, getDeliveryDraft, getDeliveryDraft);
173
+ }
174
+
175
+ // src/views/delivery-agent.ts
176
+ var DELIVERY_AREAS_INSTRUCTIONS = `
177
+ Esta \xE9 a tela de Entrega da loja. Aqui voc\xEA configura ONDE a loja entrega, ONDE ela recusa e onde precisa confirmar antes.
178
+
179
+ Uma regra tem duas partes: a \xC1REA (um munic\xEDpio ou um bairro) e o DESFECHO:
180
+ \xB7 entrego (deliver) \xB7 confirmo antes (review) \xB7 n\xE3o entrego (refuse)
181
+ Entre as regras que casam com um endere\xE7o, vence a mais estrita. Ent\xE3o "Zona Norte: entrego" + "Mar\xE9: n\xE3o entrego" s\xE3o DUAS regras, e a segunda ganha na Mar\xE9. Nunca tente recortar uma \xE1rea grande em peda\xE7os para abrir exce\xE7\xE3o \u2014 declare a exce\xE7\xE3o.
182
+
183
+ PREFIRA A \xC1REA MAIOR MENOS AS EXCE\xC7\xD5ES. \xC9 assim que o lojista fala, e \xE9 o que a regra mais estrita permite. Quando ele lista v\xE1rias zonas de uma mesma cidade \u2014 "zona norte, zona sul, zona oeste, zona sudoeste" \u2014 isso \xC9 a cidade inteira: proponha UMA regra de munic\xEDpio e as exce\xE7\xF5es, n\xE3o oitenta bairros. Uma whitelist de bairros \xE9 pior de duas formas: ela envelhece (bairro que faltar na lista vira venda recusada em sil\xEAncio) e ningu\xE9m confere oitenta linhas.
184
+
185
+ Quando a lojista colar uma mensagem descrevendo a opera\xE7\xE3o dela:
186
+ 1. Separe cada lugar citado e se ele \xE9 entrega, recusa ou d\xFAvida. "Evitar X", "exceto X" e "n\xE3o entregamos X" s\xE3o recusa.
187
+ 2. Chame \`findDeliveryAreas\` UMA vez com todos os nomes juntos.
188
+ \xB7 \`resolved\` est\xE1 pronto para virar regra.
189
+ \xB7 \`options\` \xE9 amb\xEDguo: pergunte qual, n\xE3o escolha.
190
+ \xB7 \`notFound\` \xE9 o caso importante. Muita comunidade n\xE3o tem bairro nem CEP pr\xF3prio \u2014 divide o do bairro vizinho. N\xC3O invente faixa de CEP para ela. Diga isso em uma linha e ofere\xE7a o que existe: marcar o bairro que a cont\xE9m como "confirmo antes".
191
+ \xB7 \`group\` (Zona Sul, Regi\xE3o dos Lagos) traz \`members\`. Use os membros como regras individuais S\xD3 quando ele citou uma parte da cidade. Se ele citou zonas que somam a cidade toda, jogue os grupos fora e use o munic\xEDpio.
192
+ 3. Chame \`proposeDeliveryRules\` com o que resolveu. Ele N\xC3O grava \u2014 desenha a tabela na tela dela.
193
+ 4. Pre\xE7o e prazo: s\xF3 preencha o que a mensagem disse. Se ela n\xE3o disse, deixe vazio e pergunte UMA vez, junto, no fim: "quanto voc\xEA cobra em cada regi\xE3o, e tem frete gr\xE1tis acima de algum valor?".
194
+ 5. Quando ela confirmar, chame \`commitDeliveryRules\`. Depois teste com \`checkDeliveryFor\` dois ou tr\xEAs CEPs (um que entrega, um que recusa) e relate o resultado em duas linhas.
195
+
196
+ Regras:
197
+ \xB7 Voc\xEA NUNCA escreve um CEP ou um c\xF3digo de munic\xEDpio que n\xE3o tenha vindo de \`findDeliveryAreas\`.
198
+ \xB7 N\xC3O proponha faixa de CEP. Faixa \xE9 como transportadora precifica, n\xE3o como uma pessoa descreve onde entrega, e ningu\xE9m no mercado pede isso ao lojista. Ela existe s\xF3 para quando a lojista estiver COPIANDO uma tabela de transportadora e disser os n\xFAmeros ela mesma. Se voc\xEA n\xE3o achar a \xE1rea pelo nome, pergunte \u2014 n\xE3o caia na faixa.
199
+ \xB7 Comunidade que n\xE3o resolve pelo nome (Chapad\xE3o \xE9 o caso) n\xE3o vira recusa inventada: ofere\xE7a marcar o bairro que a cont\xE9m como "confirmo antes", que aceita o pedido e deixa a decis\xE3o com ele.
200
+ \xB7 N\xE3o imprima a tabela inteira no texto: ela est\xE1 na tela. Diga quantas regras entraram, quantas s\xE3o recusa, e o que ficou em aberto.
201
+ \xB7 Portugu\xEAs do Brasil, curto.
202
+ `.trim();
203
+ var FIND_AREAS = {
204
+ id: "shop.delivery.find-areas",
205
+ name: "findDeliveryAreas",
206
+ description: 'Resolves place names the merchant wrote ("Angra", "Zona Sul", "Barra da Tijuca", "Itacuru\xE7\xE1") into real municipalities (IBGE code), neighbourhoods, or curated groups that expand into several of them. Send EVERY name of the message in one call. This is the ONLY source of a municipality code or a neighbourhood key \u2014 never write one that did not come from here. A name that answers `notFound` is a question for the merchant, not a rule to invent.',
207
+ icon: "MapPin",
208
+ mode: "read",
209
+ execution: { plane: "client" },
210
+ category: "Shop",
211
+ role: "operations",
212
+ parameters: {
213
+ type: "object",
214
+ properties: {
215
+ queries: {
216
+ type: "array",
217
+ description: 'The place names as the merchant wrote them, without "n\xE3o entrego" / "exceto".',
218
+ items: { type: "string" }
219
+ }
220
+ },
221
+ required: ["queries"]
222
+ }
223
+ };
224
+ var PROPOSE_RULES = {
225
+ id: "shop.delivery.propose-rules",
226
+ name: "proposeDeliveryRules",
227
+ description: "Draws the delivery rules the message describes as an EDITABLE TABLE on the merchant's screen. Writes nothing. Every area must carry the ids findDeliveryAreas answered. A group is expanded into one rule per member here, so the merchant sees and trims what it covers. Leave rate and ETA out when the message did not state them.",
228
+ icon: "ListChecks",
229
+ mode: "read",
230
+ execution: { plane: "client" },
231
+ category: "Shop",
232
+ role: "operations",
233
+ parameters: {
234
+ type: "object",
235
+ properties: {
236
+ rules: {
237
+ type: "array",
238
+ description: "One entry per area named in the message.",
239
+ items: {
240
+ type: "object",
241
+ properties: {
242
+ kind: {
243
+ type: "string",
244
+ enum: ["city", "district", "group", "postal_range"],
245
+ description: "The `kind` findDeliveryAreas answered. Use postal_range ONLY when the merchant is copying a carrier freight table and dictated the numbers \u2014 never to describe where they deliver."
246
+ },
247
+ label: { type: "string", description: "The place name, as it should read on the table." },
248
+ cityCode: { type: "string", description: "IBGE code from findDeliveryAreas." },
249
+ districtKey: { type: "string", description: "Neighbourhood key from findDeliveryAreas." },
250
+ postalFrom: { type: "string", description: "Only for postal_range." },
251
+ postalTo: { type: "string", description: "Only for postal_range." },
252
+ outcome: {
253
+ type: "string",
254
+ enum: ["deliver", "review", "refuse"],
255
+ description: "deliver | review (confirm before dispatch) | refuse."
256
+ },
257
+ rate: { type: "number", description: "Only if the message stated a price. Otherwise omit." },
258
+ freeAbove: { type: "number", description: "Only if the message stated a threshold." },
259
+ etaMinDays: { type: "number" },
260
+ etaMaxDays: { type: "number" },
261
+ source: { type: "string", description: "The words of the message this rule came from." }
262
+ },
263
+ required: ["kind", "label", "outcome"]
264
+ }
265
+ },
266
+ gaps: {
267
+ type: "array",
268
+ description: "What the message named and you could NOT resolve, so the merchant sees it too.",
269
+ items: {
270
+ type: "object",
271
+ properties: {
272
+ query: { type: "string" },
273
+ reason: { type: "string", description: "Plain Portuguese, one line." }
274
+ },
275
+ required: ["query", "reason"]
276
+ }
277
+ }
278
+ },
279
+ required: ["rules"]
280
+ }
281
+ };
282
+ var SET_RULE = {
283
+ id: "shop.delivery.set-rule",
284
+ name: "setDeliveryRule",
285
+ description: "Corrects one line of the draft table already on screen \u2014 its outcome, price, free-above or ETA. Use when the merchant answers a question about the rules you proposed, instead of rebuilding the whole draft.",
286
+ icon: "Pencil",
287
+ mode: "read",
288
+ execution: { plane: "client" },
289
+ category: "Shop",
290
+ role: "operations",
291
+ parameters: {
292
+ type: "object",
293
+ properties: {
294
+ id: { type: "string", description: "The rule id proposeDeliveryRules answered." },
295
+ outcome: { type: "string", enum: ["deliver", "review", "refuse"] },
296
+ rate: { type: "number" },
297
+ freeAbove: { type: "number" },
298
+ etaMinDays: { type: "number" },
299
+ etaMaxDays: { type: "number" }
300
+ },
301
+ required: ["id"]
302
+ }
303
+ };
304
+ var COMMIT_RULES = {
305
+ id: "shop.delivery.commit-rules",
306
+ name: "commitDeliveryRules",
307
+ description: "Saves the draft table as the store's delivery rules. Call ONLY after the merchant said the table is right. It changes who can buy: an address outside every rule cannot check out, and a refused one is blocked server-side. Saves the draft as it stands on screen \u2014 the merchant may have edited it, and those edits win.",
308
+ icon: "Save",
309
+ mode: "persist",
310
+ execution: { plane: "client" },
311
+ category: "Shop",
312
+ role: "operations",
313
+ permission: { feature: "shop", action: "update" },
314
+ parameters: { type: "object", properties: {} }
315
+ };
316
+ var CHECK_DELIVERY = {
317
+ id: "shop.delivery.check",
318
+ name: "checkDeliveryFor",
319
+ description: 'Answers what the store does with a given postcode right now: deliver, confirm first, refuse, or outside every rule. Use it to CHECK YOUR OWN WORK after saving, and to answer "why could this customer not buy?".',
320
+ icon: "Search",
321
+ mode: "read",
322
+ execution: { plane: "client" },
323
+ category: "Shop",
324
+ role: "operations",
325
+ parameters: {
326
+ type: "object",
327
+ properties: {
328
+ postalCodes: {
329
+ type: "array",
330
+ description: "One or more CEPs, with or without punctuation.",
331
+ items: { type: "string" }
332
+ }
333
+ },
334
+ required: ["postalCodes"]
335
+ }
336
+ };
337
+ [
338
+ FIND_AREAS.id,
339
+ PROPOSE_RULES.id,
340
+ SET_RULE.id,
341
+ COMMIT_RULES.id,
342
+ CHECK_DELIVERY.id
343
+ ];
344
+ var text = (value) => typeof value === "string" ? value.trim() : "";
345
+ var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
346
+ var OUTCOMES = ["deliver", "review", "refuse"];
347
+ var outcomeOf = (value) => OUTCOMES.includes(value) ? value : "deliver";
348
+ function candidateOut(area) {
349
+ return {
350
+ kind: area.kind,
351
+ label: area.label,
352
+ cityCode: area.cityCode ?? void 0,
353
+ cityName: area.cityName ?? void 0,
354
+ state: area.state ?? void 0,
355
+ districtKey: area.districtKey ?? void 0,
356
+ knownPostalCodes: area.knownPostalCodes || void 0,
357
+ members: area.members?.map((m) => ({
358
+ kind: m.kind,
359
+ label: m.label,
360
+ cityCode: m.cityCode ?? void 0,
361
+ districtKey: m.districtKey ?? void 0
362
+ }))
363
+ };
364
+ }
365
+ function pickCandidate(query, areas) {
366
+ if (areas.length === 0) return {};
367
+ if (areas.length === 1) return { resolved: areas[0] };
368
+ const key = query.trim().toLowerCase();
369
+ const exact = areas.filter((a) => a.label.trim().toLowerCase() === key);
370
+ if (exact.length === 1) return { resolved: exact[0] };
371
+ return { options: areas.slice(0, 6) };
372
+ }
373
+ function ruleFromArgs(raw, index) {
374
+ const kind = text(raw.kind);
375
+ const label = text(raw.label);
376
+ if (!label) return null;
377
+ const base = {
378
+ id: `draft-${index}`,
379
+ label,
380
+ outcome: outcomeOf(raw.outcome),
381
+ rate: number(raw.rate),
382
+ freeAbove: number(raw.freeAbove),
383
+ etaMinDays: number(raw.etaMinDays),
384
+ etaMaxDays: number(raw.etaMaxDays),
385
+ source: text(raw.source) || void 0
386
+ };
387
+ if (kind === "city") {
388
+ const cityCode = text(raw.cityCode);
389
+ if (!cityCode) return null;
390
+ return { ...base, matchKind: "city", cityCode };
391
+ }
392
+ if (kind === "district") {
393
+ const districtKey = text(raw.districtKey);
394
+ if (!districtKey) return null;
395
+ return {
396
+ ...base,
397
+ matchKind: "district",
398
+ districtKey,
399
+ cityCode: text(raw.cityCode) || null
400
+ };
401
+ }
402
+ if (kind === "postal_range") {
403
+ const from = text(raw.postalFrom).replace(/\D/g, "");
404
+ const to = text(raw.postalTo).replace(/\D/g, "");
405
+ if (from.length !== 8 || to.length !== 8) return null;
406
+ return { ...base, matchKind: "postal_range", postalFrom: from, postalTo: to };
407
+ }
408
+ return null;
409
+ }
410
+ function zoneInputFrom(rule) {
411
+ return {
412
+ name: rule.label,
413
+ carrier: null,
414
+ matchKind: rule.matchKind,
415
+ cityCode: rule.cityCode ?? null,
416
+ districtKey: rule.districtKey ?? null,
417
+ outcome: rule.outcome,
418
+ postalFrom: rule.postalFrom ?? null,
419
+ postalTo: rule.postalTo ?? null,
420
+ // Uma regra de recusa não cobra nada: o preço dela nunca é lido.
421
+ rate: rule.outcome === "refuse" ? 0 : rule.rate ?? 0,
422
+ freeAbove: rule.freeAbove ?? null,
423
+ etaMinDays: rule.etaMinDays ?? null,
424
+ etaMaxDays: rule.etaMaxDays ?? null,
425
+ active: true,
426
+ sortOrder: 0,
427
+ excluded: rule.outcome === "refuse"
428
+ };
429
+ }
430
+ function createDeliveryAreaTools({ getProvider, onSaved }) {
431
+ const failed = (error) => ({ ok: false, error: errorMessage(error) });
432
+ return [
433
+ {
434
+ tool: FIND_AREAS,
435
+ execute: async (args) => {
436
+ const queries = (Array.isArray(args.queries) ? args.queries : []).map(text).filter(Boolean).slice(0, 30);
437
+ if (queries.length === 0) return { ok: false, error: "no_queries" };
438
+ try {
439
+ const shop = getProvider();
440
+ if (typeof shop.findAreas !== "function") {
441
+ return { ok: false, error: "area_lookup_unavailable" };
442
+ }
443
+ const results = await Promise.all(
444
+ queries.map(async (query) => {
445
+ const areas = await shop.findAreas(query, 8).catch(() => []);
446
+ const { resolved, options } = pickCandidate(query, areas);
447
+ if (resolved) return { query, resolved: candidateOut(resolved) };
448
+ if (options) return { query, options: options.map(candidateOut) };
449
+ return {
450
+ query,
451
+ notFound: true,
452
+ // Dito aqui e não deixado para o modelo deduzir: é a frase que
453
+ // separa "não existe" de "existe mas não dá para separar por CEP".
454
+ hint: "Sem munic\xEDpio, bairro ou grupo com esse nome. Comunidade normalmente n\xE3o tem CEP pr\xF3prio \u2014 divide o do bairro vizinho. Pergunte \xE0 lojista qual bairro a cont\xE9m."
455
+ };
456
+ })
457
+ );
458
+ return { ok: true, results };
459
+ } catch (error) {
460
+ return failed(error);
461
+ }
462
+ }
463
+ },
464
+ {
465
+ tool: PROPOSE_RULES,
466
+ execute: async (args) => {
467
+ const raw = Array.isArray(args.rules) ? args.rules : [];
468
+ const rules = [];
469
+ const gaps = (Array.isArray(args.gaps) ? args.gaps : []).map((g) => ({ query: text(g?.query), reason: text(g?.reason) })).filter((g) => g.query);
470
+ raw.forEach((entry, index) => {
471
+ const rule = ruleFromArgs(entry ?? {}, rules.length);
472
+ if (rule) {
473
+ rules.push(rule);
474
+ return;
475
+ }
476
+ gaps.push({
477
+ query: text(entry?.label) || `regra ${index + 1}`,
478
+ reason: "Faltou o identificador da \xE1rea. Chame findDeliveryAreas para este nome e use o que ele responder."
479
+ });
480
+ });
481
+ if (rules.length === 0 && gaps.length === 0) return { ok: false, error: "empty_draft" };
482
+ setDeliveryDraft({ rules, gaps });
483
+ return {
484
+ ok: true,
485
+ drafted: rules.length,
486
+ refusing: rules.filter((r) => r.outcome === "refuse").length,
487
+ reviewing: rules.filter((r) => r.outcome === "review").length,
488
+ missingPrice: rules.filter((r) => r.outcome !== "refuse" && r.rate == null).length,
489
+ gaps,
490
+ rules: rules.map((r) => ({ id: r.id, label: r.label, outcome: r.outcome })),
491
+ nextStep: "commitDeliveryRules"
492
+ };
493
+ }
494
+ },
495
+ {
496
+ tool: SET_RULE,
497
+ execute: async (args) => {
498
+ const id = text(args.id);
499
+ if (!id) return { ok: false, error: "no_id" };
500
+ const patch = {};
501
+ if (args.outcome !== void 0) patch.outcome = outcomeOf(args.outcome);
502
+ if (args.rate !== void 0) patch.rate = number(args.rate);
503
+ if (args.freeAbove !== void 0) patch.freeAbove = number(args.freeAbove);
504
+ if (args.etaMinDays !== void 0) patch.etaMinDays = number(args.etaMinDays);
505
+ if (args.etaMaxDays !== void 0) patch.etaMaxDays = number(args.etaMaxDays);
506
+ if (Object.keys(patch).length === 0) return { ok: false, error: "nothing_to_change" };
507
+ if (!updateDeliveryDraftRule(id, patch)) return { ok: false, error: "rule_not_in_draft" };
508
+ return { ok: true, id, ...patch };
509
+ }
510
+ },
511
+ {
512
+ tool: COMMIT_RULES,
513
+ execute: async () => {
514
+ const { rules } = getDeliveryDraft();
515
+ if (rules.length === 0) return { ok: false, error: "nothing_to_save" };
516
+ try {
517
+ const shop = getProvider();
518
+ if (typeof shop.createShippingZone !== "function") {
519
+ return { ok: false, error: "zones_unavailable" };
520
+ }
521
+ const saved = [];
522
+ const rejected = [];
523
+ for (const rule of rules) {
524
+ try {
525
+ await shop.createShippingZone(zoneInputFrom(rule));
526
+ saved.push(rule.label);
527
+ } catch (error) {
528
+ rejected.push({ label: rule.label, error: errorMessage(error) });
529
+ }
530
+ }
531
+ if (saved.length > 0) {
532
+ clearDeliveryDraft();
533
+ onSaved();
534
+ }
535
+ return { ok: rejected.length === 0, saved: saved.length, rejected };
536
+ } catch (error) {
537
+ return failed(error);
538
+ }
539
+ }
540
+ },
541
+ {
542
+ tool: CHECK_DELIVERY,
543
+ execute: async (args) => {
544
+ const codes = (Array.isArray(args.postalCodes) ? args.postalCodes : []).map((c) => text(c).replace(/\D/g, "")).filter((c) => c.length === 8).slice(0, 10);
545
+ if (codes.length === 0) return { ok: false, error: "no_postal_codes" };
546
+ try {
547
+ const shop = getProvider();
548
+ if (typeof shop.checkDelivery !== "function") {
549
+ return { ok: false, error: "delivery_check_unavailable" };
550
+ }
551
+ const results = await Promise.all(
552
+ codes.map(async (code) => {
553
+ const check = await shop.checkDelivery(code);
554
+ return {
555
+ postalCode: code,
556
+ // null = nenhuma regra casou: fora de área, que não é o mesmo
557
+ // que recusado e o texto para a lojista é outro.
558
+ outcome: check.outcome ?? null,
559
+ rule: check.zoneName ?? check.refusedZone ?? null,
560
+ outsideEveryRule: !check.refused && !check.ownZone
561
+ };
562
+ })
563
+ );
564
+ return { ok: true, results };
565
+ } catch (error) {
566
+ return failed(error);
567
+ }
568
+ }
569
+ }
570
+ ];
571
+ }
572
+
573
+ // src/views/DeliveryAgentSurface.tsx
574
+ function DeliveryAgentSurface({ provider, zones, onSaved }) {
575
+ const live = React6.useRef({ provider, zones, onSaved });
576
+ live.current = { provider, zones, onSaved };
577
+ const contribution = React6.useMemo(() => ({
578
+ id: "shop.delivery.settings",
579
+ pluginId: "shop",
580
+ role: "operations",
581
+ instructions: DELIVERY_AREAS_INSTRUCTIONS,
582
+ describe: () => {
583
+ const { zones: rows } = live.current;
584
+ const draft2 = getDeliveryDraft();
585
+ const byOutcome = (outcome) => rows.filter((z) => z.active && (z.outcome ?? (z.excluded ? "refuse" : "deliver")) === outcome);
586
+ return {
587
+ title: "Configura\xE7\xE3o \u2014 Entrega",
588
+ state: {
589
+ screen: "/settings/shop-shipping",
590
+ savedRules: {
591
+ delivering: byOutcome("deliver").length,
592
+ reviewing: byOutcome("review").length,
593
+ refusing: byOutcome("refuse").length,
594
+ // Os nomes, não as faixas: o agente precisa saber o que JÁ existe
595
+ // para não propor a mesma região duas vezes.
596
+ names: rows.filter((z) => z.active).slice(0, 40).map((z) => z.name)
597
+ },
598
+ draft: draft2.rules.length === 0 && draft2.gaps.length === 0 ? null : {
599
+ rules: draft2.rules.map((r) => ({
600
+ id: r.id,
601
+ label: r.label,
602
+ outcome: r.outcome,
603
+ rate: r.rate ?? null,
604
+ freeAbove: r.freeAbove ?? null
605
+ })),
606
+ gaps: draft2.gaps
607
+ }
608
+ }
609
+ };
610
+ },
611
+ tools: createDeliveryAreaTools({
612
+ getProvider: () => live.current.provider(),
613
+ onSaved: () => live.current.onSaved()
614
+ })
615
+ }), []);
616
+ useAgentSurface(contribution);
617
+ return null;
618
+ }
619
+ var MIN_QUERY = 2;
620
+ var DEBOUNCE_MS = 250;
621
+ function ruleFor(area, outcome) {
622
+ if (area.kind === "city" && area.cityCode) {
623
+ return { matchKind: "city", label: area.label, cityCode: area.cityCode, outcome };
624
+ }
625
+ if (area.kind === "district" && area.districtKey) {
626
+ return {
627
+ matchKind: "district",
628
+ label: area.label,
629
+ districtKey: area.districtKey,
630
+ cityCode: area.cityCode ?? null,
631
+ outcome
632
+ };
633
+ }
634
+ return null;
635
+ }
636
+ function rulesFor(area, outcome) {
637
+ if (area.kind !== "group") {
638
+ const single = ruleFor(area, outcome);
639
+ return single ? [single] : [];
640
+ }
641
+ return (area.members ?? []).flatMap((member) => {
642
+ const rule = ruleFor(
643
+ { kind: member.kind, label: member.label, cityCode: member.cityCode, districtKey: member.districtKey },
644
+ outcome
645
+ );
646
+ return rule ? [rule] : [];
647
+ });
648
+ }
649
+ function areaMeta(area) {
650
+ if (area.matchedAs) return area.matchedAs;
651
+ if (area.kind === "group") {
652
+ const n = area.members?.length ?? 0;
653
+ return `${n} ${n === 1 ? "regi\xE3o" : "regi\xF5es"}${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
654
+ }
655
+ if (area.kind === "city") return `Cidade inteira${area.state ? ` \xB7 ${area.state}` : ""}`;
656
+ return `Bairro${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
657
+ }
658
+ var areaKeyOf = (area) => `${area.kind}:${area.districtKey ?? area.cityCode ?? area.label}`;
659
+ function AreaSearch({ provider, outcome, placeholder, onlyCityCodes, emptyHint }) {
660
+ const [query, setQuery] = React6__default.useState("");
661
+ const [results, setResults] = React6__default.useState(null);
662
+ const [loading, setLoading] = React6__default.useState(false);
663
+ const [unavailable, setUnavailable] = React6__default.useState(false);
664
+ const [justAdded, setJustAdded] = React6__default.useState(null);
665
+ React6__default.useEffect(() => {
666
+ const text2 = query.trim();
667
+ if (text2.length < MIN_QUERY) {
668
+ setResults(null);
669
+ return void 0;
670
+ }
671
+ let cancelled = false;
672
+ setLoading(true);
673
+ const timer = setTimeout(() => {
674
+ const shop = provider ? resolve(provider) : getShopProvider();
675
+ if (typeof shop.findAreas !== "function") {
676
+ if (!cancelled) {
677
+ setUnavailable(true);
678
+ setLoading(false);
679
+ }
680
+ return;
681
+ }
682
+ void shop.findAreas(text2, 10).then((list) => {
683
+ if (!cancelled) {
684
+ setResults(list);
685
+ setUnavailable(false);
686
+ }
687
+ }).catch(() => {
688
+ if (!cancelled) {
689
+ setResults([]);
690
+ setUnavailable(true);
691
+ }
692
+ }).finally(() => {
693
+ if (!cancelled) setLoading(false);
694
+ });
695
+ }, DEBOUNCE_MS);
696
+ return () => {
697
+ cancelled = true;
698
+ clearTimeout(timer);
699
+ };
700
+ }, [query, provider]);
701
+ const add = (area) => {
702
+ const rules = rulesFor(area, outcome);
703
+ if (rules.length === 0) return;
704
+ const added = addDeliveryDraftRules(rules);
705
+ setJustAdded(areaKeyOf(area));
706
+ setTimeout(() => setJustAdded(null), 1400);
707
+ if (added > 0) setQuery("");
708
+ };
709
+ const visible = (results ?? []).filter((area) => {
710
+ if (!onlyCityCodes || onlyCityCodes.length === 0) return true;
711
+ if (area.kind === "city") return true;
712
+ return !area.cityCode || onlyCityCodes.includes(area.cityCode);
713
+ });
714
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
715
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
716
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
717
+ /* @__PURE__ */ jsx(
718
+ Input,
719
+ {
720
+ value: query,
721
+ onChange: (e) => setQuery(e.target.value),
722
+ placeholder,
723
+ className: "h-auto rounded-lg border-border bg-background py-2 pl-9 pr-9"
724
+ }
725
+ ),
726
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
727
+ ] }),
728
+ results !== null && /* @__PURE__ */ jsxs("ul", { className: "mt-2 max-h-72 divide-y divide-border overflow-y-auto rounded-lg border border-border", children: [
729
+ visible.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-6 text-center text-xs text-muted-foreground", children: unavailable ? "A busca de regi\xF5es ainda n\xE3o est\xE1 dispon\xEDvel neste banco." : emptyHint }),
730
+ visible.map((area) => {
731
+ const key = areaKeyOf(area);
732
+ const already = area.kind !== "group" && isInDeliveryDraft({
733
+ matchKind: area.kind === "city" ? "city" : "district",
734
+ cityCode: area.cityCode ?? null,
735
+ districtKey: area.districtKey ?? null
736
+ });
737
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
738
+ Button,
739
+ {
740
+ variant: "bare",
741
+ size: "bare",
742
+ type: "button",
743
+ onClick: () => add(area),
744
+ disabled: already,
745
+ className: "font-normal justify-start rounded-none whitespace-normal flex w-full items-center gap-3 px-3 py-2.5 text-left transition hover:bg-muted/50 disabled:opacity-50",
746
+ children: [
747
+ area.kind === "city" ? /* @__PURE__ */ jsx(Building2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
748
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
749
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium", children: area.label }),
750
+ /* @__PURE__ */ jsxs("span", { className: "block text-xs text-muted-foreground", children: [
751
+ areaMeta(area),
752
+ area.kind === "district" && !area.knownPostalCodes && " \xB7 ainda sem CEP visto"
753
+ ] })
754
+ ] }),
755
+ justAdded === key ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-emerald-600" }) : already ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: "j\xE1 escolhido" }) : /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
756
+ ]
757
+ }
758
+ ) }, key);
759
+ })
760
+ ] })
761
+ ] });
762
+ }
763
+ function DeliveryAreaPicker({ provider, onAskAssistant }) {
764
+ const draft2 = useDeliveryDraft();
765
+ const covered = draft2.rules.filter((r) => r.outcome !== "refuse");
766
+ const coveredCities = covered.filter((r) => r.matchKind === "city");
767
+ const exceptions = draft2.rules.filter((r) => r.outcome === "refuse");
768
+ const cityCodes = covered.map((r) => r.cityCode).filter(Boolean);
769
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card px-5 py-4 shadow-sm", children: [
770
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
771
+ /* @__PURE__ */ jsxs("div", { children: [
772
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega?" }),
773
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: 'Digite uma cidade, um bairro ou uma regi\xE3o \u2014 "Rio de Janeiro", "Ipanema", "Zona Sul". Se voc\xEA atende a cidade toda, uma linha s\xF3 resolve: escolha a cidade.' })
774
+ ] }),
775
+ onAskAssistant && /* @__PURE__ */ jsxs(
776
+ Button,
777
+ {
778
+ variant: "bare",
779
+ size: "bare",
780
+ type: "button",
781
+ onClick: onAskAssistant,
782
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium hover:bg-muted",
783
+ children: [
784
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-primary" }),
785
+ "Prefiro contar por escrito"
786
+ ]
787
+ }
788
+ )
789
+ ] }),
790
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
791
+ AreaSearch,
792
+ {
793
+ provider,
794
+ outcome: "deliver",
795
+ placeholder: "Cidade, bairro ou regi\xE3o\u2026",
796
+ emptyHint: "Nada com esse nome. Tente a cidade, ou conte por escrito ao assistente."
797
+ }
798
+ ) }),
799
+ covered.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-5 border-t border-border pt-4", children: [
800
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
801
+ /* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 text-destructive" }),
802
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "E onde voc\xEA n\xE3o entrega?" })
803
+ ] }),
804
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: coveredCities.length > 0 ? `Recortes dentro do que voc\xEA j\xE1 cobre \u2014 comunidade, bairro de risco, regi\xE3o distante. A exce\xE7\xE3o vence a \xE1rea maior, ent\xE3o ${coveredCities[0].label} continua valendo em todo o resto.` : "Regi\xF5es que voc\xEA quer recusar mesmo estando perto do que j\xE1 cobre. A exce\xE7\xE3o vence a \xE1rea maior." }),
805
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
806
+ AreaSearch,
807
+ {
808
+ provider,
809
+ outcome: "refuse",
810
+ placeholder: "Bairro ou regi\xE3o que voc\xEA recusa\u2026",
811
+ onlyCityCodes: cityCodes,
812
+ emptyHint: /* @__PURE__ */ jsx(Fragment, { children: "Nada com esse nome. Comunidade normalmente n\xE3o tem CEP pr\xF3prio \u2014 divide o do bairro vizinho. Escolha o bairro que a cont\xE9m e marque como \u201Cconfirmo antes\u201D na tabela." })
813
+ }
814
+ ) })
815
+ ] }),
816
+ draft2.rules.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs text-muted-foreground", children: [
817
+ covered.length,
818
+ " ",
819
+ covered.length === 1 ? "regi\xE3o" : "regi\xF5es",
820
+ " onde voc\xEA entrega",
821
+ exceptions.length > 0 && `, ${exceptions.length} ${exceptions.length === 1 ? "exce\xE7\xE3o" : "exce\xE7\xF5es"}`,
822
+ " ",
823
+ "\u2014 confira o pre\xE7o na tabela abaixo e salve."
824
+ ] })
825
+ ] });
826
+ }
827
+ var OUTCOME_LABEL = {
828
+ deliver: "Entrego",
829
+ review: "Confirmo antes",
830
+ refuse: "N\xE3o entrego"
831
+ };
832
+ var OUTCOME_TONE = {
833
+ deliver: "text-foreground",
834
+ review: "text-amber-600 dark:text-amber-500",
835
+ refuse: "text-destructive"
836
+ };
837
+ function parseMoney(raw) {
838
+ const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
839
+ if (!cleaned.trim()) return null;
840
+ const n = Number(cleaned);
841
+ return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
842
+ }
843
+ var money = (v) => v == null ? "" : String(v).replace(".", ",");
844
+ var BULK_INPUT = "h-auto rounded-md border-border bg-background px-2 py-1 text-xs shadow-none";
845
+ var FIELD_FRAME = "inline-flex items-center rounded-md border border-border bg-background focus-within:ring-1 focus-within:ring-ring";
846
+ var FRAMED_INPUT = "h-auto rounded-none border-0 bg-transparent py-1.5 text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0";
847
+ function ruleArea(rule) {
848
+ if (rule.matchKind === "postal_range") {
849
+ const fmt = (c) => c ? `${c.slice(0, 5)}-${c.slice(5)}` : "?";
850
+ return `${fmt(rule.postalFrom)} \u2192 ${fmt(rule.postalTo)}`;
851
+ }
852
+ return rule.matchKind === "city" ? "Munic\xEDpio" : "Bairro";
853
+ }
854
+ function DeliveryRulesDraft({ provider, onSaved }) {
855
+ const draft2 = useDeliveryDraft();
856
+ const [saving, setSaving] = React6__default.useState(false);
857
+ const [error, setError] = React6__default.useState(null);
858
+ const [bulkRate, setBulkRate] = React6__default.useState("");
859
+ const [bulkFree, setBulkFree] = React6__default.useState("");
860
+ if (draft2.rules.length === 0 && draft2.gaps.length === 0) return null;
861
+ const refusing = draft2.rules.filter((r) => r.outcome === "refuse").length;
862
+ const reviewing = draft2.rules.filter((r) => r.outcome === "review").length;
863
+ const delivering = draft2.rules.length - refusing - reviewing;
864
+ const columns = [
865
+ {
866
+ id: "region",
867
+ header: "Regi\xE3o",
868
+ meta: { className: "px-5 py-2.5" },
869
+ cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsxs(Fragment, { children: [
870
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: rule.label }),
871
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: ruleArea(rule) }),
872
+ rule.source && /* @__PURE__ */ jsxs("p", { className: "text-xs italic text-muted-foreground", children: [
873
+ "\u201C",
874
+ rule.source,
875
+ "\u201D"
876
+ ] })
877
+ ] })
878
+ },
879
+ {
880
+ id: "outcome",
881
+ header: "O que fa\xE7o",
882
+ meta: { className: "px-2 py-2.5" },
883
+ cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsxs(
884
+ Select,
885
+ {
886
+ value: rule.outcome,
887
+ onValueChange: (v) => updateDeliveryDraftRule(rule.id, { outcome: v }),
888
+ children: [
889
+ /* @__PURE__ */ jsx(SelectTrigger, { className: `h-auto w-auto gap-1.5 rounded-md border-border bg-background px-2 py-1.5 text-xs shadow-none ${OUTCOME_TONE[rule.outcome]}`, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
890
+ /* @__PURE__ */ jsx(SelectContent, { children: Object.keys(OUTCOME_LABEL).map((value) => /* @__PURE__ */ jsx(SelectItem, { value, children: OUTCOME_LABEL[value] }, value)) })
891
+ ]
892
+ }
893
+ )
894
+ },
895
+ // Recusa não cobra: mostrar campo de preço numa linha que nunca o lê
896
+ // convida a lojista a preencher e a achar que cobrou.
897
+ {
898
+ id: "rate",
899
+ header: "Frete",
900
+ meta: { className: "px-2 py-2.5" },
901
+ cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "n\xE3o se cobra" }) : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
902
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
903
+ /* @__PURE__ */ jsx("div", { className: "w-16", children: /* @__PURE__ */ jsx(
904
+ Input,
905
+ {
906
+ value: money(rule.rate),
907
+ placeholder: "0,00",
908
+ "aria-label": `Frete para ${rule.label}`,
909
+ onChange: (e) => updateDeliveryDraftRule(rule.id, { rate: parseMoney(e.target.value) }),
910
+ className: `${FRAMED_INPUT} px-1.5`
911
+ }
912
+ ) })
913
+ ] })
914
+ },
915
+ {
916
+ id: "freeAbove",
917
+ header: "Gr\xE1tis acima de",
918
+ meta: { className: "px-2 py-2.5" },
919
+ cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? null : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
920
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
921
+ /* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(
922
+ Input,
923
+ {
924
+ value: money(rule.freeAbove),
925
+ placeholder: "nunca",
926
+ "aria-label": `Frete gr\xE1tis acima de, em ${rule.label}`,
927
+ onChange: (e) => updateDeliveryDraftRule(rule.id, { freeAbove: parseMoney(e.target.value) }),
928
+ className: `${FRAMED_INPUT} px-1.5`
929
+ }
930
+ ) })
931
+ ] })
932
+ },
933
+ {
934
+ id: "eta",
935
+ header: "Prazo (dias)",
936
+ meta: { className: "px-2 py-2.5" },
937
+ cell: ({ row: { original: rule } }) => rule.outcome === "refuse" ? null : /* @__PURE__ */ jsxs("span", { className: FIELD_FRAME, children: [
938
+ /* @__PURE__ */ jsx("div", { className: "w-10", children: /* @__PURE__ */ jsx(
939
+ Input,
940
+ {
941
+ value: rule.etaMinDays ?? "",
942
+ placeholder: "0",
943
+ inputMode: "numeric",
944
+ "aria-label": `Prazo m\xEDnimo em ${rule.label}`,
945
+ onChange: (e) => updateDeliveryDraftRule(rule.id, {
946
+ etaMinDays: e.target.value ? Number(e.target.value) : null
947
+ }),
948
+ className: `${FRAMED_INPUT} pl-2 pr-0 text-center`
949
+ }
950
+ ) }),
951
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "a" }),
952
+ /* @__PURE__ */ jsx("div", { className: "w-10", children: /* @__PURE__ */ jsx(
953
+ Input,
954
+ {
955
+ value: rule.etaMaxDays ?? "",
956
+ placeholder: "2",
957
+ inputMode: "numeric",
958
+ "aria-label": `Prazo m\xE1ximo em ${rule.label}`,
959
+ onChange: (e) => updateDeliveryDraftRule(rule.id, {
960
+ etaMaxDays: e.target.value ? Number(e.target.value) : null
961
+ }),
962
+ className: `${FRAMED_INPUT} pl-0 pr-2 text-center`
963
+ }
964
+ ) })
965
+ ] })
966
+ },
967
+ {
968
+ id: "remove",
969
+ header: "",
970
+ meta: { className: "px-5 py-2.5 text-right" },
971
+ cell: ({ row: { original: rule } }) => /* @__PURE__ */ jsx(
972
+ Button,
973
+ {
974
+ variant: "bare",
975
+ size: "bare",
976
+ type: "button",
977
+ onClick: () => removeDeliveryDraftRule(rule.id),
978
+ "aria-label": `Remover ${rule.label}`,
979
+ className: "rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-destructive",
980
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
981
+ }
982
+ )
983
+ }
984
+ ];
985
+ const save = async () => {
986
+ setSaving(true);
987
+ setError(null);
988
+ try {
989
+ const shop = provider ? resolve(provider) : getShopProvider();
990
+ if (typeof shop.createShippingZone !== "function") {
991
+ throw new Error("Este banco ainda n\xE3o tem as regras de entrega.");
992
+ }
993
+ for (const rule of draft2.rules) {
994
+ await shop.createShippingZone(zoneInputFrom(rule));
995
+ }
996
+ clearDeliveryDraft();
997
+ onSaved();
998
+ } catch (err) {
999
+ setError(errorMessage(err));
1000
+ } finally {
1001
+ setSaving(false);
1002
+ }
1003
+ };
1004
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-primary/40 bg-primary/5 shadow-sm", children: [
1005
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b border-primary/20 px-5 py-4", children: [
1006
+ /* @__PURE__ */ jsx(ListChecks, { className: "h-4 w-4 text-primary" }),
1007
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
1008
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Regras a salvar" }),
1009
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
1010
+ delivering,
1011
+ " entrega",
1012
+ delivering === 1 ? "" : "s",
1013
+ reviewing > 0 && `, ${reviewing} a confirmar`,
1014
+ refusing > 0 && `, ${refusing} recusa${refusing === 1 ? "" : "s"}`,
1015
+ " ",
1016
+ "\u2014 confira e corrija antes de salvar. Nada foi gravado ainda."
1017
+ ] })
1018
+ ] }),
1019
+ /* @__PURE__ */ jsx(
1020
+ Button,
1021
+ {
1022
+ variant: "bare",
1023
+ size: "bare",
1024
+ type: "button",
1025
+ onClick: clearDeliveryDraft,
1026
+ disabled: saving,
1027
+ className: "font-normal rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-muted",
1028
+ children: "Descartar"
1029
+ }
1030
+ ),
1031
+ /* @__PURE__ */ jsxs(
1032
+ Button,
1033
+ {
1034
+ variant: "bare",
1035
+ size: "bare",
1036
+ type: "button",
1037
+ onClick: save,
1038
+ disabled: saving || draft2.rules.length === 0,
1039
+ className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3.5 py-2 text-xs font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
1040
+ children: [
1041
+ saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
1042
+ "Salvar ",
1043
+ draft2.rules.length,
1044
+ " regra",
1045
+ draft2.rules.length === 1 ? "" : "s"
1046
+ ]
1047
+ }
1048
+ )
1049
+ ] }),
1050
+ error && /* @__PURE__ */ jsx("p", { className: "px-5 pt-3 text-xs text-destructive", children: error }),
1051
+ delivering + reviewing > 1 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-primary/10 px-5 py-3", children: [
1052
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Mesmo pre\xE7o em todas:" }),
1053
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1054
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "R$" }),
1055
+ /* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(
1056
+ Input,
1057
+ {
1058
+ value: bulkRate,
1059
+ onChange: (e) => setBulkRate(e.target.value),
1060
+ placeholder: "15,00",
1061
+ "aria-label": "Frete para todas as regi\xF5es",
1062
+ className: BULK_INPUT
1063
+ }
1064
+ ) })
1065
+ ] }),
1066
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1067
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "gr\xE1tis acima de R$" }),
1068
+ /* @__PURE__ */ jsx("div", { className: "w-24", children: /* @__PURE__ */ jsx(
1069
+ Input,
1070
+ {
1071
+ value: bulkFree,
1072
+ onChange: (e) => setBulkFree(e.target.value),
1073
+ placeholder: "opcional",
1074
+ "aria-label": "Frete gr\xE1tis acima de, para todas as regi\xF5es",
1075
+ className: BULK_INPUT
1076
+ }
1077
+ ) })
1078
+ ] }),
1079
+ /* @__PURE__ */ jsx(
1080
+ Button,
1081
+ {
1082
+ variant: "bare",
1083
+ size: "bare",
1084
+ type: "button",
1085
+ onClick: () => {
1086
+ setAllDeliveryDraftRates({
1087
+ rate: parseMoney(bulkRate),
1088
+ freeAbove: parseMoney(bulkFree)
1089
+ });
1090
+ setBulkRate("");
1091
+ setBulkFree("");
1092
+ },
1093
+ disabled: !bulkRate.trim() && !bulkFree.trim(),
1094
+ className: "rounded-md border border-border px-2.5 py-1 text-xs font-medium hover:bg-muted disabled:opacity-40",
1095
+ children: "Aplicar"
1096
+ }
1097
+ )
1098
+ ] }),
1099
+ draft2.rules.length > 0 && /* @__PURE__ */ jsx(DataTable, { variant: "bare", columns, data: draft2.rules }),
1100
+ draft2.gaps.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t border-primary/20 px-5 py-3.5", children: [
1101
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "O que ficou de fora \u2014 e por qu\xEA" }),
1102
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-1", children: draft2.gaps.map((gap) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-xs text-muted-foreground", children: [
1103
+ /* @__PURE__ */ jsx(X, { className: "mt-0.5 h-3 w-3 shrink-0 text-destructive" }),
1104
+ /* @__PURE__ */ jsxs("span", { children: [
1105
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: gap.query }),
1106
+ " \u2014 ",
1107
+ gap.reason
1108
+ ] })
1109
+ ] }, gap.query)) })
1110
+ ] })
1111
+ ] });
1112
+ }
1113
+ function parseMoney2(raw) {
1114
+ const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
1115
+ if (!cleaned.trim()) return null;
1116
+ const n = Number(cleaned);
1117
+ return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
1118
+ }
1119
+ var formatMoney = (v) => v == null ? "" : String(v).replace(".", ",");
1120
+ var FREIGHT_SOURCE = [
1121
+ {
1122
+ value: "own",
1123
+ label: "Eu mesmo calculo e despacho",
1124
+ description: "O frete sai do pre\xE7o base e das suas faixas de CEP, e voc\xEA despacha como quiser \u2014 carro pr\xF3prio, motoboy, ag\xEAncia. Nenhum gateway \xE9 consultado, mesmo que exista um conectado.",
1125
+ Icon: Car
1126
+ },
1127
+ {
1128
+ value: "gateway",
1129
+ label: "Perguntar a um gateway de frete",
1130
+ description: "O pre\xE7o e o prazo v\xEAm da transportadora, calculados pelo CEP do comprador. Quando o gateway n\xE3o responde a tempo, o checkout usa suas faixas \u2014 a compra nunca fica sem pre\xE7o.",
1131
+ Icon: Truck
1132
+ }
1133
+ ];
1134
+ function ShippingSettings({ provider, variant = "full" }) {
1135
+ const embedded = variant === "delivery";
1136
+ const [registryId, setRegistryId] = React6__default.useState(shippingRegistryIdFromHash);
1137
+ React6__default.useEffect(() => {
1138
+ const handler = () => setRegistryId(shippingRegistryIdFromHash());
1139
+ return onAppPathChange(handler);
1140
+ }, []);
1141
+ const shop = React6__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
1142
+ const registries = React6__default.useMemo(
1143
+ () => buildShopRegistries(provider ?? getShopProvider),
1144
+ [provider]
1145
+ );
1146
+ const storeSettings = useStoreSettings(provider);
1147
+ const supported = typeof shop.getShippingSettings === "function" && typeof shop.updateShippingSettings === "function";
1148
+ const [row, setRow] = React6__default.useState(null);
1149
+ const [zones, setZones] = React6__default.useState(null);
1150
+ const [locations, setLocations] = React6__default.useState(null);
1151
+ const [loading, setLoading] = React6__default.useState(supported);
1152
+ const [saving, setSaving] = React6__default.useState(false);
1153
+ const [error, setError] = React6__default.useState(null);
1154
+ const [loadError, setLoadError] = React6__default.useState(null);
1155
+ const [flatDraft, setFlatDraft] = React6__default.useState("");
1156
+ const [freeDraft, setFreeDraft] = React6__default.useState("");
1157
+ const [flatBad, setFlatBad] = React6__default.useState(false);
1158
+ const [freeBad, setFreeBad] = React6__default.useState(false);
1159
+ const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
1160
+ const [selected, setSelected] = React6__default.useState(null);
1161
+ const panelAvailable = useConnectorsPanelAvailable();
1162
+ const openJourney = React6__default.useCallback(
1163
+ (connector) => {
1164
+ if (panelAvailable) openConnectorsPanel(connector);
1165
+ else setSelected(connector);
1166
+ },
1167
+ [panelAvailable]
1168
+ );
1169
+ const carriers = React6__default.useMemo(
1170
+ () => (connectors ?? []).filter((c) => c.category === "logistics" || c.category === "shipping"),
1171
+ [connectors]
1172
+ );
1173
+ const carriersConnected = carriers.some((c) => c.state !== "ready");
1174
+ const [zonesEpoch, setZonesEpoch] = React6__default.useState(0);
1175
+ const reloadZones = React6__default.useCallback(() => setZonesEpoch((n) => n + 1), []);
1176
+ React6__default.useEffect(() => {
1177
+ let cancelled = false;
1178
+ if (supported) {
1179
+ setLoading(true);
1180
+ void shop.getShippingSettings().then((settings) => {
1181
+ if (cancelled) return;
1182
+ setRow(settings);
1183
+ setFlatDraft(formatMoney(settings?.flatRate ?? 0));
1184
+ setFreeDraft(formatMoney(settings?.freeAbove));
1185
+ }).catch((e) => {
1186
+ if (!cancelled) setLoadError(errorMessage(e));
1187
+ }).finally(() => {
1188
+ if (!cancelled) setLoading(false);
1189
+ });
1190
+ }
1191
+ void shop.listShippingZones?.().then((list) => {
1192
+ if (!cancelled) setZones(list);
1193
+ }).catch(() => {
1194
+ if (!cancelled) setZones([]);
1195
+ });
1196
+ void shop.listStockLocations?.().then((list) => {
1197
+ if (!cancelled) setLocations(list);
1198
+ }).catch(() => {
1199
+ if (!cancelled) setLocations([]);
1200
+ });
1201
+ return () => {
1202
+ cancelled = true;
1203
+ };
1204
+ }, [shop, supported, zonesEpoch]);
1205
+ const save = React6__default.useCallback(
1206
+ (patch) => {
1207
+ if (!supported) return;
1208
+ setSaving(true);
1209
+ setError(null);
1210
+ void shop.updateShippingSettings(patch).then((saved) => setRow(saved)).catch((e) => setError(errorMessage(e))).finally(() => setSaving(false));
1211
+ },
1212
+ [shop, supported]
1213
+ );
1214
+ const commitFlat = () => {
1215
+ const parsed = parseMoney2(flatDraft);
1216
+ const next = flatDraft.trim() === "" ? 0 : parsed;
1217
+ if (next == null) {
1218
+ setFlatBad(true);
1219
+ return;
1220
+ }
1221
+ setFlatBad(false);
1222
+ setFlatDraft(formatMoney(next));
1223
+ if (next !== (row?.flatRate ?? 0)) save({ flatRate: next });
1224
+ };
1225
+ const commitFree = () => {
1226
+ if (freeDraft.trim() === "") {
1227
+ setFreeBad(false);
1228
+ if (row?.freeAbove != null) save({ freeAbove: null });
1229
+ return;
1230
+ }
1231
+ const parsed = parseMoney2(freeDraft);
1232
+ if (parsed == null) {
1233
+ setFreeBad(true);
1234
+ return;
1235
+ }
1236
+ setFreeBad(false);
1237
+ setFreeDraft(formatMoney(parsed));
1238
+ if (parsed !== row?.freeAbove) save({ freeAbove: parsed });
1239
+ };
1240
+ const outcomeOf2 = (z) => z.outcome ?? (z.excluded ? "refuse" : "deliver");
1241
+ const activeZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "deliver");
1242
+ const reviewZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "review");
1243
+ const excludedZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "refuse");
1244
+ const usesGateway = storeSettings.value.carrierQuotes !== false;
1245
+ const dispatching = (locations ?? []).filter((l) => l.status === "active" && l.shipsOnline !== false);
1246
+ const originCep = dispatching.find((l) => l.postalCode)?.postalCode ?? null;
1247
+ const activeRegistry = registryId ? registries.find((candidate) => candidate.id === registryId) ?? null : null;
1248
+ if (activeRegistry) {
1249
+ return /* @__PURE__ */ jsx(
1250
+ RegistryCrudView,
1251
+ {
1252
+ registry: activeRegistry,
1253
+ basePath: shippingRegistryPath(activeRegistry.id)
1254
+ }
1255
+ );
1256
+ }
1257
+ if (!embedded && storeSettings.value.deliveryFlow) {
1258
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-5 text-sm shadow-sm", "data-testid": "shipping-is-delivery", children: [
1259
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "Esta loja \xE9 um delivery" }),
1260
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: "Taxa de entrega, frete gr\xE1tis, regi\xF5es, pedido m\xEDnimo e tempo estimado ficam em Configura\xE7\xF5es \u203A Delivery. Esta tela \xE9 das lojas que despacham por transportadora." }),
1261
+ /* @__PURE__ */ jsxs(Button, { className: "mt-3", size: "sm", onClick: () => adminNavigateTo("/settings/orders-delivery"), children: [
1262
+ "Abrir Delivery ",
1263
+ /* @__PURE__ */ jsx(ArrowRight, { className: "ml-1 h-3.5 w-3.5" })
1264
+ ] })
1265
+ ] });
1266
+ }
1267
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1268
+ loadError && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm", children: [
1269
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-destructive", children: [
1270
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
1271
+ "N\xE3o foi poss\xEDvel carregar o pre\xE7o base de entrega"
1272
+ ] }),
1273
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: "O valor abaixo \xE9 o padr\xE3o, n\xE3o o da sua loja. Recarregue antes de alter\xE1-lo \u2014 salvar agora sobrescreveria um pre\xE7o que n\xE3o conseguimos ler." }),
1274
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: loadError })
1275
+ ] }),
1276
+ !supported ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 p-4 text-sm", children: [
1277
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 font-medium text-amber-700 dark:text-amber-500", children: [
1278
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0" }),
1279
+ "Este app n\xE3o guarda o pre\xE7o base de entrega"
1280
+ ] }),
1281
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-muted-foreground", children: [
1282
+ "O provedor de loja montado aqui n\xE3o implementa ",
1283
+ /* @__PURE__ */ jsx("code", { children: "getShippingSettings" }),
1284
+ "."
1285
+ ] })
1286
+ ] }) : /* @__PURE__ */ jsxs(
1287
+ SettingsGroup,
1288
+ {
1289
+ title: embedded ? "Quanto custa a entrega" : "Pre\xE7o base",
1290
+ description: embedded ? "O que o site cobra para entregar quando nenhuma regi\xE3o abaixo tem pre\xE7o pr\xF3prio." : "O que a loja cobra quando nenhuma zona cobre o endere\xE7o. Uma zona que cobre o CEP sempre ganha deste n\xFAmero.",
1291
+ children: [
1292
+ /* @__PURE__ */ jsx(
1293
+ TextRow,
1294
+ {
1295
+ label: embedded ? "Taxa de entrega" : "Frete padr\xE3o",
1296
+ description: "Em reais. Deixe 0 para entrega gr\xE1tis.",
1297
+ value: flatDraft,
1298
+ onChange: setFlatDraft,
1299
+ onBlurCommit: commitFlat,
1300
+ placeholder: "0,00",
1301
+ invalid: flatBad,
1302
+ hint: flatBad ? "Informe um valor em reais (ex.: 19,90)." : void 0,
1303
+ disabled: loading
1304
+ }
1305
+ ),
1306
+ /* @__PURE__ */ jsx(
1307
+ TextRow,
1308
+ {
1309
+ label: "Frete gr\xE1tis acima de",
1310
+ description: "Subtotal, j\xE1 com desconto, a partir do qual a entrega sai de gra\xE7a. Vazio = sem regra de frete gr\xE1tis.",
1311
+ value: freeDraft,
1312
+ onChange: setFreeDraft,
1313
+ onBlurCommit: commitFree,
1314
+ placeholder: "Sem regra",
1315
+ invalid: freeBad,
1316
+ hint: freeBad ? "Informe um valor em reais, ou deixe vazio." : void 0,
1317
+ disabled: loading
1318
+ }
1319
+ )
1320
+ ]
1321
+ }
1322
+ ),
1323
+ !embedded && /* @__PURE__ */ jsxs("div", { children: [
1324
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
1325
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "De onde voc\xEA despacha" }),
1326
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Os locais que guardam estoque e despacham pedidos. O CEP do local padr\xE3o \xE9 a origem que a transportadora usa para cotar o frete \u2014 \xE9 aqui que ele se cadastra, uma vez. Cada local pode ter o seu pr\xF3prio despachante ou seguir a decis\xE3o da loja." })
1327
+ ] }),
1328
+ /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
1329
+ Button,
1330
+ {
1331
+ variant: "bare",
1332
+ size: "bare",
1333
+ type: "button",
1334
+ onClick: () => {
1335
+ navigateApp(dispatching.length === 1 && dispatching[0] ? shippingRecordPath(LOCATIONS_REGISTRY, dispatching[0].id) : shippingRegistryPath(LOCATIONS_REGISTRY));
1336
+ },
1337
+ className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
1338
+ children: [
1339
+ /* @__PURE__ */ jsx(Warehouse, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1340
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1341
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: locations == null ? "Carregando locais\u2026" : dispatching.length === 0 ? "Nenhum local despacha pedidos" : dispatching.length === 1 ? dispatching[0].name : `${dispatching.length} locais despachando` }),
1342
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: locations == null ? " " : dispatching.length === 0 ? "Cadastre o local de onde os pedidos saem." : dispatching.slice(0, 2).map((l) => locationSummary(l)).join(" \u2014 ") })
1343
+ ] }),
1344
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
1345
+ ]
1346
+ }
1347
+ ) }),
1348
+ usesGateway && locations != null && dispatching.length > 0 && !originCep && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs", children: [
1349
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }),
1350
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Nenhum local tem CEP de origem. Sem ele a transportadora n\xE3o cota, e o checkout cai nas suas zonas \u2014 preencha o CEP do local padr\xE3o." })
1351
+ ] })
1352
+ ] }),
1353
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1354
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
1355
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega" }),
1356
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "As regi\xF5es onde a loja entrega, com pre\xE7o, prazo e frete gr\xE1tis pr\xF3prios. Um endere\xE7o fora de todas elas cai no pre\xE7o base acima; se a loja n\xE3o tiver pre\xE7o base nem regi\xE3o que o cubra, o checkout diz que n\xE3o entrega ali. Uma regi\xE3o tamb\xE9m pode ser uma recusa \u2014 \xABn\xE3o entrego aqui\xBB \u2014 e ela vence qualquer outra e qualquer gateway." })
1357
+ ] }),
1358
+ /* @__PURE__ */ jsx(
1359
+ DeliveryAgentSurface,
1360
+ {
1361
+ provider: () => shop,
1362
+ zones: zones ?? [],
1363
+ onSaved: reloadZones
1364
+ }
1365
+ ),
1366
+ /* @__PURE__ */ jsx(DeliveryAreaPicker, { provider }),
1367
+ /* @__PURE__ */ jsx(DeliveryRulesDraft, { provider, onSaved: reloadZones }),
1368
+ /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
1369
+ Button,
1370
+ {
1371
+ variant: "bare",
1372
+ size: "bare",
1373
+ type: "button",
1374
+ onClick: () => {
1375
+ navigateApp(shippingRegistryPath(SHIPPING_ZONES_REGISTRY));
1376
+ },
1377
+ className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 py-3.5 text-left",
1378
+ children: [
1379
+ /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1380
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1381
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: zones == null ? "Carregando regi\xF5es\u2026" : activeZones.length === 0 ? "Nenhuma regi\xE3o salva ainda" : `${activeZones.length} ${activeZones.length === 1 ? "regi\xE3o onde entrego" : "regi\xF5es onde entrego"}` }),
1382
+ /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
1383
+ activeZones.length === 0 ? "Sem regi\xE3o salva, toda entrega usa o pre\xE7o base. Escolha as suas na busca acima." : activeZones.slice(0, 3).map((z) => z.name).join(" \xB7 "),
1384
+ activeZones.length > 3 && ` e ${activeZones.length - 3} mais`,
1385
+ reviewZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-amber-700 dark:text-amber-500", children: [
1386
+ reviewZones.length === 1 ? "1 regi\xE3o a confirmar antes de sair" : `${reviewZones.length} regi\xF5es a confirmar antes de sair`,
1387
+ " \u2014 ",
1388
+ reviewZones.slice(0, 2).map((z) => z.name).join(" \xB7 ")
1389
+ ] }),
1390
+ excludedZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-red-700 dark:text-red-400", children: [
1391
+ excludedZones.length === 1 ? "1 regi\xE3o recusada" : `${excludedZones.length} regi\xF5es recusadas`,
1392
+ " \u2014 ",
1393
+ excludedZones.slice(0, 2).map((z) => z.name).join(" \xB7 ")
1394
+ ] })
1395
+ ] })
1396
+ ] }),
1397
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground" })
1398
+ ]
1399
+ }
1400
+ ) })
1401
+ ] }),
1402
+ !embedded && /* @__PURE__ */ jsx(PackagesSettings, {}),
1403
+ !embedded && storeSettings.supported && /* @__PURE__ */ jsxs("div", { children: [
1404
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
1405
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Quem calcula o frete" }),
1406
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "O Melhor Envio n\xE3o \xE9 uma transportadora: \xE9 um gateway, uma conex\xE3o s\xF3 que traz Correios, Jadlog, Loggi e outras. Ent\xE3o a escolha aqui n\xE3o \xE9 qual transportadora \u2014 \xE9 se a loja calcula pelas pr\xF3prias faixas ou se pergunta o pre\xE7o a um gateway." })
1407
+ ] }),
1408
+ /* @__PURE__ */ jsx(
1409
+ ChoiceCards,
1410
+ {
1411
+ label: "Quem calcula o frete",
1412
+ choices: FREIGHT_SOURCE,
1413
+ value: usesGateway ? "gateway" : "own",
1414
+ onChange: (choice) => storeSettings.set({ carrierQuotes: choice === "gateway" }),
1415
+ disabled: storeSettings.loading
1416
+ }
1417
+ ),
1418
+ !usesGateway && carriersConnected && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-xs", children: [
1419
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }),
1420
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "H\xE1 um gateway ainda conectado abaixo. Para garantir que nenhum pre\xE7o de transportadora apare\xE7a no checkout, desconecte-o \u2014 a escolha acima vale para a vitrine, a conex\xE3o continua existindo." })
1421
+ ] })
1422
+ ] }),
1423
+ !embedded && carriers.length > 0 && /* @__PURE__ */ jsxs("div", { className: usesGateway ? void 0 : "opacity-60", children: [
1424
+ /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
1425
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Gateway de frete" }),
1426
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Conectado, ele cota o frete para o CEP do comprador em vez de a loja adivinhar. O pre\xE7o cotado \xE9 gravado no servidor e \xE9 ele que o pedido cobra \u2014 o navegador escolhe entre op\xE7\xF5es, nunca informa um valor. Quais transportadoras aparecem vem da conta do gateway, n\xE3o desta lista." }),
1427
+ !carriersConnected && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Nenhum conectado \u2014 a loja entrega por conta pr\xF3pria, pelo pre\xE7o base e pelas zonas acima. Conectar \xE9 opcional e pode ficar para depois." })
1428
+ ] }),
1429
+ /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card shadow-sm", children: /* @__PURE__ */ jsx("ul", { className: "divide-y", children: carriers.map((connector) => /* @__PURE__ */ jsx(ConnectorRow, { connector, onOpen: openJourney }, connector.id)) }) })
1430
+ ] }),
1431
+ !embedded && /* @__PURE__ */ jsx(HandlingSettings, { settings: storeSettings, provider }),
1432
+ /* @__PURE__ */ jsx("div", { className: "flex min-h-5 items-center gap-1.5 px-1 text-xs text-muted-foreground", children: error ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-destructive", children: [
1433
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
1434
+ "N\xE3o foi poss\xEDvel salvar: ",
1435
+ error
1436
+ ] }) : saving ? /* @__PURE__ */ jsxs(Fragment, { children: [
1437
+ /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }),
1438
+ "Salvando\u2026"
1439
+ ] }) : "As altera\xE7\xF5es s\xE3o salvas ao sair do campo." }),
1440
+ /* @__PURE__ */ jsx(
1441
+ ConnectorDetail,
1442
+ {
1443
+ connector: selected,
1444
+ open: !!selected,
1445
+ onOpenChange: (open) => {
1446
+ if (!open) setSelected(null);
1447
+ }
1448
+ }
1449
+ )
1450
+ ] });
1451
+ }
1452
+
1453
+ export { ShippingSettings };
1454
+ //# sourceMappingURL=chunk-STHGZWBQ.js.map
1455
+ //# sourceMappingURL=chunk-STHGZWBQ.js.map