@fayz-ai/plugin-shop 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ShopPage-VVLOH3RF.js → ShopPage-QFIM2HDU.js} +1494 -423
- package/dist/ShopPage-QFIM2HDU.js.map +1 -0
- package/dist/{chunk-HAKXHHAG.js → chunk-2VMTPRGC.js} +202 -11
- package/dist/chunk-2VMTPRGC.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1127 -298
- package/dist/index.js.map +1 -1
- package/dist/integrations/bling/data.d.ts.map +1 -1
- package/dist/integrations/mercadopago/connectorDef.d.ts +41 -0
- package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -0
- package/dist/integrations/neochef/data.d.ts.map +1 -1
- package/dist/integrations/vindi/connectorDef.d.ts +42 -29
- package/dist/integrations/vindi/connectorDef.d.ts.map +1 -1
- package/dist/views/CustomerModal.d.ts +33 -0
- package/dist/views/CustomerModal.d.ts.map +1 -0
- package/dist/views/NewOrderCharge.d.ts +26 -2
- package/dist/views/NewOrderCharge.d.ts.map +1 -1
- package/dist/views/NewOrderPage.d.ts +10 -1
- package/dist/views/NewOrderPage.d.ts.map +1 -1
- package/dist/views/OrderDetailView.d.ts.map +1 -1
- package/dist/views/OrdersAgentSurface.d.ts +10 -0
- package/dist/views/OrdersAgentSurface.d.ts.map +1 -0
- package/dist/views/ProductPositionsTab.d.ts +7 -0
- package/dist/views/ProductPositionsTab.d.ts.map +1 -0
- package/dist/views/ProductStockTab.d.ts +84 -0
- package/dist/views/ProductStockTab.d.ts.map +1 -0
- package/dist/views/ShopPage.d.ts.map +1 -1
- package/dist/views/StockEntryModal.d.ts +32 -0
- package/dist/views/StockEntryModal.d.ts.map +1 -0
- package/dist/views/customerEntity.d.ts.map +1 -1
- package/dist/views/discount-agent.d.ts +2 -0
- package/dist/views/discount-agent.d.ts.map +1 -0
- package/dist/views/new-order-rules.d.ts +13 -0
- package/dist/views/new-order-rules.d.ts.map +1 -1
- package/dist/views/orders-agent.d.ts +11 -0
- package/dist/views/orders-agent.d.ts.map +1 -0
- package/dist/views/product-matching.d.ts +38 -0
- package/dist/views/product-matching.d.ts.map +1 -0
- package/dist/views/productEntity.d.ts.map +1 -1
- package/dist/views/stock-agent.d.ts +82 -0
- package/dist/views/stock-agent.d.ts.map +1 -0
- package/package.json +8 -6
- package/dist/ShopPage-VVLOH3RF.js.map +0 -1
- package/dist/chunk-HAKXHHAG.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { shopCustomerLookup, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-
|
|
2
|
-
import
|
|
3
|
-
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, resolveDataProvider } from '@fayz-ai/core';
|
|
1
|
+
import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-2VMTPRGC.js';
|
|
2
|
+
import React9, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
3
|
+
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, getActiveTenantId, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
|
|
4
4
|
import { getShopTenantId, setShopTenantResolver, buildShopMessageDefaults, registerShippingLabelProvider, getShopProvider, buildShopDomainEvents, axesFromOptions, planVariantMatrix, validateVariantAxes, hasOnlyDefaultVariant, MAX_VARIANT_AXES, leavesNothingSellable, productImageToMediaFile, formatVariantCombination, applyVariantMatrix } from '@fayz-ai/shop';
|
|
5
|
-
import { fayzConnectorOAuth, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo,
|
|
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, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info } from 'lucide-react';
|
|
5
|
+
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
7
|
import { PageHeaderActions, PageTabs, Card, Button, Thumbnail, PageHeader, toast, ConfirmDialog, Input, Hint, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Skeleton } from '@fayz-ai/ui';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { listStockReasonCodes, resolveStockStore, createSupabaseStockStore } from '@fayz-ai/shop/inventory';
|
|
10
|
+
import { normalizeMovementType } from '@fayz-ai/plugin-inventory';
|
|
9
11
|
import { advanceWindow, createRateLimiter, sliceWindow, paginate, reconcileMirror, serializeCursor } from '@fayz-ai/core/connector-runtime';
|
|
10
12
|
|
|
11
13
|
// src/integrations/melhor-envio/labelProvider.ts
|
|
@@ -170,20 +172,20 @@ function ProductVariantsTab({ item, getProvider }) {
|
|
|
170
172
|
const product = item;
|
|
171
173
|
const shop = getProvider();
|
|
172
174
|
const supported = typeof shop.getProductVariants === "function" && typeof shop.saveProductOptions === "function" && typeof shop.createProductVariant === "function" && typeof shop.updateProductVariant === "function";
|
|
173
|
-
const [axes, setAxes] =
|
|
174
|
-
const [variants, setVariants] =
|
|
175
|
-
const [busy, setBusy] =
|
|
176
|
-
const [error, setError] =
|
|
177
|
-
const [selected, setSelected] =
|
|
178
|
-
const [dirty, setDirty] =
|
|
179
|
-
const reload =
|
|
175
|
+
const [axes, setAxes] = React9.useState([]);
|
|
176
|
+
const [variants, setVariants] = React9.useState([]);
|
|
177
|
+
const [busy, setBusy] = React9.useState(false);
|
|
178
|
+
const [error, setError] = React9.useState(null);
|
|
179
|
+
const [selected, setSelected] = React9.useState(/* @__PURE__ */ new Set());
|
|
180
|
+
const [dirty, setDirty] = React9.useState(false);
|
|
181
|
+
const reload = React9.useCallback(async () => {
|
|
180
182
|
const set = await getProvider().getProductVariants(product.id, { includeArchived: true });
|
|
181
183
|
setAxes(axesFromOptions(set.options));
|
|
182
184
|
setVariants(set.variants);
|
|
183
185
|
setSelected(/* @__PURE__ */ new Set());
|
|
184
186
|
setDirty(false);
|
|
185
187
|
}, [getProvider, product.id]);
|
|
186
|
-
|
|
188
|
+
React9.useEffect(() => {
|
|
187
189
|
if (!supported) return;
|
|
188
190
|
void reload().catch((e) => setError(message(e)));
|
|
189
191
|
}, [reload, supported]);
|
|
@@ -199,7 +201,7 @@ function ProductVariantsTab({ item, getProvider }) {
|
|
|
199
201
|
setBusy(false);
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
|
-
const plan =
|
|
204
|
+
const plan = React9.useMemo(() => planVariantMatrix(axes, variants), [axes, variants]);
|
|
203
205
|
const axisError = validateVariantAxes(axes);
|
|
204
206
|
const onlyDefault = hasOnlyDefaultVariant(variants);
|
|
205
207
|
const rows = plan.rows;
|
|
@@ -300,7 +302,7 @@ function ProductVariantsTab({ item, getProvider }) {
|
|
|
300
302
|
] });
|
|
301
303
|
}
|
|
302
304
|
function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }) {
|
|
303
|
-
const [draft, setDraft] =
|
|
305
|
+
const [draft, setDraft] = React9.useState("");
|
|
304
306
|
function commit(raw) {
|
|
305
307
|
const values = raw.split(",").map((v) => v.trim()).filter(Boolean);
|
|
306
308
|
if (values.length === 0) return;
|
|
@@ -447,9 +449,9 @@ function VariantRow({ row, axes, product, busy, checked, onToggle, onSave, onSto
|
|
|
447
449
|
const variant = row.variant;
|
|
448
450
|
const pending = !variant;
|
|
449
451
|
const label = formatVariantCombination(row.options, axes);
|
|
450
|
-
const commit = (
|
|
452
|
+
const commit = (field2, value, previous) => {
|
|
451
453
|
if (!variant || value === previous) return;
|
|
452
|
-
onSave(variant.id, { [
|
|
454
|
+
onSave(variant.id, { [field2]: value });
|
|
453
455
|
};
|
|
454
456
|
return /* @__PURE__ */ jsxs("tr", { className: pending ? "bg-muted/20 text-muted-foreground" : void 0, children: [
|
|
455
457
|
/* @__PURE__ */ jsx("td", { className: "px-2 py-1.5", children: /* @__PURE__ */ jsx(
|
|
@@ -573,9 +575,9 @@ function VariantRow({ row, axes, product, busy, checked, onToggle, onSave, onSto
|
|
|
573
575
|
] });
|
|
574
576
|
}
|
|
575
577
|
function BulkBar({ count, disabled, onApply, onClear }) {
|
|
576
|
-
const [price, setPrice] =
|
|
577
|
-
const [weight, setWeight] =
|
|
578
|
-
const [unit, setUnit] =
|
|
578
|
+
const [price, setPrice] = React9.useState("");
|
|
579
|
+
const [weight, setWeight] = React9.useState("");
|
|
580
|
+
const [unit, setUnit] = React9.useState("");
|
|
579
581
|
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: [
|
|
580
582
|
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
581
583
|
count,
|
|
@@ -654,8 +656,8 @@ function parseNumber(raw) {
|
|
|
654
656
|
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
655
657
|
}
|
|
656
658
|
function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", integer }) {
|
|
657
|
-
const [draft, setDraft] =
|
|
658
|
-
|
|
659
|
+
const [draft, setDraft] = React9.useState(value == null ? "" : String(value));
|
|
660
|
+
React9.useEffect(() => {
|
|
659
661
|
setDraft(value == null ? "" : String(value));
|
|
660
662
|
}, [value]);
|
|
661
663
|
return /* @__PURE__ */ jsx(
|
|
@@ -674,8 +676,8 @@ function NumberCell({ value, disabled, placeholder, onCommit, width = "w-20", in
|
|
|
674
676
|
);
|
|
675
677
|
}
|
|
676
678
|
function TextCell({ value, disabled, placeholder, onCommit }) {
|
|
677
|
-
const [draft, setDraft] =
|
|
678
|
-
|
|
679
|
+
const [draft, setDraft] = React9.useState(value);
|
|
680
|
+
React9.useEffect(() => {
|
|
679
681
|
setDraft(value);
|
|
680
682
|
}, [value]);
|
|
681
683
|
return /* @__PURE__ */ jsx(
|
|
@@ -722,6 +724,613 @@ function describePlan(plan) {
|
|
|
722
724
|
function message(error) {
|
|
723
725
|
return error instanceof Error ? error.message : String(error);
|
|
724
726
|
}
|
|
727
|
+
var KIND_OF = {
|
|
728
|
+
// `sign: 0` = o razão aceita os dois lados (ajuste corrige para cima e para baixo).
|
|
729
|
+
entry: { kind: "receipt", sign: 1, reason: "purchase" },
|
|
730
|
+
exit: { kind: "adjustment", sign: -1, reason: "correction" },
|
|
731
|
+
adjustment: { kind: "adjustment", sign: 0, reason: "correction" },
|
|
732
|
+
loss: { kind: "loss", sign: -1, reason: "breakage" },
|
|
733
|
+
// Transferência é DOIS movimentos amarrados, com origem e destino. Metade de
|
|
734
|
+
// uma transferência é estoque sumindo de um lugar e não aparecendo no outro.
|
|
735
|
+
transfer: { kind: "transfer_out", sign: -1, reason: "transfer" }
|
|
736
|
+
};
|
|
737
|
+
function planMovement(rawType, quantity, reasonCode) {
|
|
738
|
+
const type = normalizeMovementType(rawType);
|
|
739
|
+
if (!type) {
|
|
740
|
+
return { error: "unknown_movement_type", message: "Use entrada, sa\xEDda, ajuste ou perda." };
|
|
741
|
+
}
|
|
742
|
+
if (type === "transfer") {
|
|
743
|
+
return {
|
|
744
|
+
error: "transfer_unsupported",
|
|
745
|
+
message: "Transfer\xEAncia entre locais precisa de origem e destino \u2014 ainda n\xE3o fa\xE7o por aqui."
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
const magnitude = Math.abs(Number(quantity) || 0);
|
|
749
|
+
if (magnitude === 0) return { error: "zero_quantity", message: "Movimento de zero n\xE3o \xE9 movimento." };
|
|
750
|
+
const fallback = KIND_OF[type];
|
|
751
|
+
const declared = reasonCode ? listStockReasonCodes().find((r) => r.key === reasonCode) : void 0;
|
|
752
|
+
const kind = declared?.movementKind ?? fallback.kind;
|
|
753
|
+
const sign = fallback.sign === 0 ? Number(quantity) < 0 ? -1 : 1 : fallback.sign;
|
|
754
|
+
return { kind, quantity: magnitude * sign, reasonCode: declared?.key ?? fallback.reason };
|
|
755
|
+
}
|
|
756
|
+
var WHERE_IS = {
|
|
757
|
+
id: "shop.stock.position",
|
|
758
|
+
name: "getStockPosition",
|
|
759
|
+
description: 'Answers "where is my X" for a product the merchant names in words: how many are on hand, how many are held for orders, and how that splits across locations and lots. Resolves the words itself \u2014 send the term as the merchant said it. When two products fit equally it asks with a card instead of guessing.',
|
|
760
|
+
icon: "Boxes",
|
|
761
|
+
mode: "read",
|
|
762
|
+
execution: { plane: "client" },
|
|
763
|
+
category: "Shop",
|
|
764
|
+
role: "operations",
|
|
765
|
+
parameters: {
|
|
766
|
+
type: "object",
|
|
767
|
+
properties: { term: { type: "string", description: "The product, in the merchant's words." } },
|
|
768
|
+
required: ["term"]
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
var RECORD = {
|
|
772
|
+
id: "shop.stock.record",
|
|
773
|
+
name: "recordStockMovement",
|
|
774
|
+
description: "Writes one line in the stock ledger: entrada, sa\xEDda, ajuste or perda. Takes the `variantId` from getStockPosition \u2014 never a name. Send the quantity as a plain positive number and say which movement it is; the sign belongs to the ledger, not to you. The ledger is append-only: a mistake is corrected by an opposing movement, never by editing. `occurredAt` is WHEN the goods moved, which is not when they were typed \u2014 Friday's delivery entered on Monday.",
|
|
775
|
+
icon: "ArrowDownToLine",
|
|
776
|
+
mode: "persist",
|
|
777
|
+
execution: { plane: "client" },
|
|
778
|
+
category: "Shop",
|
|
779
|
+
role: "operations",
|
|
780
|
+
permission: { feature: "shop", action: "update" },
|
|
781
|
+
parameters: {
|
|
782
|
+
type: "object",
|
|
783
|
+
properties: {
|
|
784
|
+
variantId: { type: "string", description: "From getStockPosition." },
|
|
785
|
+
productName: { type: "string", description: "So the merchant reads the card without ids." },
|
|
786
|
+
movementType: {
|
|
787
|
+
type: "string",
|
|
788
|
+
description: "entrada | sa\xEDda | ajuste | perda",
|
|
789
|
+
enum: ["entrada", "saida", "ajuste", "perda"]
|
|
790
|
+
},
|
|
791
|
+
quantity: { type: "number", description: "How many, as a positive number." },
|
|
792
|
+
reasonCode: { type: "string", description: "Reason key when the merchant gave one (purchase, breakage, expiry, theft\u2026)." },
|
|
793
|
+
locationId: { type: "string", description: "Which location. Omitted means the store default." },
|
|
794
|
+
note: { type: "string", description: "Anything the merchant said that is not a field \u2014 the invoice number, who delivered." },
|
|
795
|
+
occurredAt: { type: "string", description: "ISO date when it actually moved. Omitted means now." }
|
|
796
|
+
},
|
|
797
|
+
required: ["variantId", "productName", "movementType", "quantity"]
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
function isLedgerMissing(error) {
|
|
801
|
+
const raw = error instanceof Error ? error.message : String(error?.message ?? error);
|
|
802
|
+
return /plg_shop_stock_|\btracking\b.*does not exist/i.test(raw);
|
|
803
|
+
}
|
|
804
|
+
var LEDGER_MISSING_MESSAGE = "O controle de estoque ainda n\xE3o foi aplicado nesta loja (migration 0034). Enquanto isso o saldo vive s\xF3 no n\xFAmero do produto.";
|
|
805
|
+
function occurredAtFor(chosenDay, today) {
|
|
806
|
+
if (!chosenDay || chosenDay === today) return void 0;
|
|
807
|
+
const parsed = /* @__PURE__ */ new Date(`${chosenDay}T12:00:00`);
|
|
808
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed.toISOString();
|
|
809
|
+
}
|
|
810
|
+
async function commitMovement(args) {
|
|
811
|
+
const plan = planMovement(args.movementType, args.quantity, args.reasonCode);
|
|
812
|
+
if ("error" in plan) return { ok: false, ...plan };
|
|
813
|
+
const store7 = stockStore();
|
|
814
|
+
if (plan.quantity < 0) {
|
|
815
|
+
const available = await store7.getAvailable(args.variantId);
|
|
816
|
+
if (Math.abs(plan.quantity) > available) {
|
|
817
|
+
return {
|
|
818
|
+
ok: false,
|
|
819
|
+
error: "insufficient_stock",
|
|
820
|
+
available,
|
|
821
|
+
message: `S\xF3 h\xE1 ${available} em estoque \u2014 a sa\xEDda de ${Math.abs(plan.quantity)} deixaria negativo.`
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
const movement = await store7.recordMovement({
|
|
826
|
+
variantId: args.variantId,
|
|
827
|
+
kind: plan.kind,
|
|
828
|
+
quantity: plan.quantity,
|
|
829
|
+
reasonCode: plan.reasonCode,
|
|
830
|
+
...args.locationId ? { locationId: args.locationId } : {},
|
|
831
|
+
...args.note ? { note: args.note } : {},
|
|
832
|
+
...args.occurredAt ? { occurredAt: args.occurredAt } : {}
|
|
833
|
+
});
|
|
834
|
+
for (const table of [
|
|
835
|
+
"plg_shop_products",
|
|
836
|
+
// a Lista, e é a que o usuário estava vendo
|
|
837
|
+
"plg_shop_product_variants",
|
|
838
|
+
// a matriz de variantes
|
|
839
|
+
"plg_shop_stock_balances",
|
|
840
|
+
// as Posições
|
|
841
|
+
"products"
|
|
842
|
+
// o arquétipo, que as telas de Estoque leem
|
|
843
|
+
]) {
|
|
844
|
+
emitDataChanged({ table, op: "update" });
|
|
845
|
+
}
|
|
846
|
+
return {
|
|
847
|
+
ok: true,
|
|
848
|
+
movementId: movement.id,
|
|
849
|
+
kind: plan.kind,
|
|
850
|
+
quantity: plan.quantity,
|
|
851
|
+
reasonCode: plan.reasonCode,
|
|
852
|
+
available: await store7.getAvailable(args.variantId)
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
856
|
+
var stockStore = () => resolveStockStore(getActiveTenantId() ?? "", () => createSupabaseStockStore());
|
|
857
|
+
function summarize(balances, locations) {
|
|
858
|
+
const nameOf = (id) => locations.find((l) => l.id === id)?.name ?? id;
|
|
859
|
+
const total = balances.reduce(
|
|
860
|
+
(acc, b) => ({
|
|
861
|
+
onHand: acc.onHand + b.onHand,
|
|
862
|
+
reserved: acc.reserved + b.reserved,
|
|
863
|
+
available: acc.available + b.available
|
|
864
|
+
}),
|
|
865
|
+
{ onHand: 0, reserved: 0, available: 0 }
|
|
866
|
+
);
|
|
867
|
+
const byLocation = /* @__PURE__ */ new Map();
|
|
868
|
+
for (const b of balances) {
|
|
869
|
+
const row = byLocation.get(b.locationId) ?? { location: nameOf(b.locationId), onHand: 0, available: 0 };
|
|
870
|
+
row.onHand += b.onHand;
|
|
871
|
+
row.available += b.available;
|
|
872
|
+
byLocation.set(b.locationId, row);
|
|
873
|
+
}
|
|
874
|
+
return { total, byLocation: [...byLocation.values()] };
|
|
875
|
+
}
|
|
876
|
+
function createStockAgentTools({ getProvider, onChanged }) {
|
|
877
|
+
const failed = (error) => isLedgerMissing(error) ? { ok: false, error: "ledger_not_installed", message: LEDGER_MISSING_MESSAGE } : { ok: false, error: errorMessage(error) };
|
|
878
|
+
const variantsOf = async (product) => {
|
|
879
|
+
try {
|
|
880
|
+
const set = await getProvider().getProductVariants?.(product.id);
|
|
881
|
+
const ids = (set?.variants ?? []).map((v) => v.id);
|
|
882
|
+
if (ids.length > 0) return ids;
|
|
883
|
+
} catch {
|
|
884
|
+
}
|
|
885
|
+
return [];
|
|
886
|
+
};
|
|
887
|
+
return [
|
|
888
|
+
{
|
|
889
|
+
tool: WHERE_IS,
|
|
890
|
+
execute: async (args) => {
|
|
891
|
+
const term = text(args.term);
|
|
892
|
+
if (!term) return { ok: false, error: "no_term" };
|
|
893
|
+
try {
|
|
894
|
+
const catalog = await listProductsCached(getProvider, { status: "active", limit: 500 });
|
|
895
|
+
const answer = resolveTerm(term, catalog);
|
|
896
|
+
if (!answer.resolved) {
|
|
897
|
+
if (!answer.options?.length) return { ok: true, term, notFound: true };
|
|
898
|
+
return {
|
|
899
|
+
ok: true,
|
|
900
|
+
kind: "record_choice",
|
|
901
|
+
choices: [{
|
|
902
|
+
id: "stock-product",
|
|
903
|
+
question: `Qual "${term}"?`,
|
|
904
|
+
options: answer.options.map((c) => ({ id: c.productId, label: c.name, meta: candidateMeta(c) }))
|
|
905
|
+
}],
|
|
906
|
+
term,
|
|
907
|
+
needsChoice: true
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
const product = catalog.find((p) => p.id === answer.resolved.productId);
|
|
911
|
+
const variantIds = await variantsOf(product);
|
|
912
|
+
const store7 = stockStore();
|
|
913
|
+
const [balances, locations] = await Promise.all([
|
|
914
|
+
store7.listBalances(variantIds.length > 0 ? { variantIds } : {}),
|
|
915
|
+
store7.listLocations()
|
|
916
|
+
]);
|
|
917
|
+
const mine = variantIds.length > 0 ? balances : balances.filter(() => false);
|
|
918
|
+
const { total, byLocation } = summarize(mine, locations);
|
|
919
|
+
return {
|
|
920
|
+
ok: true,
|
|
921
|
+
product: { productId: product.id, name: product.name, sku: product.sku },
|
|
922
|
+
// A variante é o que o lançamento precisa; sem ela o razão não tem
|
|
923
|
+
// onde postar, e é melhor dizer isso do que gravar no lugar errado.
|
|
924
|
+
variantId: variantIds[0] ?? null,
|
|
925
|
+
variants: variantIds,
|
|
926
|
+
total,
|
|
927
|
+
byLocation,
|
|
928
|
+
locations: locations.map((l) => ({ locationId: l.id, name: l.name, isDefault: l.isDefault }))
|
|
929
|
+
};
|
|
930
|
+
} catch (error) {
|
|
931
|
+
return failed(error);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
tool: RECORD,
|
|
937
|
+
execute: async (args) => {
|
|
938
|
+
const variantId = text(args.variantId);
|
|
939
|
+
if (!variantId) return { ok: false, error: "no_variant", message: "Preciso da variante \u2014 pergunte a posi\xE7\xE3o primeiro." };
|
|
940
|
+
try {
|
|
941
|
+
const answer = await commitMovement({
|
|
942
|
+
variantId,
|
|
943
|
+
movementType: text(args.movementType),
|
|
944
|
+
quantity: Number(args.quantity),
|
|
945
|
+
reasonCode: text(args.reasonCode) || void 0,
|
|
946
|
+
locationId: text(args.locationId) || void 0,
|
|
947
|
+
note: text(args.note) || void 0,
|
|
948
|
+
occurredAt: text(args.occurredAt) || void 0
|
|
949
|
+
});
|
|
950
|
+
if (answer.ok) onChanged?.();
|
|
951
|
+
return answer;
|
|
952
|
+
} catch (error) {
|
|
953
|
+
return failed(error);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
];
|
|
958
|
+
}
|
|
959
|
+
var STOCK_INSTRUCTIONS = `
|
|
960
|
+
Esta tela \xE9 o estoque da loja. O lojista lan\xE7a o que entrou e o que saiu, e pergunta onde est\xE3o as coisas.
|
|
961
|
+
|
|
962
|
+
\xB7 "Cad\xEA minha costela", "quanto tem de X": \`getStockPosition\` com as palavras dele. Ela resolve o produto sozinha e devolve o saldo por local. Responda com o n\xFAmero e o local, uma linha.
|
|
963
|
+
\xB7 Lan\xE7ar entrada ou sa\xEDda: pegue o \`variantId\` na posi\xE7\xE3o e chame \`recordStockMovement\`. Quantidade sempre positiva \u2014 o sinal \xE9 do raz\xE3o, n\xE3o seu.
|
|
964
|
+
\xB7 A esp\xE9cie vem do que ele disse: "chegaram 20" \xE9 entrada, "quebrou 3" \xE9 perda com raz\xE3o \`breakage\`, "vencerem 2" \xE9 perda com \`expiry\`, "contei e tem 18" \xE9 ajuste.
|
|
965
|
+
\xB7 A nota de sexta lan\xE7ada na segunda: se ele disser QUANDO chegou, mande em \`occurredAt\`. Sem isso o raz\xE3o registra hoje, e o custo cai no m\xEAs errado.
|
|
966
|
+
\xB7 O raz\xE3o n\xE3o se edita. Errou, lan\xE7a o movimento contr\xE1rio \u2014 nunca prometa "corrigir" o lan\xE7amento.
|
|
967
|
+
\xB7 Nunca invente produto nem variante. Termo amb\xEDguo j\xE1 virou pergunta com bot\xF5es: n\xE3o repita as op\xE7\xF5es no texto.
|
|
968
|
+
`.trim();
|
|
969
|
+
var stockStore2 = () => resolveStockStore(getActiveTenantId() ?? "", () => createSupabaseStockStore());
|
|
970
|
+
var MOVEMENT_TYPES = [
|
|
971
|
+
{ value: "entrada", label: "Entrada" },
|
|
972
|
+
{ value: "saida", label: "Sa\xEDda" },
|
|
973
|
+
{ value: "ajuste", label: "Ajuste" },
|
|
974
|
+
{ value: "perda", label: "Perda" }
|
|
975
|
+
];
|
|
976
|
+
var KIND_FOR = {
|
|
977
|
+
entrada: "receipt",
|
|
978
|
+
saida: "adjustment",
|
|
979
|
+
ajuste: "adjustment",
|
|
980
|
+
perda: "loss"
|
|
981
|
+
};
|
|
982
|
+
var todayISO = () => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
983
|
+
var field = "mt-1 w-full rounded-md border bg-background px-2.5 py-1.5 text-sm outline-none focus:border-primary";
|
|
984
|
+
var STOCK_INTRO = {
|
|
985
|
+
role: "operations",
|
|
986
|
+
persona: "Estoquista",
|
|
987
|
+
pitch: "Diga o que entrou ou saiu que eu lan\xE7o.",
|
|
988
|
+
description: "Lan\xE7o entrada, sa\xEDda, ajuste ou perda no raz\xE3o deste produto, e respondo quanto tem e onde. Voc\xEA confirma antes de gravar.",
|
|
989
|
+
suggestions: [
|
|
990
|
+
{ label: "Chegaram 20 unidades" },
|
|
991
|
+
{ label: "Quebraram 2" }
|
|
992
|
+
]
|
|
993
|
+
};
|
|
994
|
+
function ProductPositionsTab({ item, getProvider }) {
|
|
995
|
+
const product = item;
|
|
996
|
+
const [variants, setVariants] = useState([]);
|
|
997
|
+
const [balances, setBalances] = useState([]);
|
|
998
|
+
const [locations, setLocations] = useState([]);
|
|
999
|
+
const [movements, setMovements] = useState([]);
|
|
1000
|
+
const [loading, setLoading] = useState(true);
|
|
1001
|
+
const [error, setError] = useState(null);
|
|
1002
|
+
const [variantId, setVariantId] = useState("");
|
|
1003
|
+
const [type, setType] = useState(null);
|
|
1004
|
+
const [quantity, setQuantity] = useState("");
|
|
1005
|
+
const [reasonCode, setReasonCode] = useState("");
|
|
1006
|
+
const [note, setNote] = useState("");
|
|
1007
|
+
const [noteOpen, setNoteOpen] = useState(false);
|
|
1008
|
+
const [occurredOn, setOccurredOn] = useState(() => todayISO());
|
|
1009
|
+
const [saving, setSaving] = useState(false);
|
|
1010
|
+
const quantityRef = React9.useRef(null);
|
|
1011
|
+
const load = useCallback(async () => {
|
|
1012
|
+
setLoading(true);
|
|
1013
|
+
try {
|
|
1014
|
+
const set = await getProvider().getProductVariants?.(product.id);
|
|
1015
|
+
const list = set?.variants ?? [];
|
|
1016
|
+
setVariants(list);
|
|
1017
|
+
const ids = list.map((v) => v.id);
|
|
1018
|
+
setVariantId((current) => current || ids[0] || "");
|
|
1019
|
+
const store7 = stockStore2();
|
|
1020
|
+
const [b, l] = await Promise.all([
|
|
1021
|
+
ids.length > 0 ? store7.listBalances({ variantIds: ids }) : Promise.resolve([]),
|
|
1022
|
+
store7.listLocations()
|
|
1023
|
+
]);
|
|
1024
|
+
setBalances(b);
|
|
1025
|
+
setLocations(l);
|
|
1026
|
+
setMovements(ids[0] ? await store7.listMovements({ variantId: ids[0], limit: 10 }) : []);
|
|
1027
|
+
setError(null);
|
|
1028
|
+
} catch (err) {
|
|
1029
|
+
setError(isLedgerMissing(err) ? LEDGER_MISSING_MESSAGE : errorMessage(err));
|
|
1030
|
+
} finally {
|
|
1031
|
+
setLoading(false);
|
|
1032
|
+
}
|
|
1033
|
+
}, [getProvider, product.id]);
|
|
1034
|
+
useEffect(() => {
|
|
1035
|
+
void load();
|
|
1036
|
+
}, [load]);
|
|
1037
|
+
const nameOf = useCallback(
|
|
1038
|
+
(id) => locations.find((l) => l.id === id)?.name ?? id,
|
|
1039
|
+
[locations]
|
|
1040
|
+
);
|
|
1041
|
+
const rows = useMemo(() => {
|
|
1042
|
+
const mine = variantId ? balances.filter((b) => b.variantId === variantId) : balances;
|
|
1043
|
+
return mine.map((b) => ({
|
|
1044
|
+
key: b.id,
|
|
1045
|
+
location: nameOf(b.locationId),
|
|
1046
|
+
onHand: b.onHand,
|
|
1047
|
+
reserved: b.reserved,
|
|
1048
|
+
available: b.available
|
|
1049
|
+
}));
|
|
1050
|
+
}, [balances, variantId, nameOf]);
|
|
1051
|
+
const total = useMemo(
|
|
1052
|
+
() => rows.reduce(
|
|
1053
|
+
(acc, r) => ({
|
|
1054
|
+
onHand: acc.onHand + r.onHand,
|
|
1055
|
+
reserved: acc.reserved + r.reserved,
|
|
1056
|
+
available: acc.available + r.available
|
|
1057
|
+
}),
|
|
1058
|
+
{ onHand: 0, reserved: 0, available: 0 }
|
|
1059
|
+
),
|
|
1060
|
+
[rows]
|
|
1061
|
+
);
|
|
1062
|
+
const reasons = useMemo(
|
|
1063
|
+
() => listStockReasonCodes().filter(
|
|
1064
|
+
(r) => !r.movementKind || r.movementKind === KIND_FOR[type ?? "entrada"]
|
|
1065
|
+
),
|
|
1066
|
+
[type]
|
|
1067
|
+
);
|
|
1068
|
+
useEffect(() => {
|
|
1069
|
+
setReasonCode("");
|
|
1070
|
+
}, [type]);
|
|
1071
|
+
const open = (next) => {
|
|
1072
|
+
setType(next);
|
|
1073
|
+
setQuantity("");
|
|
1074
|
+
setNote("");
|
|
1075
|
+
setNoteOpen(false);
|
|
1076
|
+
setOccurredOn(todayISO());
|
|
1077
|
+
window.setTimeout(() => quantityRef.current?.focus(), 0);
|
|
1078
|
+
};
|
|
1079
|
+
const close = () => {
|
|
1080
|
+
setType(null);
|
|
1081
|
+
setQuantity("");
|
|
1082
|
+
setNote("");
|
|
1083
|
+
setNoteOpen(false);
|
|
1084
|
+
setOccurredOn(todayISO());
|
|
1085
|
+
};
|
|
1086
|
+
const amount = Math.abs(Number(quantity) || 0);
|
|
1087
|
+
const outgoing = type === "saida" || type === "perda";
|
|
1088
|
+
const nextBalance = amount > 0 ? outgoing ? total.available - amount : total.available + amount : null;
|
|
1089
|
+
const live = React9.useRef({ product, variantId, total, getProvider, load });
|
|
1090
|
+
live.current = { product, variantId, total, getProvider, load };
|
|
1091
|
+
const contribution = useMemo(() => ({
|
|
1092
|
+
id: "shop.product.positions",
|
|
1093
|
+
pluginId: "shop",
|
|
1094
|
+
role: "operations",
|
|
1095
|
+
instructions: STOCK_INSTRUCTIONS,
|
|
1096
|
+
describe: () => {
|
|
1097
|
+
const o = live.current;
|
|
1098
|
+
return {
|
|
1099
|
+
title: `Estoque \u2014 ${o.product.name}`,
|
|
1100
|
+
record: { entity: "shop_product", id: o.product.id, label: o.product.name },
|
|
1101
|
+
state: {
|
|
1102
|
+
screen: "/products/:id#posicoes",
|
|
1103
|
+
productId: o.product.id,
|
|
1104
|
+
productName: o.product.name,
|
|
1105
|
+
variantId: o.variantId || null,
|
|
1106
|
+
onHand: o.total.onHand,
|
|
1107
|
+
reserved: o.total.reserved,
|
|
1108
|
+
available: o.total.available
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
},
|
|
1112
|
+
tools: createStockAgentTools({
|
|
1113
|
+
getProvider: () => live.current.getProvider(),
|
|
1114
|
+
onChanged: () => {
|
|
1115
|
+
void live.current.load();
|
|
1116
|
+
}
|
|
1117
|
+
})
|
|
1118
|
+
}), []);
|
|
1119
|
+
useAgentSurface(contribution);
|
|
1120
|
+
const submit = async () => {
|
|
1121
|
+
if (!variantId || !type) return;
|
|
1122
|
+
const amount2 = Number(quantity);
|
|
1123
|
+
if (!amount2) {
|
|
1124
|
+
toast.error("Diga quantas unidades.");
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
setSaving(true);
|
|
1128
|
+
try {
|
|
1129
|
+
const answer = await commitMovement({
|
|
1130
|
+
variantId,
|
|
1131
|
+
movementType: type,
|
|
1132
|
+
quantity: amount2,
|
|
1133
|
+
reasonCode: reasonCode || void 0,
|
|
1134
|
+
note: note.trim() || void 0,
|
|
1135
|
+
occurredAt: occurredAtFor(occurredOn, todayISO())
|
|
1136
|
+
});
|
|
1137
|
+
if (!answer.ok) {
|
|
1138
|
+
toast.error(answer.message ?? answer.error);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
toast.success(`Lan\xE7ado. Saldo agora: ${answer.available}.`);
|
|
1142
|
+
close();
|
|
1143
|
+
await load();
|
|
1144
|
+
} catch (err) {
|
|
1145
|
+
toast.error(errorMessage(err));
|
|
1146
|
+
} finally {
|
|
1147
|
+
setSaving(false);
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
if (loading) {
|
|
1151
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1152
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
1153
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1154
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-24 animate-pulse rounded bg-muted" }),
|
|
1155
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-64 animate-pulse rounded bg-muted" })
|
|
1156
|
+
] }),
|
|
1157
|
+
/* @__PURE__ */ jsx("div", { className: "h-9 w-32 animate-pulse rounded-md bg-muted" })
|
|
1158
|
+
] }),
|
|
1159
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-2", children: [0, 1].map((i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-card border bg-card p-3", children: [
|
|
1160
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 flex-1 animate-pulse rounded bg-muted" }),
|
|
1161
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-12 animate-pulse rounded bg-muted" })
|
|
1162
|
+
] }, i)) })
|
|
1163
|
+
] });
|
|
1164
|
+
}
|
|
1165
|
+
if (error) {
|
|
1166
|
+
const missing = error === LEDGER_MISSING_MESSAGE;
|
|
1167
|
+
return /* @__PURE__ */ jsx("p", { className: missing ? "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground" : "rounded-lg border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive", children: error });
|
|
1168
|
+
}
|
|
1169
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1170
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
1171
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1172
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Estoque do produto" }),
|
|
1173
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1174
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.onHand }),
|
|
1175
|
+
" em m\xE3os \xB7",
|
|
1176
|
+
" ",
|
|
1177
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.reserved }),
|
|
1178
|
+
" reservado para pedidos \xB7",
|
|
1179
|
+
" ",
|
|
1180
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums text-foreground", children: total.available }),
|
|
1181
|
+
" dispon\xEDvel. O raz\xE3o n\xE3o se edita: um erro se corrige com o movimento contr\xE1rio."
|
|
1182
|
+
] })
|
|
1183
|
+
] }),
|
|
1184
|
+
variantId && type === null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1185
|
+
/* @__PURE__ */ jsxs(
|
|
1186
|
+
Button,
|
|
1187
|
+
{
|
|
1188
|
+
variant: "ghost",
|
|
1189
|
+
size: "sm",
|
|
1190
|
+
onClick: () => askAI({
|
|
1191
|
+
intro: STOCK_INTRO,
|
|
1192
|
+
hint: "O que entrou ou saiu\u2026",
|
|
1193
|
+
draft: `Entrada de unidades de ${product.name}`
|
|
1194
|
+
}),
|
|
1195
|
+
children: [
|
|
1196
|
+
/* @__PURE__ */ jsx(WandSparkles, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1197
|
+
"Com IA"
|
|
1198
|
+
]
|
|
1199
|
+
}
|
|
1200
|
+
),
|
|
1201
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => open("entrada"), children: [
|
|
1202
|
+
/* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
|
|
1203
|
+
"Dar entrada"
|
|
1204
|
+
] })
|
|
1205
|
+
] })
|
|
1206
|
+
] }),
|
|
1207
|
+
!variantId && // Nunca desabilitar um botão sem dizer por quê. Sem variante o razão não
|
|
1208
|
+
// tem onde postar, e um "Lançar" apagado sem explicação faz o lojista
|
|
1209
|
+
// clicar três vezes e concluir que a tela está quebrada.
|
|
1210
|
+
/* @__PURE__ */ jsxs("p", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: [
|
|
1211
|
+
"Este produto ainda n\xE3o tem uma variante para o estoque apontar. Crie uma em ",
|
|
1212
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Variantes" }),
|
|
1213
|
+
" e volte aqui."
|
|
1214
|
+
] }),
|
|
1215
|
+
variants.length > 1 && /* @__PURE__ */ jsxs("label", { className: "block max-w-xs", children: [
|
|
1216
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Variante" }),
|
|
1217
|
+
/* @__PURE__ */ jsx("select", { value: variantId, onChange: (e) => setVariantId(e.target.value), className: field, children: variants.map((v) => /* @__PURE__ */ jsx("option", { value: v.id, children: Object.values(v.options ?? {}).join(" \xB7 ") || v.sku || v.id.slice(0, 8) }, v.id)) })
|
|
1218
|
+
] }),
|
|
1219
|
+
type !== null && /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-4 motion-safe:animate-zoom-in", children: [
|
|
1220
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: MOVEMENT_TYPES.map((t) => /* @__PURE__ */ jsx(
|
|
1221
|
+
"button",
|
|
1222
|
+
{
|
|
1223
|
+
type: "button",
|
|
1224
|
+
onClick: () => setType(t.value),
|
|
1225
|
+
className: `fz-tap rounded-button px-3 py-1.5 text-xs font-medium transition-colors ${type === t.value ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
|
|
1226
|
+
children: t.label
|
|
1227
|
+
},
|
|
1228
|
+
t.value
|
|
1229
|
+
)) }),
|
|
1230
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-4", children: [
|
|
1231
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
1232
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Quantas?" }),
|
|
1233
|
+
/* @__PURE__ */ jsx(
|
|
1234
|
+
"input",
|
|
1235
|
+
{
|
|
1236
|
+
ref: quantityRef,
|
|
1237
|
+
type: "number",
|
|
1238
|
+
inputMode: "numeric",
|
|
1239
|
+
min: 1,
|
|
1240
|
+
value: quantity,
|
|
1241
|
+
onChange: (e) => setQuantity(e.target.value),
|
|
1242
|
+
onKeyDown: (e) => {
|
|
1243
|
+
if (e.key === "Enter") {
|
|
1244
|
+
e.preventDefault();
|
|
1245
|
+
void submit();
|
|
1246
|
+
}
|
|
1247
|
+
if (e.key === "Escape") {
|
|
1248
|
+
e.preventDefault();
|
|
1249
|
+
close();
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
className: "mt-1 h-12 w-28 rounded-md border bg-background px-3 text-2xl font-semibold tabular-nums outline-none focus:border-primary"
|
|
1253
|
+
}
|
|
1254
|
+
)
|
|
1255
|
+
] }),
|
|
1256
|
+
/* @__PURE__ */ jsx("p", { "aria-live": "polite", className: "pb-2 text-sm text-muted-foreground", children: nextBalance === null ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1257
|
+
"Fica em ",
|
|
1258
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums text-foreground", children: total.available })
|
|
1259
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1260
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.available }),
|
|
1261
|
+
/* @__PURE__ */ jsx("span", { className: "mx-1.5", children: "\u2192" }),
|
|
1262
|
+
/* @__PURE__ */ jsx("span", { className: `text-base font-semibold tabular-nums ${nextBalance < 0 ? "text-destructive" : "text-foreground"}`, children: nextBalance }),
|
|
1263
|
+
nextBalance < 0 && /* @__PURE__ */ jsx("span", { className: "ml-2 text-destructive", children: "n\xE3o h\xE1 tudo isso" })
|
|
1264
|
+
] }) }),
|
|
1265
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
1266
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
1267
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Motivo" }),
|
|
1268
|
+
/* @__PURE__ */ jsxs("select", { value: reasonCode, onChange: (e) => setReasonCode(e.target.value), className: field, children: [
|
|
1269
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Padr\xE3o" }),
|
|
1270
|
+
reasons.map((r) => /* @__PURE__ */ jsx("option", { value: r.key, children: r.label }, r.key))
|
|
1271
|
+
] })
|
|
1272
|
+
] }),
|
|
1273
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
1274
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: type === "entrada" ? "Chegou em" : "Quando" }),
|
|
1275
|
+
/* @__PURE__ */ jsx(
|
|
1276
|
+
"input",
|
|
1277
|
+
{
|
|
1278
|
+
type: "date",
|
|
1279
|
+
value: occurredOn,
|
|
1280
|
+
max: todayISO(),
|
|
1281
|
+
onChange: (e) => setOccurredOn(e.target.value || todayISO()),
|
|
1282
|
+
className: field
|
|
1283
|
+
}
|
|
1284
|
+
)
|
|
1285
|
+
] })
|
|
1286
|
+
] }),
|
|
1287
|
+
noteOpen ? /* @__PURE__ */ jsxs("label", { className: "mt-3 block", children: [
|
|
1288
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Observa\xE7\xE3o" }),
|
|
1289
|
+
/* @__PURE__ */ jsx(
|
|
1290
|
+
"input",
|
|
1291
|
+
{
|
|
1292
|
+
autoFocus: true,
|
|
1293
|
+
value: note,
|
|
1294
|
+
onChange: (e) => setNote(e.target.value),
|
|
1295
|
+
className: field,
|
|
1296
|
+
placeholder: "Nota fiscal, quem entregou\u2026"
|
|
1297
|
+
}
|
|
1298
|
+
)
|
|
1299
|
+
] }) : /* @__PURE__ */ jsx(
|
|
1300
|
+
"button",
|
|
1301
|
+
{
|
|
1302
|
+
type: "button",
|
|
1303
|
+
onClick: () => setNoteOpen(true),
|
|
1304
|
+
className: "mt-2 text-xs font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
|
|
1305
|
+
children: "+ observa\xE7\xE3o"
|
|
1306
|
+
}
|
|
1307
|
+
),
|
|
1308
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2", children: [
|
|
1309
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void submit(), disabled: saving || amount === 0, children: saving ? "Lan\xE7ando\u2026" : "Lan\xE7ar" }),
|
|
1310
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: close, disabled: saving, children: "Cancelar" }),
|
|
1311
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
1312
|
+
amount === 0 && // O motivo de o botão estar apagado, ao lado dele.
|
|
1313
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Diga quantas unidades" })
|
|
1314
|
+
] })
|
|
1315
|
+
] }),
|
|
1316
|
+
variantId && (rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: "Nenhuma posi\xE7\xE3o ainda \u2014 a primeira entrada abre o saldo." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: rows.map((r) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-card border bg-card p-3", children: [
|
|
1317
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm font-medium", children: r.location }),
|
|
1318
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1319
|
+
r.reserved,
|
|
1320
|
+
" reservado"
|
|
1321
|
+
] }),
|
|
1322
|
+
/* @__PURE__ */ jsx("span", { className: "w-16 text-right text-sm font-semibold tabular-nums", children: r.available })
|
|
1323
|
+
] }, r.key)) })),
|
|
1324
|
+
movements.length > 0 && /* @__PURE__ */ jsxs("section", { className: "space-y-2 pt-2", children: [
|
|
1325
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "\xDAltimos movimentos" }),
|
|
1326
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-2", children: movements.map((m) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-card border bg-card px-3 py-2", children: [
|
|
1327
|
+
/* @__PURE__ */ jsx("span", { className: `w-14 text-sm font-semibold tabular-nums ${m.quantity < 0 ? "text-destructive" : "text-emerald-700"}`, children: m.quantity > 0 ? `+${m.quantity}` : m.quantity }),
|
|
1328
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-sm", children: m.reasonCode ?? m.kind }),
|
|
1329
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: new Date(m.occurredAt ?? m.createdAt).toLocaleDateString("pt-BR") })
|
|
1330
|
+
] }, m.id)) })
|
|
1331
|
+
] })
|
|
1332
|
+
] });
|
|
1333
|
+
}
|
|
725
1334
|
|
|
726
1335
|
// src/data/registry-routes.ts
|
|
727
1336
|
var SHIPPING_SETTINGS_TAB = "shop-shipping";
|
|
@@ -743,20 +1352,20 @@ function shippingRegistryIdFromHash(hash) {
|
|
|
743
1352
|
}
|
|
744
1353
|
var PACKAGES_ROUTE = shippingRegistryPath(PACKAGES_REGISTRY);
|
|
745
1354
|
function PackageSelect({ value, onChange, getProvider }) {
|
|
746
|
-
const shop =
|
|
747
|
-
const [rows, setRows] =
|
|
748
|
-
const [open, setOpen] =
|
|
749
|
-
const [creating, setCreating] =
|
|
750
|
-
const ref =
|
|
751
|
-
const load =
|
|
1355
|
+
const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
|
|
1356
|
+
const [rows, setRows] = React9.useState(null);
|
|
1357
|
+
const [open, setOpen] = React9.useState(false);
|
|
1358
|
+
const [creating, setCreating] = React9.useState(false);
|
|
1359
|
+
const ref = React9.useRef(null);
|
|
1360
|
+
const load = React9.useCallback(() => {
|
|
752
1361
|
if (typeof shop.listShippingPackages !== "function") {
|
|
753
1362
|
setRows([]);
|
|
754
1363
|
return;
|
|
755
1364
|
}
|
|
756
1365
|
void shop.listShippingPackages().then(setRows).catch(() => setRows([]));
|
|
757
1366
|
}, [shop]);
|
|
758
|
-
|
|
759
|
-
|
|
1367
|
+
React9.useEffect(load, [load]);
|
|
1368
|
+
React9.useEffect(() => {
|
|
760
1369
|
if (!open) return;
|
|
761
1370
|
const away = (event) => {
|
|
762
1371
|
if (ref.current && !ref.current.contains(event.target)) setOpen(false);
|
|
@@ -851,8 +1460,8 @@ function PackageSelect({ value, onChange, getProvider }) {
|
|
|
851
1460
|
] });
|
|
852
1461
|
}
|
|
853
1462
|
function NewPackageModal({ onClose, onCreated }) {
|
|
854
|
-
const [error, setError] =
|
|
855
|
-
const provider =
|
|
1463
|
+
const [error, setError] = React9.useState(null);
|
|
1464
|
+
const provider = React9.useMemo(
|
|
856
1465
|
() => resolveDataProvider(packagesRegistry.entity),
|
|
857
1466
|
[]
|
|
858
1467
|
);
|
|
@@ -898,9 +1507,9 @@ function Option({ label, hint, active, onClick }) {
|
|
|
898
1507
|
);
|
|
899
1508
|
}
|
|
900
1509
|
function ShippingProfileSelect({ value, onChange, getProvider }) {
|
|
901
|
-
const shop =
|
|
902
|
-
const [rows, setRows] =
|
|
903
|
-
|
|
1510
|
+
const shop = React9.useMemo(() => getProvider?.() ?? getShopProvider(), [getProvider]);
|
|
1511
|
+
const [rows, setRows] = React9.useState(null);
|
|
1512
|
+
React9.useEffect(() => {
|
|
904
1513
|
let cancelled = false;
|
|
905
1514
|
if (typeof shop.listShippingProfiles !== "function") {
|
|
906
1515
|
setRows([]);
|
|
@@ -1077,14 +1686,27 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1077
1686
|
searchable: true,
|
|
1078
1687
|
showInTable: false
|
|
1079
1688
|
},
|
|
1689
|
+
// Não se DIGITA saldo.
|
|
1690
|
+
//
|
|
1691
|
+
// Este campo era um número editável, e um número editável convida a
|
|
1692
|
+
// resposta errada: alguém conta a prateleira, acha 18, escreve 18, e o
|
|
1693
|
+
// razão perde POR QUE mudou — se chegaram seis ou se sumiram quatro. É
|
|
1694
|
+
// exatamente o que um razão existe para impedir, e a 0036 já tratava a
|
|
1695
|
+
// escrita direta aqui como um ajuste às cegas para não perder o movimento.
|
|
1696
|
+
//
|
|
1697
|
+
// Vira reflexo: mostra o saldo, e o ato acontece em Posições, onde ele tem
|
|
1698
|
+
// espécie, motivo e data.
|
|
1080
1699
|
{
|
|
1081
1700
|
key: "inventoryCount",
|
|
1082
1701
|
label: "Estoque",
|
|
1083
1702
|
type: "number",
|
|
1084
1703
|
group: "inventory",
|
|
1085
1704
|
min: 0,
|
|
1086
|
-
|
|
1087
|
-
|
|
1705
|
+
// `showInForm: false` e não um campo desabilitado: some do formulário de
|
|
1706
|
+
// edição, continua na tabela e na ficha. Mostra-se o saldo; digita-se o
|
|
1707
|
+
// movimento.
|
|
1708
|
+
showInForm: false,
|
|
1709
|
+
hint: "Saldo do raz\xE3o. Entrada, sa\xEDda e perda se lan\xE7am na aba Posi\xE7\xF5es.",
|
|
1088
1710
|
showInTable: true,
|
|
1089
1711
|
sortable: true,
|
|
1090
1712
|
renderCell: (v) => {
|
|
@@ -1182,7 +1804,7 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1182
1804
|
group: "shipping",
|
|
1183
1805
|
min: 0,
|
|
1184
1806
|
placeholder: "0,4",
|
|
1185
|
-
hint: "Do produto embalado.
|
|
1807
|
+
hint: "Do produto, j\xE1 embalado. As medidas v\xEAm da embalagem escolhida acima.",
|
|
1186
1808
|
showInTable: false,
|
|
1187
1809
|
visibleWhen: (values) => values.isPhysical !== false
|
|
1188
1810
|
},
|
|
@@ -1199,9 +1821,19 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1199
1821
|
// Em centímetros, sem seletor de unidade: a coluna é `*_cm` e a caixa da
|
|
1200
1822
|
// loja também. Duas unidades para a mesma medida é como o peso passou
|
|
1201
1823
|
// quatro anos sendo lido errado.
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1824
|
+
// A medida é da EMBALAGEM, não do produto.
|
|
1825
|
+
//
|
|
1826
|
+
// O seletor logo acima já associa o produto a uma embalagem cadastrada da
|
|
1827
|
+
// loja (0047) — e é lá que altura, largura e comprimento vivem. Repetir os
|
|
1828
|
+
// três aqui pedia a mesma informação duas vezes por caminhos diferentes, e
|
|
1829
|
+
// as duas divergiam no dia em que alguém trocasse a caixa: a cotação lê a
|
|
1830
|
+
// embalagem, o formulário mostrava outro número.
|
|
1831
|
+
//
|
|
1832
|
+
// As colunas continuam existindo — um catálogo anterior à 0047 tem produto
|
|
1833
|
+
// medido e sem embalagem, e a cotação ainda os lê. O que sai é a pergunta.
|
|
1834
|
+
{ key: "heightCm", label: "Altura (cm)", type: "number", group: "shipping", min: 0, showInForm: false, showInTable: false },
|
|
1835
|
+
{ key: "widthCm", label: "Largura (cm)", type: "number", group: "shipping", min: 0, showInForm: false, showInTable: false },
|
|
1836
|
+
{ key: "lengthCm", label: "Comprimento (cm)", type: "number", group: "shipping", min: 0, showInForm: false, showInTable: false },
|
|
1205
1837
|
{
|
|
1206
1838
|
// Declarado aqui para poder viajar — para a transportadora, para o motoboy
|
|
1207
1839
|
// ou para quem embala. O que a loja FAZ com um carrinho refrigerado é uma
|
|
@@ -1228,6 +1860,17 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1228
1860
|
namePlural: "Produtos",
|
|
1229
1861
|
icon: "Package",
|
|
1230
1862
|
displayField: "name",
|
|
1863
|
+
// Qual tabela esta tela É. Sem isto, `deriveEntityKey` cai no rótulo — e a
|
|
1864
|
+
// chave da entidade vira `produto`, que muda com uma tradução. Quem avisa
|
|
1865
|
+
// "escrevi aqui" nomeia a TABELA (é o que emitDataChanged invalida no cache),
|
|
1866
|
+
// então sem esta linha o aviso não encontra a lista: o barramento casa
|
|
1867
|
+
// tabela-com-tabela, e o lado do assinante estava vazio.
|
|
1868
|
+
//
|
|
1869
|
+
// Não muda o caminho do dado: o índice do plugin passa um
|
|
1870
|
+
// `dataProvider: createShopProductDataProvider(...)` explícito, que vence o
|
|
1871
|
+
// provider genérico que `data.table` acionaria — mesma forma da entidade de
|
|
1872
|
+
// cliente logo ao lado.
|
|
1873
|
+
data: { table: "plg_shop_products" },
|
|
1231
1874
|
fields,
|
|
1232
1875
|
fieldGroups: [
|
|
1233
1876
|
// imageSlot renders the scaffolding's image uploader alongside the group.
|
|
@@ -1238,7 +1881,7 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1238
1881
|
{
|
|
1239
1882
|
id: "shipping",
|
|
1240
1883
|
label: "Envio",
|
|
1241
|
-
description: "
|
|
1884
|
+
description: "Em que caixa este produto sai e quanto ele pesa. A medida \xE9 da embalagem; o peso \xE9 do produto. Sem embalagem escolhida a transportadora \xE9 consultada com a caixa padr\xE3o da loja, e o frete cotado pode n\xE3o ser o frete pago.",
|
|
1242
1885
|
columns: 3
|
|
1243
1886
|
}
|
|
1244
1887
|
],
|
|
@@ -1262,139 +1905,21 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1262
1905
|
icon: "Layers",
|
|
1263
1906
|
component: ProductVariantsTab,
|
|
1264
1907
|
props: { getProvider }
|
|
1908
|
+
},
|
|
1909
|
+
// Separada de Variantes de propósito: aquela edita O QUE se vende,
|
|
1910
|
+
// esta responde ONDE estão as unidades — e a ficha só sabia dizer um
|
|
1911
|
+
// número só, que não distingue a loja do depósito nem o que já está
|
|
1912
|
+
// prometido a um pedido.
|
|
1913
|
+
{
|
|
1914
|
+
id: "positions",
|
|
1915
|
+
label: "Posi\xE7\xF5es",
|
|
1916
|
+
icon: "Boxes",
|
|
1917
|
+
component: ProductPositionsTab,
|
|
1918
|
+
props: { getProvider }
|
|
1265
1919
|
}
|
|
1266
1920
|
] : void 0
|
|
1267
1921
|
};
|
|
1268
1922
|
}
|
|
1269
|
-
function money2(value) {
|
|
1270
|
-
const n = Number(value);
|
|
1271
|
-
if (!Number.isFinite(n)) return "R$ 0,00";
|
|
1272
|
-
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(n);
|
|
1273
|
-
}
|
|
1274
|
-
function buildShopCustomerEntity() {
|
|
1275
|
-
const fields = [
|
|
1276
|
-
// — Contato —
|
|
1277
|
-
{
|
|
1278
|
-
key: "firstName",
|
|
1279
|
-
label: "Nome",
|
|
1280
|
-
type: "text",
|
|
1281
|
-
required: true,
|
|
1282
|
-
group: "contact",
|
|
1283
|
-
placeholder: "Maria",
|
|
1284
|
-
searchable: true,
|
|
1285
|
-
showInTable: true,
|
|
1286
|
-
sortable: true,
|
|
1287
|
-
renderCell: (_v, row) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1288
|
-
/* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: [row.firstName, row.lastName].filter(Boolean).join(" ") }),
|
|
1289
|
-
row.email && /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: row.email })
|
|
1290
|
-
] })
|
|
1291
|
-
},
|
|
1292
|
-
{ key: "lastName", label: "Sobrenome", type: "text", group: "contact", placeholder: "Silva", searchable: true, showInTable: false },
|
|
1293
|
-
{
|
|
1294
|
-
key: "email",
|
|
1295
|
-
label: "E-mail",
|
|
1296
|
-
type: "text",
|
|
1297
|
-
group: "contact",
|
|
1298
|
-
placeholder: "maria@exemplo.com",
|
|
1299
|
-
searchable: true,
|
|
1300
|
-
hint: "\xC9 por ele que o pedido \xE9 vinculado ao cliente no checkout.",
|
|
1301
|
-
showInTable: false
|
|
1302
|
-
},
|
|
1303
|
-
{
|
|
1304
|
-
key: "phone",
|
|
1305
|
-
label: "Telefone",
|
|
1306
|
-
type: "text",
|
|
1307
|
-
group: "contact",
|
|
1308
|
-
placeholder: "(21) 99999-0000",
|
|
1309
|
-
searchable: true,
|
|
1310
|
-
showInTable: true,
|
|
1311
|
-
sortable: false,
|
|
1312
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: v || "\u2014" })
|
|
1313
|
-
},
|
|
1314
|
-
// The column, the type and the provider all carried `document` already; the
|
|
1315
|
-
// form was the only place it could not be typed. Which meant a store could
|
|
1316
|
-
// see "cliente sem CPF/CNPJ" on every Bling run and have nowhere to fix it.
|
|
1317
|
-
{
|
|
1318
|
-
key: "document",
|
|
1319
|
-
label: "CPF / CNPJ",
|
|
1320
|
-
type: "text",
|
|
1321
|
-
group: "contact",
|
|
1322
|
-
placeholder: "000.000.000-00",
|
|
1323
|
-
searchable: true,
|
|
1324
|
-
hint: "Necess\xE1rio para emitir nota \u2014 sem ele o cliente n\xE3o sobe para o ERP.",
|
|
1325
|
-
showInTable: false
|
|
1326
|
-
},
|
|
1327
|
-
// — Histórico (somente leitura) —
|
|
1328
|
-
{
|
|
1329
|
-
key: "ordersCount",
|
|
1330
|
-
label: "Pedidos",
|
|
1331
|
-
type: "number",
|
|
1332
|
-
showInForm: false,
|
|
1333
|
-
showInTable: true,
|
|
1334
|
-
sortable: true,
|
|
1335
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right", children: Number(v) || 0 })
|
|
1336
|
-
},
|
|
1337
|
-
{
|
|
1338
|
-
key: "totalSpent",
|
|
1339
|
-
label: "Total gasto",
|
|
1340
|
-
type: "number",
|
|
1341
|
-
showInForm: false,
|
|
1342
|
-
showInTable: true,
|
|
1343
|
-
sortable: true,
|
|
1344
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money2(v) })
|
|
1345
|
-
},
|
|
1346
|
-
// — Interno —
|
|
1347
|
-
{
|
|
1348
|
-
key: "notes",
|
|
1349
|
-
label: "Observa\xE7\xF5es",
|
|
1350
|
-
type: "textarea",
|
|
1351
|
-
group: "internal",
|
|
1352
|
-
span: 2,
|
|
1353
|
-
placeholder: "Prefer\xEAncias, restri\xE7\xF5es, combinados de entrega\u2026",
|
|
1354
|
-
hint: "Vis\xEDvel s\xF3 no admin. O cliente nunca v\xEA.",
|
|
1355
|
-
showInTable: false
|
|
1356
|
-
}
|
|
1357
|
-
];
|
|
1358
|
-
return {
|
|
1359
|
-
name: "Cliente",
|
|
1360
|
-
namePlural: "Clientes",
|
|
1361
|
-
icon: "Users",
|
|
1362
|
-
displayField: "firstName",
|
|
1363
|
-
// `person` layout brings the shared archetype tabs — including Endereços,
|
|
1364
|
-
// which reads the core addresses table and therefore works in every app.
|
|
1365
|
-
layout: "person",
|
|
1366
|
-
// Without archetypeKind the shell shows every person tab, including the
|
|
1367
|
-
// staff-only Agenda (its visibleFor filter only runs when a kind is set).
|
|
1368
|
-
data: { table: "plg_shop_customers", archetypeKind: "customer" },
|
|
1369
|
-
fields,
|
|
1370
|
-
// The SAME tab and the SAME provider beauty-saas uses. Since the shop's
|
|
1371
|
-
// orders now live in public.orders with party_id set, the canonical provider
|
|
1372
|
-
// already returns them — a shop-specific adapter would be a second
|
|
1373
|
-
// implementation of "list this person's orders", which is how two apps end
|
|
1374
|
-
// up with two different answers to the same question.
|
|
1375
|
-
detailTabs: [{
|
|
1376
|
-
id: "orders",
|
|
1377
|
-
label: "Pedidos",
|
|
1378
|
-
icon: "ShoppingBag",
|
|
1379
|
-
aliases: ["appointments", "quotes"],
|
|
1380
|
-
component: ClientOrdersTab,
|
|
1381
|
-
props: {
|
|
1382
|
-
provider: createClientOrdersProvider(),
|
|
1383
|
-
currency: { code: "BRL", locale: "pt-BR" },
|
|
1384
|
-
// The one part that legitimately differs per app: where a document
|
|
1385
|
-
// leads. Beauty sends a booking to the agenda and an invoice to
|
|
1386
|
-
// receivables; a shop order goes to the order screen.
|
|
1387
|
-
navigator: {
|
|
1388
|
-
onNavigate: (doc) => adminNavigateTo(`/shop/orders/${doc.id}`)
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
}],
|
|
1392
|
-
fieldGroups: [
|
|
1393
|
-
{ id: "contact", label: "Contato", columns: 2 },
|
|
1394
|
-
{ id: "internal", label: "Notas internas", columns: 1 }
|
|
1395
|
-
]
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
1923
|
var STATUS_TONE2 = {
|
|
1399
1924
|
active: "bg-emerald-100 text-emerald-800",
|
|
1400
1925
|
draft: "bg-gray-100 text-gray-700",
|
|
@@ -1556,6 +2081,34 @@ function buildShopDiscountEntity() {
|
|
|
1556
2081
|
facets: [{ field: "status", allLabel: "Todos" }]
|
|
1557
2082
|
};
|
|
1558
2083
|
}
|
|
2084
|
+
|
|
2085
|
+
// src/views/discount-agent.ts
|
|
2086
|
+
var DISCOUNT_CREATE_INSTRUCTIONS = `
|
|
2087
|
+
Esta lista \xE9 a de Descontos da loja. Um registro aqui \xE9 um CUPOM que o comprador digita no carrinho.
|
|
2088
|
+
|
|
2089
|
+
O c\xF3digo \xE9 seu para escrever:
|
|
2090
|
+
\xB7 Se o lojista n\xE3o ditar um, invente a partir da campanha: MAI\xDASCULAS, s\xF3 letras A-Z e n\xFAmeros, de 4 a 16 caracteres, sem espa\xE7o, acento, h\xEDfen ou pontua\xE7\xE3o.
|
|
2091
|
+
\xB7 Curto e dit\xE1vel ao telefone. Evite 0/O e 1/I juntos, evite palavra que o comprador precise soletrar.
|
|
2092
|
+
\xB7 Nunca repita um c\xF3digo que j\xE1 aparece em \`taken.code\`. Se a cria\xE7\xE3o voltar \`duplicate_value\`, escolha outro e chame de novo \u2014 sem perguntar nada ao lojista.
|
|
2093
|
+
\xB7 \`title\` \xE9 o nome interno, s\xF3 o lojista v\xEA: escreva a campanha em portugu\xEAs ("Frete gr\xE1tis fim de semana"), n\xE3o repita o c\xF3digo.
|
|
2094
|
+
|
|
2095
|
+
A regra:
|
|
2096
|
+
\xB7 "frete gr\xE1tis" \u2192 \`type: free_shipping\` e SEM \`value\` \u2014 frete gr\xE1tis n\xE3o desconta o subtotal.
|
|
2097
|
+
\xB7 "10%", "10 por cento" \u2192 \`type: percentage\`, \`value: 10\`.
|
|
2098
|
+
\xB7 "R$ 50", "50 reais de desconto" \u2192 \`type: fixed_amount\`, \`value: 50\`.
|
|
2099
|
+
\xB7 "acima de R$ 200", "a partir de 200" \u2192 \`minSubtotal: 200\`.
|
|
2100
|
+
\xB7 "s\xF3 a primeira compra", "uma vez por cliente" \u2192 \`oncePerCustomer: true\`.
|
|
2101
|
+
\xB7 "os 100 primeiros" \u2192 \`usageLimit: 100\`.
|
|
2102
|
+
\xB7 Prazo vira data em \`endsAt\`: "at\xE9 o fim dessa semana" \xE9 o domingo desta semana, "at\xE9 o fim do m\xEAs" \xE9 o \xFAltimo dia do m\xEAs. Sem prazo dito, deixe vazio \u2014 cupom sem fim \xE9 uma escolha, n\xE3o um esquecimento.
|
|
2103
|
+
|
|
2104
|
+
Status:
|
|
2105
|
+
\xB7 \`active\` por padr\xE3o. O lojista est\xE1 pedindo uma promo\xE7\xE3o para divulgar hoje; um cupom que nasce dormindo \xE9 um cupom que o cliente tenta usar e n\xE3o funciona.
|
|
2106
|
+
\xB7 \`draft\` s\xF3 quando ele disser que \xE9 para deixar pronto para depois.
|
|
2107
|
+
|
|
2108
|
+
O que voc\xEA N\xC3O inventa: o tamanho do desconto. Se ele disser "desconto para clientes VIP" sem dizer quanto, essa \xE9 a \xFAnica pergunta a fazer \u2014 e fa\xE7a s\xF3 ela.
|
|
2109
|
+
|
|
2110
|
+
Depois de criar, diga em uma linha o c\xF3digo, o efeito e at\xE9 quando vale, para ele j\xE1 poder divulgar.
|
|
2111
|
+
`.trim();
|
|
1559
2112
|
function buildShopCategoryEntity() {
|
|
1560
2113
|
const fields = [
|
|
1561
2114
|
{
|
|
@@ -1744,7 +2297,7 @@ var STATUS_LABEL2 = {
|
|
|
1744
2297
|
disabled: "Desativado"
|
|
1745
2298
|
};
|
|
1746
2299
|
function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
1747
|
-
const
|
|
2300
|
+
const money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(Number(v) || 0);
|
|
1748
2301
|
const fields = [
|
|
1749
2302
|
{
|
|
1750
2303
|
key: "code",
|
|
@@ -1772,7 +2325,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
|
1772
2325
|
hint: "Valor de face do cart\xE3o. N\xE3o muda depois de emitido.",
|
|
1773
2326
|
showInTable: true,
|
|
1774
2327
|
sortable: true,
|
|
1775
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children:
|
|
2328
|
+
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right text-muted-foreground", children: money2(v) })
|
|
1776
2329
|
},
|
|
1777
2330
|
{
|
|
1778
2331
|
key: "balance",
|
|
@@ -1786,7 +2339,7 @@ function buildShopGiftCardEntity(currencyCode = "BRL") {
|
|
|
1786
2339
|
hint: "O que ainda pode ser gasto. Cai a cada resgate.",
|
|
1787
2340
|
showInTable: true,
|
|
1788
2341
|
sortable: true,
|
|
1789
|
-
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children:
|
|
2342
|
+
renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money2(v) })
|
|
1790
2343
|
},
|
|
1791
2344
|
{
|
|
1792
2345
|
key: "status",
|
|
@@ -1921,20 +2474,20 @@ function buildShopCustomerGroupEntity() {
|
|
|
1921
2474
|
fieldGroups: [{ id: "basic", label: "Grupo", columns: 2 }]
|
|
1922
2475
|
};
|
|
1923
2476
|
}
|
|
1924
|
-
function describeSegment(s,
|
|
2477
|
+
function describeSegment(s, money2) {
|
|
1925
2478
|
const parts = [];
|
|
1926
2479
|
if (s.minOrders != null && s.maxOrders != null) parts.push(`${s.minOrders}\u2013${s.maxOrders} pedidos`);
|
|
1927
2480
|
else if (s.minOrders != null) parts.push(`${s.minOrders}+ pedidos`);
|
|
1928
2481
|
else if (s.maxOrders != null) parts.push(`at\xE9 ${s.maxOrders} pedidos`);
|
|
1929
|
-
if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${
|
|
1930
|
-
else if (s.minSpent != null) parts.push(`gastou ${
|
|
1931
|
-
else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${
|
|
2482
|
+
if (s.minSpent != null && s.maxSpent != null) parts.push(`gastou ${money2(s.minSpent)}\u2013${money2(s.maxSpent)}`);
|
|
2483
|
+
else if (s.minSpent != null) parts.push(`gastou ${money2(s.minSpent)}+`);
|
|
2484
|
+
else if (s.maxSpent != null) parts.push(`gastou at\xE9 ${money2(s.maxSpent)}`);
|
|
1932
2485
|
if (s.joinedAfter) parts.push(`entrou ap\xF3s ${new Date(s.joinedAfter).toLocaleDateString("pt-BR")}`);
|
|
1933
2486
|
if (s.joinedBefore) parts.push(`entrou antes de ${new Date(s.joinedBefore).toLocaleDateString("pt-BR")}`);
|
|
1934
2487
|
return parts.length ? parts.join(" \xB7 ") : "Todos os clientes";
|
|
1935
2488
|
}
|
|
1936
2489
|
function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
|
|
1937
|
-
const
|
|
2490
|
+
const money2 = (v) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currencyCode }).format(v);
|
|
1938
2491
|
const fields = [
|
|
1939
2492
|
{
|
|
1940
2493
|
key: "name",
|
|
@@ -2011,7 +2564,7 @@ function buildShopCustomerSegmentEntity(currencyCode = "BRL") {
|
|
|
2011
2564
|
showInForm: false,
|
|
2012
2565
|
showInTable: true,
|
|
2013
2566
|
sortable: false,
|
|
2014
|
-
renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row,
|
|
2567
|
+
renderCell: (_v, row) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: describeSegment(row, money2) })
|
|
2015
2568
|
},
|
|
2016
2569
|
{
|
|
2017
2570
|
key: "matchCount",
|
|
@@ -2341,17 +2894,17 @@ function createShopCustomerSegmentDataProvider(provider) {
|
|
|
2341
2894
|
}
|
|
2342
2895
|
var DEBOUNCE_MS = 400;
|
|
2343
2896
|
function useStoreSettings(provider, defaults = {}) {
|
|
2344
|
-
const shop =
|
|
2897
|
+
const shop = React9.useMemo(
|
|
2345
2898
|
() => provider ? resolve(provider) : getShopProvider(),
|
|
2346
2899
|
[provider]
|
|
2347
2900
|
);
|
|
2348
2901
|
const supported = typeof shop.getStoreSettings === "function" && typeof shop.updateStoreSettings === "function";
|
|
2349
|
-
const [stored, setStored] =
|
|
2350
|
-
const [loading, setLoading] =
|
|
2351
|
-
const [saving, setSaving] =
|
|
2352
|
-
const [error, setError] =
|
|
2353
|
-
const [loadError, setLoadError] =
|
|
2354
|
-
|
|
2902
|
+
const [stored, setStored] = React9.useState(null);
|
|
2903
|
+
const [loading, setLoading] = React9.useState(supported);
|
|
2904
|
+
const [saving, setSaving] = React9.useState(false);
|
|
2905
|
+
const [error, setError] = React9.useState(null);
|
|
2906
|
+
const [loadError, setLoadError] = React9.useState(null);
|
|
2907
|
+
React9.useEffect(() => {
|
|
2355
2908
|
if (!supported) return;
|
|
2356
2909
|
let cancelled = false;
|
|
2357
2910
|
setLoading(true);
|
|
@@ -2367,12 +2920,12 @@ function useStoreSettings(provider, defaults = {}) {
|
|
|
2367
2920
|
cancelled = true;
|
|
2368
2921
|
};
|
|
2369
2922
|
}, [shop, supported]);
|
|
2370
|
-
const pendingRef =
|
|
2371
|
-
const timerRef =
|
|
2372
|
-
|
|
2923
|
+
const pendingRef = React9.useRef({});
|
|
2924
|
+
const timerRef = React9.useRef(null);
|
|
2925
|
+
React9.useEffect(() => () => {
|
|
2373
2926
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2374
2927
|
}, []);
|
|
2375
|
-
const set =
|
|
2928
|
+
const set = React9.useCallback(
|
|
2376
2929
|
(patch) => {
|
|
2377
2930
|
setStored((current) => ({ ...current ?? {}, ...patch }));
|
|
2378
2931
|
if (!supported) return;
|
|
@@ -2388,7 +2941,7 @@ function useStoreSettings(provider, defaults = {}) {
|
|
|
2388
2941
|
},
|
|
2389
2942
|
[shop, supported]
|
|
2390
2943
|
);
|
|
2391
|
-
const value =
|
|
2944
|
+
const value = React9.useMemo(() => ({ ...defaults, ...stored ?? {} }), [defaults, stored]);
|
|
2392
2945
|
return { value, stored, set, loading, saving, error, loadError, supported };
|
|
2393
2946
|
}
|
|
2394
2947
|
function SettingsState({
|
|
@@ -2617,25 +3170,25 @@ function PaymentsSettings({ provider }) {
|
|
|
2617
3170
|
const settings = useStoreSettings(provider);
|
|
2618
3171
|
const { value, set } = settings;
|
|
2619
3172
|
const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
|
|
2620
|
-
const [adding, setAdding] =
|
|
2621
|
-
const [selected, setSelected] =
|
|
3173
|
+
const [adding, setAdding] = React9.useState(false);
|
|
3174
|
+
const [selected, setSelected] = React9.useState(null);
|
|
2622
3175
|
const panelAvailable = useConnectorsPanelAvailable();
|
|
2623
|
-
const openJourney =
|
|
3176
|
+
const openJourney = React9.useCallback(
|
|
2624
3177
|
(connector) => {
|
|
2625
3178
|
if (panelAvailable) openConnectorsPanel(connector);
|
|
2626
3179
|
else setSelected(connector);
|
|
2627
3180
|
},
|
|
2628
3181
|
[panelAvailable]
|
|
2629
3182
|
);
|
|
2630
|
-
const payment =
|
|
3183
|
+
const payment = React9.useMemo(
|
|
2631
3184
|
() => (connectors ?? []).filter((c) => c.category && PAYMENT_CATEGORIES.includes(c.category)),
|
|
2632
3185
|
[connectors]
|
|
2633
3186
|
);
|
|
2634
|
-
const live =
|
|
3187
|
+
const live = React9.useMemo(() => payment.filter((c) => c.state === "connected"), [payment]);
|
|
2635
3188
|
const chosen = value.paymentMethods ?? [];
|
|
2636
3189
|
const staleProvider = !!value.paymentProvider && !live.some((c) => c.slug === value.paymentProvider) && live.length === 0;
|
|
2637
|
-
const movedFrom =
|
|
2638
|
-
|
|
3190
|
+
const movedFrom = React9.useRef(null);
|
|
3191
|
+
React9.useEffect(() => {
|
|
2639
3192
|
if (!settings.supported || settings.loading) return;
|
|
2640
3193
|
const next = resolvePaymentProvider(value.paymentProvider, live.map((c) => c.slug));
|
|
2641
3194
|
if (!next) return;
|
|
@@ -2841,17 +3394,17 @@ function CheckoutSettings({ provider }) {
|
|
|
2841
3394
|
{
|
|
2842
3395
|
title: "Campos do cliente",
|
|
2843
3396
|
description: "O que o comprador v\xEA e o que ele precisa preencher. Campos travados s\xE3o os que a entrega n\xE3o dispensa.",
|
|
2844
|
-
children: FIELDS.map((
|
|
3397
|
+
children: FIELDS.map((field2) => /* @__PURE__ */ jsx(
|
|
2845
3398
|
SelectRow,
|
|
2846
3399
|
{
|
|
2847
|
-
label:
|
|
2848
|
-
description:
|
|
2849
|
-
value: toRequirement(fields[
|
|
2850
|
-
options:
|
|
2851
|
-
onChange: (choice) => setField(
|
|
2852
|
-
disabled:
|
|
3400
|
+
label: field2.label,
|
|
3401
|
+
description: field2.hint,
|
|
3402
|
+
value: toRequirement(fields[field2.key], !!field2.locked),
|
|
3403
|
+
options: field2.locked ? [{ value: "required", label: "Pedir (obrigat\xF3rio)" }] : REQUIREMENT,
|
|
3404
|
+
onChange: (choice) => setField(field2.key, choice),
|
|
3405
|
+
disabled: field2.locked
|
|
2853
3406
|
},
|
|
2854
|
-
|
|
3407
|
+
field2.key
|
|
2855
3408
|
))
|
|
2856
3409
|
}
|
|
2857
3410
|
)
|
|
@@ -2867,11 +3420,11 @@ function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }
|
|
|
2867
3420
|
] });
|
|
2868
3421
|
}
|
|
2869
3422
|
function HandlingSettings({ settings, provider }) {
|
|
2870
|
-
const shop =
|
|
3423
|
+
const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
|
|
2871
3424
|
const { value, set, loading, supported } = settings;
|
|
2872
|
-
const [noteDraft, setNoteDraft] =
|
|
2873
|
-
const [profiles, setProfiles] =
|
|
2874
|
-
|
|
3425
|
+
const [noteDraft, setNoteDraft] = React9.useState(null);
|
|
3426
|
+
const [profiles, setProfiles] = React9.useState(null);
|
|
3427
|
+
React9.useEffect(() => {
|
|
2875
3428
|
let cancelled = false;
|
|
2876
3429
|
void shop.listShippingProfiles?.().then((list) => {
|
|
2877
3430
|
if (!cancelled) setProfiles(list);
|
|
@@ -3001,46 +3554,46 @@ var FREIGHT_SOURCE = [
|
|
|
3001
3554
|
}
|
|
3002
3555
|
];
|
|
3003
3556
|
function ShippingSettings({ provider }) {
|
|
3004
|
-
const [registryId, setRegistryId] =
|
|
3005
|
-
|
|
3557
|
+
const [registryId, setRegistryId] = React9.useState(shippingRegistryIdFromHash);
|
|
3558
|
+
React9.useEffect(() => {
|
|
3006
3559
|
const handler = () => setRegistryId(shippingRegistryIdFromHash());
|
|
3007
3560
|
window.addEventListener("hashchange", handler);
|
|
3008
3561
|
return () => window.removeEventListener("hashchange", handler);
|
|
3009
3562
|
}, []);
|
|
3010
|
-
const shop =
|
|
3011
|
-
const registries =
|
|
3563
|
+
const shop = React9.useMemo(() => provider ? resolve(provider) : getShopProvider(), [provider]);
|
|
3564
|
+
const registries = React9.useMemo(
|
|
3012
3565
|
() => buildShopRegistries(provider ?? getShopProvider),
|
|
3013
3566
|
[provider]
|
|
3014
3567
|
);
|
|
3015
3568
|
const storeSettings = useStoreSettings(provider);
|
|
3016
3569
|
const supported = typeof shop.getShippingSettings === "function" && typeof shop.updateShippingSettings === "function";
|
|
3017
|
-
const [row, setRow] =
|
|
3018
|
-
const [zones, setZones] =
|
|
3019
|
-
const [locations, setLocations] =
|
|
3020
|
-
const [loading, setLoading] =
|
|
3021
|
-
const [saving, setSaving] =
|
|
3022
|
-
const [error, setError] =
|
|
3023
|
-
const [loadError, setLoadError] =
|
|
3024
|
-
const [flatDraft, setFlatDraft] =
|
|
3025
|
-
const [freeDraft, setFreeDraft] =
|
|
3026
|
-
const [flatBad, setFlatBad] =
|
|
3027
|
-
const [freeBad, setFreeBad] =
|
|
3570
|
+
const [row, setRow] = React9.useState(null);
|
|
3571
|
+
const [zones, setZones] = React9.useState(null);
|
|
3572
|
+
const [locations, setLocations] = React9.useState(null);
|
|
3573
|
+
const [loading, setLoading] = React9.useState(supported);
|
|
3574
|
+
const [saving, setSaving] = React9.useState(false);
|
|
3575
|
+
const [error, setError] = React9.useState(null);
|
|
3576
|
+
const [loadError, setLoadError] = React9.useState(null);
|
|
3577
|
+
const [flatDraft, setFlatDraft] = React9.useState("");
|
|
3578
|
+
const [freeDraft, setFreeDraft] = React9.useState("");
|
|
3579
|
+
const [flatBad, setFlatBad] = React9.useState(false);
|
|
3580
|
+
const [freeBad, setFreeBad] = React9.useState(false);
|
|
3028
3581
|
const { connectors } = useConnectors({ hostPluginId: "shop", includeGrants: false });
|
|
3029
|
-
const [selected, setSelected] =
|
|
3582
|
+
const [selected, setSelected] = React9.useState(null);
|
|
3030
3583
|
const panelAvailable = useConnectorsPanelAvailable();
|
|
3031
|
-
const openJourney =
|
|
3584
|
+
const openJourney = React9.useCallback(
|
|
3032
3585
|
(connector) => {
|
|
3033
3586
|
if (panelAvailable) openConnectorsPanel(connector);
|
|
3034
3587
|
else setSelected(connector);
|
|
3035
3588
|
},
|
|
3036
3589
|
[panelAvailable]
|
|
3037
3590
|
);
|
|
3038
|
-
const carriers =
|
|
3591
|
+
const carriers = React9.useMemo(
|
|
3039
3592
|
() => (connectors ?? []).filter((c) => c.category === "logistics" || c.category === "shipping"),
|
|
3040
3593
|
[connectors]
|
|
3041
3594
|
);
|
|
3042
3595
|
const carriersConnected = carriers.some((c) => c.state !== "ready");
|
|
3043
|
-
|
|
3596
|
+
React9.useEffect(() => {
|
|
3044
3597
|
let cancelled = false;
|
|
3045
3598
|
if (supported) {
|
|
3046
3599
|
setLoading(true);
|
|
@@ -3069,7 +3622,7 @@ function ShippingSettings({ provider }) {
|
|
|
3069
3622
|
cancelled = true;
|
|
3070
3623
|
};
|
|
3071
3624
|
}, [shop, supported]);
|
|
3072
|
-
const save =
|
|
3625
|
+
const save = React9.useCallback(
|
|
3073
3626
|
(patch) => {
|
|
3074
3627
|
if (!supported) return;
|
|
3075
3628
|
setSaving(true);
|
|
@@ -4040,6 +4593,10 @@ var stripeConnectConnectorDef = {
|
|
|
4040
4593
|
};
|
|
4041
4594
|
var VINDI_CONNECTOR_ID = "vindi";
|
|
4042
4595
|
var VINDI_LATENCY_BUDGET_MS = 6e3;
|
|
4596
|
+
var VINDI_AUTHENTICATION_URLS = {
|
|
4597
|
+
sandbox: "https://tc-intermediador-sandbox.yapay.com.br/authentication?consumer_key=",
|
|
4598
|
+
production: "https://tc.intermediador.yapay.com.br/authentication?consumer_key="
|
|
4599
|
+
};
|
|
4043
4600
|
var VINDI_FIELDS = [
|
|
4044
4601
|
{
|
|
4045
4602
|
key: "tokenAccount",
|
|
@@ -4047,6 +4604,27 @@ var VINDI_FIELDS = [
|
|
|
4047
4604
|
type: "password",
|
|
4048
4605
|
secret: true,
|
|
4049
4606
|
placeholder: "Vindi Pagamentos \u2192 Minha Conta \u2192 Token"
|
|
4607
|
+
},
|
|
4608
|
+
{
|
|
4609
|
+
key: "consumerKey",
|
|
4610
|
+
label: "Chave de consumidor (para links de pagamento)",
|
|
4611
|
+
type: "password",
|
|
4612
|
+
secret: true,
|
|
4613
|
+
placeholder: "consumer_key"
|
|
4614
|
+
},
|
|
4615
|
+
{
|
|
4616
|
+
key: "consumerSecret",
|
|
4617
|
+
label: "Segredo de consumidor",
|
|
4618
|
+
type: "password",
|
|
4619
|
+
secret: true,
|
|
4620
|
+
placeholder: "consumer_secret"
|
|
4621
|
+
},
|
|
4622
|
+
{
|
|
4623
|
+
key: "authorizationCode",
|
|
4624
|
+
label: "CODE de autoriza\xE7\xE3o",
|
|
4625
|
+
type: "password",
|
|
4626
|
+
secret: true,
|
|
4627
|
+
placeholder: "copiado da barra de endere\xE7o, uma vez por ambiente"
|
|
4050
4628
|
}
|
|
4051
4629
|
];
|
|
4052
4630
|
var VINDI_ENVIRONMENT_SETTING = {
|
|
@@ -4145,11 +4723,14 @@ function connectionDetail(settings) {
|
|
|
4145
4723
|
}
|
|
4146
4724
|
function VindiExtraPanel() {
|
|
4147
4725
|
const [connected, setConnected] = useState(false);
|
|
4726
|
+
const [environment, setEnvironment] = useState("");
|
|
4148
4727
|
const [loaded, setLoaded] = useState(false);
|
|
4149
4728
|
useEffect(() => {
|
|
4150
4729
|
let cancelled = false;
|
|
4151
4730
|
void store5.get().then((c) => {
|
|
4152
|
-
if (
|
|
4731
|
+
if (cancelled) return;
|
|
4732
|
+
setConnected(!!c?.active);
|
|
4733
|
+
setEnvironment(String(c?.settings?.environment ?? ""));
|
|
4153
4734
|
}).catch(() => {
|
|
4154
4735
|
}).finally(() => {
|
|
4155
4736
|
if (!cancelled) setLoaded(true);
|
|
@@ -4159,16 +4740,53 @@ function VindiExtraPanel() {
|
|
|
4159
4740
|
};
|
|
4160
4741
|
}, []);
|
|
4161
4742
|
if (!loaded) return null;
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
/* @__PURE__ */ jsxs(
|
|
4165
|
-
"
|
|
4166
|
-
/* @__PURE__ */
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4743
|
+
const authenticationUrl = VINDI_AUTHENTICATION_URLS[environment];
|
|
4744
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t pt-4 text-xs text-muted-foreground", children: [
|
|
4745
|
+
connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4746
|
+
/* @__PURE__ */ jsx("p", { children: "Pix e boleto acontecem dentro da sua loja: o cliente v\xEA o QR Code ou a linha digit\xE1vel sem sair da p\xE1gina. O cart\xE3o \xE9 diferente \u2014 ele \xE9 digitado na p\xE1gina segura da Vindi, e o cliente volta para a loja em seguida. \xC9 assim de prop\xF3sito: nenhum n\xFAmero de cart\xE3o passa pelo seu site." }),
|
|
4747
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
4748
|
+
"Voc\xEA tamb\xE9m pode gerar um ",
|
|
4749
|
+
/* @__PURE__ */ jsx("strong", { children: "link de pagamento" }),
|
|
4750
|
+
" em Pedidos \u2192 Novo e mandar no WhatsApp. O cliente escolhe como pagar na p\xE1gina da Vindi, e o pedido aparece aqui sozinho quando o dinheiro cair."
|
|
4751
|
+
] }),
|
|
4752
|
+
/* @__PURE__ */ jsx("p", { children: "Sandbox e produ\xE7\xE3o s\xE3o contas diferentes, com tokens diferentes. Se voc\xEA trocar o ambiente aqui, troque tamb\xE9m as credenciais \u2014 as de teste n\xE3o cobram em produ\xE7\xE3o e as de produ\xE7\xE3o n\xE3o funcionam no sandbox." }),
|
|
4753
|
+
/* @__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.' })
|
|
4754
|
+
] }) : /* @__PURE__ */ jsx("p", { children: "O token da conta voc\xEA pega no painel da Vindi Pagamentos, em Minha Conta. Confira se ele \xE9 o do mesmo ambiente que voc\xEA vai escolher aqui \u2014 o token de sandbox n\xE3o cobra em produ\xE7\xE3o." }),
|
|
4755
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-dashed p-3", children: [
|
|
4756
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Para gerar links de pagamento" }),
|
|
4757
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
|
|
4758
|
+
"O token da conta n\xE3o emite link \u2014 a Vindi exige uma autoriza\xE7\xE3o \xE0 parte. Pe\xE7a a",
|
|
4759
|
+
" ",
|
|
4760
|
+
/* @__PURE__ */ jsx("strong", { children: "chave" }),
|
|
4761
|
+
" e o ",
|
|
4762
|
+
/* @__PURE__ */ jsx("strong", { children: "segredo de consumidor" }),
|
|
4763
|
+
" no painel da Vindi e preencha os campos acima. Falta ainda o ",
|
|
4764
|
+
/* @__PURE__ */ jsx("strong", { children: "CODE" }),
|
|
4765
|
+
":"
|
|
4766
|
+
] }),
|
|
4767
|
+
/* @__PURE__ */ jsxs("ol", { className: "mt-2 list-decimal space-y-1 pl-4", children: [
|
|
4768
|
+
/* @__PURE__ */ jsx("li", { children: authenticationUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4769
|
+
"Abra no navegador",
|
|
4770
|
+
" ",
|
|
4771
|
+
/* @__PURE__ */ jsxs("code", { className: "break-all rounded bg-muted px-1 py-0.5", children: [
|
|
4772
|
+
authenticationUrl,
|
|
4773
|
+
"SUA_CHAVE"
|
|
4774
|
+
] }),
|
|
4775
|
+
" ",
|
|
4776
|
+
"trocando ",
|
|
4777
|
+
/* @__PURE__ */ jsx("code", { children: "SUA_CHAVE" }),
|
|
4778
|
+
" pela sua chave de consumidor."
|
|
4779
|
+
] }) : /* @__PURE__ */ jsx(Fragment, { children: "Escolha o ambiente acima e o endere\xE7o para buscar o CODE aparece aqui." }) }),
|
|
4780
|
+
/* @__PURE__ */ jsx("li", { children: "A Vindi redireciona. O CODE fica na barra de endere\xE7o \u2014 copie de l\xE1." }),
|
|
4781
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
4782
|
+
"Cole no campo ",
|
|
4783
|
+
/* @__PURE__ */ jsx("strong", { children: "CODE de autoriza\xE7\xE3o" }),
|
|
4784
|
+
" e salve."
|
|
4785
|
+
] })
|
|
4786
|
+
] }),
|
|
4787
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2", children: "\xC9 uma vez por ambiente. A partir da\xED a renova\xE7\xE3o di\xE1ria acontece sozinha." })
|
|
4788
|
+
] })
|
|
4789
|
+
] });
|
|
4172
4790
|
}
|
|
4173
4791
|
var vindiConnectorDef = {
|
|
4174
4792
|
id: VINDI_CONNECTOR_ID,
|
|
@@ -4267,6 +4885,188 @@ var vindiConnectorDef = {
|
|
|
4267
4885
|
disconnect: () => store5.disconnect(),
|
|
4268
4886
|
ExtraPanel: VindiExtraPanel
|
|
4269
4887
|
};
|
|
4888
|
+
var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
|
|
4889
|
+
var MERCADOPAGO_LATENCY_BUDGET_MS = 6e3;
|
|
4890
|
+
var MERCADOPAGO_FIELDS = [
|
|
4891
|
+
{
|
|
4892
|
+
key: "accessToken",
|
|
4893
|
+
label: "Access token",
|
|
4894
|
+
type: "password",
|
|
4895
|
+
secret: true,
|
|
4896
|
+
placeholder: "APP_USR-\u2026 (Suas integra\xE7\xF5es \u2192 sua aplica\xE7\xE3o \u2192 Credenciais)"
|
|
4897
|
+
},
|
|
4898
|
+
{
|
|
4899
|
+
key: "publicKey",
|
|
4900
|
+
label: "Public key",
|
|
4901
|
+
// Not masked: it travels to the browser anyway, and a merchant who cannot
|
|
4902
|
+
// see it cannot tell the test key from the production one at a glance.
|
|
4903
|
+
type: "text",
|
|
4904
|
+
secret: true,
|
|
4905
|
+
placeholder: "APP_USR-\u2026"
|
|
4906
|
+
}
|
|
4907
|
+
];
|
|
4908
|
+
var store6 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID, {
|
|
4909
|
+
fields: MERCADOPAGO_FIELDS
|
|
4910
|
+
});
|
|
4911
|
+
function connectionDetail2(settings) {
|
|
4912
|
+
const key = String(settings?.publicKey ?? "").trim();
|
|
4913
|
+
if (!key) return "Pix e cart\xE3o";
|
|
4914
|
+
return /^TEST-/i.test(key) ? "Sandbox \xB7 Pix e cart\xE3o" : "Produ\xE7\xE3o \xB7 Pix e cart\xE3o";
|
|
4915
|
+
}
|
|
4916
|
+
function MercadoPagoExtraPanel() {
|
|
4917
|
+
const [connected, setConnected] = useState(false);
|
|
4918
|
+
const [loaded, setLoaded] = useState(false);
|
|
4919
|
+
useEffect(() => {
|
|
4920
|
+
let cancelled = false;
|
|
4921
|
+
void store6.get().then((c) => {
|
|
4922
|
+
if (!cancelled) setConnected(!!c?.active);
|
|
4923
|
+
}).catch(() => {
|
|
4924
|
+
}).finally(() => {
|
|
4925
|
+
if (!cancelled) setLoaded(true);
|
|
4926
|
+
});
|
|
4927
|
+
return () => {
|
|
4928
|
+
cancelled = true;
|
|
4929
|
+
};
|
|
4930
|
+
}, []);
|
|
4931
|
+
if (!loaded) return null;
|
|
4932
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t pt-4 text-xs text-muted-foreground", children: [
|
|
4933
|
+
connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4934
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
4935
|
+
"Pix e cart\xE3o acontecem ",
|
|
4936
|
+
/* @__PURE__ */ jsx("strong", { children: "dentro da sua loja" }),
|
|
4937
|
+
". O cliente v\xEA o QR Code ou digita o cart\xE3o sem sair da p\xE1gina, e volta para a confirma\xE7\xE3o no mesmo lugar. Diferente das outras adquirentes, aqui o cart\xE3o n\xE3o redireciona."
|
|
4938
|
+
] }),
|
|
4939
|
+
/* @__PURE__ */ jsx("p", { children: "O n\xFAmero do cart\xE3o n\xE3o passa pelo seu site: ele \xE9 digitado dentro de um formul\xE1rio do pr\xF3prio Mercado Pago e vira um c\xF3digo de uso \xFAnico antes de chegar at\xE9 n\xF3s. \xC9 por isso que d\xE1 para receber cart\xE3o na sua p\xE1gina sem a auditoria que isso normalmente exige." }),
|
|
4940
|
+
/* @__PURE__ */ jsx("p", { children: 'O valor cobrado \xE9 sempre recalculado aqui no servidor a partir do pedido. Pre\xE7o alterado no navegador n\xE3o \xE9 aceito, e clicar duas vezes em "pagar" gera uma cobran\xE7a s\xF3.' }),
|
|
4941
|
+
/* @__PURE__ */ jsx("p", { children: "O Pix vale por 30 minutos. Se o cliente fechar a aba depois de pagar, o pedido aparece aqui do mesmo jeito \u2014 o Mercado Pago avisa a loja assim que o dinheiro cai." })
|
|
4942
|
+
] }) : /* @__PURE__ */ jsxs("p", { children: [
|
|
4943
|
+
"As duas chaves ficam no painel do Mercado Pago, em ",
|
|
4944
|
+
/* @__PURE__ */ jsx("strong", { children: "Suas integra\xE7\xF5es" }),
|
|
4945
|
+
" \u2192 sua aplica\xE7\xE3o \u2192 ",
|
|
4946
|
+
/* @__PURE__ */ jsx("strong", { children: "Credenciais de produ\xE7\xE3o" }),
|
|
4947
|
+
". Copie o ",
|
|
4948
|
+
/* @__PURE__ */ jsx("em", { children: "access token" }),
|
|
4949
|
+
" e a",
|
|
4950
|
+
" ",
|
|
4951
|
+
/* @__PURE__ */ jsx("em", { children: "public key" }),
|
|
4952
|
+
" da ",
|
|
4953
|
+
/* @__PURE__ */ jsx("strong", { children: "mesma" }),
|
|
4954
|
+
" aplica\xE7\xE3o: elas s\xE3o um par, e uma sem a outra n\xE3o fecha uma venda."
|
|
4955
|
+
] }),
|
|
4956
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-dashed p-3", children: [
|
|
4957
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Para testar sem cobrar de verdade" }),
|
|
4958
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
|
|
4959
|
+
"As chaves que come\xE7am com ",
|
|
4960
|
+
/* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5", children: "TEST-" }),
|
|
4961
|
+
" ",
|
|
4962
|
+
/* @__PURE__ */ jsx("strong", { children: "n\xE3o funcionam" }),
|
|
4963
|
+
" aqui \u2014 o Mercado Pago as recusa nesta API. O teste \xE9 feito com ",
|
|
4964
|
+
/* @__PURE__ */ jsx("strong", { children: "usu\xE1rios de teste" }),
|
|
4965
|
+
": crie um vendedor e um comprador em Suas integra\xE7\xF5es \u2192 Contas de teste, e use as chaves de ",
|
|
4966
|
+
/* @__PURE__ */ jsx("em", { children: "produ\xE7\xE3o" }),
|
|
4967
|
+
" do vendedor de teste. N\xE3o \xE9 dinheiro real, e \xE9 assim que o pr\xF3prio Mercado Pago recomenda."
|
|
4968
|
+
] })
|
|
4969
|
+
] })
|
|
4970
|
+
] });
|
|
4971
|
+
}
|
|
4972
|
+
var mercadoPagoConnectorDef = {
|
|
4973
|
+
id: MERCADOPAGO_CONNECTOR_ID,
|
|
4974
|
+
hostPluginId: "shop",
|
|
4975
|
+
name: "Mercado Pago",
|
|
4976
|
+
description: "Pix e cart\xE3o pelo Mercado Pago, os dois pagos dentro da loja \u2014 o cart\xE3o \xE9 digitado sem o cliente sair da p\xE1gina.",
|
|
4977
|
+
icon: "CreditCard",
|
|
4978
|
+
// Declared rather than borrowed from the catalogue, for the same reason Vindi
|
|
4979
|
+
// declares its own: the catalogue row is a change in another repo that has to
|
|
4980
|
+
// be seeded before it exists anywhere, and until then the shelf renders a grey
|
|
4981
|
+
// plug. The absolute URL answers from any origin — a tenant app is served from
|
|
4982
|
+
// its own domain, so a path would not.
|
|
4983
|
+
logoUrl: "https://assets.fayz.ai/connectors/mercadopago.png",
|
|
4984
|
+
category: "payments",
|
|
4985
|
+
// Pasted keys today. Unlike Vindi — whose consent screen only LOOKS like OAuth
|
|
4986
|
+
// — Mercado Pago has a real merchant OAuth, and it is the right end state: one
|
|
4987
|
+
// platform application, each seller authorizing it, `marketplace_fee` taken on
|
|
4988
|
+
// the charge instead of invoiced afterwards. What it needs is the redirect URI
|
|
4989
|
+
// registered against the platform broker, which is a change in ymaia and not
|
|
4990
|
+
// here. The same slug is already declared by plugin-financial with
|
|
4991
|
+
// `authKind: 'oauth'`, and because a credential is keyed by slug + tenant, a
|
|
4992
|
+
// store that authorizes there is redeemable from here the day that lands.
|
|
4993
|
+
authKind: "api-key",
|
|
4994
|
+
// The charge is opened in the buyer's request path; the notification and the
|
|
4995
|
+
// refund are outbound sync work.
|
|
4996
|
+
planes: ["request", "sync"],
|
|
4997
|
+
direction: "outbound",
|
|
4998
|
+
docsUrl: "https://www.mercadopago.com.br/developers/pt/docs/checkout-api/landing",
|
|
4999
|
+
providerInstall: { url: "https://www.mercadopago.com.br/developers/panel/app" },
|
|
5000
|
+
// Grounded in what the request plane does, and padded with nothing: the total
|
|
5001
|
+
// is recomputed server-side from the order before the charge is opened, which
|
|
5002
|
+
// is a read of the order and never a write of it; the charge and its
|
|
5003
|
+
// confirmation are the write.
|
|
5004
|
+
permissions: [
|
|
5005
|
+
{
|
|
5006
|
+
kind: "data",
|
|
5007
|
+
resource: "order",
|
|
5008
|
+
actions: ["read"],
|
|
5009
|
+
reason: "Ler o pedido para recalcular o valor no servidor antes de abrir a cobran\xE7a \u2014 o total nunca vem do navegador do cliente.",
|
|
5010
|
+
sensitive: true
|
|
5011
|
+
},
|
|
5012
|
+
{
|
|
5013
|
+
kind: "data",
|
|
5014
|
+
resource: "payment",
|
|
5015
|
+
actions: ["write"],
|
|
5016
|
+
reason: "Registrar aqui a cobran\xE7a aberta e a confirma\xE7\xE3o do pagamento. O n\xFAmero do cart\xE3o n\xE3o passa por este app \u2014 ele \xE9 digitado num formul\xE1rio do pr\xF3prio Mercado Pago.",
|
|
5017
|
+
sensitive: true
|
|
5018
|
+
}
|
|
5019
|
+
],
|
|
5020
|
+
latencyBudgetMs: MERCADOPAGO_LATENCY_BUDGET_MS,
|
|
5021
|
+
// A gateway that does not answer must stop the sale: an order that reads as
|
|
5022
|
+
// confirmed without a payment is worse than a refused checkout. Same verdict
|
|
5023
|
+
// as the other four gateways, opposite to the carrier next door.
|
|
5024
|
+
onFailure: "block",
|
|
5025
|
+
fields: MERCADOPAGO_FIELDS,
|
|
5026
|
+
invokedAt: [
|
|
5027
|
+
{
|
|
5028
|
+
key: "shop.checkout.session_open",
|
|
5029
|
+
label: "O cliente finaliza a compra e a cobran\xE7a \xE9 aberta no Mercado Pago (QR do Pix ou cart\xE3o digitado na pr\xF3pria loja)",
|
|
5030
|
+
touches: ["order", "payment"]
|
|
5031
|
+
},
|
|
5032
|
+
{
|
|
5033
|
+
key: "shop.payment.capture",
|
|
5034
|
+
label: "O Mercado Pago avisa que o pagamento caiu, a cobran\xE7a \xE9 reconsultada e o pedido \xE9 liberado",
|
|
5035
|
+
plane: "sync",
|
|
5036
|
+
trigger: "webhook",
|
|
5037
|
+
touches: ["payment"]
|
|
5038
|
+
}
|
|
5039
|
+
],
|
|
5040
|
+
// Read from the row the list already batched, so a screen with N connectors
|
|
5041
|
+
// costs one plg_connections request rather than N.
|
|
5042
|
+
//
|
|
5043
|
+
// No `settingsStatus`: this connector has nothing to configure, so a
|
|
5044
|
+
// connection either has its credential pair or does not. "Conectado, falta
|
|
5045
|
+
// configurar" would be a state that can never be true here.
|
|
5046
|
+
...connectionStatus(
|
|
5047
|
+
MERCADOPAGO_CONNECTOR_ID,
|
|
5048
|
+
(connection) => ({
|
|
5049
|
+
connected: !!connection?.active,
|
|
5050
|
+
detail: connectionDetail2(connection?.settings),
|
|
5051
|
+
plane: "request"
|
|
5052
|
+
}),
|
|
5053
|
+
{ store: () => store6 }
|
|
5054
|
+
),
|
|
5055
|
+
...connectionSettings(store6),
|
|
5056
|
+
// Reached only AFTER the credential sink accepted the pair — that ordering is
|
|
5057
|
+
// what makes `active = true` mean "credentials were placed for this store"
|
|
5058
|
+
// rather than "somebody pressed a button". The secret never arrives here: the
|
|
5059
|
+
// hub split it out before calling this.
|
|
5060
|
+
//
|
|
5061
|
+
// No `testConnection`: the probe is handed the non-secret half, and a public
|
|
5062
|
+
// key alone cannot open an order. Testing belongs to the edge function that
|
|
5063
|
+
// holds the credential.
|
|
5064
|
+
saveConnection: async () => {
|
|
5065
|
+
await store6.save({ active: true });
|
|
5066
|
+
},
|
|
5067
|
+
disconnect: () => store6.disconnect(),
|
|
5068
|
+
ExtraPanel: MercadoPagoExtraPanel
|
|
5069
|
+
};
|
|
4270
5070
|
var KIND_META = {
|
|
4271
5071
|
online_store: { label: "Loja Online", icon: Globe, hint: "Seu storefront fayz \u2014 a vitrine p\xFAblica da loja.", placeholder: "https://minhaloja.com.br/" },
|
|
4272
5072
|
whatsapp: { label: "WhatsApp", icon: MessageCircle, hint: "Link direto para o WhatsApp Business.", placeholder: "https://wa.me/5521999999999" },
|
|
@@ -4284,12 +5084,12 @@ function notifyChannelsChanged() {
|
|
|
4284
5084
|
if (typeof window !== "undefined") window.dispatchEvent(new Event("fayz:channels-changed"));
|
|
4285
5085
|
}
|
|
4286
5086
|
function ChannelsPage({ resolveProvider }) {
|
|
4287
|
-
const provider =
|
|
5087
|
+
const provider = React9.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
|
|
4288
5088
|
const supported = typeof provider.listSalesChannels === "function";
|
|
4289
|
-
const [channels, setChannels] =
|
|
4290
|
-
const [loading, setLoading] =
|
|
4291
|
-
const [adding, setAdding] =
|
|
4292
|
-
const reload =
|
|
5089
|
+
const [channels, setChannels] = React9.useState([]);
|
|
5090
|
+
const [loading, setLoading] = React9.useState(true);
|
|
5091
|
+
const [adding, setAdding] = React9.useState(null);
|
|
5092
|
+
const reload = React9.useCallback(async () => {
|
|
4293
5093
|
if (!provider.listSalesChannels) {
|
|
4294
5094
|
setLoading(false);
|
|
4295
5095
|
return;
|
|
@@ -4307,7 +5107,7 @@ function ChannelsPage({ resolveProvider }) {
|
|
|
4307
5107
|
setLoading(false);
|
|
4308
5108
|
}
|
|
4309
5109
|
}, [provider]);
|
|
4310
|
-
|
|
5110
|
+
React9.useEffect(() => {
|
|
4311
5111
|
void reload();
|
|
4312
5112
|
}, [reload]);
|
|
4313
5113
|
const patch = async (id, input) => {
|
|
@@ -4363,7 +5163,7 @@ function ChannelsPage({ resolveProvider }) {
|
|
|
4363
5163
|
] });
|
|
4364
5164
|
}
|
|
4365
5165
|
function OnlineStoreCard({ channel, onSave, onToggle }) {
|
|
4366
|
-
const [url, setUrl] =
|
|
5166
|
+
const [url, setUrl] = React9.useState(channel.url ?? "");
|
|
4367
5167
|
const dirty = (channel.url ?? "") !== url;
|
|
4368
5168
|
const meta = KIND_META.online_store;
|
|
4369
5169
|
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: [
|
|
@@ -4389,8 +5189,8 @@ function OnlineStoreCard({ channel, onSave, onToggle }) {
|
|
|
4389
5189
|
] }) });
|
|
4390
5190
|
}
|
|
4391
5191
|
function ChannelRow({ channel, onSave, onToggle, onRemove }) {
|
|
4392
|
-
const [url, setUrl] =
|
|
4393
|
-
const [editing, setEditing] =
|
|
5192
|
+
const [url, setUrl] = React9.useState(channel.url ?? "");
|
|
5193
|
+
const [editing, setEditing] = React9.useState(false);
|
|
4394
5194
|
const meta = KIND_META[channel.kind] ?? KIND_META.custom;
|
|
4395
5195
|
const Icon = meta.icon;
|
|
4396
5196
|
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: [
|
|
@@ -4418,9 +5218,9 @@ function ChannelRow({ channel, onSave, onToggle, onRemove }) {
|
|
|
4418
5218
|
}
|
|
4419
5219
|
function AddChannelForm({ kind, onKind, onCancel, onAdd }) {
|
|
4420
5220
|
const meta = KIND_META[kind];
|
|
4421
|
-
const [label, setLabel] =
|
|
4422
|
-
const [url, setUrl] =
|
|
4423
|
-
|
|
5221
|
+
const [label, setLabel] = React9.useState(meta.label);
|
|
5222
|
+
const [url, setUrl] = React9.useState("");
|
|
5223
|
+
React9.useEffect(() => {
|
|
4424
5224
|
setLabel(KIND_META[kind].label);
|
|
4425
5225
|
}, [kind]);
|
|
4426
5226
|
return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-card border border-border bg-card p-4", children: [
|
|
@@ -4472,17 +5272,17 @@ function formatSize(bytes) {
|
|
|
4472
5272
|
return `${(bytes / 1024 ** i).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
4473
5273
|
}
|
|
4474
5274
|
function ContentPage({ resolveProvider }) {
|
|
4475
|
-
const provider =
|
|
5275
|
+
const provider = React9.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
|
|
4476
5276
|
const supported = typeof provider.listMediaFiles === "function";
|
|
4477
|
-
const [files, setFiles] =
|
|
4478
|
-
const [tab, setTab] =
|
|
4479
|
-
const [uploading, setUploading] =
|
|
4480
|
-
const [pendingDelete, setPendingDelete] =
|
|
4481
|
-
const [deleting, setDeleting] =
|
|
4482
|
-
const [replacing, setReplacing] =
|
|
4483
|
-
const [openId, setOpenId] =
|
|
4484
|
-
const [dimensions, setDimensions] =
|
|
4485
|
-
const reload =
|
|
5277
|
+
const [files, setFiles] = React9.useState(null);
|
|
5278
|
+
const [tab, setTab] = React9.useState("all");
|
|
5279
|
+
const [uploading, setUploading] = React9.useState(false);
|
|
5280
|
+
const [pendingDelete, setPendingDelete] = React9.useState(null);
|
|
5281
|
+
const [deleting, setDeleting] = React9.useState(false);
|
|
5282
|
+
const [replacing, setReplacing] = React9.useState(false);
|
|
5283
|
+
const [openId, setOpenId] = React9.useState(null);
|
|
5284
|
+
const [dimensions, setDimensions] = React9.useState({});
|
|
5285
|
+
const reload = React9.useCallback(async () => {
|
|
4486
5286
|
if (!provider.listMediaFiles) {
|
|
4487
5287
|
setFiles([]);
|
|
4488
5288
|
return;
|
|
@@ -4494,10 +5294,10 @@ function ContentPage({ resolveProvider }) {
|
|
|
4494
5294
|
setFiles([]);
|
|
4495
5295
|
}
|
|
4496
5296
|
}, [provider]);
|
|
4497
|
-
|
|
5297
|
+
React9.useEffect(() => {
|
|
4498
5298
|
void reload();
|
|
4499
5299
|
}, [reload]);
|
|
4500
|
-
const measure =
|
|
5300
|
+
const measure = React9.useCallback((id, d) => {
|
|
4501
5301
|
setDimensions((prev) => prev[id] ? prev : { ...prev, [id]: d });
|
|
4502
5302
|
}, []);
|
|
4503
5303
|
const handleUpload = async (picked) => {
|
|
@@ -4687,7 +5487,7 @@ function SectionTitle({ title, count }) {
|
|
|
4687
5487
|
] });
|
|
4688
5488
|
}
|
|
4689
5489
|
function UploadButton({ uploading, onPick }) {
|
|
4690
|
-
const inputRef =
|
|
5490
|
+
const inputRef = React9.useRef(null);
|
|
4691
5491
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4692
5492
|
/* @__PURE__ */ jsx(
|
|
4693
5493
|
"input",
|
|
@@ -4767,8 +5567,8 @@ function MediaDetail({
|
|
|
4767
5567
|
onDelete,
|
|
4768
5568
|
replacing
|
|
4769
5569
|
}) {
|
|
4770
|
-
const replaceRef =
|
|
4771
|
-
|
|
5570
|
+
const replaceRef = React9.useRef(null);
|
|
5571
|
+
React9.useEffect(() => {
|
|
4772
5572
|
function onKey(e) {
|
|
4773
5573
|
if (e.key === "ArrowLeft") onNavigate(-1);
|
|
4774
5574
|
if (e.key === "ArrowRight") onNavigate(1);
|
|
@@ -4995,7 +5795,7 @@ async function pendingCustomers() {
|
|
|
4995
5795
|
if (!tenantId || !supabase) return null;
|
|
4996
5796
|
const { data: linked } = await supabase.from("plg_shop_customer_links").select("customer_id").eq("tenant_id", tenantId).eq("source", "bling");
|
|
4997
5797
|
const already = new Set((linked ?? []).map((row) => row.customer_id));
|
|
4998
|
-
const { data, error } = await supabase.from("
|
|
5798
|
+
const { data, error } = await supabase.from("v_shop_customers").select("id").eq("tenant_id", tenantId).gt("orders_count", 0);
|
|
4999
5799
|
if (error) return null;
|
|
5000
5800
|
return (data ?? []).filter((row) => !already.has(row.id)).length;
|
|
5001
5801
|
}
|
|
@@ -5005,7 +5805,7 @@ async function pendingOrders() {
|
|
|
5005
5805
|
if (!tenantId || !supabase) return null;
|
|
5006
5806
|
const { data: linked } = await supabase.from("plg_shop_order_links").select("order_id").eq("tenant_id", tenantId).eq("source", "bling").eq("kind", "sales_order");
|
|
5007
5807
|
const already = new Set((linked ?? []).map((row) => row.order_id));
|
|
5008
|
-
const { data, error } = await supabase.from("
|
|
5808
|
+
const { data, error } = await supabase.from("v_shop_orders").select("id").eq("tenant_id", tenantId).neq("status", "cancelled");
|
|
5009
5809
|
if (error) return null;
|
|
5010
5810
|
return (data ?? []).filter((row) => !already.has(row.id)).length;
|
|
5011
5811
|
}
|
|
@@ -5013,7 +5813,7 @@ async function listOrderDeliveries(limit = 10) {
|
|
|
5013
5813
|
const tenantId = getShopTenantId();
|
|
5014
5814
|
const supabase = getSupabaseClientOptional();
|
|
5015
5815
|
if (!tenantId || !supabase) return [];
|
|
5016
|
-
const { data: orders } = await supabase.from("
|
|
5816
|
+
const { data: orders } = await supabase.from("v_shop_orders").select("id,order_number,customer_name,customer_document,paid_at,created_at").eq("tenant_id", tenantId).neq("status", "cancelled").order("created_at", { ascending: false }).limit(limit);
|
|
5017
5817
|
const rows = orders ?? [];
|
|
5018
5818
|
if (rows.length === 0) return [];
|
|
5019
5819
|
const { data: filed } = await supabase.from("plg_shop_order_links").select("order_id,external_id,external_number,synced_at").eq("tenant_id", tenantId).eq("source", "bling").eq("kind", "sales_order").in("order_id", rows.map((row) => row.id));
|
|
@@ -5537,7 +6337,7 @@ async function pendingOrders2() {
|
|
|
5537
6337
|
if (!tenantId || !supabase) return null;
|
|
5538
6338
|
const { data: linked } = await supabase.from("plg_shop_order_links").select("order_id").eq("tenant_id", tenantId).eq("source", "neochef").eq("kind", "kitchen_order");
|
|
5539
6339
|
const already = new Set((linked ?? []).map((row) => row.order_id));
|
|
5540
|
-
const { data, error } = await supabase.from("
|
|
6340
|
+
const { data, error } = await supabase.from("v_shop_orders").select("id").eq("tenant_id", tenantId).neq("status", "cancelled");
|
|
5541
6341
|
if (error) return null;
|
|
5542
6342
|
return (data ?? []).filter((row) => !already.has(row.id)).length;
|
|
5543
6343
|
}
|
|
@@ -5545,7 +6345,7 @@ async function listOrderDeliveries2(limit = 10) {
|
|
|
5545
6345
|
const tenantId = getShopTenantId();
|
|
5546
6346
|
const supabase = getSupabaseClientOptional();
|
|
5547
6347
|
if (!tenantId || !supabase) return [];
|
|
5548
|
-
const { data: orders } = await supabase.from("
|
|
6348
|
+
const { data: orders } = await supabase.from("v_shop_orders").select("id,order_number,paid_at,created_at,customer_name,status").eq("tenant_id", tenantId).order("created_at", { ascending: false }).limit(limit);
|
|
5549
6349
|
const rows = orders ?? [];
|
|
5550
6350
|
if (rows.length === 0) return [];
|
|
5551
6351
|
const { data: filed } = await supabase.from("plg_shop_order_links").select("order_id,external_id,external_number,synced_at").eq("tenant_id", tenantId).eq("source", "neochef").eq("kind", "kitchen_order").in("order_id", rows.map((row) => row.id));
|
|
@@ -6623,7 +7423,7 @@ function buildShopAnalyticsCards() {
|
|
|
6623
7423
|
}
|
|
6624
7424
|
|
|
6625
7425
|
// src/index.ts
|
|
6626
|
-
var ShopPage =
|
|
7426
|
+
var ShopPage = React9.lazy(() => import('./ShopPage-QFIM2HDU.js').then((m) => ({ default: m.ShopPage })));
|
|
6627
7427
|
function createShopPlugin(options) {
|
|
6628
7428
|
const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
|
|
6629
7429
|
setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
|
|
@@ -6632,10 +7432,10 @@ function createShopPlugin(options) {
|
|
|
6632
7432
|
registerEntityPath("shop_customer", "/orders/customers");
|
|
6633
7433
|
registerPersonLookup("shop_customer", shopCustomerLookup(() => resolveProvider()));
|
|
6634
7434
|
const pageFor = (section) => {
|
|
6635
|
-
const Wrapped = () =>
|
|
6636
|
-
|
|
7435
|
+
const Wrapped = () => React9.createElement(
|
|
7436
|
+
React9.Suspense,
|
|
6637
7437
|
{ fallback: null },
|
|
6638
|
-
|
|
7438
|
+
React9.createElement(
|
|
6639
7439
|
ShopPage,
|
|
6640
7440
|
{ provider: options?.provider, section }
|
|
6641
7441
|
)
|
|
@@ -6761,6 +7561,29 @@ function createShopPlugin(options) {
|
|
|
6761
7561
|
const DiscountsCrudPage = createCrudPage(buildShopDiscountEntity(), {
|
|
6762
7562
|
dataProvider: createShopDiscountDataProvider(resolveProvider),
|
|
6763
7563
|
feature: "shop",
|
|
7564
|
+
// O cupom vive atrás do provider de commerce, sem tabela que as primitivas
|
|
7565
|
+
// de dado do agente alcancem — então a lista empresta o próprio store, como
|
|
7566
|
+
// a de produtos faz (ver CrudAgentSurface).
|
|
7567
|
+
aiCreate: {
|
|
7568
|
+
hint: "Descreva o cupom\u2026",
|
|
7569
|
+
description: "Descreva a promo\xE7\xE3o e eu crio o cupom.",
|
|
7570
|
+
// O código não se pergunta: ver DISCOUNT_CREATE_INSTRUCTIONS. `code` é
|
|
7571
|
+
// único no banco (0001:208), então a lista publica os que já existem e a
|
|
7572
|
+
// recusa do índice volta legível se dois turnos escolherem o mesmo.
|
|
7573
|
+
instructions: DISCOUNT_CREATE_INSTRUCTIONS,
|
|
7574
|
+
uniqueFields: ["code"],
|
|
7575
|
+
intro: {
|
|
7576
|
+
role: "operations",
|
|
7577
|
+
persona: "Cupons e promo\xE7\xF5es",
|
|
7578
|
+
pitch: "Diga a promo\xE7\xE3o que voc\xEA quer fazer.",
|
|
7579
|
+
description: "Frete gr\xE1tis, percentual ou valor fixo \u2014 diga a regra do jeito que voc\xEA falaria com um cliente, e eu escrevo o c\xF3digo, o tipo, o m\xEDnimo e a validade. S\xF3 pergunto o tamanho do desconto quando voc\xEA n\xE3o disser.",
|
|
7580
|
+
suggestions: [
|
|
7581
|
+
{ label: "Frete gr\xE1tis at\xE9 o fim da semana" },
|
|
7582
|
+
{ label: "10% na primeira compra, uma vez por cliente" },
|
|
7583
|
+
{ label: "R$ 50 off acima de R$ 300, limite de 100 usos" }
|
|
7584
|
+
]
|
|
7585
|
+
}
|
|
7586
|
+
},
|
|
6764
7587
|
stats: (items, total) => [
|
|
6765
7588
|
{ id: "total", label: "Total de cupons", value: total, icon: Tag },
|
|
6766
7589
|
{ id: "active", label: "Ativos agora", value: items?.filter((d) => d.status === "active").length ?? 0, icon: Eye },
|
|
@@ -6791,7 +7614,7 @@ function createShopPlugin(options) {
|
|
|
6791
7614
|
dataProvider: createShopGiftCardDataProvider(resolveProvider),
|
|
6792
7615
|
feature: "shop",
|
|
6793
7616
|
stats: (items, total) => {
|
|
6794
|
-
const
|
|
7617
|
+
const money2 = (v) => new Intl.NumberFormat(curr.locale, { style: "currency", currency: curr.code }).format(v);
|
|
6795
7618
|
const active = items?.filter((g) => g.status === "active") ?? [];
|
|
6796
7619
|
return [
|
|
6797
7620
|
{ id: "total", label: "Cart\xF5es emitidos", value: total, icon: CreditCard },
|
|
@@ -6802,20 +7625,20 @@ function createShopPlugin(options) {
|
|
|
6802
7625
|
id: "outstanding",
|
|
6803
7626
|
label: "Saldo em aberto",
|
|
6804
7627
|
icon: CircleDollarSign,
|
|
6805
|
-
value:
|
|
7628
|
+
value: money2(active.reduce((sum, g) => sum + (g.balance ?? 0), 0))
|
|
6806
7629
|
},
|
|
6807
7630
|
{
|
|
6808
7631
|
id: "issued",
|
|
6809
7632
|
label: "Valor emitido",
|
|
6810
7633
|
icon: TicketCheck,
|
|
6811
|
-
value:
|
|
7634
|
+
value: money2(items?.reduce((sum, g) => sum + (g.initialBalance ?? 0), 0) ?? 0)
|
|
6812
7635
|
}
|
|
6813
7636
|
];
|
|
6814
7637
|
}
|
|
6815
7638
|
});
|
|
6816
|
-
const ContentPageWrapped = () =>
|
|
7639
|
+
const ContentPageWrapped = () => React9.createElement(ContentPage, { resolveProvider: options?.provider });
|
|
6817
7640
|
ContentPageWrapped.displayName = "ContentPage";
|
|
6818
|
-
const ChannelsPageWrapped = () =>
|
|
7641
|
+
const ChannelsPageWrapped = () => React9.createElement(ChannelsPage, { resolveProvider: options?.provider });
|
|
6819
7642
|
ChannelsPageWrapped.displayName = "ChannelsPage";
|
|
6820
7643
|
const navSection = options?.navSection ?? "main";
|
|
6821
7644
|
const navPosition = options?.navPosition ?? 6;
|
|
@@ -6905,7 +7728,7 @@ function createShopPlugin(options) {
|
|
|
6905
7728
|
];
|
|
6906
7729
|
const redirectTo = (target) => {
|
|
6907
7730
|
const Redirect = () => {
|
|
6908
|
-
|
|
7731
|
+
React9.useEffect(() => {
|
|
6909
7732
|
adminNavigateTo(target);
|
|
6910
7733
|
}, []);
|
|
6911
7734
|
return null;
|
|
@@ -7029,7 +7852,7 @@ function createShopPlugin(options) {
|
|
|
7029
7852
|
label: label("store", "Loja"),
|
|
7030
7853
|
icon: "ShoppingBag",
|
|
7031
7854
|
group: COMMERCE_GROUP,
|
|
7032
|
-
component: settingsTab("StoreGeneralSettings", () =>
|
|
7855
|
+
component: settingsTab("StoreGeneralSettings", () => React9.createElement(StoreGeneralSettings, { currency: curr, provider: options?.provider })),
|
|
7033
7856
|
order: 30,
|
|
7034
7857
|
permission: READ
|
|
7035
7858
|
},
|
|
@@ -7038,7 +7861,7 @@ function createShopPlugin(options) {
|
|
|
7038
7861
|
label: "Pagamentos",
|
|
7039
7862
|
icon: "CreditCard",
|
|
7040
7863
|
group: COMMERCE_GROUP,
|
|
7041
|
-
component: settingsTab("PaymentsSettings", () =>
|
|
7864
|
+
component: settingsTab("PaymentsSettings", () => React9.createElement(PaymentsSettings, { provider: options?.provider })),
|
|
7042
7865
|
order: 31,
|
|
7043
7866
|
permission: READ
|
|
7044
7867
|
},
|
|
@@ -7047,7 +7870,7 @@ function createShopPlugin(options) {
|
|
|
7047
7870
|
label: "Checkout",
|
|
7048
7871
|
icon: "ShoppingCart",
|
|
7049
7872
|
group: COMMERCE_GROUP,
|
|
7050
|
-
component: settingsTab("CheckoutSettings", () =>
|
|
7873
|
+
component: settingsTab("CheckoutSettings", () => React9.createElement(CheckoutSettings, { provider: options?.provider })),
|
|
7051
7874
|
order: 32,
|
|
7052
7875
|
permission: READ
|
|
7053
7876
|
},
|
|
@@ -7056,7 +7879,7 @@ function createShopPlugin(options) {
|
|
|
7056
7879
|
label: label("shipping", "Entrega"),
|
|
7057
7880
|
icon: "Truck",
|
|
7058
7881
|
group: COMMERCE_GROUP,
|
|
7059
|
-
component: settingsTab("ShippingSettings", () =>
|
|
7882
|
+
component: settingsTab("ShippingSettings", () => React9.createElement(ShippingSettings, { provider: options?.provider })),
|
|
7060
7883
|
order: 33,
|
|
7061
7884
|
permission: READ
|
|
7062
7885
|
},
|
|
@@ -7065,7 +7888,7 @@ function createShopPlugin(options) {
|
|
|
7065
7888
|
label: "Fulfillment",
|
|
7066
7889
|
icon: "PackageCheck",
|
|
7067
7890
|
group: COMMERCE_GROUP,
|
|
7068
|
-
component: settingsTab("FulfillmentSettings", () =>
|
|
7891
|
+
component: settingsTab("FulfillmentSettings", () => React9.createElement(FulfillmentSettings, { provider: options?.provider })),
|
|
7069
7892
|
order: 34,
|
|
7070
7893
|
permission: READ
|
|
7071
7894
|
}
|
|
@@ -7078,27 +7901,33 @@ function createShopPlugin(options) {
|
|
|
7078
7901
|
// ones — nobody is waiting on them, and what they declare instead is a call
|
|
7079
7902
|
// budget and a cursor.
|
|
7080
7903
|
//
|
|
7081
|
-
//
|
|
7082
|
-
//
|
|
7083
|
-
//
|
|
7084
|
-
//
|
|
7904
|
+
// Four gateways on the shelf is the point: the store picks one, and each
|
|
7905
|
+
// one arriving changed no contract — only how it authorizes. Mercado Pago
|
|
7906
|
+
// pushes that furthest, because it is the first whose CARD is paid inside
|
|
7907
|
+
// the store: it tokenises in the browser, so there is no PAN for an edge
|
|
7908
|
+
// function to hold and no redirect to send the buyer through. The shelf
|
|
7909
|
+
// absorbed that too, as a capability the checkout reads rather than a branch
|
|
7910
|
+
// it hard-codes. NeoChef makes the same point about the back office, and one
|
|
7911
|
+
// better: its provider is another Fayz app, and it still fits without a
|
|
7912
|
+
// special case.
|
|
7085
7913
|
connectors: [
|
|
7086
7914
|
melhorEnvioConnectorDef,
|
|
7087
7915
|
appmaxConnectorDef,
|
|
7088
7916
|
abacatePayConnectorDef,
|
|
7089
7917
|
stripeConnectConnectorDef,
|
|
7090
7918
|
vindiConnectorDef,
|
|
7919
|
+
mercadoPagoConnectorDef,
|
|
7091
7920
|
blingConnectorDef,
|
|
7092
7921
|
neoChefConnectorDef
|
|
7093
7922
|
],
|
|
7094
|
-
// What having a store makes plugable — the
|
|
7923
|
+
// What having a store makes plugable — the ones above plus the ones still
|
|
7095
7924
|
// to be built. Carried over from the Fayz plugin page's "Integra com" row,
|
|
7096
7925
|
// minus Nuvemshop: the platform catalogue has no row for it, and a slug the
|
|
7097
7926
|
// directory cannot back is refused rather than shown.
|
|
7098
|
-
integrations: ["shopify", "stripe"
|
|
7927
|
+
integrations: ["shopify", "stripe"]
|
|
7099
7928
|
};
|
|
7100
7929
|
}
|
|
7101
7930
|
|
|
7102
|
-
export { ABACATEPAY_CONNECTOR_ID, ABACATEPAY_LATENCY_BUDGET_MS, APPMAX_CONNECTOR_ID, APPMAX_LATENCY_BUDGET_MS, BLING_CONNECTOR_ID, BLING_MARKETPLACE_SOURCE, BLING_MAX_WINDOW_MS, BLING_PAGE_SIZE, BLING_RATE_LIMIT, BLING_SAMPLE_SIZE, BLING_SIGNATURE_HEADER, BLING_TOKEN_LEAD_MS, BLING_WEBHOOK_DEADLINE_MS, BLING_WINDOW_OVERLAP_MS, MELHOR_ENVIO_CONNECTOR_ID, MELHOR_ENVIO_LATENCY_BUDGET_MS, NEOCHEF_CONNECTOR_ID, NEOCHEF_CURSOR_OVERLAP_MS, NEOCHEF_FUNCTION, NEOCHEF_MARK, NEOCHEF_PAGE_SIZE, NEOCHEF_RATE_LIMIT, NEOCHEF_SAMPLE_SIZE, NEOCHEF_TOKEN_LEAD_MS, STRIPE_CONNECT_CONNECTOR_ID, STRIPE_CONNECT_LATENCY_BUDGET_MS, VINDI_CONNECTOR_ID, VINDI_LATENCY_BUDGET_MS, abacatePayConnectorDef, acceptWebhook, advanceNeoChefCursor, appmaxConnectorDef, blingConnectionStore, blingConnectorDef, buildShopAnalyticsCards, createShopPlugin, firstWindow, listOrderDeliveries2 as listOrderDeliveries, melhorEnvioConnectorDef, neoChefConnectionStore, neoChefConnectorDef, orphanConnection, pendingOrders2 as pendingOrders, pullCatalogue2 as pullCatalogue, pullProducts, pushOrders2 as pushOrders, reconcile2 as reconcile, routeCallback, sampleProducts, stripeConnectConnectorDef, testNeoChef, toMirroredNeoChefProduct, toMirroredProduct, toNeoChefOrder, vindiConnectorDef, windowFor };
|
|
7931
|
+
export { ABACATEPAY_CONNECTOR_ID, ABACATEPAY_LATENCY_BUDGET_MS, APPMAX_CONNECTOR_ID, APPMAX_LATENCY_BUDGET_MS, BLING_CONNECTOR_ID, BLING_MARKETPLACE_SOURCE, BLING_MAX_WINDOW_MS, BLING_PAGE_SIZE, BLING_RATE_LIMIT, BLING_SAMPLE_SIZE, BLING_SIGNATURE_HEADER, BLING_TOKEN_LEAD_MS, BLING_WEBHOOK_DEADLINE_MS, BLING_WINDOW_OVERLAP_MS, MELHOR_ENVIO_CONNECTOR_ID, MELHOR_ENVIO_LATENCY_BUDGET_MS, MERCADOPAGO_CONNECTOR_ID, MERCADOPAGO_LATENCY_BUDGET_MS, NEOCHEF_CONNECTOR_ID, NEOCHEF_CURSOR_OVERLAP_MS, NEOCHEF_FUNCTION, NEOCHEF_MARK, NEOCHEF_PAGE_SIZE, NEOCHEF_RATE_LIMIT, NEOCHEF_SAMPLE_SIZE, NEOCHEF_TOKEN_LEAD_MS, STRIPE_CONNECT_CONNECTOR_ID, STRIPE_CONNECT_LATENCY_BUDGET_MS, VINDI_CONNECTOR_ID, VINDI_LATENCY_BUDGET_MS, abacatePayConnectorDef, acceptWebhook, advanceNeoChefCursor, appmaxConnectorDef, blingConnectionStore, blingConnectorDef, buildShopAnalyticsCards, createShopPlugin, firstWindow, listOrderDeliveries2 as listOrderDeliveries, melhorEnvioConnectorDef, mercadoPagoConnectorDef, neoChefConnectionStore, neoChefConnectorDef, orphanConnection, pendingOrders2 as pendingOrders, pullCatalogue2 as pullCatalogue, pullProducts, pushOrders2 as pushOrders, reconcile2 as reconcile, routeCallback, sampleProducts, stripeConnectConnectorDef, testNeoChef, toMirroredNeoChefProduct, toMirroredProduct, toNeoChefOrder, vindiConnectorDef, windowFor };
|
|
7103
7932
|
//# sourceMappingURL=index.js.map
|
|
7104
7933
|
//# sourceMappingURL=index.js.map
|