@fayz-ai/plugin-shop 0.13.0 → 0.14.1

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/index.js CHANGED
@@ -1,15 +1,197 @@
1
- import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-2VMTPRGC.js';
2
- import React9, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
3
- import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, getActiveTenantId, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
1
+ import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-NFFJVQCY.js';
2
+ import * as React13 from 'react';
3
+ import React13__default, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
+ import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, countByTenant, getActiveTenantId, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
4
5
  import { getShopTenantId, setShopTenantResolver, buildShopMessageDefaults, registerShippingLabelProvider, getShopProvider, buildShopDomainEvents, axesFromOptions, planVariantMatrix, validateVariantAxes, hasOnlyDefaultVariant, MAX_VARIANT_AXES, leavesNothingSellable, productImageToMediaFile, formatVariantCombination, applyVariantMatrix } from '@fayz-ai/shop';
5
6
  import { fayzConnectorOAuth, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo, useAgentSurface, askAI, AskAIButton, CrudFormPage, SettingsGroup, SelectRow, ToggleRow, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, MultiSelectRow, AddConnectorButton, AddConnectorPanel, ConnectorDetail, RegistryCrudView, TextRow, ConnectorRow, ConnectorMark, StateChip } from '@fayz-ai/admin';
6
- import { Package, Eye, FileEdit, PackageX, UsersRound, Users, UserX, Filter, ShoppingBag, Repeat, CircleDollarSign, Tag, TicketCheck, Layers, CreditCard, ImageOff, Link2, MapPin, Music, Camera, MessageCircle, Globe, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info } from 'lucide-react';
7
+ import { Package, Eye, FileEdit, PackageX, UsersRound, Users, UserX, Filter, ShoppingBag, Repeat, CircleDollarSign, Tag, TicketCheck, Layers, CreditCard, ImageOff, Link2, MapPin, Music, Camera, MessageCircle, Globe, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info, Sparkles, Ban, ListChecks, Search, Building2 } from 'lucide-react';
7
8
  import { PageHeaderActions, PageTabs, Card, Button, Thumbnail, PageHeader, toast, ConfirmDialog, Input, Hint, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Skeleton } from '@fayz-ai/ui';
8
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
10
  import { listStockReasonCodes, resolveStockStore, createSupabaseStockStore } from '@fayz-ai/shop/inventory';
10
11
  import { normalizeMovementType } from '@fayz-ai/plugin-inventory';
11
12
  import { advanceWindow, createRateLimiter, sliceWindow, paginate, reconcileMirror, serializeCursor } from '@fayz-ai/core/connector-runtime';
12
13
 
