@fayz-ai/plugin-shop 0.14.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,9 +1,10 @@
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';
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';
3
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';
@@ -353,20 +354,20 @@ function ProductVariantsTab({ item, getProvider }) {
353
354
  const product = item;
354
355
  const shop = getProvider();
355
356
  const supported = typeof shop.getProductVariants === "function" && typeof shop.saveProductOptions === "function" && typeof shop.createProductVariant === "function" && typeof shop.updateProductVariant === "function";
356
- const [axes, setAxes] = React9.useState([]);
357
- const [variants, setVariants] = React9.useState([]);
358
- const [busy, setBusy] = React9.useState(false);
359
- const [error, setError] = React9.useState(null);
360
- const [selected, setSelected] = React9.useState(/* @__PURE__ */ new Set());
361
- const [dirty, setDirty] = React9.useState(false);
362
- 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 () => {
363
364
  const set = await getProvider().getProductVariants(product.id, { includeArchived: true });
364
365
  setAxes(axesFromOptions(set.options));
365
366
  setVariants(set.variants);
366
367
  setSelected(/* @__PURE__ */ new Set());
367
368
  setDirty(false);
368
369
  }, [getProvider, product.id]);
369
- React9.useEffect(() => {
370
+ React13__default.useEffect(() => {
370
371
  if (!supported) return;
371
372
  void reload().catch((e) => setError(message(e)));
372
373
  }, [reload, supported]);
@@ -382,7 +383,7 @@ function ProductVariantsTab({ item, getProvider }) {
382
383
  setBusy(false);
383
384
  }
384
385
  }
385
- const plan = React9.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
386
+ const plan = React13__default.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
386
387
  const axisError = validateVariantAxes(axes);
387
388
  const onlyDefault = hasOnlyDefaultVariant(variants);
388
389
  const rows = plan.rows;
@@ -483,7 +484,7 @@ function ProductVariantsTab({ item, getProvider }) {
483
484
  ] });
484
485
  }
