@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/{ShopPage-QFIM2HDU.js → ShopPage-6IQ7WKQG.js} +3 -3
- package/dist/{ShopPage-QFIM2HDU.js.map → ShopPage-6IQ7WKQG.js.map} +1 -1
- package/dist/{chunk-2VMTPRGC.js → chunk-NFFJVQCY.js} +93 -34
- package/dist/chunk-NFFJVQCY.js.map +1 -0
- package/dist/index.js +1191 -205
- package/dist/index.js.map +1 -1
- package/dist/integrations/mercadopago/connectorDef.d.ts +1 -28
- package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -1
- package/dist/settings/ShippingSettings.d.ts.map +1 -1
- package/dist/views/DeliveryAgentSurface.d.ts +10 -0
- package/dist/views/DeliveryAgentSurface.d.ts.map +1 -0
- package/dist/views/DeliveryAreaPicker.d.ts +9 -0
- package/dist/views/DeliveryAreaPicker.d.ts.map +1 -0
- package/dist/views/DeliveryRulesDraft.d.ts +9 -0
- package/dist/views/DeliveryRulesDraft.d.ts.map +1 -0
- package/dist/views/delivery-agent.d.ts +14 -0
- package/dist/views/delivery-agent.d.ts.map +1 -0
- package/dist/views/delivery-draft.d.ts +58 -0
- package/dist/views/delivery-draft.d.ts.map +1 -0
- package/dist/views/shippingZoneEntity.d.ts.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-2VMTPRGC.js.map +0 -1
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-
|
|
2
|
-
import
|
|
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] =
|
|
357
|
-
const [variants, setVariants] =
|
|
358
|
-
const [busy, setBusy] =
|
|
359
|
-
const [error, setError] =
|
|
360
|
-
const [selected, setSelected] =
|
|
361
|
-
const [dirty, setDirty] =
|
|
362
|
-
const reload =
|
|
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
|
-
|
|
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 =
|
|
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 [
|
|
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:
|
|
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(
|
|
548
|
+
commit(draft2);
|
|
548
549
|
}
|
|
549
550
|
},
|
|
550
|
-
onBlur: () => commit(
|
|
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] =
|
|
760
|
-
const [weight, setWeight] =
|
|
761
|
-
const [unit, setUnit] =
|
|
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 [
|
|
841
|
-
|
|
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:
|
|
848
|
+
value: draft2,
|
|
848
849
|
disabled,
|
|
849
850
|
placeholder,
|
|
850
851
|
onChange: (e) => setDraft(e.target.value),
|
|
851
852
|
onBlur: () => {
|
|
852
|
-
const parsed = parseNumber(
|
|
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 [
|
|
861
|
-
|
|
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:
|
|
868
|
+
value: draft2,
|
|
868
869
|
disabled,
|
|
869
870
|
placeholder,
|
|
870
871
|
onChange: (e) => setDraft(e.target.value),
|
|
871
|
-
onBlur: () => onCommit(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1537
|
-
const [rows, setRows] =
|
|
1538
|
-
const [open, setOpen] =
|
|
1539
|
-
const [creating, setCreating] =
|
|
1540
|
-
const ref =
|
|
1541
|
-
const load =
|
|
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
|
-
|
|
1549
|
-
|
|
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] =
|
|
1645
|
-
const provider =
|
|
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 =
|
|
1692
|
-
const [rows, setRows] =
|
|
1693
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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,
|
|
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 ${
|
|
2664
|
-
else if (s.minSpent != null) parts.push(`gastou ${
|
|
2665
|
-
else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${
|
|
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
|
|
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,
|
|
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 =
|
|
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] =
|
|
3084
|
-
const [loading, setLoading] =
|
|
3085
|
-
const [saving, setSaving] =
|
|
3086
|
-
const [error, setError] =
|
|
3087
|
-
const [loadError, setLoadError] =
|
|
3088
|
-
|
|
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 =
|
|
3105
|
-
const timerRef =
|
|
3106
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
3355
|
-
const [selected, setSelected] =
|
|
3355
|
+
const [adding, setAdding] = React13__default.useState(false);
|
|
3356
|
+
const [selected, setSelected] = React13__default.useState(null);
|
|
3356
3357
|
const panelAvailable = useConnectorsPanelAvailable();
|
|
3357
|
-
const openJourney =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3372
|
-
|
|
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 =
|
|
3605
|
+
const shop = React13__default.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
|
|
3605
3606
|
const { value, set, loading, supported } = settings;
|
|
3606
|
-
const [noteDraft, setNoteDraft] =
|
|
3607
|
-
const [profiles, setProfiles] =
|
|
3608
|
-
|
|
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] =
|
|
3739
|
-
|
|
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 =
|
|
3745
|
-
const registries =
|
|
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] =
|
|
3752
|
-
const [zones, setZones] =
|
|
3753
|
-
const [locations, setLocations] =
|
|
3754
|
-
const [loading, setLoading] =
|
|
3755
|
-
const [saving, setSaving] =
|
|
3756
|
-
const [error, setError] =
|
|
3757
|
-
const [loadError, setLoadError] =
|
|
3758
|
-
const [flatDraft, setFlatDraft] =
|
|
3759
|
-
const [freeDraft, setFreeDraft] =
|
|
3760
|
-
const [flatBad, setFlatBad] =
|
|
3761
|
-
const [freeBad, setFreeBad] =
|
|
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] =
|
|
4740
|
+
const [selected, setSelected] = React13__default.useState(null);
|
|
3764
4741
|
const panelAvailable = useConnectorsPanelAvailable();
|
|
3765
|
-
const openJourney =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
3842
|
-
const
|
|
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: "
|
|
3943
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "
|
|
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
|
|
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
|
|
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
|
|
5072
|
-
|
|
5073
|
-
|
|
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(
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
/* @__PURE__ */
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
/* @__PURE__ */
|
|
5135
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
5145
|
-
|
|
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
|
-
//
|
|
5167
|
-
//
|
|
5168
|
-
//
|
|
5169
|
-
//
|
|
5170
|
-
//
|
|
5171
|
-
//
|
|
5172
|
-
//
|
|
5173
|
-
//
|
|
5174
|
-
|
|
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(
|
|
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 =
|
|
6254
|
+
const provider = React13__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
|
|
5269
6255
|
const supported = typeof provider.listSalesChannels === "function";
|
|
5270
|
-
const [channels, setChannels] =
|
|
5271
|
-
const [loading, setLoading] =
|
|
5272
|
-
const [adding, setAdding] =
|
|
5273
|
-
const reload =
|
|
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
|
-
|
|
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] =
|
|
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] =
|
|
5374
|
-
const [editing, setEditing] =
|
|
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] =
|
|
5403
|
-
const [url, setUrl] =
|
|
5404
|
-
|
|
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 =
|
|
6442
|
+
const provider = React13__default.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
|
|
5457
6443
|
const supported = typeof provider.listMediaFiles === "function";
|
|
5458
|
-
const [files, setFiles] =
|
|
5459
|
-
const [tab, setTab] =
|
|
5460
|
-
const [uploading, setUploading] =
|
|
5461
|
-
const [pendingDelete, setPendingDelete] =
|
|
5462
|
-
const [deleting, setDeleting] =
|
|
5463
|
-
const [replacing, setReplacing] =
|
|
5464
|
-
const [openId, setOpenId] =
|
|
5465
|
-
const [dimensions, setDimensions] =
|
|
5466
|
-
const reload =
|
|
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
|
-
|
|
6464
|
+
React13__default.useEffect(() => {
|
|
5479
6465
|
void reload();
|
|
5480
6466
|
}, [reload]);
|
|
5481
|
-
const measure =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
6498
|
+
nextId2 = uploaded.id;
|
|
5513
6499
|
}
|
|
5514
6500
|
await reload();
|
|
5515
|
-
setOpenId(
|
|
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 =
|
|
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 =
|
|
5752
|
-
|
|
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 =
|
|
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 = () =>
|
|
7617
|
-
|
|
8602
|
+
const Wrapped = () => React13__default.createElement(
|
|
8603
|
+
React13__default.Suspense,
|
|
7618
8604
|
{ fallback: null },
|
|
7619
|
-
|
|
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
|
|
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:
|
|
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:
|
|
8801
|
+
value: money3(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
|
|
7816
8802
|
}
|
|
7817
8803
|
];
|
|
7818
8804
|
}
|
|
7819
8805
|
});
|
|
7820
|
-
const ContentPageWrapped = () =>
|
|
8806
|
+
const ContentPageWrapped = () => React13__default.createElement(ContentPage, { resolveProvider: options?.provider });
|
|
7821
8807
|
ContentPageWrapped.displayName = "ContentPage";
|
|
7822
|
-
const ChannelsPageWrapped = () =>
|
|
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
|
-
|
|
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", () =>
|
|
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", () =>
|
|
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", () =>
|
|
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", () =>
|
|
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", () =>
|
|
9062
|
+
component: settingsTab("FulfillmentSettings", () => React13__default.createElement(FulfillmentSettings, { provider: options?.provider })),
|
|
8077
9063
|
order: 34,
|
|
8078
9064
|
permission: READ
|
|
8079
9065
|
}
|