14
+ function buildShopOnboarding() {
15
+ return {
16
+ id: "shop",
17
+ title: "Sua loja no ar",
18
+ order: 10,
19
+ role: "operations",
20
+ instructions: `Voc\xEA est\xE1 implantando uma LOJA. A pessoa n\xE3o quer aprender um sistema: ela quer vender.
21
+
22
+ - A ordem importa e \xE9 esta: ter o que vender \u2192 onde vender \u2192 como receber \u2192 como entregar \u2192 um motivo para comprar \u2192 a primeira venda. N\xE3o pule para "divulgue" antes de a loja aceitar um pedido.
23
+ - Importar cat\xE1logo \xE9 o passo que trava mais gente. Ofere\xE7a planilha, integra\xE7\xE3o (Bling, Nuvemshop) ou cadastrar um produto \xE0 m\xE3o para destravar \u2014 um produto j\xE1 \xE9 o suficiente para seguir.
24
+ - Em pagamento, pergunte COMO ela recebe hoje (Pix, cart\xE3o, maquininha) antes de falar de gateway. O nome do provedor \xE9 detalhe nosso, n\xE3o dela.
25
+ - Antes de dizer que a loja est\xE1 pronta, confira: tem produto ativo, tem forma de pagamento, e a vitrine est\xE1 publicada. Se faltar uma, diga qual falta.
26
+ - Quando estiver pronta para receber pedidos, chame o colega de growth para a primeira campanha \u2014 n\xE3o invente plano de m\xEDdia por conta pr\xF3pria.`,
27
+ steps: [
28
+ {
29
+ key: "catalog",
30
+ title: "Cadastre seu primeiro produto",
31
+ description: "Seu cat\xE1logo \xE9 o que a loja vende. Um produto j\xE1 destrava o resto.",
32
+ icon: "Package",
33
+ route: "/products",
34
+ priority: "high",
35
+ // O passo genérico da plataforma ("cadastre seu primeiro produto") e
36
+ // este são o mesmo recado — a loja é dona dele.
37
+ replaces: "catalog",
38
+ agentHint: "Pergunte onde o cat\xE1logo est\xE1 hoje (planilha, ERP, Instagram). Ofere\xE7a importar; se travar, um produto \xE0 m\xE3o j\xE1 destrava.",
39
+ howTo: "Produtos \u203A Adicionar. Nome, pre\xE7o e uma foto j\xE1 bastam para o produto aparecer na loja \u2014 estoque e varia\xE7\xF5es d\xE1 para completar depois. Se voc\xEA j\xE1 tem uma planilha, use Adicionar \u203A Importar: colunas de nome, pre\xE7o e estoque viram o cat\xE1logo inteiro de uma vez.",
40
+ celebrate: "Produto no ar \u{1F389}",
41
+ // A coreografia, como dado: nenhuma dessas quatro falas varia, e cada
42
+ // uma delas custava um turno de modelo para ser escrita de novo.
43
+ script: [
44
+ { kind: "say", text: "Vamos come\xE7ar pelo que a loja vende. Te levo at\xE9 os Produtos." },
45
+ { kind: "goto", route: "/products" },
46
+ {
47
+ kind: "say",
48
+ text: "Esta \xE9 a sua lista de produtos \u2014 hoje vazia. Tudo que entrar aqui aparece na vitrine."
49
+ },
50
+ {
51
+ // Três caminhos, e "cadastre um produto" assumia que só existia um.
52
+ // A pergunta é o passo: quem tem planilha não quer digitar, e quem
53
+ // não tem nada quer que alguém comece por ele.
54
+ kind: "choose",
55
+ id: "shop.catalog.how",
56
+ question: "Como voc\xEA prefere trazer seus produtos?",
57
+ options: [
58
+ { id: "import", label: "Tenho uma planilha", meta: "Importo e mapeio as colunas com voc\xEA" },
59
+ { id: "manual", label: "Vou cadastrar \xE0 m\xE3o", meta: "Te mostro onde, come\xE7ando por um" },
60
+ {
61
+ id: "suggest",
62
+ label: "Me sugira o cat\xE1logo",
63
+ meta: "Monto uma lista a partir do que j\xE1 sei do seu neg\xF3cio"
64
+ }
65
+ ]
66
+ }
67
+ ],
68
+ check: async () => await countByTenant("plg_shop_products") > 0,
69
+ tour: {
70
+ id: "shop.catalog",
71
+ stops: [
72
+ {
73
+ anchor: "action.new",
74
+ title: "\xC9 aqui que nasce um produto",
75
+ body: "Nome, pre\xE7o e uma foto j\xE1 bastam. Pode cadastrar agora \u2014 eu vejo quando ele entrar.",
76
+ route: "/products",
77
+ waitForUser: true
78
+ },
79
+ {
80
+ anchor: "action.import",
81
+ title: "Ou traga tudo de uma vez",
82
+ body: "Uma planilha com nome, pre\xE7o e estoque vira o cat\xE1logo inteiro."
83
+ }
84
+ ]
85
+ }
86
+ },
87
+ {
88
+ key: "storefront",
89
+ title: "Configure sua loja online",
90
+ description: "O endere\xE7o que voc\xEA manda para o cliente \u2014 e os canais que apontam para ela.",
91
+ icon: "Store",
92
+ route: "/store",
93
+ priority: "high",
94
+ // A plataforma chama de "publique seu site"; numa loja é a vitrine, e
95
+ // quem sabe configurá-la é este módulo.
96
+ replaces: "publish",
97
+ agentHint: "Confira o endere\xE7o da vitrine e os canais ativos. Publicar sem frete configurado gera pedido que ningu\xE9m consegue entregar.",
98
+ howTo: 'Canais de venda (o item "Sua loja" na barra lateral). A Loja Online \xE9 a sua vitrine: confira o endere\xE7o, veja como est\xE1 e publique. Esse link \xE9 o que vai para a bio do Instagram e para o WhatsApp.',
99
+ celebrate: "Vitrine pronta \u2728",
100
+ script: [
101
+ { kind: "say", text: "Agora a vitrine \u2014 o endere\xE7o que voc\xEA manda para o cliente." },
102
+ { kind: "goto", route: "/store" },
103
+ {
104
+ kind: "ask",
105
+ text: "Estou na tela de canais de venda. Confira comigo o endere\xE7o da Loja Online e me diga o que falta para publicar."
106
+ }
107
+ ]
108
+ },
109
+ {
110
+ key: "payments",
111
+ title: "Decida como vai receber",
112
+ description: "Pix, cart\xE3o ou boleto \u2014 o dinheiro precisa de um caminho.",
113
+ icon: "CreditCard",
114
+ route: "/settings/shop-payments",
115
+ priority: "high",
116
+ replaces: "payments",
117
+ agentHint: "Comece por como ela j\xE1 recebe hoje. Pix costuma ser o caminho mais curto para a primeira venda; cart\xE3o vem depois.",
118
+ howTo: "Configura\xE7\xF5es \u203A Pagamentos. Ligue as formas que voc\xEA aceita e conecte o provedor de cada uma. Para Pix voc\xEA precisa da chave ou das credenciais do provedor; sem isso o pedido entra, mas o pagamento n\xE3o \xE9 confirmado sozinho.",
119
+ celebrate: "Recebimento no lugar \u{1F4B3}",
120
+ script: [
121
+ { kind: "goto", route: "/settings/shop-payments" },
122
+ {
123
+ kind: "ask",
124
+ text: "Estamos em Pagamentos. Pergunte como a pessoa recebe hoje (Pix, cart\xE3o, maquininha) antes de falar de provedor, e ligue a forma que ela usar."
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ key: "shipping",
130
+ title: "Configure a entrega",
131
+ description: "Zonas, prazos e quanto custa o frete.",
132
+ icon: "Truck",
133
+ route: "/settings/shop-shipping",
134
+ agentHint: "Pergunte at\xE9 onde entregam e como (carro pr\xF3prio, transportadora, retirada). Frete errado \xE9 o motivo n\xBA 1 de carrinho abandonado.",
135
+ howTo: "Configura\xE7\xF5es \u203A Entrega. Crie uma zona (por CEP ou por cidade), diga o prazo e o pre\xE7o \u2014 pode ser valor fixo para come\xE7ar. Retirada no local \xE9 uma zona com frete zero.",
136
+ celebrate: "Entrega configurada \u{1F69A}",
137
+ script: [
138
+ { kind: "goto", route: "/settings/shop-shipping" },
139
+ {
140
+ kind: "ask",
141
+ text: "Estamos em Entrega. Pergunte at\xE9 onde a pessoa entrega e como, e monte a primeira zona junto com ela."
142
+ }
143
+ ]
144
+ },
145
+ {
146
+ key: "discount",
147
+ title: "Crie um cupom de desconto",
148
+ description: "D\xEA um motivo para o cliente fechar a primeira compra.",
149
+ icon: "Tag",
150
+ route: "/discounts",
151
+ priority: "low",
152
+ agentHint: "Este \xE9 passo de growth: sugira um cupom de primeira compra e ajude a decidir o valor. Se houver colega de growth, passe a bola.",
153
+ howTo: "Descontos \u203A Adicionar. Escolha o c\xF3digo (algo curto, tipo BEMVINDO10), o tipo (percentual ou valor) e at\xE9 quando vale. O cliente digita esse c\xF3digo no checkout."
154
+ },
155
+ {
156
+ key: "first-order",
157
+ title: "Receba seu primeiro pedido",
158
+ description: "Fa\xE7a um pedido de teste e veja ele cair aqui dentro.",
159
+ icon: "ShoppingBag",
160
+ route: "/orders",
161
+ agentHint: 'Um pedido de teste \xE9 o que transforma "acho que est\xE1 no ar" em "est\xE1 no ar". Acompanhe at\xE9 ele aparecer na lista.',
162
+ howTo: "Abra sua vitrine como se fosse um cliente, escolha um produto e finalize a compra. O pedido aparece em Pedidos em segundos \u2014 e \xE9 assim que voc\xEA confere que pre\xE7o, frete e pagamento est\xE3o certos antes do primeiro cliente de verdade.",
163
+ celebrate: "Primeiro pedido na casa \u{1F6CD}\uFE0F",
164
+ script: [
165
+ {
166
+ kind: "say",
167
+ text: "\xDAltimo: um pedido de teste. Abra sua vitrine como se fosse um cliente e finalize uma compra \u2014 eu aviso aqui quando ele cair."
168
+ },
169
+ { kind: "goto", route: "/orders" },
170
+ {
171
+ kind: "point",
172
+ anchor: "nav./orders",
173
+ title: "Todo pedido cai aqui",
174
+ body: "Pago, em separa\xE7\xE3o, enviado \u2014 o estado de cada venda vive nesta lista.",
175
+ waitForUser: true
176
+ }
177
+ ],
178
+ check: async () => await countByTenant("plg_shop_orders") > 0,
179
+ tour: {
180
+ id: "shop.orders",
181
+ stops: [
182
+ {
183
+ anchor: "nav./orders",
184
+ title: "Todo pedido cai aqui",
185
+ body: "Pago, em separa\xE7\xE3o, enviado \u2014 o estado de cada venda vive nesta lista.",
186
+ route: "/orders"
187
+ }
188
+ ]
189
+ }
190
+ }
191
+ ]
192
+ };
193
+ }
194
+
13
195
  // src/integrations/melhor-envio/labelProvider.ts
14
196
  var MELHOR_ENVIO_CARRIER = "melhor-envio";
15
197
  function createMelhorEnvioLabelProvider(resolve4) {
@@ -172,20 +354,20 @@ function ProductVariantsTab({ item, getProvider }) {
172
354
  const product = item;
173
355
  const shop = getProvider();
174
356
  const supported = typeof shop.getProductVariants === "function" && typeof shop.saveProductOptions === "function" && typeof shop.createProductVariant === "function" && typeof shop.updateProductVariant === "function";
175
- const [axes, setAxes] = React9.useState([]);
176
- const [variants, setVariants] = React9.useState([]);
177
- const [busy, setBusy] = React9.useState(false);
178
- const [error, setError] = React9.useState(null);
179
- const [selected, setSelected] = React9.useState(/* @__PURE__ */ new Set());
180
- const [dirty, setDirty] = React9.useState(false);
181
- const reload = React9.useCallback(async () => {
357
+ const [axes, setAxes] = React13__default.useState([]);
358
+ const [variants, setVariants] = React13__default.useState([]);
359
+ const [busy, setBusy] = React13__default.useState(false);
360
+ const [error, setError] = React13__default.useState(null);
361
+ const [selected, setSelected] = React13__default.useState(/* @__PURE__ */ new Set());
362
+ const [dirty, setDirty] = React13__default.useState(false);
363
+ const reload = React13__default.useCallback(async () => {
182
364
  const set = await getProvider().getProductVariants(product.id, { includeArchived: true });
183
365
  setAxes(axesFromOptions(set.options));
184
366
  setVariants(set.variants);
185
367
  setSelected(/* @__PURE__ */ new Set());
186
368
  setDirty(false);
187
369
  }, [getProvider, product.id]);
188
- React9.useEffect(() => {
370
+ React13__default.useEffect(() => {
189
371
  if (!supported) return;
190
372
  void reload().catch((e) => setError(message(e)));
191
373
  }, [reload, supported]);
@@ -201,7 +383,7 @@ function ProductVariantsTab({ item, getProvider }) {
201
383
  setBusy(false);
202
384
  }
203
385
  }
204
- const plan = React9.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
386
+ const plan = React13__default.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
205
387
  const axisError = validateVariantAxes(axes);
206
388
  const onlyDefault = hasOnlyDefaultVariant(variants);
207
389
  const rows = plan.rows;
@@ -302,7 +484,7 @@ function ProductVariantsTab({ item, getProvider }) {
302
484
  ] });
303
485
  }
304
486
  function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }) {
305
- const [draft, setDraft] = React9.useState("");
487
+ const [draft2, setDraft] = React13__default.useState("");
306
488
  function commit(raw) {
307
489
  const values = raw.split(",").map((v) => v.trim()).filter(Boolean);
308
490
  if (values.length === 0) return;
@@ -357,16 +539,16 @@ function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }
357
539
  /* @__PURE__ */ jsx(
358
540
  "input",
359
541
  {
360
- value: draft,
542
+ value: draft2,
361
543
  disabled,
362
544
  onChange: (e) => setDraft(e.target.value),
363
545
  onKeyDown: (e) => {
364
546
  if (e.key === "Enter") {
365
547
  e.preventDefault();
366
- commit(draft);
548
+ commit(draft2);
367
549
  }
368
550
  },
369
- onBlur: () => commit(draft),
551
+ onBlur: () => commit(draft2),
370
552
  placeholder: axis.values.length === 0 ? "P, M, G" : "adicionar",
371
553
  "aria-label": `Valores da op\xE7\xE3o ${index + 1}`,
372
554
  className: "h-7 min-w-28 flex-1 rounded-input border border-dashed border-input bg-transparent px-2 text-xs"
@@ -575,9 +757,9 @@ function VariantRow({ row, axes, product, busy, checked, onToggle, onSave, onSto
575
757
  ] });
576
758
  }
577
759
  function BulkBar({ count, disabled, onApply, onClear }) {
578
- const [price, setPrice] = React9.useState("");
579
- const [weight, setWeight] = React9.useState("");
580
- const [unit, setUnit] = React9.useState("");
760
+ const [price, setPrice] = React13__default.useState("");
761
+ const [weight, setWeight] = React13__default.useState("");
762
+ const [unit, setUnit] = React13__default.useState("");
581
763
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border bg-muted/30 px-3 py-2 text-xs", children: [
582
764
  /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
583
765
  count,
@@ -656,19 +838,19 @@ function parseNumber(raw) {
656
838
  return Number.isFinite(n) && n >= 0 ? n : null;
657
839
  }
658
840
  function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", integer }) {
659
- const [draft, setDraft] = React9.useState(value == null ? "" : String(value));
660
- React9.useEffect(() => {
841
+ const [draft2, setDraft] = React13__default.useState(value == null ? "" : String(value));
842
+ React13__default.useEffect(() => {
661
843
  setDraft(value == null ? "" : String(value));
662
844
  }, [value]);
663
845
  return /* @__PURE__ */ jsx(
664
846
  "input",
665
847
  {
666
- value: draft,
848
+ value: draft2,
667
849
  disabled,
668
850
  placeholder,
669
851
  onChange: (e) => setDraft(e.target.value),
670
852
  onBlur: () => {
671
- const parsed = parseNumber(draft);
853
+ const parsed = parseNumber(draft2);
672
854
  onCommit(parsed == null ? null : integer ? Math.trunc(parsed) : parsed);
673
855
  },
674
856
  className: `h-7 ${width} rounded-input border border-input bg-background px-2 text-xs tabular-nums`
@@ -676,18 +858,18 @@ function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", in
676
858
  );
677
859
  }
678
860
  function TextCell({ value, disabled, placeholder, onCommit }) {
679
- const [draft, setDraft] = React9.useState(value);
680
- React9.useEffect(() => {
861
+ const [draft2, setDraft] = React13__default.useState(value);
862
+ React13__default.useEffect(() => {
681
863
  setDraft(value);
682
864
  }, [value]);
683
865
  return /* @__PURE__ */ jsx(
684
866
  "input",
685
867
  {
686
- value: draft,
868
+ value: draft2,
687
869
  disabled,
688
870
  placeholder,
689
871
  onChange: (e) => setDraft(e.target.value),
690
- onBlur: () => onCommit(draft.trim()),
872
+ onBlur: () => onCommit(draft2.trim()),
691
873
  className: "h-7 w-28 rounded-input border border-input bg-background px-2 text-xs"
692
874
  }
693
875
  );
@@ -1007,7 +1189,7 @@ function ProductPositionsTab({ item, getProvider }) {
1007
1189
  const [noteOpen, setNoteOpen] = useState(false);
1008
1190
  const [occurredOn, setOccurredOn] = useState(() => todayISO());
1009
1191
  const [saving, setSaving] = useState(false);
1010
- const quantityRef = React9.useRef(null);
1192
+ const quantityRef = React13__default.useRef(null);
1011
1193
  const load = useCallback(async () => {
1012
1194
  setLoading(true);
1013
1195
  try {
@@ -1086,7 +1268,7 @@ function ProductPositionsTab({ item, getProvider }) {
1086
1268
  const amount = Math.abs(Number(quantity) || 0);
1087
1269
  const outgoing = type === "saida" || type === "perda";
1088
1270
  const nextBalance = amount > 0 ? outgoing ? total.available - amount : total.available + amount : null;
1089
- const live = React9.useRef({ product, variantId, total, getProvider, load });
1271
+ const live = React13__default.useRef({ product, variantId, total, getProvider, load });
1090
1272
  live.current = { product, variantId, total, getProvider, load };
1091
1273
  const contribution = useMemo(() => ({
1092
1274
  id: "shop.product.positions",
@@ -1352,20 +1534,20 @@ function shippingRegistryIdFromHash(hash) {
1352
1534
  }
1353
1535
  var PACKAGES_ROUTE = shippingRegistryPath(PACKAGES_REGISTRY);
1354
1536
  function PackageSelect({ value, onChange, getProvider }) {
1355
- const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1356
- const [rows, setRows] = React9.useState(null);
1357
- const [open, setOpen] = React9.useState(false);
1358
- const [creating, setCreating] = React9.useState(false);
1359
- const ref = React9.useRef(null);
1360
- const load = React9.useCallback(() => {
1537
+ const shop = React13__default.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1538
+ const [rows, setRows] = React13__default.useState(null);
1539
+ const [open, setOpen] = React13__default.useState(false);
1540
+ const [creating, setCreating] = React13__default.useState(false);
1541
+ const ref = React13__default.useRef(null);
1542
+ const load = React13__default.useCallback(() => {
1361
1543
  if (typeof shop.listShippingPackages !== "function") {
1362
1544
  setRows([]);
1363
1545
  return;
1364
1546
  }
1365
1547
  void shop.listShippingPackages().then(setRows).catch(() => setRows([]));
1366
1548
  }, [shop]);
1367
- React9.useEffect(load, [load]);
1368
- React9.useEffect(() => {
1549
+ React13__default.useEffect(load, [load]);
1550
+ React13__default.useEffect(() => {
1369
1551
  if (!open) return;
1370
1552
  const away = (event) => {
1371
1553
  if (ref.current && !ref.current.contains(event.target)) setOpen(false);
@@ -1460,8 +1642,8 @@ function PackageSelect({ value, onChange, getProvider }) {
1460
1642
  ] });
1461
1643
  }
1462
1644
  function NewPackageModal({ onClose, onCreated }) {
1463
- const [error, setError] = React9.useState(null);
1464
- const provider = React9.useMemo(
1645
+ const [error, setError] = React13__default.useState(null);
1646
+ const provider = React13__default.useMemo(
1465
1647
  () => resolveDataProvider(packagesRegistry.entity),
1466
1648
  []
1467
1649
  );
@@ -1507,9 +1689,9 @@ function Option({ label, hint, active, onClick }) {
1507
1689
  );
1508
1690
  }
1509
1691
  function ShippingProfileSelect({ value, onChange, getProvider }) {
1510
- const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1511
- const [rows, setRows] = React9.useState(null);
1512
- React9.useEffect(() => {
1692
+ const shop = React13__default.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1693
+ const [rows, setRows] = React13__default.useState(null);
1694
+ React13__default.useEffect(() => {
1513
1695
  let cancelled = false;
1514
1696
  if (typeof shop.listShippingProfiles !== "function") {
1515
1697
  setRows([]);
@@ -2297,7 +2479,7 @@ var STATUS_LABEL2 = {
2297
2479
  disabled: "Desativado"
2298
2480
  };
2299
2481
  function buildShopGiftCardEntity(currencyCode = "BRL") {
2300
- const money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(Number(v) || 0);
2482
+ const money3 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(Number(v) || 0);
2301
2483
  const fields = [
2302
2484
  {
2303
2485
  key: "code",
@@ -2325,7 +2507,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
2325
2507
  hint: "Valor de face do cart\xE3o. N\xE3o muda depois de emitido.",
2326
2508
  showInTable: true,
2327
2509
  sortable: true,
2328
- renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children: money2(v) })
2510
+ renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children: money3(v) })
2329
2511
  },
2330
2512
  {
2331
2513
  key: "balance",
@@ -2339,7 +2521,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
2339
2521
  hint: "O que ainda pode ser gasto. Cai a cada resgate.",
2340
2522
  showInTable: true,
2341
2523
  sortable: true,
2342
- renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money2(v) })
2524
+ renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money3(v) })
2343
2525
  },
2344
2526
  {
2345
2527
  key: "status",
@@ -2474,20 +2656,20 @@ function buildShopCustomerGroupEntity() {
2474
2656
  fieldGroups: [{ id: "basic", label: "Grupo", columns: 2 }]
2475
2657
  };
2476
2658
  }
2477
- function describeSegment(s, money2) {
2659
+ function describeSegment(s, money3) {
2478
2660
  const parts = [];
2479
2661
  if (s.minOrders != null && s.maxOrders != null) parts.push(`${s.minOrders}\u2013${s.maxOrders} pedidos`);
2480
2662
  else if (s.minOrders != null) parts.push(`${s.minOrders}+ pedidos`);
2481
2663
  else if (s.maxOrders != null) parts.push(`at\xE9 ${s.maxOrders} pedidos`);
2482
- if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${money2(s.minSpent)}\u2013${money2(s.maxSpent)}`);
2483
- else if (s.minSpent != null) parts.push(`gastou ${money2(s.minSpent)}+`);
2484
- else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${money2(s.maxSpent)}`);
2664
+ if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${money3(s.minSpent)}\u2013${money3(s.maxSpent)}`);
2665
+ else if (s.minSpent != null) parts.push(`gastou ${money3(s.minSpent)}+`);
2666
+ else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${money3(s.maxSpent)}`);
2485
2667
  if (s.joinedAfter) parts.push(`entrou ap\xF3s ${new Date(s.joinedAfter).toLocaleDateString("pt-BR")}`);
2486
2668
  if (s.joinedBefore) parts.push(`entrou antes de ${new Date(s.joinedBefore).toLocaleDateString("pt-BR")}`);
2487
2669
  return parts.length ? parts.join(" \xB7 ") : "Todos os clientes";
2488
2670
  }
2489
2671
  function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
2490
- const money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(v);
2672
+ const money3 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(v);
2491
2673
  const fields = [
2492
2674
  {
2493
2675
  key: "name",
@@ -2564,7 +2746,7 @@ function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
2564
2746
  showInForm: false,
2565
2747
  showInTable: true,
2566
2748
  sortable: false,
2567
- renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row, money2) })
2749
+ renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row, money3) })
2568
2750
  },
2569
2751
  {
2570
2752
  key: "matchCount",
@@ -2894,17 +3076,17 @@ function createShopCustomerSegmentDataProvider(provider) {
2894
3076
  }
2895
3077
  var DEBOUNCE_MS = 400;
2896
3078
  function useStoreSettings(provider, defaults = {}) {
2897
- const shop = React9.useMemo(
3079
+ const shop = React13__default.useMemo(
2898
3080
  () => provider ? resolve(provider) : getShopProvider(),
2899
3081
  [provider]
2900
3082
  );
2901
3083
  const supported = typeof shop.getStoreSettings === "function" && typeof shop.updateStoreSettings === "function";
2902
- const [stored, setStored] = React9.useState(null);
2903
- const [loading, setLoading] = React9.useState(supported);
2904
- const [saving, setSaving] = React9.useState(false);
2905
- const [error, setError] = React9.useState(null);
2906
- const [loadError, setLoadError] = React9.useState(null);
2907
- React9.useEffect(() => {
3084
+ const [stored, setStored] = React13__default.useState(null);
3085
+ const [loading, setLoading] = React13__default.useState(supported);
3086
+ const [saving, setSaving] = React13__default.useState(false);
3087
+ const [error, setError] = React13__default.useState(null);
3088
+ const [loadError, setLoadError] = React13__default.useState(null);
3089
+ React13__default.useEffect(() => {
2908
3090
  if (!supported) return;
2909
3091
  let cancelled = false;
2910
3092
  setLoading(true);
@@ -2920,12 +3102,12 @@ function useStoreSettings(provider, defaults = {}) {
2920
3102
  cancelled = true;
2921
3103
  };
2922
3104
  }, [shop, supported]);
2923
- const pendingRef = React9.useRef({});
2924
- const timerRef = React9.useRef(null);
2925
- React9.useEffect(() => () => {
3105
+ const pendingRef = React13__default.useRef({});
3106
+ const timerRef = React13__default.useRef(null);
3107
+ React13__default.useEffect(() => () => {
2926
3108
  if (timerRef.current) clearTimeout(timerRef.current);
2927
3109
  }, []);
2928
- const set = React9.useCallback(
3110
+ const set = React13__default.useCallback(
2929
3111
  (patch) => {
2930
3112
  setStored((current) => ({ ...current ?? {}, ...patch }));
2931
3113
  if (!supported) return;
@@ -2941,7 +3123,7 @@ function useStoreSettings(provider, defaults = {}) {
2941
3123
  },
2942
3124
  [shop, supported]
2943
3125
  );
2944
- const value = React9.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
3126
+ const value = React13__default.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
2945
3127
  return { value, stored, set, loading, saving, error, loadError, supported };
2946
3128
  }
2947
3129
  function SettingsState({
@@ -3170,25 +3352,25 @@ function PaymentsSettings({ provider }) {
3170
3352
  const settings = useStoreSettings(provider);
3171
3353
  const { value, set } = settings;
3172
3354
  const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
3173
- const [adding, setAdding] = React9.useState(false);
3174
- const [selected, setSelected] = React9.useState(null);
3355
+ const [adding, setAdding] = React13__default.useState(false);
3356
+ const [selected, setSelected] = React13__default.useState(null);
3175
3357
  const panelAvailable = useConnectorsPanelAvailable();
3176
- const openJourney = React9.useCallback(
3358
+ const openJourney = React13__default.useCallback(
3177
3359
  (connector) => {
3178
3360
  if (panelAvailable) openConnectorsPanel(connector);
3179
3361
  else setSelected(connector);
3180
3362
  },
3181
3363
  [panelAvailable]
3182
3364
  );
3183
- const payment = React9.useMemo(
3365
+ const payment = React13__default.useMemo(
3184
3366
  () => (connectors ?? []).filter((c) => c.category && PAYMENT_CATEGORIES.includes(c.category)),
3185
3367
  [connectors]
3186
3368
  );
3187
- const live = React9.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
3369
+ const live = React13__default.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
3188
3370
  const chosen = value.paymentMethods ?? [];
3189
3371
  const staleProvider = !!value.paymentProvider && !live.some((c) => c.slug === value.paymentProvider) && live.length === 0;
3190
- const movedFrom = React9.useRef(null);
3191
- React9.useEffect(() => {
3372
+ const movedFrom = React13__default.useRef(null);
3373
+ React13__default.useEffect(() => {
3192
3374
  if (!settings.supported || settings.loading) return;
3193
3375
  const next = resolvePaymentProvider(value.paymentProvider, live.map((c) => c.slug));
3194
3376
  if (!next) return;
@@ -3420,11 +3602,11 @@ function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }
3420
3602
  ] });
3421
3603
  }
3422
3604
  function HandlingSettings({ settings, provider }) {
3423
- const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3605
+ const shop = React13__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3424
3606
  const { value, set, loading, supported } = settings;
3425
- const [noteDraft, setNoteDraft] = React9.useState(null);
3426
- const [profiles, setProfiles] = React9.useState(null);
3427
- React9.useEffect(() => {
3607
+ const [noteDraft, setNoteDraft] = React13__default.useState(null);
3608
+ const [profiles, setProfiles] = React13__default.useState(null);
3609
+ React13__default.useEffect(() => {
3428
3610
  let cancelled = false;
3429
3611
  void shop.listShippingProfiles?.().then((list) => {
3430
3612
  if (!cancelled) setProfiles(list);
@@ -3532,12 +3714,988 @@ function ChoiceCards({
3532
3714
  );
3533
3715
  }) });
3534
3716
  }
3717
+ var EMPTY = { rules: [], gaps: [] };
3718
+ var draft = EMPTY;
3719
+ var listeners = /* @__PURE__ */ new Set();
3720
+ var seq = 0;
3721
+ var nextId = () => `rule-${++seq}`;
3722
+ var identity = (rule) => [rule.matchKind, rule.cityCode ?? "", rule.districtKey ?? "", rule.postalFrom ?? "", rule.postalTo ?? ""].join("|");
3723
+ function emit() {
3724
+ for (const listener of listeners) listener();
3725
+ }
3726
+ function getDeliveryDraft() {
3727
+ return draft;
3728
+ }
3729
+ function setDeliveryDraft(next) {
3730
+ draft = { ...next, rules: next.rules.map((rule) => ({ ...rule, id: rule.id || nextId() })) };
3731
+ emit();
3732
+ }
3733
+ function addDeliveryDraftRules(incoming) {
3734
+ const seen = new Set(draft.rules.map(identity));
3735
+ const added = [];
3736
+ for (const rule of incoming) {
3737
+ const key = identity(rule);
3738
+ if (seen.has(key)) continue;
3739
+ seen.add(key);
3740
+ added.push({ ...rule, id: nextId() });
3741
+ }
3742
+ if (added.length === 0) return 0;
3743
+ draft = { ...draft, rules: [...draft.rules, ...added] };
3744
+ emit();
3745
+ return added.length;
3746
+ }
3747
+ function isInDeliveryDraft(rule) {
3748
+ const key = identity(rule);
3749
+ return draft.rules.some((existing) => identity(existing) === key);
3750
+ }
3751
+ function setAllDeliveryDraftRates(patch) {
3752
+ let touched = 0;
3753
+ const rules = draft.rules.map((rule) => {
3754
+ if (rule.outcome === "refuse") return rule;
3755
+ touched++;
3756
+ return { ...rule, ...patch };
3757
+ });
3758
+ if (touched === 0) return 0;
3759
+ draft = { ...draft, rules };
3760
+ emit();
3761
+ return touched;
3762
+ }
3763
+ function updateDeliveryDraftRule(id, patch) {
3764
+ const index = draft.rules.findIndex((rule) => rule.id === id);
3765
+ if (index < 0) return false;
3766
+ const rules = draft.rules.slice();
3767
+ rules[index] = { ...rules[index], ...patch, id };
3768
+ draft = { ...draft, rules };
3769
+ emit();
3770
+ return true;
3771
+ }
3772
+ function removeDeliveryDraftRule(id) {
3773
+ const rules = draft.rules.filter((rule) => rule.id !== id);
3774
+ if (rules.length === draft.rules.length) return false;
3775
+ draft = { ...draft, rules };
3776
+ emit();
3777
+ return true;
3778
+ }
3779
+ function clearDeliveryDraft() {
3780
+ draft = EMPTY;
3781
+ emit();
3782
+ }
3783
+ function subscribeDeliveryDraft(listener) {
3784
+ listeners.add(listener);
3785
+ return () => {
3786
+ listeners.delete(listener);
3787
+ };
3788
+ }
3789
+ function useDeliveryDraft() {
3790
+ return React13.useSyncExternalStore(subscribeDeliveryDraft, getDeliveryDraft, getDeliveryDraft);
3791
+ }
3792
+
3793
+ // src/views/delivery-agent.ts
3794
+ var DELIVERY_AREAS_INSTRUCTIONS = `
3795
+ Esta \xE9 a tela de Entrega da loja. Aqui voc\xEA configura ONDE a loja entrega, ONDE ela recusa e onde precisa confirmar antes.
3796
+
3797
+ Uma regra tem duas partes: a \xC1REA (um munic\xEDpio ou um bairro) e o DESFECHO:
3798
+ \xB7 entrego (deliver) \xB7 confirmo antes (review) \xB7 n\xE3o entrego (refuse)
3799
+ 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.
3800
+
3801
+ 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.
3802
+
3803
+ Quando a lojista colar uma mensagem descrevendo a opera\xE7\xE3o dela:
3804
+ 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.
3805
+ 2. Chame \`findDeliveryAreas\` UMA vez com todos os nomes juntos.
3806
+ \xB7 \`resolved\` est\xE1 pronto para virar regra.
3807
+ \xB7 \`options\` \xE9 amb\xEDguo: pergunte qual, n\xE3o escolha.
3808
+ \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".
3809
+ \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.
3810
+ 3. Chame \`proposeDeliveryRules\` com o que resolveu. Ele N\xC3O grava \u2014 desenha a tabela na tela dela.
3811
+ 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?".
3812
+ 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.
3813
+
3814
+ Regras:
3815
+ \xB7 Voc\xEA NUNCA escreve um CEP ou um c\xF3digo de munic\xEDpio que n\xE3o tenha vindo de \`findDeliveryAreas\`.
3816
+ \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.
3817
+ \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.
3818
+ \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.
3819
+ \xB7 Portugu\xEAs do Brasil, curto.
3820
+ `.trim();
3821
+ var FIND_AREAS = {
3822
+ id: "shop.delivery.find-areas",
3823
+ name: "findDeliveryAreas",
3824
+ 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.',
3825
+ icon: "MapPin",
3826
+ mode: "read",
3827
+ execution: { plane: "client" },
3828
+ category: "Shop",
3829
+ role: "operations",
3830
+ parameters: {
3831
+ type: "object",
3832
+ properties: {
3833
+ queries: {
3834
+ type: "array",
3835
+ description: 'The place names as the merchant wrote them, without "n\xE3o entrego" / "exceto".',
3836
+ items: { type: "string" }
3837
+ }
3838
+ },
3839
+ required: ["queries"]
3840
+ }
3841
+ };
3842
+ var PROPOSE_RULES = {
3843
+ id: "shop.delivery.propose-rules",
3844
+ name: "proposeDeliveryRules",
3845
+ 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.",
3846
+ icon: "ListChecks",
3847
+ mode: "read",
3848
+ execution: { plane: "client" },
3849
+ category: "Shop",
3850
+ role: "operations",
3851
+ parameters: {
3852
+ type: "object",
3853
+ properties: {
3854
+ rules: {
3855
+ type: "array",
3856
+ description: "One entry per area named in the message.",
3857
+ items: {
3858
+ type: "object",
3859
+ properties: {
3860
+ kind: {
3861
+ type: "string",
3862
+ enum: ["city", "district", "group", "postal_range"],
3863
+ 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."
3864
+ },
3865
+ label: { type: "string", description: "The place name, as it should read on the table." },
3866
+ cityCode: { type: "string", description: "IBGE code from findDeliveryAreas." },
3867
+ districtKey: { type: "string", description: "Neighbourhood key from findDeliveryAreas." },
3868
+ postalFrom: { type: "string", description: "Only for postal_range." },
3869
+ postalTo: { type: "string", description: "Only for postal_range." },
3870
+ outcome: {
3871
+ type: "string",
3872
+ enum: ["deliver", "review", "refuse"],
3873
+ description: "deliver | review (confirm before dispatch) | refuse."
3874
+ },
3875
+ rate: { type: "number", description: "Only if the message stated a price. Otherwise omit." },
3876
+ freeAbove: { type: "number", description: "Only if the message stated a threshold." },
3877
+ etaMinDays: { type: "number" },
3878
+ etaMaxDays: { type: "number" },
3879
+ source: { type: "string", description: "The words of the message this rule came from." }
3880
+ },
3881
+ required: ["kind", "label", "outcome"]
3882
+ }
3883
+ },
3884
+ gaps: {
3885
+ type: "array",
3886
+ description: "What the message named and you could NOT resolve, so the merchant sees it too.",
3887
+ items: {
3888
+ type: "object",
3889
+ properties: {
3890
+ query: { type: "string" },
3891
+ reason: { type: "string", description: "Plain Portuguese, one line." }
3892
+ },
3893
+ required: ["query", "reason"]
3894
+ }
3895
+ }
3896
+ },
3897
+ required: ["rules"]
3898
+ }
3899
+ };
3900
+ var SET_RULE = {
3901
+ id: "shop.delivery.set-rule",
3902
+ name: "setDeliveryRule",
3903
+ 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.",
3904
+ icon: "Pencil",
3905
+ mode: "read",
3906
+ execution: { plane: "client" },
3907
+ category: "Shop",
3908
+ role: "operations",
3909
+ parameters: {
3910
+ type: "object",
3911
+ properties: {
3912
+ id: { type: "string", description: "The rule id proposeDeliveryRules answered." },
3913
+ outcome: { type: "string", enum: ["deliver", "review", "refuse"] },
3914
+ rate: { type: "number" },
3915
+ freeAbove: { type: "number" },
3916
+ etaMinDays: { type: "number" },
3917
+ etaMaxDays: { type: "number" }
3918
+ },
3919
+ required: ["id"]
3920
+ }
3921
+ };
3922
+ var COMMIT_RULES = {
3923
+ id: "shop.delivery.commit-rules",
3924
+ name: "commitDeliveryRules",
3925
+ 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.",
3926
+ icon: "Save",
3927
+ mode: "persist",
3928
+ execution: { plane: "client" },
3929
+ category: "Shop",
3930
+ role: "operations",
3931
+ permission: { feature: "shop", action: "update" },
3932
+ parameters: { type: "object", properties: {} }
3933
+ };
3934
+ var CHECK_DELIVERY = {
3935
+ id: "shop.delivery.check",
3936
+ name: "checkDeliveryFor",
3937
+ 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?".',
3938
+ icon: "Search",
3939
+ mode: "read",
3940
+ execution: { plane: "client" },
3941
+ category: "Shop",
3942
+ role: "operations",
3943
+ parameters: {
3944
+ type: "object",
3945
+ properties: {
3946
+ postalCodes: {
3947
+ type: "array",
3948
+ description: "One or more CEPs, with or without punctuation.",
3949
+ items: { type: "string" }
3950
+ }
3951
+ },
3952
+ required: ["postalCodes"]
3953
+ }
3954
+ };
3955
+ [
3956
+ FIND_AREAS.id,
3957
+ PROPOSE_RULES.id,
3958
+ SET_RULE.id,
3959
+ COMMIT_RULES.id,
3960
+ CHECK_DELIVERY.id
3961
+ ];
3962
+ var text2 = (value) => typeof value === "string" ? value.trim() : "";
3963
+ var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
3964
+ var OUTCOMES = ["deliver", "review", "refuse"];
3965
+ var outcomeOf = (value) => OUTCOMES.includes(value) ? value : "deliver";
3966
+ function candidateOut(area) {
3967
+ return {
3968
+ kind: area.kind,
3969
+ label: area.label,
3970
+ cityCode: area.cityCode ?? void 0,
3971
+ cityName: area.cityName ?? void 0,
3972
+ state: area.state ?? void 0,
3973
+ districtKey: area.districtKey ?? void 0,
3974
+ knownPostalCodes: area.knownPostalCodes || void 0,
3975
+ members: area.members?.map((m) => ({
3976
+ kind: m.kind,
3977
+ label: m.label,
3978
+ cityCode: m.cityCode ?? void 0,
3979
+ districtKey: m.districtKey ?? void 0
3980
+ }))
3981
+ };
3982
+ }
3983
+ function pickCandidate(query, areas) {
3984
+ if (areas.length === 0) return {};
3985
+ if (areas.length === 1) return { resolved: areas[0] };
3986
+ const key = query.trim().toLowerCase();
3987
+ const exact = areas.filter((a) => a.label.trim().toLowerCase() === key);
3988
+ if (exact.length === 1) return { resolved: exact[0] };
3989
+ return { options: areas.slice(0, 6) };
3990
+ }
3991
+ function ruleFromArgs(raw, index) {
3992
+ const kind = text2(raw.kind);
3993
+ const label = text2(raw.label);
3994
+ if (!label) return null;
3995
+ const base = {
3996
+ id: `draft-${index}`,
3997
+ label,
3998
+ outcome: outcomeOf(raw.outcome),
3999
+ rate: number(raw.rate),
4000
+ freeAbove: number(raw.freeAbove),
4001
+ etaMinDays: number(raw.etaMinDays),
4002
+ etaMaxDays: number(raw.etaMaxDays),
4003
+ source: text2(raw.source) || void 0
4004
+ };
4005
+ if (kind === "city") {
4006
+ const cityCode = text2(raw.cityCode);
4007
+ if (!cityCode) return null;
4008
+ return { ...base, matchKind: "city", cityCode };
4009
+ }
4010
+ if (kind === "district") {
4011
+ const districtKey = text2(raw.districtKey);
4012
+ if (!districtKey) return null;
4013
+ return {
4014
+ ...base,
4015
+ matchKind: "district",
4016
+ districtKey,
4017
+ cityCode: text2(raw.cityCode) || null
4018
+ };
4019
+ }
4020
+ if (kind === "postal_range") {
4021
+ const from = text2(raw.postalFrom).replace(/\D/g, "");
4022
+ const to = text2(raw.postalTo).replace(/\D/g, "");
4023
+ if (from.length !== 8 || to.length !== 8) return null;
4024
+ return { ...base, matchKind: "postal_range", postalFrom: from, postalTo: to };
4025
+ }
4026
+ return null;
4027
+ }
4028
+ function zoneInputFrom(rule) {
4029
+ return {
4030
+ name: rule.label,
4031
+ carrier: null,
4032
+ matchKind: rule.matchKind,
4033
+ cityCode: rule.cityCode ?? null,
4034
+ districtKey: rule.districtKey ?? null,
4035
+ outcome: rule.outcome,
4036
+ postalFrom: rule.postalFrom ?? null,
4037
+ postalTo: rule.postalTo ?? null,
4038
+ // Uma regra de recusa não cobra nada: o preço dela nunca é lido.
4039
+ rate: rule.outcome === "refuse" ? 0 : rule.rate ?? 0,
4040
+ freeAbove: rule.freeAbove ?? null,
4041
+ etaMinDays: rule.etaMinDays ?? null,
4042
+ etaMaxDays: rule.etaMaxDays ?? null,
4043
+ active: true,
4044
+ sortOrder: 0,
4045
+ excluded: rule.outcome === "refuse"
4046
+ };
4047
+ }
4048
+ function createDeliveryAreaTools({ getProvider, onSaved }) {
4049
+ const failed = (error) => ({ ok: false, error: errorMessage(error) });
4050
+ return [
4051
+ {
4052
+ tool: FIND_AREAS,
4053
+ execute: async (args) => {
4054
+ const queries = (Array.isArray(args.queries) ? args.queries : []).map(text2).filter(Boolean).slice(0, 30);
4055
+ if (queries.length === 0) return { ok: false, error: "no_queries" };
4056
+ try {
4057
+ const shop = getProvider();
4058
+ if (typeof shop.findAreas !== "function") {
4059
+ return { ok: false, error: "area_lookup_unavailable" };
4060
+ }
4061
+ const results = await Promise.all(
4062
+ queries.map(async (query) => {
4063
+ const areas = await shop.findAreas(query, 8).catch(() => []);
4064
+ const { resolved, options } = pickCandidate(query, areas);
4065
+ if (resolved) return { query, resolved: candidateOut(resolved) };
4066
+ if (options) return { query, options: options.map(candidateOut) };
4067
+ return {
4068
+ query,
4069
+ notFound: true,
4070
+ // Dito aqui e não deixado para o modelo deduzir: é a frase que
4071
+ // separa "não existe" de "existe mas não dá para separar por CEP".
4072
+ 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."
4073
+ };
4074
+ })
4075
+ );
4076
+ return { ok: true, results };
4077
+ } catch (error) {
4078
+ return failed(error);
4079
+ }
4080
+ }
4081
+ },
4082
+ {
4083
+ tool: PROPOSE_RULES,
4084
+ execute: async (args) => {
4085
+ const raw = Array.isArray(args.rules) ? args.rules : [];
4086
+ const rules = [];
4087
+ const gaps = (Array.isArray(args.gaps) ? args.gaps : []).map((g) => ({ query: text2(g?.query), reason: text2(g?.reason) })).filter((g) => g.query);
4088
+ raw.forEach((entry, index) => {
4089
+ const rule = ruleFromArgs(entry ?? {}, rules.length);
4090
+ if (rule) {
4091
+ rules.push(rule);
4092
+ return;
4093
+ }
4094
+ gaps.push({
4095
+ query: text2(entry?.label) || `regra ${index + 1}`,
4096
+ reason: "Faltou o identificador da \xE1rea. Chame findDeliveryAreas para este nome e use o que ele responder."
4097
+ });
4098
+ });
4099
+ if (rules.length === 0 && gaps.length === 0) return { ok: false, error: "empty_draft" };
4100
+ setDeliveryDraft({ rules, gaps });
4101
+ return {
4102
+ ok: true,
4103
+ drafted: rules.length,
4104
+ refusing: rules.filter((r) => r.outcome === "refuse").length,
4105
+ reviewing: rules.filter((r) => r.outcome === "review").length,
4106
+ missingPrice: rules.filter((r) => r.outcome !== "refuse" && r.rate == null).length,
4107
+ gaps,
4108
+ rules: rules.map((r) => ({ id: r.id, label: r.label, outcome: r.outcome })),
4109
+ nextStep: "commitDeliveryRules"
4110
+ };
4111
+ }
4112
+ },
4113
+ {
4114
+ tool: SET_RULE,
4115
+ execute: async (args) => {
4116
+ const id = text2(args.id);
4117
+ if (!id) return { ok: false, error: "no_id" };
4118
+ const patch = {};
4119
+ if (args.outcome !== void 0) patch.outcome = outcomeOf(args.outcome);
4120
+ if (args.rate !== void 0) patch.rate = number(args.rate);
4121
+ if (args.freeAbove !== void 0) patch.freeAbove = number(args.freeAbove);
4122
+ if (args.etaMinDays !== void 0) patch.etaMinDays = number(args.etaMinDays);
4123
+ if (args.etaMaxDays !== void 0) patch.etaMaxDays = number(args.etaMaxDays);
4124
+ if (Object.keys(patch).length === 0) return { ok: false, error: "nothing_to_change" };
4125
+ if (!updateDeliveryDraftRule(id, patch)) return { ok: false, error: "rule_not_in_draft" };
4126
+ return { ok: true, id, ...patch };
4127
+ }
4128
+ },
4129
+ {
4130
+ tool: COMMIT_RULES,
4131
+ execute: async () => {
4132
+ const { rules } = getDeliveryDraft();
4133
+ if (rules.length === 0) return { ok: false, error: "nothing_to_save" };
4134
+ try {
4135
+ const shop = getProvider();
4136
+ if (typeof shop.createShippingZone !== "function") {
4137
+ return { ok: false, error: "zones_unavailable" };
4138
+ }
4139
+ const saved = [];
4140
+ const rejected = [];
4141
+ for (const rule of rules) {
4142
+ try {
4143
+ await shop.createShippingZone(zoneInputFrom(rule));
4144
+ saved.push(rule.label);
4145
+ } catch (error) {
4146
+ rejected.push({ label: rule.label, error: errorMessage(error) });
4147
+ }
4148
+ }
4149
+ if (saved.length > 0) {
4150
+ clearDeliveryDraft();
4151
+ onSaved();
4152
+ }
4153
+ return { ok: rejected.length === 0, saved: saved.length, rejected };
4154
+ } catch (error) {
4155
+ return failed(error);
4156
+ }
4157
+ }
4158
+ },
4159
+ {
4160
+ tool: CHECK_DELIVERY,
4161
+ execute: async (args) => {
4162
+ const codes = (Array.isArray(args.postalCodes) ? args.postalCodes : []).map((c) => text2(c).replace(/\D/g, "")).filter((c) => c.length === 8).slice(0, 10);
4163
+ if (codes.length === 0) return { ok: false, error: "no_postal_codes" };
4164
+ try {
4165
+ const shop = getProvider();
4166
+ if (typeof shop.checkDelivery !== "function") {
4167
+ return { ok: false, error: "delivery_check_unavailable" };
4168
+ }
4169
+ const results = await Promise.all(
4170
+ codes.map(async (code) => {
4171
+ const check = await shop.checkDelivery(code);
4172
+ return {
4173
+ postalCode: code,
4174
+ // null = nenhuma regra casou: fora de área, que não é o mesmo
4175
+ // que recusado e o texto para a lojista é outro.
4176
+ outcome: check.outcome ?? null,
4177
+ rule: check.zoneName ?? check.refusedZone ?? null,
4178
+ outsideEveryRule: !check.refused && !check.ownZone
4179
+ };
4180
+ })
4181
+ );
4182
+ return { ok: true, results };
4183
+ } catch (error) {
4184
+ return failed(error);
4185
+ }
4186
+ }
4187
+ }
4188
+ ];
4189
+ }
4190
+
4191
+ // src/views/DeliveryAgentSurface.tsx
4192
+ function DeliveryAgentSurface({ provider, zones, onSaved }) {
4193
+ const live = React13.useRef({ provider, zones, onSaved });
4194
+ live.current = { provider, zones, onSaved };
4195
+ const contribution = React13.useMemo(() => ({
4196
+ id: "shop.delivery.settings",
4197
+ pluginId: "shop",
4198
+ role: "operations",
4199
+ instructions: DELIVERY_AREAS_INSTRUCTIONS,
4200
+ describe: () => {
4201
+ const { zones: rows } = live.current;
4202
+ const draft2 = getDeliveryDraft();
4203
+ const byOutcome = (outcome) => rows.filter((z) => z.active && (z.outcome ?? (z.excluded ? "refuse" : "deliver")) === outcome);
4204
+ return {
4205
+ title: "Configura\xE7\xE3o \u2014 Entrega",
4206
+ state: {
4207
+ screen: "/settings/shop-shipping",
4208
+ savedRules: {
4209
+ delivering: byOutcome("deliver").length,
4210
+ reviewing: byOutcome("review").length,
4211
+ refusing: byOutcome("refuse").length,
4212
+ // Os nomes, não as faixas: o agente precisa saber o que JÁ existe
4213
+ // para não propor a mesma região duas vezes.
4214
+ names: rows.filter((z) => z.active).slice(0, 40).map((z) => z.name)
4215
+ },
4216
+ draft: draft2.rules.length === 0 && draft2.gaps.length === 0 ? null : {
4217
+ rules: draft2.rules.map((r) => ({
4218
+ id: r.id,
4219
+ label: r.label,
4220
+ outcome: r.outcome,
4221
+ rate: r.rate ?? null,
4222
+ freeAbove: r.freeAbove ?? null
4223
+ })),
4224
+ gaps: draft2.gaps
4225
+ }
4226
+ }
4227
+ };
4228
+ },
4229
+ tools: createDeliveryAreaTools({
4230
+ getProvider: () => live.current.provider(),
4231
+ onSaved: () => live.current.onSaved()
4232
+ })
4233
+ }), []);
4234
+ useAgentSurface(contribution);
4235
+ return null;
4236
+ }
4237
+ var MIN_QUERY = 2;
4238
+ var DEBOUNCE_MS2 = 250;
4239
+ function ruleFor(area, outcome) {
4240
+ if (area.kind === "city" && area.cityCode) {
4241
+ return { matchKind: "city", label: area.label, cityCode: area.cityCode, outcome };
4242
+ }
4243
+ if (area.kind === "district" && area.districtKey) {
4244
+ return {
4245
+ matchKind: "district",
4246
+ label: area.label,
4247
+ districtKey: area.districtKey,
4248
+ cityCode: area.cityCode ?? null,
4249
+ outcome
4250
+ };
4251
+ }
4252
+ return null;
4253
+ }
4254
+ function rulesFor(area, outcome) {
4255
+ if (area.kind !== "group") {
4256
+ const single = ruleFor(area, outcome);
4257
+ return single ? [single] : [];
4258
+ }
4259
+ return (area.members ?? []).flatMap((member) => {
4260
+ const rule = ruleFor(
4261
+ { kind: member.kind, label: member.label, cityCode: member.cityCode, districtKey: member.districtKey },
4262
+ outcome
4263
+ );
4264
+ return rule ? [rule] : [];
4265
+ });
4266
+ }
4267
+ function areaMeta(area) {
4268
+ if (area.matchedAs) return area.matchedAs;
4269
+ if (area.kind === "group") {
4270
+ const n = area.members?.length ?? 0;
4271
+ return `${n} ${n === 1 ? "regi\xE3o" : "regi\xF5es"}${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
4272
+ }
4273
+ if (area.kind === "city") return `Cidade inteira${area.state ? ` \xB7 ${area.state}` : ""}`;
4274
+ return `Bairro${area.cityName ? ` \xB7 ${area.cityName}` : ""}`;
4275
+ }
4276
+ var areaKeyOf = (area) => `${area.kind}:${area.districtKey ?? area.cityCode ?? area.label}`;
4277
+ function AreaSearch({ provider, outcome, placeholder, onlyCityCodes, emptyHint }) {
4278
+ const [query, setQuery] = React13__default.useState("");
4279
+ const [results, setResults] = React13__default.useState(null);
4280
+ const [loading, setLoading] = React13__default.useState(false);
4281
+ const [unavailable, setUnavailable] = React13__default.useState(false);
4282
+ const [justAdded, setJustAdded] = React13__default.useState(null);
4283
+ React13__default.useEffect(() => {
4284
+ const text3 = query.trim();
4285
+ if (text3.length < MIN_QUERY) {
4286
+ setResults(null);
4287
+ return void 0;
4288
+ }
4289
+ let cancelled = false;
4290
+ setLoading(true);
4291
+ const timer = setTimeout(() => {
4292
+ const shop = provider ? resolve(provider) : getShopProvider();
4293
+ if (typeof shop.findAreas !== "function") {
4294
+ if (!cancelled) {
4295
+ setUnavailable(true);
4296
+ setLoading(false);
4297
+ }
4298
+ return;
4299
+ }
4300
+ void shop.findAreas(text3, 10).then((list) => {
4301
+ if (!cancelled) {
4302
+ setResults(list);
4303
+ setUnavailable(false);
4304
+ }
4305
+ }).catch(() => {
4306
+ if (!cancelled) {
4307
+ setResults([]);
4308
+ setUnavailable(true);
4309
+ }
4310
+ }).finally(() => {
4311
+ if (!cancelled) setLoading(false);
4312
+ });
4313
+ }, DEBOUNCE_MS2);
4314
+ return () => {
4315
+ cancelled = true;
4316
+ clearTimeout(timer);
4317
+ };
4318
+ }, [query, provider]);
4319
+ const add = (area) => {
4320
+ const rules = rulesFor(area, outcome);
4321
+ if (rules.length === 0) return;
4322
+ const added = addDeliveryDraftRules(rules);
4323
+ setJustAdded(areaKeyOf(area));
4324
+ setTimeout(() => setJustAdded(null), 1400);
4325
+ if (added > 0) setQuery("");
4326
+ };
4327
+ const visible = (results ?? []).filter((area) => {
4328
+ if (!onlyCityCodes || onlyCityCodes.length === 0) return true;
4329
+ if (area.kind === "city") return true;
4330
+ return !area.cityCode || onlyCityCodes.includes(area.cityCode);
4331
+ });
4332
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4333
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4334
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
4335
+ /* @__PURE__ */ jsx(
4336
+ "input",
4337
+ {
4338
+ value: query,
4339
+ onChange: (e) => setQuery(e.target.value),
4340
+ placeholder,
4341
+ className: "w-full rounded-lg border border-border bg-background py-2 pl-9 pr-9 text-sm"
4342
+ }
4343
+ ),
4344
+ loading && /* @__PURE__ */ jsx(Loader2, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
4345
+ ] }),
4346
+ results !== null && /* @__PURE__ */ jsxs("ul", { className: "mt-2 max-h-72 divide-y divide-border overflow-y-auto rounded-lg border border-border", children: [
4347
+ 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 }),
4348
+ visible.map((area) => {
4349
+ const key = areaKeyOf(area);
4350
+ const already = area.kind !== "group" && isInDeliveryDraft({
4351
+ matchKind: area.kind === "city" ? "city" : "district",
4352
+ cityCode: area.cityCode ?? null,
4353
+ districtKey: area.districtKey ?? null
4354
+ });
4355
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
4356
+ "button",
4357
+ {
4358
+ type: "button",
4359
+ onClick: () => add(area),
4360
+ disabled: already,
4361
+ className: "flex w-full items-center gap-3 px-3 py-2.5 text-left transition hover:bg-muted/50 disabled:opacity-50",
4362
+ children: [
4363
+ 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" }),
4364
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
4365
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium", children: area.label }),
4366
+ /* @__PURE__ */ jsxs("span", { className: "block text-xs text-muted-foreground", children: [
4367
+ areaMeta(area),
4368
+ area.kind === "district" && !area.knownPostalCodes && " \xB7 ainda sem CEP visto"
4369
+ ] })
4370
+ ] }),
4371
+ 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" })
4372
+ ]
4373
+ }
4374
+ ) }, key);
4375
+ })
4376
+ ] })
4377
+ ] });
4378
+ }
4379
+ function DeliveryAreaPicker({ provider, onAskAssistant }) {
4380
+ const draft2 = useDeliveryDraft();
4381
+ const covered = draft2.rules.filter((r) => r.outcome !== "refuse");
4382
+ const coveredCities = covered.filter((r) => r.matchKind === "city");
4383
+ const exceptions = draft2.rules.filter((r) => r.outcome === "refuse");
4384
+ const cityCodes = covered.map((r) => r.cityCode).filter(Boolean);
4385
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card px-5 py-4 shadow-sm", children: [
4386
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
4387
+ /* @__PURE__ */ jsxs("div", { children: [
4388
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega?" }),
4389
+ /* @__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.' })
4390
+ ] }),
4391
+ onAskAssistant && /* @__PURE__ */ jsxs(
4392
+ "button",
4393
+ {
4394
+ type: "button",
4395
+ onClick: onAskAssistant,
4396
+ 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",
4397
+ children: [
4398
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 text-primary" }),
4399
+ "Prefiro contar por escrito"
4400
+ ]
4401
+ }
4402
+ )
4403
+ ] }),
4404
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
4405
+ AreaSearch,
4406
+ {
4407
+ provider,
4408
+ outcome: "deliver",
4409
+ placeholder: "Cidade, bairro ou regi\xE3o\u2026",
4410
+ emptyHint: "Nada com esse nome. Tente a cidade, ou conte por escrito ao assistente."
4411
+ }
4412
+ ) }),
4413
+ covered.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-5 border-t border-border pt-4", children: [
4414
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4415
+ /* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 text-destructive" }),
4416
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "E onde voc\xEA n\xE3o entrega?" })
4417
+ ] }),
4418
+ /* @__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." }),
4419
+ /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
4420
+ AreaSearch,
4421
+ {
4422
+ provider,
4423
+ outcome: "refuse",
4424
+ placeholder: "Bairro ou regi\xE3o que voc\xEA recusa\u2026",
4425
+ onlyCityCodes: cityCodes,
4426
+ 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." })
4427
+ }
4428
+ ) })
4429
+ ] }),
4430
+ draft2.rules.length > 0 && /* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs text-muted-foreground", children: [
4431
+ covered.length,
4432
+ " ",
4433
+ covered.length === 1 ? "regi\xE3o" : "regi\xF5es",
4434
+ " onde voc\xEA entrega",
4435
+ exceptions.length > 0 && `, ${exceptions.length} ${exceptions.length === 1 ? "exce\xE7\xE3o" : "exce\xE7\xF5es"}`,
4436
+ " ",
4437
+ "\u2014 confira o pre\xE7o na tabela abaixo e salve."
4438
+ ] })
4439
+ ] });
4440
+ }
4441
+ var OUTCOME_LABEL = {
4442
+ deliver: "Entrego",
4443
+ review: "Confirmo antes",
4444
+ refuse: "N\xE3o entrego"
4445
+ };
4446
+ var OUTCOME_TONE = {
4447
+ deliver: "text-foreground",
4448
+ review: "text-amber-600 dark:text-amber-500",
4449
+ refuse: "text-destructive"
4450
+ };
3535
4451
  function parseMoney(raw) {
3536
4452
  const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
3537
4453
  if (!cleaned.trim()) return null;
3538
4454
  const n = Number(cleaned);
3539
4455
  return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
3540
4456
  }
4457
+ var money2 = (v) => v == null ? "" : String(v).replace(".", ",");
4458
+ function ruleArea(rule) {
4459
+ if (rule.matchKind === "postal_range") {
4460
+ const fmt = (c) => c ? `${c.slice(0, 5)}-${c.slice(5)}` : "?";
4461
+ return `${fmt(rule.postalFrom)} \u2192 ${fmt(rule.postalTo)}`;
4462
+ }
4463
+ return rule.matchKind === "city" ? "Munic\xEDpio" : "Bairro";
4464
+ }
4465
+ function DeliveryRulesDraft({ provider, onSaved }) {
4466
+ const draft2 = useDeliveryDraft();
4467
+ const [saving, setSaving] = React13__default.useState(false);
4468
+ const [error, setError] = React13__default.useState(null);
4469
+ const [bulkRate, setBulkRate] = React13__default.useState("");
4470
+ const [bulkFree, setBulkFree] = React13__default.useState("");
4471
+ if (draft2.rules.length === 0 && draft2.gaps.length === 0) return null;
4472
+ const refusing = draft2.rules.filter((r) => r.outcome === "refuse").length;
4473
+ const reviewing = draft2.rules.filter((r) => r.outcome === "review").length;
4474
+ const delivering = draft2.rules.length - refusing - reviewing;
4475
+ const save = async () => {
4476
+ setSaving(true);
4477
+ setError(null);
4478
+ try {
4479
+ const shop = provider ? resolve(provider) : getShopProvider();
4480
+ if (typeof shop.createShippingZone !== "function") {
4481
+ throw new Error("Este banco ainda n\xE3o tem as regras de entrega.");
4482
+ }
4483
+ for (const rule of draft2.rules) {
4484
+ await shop.createShippingZone(zoneInputFrom(rule));
4485
+ }
4486
+ clearDeliveryDraft();
4487
+ onSaved();
4488
+ } catch (err) {
4489
+ setError(errorMessage(err));
4490
+ } finally {
4491
+ setSaving(false);
4492
+ }
4493
+ };
4494
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-primary/40 bg-primary/5 shadow-sm", children: [
4495
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b border-primary/20 px-5 py-4", children: [
4496
+ /* @__PURE__ */ jsx(ListChecks, { className: "h-4 w-4 text-primary" }),
4497
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
4498
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Regras a salvar" }),
4499
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
4500
+ delivering,
4501
+ " entrega",
4502
+ delivering === 1 ? "" : "s",
4503
+ reviewing > 0 && `, ${reviewing} a confirmar`,
4504
+ refusing > 0 && `, ${refusing} recusa${refusing === 1 ? "" : "s"}`,
4505
+ " ",
4506
+ "\u2014 confira e corrija antes de salvar. Nada foi gravado ainda."
4507
+ ] })
4508
+ ] }),
4509
+ /* @__PURE__ */ jsx(
4510
+ "button",
4511
+ {
4512
+ type: "button",
4513
+ onClick: clearDeliveryDraft,
4514
+ disabled: saving,
4515
+ className: "rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-muted",
4516
+ children: "Descartar"
4517
+ }
4518
+ ),
4519
+ /* @__PURE__ */ jsxs(
4520
+ "button",
4521
+ {
4522
+ type: "button",
4523
+ onClick: save,
4524
+ disabled: saving || draft2.rules.length === 0,
4525
+ 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",
4526
+ children: [
4527
+ saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
4528
+ "Salvar ",
4529
+ draft2.rules.length,
4530
+ " regra",
4531
+ draft2.rules.length === 1 ? "" : "s"
4532
+ ]
4533
+ }
4534
+ )
4535
+ ] }),
4536
+ error && /* @__PURE__ */ jsx("p", { className: "px-5 pt-3 text-xs text-destructive", children: error }),
4537
+ delivering + reviewing > 1 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b border-primary/10 px-5 py-3", children: [
4538
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Mesmo pre\xE7o em todas:" }),
4539
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
4540
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "R$" }),
4541
+ /* @__PURE__ */ jsx(
4542
+ "input",
4543
+ {
4544
+ value: bulkRate,
4545
+ onChange: (e) => setBulkRate(e.target.value),
4546
+ placeholder: "15,00",
4547
+ "aria-label": "Frete para todas as regi\xF5es",
4548
+ className: "w-20 rounded-md border border-border bg-background px-2 py-1 text-xs"
4549
+ }
4550
+ )
4551
+ ] }),
4552
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
4553
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "gr\xE1tis acima de R$" }),
4554
+ /* @__PURE__ */ jsx(
4555
+ "input",
4556
+ {
4557
+ value: bulkFree,
4558
+ onChange: (e) => setBulkFree(e.target.value),
4559
+ placeholder: "opcional",
4560
+ "aria-label": "Frete gr\xE1tis acima de, para todas as regi\xF5es",
4561
+ className: "w-24 rounded-md border border-border bg-background px-2 py-1 text-xs"
4562
+ }
4563
+ )
4564
+ ] }),
4565
+ /* @__PURE__ */ jsx(
4566
+ "button",
4567
+ {
4568
+ type: "button",
4569
+ onClick: () => {
4570
+ setAllDeliveryDraftRates({
4571
+ rate: parseMoney(bulkRate),
4572
+ freeAbove: parseMoney(bulkFree)
4573
+ });
4574
+ setBulkRate("");
4575
+ setBulkFree("");
4576
+ },
4577
+ disabled: !bulkRate.trim() && !bulkFree.trim(),
4578
+ className: "rounded-md border border-border px-2.5 py-1 text-xs font-medium hover:bg-muted disabled:opacity-40",
4579
+ children: "Aplicar"
4580
+ }
4581
+ )
4582
+ ] }),
4583
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
4584
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-left text-xs text-muted-foreground", children: [
4585
+ /* @__PURE__ */ jsx("th", { className: "px-5 py-2.5 font-medium", children: "Regi\xE3o" }),
4586
+ /* @__PURE__ */ jsx("th", { className: "px-2 py-2.5 font-medium", children: "O que fa\xE7o" }),
4587
+ /* @__PURE__ */ jsx("th", { className: "px-2 py-2.5 font-medium", children: "Frete" }),
4588
+ /* @__PURE__ */ jsx("th", { className: "px-2 py-2.5 font-medium", children: "Gr\xE1tis acima de" }),
4589
+ /* @__PURE__ */ jsx("th", { className: "px-2 py-2.5 font-medium", children: "Prazo (dias)" }),
4590
+ /* @__PURE__ */ jsx("th", { className: "px-2 py-2" })
4591
+ ] }) }),
4592
+ /* @__PURE__ */ jsx("tbody", { children: draft2.rules.map((rule) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-primary/10", children: [
4593
+ /* @__PURE__ */ jsxs("td", { className: "px-5 py-2.5", children: [
4594
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: rule.label }),
4595
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: ruleArea(rule) }),
4596
+ rule.source && /* @__PURE__ */ jsxs("p", { className: "text-xs italic text-muted-foreground", children: [
4597
+ "\u201C",
4598
+ rule.source,
4599
+ "\u201D"
4600
+ ] })
4601
+ ] }),
4602
+ /* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsx(
4603
+ "select",
4604
+ {
4605
+ value: rule.outcome,
4606
+ onChange: (e) => updateDeliveryDraftRule(rule.id, { outcome: e.target.value }),
4607
+ className: `rounded-md border border-border bg-background px-2 py-1.5 text-xs ${OUTCOME_TONE[rule.outcome]}`,
4608
+ children: Object.keys(OUTCOME_LABEL).map((value) => /* @__PURE__ */ jsx("option", { value, children: OUTCOME_LABEL[value] }, value))
4609
+ }
4610
+ ) }),
4611
+ rule.outcome === "refuse" ? /* @__PURE__ */ jsx("td", { className: "px-2 py-2.5 text-xs text-muted-foreground", colSpan: 3, children: "n\xE3o se cobra" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4612
+ /* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded-md border border-border bg-background focus-within:ring-1 focus-within:ring-ring", children: [
4613
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
4614
+ /* @__PURE__ */ jsx(
4615
+ "input",
4616
+ {
4617
+ value: money2(rule.rate),
4618
+ placeholder: "0,00",
4619
+ "aria-label": `Frete para ${rule.label}`,
4620
+ onChange: (e) => updateDeliveryDraftRule(rule.id, { rate: parseMoney(e.target.value) }),
4621
+ className: "w-16 bg-transparent px-1.5 py-1.5 text-xs outline-none"
4622
+ }
4623
+ )
4624
+ ] }) }),
4625
+ /* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded-md border border-border bg-background focus-within:ring-1 focus-within:ring-ring", children: [
4626
+ /* @__PURE__ */ jsx("span", { className: "pl-2 text-xs text-muted-foreground", children: "R$" }),
4627
+ /* @__PURE__ */ jsx(
4628
+ "input",
4629
+ {
4630
+ value: money2(rule.freeAbove),
4631
+ placeholder: "nunca",
4632
+ "aria-label": `Frete gr\xE1tis acima de, em ${rule.label}`,
4633
+ onChange: (e) => updateDeliveryDraftRule(rule.id, { freeAbove: parseMoney(e.target.value) }),
4634
+ className: "w-20 bg-transparent px-1.5 py-1.5 text-xs outline-none"
4635
+ }
4636
+ )
4637
+ ] }) }),
4638
+ /* @__PURE__ */ jsx("td", { className: "px-2 py-2.5", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center rounded-md border border-border bg-background focus-within:ring-1 focus-within:ring-ring", children: [
4639
+ /* @__PURE__ */ jsx(
4640
+ "input",
4641
+ {
4642
+ value: rule.etaMinDays ?? "",
4643
+ placeholder: "0",
4644
+ inputMode: "numeric",
4645
+ "aria-label": `Prazo m\xEDnimo em ${rule.label}`,
4646
+ onChange: (e) => updateDeliveryDraftRule(rule.id, {
4647
+ etaMinDays: e.target.value ? Number(e.target.value) : null
4648
+ }),
4649
+ className: "w-10 bg-transparent py-1.5 pl-2 text-center text-xs outline-none"
4650
+ }
4651
+ ),
4652
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "a" }),
4653
+ /* @__PURE__ */ jsx(
4654
+ "input",
4655
+ {
4656
+ value: rule.etaMaxDays ?? "",
4657
+ placeholder: "2",
4658
+ inputMode: "numeric",
4659
+ "aria-label": `Prazo m\xE1ximo em ${rule.label}`,
4660
+ onChange: (e) => updateDeliveryDraftRule(rule.id, {
4661
+ etaMaxDays: e.target.value ? Number(e.target.value) : null
4662
+ }),
4663
+ className: "w-10 bg-transparent py-1.5 pr-2 text-center text-xs outline-none"
4664
+ }
4665
+ )
4666
+ ] }) })
4667
+ ] }),
4668
+ /* @__PURE__ */ jsx("td", { className: "px-5 py-2.5 text-right", children: /* @__PURE__ */ jsx(
4669
+ "button",
4670
+ {
4671
+ type: "button",
4672
+ onClick: () => removeDeliveryDraftRule(rule.id),
4673
+ "aria-label": `Remover ${rule.label}`,
4674
+ className: "rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-destructive",
4675
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
4676
+ }
4677
+ ) })
4678
+ ] }, rule.id)) })
4679
+ ] }) }),
4680
+ draft2.gaps.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t border-primary/20 px-5 py-3.5", children: [
4681
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "O que ficou de fora \u2014 e por qu\xEA" }),
4682
+ /* @__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: [
4683
+ /* @__PURE__ */ jsx(X, { className: "mt-0.5 h-3 w-3 shrink-0 text-destructive" }),
4684
+ /* @__PURE__ */ jsxs("span", { children: [
4685
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: gap.query }),
4686
+ " \u2014 ",
4687
+ gap.reason
4688
+ ] })
4689
+ ] }, gap.query)) })
4690
+ ] })
4691
+ ] });
4692
+ }
4693
+ function parseMoney2(raw) {
4694
+ const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
4695
+ if (!cleaned.trim()) return null;
4696
+ const n = Number(cleaned);
4697
+ return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
4698
+ }
3541
4699
  var formatMoney = (v) => v == null ? "" : String(v).replace(".", ",");
3542
4700
  var FREIGHT_SOURCE = [
3543
4701
  {
@@ -3554,46 +4712,48 @@ var FREIGHT_SOURCE = [
3554
4712
  }
3555
4713
  ];
3556
4714
  function ShippingSettings({ provider }) {
3557
- const [registryId, setRegistryId] = React9.useState(shippingRegistryIdFromHash);
3558
- React9.useEffect(() => {
4715
+ const [registryId, setRegistryId] = React13__default.useState(shippingRegistryIdFromHash);
4716
+ React13__default.useEffect(() => {
3559
4717
  const handler = () => setRegistryId(shippingRegistryIdFromHash());
3560
4718
  window.addEventListener("hashchange", handler);
3561
4719
  return () => window.removeEventListener("hashchange", handler);
3562
4720
  }, []);
3563
- const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3564
- const registries = React9.useMemo(
4721
+ const shop = React13__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
4722
+ const registries = React13__default.useMemo(
3565
4723
  () => buildShopRegistries(provider ?? getShopProvider),
3566
4724
  [provider]
3567
4725
  );
3568
4726
  const storeSettings = useStoreSettings(provider);
3569
4727
  const supported = typeof shop.getShippingSettings === "function" && typeof shop.updateShippingSettings === "function";
3570
- const [row, setRow] = React9.useState(null);
3571
- const [zones, setZones] = React9.useState(null);
3572
- const [locations, setLocations] = React9.useState(null);
3573
- const [loading, setLoading] = React9.useState(supported);
3574
- const [saving, setSaving] = React9.useState(false);
3575
- const [error, setError] = React9.useState(null);
3576
- const [loadError, setLoadError] = React9.useState(null);
3577
- const [flatDraft, setFlatDraft] = React9.useState("");
3578
- const [freeDraft, setFreeDraft] = React9.useState("");
3579
- const [flatBad, setFlatBad] = React9.useState(false);
3580
- const [freeBad, setFreeBad] = React9.useState(false);
4728
+ const [row, setRow] = React13__default.useState(null);
4729
+ const [zones, setZones] = React13__default.useState(null);
4730
+ const [locations, setLocations] = React13__default.useState(null);
4731
+ const [loading, setLoading] = React13__default.useState(supported);
4732
+ const [saving, setSaving] = React13__default.useState(false);
4733
+ const [error, setError] = React13__default.useState(null);
4734
+ const [loadError, setLoadError] = React13__default.useState(null);
4735
+ const [flatDraft, setFlatDraft] = React13__default.useState("");
4736
+ const [freeDraft, setFreeDraft] = React13__default.useState("");
4737
+ const [flatBad, setFlatBad] = React13__default.useState(false);
4738
+ const [freeBad, setFreeBad] = React13__default.useState(false);
3581
4739
  const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
3582
- const [selected, setSelected] = React9.useState(null);
4740
+ const [selected, setSelected] = React13__default.useState(null);
3583
4741
  const panelAvailable = useConnectorsPanelAvailable();
3584
- const openJourney = React9.useCallback(
4742
+ const openJourney = React13__default.useCallback(
3585
4743
  (connector) => {
3586
4744
  if (panelAvailable) openConnectorsPanel(connector);
3587
4745
  else setSelected(connector);
3588
4746
  },
3589
4747
  [panelAvailable]
3590
4748
  );
3591
- const carriers = React9.useMemo(
4749
+ const carriers = React13__default.useMemo(
3592
4750
  () => (connectors ?? []).filter((c) => c.category === "logistics" || c.category === "shipping"),
3593
4751
  [connectors]
3594
4752
  );
3595
4753
  const carriersConnected = carriers.some((c) => c.state !== "ready");
3596
- React9.useEffect(() => {
4754
+ const [zonesEpoch, setZonesEpoch] = React13__default.useState(0);
4755
+ const reloadZones = React13__default.useCallback(() => setZonesEpoch((n) => n + 1), []);
4756
+ React13__default.useEffect(() => {
3597
4757
  let cancelled = false;
3598
4758
  if (supported) {
3599
4759
  setLoading(true);
@@ -3621,8 +4781,8 @@ function ShippingSettings({ provider }) {
3621
4781
  return () => {
3622
4782
  cancelled = true;
3623
4783
  };
3624
- }, [shop, supported]);
3625
- const save = React9.useCallback(
4784
+ }, [shop, supported, zonesEpoch]);
4785
+ const save = React13__default.useCallback(
3626
4786
  (patch) => {
3627
4787
  if (!supported) return;
3628
4788
  setSaving(true);
@@ -3632,7 +4792,7 @@ function ShippingSettings({ provider }) {
3632
4792
  [shop, supported]
3633
4793
  );
3634
4794
  const commitFlat = () => {
3635
- const parsed = parseMoney(flatDraft);
4795
+ const parsed = parseMoney2(flatDraft);
3636
4796
  const next = flatDraft.trim() === "" ? 0 : parsed;
3637
4797
  if (next == null) {
3638
4798
  setFlatBad(true);
@@ -3648,7 +4808,7 @@ function ShippingSettings({ provider }) {
3648
4808
  if (row?.freeAbove != null) save({ freeAbove: null });
3649
4809
  return;
3650
4810
  }
3651
- const parsed = parseMoney(freeDraft);
4811
+ const parsed = parseMoney2(freeDraft);
3652
4812
  if (parsed == null) {
3653
4813
  setFreeBad(true);
3654
4814
  return;
@@ -3657,8 +4817,10 @@ function ShippingSettings({ provider }) {
3657
4817
  setFreeDraft(formatMoney(parsed));
3658
4818
  if (parsed !== row?.freeAbove) save({ freeAbove: parsed });
3659
4819
  };
3660
- const activeZones = (zones ?? []).filter((z) => z.active && !z.excluded);
3661
- const excludedZones = (zones ?? []).filter((z) => z.active && z.excluded);
4820
+ const outcomeOf2 = (z) => z.outcome ?? (z.excluded ? "refuse" : "deliver");
4821
+ const activeZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "deliver");
4822
+ const reviewZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "review");
4823
+ const excludedZones = (zones ?? []).filter((z) => z.active && outcomeOf2(z) === "refuse");
3662
4824
  const usesGateway = storeSettings.value.carrierQuotes !== false;
3663
4825
  const dispatching = (locations ?? []).filter((l) => l.status === "active" && l.shipsOnline !== false);
3664
4826
  const originCep = dispatching.find((l) => l.postalCode)?.postalCode ?? null;
@@ -3756,11 +4918,21 @@ function ShippingSettings({ provider }) {
3756
4918
  /* @__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." })
3757
4919
  ] })
3758
4920
  ] }),
3759
- /* @__PURE__ */ jsxs("div", { children: [
4921
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
3760
4922
  /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
3761
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Zonas de entrega" }),
3762
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Faixas de CEP com pre\xE7o, prazo e frete gr\xE1tis pr\xF3prios. Um CEP fora de todas as zonas cai no pre\xE7o base acima; se a loja n\xE3o tiver pre\xE7o base nem zona que o cubra, o checkout diz que n\xE3o entrega ali. Uma faixa tamb\xE9m pode ser uma recusa \u2014 \xABn\xE3o entrego aqui\xBB \u2014 e ela vence qualquer zona e qualquer gateway." })
4923
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Onde voc\xEA entrega" }),
4924
+ /* @__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." })
3763
4925
  ] }),
4926
+ /* @__PURE__ */ jsx(
4927
+ DeliveryAgentSurface,
4928
+ {
4929
+ provider: () => shop,
4930
+ zones: zones ?? [],
4931
+ onSaved: reloadZones
4932
+ }
4933
+ ),
4934
+ /* @__PURE__ */ jsx(DeliveryAreaPicker, { provider }),
4935
+ /* @__PURE__ */ jsx(DeliveryRulesDraft, { provider, onSaved: reloadZones }),
3764
4936
  /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
3765
4937
  "button",
3766
4938
  {
@@ -3772,10 +4944,15 @@ function ShippingSettings({ provider }) {
3772
4944
  children: [
3773
4945
  /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3774
4946
  /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
3775
- /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium", children: zones == null ? "Carregando zonas\u2026" : activeZones.length === 0 ? "Nenhuma zona ainda" : `${activeZones.length} ${activeZones.length === 1 ? "zona ativa" : "zonas ativas"}` }),
4947
+ /* @__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"}` }),
3776
4948
  /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
3777
- activeZones.length === 0 ? "Sem zonas, toda entrega usa o pre\xE7o base." : activeZones.slice(0, 3).map((z) => z.name).join(" \xB7 "),
4949
+ 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 "),
3778
4950
  activeZones.length > 3 && ` e ${activeZones.length - 3} mais`,
4951
+ reviewZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-amber-700 dark:text-amber-500", children: [
4952
+ reviewZones.length === 1 ? "1 regi\xE3o a confirmar antes de sair" : `${reviewZones.length} regi\xF5es a confirmar antes de sair`,
4953
+ " \u2014 ",
4954
+ reviewZones.slice(0, 2).map((z) => z.name).join(" \xB7 ")
4955
+ ] }),
3779
4956
  excludedZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-red-700 dark:text-red-400", children: [
3780
4957
  excludedZones.length === 1 ? "1 regi\xE3o recusada" : `${excludedZones.length} regi\xF5es recusadas`,
3781
4958
  " \u2014 ",
@@ -4887,31 +6064,9 @@ var vindiConnectorDef = {
4887
6064
  };
4888
6065
  var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
4889
6066
  var MERCADOPAGO_LATENCY_BUDGET_MS = 6e3;
4890
- var MERCADOPAGO_FIELDS = [
4891
- {
4892
- key: "accessToken",
4893
- label: "Access token",
4894
- type: "password",
4895
- secret: true,
4896
- placeholder: "APP_USR-\u2026 (Suas integra\xE7\xF5es \u2192 sua aplica\xE7\xE3o \u2192 Credenciais)"
4897
- },
4898
- {
4899
- key: "publicKey",
4900
- label: "Public key",
4901
- // Not masked: it travels to the browser anyway, and a merchant who cannot
4902
- // see it cannot tell the test key from the production one at a glance.
4903
- type: "text",
4904
- secret: true,
4905
- placeholder: "APP_USR-\u2026"
4906
- }
4907
- ];
4908
- var store6 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
4909
- fields: MERCADOPAGO_FIELDS
4910
- });
4911
- function connectionDetail2(settings) {
4912
- const key = String(settings?.publicKey ?? "").trim();
4913
- if (!key) return "Pix e cart\xE3o";
4914
- return /^TEST-/i.test(key) ? "Sandbox \xB7 Pix e cart\xE3o" : "Produ\xE7\xE3o \xB7 Pix e cart\xE3o";
6067
+ var store6 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID);
6068
+ function connectionDetail2() {
6069
+ return "Pix e cart\xE3o";
4915
6070
  }
4916
6071
  function MercadoPagoExtraPanel() {
4917
6072
  const [connected, setConnected] = useState(false);
@@ -4939,32 +6094,35 @@ function MercadoPagoExtraPanel() {
4939
6094
  /* @__PURE__ */ jsx("p", { children: "O n\xFAmero do cart\xE3o n\xE3o passa pelo seu site: ele \xE9 digitado dentro de um formul\xE1rio do pr\xF3prio Mercado Pago e vira um c\xF3digo de uso \xFAnico antes de chegar at\xE9 n\xF3s. \xC9 por isso que d\xE1 para receber cart\xE3o na sua p\xE1gina sem a auditoria que isso normalmente exige." }),
4940
6095
  /* @__PURE__ */ jsx("p", { children: 'O valor cobrado \xE9 sempre recalculado aqui no servidor a partir do pedido. Pre\xE7o alterado no navegador n\xE3o \xE9 aceito, e clicar duas vezes em "pagar" gera uma cobran\xE7a s\xF3.' }),
4941
6096
  /* @__PURE__ */ jsx("p", { children: "O Pix vale por 30 minutos. Se o cliente fechar a aba depois de pagar, o pedido aparece aqui do mesmo jeito \u2014 o Mercado Pago avisa a loja assim que o dinheiro cai." })
4942
- ] }) : /* @__PURE__ */ jsxs("p", { children: [
4943
- "As duas chaves ficam no painel do Mercado Pago, em ",
4944
- /* @__PURE__ */ jsx("strong", { children: "Suas integra\xE7\xF5es" }),
4945
- " \u2192 sua aplica\xE7\xE3o \u2192 ",
4946
- /* @__PURE__ */ jsx("strong", { children: "Credenciais de produ\xE7\xE3o" }),
4947
- ". Copie o ",
4948
- /* @__PURE__ */ jsx("em", { children: "access token" }),
4949
- " e a",
4950
- " ",
4951
- /* @__PURE__ */ jsx("em", { children: "public key" }),
4952
- " da ",
4953
- /* @__PURE__ */ jsx("strong", { children: "mesma" }),
4954
- " aplica\xE7\xE3o: elas s\xE3o um par, e uma sem a outra n\xE3o fecha uma venda."
6097
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6098
+ /* @__PURE__ */ jsxs("p", { children: [
6099
+ "Clique em ",
6100
+ /* @__PURE__ */ jsx("strong", { children: "Conectar" }),
6101
+ " e voc\xEA vai para o Mercado Pago autorizar a Fayz. \xC9 na tela deles, com o seu login \u2014 nenhuma senha e nenhuma chave passam por aqui."
6102
+ ] }),
6103
+ /* @__PURE__ */ jsxs("p", { children: [
6104
+ "O dinheiro continua caindo na ",
6105
+ /* @__PURE__ */ jsx("strong", { children: "sua" }),
6106
+ " conta do Mercado Pago. A autoriza\xE7\xE3o s\xF3 permite abrir cobran\xE7as em nome da sua loja, e voc\xEA pode cancel\xE1-la quando quiser no painel do Mercado Pago."
6107
+ ] }),
6108
+ /* @__PURE__ */ jsxs("p", { children: [
6109
+ "Use uma conta ",
6110
+ /* @__PURE__ */ jsx("strong", { children: "PJ" }),
6111
+ ", com o CNPJ da loja. Conta pessoa f\xEDsica costuma vir com bandeiras de cart\xE3o bloqueadas at\xE9 a verifica\xE7\xE3o terminar \u2014 e o bloqueio aparece como uma compra recusada, n\xE3o como um aviso."
6112
+ ] })
4955
6113
  ] }),
4956
6114
  /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-dashed p-3", children: [
4957
6115
  /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Para testar sem cobrar de verdade" }),
4958
6116
  /* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
4959
- "As chaves que come\xE7am com ",
6117
+ "O teste \xE9 feito com ",
6118
+ /* @__PURE__ */ jsx("strong", { children: "usu\xE1rios de teste" }),
6119
+ ": crie um vendedor e um comprador em Suas integra\xE7\xF5es \u2192 Contas de teste, e autorize aqui logado como o vendedor de teste. N\xE3o \xE9 dinheiro real, e \xE9 assim que o pr\xF3prio Mercado Pago recomenda. Credenciais que come\xE7am com",
6120
+ " ",
4960
6121
  /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5", children: "TEST-" }),
4961
6122
  " ",
4962
6123
  /* @__PURE__ */ jsx("strong", { children: "n\xE3o funcionam" }),
4963
- " aqui \u2014 o Mercado Pago as recusa nesta API. O teste \xE9 feito com ",
4964
- /* @__PURE__ */ jsx("strong", { children: "usu\xE1rios de teste" }),
4965
- ": crie um vendedor e um comprador em Suas integra\xE7\xF5es \u2192 Contas de teste, e use as chaves de ",
4966
- /* @__PURE__ */ jsx("em", { children: "produ\xE7\xE3o" }),
4967
- " do vendedor de teste. N\xE3o \xE9 dinheiro real, e \xE9 assim que o pr\xF3prio Mercado Pago recomenda."
6124
+ " ",
6125
+ "nesta API \u2014 o usu\xE1rio de teste usa as chaves de produ\xE7\xE3o dele."
4968
6126
  ] })
4969
6127
  ] })
4970
6128
  ] });
@@ -4982,15 +6140,25 @@ var mercadoPagoConnectorDef = {
4982
6140
  // its own domain, so a path would not.
4983
6141
  logoUrl: "https://assets.fayz.ai/connectors/mercadopago.png",
4984
6142
  category: "payments",
4985
- // Pasted keys today. Unlike Vindi — whose consent screen only LOOKS like OAuth
4986
- // — Mercado Pago has a real merchant OAuth, and it is the right end state: one
4987
- // platform application, each seller authorizing it, `marketplace_fee` taken on
4988
- // the charge instead of invoiced afterwards. What it needs is the redirect URI
4989
- // registered against the platform broker, which is a change in ymaia and not
4990
- // here. The same slug is already declared by plugin-financial with
4991
- // `authKind: 'oauth'`, and because a credential is keyed by slug + tenant, a
4992
- // store that authorizes there is redeemable from here the day that lands.
4993
- authKind: "api-key",
6143
+ // A real consent screen, unlike Vindi's — whose flow only LOOKS like OAuth and
6144
+ // is a CODE copied out of an address bar. The seller authorizes Fayz once at
6145
+ // auth.mercadopago.com, the money keeps landing in their own account, and
6146
+ // `marketplace_fee` takes our cut on the way past.
6147
+ //
6148
+ // That fee is the reason this is not pasted keys: Mercado Pago's docs are
6149
+ // explicit that it is "exclusivo para integrações com OAuth", so a connection
6150
+ // made by copying credentials out of a panel can never earn. The pasted-key
6151
+ // version shipped first because it could be tested the same afternoon; this
6152
+ // is the one that can be a product.
6153
+ //
6154
+ // A store connected the old way keeps working and does not have to be
6155
+ // migrated by hand: the vault is keyed by slug + tenant, and the runtime reads
6156
+ // `accessToken` and `access_token` alike. Reconnecting through the button
6157
+ // upgrades it in place — same slug, same row, now with a refresh token.
6158
+ authKind: "oauth",
6159
+ // The whole of it. `provider` defaults to the connector id, and both are
6160
+ // `mercadopago` — the platform registry keys on that word.
6161
+ startOAuth: fayzConnectorOAuth(MERCADOPAGO_CONNECTOR_ID),
4994
6162
  // The charge is opened in the buyer's request path; the notification and the
4995
6163
  // refund are outbound sync work.
4996
6164
  planes: ["request", "sync"],
@@ -5022,7 +6190,6 @@ var mercadoPagoConnectorDef = {
5022
6190
  // confirmed without a payment is worse than a refused checkout. Same verdict
5023
6191
  // as the other four gateways, opposite to the carrier next door.
5024
6192
  onFailure: "block",
5025
- fields: MERCADOPAGO_FIELDS,
5026
6193
  invokedAt: [
5027
6194
  {
5028
6195
  key: "shop.checkout.session_open",
@@ -5047,7 +6214,7 @@ var mercadoPagoConnectorDef = {
5047
6214
  MERCADOPAGO_CONNECTOR_ID,
5048
6215
  (connection) => ({
5049
6216
  connected: !!connection?.active,
5050
- detail: connectionDetail2(connection?.settings),
6217
+ detail: connectionDetail2(),
5051
6218
  plane: "request"
5052
6219
  }),
5053
6220
  { store: () => store6 }
@@ -5084,12 +6251,12 @@ function notifyChannelsChanged() {
5084
6251
  if (typeof window !== "undefined") window.dispatchEvent(new Event("fayz:channels-changed"));
5085
6252
  }
5086
6253
  function ChannelsPage({ resolveProvider }) {
5087
- const provider = React9.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
6254
+ const provider = React13__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
5088
6255
  const supported = typeof provider.listSalesChannels === "function";
5089
- const [channels, setChannels] = React9.useState([]);
5090
- const [loading, setLoading] = React9.useState(true);
5091
- const [adding, setAdding] = React9.useState(null);
5092
- const reload = React9.useCallback(async () => {
6256
+ const [channels, setChannels] = React13__default.useState([]);
6257
+ const [loading, setLoading] = React13__default.useState(true);
6258
+ const [adding, setAdding] = React13__default.useState(null);
6259
+ const reload = React13__default.useCallback(async () => {
5093
6260
  if (!provider.listSalesChannels) {
5094
6261
  setLoading(false);
5095
6262
  return;
@@ -5107,7 +6274,7 @@ function ChannelsPage({ resolveProvider }) {
5107
6274
  setLoading(false);
5108
6275
  }
5109
6276
  }, [provider]);
5110
- React9.useEffect(() => {
6277
+ React13__default.useEffect(() => {
5111
6278
  void reload();
5112
6279
  }, [reload]);
5113
6280
  const patch = async (id, input) => {
@@ -5163,7 +6330,7 @@ function ChannelsPage({ resolveProvider }) {
5163
6330
  ] });
5164
6331
  }
5165
6332
  function OnlineStoreCard({ channel, onSave, onToggle }) {
5166
- const [url, setUrl] = React9.useState(channel.url ?? "");
6333
+ const [url, setUrl] = React13__default.useState(channel.url ?? "");
5167
6334
  const dirty = (channel.url ?? "") !== url;
5168
6335
  const meta = KIND_META.online_store;
5169
6336
  return /* @__PURE__ */ jsx("div", { className: "rounded-card border border-border bg-card p-5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
@@ -5189,8 +6356,8 @@ function OnlineStoreCard({ channel, onSave, onToggle }) {
5189
6356
  ] }) });
5190
6357
  }
5191
6358
  function ChannelRow({ channel, onSave, onToggle, onRemove }) {
5192
- const [url, setUrl] = React9.useState(channel.url ?? "");
5193
- const [editing, setEditing] = React9.useState(false);
6359
+ const [url, setUrl] = React13__default.useState(channel.url ?? "");
6360
+ const [editing, setEditing] = React13__default.useState(false);
5194
6361
  const meta = KIND_META[channel.kind] ?? KIND_META.custom;
5195
6362
  const Icon = meta.icon;
5196
6363
  return /* @__PURE__ */ jsx("div", { className: "rounded-card border border-border bg-card p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
@@ -5218,9 +6385,9 @@ function ChannelRow({ channel, onSave, onToggle, onRemove }) {
5218
6385
  }
5219
6386
  function AddChannelForm({ kind, onKind, onCancel, onAdd }) {
5220
6387
  const meta = KIND_META[kind];
5221
- const [label, setLabel] = React9.useState(meta.label);
5222
- const [url, setUrl] = React9.useState("");
5223
- React9.useEffect(() => {
6388
+ const [label, setLabel] = React13__default.useState(meta.label);
6389
+ const [url, setUrl] = React13__default.useState("");
6390
+ React13__default.useEffect(() => {
5224
6391
  setLabel(KIND_META[kind].label);
5225
6392
  }, [kind]);
5226
6393
  return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-card border border-border bg-card p-4", children: [
@@ -5272,17 +6439,17 @@ function formatSize(bytes) {
5272
6439
  return `${(bytes / 1024 ** i).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
5273
6440
  }
5274
6441
  function ContentPage({ resolveProvider }) {
5275
- const provider = React9.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
6442
+ const provider = React13__default.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
5276
6443
  const supported = typeof provider.listMediaFiles === "function";
5277
- const [files, setFiles] = React9.useState(null);
5278
- const [tab, setTab] = React9.useState("all");
5279
- const [uploading, setUploading] = React9.useState(false);
5280
- const [pendingDelete, setPendingDelete] = React9.useState(null);
5281
- const [deleting, setDeleting] = React9.useState(false);
5282
- const [replacing, setReplacing] = React9.useState(false);
5283
- const [openId, setOpenId] = React9.useState(null);
5284
- const [dimensions, setDimensions] = React9.useState({});
5285
- const reload = React9.useCallback(async () => {
6444
+ const [files, setFiles] = React13__default.useState(null);
6445
+ const [tab, setTab] = React13__default.useState("all");
6446
+ const [uploading, setUploading] = React13__default.useState(false);
6447
+ const [pendingDelete, setPendingDelete] = React13__default.useState(null);
6448
+ const [deleting, setDeleting] = React13__default.useState(false);
6449
+ const [replacing, setReplacing] = React13__default.useState(false);
6450
+ const [openId, setOpenId] = React13__default.useState(null);
6451
+ const [dimensions, setDimensions] = React13__default.useState({});
6452
+ const reload = React13__default.useCallback(async () => {
5286
6453
  if (!provider.listMediaFiles) {
5287
6454
  setFiles([]);
5288
6455
  return;
@@ -5294,10 +6461,10 @@ function ContentPage({ resolveProvider }) {
5294
6461
  setFiles([]);
5295
6462
  }
5296
6463
  }, [provider]);
5297
- React9.useEffect(() => {
6464
+ React13__default.useEffect(() => {
5298
6465
  void reload();
5299
6466
  }, [reload]);
5300
- const measure = React9.useCallback((id, d) => {
6467
+ const measure = React13__default.useCallback((id, d) => {
5301
6468
  setDimensions((prev) => prev[id] ? prev : { ...prev, [id]: d });
5302
6469
  }, []);
5303
6470
  const handleUpload = async (picked) => {
@@ -5319,19 +6486,19 @@ function ContentPage({ resolveProvider }) {
5319
6486
  const handleReplace = async (file, picked) => {
5320
6487
  setReplacing(true);
5321
6488
  try {
5322
- let nextId;
6489
+ let nextId2;
5323
6490
  if (file.reference) {
5324
6491
  if (!provider.replaceProductImage) throw new Error("unsupported");
5325
6492
  const image = await provider.replaceProductImage(file.reference.attachmentId, picked);
5326
- nextId = productImageToMediaFile(image, file.reference.label).id;
6493
+ nextId2 = productImageToMediaFile(image, file.reference.label).id;
5327
6494
  } else {
5328
6495
  if (!provider.uploadMediaFile) throw new Error("unsupported");
5329
6496
  const uploaded = await provider.uploadMediaFile(picked);
5330
6497
  if (provider.deleteMediaFile) await provider.deleteMediaFile(file.id);
5331
- nextId = uploaded.id;
6498
+ nextId2 = uploaded.id;
5332
6499
  }
5333
6500
  await reload();
5334
- setOpenId(nextId);
6501
+ setOpenId(nextId2);
5335
6502
  toast.success("Arquivo trocado");
5336
6503
  } catch (err) {
5337
6504
  console.warn("[content] replace failed", err);
@@ -5487,7 +6654,7 @@ function SectionTitle({ title, count }) {
5487
6654
  ] });
5488
6655
  }
5489
6656
  function UploadButton({ uploading, onPick }) {
5490
- const inputRef = React9.useRef(null);
6657
+ const inputRef = React13__default.useRef(null);
5491
6658
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5492
6659
  /* @__PURE__ */ jsx(
5493
6660
  "input",
@@ -5567,8 +6734,8 @@ function MediaDetail({
5567
6734
  onDelete,
5568
6735
  replacing
5569
6736
  }) {
5570
- const replaceRef = React9.useRef(null);
5571
- React9.useEffect(() => {
6737
+ const replaceRef = React13__default.useRef(null);
6738
+ React13__default.useEffect(() => {
5572
6739
  function onKey(e) {
5573
6740
  if (e.key === "ArrowLeft") onNavigate(-1);
5574
6741
  if (e.key === "ArrowRight") onNavigate(1);
@@ -7423,7 +8590,7 @@ function buildShopAnalyticsCards() {
7423
8590
  }
7424
8591
 
7425
8592
  // src/index.ts
7426
- var ShopPage = React9.lazy(() => import('./ShopPage-QFIM2HDU.js').then((m) => ({ default: m.ShopPage })));
8593
+ var ShopPage = React13__default.lazy(() => import('./ShopPage-6IQ7WKQG.js').then((m) => ({ default: m.ShopPage })));
7427
8594
  function createShopPlugin(options) {
7428
8595
  const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
7429
8596
  setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
@@ -7432,10 +8599,10 @@ function createShopPlugin(options) {
7432
8599
  registerEntityPath("shop_customer", "/orders/customers");
7433
8600
  registerPersonLookup("shop_customer", shopCustomerLookup(() => resolveProvider()));
7434
8601
  const pageFor = (section) => {
7435
- const Wrapped = () => React9.createElement(
7436
- React9.Suspense,
8602
+ const Wrapped = () => React13__default.createElement(
8603
+ React13__default.Suspense,
7437
8604
  { fallback: null },
7438
- React9.createElement(
8605
+ React13__default.createElement(
7439
8606
  ShopPage,
7440
8607
  { provider: options?.provider, section }
7441
8608
  )
@@ -7614,7 +8781,7 @@ function createShopPlugin(options) {
7614
8781
  dataProvider: createShopGiftCardDataProvider(resolveProvider),
7615
8782
  feature: "shop",
7616
8783
  stats: (items, total) => {
7617
- const money2 = (v) => new Intl.NumberFormat(curr.locale, { style: "currency", currency: curr.code }).format(v);
8784
+ const money3 = (v) => new Intl.NumberFormat(curr.locale, { style: "currency", currency: curr.code }).format(v);
7618
8785
  const active = items?.filter((g) => g.status === "active") ?? [];
7619
8786
  return [
7620
8787
  { id: "total", label: "Cart\xF5es emitidos", value: total, icon: CreditCard },
@@ -7625,20 +8792,20 @@ function createShopPlugin(options) {
7625
8792
  id: "outstanding",
7626
8793
  label: "Saldo em aberto",
7627
8794
  icon: CircleDollarSign,
7628
- value: money2(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
8795
+ value: money3(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
7629
8796
  },
7630
8797
  {
7631
8798
  id: "issued",
7632
8799
  label: "Valor emitido",
7633
8800
  icon: TicketCheck,
7634
- value: money2(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
8801
+ value: money3(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
7635
8802
  }
7636
8803
  ];
7637
8804
  }
7638
8805
  });
7639
- const ContentPageWrapped = () => React9.createElement(ContentPage, { resolveProvider: options?.provider });
8806
+ const ContentPageWrapped = () => React13__default.createElement(ContentPage, { resolveProvider: options?.provider });
7640
8807
  ContentPageWrapped.displayName = "ContentPage";
7641
- const ChannelsPageWrapped = () => React9.createElement(ChannelsPage, { resolveProvider: options?.provider });
8808
+ const ChannelsPageWrapped = () => React13__default.createElement(ChannelsPage, { resolveProvider: options?.provider });
7642
8809
  ChannelsPageWrapped.displayName = "ChannelsPage";
7643
8810
  const navSection = options?.navSection ?? "main";
7644
8811
  const navPosition = options?.navPosition ?? 6;
@@ -7728,7 +8895,7 @@ function createShopPlugin(options) {
7728
8895
  ];
7729
8896
  const redirectTo = (target) => {
7730
8897
  const Redirect = () => {
7731
- React9.useEffect(() => {
8898
+ React13__default.useEffect(() => {
7732
8899
  adminNavigateTo(target);
7733
8900
  }, []);
7734
8901
  return null;
@@ -7760,6 +8927,10 @@ function createShopPlugin(options) {
7760
8927
  return {
7761
8928
  id: "shop",
7762
8929
  defaultAgentRole: "operations",
8930
+ // O que a loja pede de um tenant novo, na ordem em que faz diferença.
8931
+ // Vira linha de tarefa, aparece no checklist da trilha e é o roteiro que o
8932
+ // assistente segue — ver ./lib/onboarding.ts.
8933
+ onboarding: { program: buildShopOnboarding() },
7763
8934
  name: options?.navLabel ?? "Shop",
7764
8935
  icon: "ShoppingBag",
7765
8936
  version: "1.0.0",
@@ -7852,7 +9023,7 @@ function createShopPlugin(options) {
7852
9023
  label: label("store", "Loja"),
7853
9024
  icon: "ShoppingBag",
7854
9025
  group: COMMERCE_GROUP,
7855
- component: settingsTab("StoreGeneralSettings", () => React9.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
9026
+ component: settingsTab("StoreGeneralSettings", () => React13__default.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
7856
9027
  order: 30,
7857
9028
  permission: READ
7858
9029
  },
@@ -7861,7 +9032,7 @@ function createShopPlugin(options) {
7861
9032
  label: "Pagamentos",
7862
9033
  icon: "CreditCard",
7863
9034
  group: COMMERCE_GROUP,
7864
- component: settingsTab("PaymentsSettings", () => React9.createElement(PaymentsSettings, { provider: options?.provider })),
9035
+ component: settingsTab("PaymentsSettings", () => React13__default.createElement(PaymentsSettings, { provider: options?.provider })),
7865
9036
  order: 31,
7866
9037
  permission: READ
7867
9038
  },
@@ -7870,7 +9041,7 @@ function createShopPlugin(options) {
7870
9041
  label: "Checkout",
7871
9042
  icon: "ShoppingCart",
7872
9043
  group: COMMERCE_GROUP,
7873
- component: settingsTab("CheckoutSettings", () => React9.createElement(CheckoutSettings, { provider: options?.provider })),
9044
+ component: settingsTab("CheckoutSettings", () => React13__default.createElement(CheckoutSettings, { provider: options?.provider })),
7874
9045
  order: 32,
7875
9046
  permission: READ
7876
9047
  },
@@ -7879,7 +9050,7 @@ function createShopPlugin(options) {
7879
9050
  label: label("shipping", "Entrega"),
7880
9051
  icon: "Truck",
7881
9052
  group: COMMERCE_GROUP,
7882
- component: settingsTab("ShippingSettings", () => React9.createElement(ShippingSettings, { provider: options?.provider })),
9053
+ component: settingsTab("ShippingSettings", () => React13__default.createElement(ShippingSettings, { provider: options?.provider })),
7883
9054
  order: 33,
7884
9055
  permission: READ
7885
9056
  },
@@ -7888,7 +9059,7 @@ function createShopPlugin(options) {
7888
9059
  label: "Fulfillment",
7889
9060
  icon: "PackageCheck",
7890
9061
  group: COMMERCE_GROUP,
7891
- component: settingsTab("FulfillmentSettings", () => React9.createElement(FulfillmentSettings, { provider: options?.provider })),
9062
+ component: settingsTab("FulfillmentSettings", () => React13__default.createElement(FulfillmentSettings, { provider: options?.provider })),
7892
9063
  order: 34,
7893
9064
  permission: READ
7894
9065
  }