485
486
  function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }) {
486
- const [draft, setDraft] = React9.useState("");
487
+ const [draft2, setDraft] = React13__default.useState("");
487
488
  function commit(raw) {
488
489
  const values = raw.split(",").map((v) => v.trim()).filter(Boolean);
489
490
  if (values.length === 0) return;
@@ -538,16 +539,16 @@ function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }
538
539
  /* @__PURE__ */ jsx(
539
540
  "input",
540
541
  {
541
- value: draft,
542
+ value: draft2,
542
543
  disabled,
543
544
  onChange: (e) => setDraft(e.target.value),
544
545
  onKeyDown: (e) => {
545
546
  if (e.key === "Enter") {
546
547
  e.preventDefault();
547
- commit(draft);
548
+ commit(draft2);
548
549
  }
549
550
  },
550
- onBlur: () => commit(draft),
551
+ onBlur: () => commit(draft2),
551
552
  placeholder: axis.values.length === 0 ? "P, M, G" : "adicionar",
552
553
  "aria-label": `Valores da op\xE7\xE3o ${index + 1}`,
553
554
  className: "h-7 min-w-28 flex-1 rounded-input border border-dashed border-input bg-transparent px-2 text-xs"
@@ -756,9 +757,9 @@ function VariantRow({ row, axes, product, busy, checked, onToggle, onSave, onSto
756
757
  ] });
757
758
  }
758
759
  function BulkBar({ count, disabled, onApply, onClear }) {
759
- const [price, setPrice] = React9.useState("");
760
- const [weight, setWeight] = React9.useState("");
761
- 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("");
762
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: [
763
764
  /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
764
765
  count,
@@ -837,19 +838,19 @@ function parseNumber(raw) {
837
838
  return Number.isFinite(n) && n >= 0 ? n : null;
838
839
  }
839
840
  function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", integer }) {
840
- const [draft, setDraft] = React9.useState(value == null ? "" : String(value));
841
- React9.useEffect(() => {
841
+ const [draft2, setDraft] = React13__default.useState(value == null ? "" : String(value));
842
+ React13__default.useEffect(() => {
842
843
  setDraft(value == null ? "" : String(value));
843
844
  }, [value]);
844
845
  return /* @__PURE__ */ jsx(
845
846
  "input",
846
847
  {
847
- value: draft,
848
+ value: draft2,
848
849
  disabled,
849
850
  placeholder,
850
851
  onChange: (e) => setDraft(e.target.value),
851
852
  onBlur: () => {
852
- const parsed = parseNumber(draft);
853
+ const parsed = parseNumber(draft2);
853
854
  onCommit(parsed == null ? null : integer ? Math.trunc(parsed) : parsed);
854
855
  },
855
856
  className: `h-7 ${width} rounded-input border border-input bg-background px-2 text-xs tabular-nums`
@@ -857,18 +858,18 @@ function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", in
857
858
  );
858
859
  }
859
860
  function TextCell({ value, disabled, placeholder, onCommit }) {
860
- const [draft, setDraft] = React9.useState(value);
861
- React9.useEffect(() => {
861
+ const [draft2, setDraft] = React13__default.useState(value);
862
+ React13__default.useEffect(() => {
862
863
  setDraft(value);
863
864
  }, [value]);
864
865
  return /* @__PURE__ */ jsx(
865
866
  "input",
866
867
  {
867
- value: draft,
868
+ value: draft2,
868
869
  disabled,
869
870
  placeholder,
870
871
  onChange: (e) => setDraft(e.target.value),
871
- onBlur: () => onCommit(draft.trim()),
872
+ onBlur: () => onCommit(draft2.trim()),
872
873
  className: "h-7 w-28 rounded-input border border-input bg-background px-2 text-xs"
873
874
  }
874
875
  );
@@ -1188,7 +1189,7 @@ function ProductPositionsTab({ item, getProvider }) {
1188
1189
  const [noteOpen, setNoteOpen] = useState(false);
1189
1190
  const [occurredOn, setOccurredOn] = useState(() => todayISO());
1190
1191
  const [saving, setSaving] = useState(false);
1191
- const quantityRef = React9.useRef(null);
1192
+ const quantityRef = React13__default.useRef(null);
1192
1193
  const load = useCallback(async () => {
1193
1194
  setLoading(true);
1194
1195
  try {
@@ -1267,7 +1268,7 @@ function ProductPositionsTab({ item, getProvider }) {
1267
1268
  const amount = Math.abs(Number(quantity) || 0);
1268
1269
  const outgoing = type === "saida" || type === "perda";
1269
1270
  const nextBalance = amount > 0 ? outgoing ? total.available - amount : total.available + amount : null;
1270
- const live = React9.useRef({ product, variantId, total, getProvider, load });
1271
+ const live = React13__default.useRef({ product, variantId, total, getProvider, load });
1271
1272
  live.current = { product, variantId, total, getProvider, load };
1272
1273
  const contribution = useMemo(() => ({
1273
1274
  id: "shop.product.positions",
@@ -1533,20 +1534,20 @@ function shippingRegistryIdFromHash(hash) {
1533
1534
  }
1534
1535
  var PACKAGES_ROUTE = shippingRegistryPath(PACKAGES_REGISTRY);
1535
1536
  function PackageSelect({ value, onChange, getProvider }) {
1536
- const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1537
- const [rows, setRows] = React9.useState(null);
1538
- const [open, setOpen] = React9.useState(false);
1539
- const [creating, setCreating] = React9.useState(false);
1540
- const ref = React9.useRef(null);
1541
- 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(() => {
1542
1543
  if (typeof shop.listShippingPackages !== "function") {
1543
1544
  setRows([]);
1544
1545
  return;
1545
1546
  }
1546
1547
  void shop.listShippingPackages().then(setRows).catch(() => setRows([]));
1547
1548
  }, [shop]);
1548
- React9.useEffect(load, [load]);
1549
- React9.useEffect(() => {
1549
+ React13__default.useEffect(load, [load]);
1550
+ React13__default.useEffect(() => {
1550
1551
  if (!open) return;
1551
1552
  const away = (event) => {
1552
1553
  if (ref.current && !ref.current.contains(event.target)) setOpen(false);
@@ -1641,8 +1642,8 @@ function PackageSelect({ value, onChange, getProvider }) {
1641
1642
  ] });
1642
1643
  }
1643
1644
  function NewPackageModal({ onClose, onCreated }) {
1644
- const [error, setError] = React9.useState(null);
1645
- const provider = React9.useMemo(
1645
+ const [error, setError] = React13__default.useState(null);
1646
+ const provider = React13__default.useMemo(
1646
1647
  () => resolveDataProvider(packagesRegistry.entity),
1647
1648
  []
1648
1649
  );
@@ -1688,9 +1689,9 @@ function Option({ label, hint, active, onClick }) {
1688
1689
  );
1689
1690
  }
1690
1691
  function ShippingProfileSelect({ value, onChange, getProvider }) {
1691
- const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1692
- const [rows, setRows] = React9.useState(null);
1693
- React9.useEffect(() => {
1692
+ const shop = React13__default.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
1693
+ const [rows, setRows] = React13__default.useState(null);
1694
+ React13__default.useEffect(() => {
1694
1695
  let cancelled = false;
1695
1696
  if (typeof shop.listShippingProfiles !== "function") {
1696
1697
  setRows([]);
@@ -2478,7 +2479,7 @@ var STATUS_LABEL2 = {
2478
2479
  disabled: "Desativado"
2479
2480
  };
2480
2481
  function buildShopGiftCardEntity(currencyCode = "BRL") {
2481
- 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);
2482
2483
  const fields = [
2483
2484
  {
2484
2485
  key: "code",
@@ -2506,7 +2507,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
2506
2507
  hint: "Valor de face do cart\xE3o. N\xE3o muda depois de emitido.",
2507
2508
  showInTable: true,
2508
2509
  sortable: true,
2509
- 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) })
2510
2511
  },
2511
2512
  {
2512
2513
  key: "balance",
@@ -2520,7 +2521,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
2520
2521
  hint: "O que ainda pode ser gasto. Cai a cada resgate.",
2521
2522
  showInTable: true,
2522
2523
  sortable: true,
2523
- 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) })
2524
2525
  },
2525
2526
  {
2526
2527
  key: "status",
@@ -2655,20 +2656,20 @@ function buildShopCustomerGroupEntity() {
2655
2656
  fieldGroups: [{ id: "basic", label: "Grupo", columns: 2 }]
2656
2657
  };
2657
2658
  }
2658
- function describeSegment(s, money2) {
2659
+ function describeSegment(s, money3) {
2659
2660
  const parts = [];
2660
2661
  if (s.minOrders != null && s.maxOrders != null) parts.push(`${s.minOrders}\u2013${s.maxOrders} pedidos`);
2661
2662
  else if (s.minOrders != null) parts.push(`${s.minOrders}+ pedidos`);
2662
2663
  else if (s.maxOrders != null) parts.push(`at\xE9 ${s.maxOrders} pedidos`);
2663
- if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${money2(s.minSpent)}\u2013${money2(s.maxSpent)}`);
2664
- else if (s.minSpent != null) parts.push(`gastou ${money2(s.minSpent)}+`);
2665
- 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)}`);
2666
2667
  if (s.joinedAfter) parts.push(`entrou ap\xF3s ${new Date(s.joinedAfter).toLocaleDateString("pt-BR")}`);
2667
2668
  if (s.joinedBefore) parts.push(`entrou antes de ${new Date(s.joinedBefore).toLocaleDateString("pt-BR")}`);
2668
2669
  return parts.length ? parts.join(" \xB7 ") : "Todos os clientes";
2669
2670
  }
2670
2671
  function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
2671
- 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);
2672
2673
  const fields = [
2673
2674
  {
2674
2675
  key: "name",
@@ -2745,7 +2746,7 @@ function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
2745
2746
  showInForm: false,
2746
2747
  showInTable: true,
2747
2748
  sortable: false,
2748
- 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) })
2749
2750
  },
2750
2751
  {
2751
2752
  key: "matchCount",
@@ -3075,17 +3076,17 @@ function createShopCustomerSegmentDataProvider(provider) {
3075
3076
  }
3076
3077
  var DEBOUNCE_MS = 400;
3077
3078
  function useStoreSettings(provider, defaults = {}) {
3078
- const shop = React9.useMemo(
3079
+ const shop = React13__default.useMemo(
3079
3080
  () => provider ? resolve(provider) : getShopProvider(),
3080
3081
  [provider]
3081
3082
  );
3082
3083
  const supported = typeof shop.getStoreSettings === "function" && typeof shop.updateStoreSettings === "function";
3083
- const [stored, setStored] = React9.useState(null);
3084
- const [loading, setLoading] = React9.useState(supported);
3085
- const [saving, setSaving] = React9.useState(false);
3086
- const [error, setError] = React9.useState(null);
3087
- const [loadError, setLoadError] = React9.useState(null);
3088
- 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(() => {
3089
3090
  if (!supported) return;
3090
3091
  let cancelled = false;
3091
3092
  setLoading(true);
@@ -3101,12 +3102,12 @@ function useStoreSettings(provider, defaults = {}) {
3101
3102
  cancelled = true;
3102
3103
  };
3103
3104
  }, [shop, supported]);
3104
- const pendingRef = React9.useRef({});
3105
- const timerRef = React9.useRef(null);
3106
- React9.useEffect(() => () => {
3105
+ const pendingRef = React13__default.useRef({});
3106
+ const timerRef = React13__default.useRef(null);
3107
+ React13__default.useEffect(() => () => {
3107
3108
  if (timerRef.current) clearTimeout(timerRef.current);
3108
3109
  }, []);
3109
- const set = React9.useCallback(
3110
+ const set = React13__default.useCallback(
3110
3111
  (patch) => {
3111
3112
  setStored((current) => ({ ...current ?? {}, ...patch }));
3112
3113
  if (!supported) return;
@@ -3122,7 +3123,7 @@ function useStoreSettings(provider, defaults = {}) {
3122
3123
  },
3123
3124
  [shop, supported]
3124
3125
  );
3125
- const value = React9.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
3126
+ const value = React13__default.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
3126
3127
  return { value, stored, set, loading, saving, error, loadError, supported };
3127
3128
  }
3128
3129
  function SettingsState({
@@ -3351,25 +3352,25 @@ function PaymentsSettings({ provider }) {
3351
3352
  const settings = useStoreSettings(provider);
3352
3353
  const { value, set } = settings;
3353
3354
  const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
3354
- const [adding, setAdding] = React9.useState(false);
3355
- const [selected, setSelected] = React9.useState(null);
3355
+ const [adding, setAdding] = React13__default.useState(false);
3356
+ const [selected, setSelected] = React13__default.useState(null);
3356
3357
  const panelAvailable = useConnectorsPanelAvailable();
3357
- const openJourney = React9.useCallback(
3358
+ const openJourney = React13__default.useCallback(
3358
3359
  (connector) => {
3359
3360
  if (panelAvailable) openConnectorsPanel(connector);
3360
3361
  else setSelected(connector);
3361
3362
  },
3362
3363
  [panelAvailable]
3363
3364
  );
3364
- const payment = React9.useMemo(
3365
+ const payment = React13__default.useMemo(
3365
3366
  () => (connectors ?? []).filter((c) => c.category && PAYMENT_CATEGORIES.includes(c.category)),
3366
3367
  [connectors]
3367
3368
  );
3368
- const live = React9.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
3369
+ const live = React13__default.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
3369
3370
  const chosen = value.paymentMethods ?? [];
3370
3371
  const staleProvider = !!value.paymentProvider && !live.some((c) => c.slug === value.paymentProvider) && live.length === 0;
3371
- const movedFrom = React9.useRef(null);
3372
- React9.useEffect(() => {
3372
+ const movedFrom = React13__default.useRef(null);
3373
+ React13__default.useEffect(() => {
3373
3374
  if (!settings.supported || settings.loading) return;
3374
3375
  const next = resolvePaymentProvider(value.paymentProvider, live.map((c) => c.slug));
3375
3376
  if (!next) return;
@@ -3601,11 +3602,11 @@ function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }
3601
3602
  ] });
3602
3603
  }
3603
3604
  function HandlingSettings({ settings, provider }) {
3604
- const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3605
+ const shop = React13__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3605
3606
  const { value, set, loading, supported } = settings;
3606
- const [noteDraft, setNoteDraft] = React9.useState(null);
3607
- const [profiles, setProfiles] = React9.useState(null);
3608
- React9.useEffect(() => {
3607
+ const [noteDraft, setNoteDraft] = React13__default.useState(null);
3608
+ const [profiles, setProfiles] = React13__default.useState(null);
3609
+ React13__default.useEffect(() => {
3609
3610
  let cancelled = false;
3610
3611
  void shop.listShippingProfiles?.().then((list) => {
3611
3612
  if (!cancelled) setProfiles(list);
@@ -3713,12 +3714,988 @@ function ChoiceCards({
3713
3714
  );
3714
3715
  }) });
3715
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
+ };
3716
4451
  function parseMoney(raw) {
3717
4452
  const cleaned = raw.replace(/[^\d,.-]/g, "").replace(",", ".");
3718
4453
  if (!cleaned.trim()) return null;
3719
4454
  const n = Number(cleaned);
3720
4455
  return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
3721
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
+ }
3722
4699
  var formatMoney = (v) => v == null ? "" : String(v).replace(".", ",");
3723
4700
  var FREIGHT_SOURCE = [
3724
4701
  {
@@ -3735,46 +4712,48 @@ var FREIGHT_SOURCE = [
3735
4712
  }
3736
4713
  ];
3737
4714
  function ShippingSettings({ provider }) {
3738
- const [registryId, setRegistryId] = React9.useState(shippingRegistryIdFromHash);
3739
- React9.useEffect(() => {
4715
+ const [registryId, setRegistryId] = React13__default.useState(shippingRegistryIdFromHash);
4716
+ React13__default.useEffect(() => {
3740
4717
  const handler = () => setRegistryId(shippingRegistryIdFromHash());
3741
4718
  window.addEventListener("hashchange", handler);
3742
4719
  return () => window.removeEventListener("hashchange", handler);
3743
4720
  }, []);
3744
- const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
3745
- const registries = React9.useMemo(
4721
+ const shop = React13__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
4722
+ const registries = React13__default.useMemo(
3746
4723
  () => buildShopRegistries(provider ?? getShopProvider),
3747
4724
  [provider]
3748
4725
  );
3749
4726
  const storeSettings = useStoreSettings(provider);
3750
4727
  const supported = typeof shop.getShippingSettings === "function" && typeof shop.updateShippingSettings === "function";
3751
- const [row, setRow] = React9.useState(null);
3752
- const [zones, setZones] = React9.useState(null);
3753
- const [locations, setLocations] = React9.useState(null);
3754
- const [loading, setLoading] = React9.useState(supported);
3755
- const [saving, setSaving] = React9.useState(false);
3756
- const [error, setError] = React9.useState(null);
3757
- const [loadError, setLoadError] = React9.useState(null);
3758
- const [flatDraft, setFlatDraft] = React9.useState("");
3759
- const [freeDraft, setFreeDraft] = React9.useState("");
3760
- const [flatBad, setFlatBad] = React9.useState(false);
3761
- 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);
3762
4739
  const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
3763
- const [selected, setSelected] = React9.useState(null);
4740
+ const [selected, setSelected] = React13__default.useState(null);
3764
4741
  const panelAvailable = useConnectorsPanelAvailable();
3765
- const openJourney = React9.useCallback(
4742
+ const openJourney = React13__default.useCallback(
3766
4743
  (connector) => {
3767
4744
  if (panelAvailable) openConnectorsPanel(connector);
3768
4745
  else setSelected(connector);
3769
4746
  },
3770
4747
  [panelAvailable]
3771
4748
  );
3772
- const carriers = React9.useMemo(
4749
+ const carriers = React13__default.useMemo(
3773
4750
  () => (connectors ?? []).filter((c) => c.category === "logistics" || c.category === "shipping"),
3774
4751
  [connectors]
3775
4752
  );
3776
4753
  const carriersConnected = carriers.some((c) => c.state !== "ready");
3777
- React9.useEffect(() => {
4754
+ const [zonesEpoch, setZonesEpoch] = React13__default.useState(0);
4755
+ const reloadZones = React13__default.useCallback(() => setZonesEpoch((n) => n + 1), []);
4756
+ React13__default.useEffect(() => {
3778
4757
  let cancelled = false;
3779
4758
  if (supported) {
3780
4759
  setLoading(true);
@@ -3802,8 +4781,8 @@ function ShippingSettings({ provider }) {
3802
4781
  return () => {
3803
4782
  cancelled = true;
3804
4783
  };
3805
- }, [shop, supported]);
3806
- const save = React9.useCallback(
4784
+ }, [shop, supported, zonesEpoch]);
4785
+ const save = React13__default.useCallback(
3807
4786
  (patch) => {
3808
4787
  if (!supported) return;
3809
4788
  setSaving(true);
@@ -3813,7 +4792,7 @@ function ShippingSettings({ provider }) {
3813
4792
  [shop, supported]
3814
4793
  );
3815
4794
  const commitFlat = () => {
3816
- const parsed = parseMoney(flatDraft);
4795
+ const parsed = parseMoney2(flatDraft);
3817
4796
  const next = flatDraft.trim() === "" ? 0 : parsed;
3818
4797
  if (next == null) {
3819
4798
  setFlatBad(true);
@@ -3829,7 +4808,7 @@ function ShippingSettings({ provider }) {
3829
4808
  if (row?.freeAbove != null) save({ freeAbove: null });
3830
4809
  return;
3831
4810
  }
3832
- const parsed = parseMoney(freeDraft);
4811
+ const parsed = parseMoney2(freeDraft);
3833
4812
  if (parsed == null) {
3834
4813
  setFreeBad(true);
3835
4814
  return;
@@ -3838,8 +4817,10 @@ function ShippingSettings({ provider }) {
3838
4817
  setFreeDraft(formatMoney(parsed));
3839
4818
  if (parsed !== row?.freeAbove) save({ freeAbove: parsed });
3840
4819
  };
3841
- const activeZones = (zones ?? []).filter((z) => z.active && !z.excluded);
3842
- 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");
3843
4824
  const usesGateway = storeSettings.value.carrierQuotes !== false;
3844
4825
  const dispatching = (locations ?? []).filter((l) => l.status === "active" && l.shipsOnline !== false);
3845
4826
  const originCep = dispatching.find((l) => l.postalCode)?.postalCode ?? null;
@@ -3937,11 +4918,21 @@ function ShippingSettings({ provider }) {
3937
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." })
3938
4919
  ] })
3939
4920
  ] }),
3940
- /* @__PURE__ */ jsxs("div", { children: [
4921
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
3941
4922
  /* @__PURE__ */ jsxs("div", { className: "px-1 pb-1 pt-4", children: [
3942
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Zonas de entrega" }),
3943
- /* @__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." })
3944
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 }),
3945
4936
  /* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
3946
4937
  "button",
3947
4938
  {
@@ -3953,10 +4944,15 @@ function ShippingSettings({ provider }) {
3953
4944
  children: [
3954
4945
  /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3955
4946
  /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
3956
- /* @__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"}` }),
3957
4948
  /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: [
3958
- 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 "),
3959
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
+ ] }),
3960
4956
  excludedZones.length > 0 && /* @__PURE__ */ jsxs("span", { className: "mt-0.5 block text-red-700 dark:text-red-400", children: [
3961
4957
  excludedZones.length === 1 ? "1 regi\xE3o recusada" : `${excludedZones.length} regi\xF5es recusadas`,
3962
4958
  " \u2014 ",
@@ -5068,31 +6064,9 @@ var vindiConnectorDef = {
5068
6064
  };
5069
6065
  var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
5070
6066
  var MERCADOPAGO_LATENCY_BUDGET_MS = 6e3;
5071
- var MERCADOPAGO_FIELDS = [
5072
- {
5073
- key: "accessToken",
5074
- label: "Access token",
5075
- type: "password",
5076
- secret: true,
5077
- placeholder: "APP_USR-\u2026 (Suas integra\xE7\xF5es \u2192 sua aplica\xE7\xE3o \u2192 Credenciais)"
5078
- },
5079
- {
5080
- key: "publicKey",
5081
- label: "Public key",
5082
- // Not masked: it travels to the browser anyway, and a merchant who cannot
5083
- // see it cannot tell the test key from the production one at a glance.
5084
- type: "text",
5085
- secret: true,
5086
- placeholder: "APP_USR-\u2026"
5087
- }
5088
- ];
5089
- var store6 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
5090
- fields: MERCADOPAGO_FIELDS
5091
- });
5092
- function connectionDetail2(settings) {
5093
- const key = String(settings?.publicKey ?? "").trim();
5094
- if (!key) return "Pix e cart\xE3o";
5095
- 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";
5096
6070
  }
5097
6071
  function MercadoPagoExtraPanel() {
5098
6072
  const [connected, setConnected] = useState(false);
@@ -5120,32 +6094,35 @@ function MercadoPagoExtraPanel() {
5120
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." }),
5121
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.' }),
5122
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." })
5123
- ] }) : /* @__PURE__ */ jsxs("p", { children: [
5124
- "As duas chaves ficam no painel do Mercado Pago, em ",
5125
- /* @__PURE__ */ jsx("strong", { children: "Suas integra\xE7\xF5es" }),
5126
- " \u2192 sua aplica\xE7\xE3o \u2192 ",
5127
- /* @__PURE__ */ jsx("strong", { children: "Credenciais de produ\xE7\xE3o" }),
5128
- ". Copie o ",
5129
- /* @__PURE__ */ jsx("em", { children: "access token" }),
5130
- " e a",
5131
- " ",
5132
- /* @__PURE__ */ jsx("em", { children: "public key" }),
5133
- " da ",
5134
- /* @__PURE__ */ jsx("strong", { children: "mesma" }),
5135
- " 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
+ ] })
5136
6113
  ] }),
5137
6114
  /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-dashed p-3", children: [
5138
6115
  /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Para testar sem cobrar de verdade" }),
5139
6116
  /* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
5140
- "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
+ " ",
5141
6121
  /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5", children: "TEST-" }),
5142
6122
  " ",
5143
6123
  /* @__PURE__ */ jsx("strong", { children: "n\xE3o funcionam" }),
5144
- " aqui \u2014 o Mercado Pago as recusa nesta API. O teste \xE9 feito com ",
5145
- /* @__PURE__ */ jsx("strong", { children: "usu\xE1rios de teste" }),
5146
- ": crie um vendedor e um comprador em Suas integra\xE7\xF5es \u2192 Contas de teste, e use as chaves de ",
5147
- /* @__PURE__ */ jsx("em", { children: "produ\xE7\xE3o" }),
5148
- " 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."
5149
6126
  ] })
5150
6127
  ] })
5151
6128
  ] });
@@ -5163,15 +6140,25 @@ var mercadoPagoConnectorDef = {
5163
6140
  // its own domain, so a path would not.
5164
6141
  logoUrl: "https://assets.fayz.ai/connectors/mercadopago.png",
5165
6142
  category: "payments",
5166
- // Pasted keys today. Unlike Vindi — whose consent screen only LOOKS like OAuth
5167
- // — Mercado Pago has a real merchant OAuth, and it is the right end state: one
5168
- // platform application, each seller authorizing it, `marketplace_fee` taken on
5169
- // the charge instead of invoiced afterwards. What it needs is the redirect URI
5170
- // registered against the platform broker, which is a change in ymaia and not
5171
- // here. The same slug is already declared by plugin-financial with
5172
- // `authKind: 'oauth'`, and because a credential is keyed by slug + tenant, a
5173
- // store that authorizes there is redeemable from here the day that lands.
5174
- 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),
5175
6162
  // The charge is opened in the buyer's request path; the notification and the
5176
6163
  // refund are outbound sync work.
5177
6164
  planes: ["request", "sync"],
@@ -5203,7 +6190,6 @@ var mercadoPagoConnectorDef = {
5203
6190
  // confirmed without a payment is worse than a refused checkout. Same verdict
5204
6191
  // as the other four gateways, opposite to the carrier next door.
5205
6192
  onFailure: "block",
5206
- fields: MERCADOPAGO_FIELDS,
5207
6193
  invokedAt: [
5208
6194
  {
5209
6195
  key: "shop.checkout.session_open",
@@ -5228,7 +6214,7 @@ var mercadoPagoConnectorDef = {
5228
6214
  MERCADOPAGO_CONNECTOR_ID,
5229
6215
  (connection) => ({
5230
6216
  connected: !!connection?.active,
5231
- detail: connectionDetail2(connection?.settings),
6217
+ detail: connectionDetail2(),
5232
6218
  plane: "request"
5233
6219
  }),
5234
6220
  { store: () => store6 }
@@ -5265,12 +6251,12 @@ function notifyChannelsChanged() {
5265
6251
  if (typeof window !== "undefined") window.dispatchEvent(new Event("fayz:channels-changed"));
5266
6252
  }
5267
6253
  function ChannelsPage({ resolveProvider }) {
5268
- const provider = React9.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
6254
+ const provider = React13__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
5269
6255
  const supported = typeof provider.listSalesChannels === "function";
5270
- const [channels, setChannels] = React9.useState([]);
5271
- const [loading, setLoading] = React9.useState(true);
5272
- const [adding, setAdding] = React9.useState(null);
5273
- 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 () => {
5274
6260
  if (!provider.listSalesChannels) {
5275
6261
  setLoading(false);
5276
6262
  return;
@@ -5288,7 +6274,7 @@ function ChannelsPage({ resolveProvider }) {
5288
6274
  setLoading(false);
5289
6275
  }
5290
6276
  }, [provider]);
5291
- React9.useEffect(() => {
6277
+ React13__default.useEffect(() => {
5292
6278
  void reload();
5293
6279
  }, [reload]);
5294
6280
  const patch = async (id, input) => {
@@ -5344,7 +6330,7 @@ function ChannelsPage({ resolveProvider }) {
5344
6330
  ] });
5345
6331
  }
5346
6332
  function OnlineStoreCard({ channel, onSave, onToggle }) {
5347
- const [url, setUrl] = React9.useState(channel.url ?? "");
6333
+ const [url, setUrl] = React13__default.useState(channel.url ?? "");
5348
6334
  const dirty = (channel.url ?? "") !== url;
5349
6335
  const meta = KIND_META.online_store;
5350
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: [
@@ -5370,8 +6356,8 @@ function OnlineStoreCard({ channel, onSave, onToggle }) {
5370
6356
  ] }) });
5371
6357
  }
5372
6358
  function ChannelRow({ channel, onSave, onToggle, onRemove }) {
5373
- const [url, setUrl] = React9.useState(channel.url ?? "");
5374
- const [editing, setEditing] = React9.useState(false);
6359
+ const [url, setUrl] = React13__default.useState(channel.url ?? "");
6360
+ const [editing, setEditing] = React13__default.useState(false);
5375
6361
  const meta = KIND_META[channel.kind] ?? KIND_META.custom;
5376
6362
  const Icon = meta.icon;
5377
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: [
@@ -5399,9 +6385,9 @@ function ChannelRow({ channel, onSave, onToggle, onRemove }) {
5399
6385
  }
5400
6386
  function AddChannelForm({ kind, onKind, onCancel, onAdd }) {
5401
6387
  const meta = KIND_META[kind];
5402
- const [label, setLabel] = React9.useState(meta.label);
5403
- const [url, setUrl] = React9.useState("");
5404
- React9.useEffect(() => {
6388
+ const [label, setLabel] = React13__default.useState(meta.label);
6389
+ const [url, setUrl] = React13__default.useState("");
6390
+ React13__default.useEffect(() => {
5405
6391
  setLabel(KIND_META[kind].label);
5406
6392
  }, [kind]);
5407
6393
  return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-card border border-border bg-card p-4", children: [
@@ -5453,17 +6439,17 @@ function formatSize(bytes) {
5453
6439
  return `${(bytes / 1024 ** i).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
5454
6440
  }
5455
6441
  function ContentPage({ resolveProvider }) {
5456
- const provider = React9.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
6442
+ const provider = React13__default.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
5457
6443
  const supported = typeof provider.listMediaFiles === "function";
5458
- const [files, setFiles] = React9.useState(null);
5459
- const [tab, setTab] = React9.useState("all");
5460
- const [uploading, setUploading] = React9.useState(false);
5461
- const [pendingDelete, setPendingDelete] = React9.useState(null);
5462
- const [deleting, setDeleting] = React9.useState(false);
5463
- const [replacing, setReplacing] = React9.useState(false);
5464
- const [openId, setOpenId] = React9.useState(null);
5465
- const [dimensions, setDimensions] = React9.useState({});
5466
- 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 () => {
5467
6453
  if (!provider.listMediaFiles) {
5468
6454
  setFiles([]);
5469
6455
  return;
@@ -5475,10 +6461,10 @@ function ContentPage({ resolveProvider }) {
5475
6461
  setFiles([]);
5476
6462
  }
5477
6463
  }, [provider]);
5478
- React9.useEffect(() => {
6464
+ React13__default.useEffect(() => {
5479
6465
  void reload();
5480
6466
  }, [reload]);
5481
- const measure = React9.useCallback((id, d) => {
6467
+ const measure = React13__default.useCallback((id, d) => {
5482
6468
  setDimensions((prev) => prev[id] ? prev : { ...prev, [id]: d });
5483
6469
  }, []);
5484
6470
  const handleUpload = async (picked) => {
@@ -5500,19 +6486,19 @@ function ContentPage({ resolveProvider }) {
5500
6486
  const handleReplace = async (file, picked) => {
5501
6487
  setReplacing(true);
5502
6488
  try {
5503
- let nextId;
6489
+ let nextId2;
5504
6490
  if (file.reference) {
5505
6491
  if (!provider.replaceProductImage) throw new Error("unsupported");
5506
6492
  const image = await provider.replaceProductImage(file.reference.attachmentId, picked);
5507
- nextId = productImageToMediaFile(image, file.reference.label).id;
6493
+ nextId2 = productImageToMediaFile(image, file.reference.label).id;
5508
6494
  } else {
5509
6495
  if (!provider.uploadMediaFile) throw new Error("unsupported");
5510
6496
  const uploaded = await provider.uploadMediaFile(picked);
5511
6497
  if (provider.deleteMediaFile) await provider.deleteMediaFile(file.id);
5512
- nextId = uploaded.id;
6498
+ nextId2 = uploaded.id;
5513
6499
  }
5514
6500
  await reload();
5515
- setOpenId(nextId);
6501
+ setOpenId(nextId2);
5516
6502
  toast.success("Arquivo trocado");
5517
6503
  } catch (err) {
5518
6504
  console.warn("[content] replace failed", err);
@@ -5668,7 +6654,7 @@ function SectionTitle({ title, count }) {
5668
6654
  ] });
5669
6655
  }
5670
6656
  function UploadButton({ uploading, onPick }) {
5671
- const inputRef = React9.useRef(null);
6657
+ const inputRef = React13__default.useRef(null);
5672
6658
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5673
6659
  /* @__PURE__ */ jsx(
5674
6660
  "input",
@@ -5748,8 +6734,8 @@ function MediaDetail({
5748
6734
  onDelete,
5749
6735
  replacing
5750
6736
  }) {
5751
- const replaceRef = React9.useRef(null);
5752
- React9.useEffect(() => {
6737
+ const replaceRef = React13__default.useRef(null);
6738
+ React13__default.useEffect(() => {
5753
6739
  function onKey(e) {
5754
6740
  if (e.key === "ArrowLeft") onNavigate(-1);
5755
6741
  if (e.key === "ArrowRight") onNavigate(1);
@@ -7604,7 +8590,7 @@ function buildShopAnalyticsCards() {
7604
8590
  }
7605
8591
 
7606
8592
  // src/index.ts
7607
- 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 })));
7608
8594
  function createShopPlugin(options) {
7609
8595
  const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
7610
8596
  setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
@@ -7613,10 +8599,10 @@ function createShopPlugin(options) {
7613
8599
  registerEntityPath("shop_customer", "/orders/customers");
7614
8600
  registerPersonLookup("shop_customer", shopCustomerLookup(() => resolveProvider()));
7615
8601
  const pageFor = (section) => {
7616
- const Wrapped = () => React9.createElement(
7617
- React9.Suspense,
8602
+ const Wrapped = () => React13__default.createElement(
8603
+ React13__default.Suspense,
7618
8604
  { fallback: null },
7619
- React9.createElement(
8605
+ React13__default.createElement(
7620
8606
  ShopPage,
7621
8607
  { provider: options?.provider, section }
7622
8608
  )
@@ -7795,7 +8781,7 @@ function createShopPlugin(options) {
7795
8781
  dataProvider: createShopGiftCardDataProvider(resolveProvider),
7796
8782
  feature: "shop",
7797
8783
  stats: (items, total) => {
7798
- 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);
7799
8785
  const active = items?.filter((g) => g.status === "active") ?? [];
7800
8786
  return [
7801
8787
  { id: "total", label: "Cart\xF5es emitidos", value: total, icon: CreditCard },
@@ -7806,20 +8792,20 @@ function createShopPlugin(options) {
7806
8792
  id: "outstanding",
7807
8793
  label: "Saldo em aberto",
7808
8794
  icon: CircleDollarSign,
7809
- value: money2(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
8795
+ value: money3(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
7810
8796
  },
7811
8797
  {
7812
8798
  id: "issued",
7813
8799
  label: "Valor emitido",
7814
8800
  icon: TicketCheck,
7815
- 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)
7816
8802
  }
7817
8803
  ];
7818
8804
  }
7819
8805
  });
7820
- const ContentPageWrapped = () => React9.createElement(ContentPage, { resolveProvider: options?.provider });
8806
+ const ContentPageWrapped = () => React13__default.createElement(ContentPage, { resolveProvider: options?.provider });
7821
8807
  ContentPageWrapped.displayName = "ContentPage";
7822
- const ChannelsPageWrapped = () => React9.createElement(ChannelsPage, { resolveProvider: options?.provider });
8808
+ const ChannelsPageWrapped = () => React13__default.createElement(ChannelsPage, { resolveProvider: options?.provider });
7823
8809
  ChannelsPageWrapped.displayName = "ChannelsPage";
7824
8810
  const navSection = options?.navSection ?? "main";
7825
8811
  const navPosition = options?.navPosition ?? 6;
@@ -7909,7 +8895,7 @@ function createShopPlugin(options) {
7909
8895
  ];
7910
8896
  const redirectTo = (target) => {
7911
8897
  const Redirect = () => {
7912
- React9.useEffect(() => {
8898
+ React13__default.useEffect(() => {
7913
8899
  adminNavigateTo(target);
7914
8900
  }, []);
7915
8901
  return null;
@@ -8037,7 +9023,7 @@ function createShopPlugin(options) {
8037
9023
  label: label("store", "Loja"),
8038
9024
  icon: "ShoppingBag",
8039
9025
  group: COMMERCE_GROUP,
8040
- component: settingsTab("StoreGeneralSettings", () => React9.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
9026
+ component: settingsTab("StoreGeneralSettings", () => React13__default.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
8041
9027
  order: 30,
8042
9028
  permission: READ
8043
9029
  },
@@ -8046,7 +9032,7 @@ function createShopPlugin(options) {
8046
9032
  label: "Pagamentos",
8047
9033
  icon: "CreditCard",
8048
9034
  group: COMMERCE_GROUP,
8049
- component: settingsTab("PaymentsSettings", () => React9.createElement(PaymentsSettings, { provider: options?.provider })),
9035
+ component: settingsTab("PaymentsSettings", () => React13__default.createElement(PaymentsSettings, { provider: options?.provider })),
8050
9036
  order: 31,
8051
9037
  permission: READ
8052
9038
  },
@@ -8055,7 +9041,7 @@ function createShopPlugin(options) {
8055
9041
  label: "Checkout",
8056
9042
  icon: "ShoppingCart",
8057
9043
  group: COMMERCE_GROUP,
8058
- component: settingsTab("CheckoutSettings", () => React9.createElement(CheckoutSettings, { provider: options?.provider })),
9044
+ component: settingsTab("CheckoutSettings", () => React13__default.createElement(CheckoutSettings, { provider: options?.provider })),
8059
9045
  order: 32,
8060
9046
  permission: READ
8061
9047
  },
@@ -8064,7 +9050,7 @@ function createShopPlugin(options) {
8064
9050
  label: label("shipping", "Entrega"),
8065
9051
  icon: "Truck",
8066
9052
  group: COMMERCE_GROUP,
8067
- component: settingsTab("ShippingSettings", () => React9.createElement(ShippingSettings, { provider: options?.provider })),
9053
+ component: settingsTab("ShippingSettings", () => React13__default.createElement(ShippingSettings, { provider: options?.provider })),
8068
9054
  order: 33,
8069
9055
  permission: READ
8070
9056
  },
@@ -8073,7 +9059,7 @@ function createShopPlugin(options) {
8073
9059
  label: "Fulfillment",
8074
9060
  icon: "PackageCheck",
8075
9061
  group: COMMERCE_GROUP,
8076
- component: settingsTab("FulfillmentSettings", () => React9.createElement(FulfillmentSettings, { provider: options?.provider })),
9062
+ component: settingsTab("FulfillmentSettings", () => React13__default.createElement(FulfillmentSettings, { provider: options?.provider })),
8077
9063
  order: 34,
8078
9064
  permission: READ
8079
9065
  }