@fayz-ai/plugin-shop 0.11.1 → 0.12.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-YB5ZWECJ.js → ShopPage-VVLOH3RF.js} +67 -28
- package/dist/ShopPage-VVLOH3RF.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +702 -152
- package/dist/index.js.map +1 -1
- package/dist/integrations/abacatepay/connectorDef.d.ts.map +1 -1
- package/dist/integrations/abacatepay/data.d.ts +40 -0
- package/dist/integrations/abacatepay/data.d.ts.map +1 -0
- package/dist/integrations/abacatepay/limits.d.ts +26 -0
- package/dist/integrations/abacatepay/limits.d.ts.map +1 -0
- package/dist/integrations/abacatepay/sync.d.ts +88 -0
- package/dist/integrations/abacatepay/sync.d.ts.map +1 -0
- package/dist/integrations/neochef/connectorDef.d.ts +12 -0
- package/dist/integrations/neochef/connectorDef.d.ts.map +1 -0
- package/dist/integrations/neochef/data.d.ts +54 -0
- package/dist/integrations/neochef/data.d.ts.map +1 -0
- package/dist/integrations/neochef/index.d.ts +8 -0
- package/dist/integrations/neochef/index.d.ts.map +1 -0
- package/dist/integrations/neochef/limits.d.ts +26 -0
- package/dist/integrations/neochef/limits.d.ts.map +1 -0
- package/dist/integrations/neochef/mapping.d.ts +31 -0
- package/dist/integrations/neochef/mapping.d.ts.map +1 -0
- package/dist/integrations/neochef/mark.d.ts +21 -0
- package/dist/integrations/neochef/mark.d.ts.map +1 -0
- package/dist/integrations/neochef/types.d.ts +71 -0
- package/dist/integrations/neochef/types.d.ts.map +1 -0
- package/dist/integrations/vindi/connectorDef.d.ts +25 -6
- package/dist/integrations/vindi/connectorDef.d.ts.map +1 -1
- package/dist/settings/PackagesSettings.d.ts.map +1 -1
- package/dist/views/ContentPage.d.ts.map +1 -1
- package/dist/views/NewOrderCharge.d.ts +14 -2
- package/dist/views/NewOrderCharge.d.ts.map +1 -1
- package/dist/views/NewOrderPage.d.ts.map +1 -1
- package/dist/views/new-order-rules.d.ts +10 -1
- package/dist/views/new-order-rules.d.ts.map +1 -1
- package/dist/views/productEntity.d.ts.map +1 -1
- package/package.json +12 -11
- package/dist/ShopPage-YB5ZWECJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import { shopCustomerLookup, resolve, invalidateProductLists, listProductsDetail
|
|
|
2
2
|
import React8, { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, 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, ClientOrdersTab, createClientOrdersProvider, 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, ChevronLeft, ChevronRight,
|
|
7
|
-
import {
|
|
5
|
+
import { fayzConnectorOAuth, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo, ClientOrdersTab, createClientOrdersProvider, 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, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info } from 'lucide-react';
|
|
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
9
|
import { advanceWindow, createRateLimiter, sliceWindow, paginate, reconcileMirror, serializeCursor } from '@fayz-ai/core/connector-runtime';
|
|
10
10
|
|
|
@@ -309,7 +309,7 @@ function AxisEditor({ axis, index, count, disabled, onChange, onMove, onRemove }
|
|
|
309
309
|
onChange({ ...axis, values: merged });
|
|
310
310
|
setDraft("");
|
|
311
311
|
}
|
|
312
|
-
return /* @__PURE__ */ jsxs("li", { className: "rounded-
|
|
312
|
+
return /* @__PURE__ */ jsxs("li", { className: "rounded-card border bg-card p-3", children: [
|
|
313
313
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
314
314
|
/* @__PURE__ */ jsx(
|
|
315
315
|
"input",
|
|
@@ -1035,9 +1035,11 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
|
|
|
1035
1035
|
currencyLocale: "pt-BR",
|
|
1036
1036
|
showInTable: true,
|
|
1037
1037
|
sortable: true,
|
|
1038
|
-
|
|
1038
|
+
// O "de" riscado vai EMBAIXO do preço em vigor, não ao lado: lado a lado
|
|
1039
|
+
// as duas quantias competem na mesma linha e o olho lê a maior primeiro.
|
|
1040
|
+
renderCell: (v, row) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end leading-tight", children: [
|
|
1039
1041
|
/* @__PURE__ */ jsx("span", { className: "font-medium", children: money(v, row.currency) }),
|
|
1040
|
-
row.compareAtPrice > row.price && /* @__PURE__ */ jsx("span", { className: "
|
|
1042
|
+
row.compareAtPrice > row.price && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground line-through", children: money(row.compareAtPrice, row.currency) })
|
|
1041
1043
|
] })
|
|
1042
1044
|
},
|
|
1043
1045
|
{
|
|
@@ -2674,7 +2676,7 @@ function PaymentsSettings({ provider }) {
|
|
|
2674
2676
|
] }),
|
|
2675
2677
|
/* @__PURE__ */ jsx(AddConnectorButton, { onClick: () => setAdding(true) })
|
|
2676
2678
|
] }),
|
|
2677
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-
|
|
2679
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card shadow-sm", children: payment.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-muted-foreground", children: "Nenhum provedor de pagamento dispon\xEDvel para este app ainda." }) : /* @__PURE__ */ jsxs("ul", { className: "divide-y", role: "radiogroup", "aria-label": "Quem cobra", children: [
|
|
2678
2680
|
payment.map((connector) => /* @__PURE__ */ jsx(
|
|
2679
2681
|
PaymentProviderRow,
|
|
2680
2682
|
{
|
|
@@ -2861,7 +2863,7 @@ function PackagesSettings({ basePath = shippingRegistryPath(PACKAGES_REGISTRY) }
|
|
|
2861
2863
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Embalagens" }),
|
|
2862
2864
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: packagesRegistry.description })
|
|
2863
2865
|
] }),
|
|
2864
|
-
/* @__PURE__ */ jsx(RegistryCrudView, { registry: packagesRegistry, basePath })
|
|
2866
|
+
/* @__PURE__ */ jsx(RegistryCrudView, { registry: packagesRegistry, basePath, embedded: true })
|
|
2865
2867
|
] });
|
|
2866
2868
|
}
|
|
2867
2869
|
function HandlingSettings({ settings, provider }) {
|
|
@@ -2892,7 +2894,7 @@ function HandlingSettings({ settings, provider }) {
|
|
|
2892
2894
|
] }),
|
|
2893
2895
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Um perfil \xE9 um grupo de produtos com zonas pr\xF3prias \u2014 refrigerados, fr\xE1geis, volumosos, sob encomenda. Um perfil com s\xF3 as suas zonas e o gateway desligado \xE9 como se diz \xABisto n\xE3o vai de transportadora\xBB. Produto sem perfil usa o geral, que \xE9 o de todos hoje." })
|
|
2894
2896
|
] }),
|
|
2895
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-
|
|
2897
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
2896
2898
|
"button",
|
|
2897
2899
|
{
|
|
2898
2900
|
type: "button",
|
|
@@ -3178,7 +3180,7 @@ function ShippingSettings({ provider }) {
|
|
|
3178
3180
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "De onde voc\xEA despacha" }),
|
|
3179
3181
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Os locais que guardam estoque e despacham pedidos. O CEP do local padr\xE3o \xE9 a origem que a transportadora usa para cotar o frete \u2014 \xE9 aqui que ele se cadastra, uma vez. Cada local pode ter o seu pr\xF3prio despachante ou seguir a decis\xE3o da loja." })
|
|
3180
3182
|
] }),
|
|
3181
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-
|
|
3183
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
3182
3184
|
"button",
|
|
3183
3185
|
{
|
|
3184
3186
|
type: "button",
|
|
@@ -3206,7 +3208,7 @@ function ShippingSettings({ provider }) {
|
|
|
3206
3208
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Zonas de entrega" }),
|
|
3207
3209
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Faixas de CEP com pre\xE7o, prazo e frete gr\xE1tis pr\xF3prios. Um CEP fora de todas as zonas cai no pre\xE7o base acima; se a loja n\xE3o tiver pre\xE7o base nem zona que o cubra, o checkout diz que n\xE3o entrega ali. Uma faixa tamb\xE9m pode ser uma recusa \u2014 \xABn\xE3o entrego aqui\xBB \u2014 e ela vence qualquer zona e qualquer gateway." })
|
|
3208
3210
|
] }),
|
|
3209
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-
|
|
3211
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
|
|
3210
3212
|
"button",
|
|
3211
3213
|
{
|
|
3212
3214
|
type: "button",
|
|
@@ -3260,7 +3262,7 @@ function ShippingSettings({ provider }) {
|
|
|
3260
3262
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Conectado, ele cota o frete para o CEP do comprador em vez de a loja adivinhar. O pre\xE7o cotado \xE9 gravado no servidor e \xE9 ele que o pedido cobra \u2014 o navegador escolhe entre op\xE7\xF5es, nunca informa um valor. Quais transportadoras aparecem vem da conta do gateway, n\xE3o desta lista." }),
|
|
3261
3263
|
!carriersConnected && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Nenhum conectado \u2014 a loja entrega por conta pr\xF3pria, pelo pre\xE7o base e pelas zonas acima. Conectar \xE9 opcional e pode ficar para depois." })
|
|
3262
3264
|
] }),
|
|
3263
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-
|
|
3265
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-card border bg-card shadow-sm", children: /* @__PURE__ */ jsx("ul", { className: "divide-y", children: carriers.map((connector) => /* @__PURE__ */ jsx(ConnectorRow, { connector, onOpen: openJourney }, connector.id)) }) })
|
|
3264
3266
|
] }),
|
|
3265
3267
|
/* @__PURE__ */ jsx(HandlingSettings, { settings: storeSettings, provider }),
|
|
3266
3268
|
/* @__PURE__ */ jsx("div", { className: "flex min-h-5 items-center gap-1.5 px-1 text-xs text-muted-foreground", children: error ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-destructive", children: [
|
|
@@ -3628,6 +3630,39 @@ var appmaxConnectorDef = {
|
|
|
3628
3630
|
disconnect: () => store2.disconnect(),
|
|
3629
3631
|
ExtraPanel: AppmaxExtraPanel
|
|
3630
3632
|
};
|
|
3633
|
+
var ABACATEPAY_FUNCTION = "abacatepay-sync";
|
|
3634
|
+
function client() {
|
|
3635
|
+
const supabase = getSupabaseClientOptional();
|
|
3636
|
+
if (!supabase) throw new Error("Sem conex\xE3o com o banco para falar com a AbacatePay.");
|
|
3637
|
+
return supabase;
|
|
3638
|
+
}
|
|
3639
|
+
async function invoke(body) {
|
|
3640
|
+
const tenantId = getShopTenantId();
|
|
3641
|
+
if (!tenantId) throw new Error("Sem loja selecionada para sincronizar com a AbacatePay.");
|
|
3642
|
+
const runtimeToken = await connectorRuntimeToken({ tenantId });
|
|
3643
|
+
const { data, error } = await client().functions.invoke(ABACATEPAY_FUNCTION, {
|
|
3644
|
+
body: { ...body, tenantId },
|
|
3645
|
+
headers: { [CONNECTOR_RUNTIME_TOKEN_HEADER]: runtimeToken }
|
|
3646
|
+
});
|
|
3647
|
+
if (!error) return data;
|
|
3648
|
+
const status = Number(error?.context?.status ?? 0);
|
|
3649
|
+
let message2 = "";
|
|
3650
|
+
try {
|
|
3651
|
+
const payload = await error?.context?.json?.();
|
|
3652
|
+
message2 = String(payload?.message ?? payload?.error ?? "").trim();
|
|
3653
|
+
} catch {
|
|
3654
|
+
}
|
|
3655
|
+
if (!message2 && status === 404) {
|
|
3656
|
+
message2 = "Esta loja n\xE3o tem uma conta AbacatePay conectada.";
|
|
3657
|
+
}
|
|
3658
|
+
throw new Error(message2 || error.message);
|
|
3659
|
+
}
|
|
3660
|
+
function pushCatalogue() {
|
|
3661
|
+
return invoke({ action: "push_products" });
|
|
3662
|
+
}
|
|
3663
|
+
function reconcileCatalogue() {
|
|
3664
|
+
return invoke({ action: "reconcile" });
|
|
3665
|
+
}
|
|
3631
3666
|
var ABACATEPAY_CONNECTOR_ID = "abacatepay";
|
|
3632
3667
|
var ABACATEPAY_LATENCY_BUDGET_MS = 6e3;
|
|
3633
3668
|
var ABACATEPAY_FIELDS = [
|
|
@@ -3647,8 +3682,13 @@ var ABACATEPAY_SETTINGS = [
|
|
|
3647
3682
|
type: "multi-select",
|
|
3648
3683
|
options: [
|
|
3649
3684
|
{ value: "PIX", label: "Pix" },
|
|
3650
|
-
{ value: "BOLETO", label: "Boleto" }
|
|
3651
|
-
|
|
3685
|
+
{ value: "BOLETO", label: "Boleto" }
|
|
3686
|
+
// Credit card does NOT belong here. AbacatePay discontinued it for new
|
|
3687
|
+
// clients and new integrations, so offering the checkbox would let a
|
|
3688
|
+
// merchant switch on a method the acquirer refuses — and the first to find
|
|
3689
|
+
// out would be a buyer. `CARD` stays in METHOD_LABELS because an older
|
|
3690
|
+
// connection may have the option stored and its row still has to describe
|
|
3691
|
+
// itself.
|
|
3652
3692
|
],
|
|
3653
3693
|
requiredToOperate: true
|
|
3654
3694
|
}
|
|
@@ -3663,6 +3703,97 @@ function methodsDetail2(value) {
|
|
|
3663
3703
|
const labels = list.map((v) => METHOD_LABELS2[String(v).trim()] ?? String(v).trim()).filter(Boolean);
|
|
3664
3704
|
return labels.length > 0 ? labels.join(", ") : void 0;
|
|
3665
3705
|
}
|
|
3706
|
+
function CatalogueSync() {
|
|
3707
|
+
const [busy, setBusy] = useState(null);
|
|
3708
|
+
const [result, setResult] = useState(null);
|
|
3709
|
+
const [error, setError] = useState(null);
|
|
3710
|
+
const run = async (kind) => {
|
|
3711
|
+
setBusy(kind);
|
|
3712
|
+
setError(null);
|
|
3713
|
+
try {
|
|
3714
|
+
setResult(await (kind === "push" ? pushCatalogue() : reconcileCatalogue()));
|
|
3715
|
+
} catch (caught) {
|
|
3716
|
+
setError(caught instanceof Error ? caught.message : "N\xE3o conseguimos sincronizar agora.");
|
|
3717
|
+
} finally {
|
|
3718
|
+
setBusy(null);
|
|
3719
|
+
}
|
|
3720
|
+
};
|
|
3721
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
3722
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3723
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Cat\xE1logo" }),
|
|
3724
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: "O link de pagamento \xE9 aberto na p\xE1gina da AbacatePay, e ela cobra por produtos que precisam existir l\xE1. Envie os seus antes de mandar um link para um cliente." })
|
|
3725
|
+
] }),
|
|
3726
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
3727
|
+
/* @__PURE__ */ jsx(
|
|
3728
|
+
"button",
|
|
3729
|
+
{
|
|
3730
|
+
type: "button",
|
|
3731
|
+
onClick: () => {
|
|
3732
|
+
void run("push");
|
|
3733
|
+
},
|
|
3734
|
+
disabled: busy !== null,
|
|
3735
|
+
className: "inline-flex min-h-9 items-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
|
|
3736
|
+
children: busy === "push" ? "Enviando\u2026" : "Enviar produtos"
|
|
3737
|
+
}
|
|
3738
|
+
),
|
|
3739
|
+
/* @__PURE__ */ jsx(
|
|
3740
|
+
"button",
|
|
3741
|
+
{
|
|
3742
|
+
type: "button",
|
|
3743
|
+
onClick: () => {
|
|
3744
|
+
void run("reconcile");
|
|
3745
|
+
},
|
|
3746
|
+
disabled: busy !== null,
|
|
3747
|
+
className: "inline-flex min-h-9 items-center rounded-md border px-3 text-xs font-medium transition hover:bg-muted/50 disabled:opacity-60",
|
|
3748
|
+
children: busy === "reconcile" ? "Conferindo\u2026" : "Conferir pre\xE7os"
|
|
3749
|
+
}
|
|
3750
|
+
)
|
|
3751
|
+
] }),
|
|
3752
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }),
|
|
3753
|
+
result && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 rounded-md border bg-muted/30 p-3 text-xs", children: [
|
|
3754
|
+
/* @__PURE__ */ jsxs("p", { className: "text-foreground", children: [
|
|
3755
|
+
result.pushed > 0 ? `${result.pushed} produto${result.pushed > 1 ? "s" : ""} enviado${result.pushed > 1 ? "s" : ""}.` : "Nenhum produto novo para enviar.",
|
|
3756
|
+
" ",
|
|
3757
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
3758
|
+
result.unchanged,
|
|
3759
|
+
" j\xE1 ",
|
|
3760
|
+
result.unchanged === 1 ? "estava" : "estavam",
|
|
3761
|
+
" l\xE1, de ",
|
|
3762
|
+
result.total,
|
|
3763
|
+
"."
|
|
3764
|
+
] })
|
|
3765
|
+
] }),
|
|
3766
|
+
result.hasMore && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Ainda h\xE1 produtos na fila \u2014 clique novamente para continuar." }),
|
|
3767
|
+
result.withoutImage?.length > 0 && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
3768
|
+
result.withoutImage.length,
|
|
3769
|
+
" produto",
|
|
3770
|
+
result.withoutImage.length > 1 ? "s foram enviados" : " foi enviado",
|
|
3771
|
+
" sem foto \u2014 a AbacatePay recusou a imagem. O produto vende normalmente; s\xF3 aparece sem imagem na p\xE1gina dela."
|
|
3772
|
+
] }),
|
|
3773
|
+
result.skipped?.length > 0 && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
|
|
3774
|
+
result.skipped.length,
|
|
3775
|
+
" ignorado",
|
|
3776
|
+
result.skipped.length > 1 ? "s" : "",
|
|
3777
|
+
" por estar sem nome ou com pre\xE7o zerado."
|
|
3778
|
+
] }),
|
|
3779
|
+
result.failed?.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-destructive", children: [
|
|
3780
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
3781
|
+
result.failed.length,
|
|
3782
|
+
" recusado",
|
|
3783
|
+
result.failed.length > 1 ? "s" : "",
|
|
3784
|
+
" pela AbacatePay:"
|
|
3785
|
+
] }),
|
|
3786
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-0.5 list-disc pl-4", children: result.failed.slice(0, 3).map((f) => /* @__PURE__ */ jsx("li", { children: f.error }, f.productId)) })
|
|
3787
|
+
] }),
|
|
3788
|
+
result.drifted && result.drifted.length > 0 && /* @__PURE__ */ jsxs("p", { className: "text-amber-600 dark:text-amber-500", children: [
|
|
3789
|
+
result.drifted.length,
|
|
3790
|
+
" produto",
|
|
3791
|
+
result.drifted.length > 1 ? "s mudaram" : " mudou",
|
|
3792
|
+
" de pre\xE7o aqui depois de enviado. A AbacatePay n\xE3o permite alterar um produto j\xE1 criado, ent\xE3o o link ainda cobra o pre\xE7o antigo \u2014 refa\xE7a o produto l\xE1 se o novo valor precisa valer."
|
|
3793
|
+
] })
|
|
3794
|
+
] })
|
|
3795
|
+
] });
|
|
3796
|
+
}
|
|
3666
3797
|
function AbacatePayExtraPanel() {
|
|
3667
3798
|
const [connected, setConnected] = useState(false);
|
|
3668
3799
|
const [loaded, setLoaded] = useState(false);
|
|
@@ -3679,17 +3810,20 @@ function AbacatePayExtraPanel() {
|
|
|
3679
3810
|
};
|
|
3680
3811
|
}, []);
|
|
3681
3812
|
if (!loaded || !connected) return null;
|
|
3682
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
3683
|
-
/* @__PURE__ */ jsx(
|
|
3684
|
-
/* @__PURE__ */
|
|
3685
|
-
|
|
3813
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 border-t pt-4", children: [
|
|
3814
|
+
/* @__PURE__ */ jsx(CatalogueSync, {}),
|
|
3815
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 text-xs text-muted-foreground", children: [
|
|
3816
|
+
/* @__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. Cart\xE3o de cr\xE9dito a AbacatePay encerrou para contas novas \u2014 para vender no cart\xE3o, conecte outro provedor em Pagamentos." }),
|
|
3817
|
+
/* @__PURE__ */ jsx("p", { children: "Quem decide se a cobran\xE7a \xE9 de verdade \xE9 a chave. Uma chave de teste simula o pagamento e n\xE3o movimenta dinheiro; a de produ\xE7\xE3o cobra o cliente. \xC9 a mesma tela para as duas, ent\xE3o confira no painel da AbacatePay qual chave voc\xEA colou aqui." }),
|
|
3818
|
+
/* @__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.' })
|
|
3819
|
+
] })
|
|
3686
3820
|
] });
|
|
3687
3821
|
}
|
|
3688
3822
|
var abacatePayConnectorDef = {
|
|
3689
3823
|
id: ABACATEPAY_CONNECTOR_ID,
|
|
3690
3824
|
hostPluginId: "shop",
|
|
3691
3825
|
name: "AbacatePay",
|
|
3692
|
-
description: "Pix
|
|
3826
|
+
description: "Pix e boleto com uma chave s\xF3, sem contrato de adquirente e sem tirar o cliente da loja.",
|
|
3693
3827
|
icon: "CreditCard",
|
|
3694
3828
|
category: "payments",
|
|
3695
3829
|
// One pasted key, no consent screen: AbacatePay has no OAuth and no app to
|
|
@@ -3738,7 +3872,7 @@ var abacatePayConnectorDef = {
|
|
|
3738
3872
|
invokedAt: [
|
|
3739
3873
|
{
|
|
3740
3874
|
key: "shop.checkout.session_open",
|
|
3741
|
-
label: "O cliente finaliza a compra e a cobran\xE7a \xE9 aberta na AbacatePay (Pix
|
|
3875
|
+
label: "O cliente finaliza a compra e a cobran\xE7a \xE9 aberta na AbacatePay (Pix ou boleto)",
|
|
3742
3876
|
touches: ["order", "payment"]
|
|
3743
3877
|
},
|
|
3744
3878
|
{
|
|
@@ -3926,26 +4060,80 @@ var VINDI_ENVIRONMENT_SETTING = {
|
|
|
3926
4060
|
],
|
|
3927
4061
|
requiredToOperate: true
|
|
3928
4062
|
};
|
|
4063
|
+
var VINDI_CARD_METHOD = "3";
|
|
3929
4064
|
var VINDI_SETTINGS = [
|
|
3930
4065
|
VINDI_ENVIRONMENT_SETTING,
|
|
3931
4066
|
{
|
|
3932
4067
|
key: "methods",
|
|
3933
4068
|
label: "Formas de pagamento",
|
|
3934
|
-
description: "O que o cliente pode escolher
|
|
4069
|
+
description: "O que o cliente pode escolher. O cart\xE3o \xE9 pago na p\xE1gina segura da Vindi.",
|
|
3935
4070
|
type: "multi-select",
|
|
3936
4071
|
options: [
|
|
3937
4072
|
{ value: "27", label: "Pix" },
|
|
3938
4073
|
{ value: "28", label: "Bolepix (boleto com QR)" },
|
|
3939
|
-
{ value: "6", label: "Boleto" }
|
|
4074
|
+
{ value: "6", label: "Boleto" },
|
|
4075
|
+
{ value: VINDI_CARD_METHOD, label: "Cart\xE3o de cr\xE9dito (p\xE1gina da Vindi)" }
|
|
3940
4076
|
],
|
|
3941
4077
|
requiredToOperate: true
|
|
4078
|
+
},
|
|
4079
|
+
/**
|
|
4080
|
+
* The seller's postcode, which Vindi's cart endpoint refuses to open without.
|
|
4081
|
+
*
|
|
4082
|
+
* NOT `requiredToOperate` in the static list — a store selling Pix has never
|
|
4083
|
+
* needed it, and turning it into a requirement would put every working
|
|
4084
|
+
* connection into "falta configurar" the day this version ships. It becomes
|
|
4085
|
+
* required the moment card is enabled: see `requiredSettingsFor`.
|
|
4086
|
+
*/
|
|
4087
|
+
{
|
|
4088
|
+
key: "sellerPostalCode",
|
|
4089
|
+
label: "CEP da loja",
|
|
4090
|
+
description: "De onde a loja vende. A Vindi exige esse dado para abrir a p\xE1gina de cart\xE3o.",
|
|
4091
|
+
type: "text",
|
|
4092
|
+
placeholder: "01310-100",
|
|
4093
|
+
pattern: "\\d{5}-?\\d{3}",
|
|
4094
|
+
patternHint: "Oito d\xEDgitos, com ou sem h\xEDfen."
|
|
4095
|
+
},
|
|
4096
|
+
/**
|
|
4097
|
+
* How many instalments Vindi's page may offer.
|
|
4098
|
+
*
|
|
4099
|
+
* A `select` rather than free text because it is a number with a ceiling the
|
|
4100
|
+
* merchant does not know: Vindi refuses a split above what the account was
|
|
4101
|
+
* accredited for, and a typed "24" would be a checkout that fails at the last
|
|
4102
|
+
* click. The default is 1 — a store that never thought about instalments
|
|
4103
|
+
* should not discover it is financing purchases.
|
|
4104
|
+
*/
|
|
4105
|
+
{
|
|
4106
|
+
key: "maxSplit",
|
|
4107
|
+
label: "Parcelas no cart\xE3o",
|
|
4108
|
+
description: "Em quantas vezes o cliente pode dividir. Depende do que a Vindi liberou para a conta.",
|
|
4109
|
+
type: "select",
|
|
4110
|
+
options: [
|
|
4111
|
+
{ value: "1", label: "\xC0 vista" },
|
|
4112
|
+
{ value: "2", label: "At\xE9 2x" },
|
|
4113
|
+
{ value: "3", label: "At\xE9 3x" },
|
|
4114
|
+
{ value: "6", label: "At\xE9 6x" },
|
|
4115
|
+
{ value: "10", label: "At\xE9 10x" },
|
|
4116
|
+
{ value: "12", label: "At\xE9 12x" }
|
|
4117
|
+
],
|
|
4118
|
+
defaultValue: "1"
|
|
3942
4119
|
}
|
|
3943
4120
|
];
|
|
4121
|
+
function requiredSettingsFor(settings) {
|
|
4122
|
+
const raw = settings?.methods;
|
|
4123
|
+
const list = Array.isArray(raw) ? raw : String(raw ?? "").split(",");
|
|
4124
|
+
const hasCard = list.map((v) => String(v).trim()).includes(VINDI_CARD_METHOD);
|
|
4125
|
+
return VINDI_SETTINGS.map((setting) => setting.key === "sellerPostalCode" && hasCard ? { ...setting, requiredToOperate: true } : setting);
|
|
4126
|
+
}
|
|
3944
4127
|
var store5 = createConnectionStore(VINDI_CONNECTOR_ID, {
|
|
3945
4128
|
fields: VINDI_FIELDS,
|
|
3946
4129
|
settings: VINDI_SETTINGS
|
|
3947
4130
|
});
|
|
3948
|
-
var METHOD_LABELS4 = {
|
|
4131
|
+
var METHOD_LABELS4 = {
|
|
4132
|
+
"27": "Pix",
|
|
4133
|
+
"28": "Bolepix",
|
|
4134
|
+
"6": "Boleto",
|
|
4135
|
+
[VINDI_CARD_METHOD]: "Cart\xE3o"
|
|
4136
|
+
};
|
|
3949
4137
|
var ENVIRONMENT_LABELS = { sandbox: "Sandbox", production: "Produ\xE7\xE3o" };
|
|
3950
4138
|
function connectionDetail(settings) {
|
|
3951
4139
|
const environment = ENVIRONMENT_LABELS[String(settings?.environment ?? "")];
|
|
@@ -3972,7 +4160,12 @@ function VindiExtraPanel() {
|
|
|
3972
4160
|
}, []);
|
|
3973
4161
|
if (!loaded) return null;
|
|
3974
4162
|
return /* @__PURE__ */ jsx("div", { className: "space-y-2 border-t pt-4 text-xs text-muted-foreground", children: connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3975
|
-
/* @__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.
|
|
4163
|
+
/* @__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." }),
|
|
4164
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
4165
|
+
"Voc\xEA tamb\xE9m pode gerar um ",
|
|
4166
|
+
/* @__PURE__ */ jsx("strong", { children: "link de pagamento" }),
|
|
4167
|
+
" 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."
|
|
4168
|
+
] }),
|
|
3976
4169
|
/* @__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." }),
|
|
3977
4170
|
/* @__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.' })
|
|
3978
4171
|
] }) : /* @__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." }) });
|
|
@@ -3981,7 +4174,7 @@ var vindiConnectorDef = {
|
|
|
3981
4174
|
id: VINDI_CONNECTOR_ID,
|
|
3982
4175
|
hostPluginId: "shop",
|
|
3983
4176
|
name: "Vindi",
|
|
3984
|
-
description: "Pix e
|
|
4177
|
+
description: "Pix, boleto e cart\xE3o pela Vindi. A loja recebe na conta dela; o cart\xE3o \xE9 digitado na p\xE1gina segura da Vindi.",
|
|
3985
4178
|
icon: "CreditCard",
|
|
3986
4179
|
// Declared rather than borrowed from the catalogue, because the catalogue row
|
|
3987
4180
|
// is a change in another repo that has to be seeded before it exists anywhere:
|
|
@@ -4019,7 +4212,7 @@ var vindiConnectorDef = {
|
|
|
4019
4212
|
kind: "data",
|
|
4020
4213
|
resource: "payment",
|
|
4021
4214
|
actions: ["write"],
|
|
4022
|
-
reason: "Registrar aqui a cobran\xE7a aberta e a confirma\xE7\xE3o do pagamento. Dados do cart\xE3o n\xE3o passam por este app.",
|
|
4215
|
+
reason: "Registrar aqui a cobran\xE7a aberta e a confirma\xE7\xE3o do pagamento. Dados do cart\xE3o n\xE3o passam por este app \u2014 s\xE3o digitados na p\xE1gina da Vindi.",
|
|
4023
4216
|
sensitive: true
|
|
4024
4217
|
}
|
|
4025
4218
|
],
|
|
@@ -4033,7 +4226,7 @@ var vindiConnectorDef = {
|
|
|
4033
4226
|
invokedAt: [
|
|
4034
4227
|
{
|
|
4035
4228
|
key: "shop.checkout.session_open",
|
|
4036
|
-
label: "O cliente finaliza a compra e a cobran\xE7a \xE9 aberta na Vindi (Pix ou
|
|
4229
|
+
label: "O cliente finaliza a compra e a cobran\xE7a \xE9 aberta na Vindi (Pix, boleto ou a p\xE1gina de cart\xE3o)",
|
|
4037
4230
|
touches: ["order", "payment"]
|
|
4038
4231
|
},
|
|
4039
4232
|
{
|
|
@@ -4052,7 +4245,10 @@ var vindiConnectorDef = {
|
|
|
4052
4245
|
connected: !!connection?.active,
|
|
4053
4246
|
detail: connectionDetail(connection?.settings),
|
|
4054
4247
|
plane: "request",
|
|
4055
|
-
|
|
4248
|
+
// Judged against what THIS connection needs, not against a constant: a
|
|
4249
|
+
// store that ticked card owes a postcode, and one that did not owes
|
|
4250
|
+
// nothing. See `requiredSettingsFor`.
|
|
4251
|
+
...settingsStatus(requiredSettingsFor(connection?.settings), connection)
|
|
4056
4252
|
}),
|
|
4057
4253
|
{ store: () => store5 }
|
|
4058
4254
|
),
|
|
@@ -4415,13 +4611,18 @@ function ContentPage({ resolveProvider }) {
|
|
|
4415
4611
|
file.id
|
|
4416
4612
|
)) });
|
|
4417
4613
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
4418
|
-
/* @__PURE__ */
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4614
|
+
supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(UploadButton, { uploading, onPick: handleUpload }) }),
|
|
4615
|
+
/* @__PURE__ */ jsx(
|
|
4616
|
+
PageTabs,
|
|
4617
|
+
{
|
|
4618
|
+
className: "mb-0",
|
|
4619
|
+
tabs: TABS,
|
|
4620
|
+
value: tab,
|
|
4621
|
+
onChange: setTab,
|
|
4622
|
+
"aria-label": "Arquivos",
|
|
4623
|
+
actions: caption ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: caption }) : void 0
|
|
4624
|
+
}
|
|
4625
|
+
),
|
|
4425
4626
|
!supported ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
|
|
4426
4627
|
/* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
|
|
4427
4628
|
/* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Armazenamento n\xE3o configurado" }),
|
|
@@ -4575,116 +4776,138 @@ function MediaDetail({
|
|
|
4575
4776
|
document.addEventListener("keydown", onKey);
|
|
4576
4777
|
return () => document.removeEventListener("keydown", onKey);
|
|
4577
4778
|
}, [onNavigate]);
|
|
4578
|
-
return
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
" de ",
|
|
4595
|
-
position.total
|
|
4596
|
-
] }),
|
|
4597
|
-
/* @__PURE__ */ jsx(
|
|
4598
|
-
"button",
|
|
4599
|
-
{
|
|
4600
|
-
type: "button",
|
|
4601
|
-
"aria-label": "Pr\xF3ximo arquivo",
|
|
4602
|
-
disabled: position.index === position.total - 1,
|
|
4603
|
-
onClick: () => onNavigate(1),
|
|
4604
|
-
className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
|
|
4605
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
4606
|
-
}
|
|
4607
|
-
)
|
|
4608
|
-
] }) }),
|
|
4609
|
-
/* @__PURE__ */ jsxs("div", { className: "fz-split", children: [
|
|
4610
|
-
/* @__PURE__ */ jsx(Card, { className: "flex min-h-[320px] items-center justify-center overflow-hidden bg-muted p-2", children: file.kind === "image" ? /* @__PURE__ */ jsx(
|
|
4611
|
-
"img",
|
|
4612
|
-
{
|
|
4613
|
-
src: file.url,
|
|
4614
|
-
alt: file.name,
|
|
4615
|
-
className: "max-h-[70vh] w-auto max-w-full object-contain",
|
|
4616
|
-
onLoad: (e) => {
|
|
4617
|
-
const img = e.currentTarget;
|
|
4618
|
-
if (img.naturalWidth) onMeasured({ width: img.naturalWidth, height: img.naturalHeight });
|
|
4779
|
+
return (
|
|
4780
|
+
// `fz-container` é o ponto de MEDIÇÃO: sem ele a container query de
|
|
4781
|
+
// `fz-split` nunca casa e as duas colunas ficam empilhadas para sempre,
|
|
4782
|
+
// por mais larga que a janela esteja.
|
|
4783
|
+
/* @__PURE__ */ jsxs("div", { className: "fz-container space-y-4", children: [
|
|
4784
|
+
/* @__PURE__ */ jsx(PageHeader, { title: file.name, parentLabel: "Conte\xFAdo", onBack }),
|
|
4785
|
+
position.total > 1 && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
4786
|
+
/* @__PURE__ */ jsx(
|
|
4787
|
+
"button",
|
|
4788
|
+
{
|
|
4789
|
+
type: "button",
|
|
4790
|
+
"aria-label": "Arquivo anterior",
|
|
4791
|
+
disabled: position.index === 0,
|
|
4792
|
+
onClick: () => onNavigate(-1),
|
|
4793
|
+
className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
|
|
4794
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
4619
4795
|
}
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
] }) }),
|
|
4626
|
-
/* @__PURE__ */ jsxs(Card, { className: "space-y-4 p-4", children: [
|
|
4627
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4628
|
-
/* @__PURE__ */ jsx("p", { className: "break-words font-medium", title: file.name, children: file.name }),
|
|
4629
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: /* @__PURE__ */ jsx(FileMeta, { file, dimensions }) }),
|
|
4630
|
-
isLowResolution(file, dimensions) && /* @__PURE__ */ jsxs("p", { className: "mt-2 rounded-md bg-warning/10 px-2 py-1.5 text-xs font-medium text-warning", children: [
|
|
4631
|
-
"Baixa resolu\xE7\xE3o \u2014 no zoom da loja essa foto fica borrada. Troque por uma de pelo menos ",
|
|
4632
|
-
LOW_RESOLUTION_PX,
|
|
4633
|
-
"px no lado maior."
|
|
4634
|
-
] })
|
|
4635
|
-
] }),
|
|
4636
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4637
|
-
/* @__PURE__ */ jsx("p", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Onde \xE9 usada" }),
|
|
4638
|
-
file.reference ? /* @__PURE__ */ jsx(RecordLink, { reference: file.reference, className: "mt-1 text-sm" }) : /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Biblioteca \u2014 este arquivo n\xE3o est\xE1 preso a nenhum registro." }),
|
|
4639
|
-
file.reference?.isCover && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "\xC9 a capa: \xE9 ela que aparece no cat\xE1logo, no carrinho e no pedido." })
|
|
4796
|
+
),
|
|
4797
|
+
/* @__PURE__ */ jsxs("span", { className: "px-1 tabular-nums", children: [
|
|
4798
|
+
position.index + 1,
|
|
4799
|
+
" de ",
|
|
4800
|
+
position.total
|
|
4640
4801
|
] }),
|
|
4641
|
-
/* @__PURE__ */
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4802
|
+
/* @__PURE__ */ jsx(
|
|
4803
|
+
"button",
|
|
4804
|
+
{
|
|
4805
|
+
type: "button",
|
|
4806
|
+
"aria-label": "Pr\xF3ximo arquivo",
|
|
4807
|
+
disabled: position.index === position.total - 1,
|
|
4808
|
+
onClick: () => onNavigate(1),
|
|
4809
|
+
className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
|
|
4810
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
4811
|
+
}
|
|
4812
|
+
)
|
|
4813
|
+
] }) }),
|
|
4814
|
+
/* @__PURE__ */ jsxs("div", { className: "fz-split", children: [
|
|
4815
|
+
/* @__PURE__ */ jsx(Card, { className: "flex min-h-[320px] items-center justify-center overflow-hidden bg-muted p-2", children: file.kind === "image" ? /* @__PURE__ */ jsx(
|
|
4816
|
+
"img",
|
|
4817
|
+
{
|
|
4818
|
+
src: file.url,
|
|
4819
|
+
alt: file.name,
|
|
4820
|
+
className: "max-h-[70vh] w-auto max-w-full object-contain",
|
|
4821
|
+
onLoad: (e) => {
|
|
4822
|
+
const img = e.currentTarget;
|
|
4823
|
+
if (img.naturalWidth) onMeasured({ width: img.naturalWidth, height: img.naturalHeight });
|
|
4824
|
+
}
|
|
4825
|
+
},
|
|
4826
|
+
file.url
|
|
4827
|
+
) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 p-12 text-muted-foreground", children: [
|
|
4828
|
+
/* @__PURE__ */ jsx(KindIcon, { kind: file.kind, className: "h-10 w-10" }),
|
|
4829
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: "Pr\xE9-visualiza\xE7\xE3o n\xE3o dispon\xEDvel" })
|
|
4830
|
+
] }) }),
|
|
4831
|
+
/* @__PURE__ */ jsxs(Card, { className: "space-y-4 p-4", children: [
|
|
4832
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4833
|
+
/* @__PURE__ */ jsx("p", { className: "break-words font-medium", title: file.name, children: file.name }),
|
|
4834
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: /* @__PURE__ */ jsx(FileMeta, { file, dimensions }) }),
|
|
4835
|
+
isLowResolution(file, dimensions) && /* @__PURE__ */ jsxs("p", { className: "mt-2 rounded-md bg-warning/10 px-2 py-1.5 text-xs font-medium text-warning", children: [
|
|
4836
|
+
"Baixa resolu\xE7\xE3o \u2014 no zoom da loja essa foto fica borrada. Troque por uma de pelo menos ",
|
|
4837
|
+
LOW_RESOLUTION_PX,
|
|
4838
|
+
"px no lado maior."
|
|
4839
|
+
] })
|
|
4645
4840
|
] }),
|
|
4646
|
-
/* @__PURE__ */
|
|
4647
|
-
/* @__PURE__ */ jsx(
|
|
4648
|
-
"
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
ref: replaceRef,
|
|
4655
|
-
type: "file",
|
|
4656
|
-
accept: file.kind === "image" ? "image/*" : void 0,
|
|
4657
|
-
className: "hidden",
|
|
4658
|
-
onChange: (e) => {
|
|
4659
|
-
const picked = e.target.files?.[0];
|
|
4660
|
-
e.target.value = "";
|
|
4661
|
-
if (picked) onReplace(picked);
|
|
4662
|
-
}
|
|
4663
|
-
}
|
|
4664
|
-
),
|
|
4665
|
-
/* @__PURE__ */ jsxs(
|
|
4666
|
-
Button,
|
|
4841
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4842
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: "Onde \xE9 usada" }),
|
|
4843
|
+
file.reference ? /* @__PURE__ */ jsx(RecordLink, { reference: file.reference, className: "mt-1 text-sm" }) : /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Biblioteca \u2014 este arquivo n\xE3o est\xE1 preso a nenhum registro." }),
|
|
4844
|
+
file.reference?.isCover && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "\xC9 a capa: \xE9 ela que aparece no cat\xE1logo, no carrinho e no pedido." })
|
|
4845
|
+
] }),
|
|
4846
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4847
|
+
file.kind === "image" && /* @__PURE__ */ jsx(
|
|
4848
|
+
AskAIButton,
|
|
4667
4849
|
{
|
|
4668
|
-
variant: "outline",
|
|
4669
4850
|
className: "w-full",
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4851
|
+
size: "lg",
|
|
4852
|
+
label: "Melhorar com IA",
|
|
4853
|
+
media: { url: file.url, name: file.name },
|
|
4854
|
+
draft: "Melhora essa imagem",
|
|
4855
|
+
hint: "o que voc\xEA quer mudar nessa foto",
|
|
4856
|
+
intro: {
|
|
4857
|
+
persona: "Est\xFAdio de imagem",
|
|
4858
|
+
pitch: `Vamos melhorar ${file.name}.`,
|
|
4859
|
+
description: "Posso limpar o fundo, corrigir luz e cor, e gerar uma vers\xE3o em resolu\xE7\xE3o maior para o cat\xE1logo.",
|
|
4860
|
+
suggestions: [
|
|
4861
|
+
{ label: "Fundo branco", prompt: "Remove o fundo e deixa branco" },
|
|
4862
|
+
{ label: "Mais n\xEDtida", prompt: "Aumenta a resolu\xE7\xE3o e a nitidez" },
|
|
4863
|
+
{ label: "Corrigir a luz", prompt: "Corrige a ilumina\xE7\xE3o e as cores" }
|
|
4864
|
+
]
|
|
4865
|
+
}
|
|
4676
4866
|
}
|
|
4677
4867
|
),
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4868
|
+
/* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", className: "w-full", children: /* @__PURE__ */ jsxs("a", { href: downloadUrlOf(file), download: file.name, children: [
|
|
4869
|
+
/* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
|
|
4870
|
+
"Baixar"
|
|
4871
|
+
] }) }),
|
|
4872
|
+
onReplace && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4873
|
+
/* @__PURE__ */ jsx(
|
|
4874
|
+
"input",
|
|
4875
|
+
{
|
|
4876
|
+
ref: replaceRef,
|
|
4877
|
+
type: "file",
|
|
4878
|
+
accept: file.kind === "image" ? "image/*" : void 0,
|
|
4879
|
+
className: "hidden",
|
|
4880
|
+
onChange: (e) => {
|
|
4881
|
+
const picked = e.target.files?.[0];
|
|
4882
|
+
e.target.value = "";
|
|
4883
|
+
if (picked) onReplace(picked);
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
),
|
|
4887
|
+
/* @__PURE__ */ jsxs(
|
|
4888
|
+
Button,
|
|
4889
|
+
{
|
|
4890
|
+
variant: "outline",
|
|
4891
|
+
className: "w-full",
|
|
4892
|
+
disabled: replacing,
|
|
4893
|
+
onClick: () => replaceRef.current?.click(),
|
|
4894
|
+
children: [
|
|
4895
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${replacing ? "animate-spin" : ""}` }),
|
|
4896
|
+
replacing ? "Trocando\u2026" : "Trocar arquivo"
|
|
4897
|
+
]
|
|
4898
|
+
}
|
|
4899
|
+
),
|
|
4900
|
+
file.reference && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: "A troca mant\xE9m a posi\xE7\xE3o da foto na galeria do produto." })
|
|
4901
|
+
] }),
|
|
4902
|
+
onDelete && /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "w-full text-destructive hover:text-destructive", onClick: onDelete, children: [
|
|
4903
|
+
/* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
|
|
4904
|
+
"Excluir"
|
|
4905
|
+
] })
|
|
4683
4906
|
] })
|
|
4684
4907
|
] })
|
|
4685
4908
|
] })
|
|
4686
4909
|
] })
|
|
4687
|
-
|
|
4910
|
+
);
|
|
4688
4911
|
}
|
|
4689
4912
|
function RecordLink({ reference, className = "" }) {
|
|
4690
4913
|
return /* @__PURE__ */ jsxs(
|
|
@@ -4723,16 +4946,16 @@ var BLING_SAMPLE_SIZE = 100;
|
|
|
4723
4946
|
var BLING_SIGNATURE_HEADER = "x-bling-signature-256";
|
|
4724
4947
|
var BLING_WEBHOOK_DEADLINE_MS = 5e3;
|
|
4725
4948
|
var BLING_FUNCTION = "bling-sync";
|
|
4726
|
-
function
|
|
4949
|
+
function client2() {
|
|
4727
4950
|
const supabase = getSupabaseClientOptional();
|
|
4728
4951
|
if (!supabase) throw new Error("Sem conex\xE3o com o banco para falar com o Bling.");
|
|
4729
4952
|
return supabase;
|
|
4730
4953
|
}
|
|
4731
|
-
async function
|
|
4954
|
+
async function invoke2(body) {
|
|
4732
4955
|
const tenantId = getShopTenantId();
|
|
4733
4956
|
if (!tenantId) throw new Error("Sem loja selecionada para sincronizar com o Bling.");
|
|
4734
4957
|
const runtimeToken = await connectorRuntimeToken({ tenantId });
|
|
4735
|
-
const { data, error } = await
|
|
4958
|
+
const { data, error } = await client2().functions.invoke(BLING_FUNCTION, {
|
|
4736
4959
|
body: { ...body, tenantId },
|
|
4737
4960
|
headers: { [CONNECTOR_RUNTIME_TOKEN_HEADER]: runtimeToken }
|
|
4738
4961
|
});
|
|
@@ -4740,19 +4963,19 @@ async function invoke(body) {
|
|
|
4740
4963
|
return data;
|
|
4741
4964
|
}
|
|
4742
4965
|
function pullCatalogue(mode) {
|
|
4743
|
-
return
|
|
4966
|
+
return invoke2({ action: "pull_products", ...{ mode } });
|
|
4744
4967
|
}
|
|
4745
|
-
function
|
|
4746
|
-
return
|
|
4968
|
+
function pushCatalogue2() {
|
|
4969
|
+
return invoke2({ action: "push_products" });
|
|
4747
4970
|
}
|
|
4748
4971
|
function pushCustomers() {
|
|
4749
|
-
return
|
|
4972
|
+
return invoke2({ action: "push_customers" });
|
|
4750
4973
|
}
|
|
4751
4974
|
function pushOrders() {
|
|
4752
|
-
return
|
|
4975
|
+
return invoke2({ action: "push_orders" });
|
|
4753
4976
|
}
|
|
4754
4977
|
function pushStock() {
|
|
4755
|
-
return
|
|
4978
|
+
return invoke2({ action: "push_stock" });
|
|
4756
4979
|
}
|
|
4757
4980
|
async function countRows(table, apply) {
|
|
4758
4981
|
const tenantId = getShopTenantId();
|
|
@@ -4829,7 +5052,7 @@ async function listOrderDeliveries(limit = 10) {
|
|
|
4829
5052
|
});
|
|
4830
5053
|
}
|
|
4831
5054
|
function reconcile() {
|
|
4832
|
-
return
|
|
5055
|
+
return invoke2({ action: "reconcile" });
|
|
4833
5056
|
}
|
|
4834
5057
|
|
|
4835
5058
|
// src/integrations/bling/connectorDef.tsx
|
|
@@ -5055,7 +5278,7 @@ var blingConnectorDef = {
|
|
|
5055
5278
|
},
|
|
5056
5279
|
pending: pendingProducts,
|
|
5057
5280
|
run: async () => {
|
|
5058
|
-
const result = await
|
|
5281
|
+
const result = await pushCatalogue2();
|
|
5059
5282
|
return { message: result.message, status: result.status };
|
|
5060
5283
|
}
|
|
5061
5284
|
},
|
|
@@ -5268,6 +5491,311 @@ async function acceptWebhook(request, options) {
|
|
|
5268
5491
|
}
|
|
5269
5492
|
return { status: 200, queued: true };
|
|
5270
5493
|
}
|
|
5494
|
+
|
|
5495
|
+
// src/integrations/neochef/limits.ts
|
|
5496
|
+
var NEOCHEF_CONNECTOR_ID = "neochef";
|
|
5497
|
+
var NEOCHEF_RATE_LIMIT = { perSecond: 5, perDay: 5e4 };
|
|
5498
|
+
var NEOCHEF_PAGE_SIZE = 100;
|
|
5499
|
+
var NEOCHEF_SAMPLE_SIZE = 100;
|
|
5500
|
+
var NEOCHEF_CURSOR_OVERLAP_MS = 6e4;
|
|
5501
|
+
var NEOCHEF_TOKEN_LEAD_MS = 10 * 6e4;
|
|
5502
|
+
|
|
5503
|
+
// src/integrations/neochef/mark.ts
|
|
5504
|
+
var NEOCHEF_MARK = "data:image/png;base64,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";
|
|
5505
|
+
var NEOCHEF_FUNCTION = "neochef-sync";
|
|
5506
|
+
function client3() {
|
|
5507
|
+
const supabase = getSupabaseClientOptional();
|
|
5508
|
+
if (!supabase) throw new Error("Sem conex\xE3o com o banco para falar com o NeoChef.");
|
|
5509
|
+
return supabase;
|
|
5510
|
+
}
|
|
5511
|
+
async function invoke3(body) {
|
|
5512
|
+
const tenantId = getShopTenantId();
|
|
5513
|
+
if (!tenantId) throw new Error("Sem loja selecionada para sincronizar com o NeoChef.");
|
|
5514
|
+
const runtimeToken = await connectorRuntimeToken({ tenantId });
|
|
5515
|
+
const { data, error } = await client3().functions.invoke(NEOCHEF_FUNCTION, {
|
|
5516
|
+
body: { ...body, tenantId },
|
|
5517
|
+
headers: { [CONNECTOR_RUNTIME_TOKEN_HEADER]: runtimeToken }
|
|
5518
|
+
});
|
|
5519
|
+
if (error) throw new Error(error.message);
|
|
5520
|
+
return data;
|
|
5521
|
+
}
|
|
5522
|
+
function testNeoChef() {
|
|
5523
|
+
return invoke3({ action: "test" });
|
|
5524
|
+
}
|
|
5525
|
+
function pullCatalogue2(mode) {
|
|
5526
|
+
return invoke3({ action: "pull_products", ...mode ? { mode } : {} });
|
|
5527
|
+
}
|
|
5528
|
+
function pushOrders2() {
|
|
5529
|
+
return invoke3({ action: "push_orders" });
|
|
5530
|
+
}
|
|
5531
|
+
function reconcile2() {
|
|
5532
|
+
return invoke3({ action: "reconcile" });
|
|
5533
|
+
}
|
|
5534
|
+
async function pendingOrders2() {
|
|
5535
|
+
const tenantId = getShopTenantId();
|
|
5536
|
+
const supabase = getSupabaseClientOptional();
|
|
5537
|
+
if (!tenantId || !supabase) return null;
|
|
5538
|
+
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
|
+
const already = new Set((linked ?? []).map((row) => row.order_id));
|
|
5540
|
+
const { data, error } = await supabase.from("plg_shop_orders").select("id").eq("tenant_id", tenantId).neq("status", "cancelled");
|
|
5541
|
+
if (error) return null;
|
|
5542
|
+
return (data ?? []).filter((row) => !already.has(row.id)).length;
|
|
5543
|
+
}
|
|
5544
|
+
async function listOrderDeliveries2(limit = 10) {
|
|
5545
|
+
const tenantId = getShopTenantId();
|
|
5546
|
+
const supabase = getSupabaseClientOptional();
|
|
5547
|
+
if (!tenantId || !supabase) return [];
|
|
5548
|
+
const { data: orders } = await supabase.from("plg_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
|
+
const rows = orders ?? [];
|
|
5550
|
+
if (rows.length === 0) return [];
|
|
5551
|
+
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));
|
|
5552
|
+
const byOrder = new Map(
|
|
5553
|
+
(filed ?? []).map((row) => [row.order_id, row])
|
|
5554
|
+
);
|
|
5555
|
+
return rows.map((order) => {
|
|
5556
|
+
const subject = order.order_number ? `Pedido ${order.order_number}` : "Pedido";
|
|
5557
|
+
const link = byOrder.get(order.id);
|
|
5558
|
+
if (link) {
|
|
5559
|
+
return {
|
|
5560
|
+
subject,
|
|
5561
|
+
subjectId: order.id,
|
|
5562
|
+
status: "delivered",
|
|
5563
|
+
externalId: link.external_number ?? link.external_id,
|
|
5564
|
+
at: link.synced_at ?? order.paid_at,
|
|
5565
|
+
...order.customer_name ? { detail: String(order.customer_name) } : {}
|
|
5566
|
+
};
|
|
5567
|
+
}
|
|
5568
|
+
if (String(order.status ?? "") === "cancelled") {
|
|
5569
|
+
return {
|
|
5570
|
+
subject,
|
|
5571
|
+
subjectId: order.id,
|
|
5572
|
+
status: "failed",
|
|
5573
|
+
detail: "Cancelado \u2014 n\xE3o sobe para a cozinha.",
|
|
5574
|
+
at: order.created_at
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
return {
|
|
5578
|
+
subject,
|
|
5579
|
+
subjectId: order.id,
|
|
5580
|
+
status: "pending",
|
|
5581
|
+
detail: "Ainda n\xE3o subiu \u2014 vai na pr\xF3xima sincroniza\xE7\xE3o.",
|
|
5582
|
+
at: order.paid_at ?? order.created_at
|
|
5583
|
+
};
|
|
5584
|
+
});
|
|
5585
|
+
}
|
|
5586
|
+
|
|
5587
|
+
// src/integrations/neochef/connectorDef.tsx
|
|
5588
|
+
var neoChefConnectionStore = () => createConnectionStore(NEOCHEF_CONNECTOR_ID, {
|
|
5589
|
+
settingsKeys: ["kitchenId", "backfillFrom", "stockOwner"]
|
|
5590
|
+
});
|
|
5591
|
+
var neoChefConnectorDef = {
|
|
5592
|
+
id: NEOCHEF_CONNECTOR_ID,
|
|
5593
|
+
hostPluginId: "shop",
|
|
5594
|
+
name: "NeoChef",
|
|
5595
|
+
description: "Traz o card\xE1pio, o pre\xE7o e o estoque do NeoChef para a loja e devolve cada pedido pago como pedido na cozinha.",
|
|
5596
|
+
// The first question anyone connecting asks, answered before they connect.
|
|
5597
|
+
howItSyncs: [
|
|
5598
|
+
"De tempos em tempos, sozinho: desce do NeoChef o que mudou l\xE1 e sobe o que estiver pendente daqui.",
|
|
5599
|
+
"A qualquer momento, no bot\xE3o Sincronizar agora.",
|
|
5600
|
+
"Nada sobe duas vezes: cada pedido guarda o n\xFAmero que recebeu na cozinha.",
|
|
5601
|
+
"Produto desativado no NeoChef sai de venda na loja \u2014 nenhum produto \xE9 apagado."
|
|
5602
|
+
],
|
|
5603
|
+
icon: "ChefHat",
|
|
5604
|
+
// The app's own mark, so the row wears the same face the consent screen does.
|
|
5605
|
+
// `icon` stays as the fallback the mark resolver advances to — see mark.ts.
|
|
5606
|
+
logoUrl: NEOCHEF_MARK,
|
|
5607
|
+
authKind: "oauth",
|
|
5608
|
+
// `erp` and not `ecommerce`: NeoChef is the back office, not the storefront.
|
|
5609
|
+
// Same reading of §2 that moved Bling off the word 'commerce'.
|
|
5610
|
+
category: "erp",
|
|
5611
|
+
// Both ways, and the connector says so before anyone connects: the catalogue
|
|
5612
|
+
// comes in, the paid order goes out.
|
|
5613
|
+
direction: "inbound",
|
|
5614
|
+
planes: ["sync"],
|
|
5615
|
+
docsUrl: "https://fayz.ai/docs/integrations/neochef",
|
|
5616
|
+
/**
|
|
5617
|
+
* The consent names these, entity by entity, on NeoChef's own screen — this
|
|
5618
|
+
* list is what the STORE says it will do with them, which is the other half
|
|
5619
|
+
* of the same sentence.
|
|
5620
|
+
*
|
|
5621
|
+
* `shop_product` is read as well as written because the catalogue is a
|
|
5622
|
+
* MIRROR: `reconcileMirror` has to see what is here before it can decide what
|
|
5623
|
+
* a product missing from a page means.
|
|
5624
|
+
*/
|
|
5625
|
+
permissions: [
|
|
5626
|
+
{
|
|
5627
|
+
kind: "data",
|
|
5628
|
+
resource: "shop_product",
|
|
5629
|
+
actions: ["read", "write"],
|
|
5630
|
+
reason: "Trazer do NeoChef o card\xE1pio, o pre\xE7o e o saldo do estoque, comparando com o que j\xE1 est\xE1 na loja para n\xE3o duplicar nem tirar de venda por engano."
|
|
5631
|
+
},
|
|
5632
|
+
{
|
|
5633
|
+
kind: "data",
|
|
5634
|
+
resource: "shop_order",
|
|
5635
|
+
actions: ["read", "write"],
|
|
5636
|
+
// `write` is the LINK, not the order: without it every sync would file the
|
|
5637
|
+
// same sale again.
|
|
5638
|
+
reason: "Ler o pedido pago para lan\xE7\xE1-lo na cozinha, e guardar aqui o n\xFAmero que ele recebeu l\xE1. O conte\xFAdo do pedido n\xE3o \xE9 alterado.",
|
|
5639
|
+
sensitive: true
|
|
5640
|
+
},
|
|
5641
|
+
{
|
|
5642
|
+
kind: "event",
|
|
5643
|
+
resource: "shop.order_paid",
|
|
5644
|
+
actions: ["listen"],
|
|
5645
|
+
reason: "Saber que um pedido foi pago, para mand\xE1-lo para a cozinha."
|
|
5646
|
+
},
|
|
5647
|
+
{
|
|
5648
|
+
kind: "event",
|
|
5649
|
+
resource: "shop.order_cancelled",
|
|
5650
|
+
actions: ["listen"],
|
|
5651
|
+
reason: "Saber que um pedido foi cancelado, para n\xE3o mand\xE1-lo para a cozinha."
|
|
5652
|
+
}
|
|
5653
|
+
],
|
|
5654
|
+
// Declared, so the scheduler owns the clock. The budget is ours and not the
|
|
5655
|
+
// provider's — see limits.ts.
|
|
5656
|
+
rateLimit: NEOCHEF_RATE_LIMIT,
|
|
5657
|
+
// A connect screen that waits for a whole catalogue is a connect screen nobody
|
|
5658
|
+
// finishes, so the first load answers with a page and the backfill follows.
|
|
5659
|
+
firstLoad: { sampleSize: NEOCHEF_SAMPLE_SIZE, samplePages: 1, stream: "products" },
|
|
5660
|
+
// With underscores. `shop.order.paid` is not a key anything publishes — the
|
|
5661
|
+
// shop builds its facts as `shop.${def.event}` over SHOP_EMAIL_EVENTS.
|
|
5662
|
+
subscribesTo: ["shop.order_paid", "shop.order_cancelled"],
|
|
5663
|
+
managedAutomations: [
|
|
5664
|
+
{ eventKey: "shop.order_paid", label: "Mandar o pedido pago para a cozinha do NeoChef" },
|
|
5665
|
+
{ eventKey: "shop.order_cancelled", label: "Parar de mandar para a cozinha um pedido cancelado" }
|
|
5666
|
+
],
|
|
5667
|
+
// What HAPPENS, not what is declared. Nothing here names a moment that has no
|
|
5668
|
+
// code behind it.
|
|
5669
|
+
invokedAt: [
|
|
5670
|
+
{
|
|
5671
|
+
key: "shop.order.push",
|
|
5672
|
+
label: "O pedido pago sobe como pedido na cozinha",
|
|
5673
|
+
plane: "sync",
|
|
5674
|
+
trigger: "scheduled",
|
|
5675
|
+
touches: ["shop_order"]
|
|
5676
|
+
},
|
|
5677
|
+
{
|
|
5678
|
+
key: "shop.catalogue.pull",
|
|
5679
|
+
label: "O card\xE1pio, o pre\xE7o e o estoque do NeoChef descem para a loja",
|
|
5680
|
+
plane: "sync",
|
|
5681
|
+
trigger: "scheduled",
|
|
5682
|
+
touches: ["shop_product"]
|
|
5683
|
+
}
|
|
5684
|
+
],
|
|
5685
|
+
// Read from the row the list already batched — one request for N connectors.
|
|
5686
|
+
...connectionStatus(
|
|
5687
|
+
NEOCHEF_CONNECTOR_ID,
|
|
5688
|
+
(connection) => {
|
|
5689
|
+
if (!connection?.active) return { connected: false };
|
|
5690
|
+
const kitchen = connection.settings?.kitchenId;
|
|
5691
|
+
return {
|
|
5692
|
+
connected: connection.status === "connected",
|
|
5693
|
+
detail: kitchen ? `Cozinha ${kitchen}` : void 0,
|
|
5694
|
+
plane: "sync"
|
|
5695
|
+
};
|
|
5696
|
+
},
|
|
5697
|
+
{ store: neoChefConnectionStore }
|
|
5698
|
+
),
|
|
5699
|
+
// The whole connect step. Declaring `oauth` and no `startOAuth` is what renders
|
|
5700
|
+
// the Conectar button disabled with nothing on screen saying why.
|
|
5701
|
+
startOAuth: fayzConnectorOAuth(NEOCHEF_CONNECTOR_ID),
|
|
5702
|
+
// The other half of the consent: the platform stores the token and the pool
|
|
5703
|
+
// stores "is it on?". With nobody writing the second, the connector answers
|
|
5704
|
+
// "não conectado" to a merchant who has just authorized.
|
|
5705
|
+
saveConnection: async () => {
|
|
5706
|
+
await neoChefConnectionStore().save({ active: true });
|
|
5707
|
+
},
|
|
5708
|
+
// Disconnect FREEZES the mirror. A merchant who unplugs the kitchen still has
|
|
5709
|
+
// a store, and a connector that empties the catalogue on disconnect is one
|
|
5710
|
+
// nobody dares to disconnect.
|
|
5711
|
+
disconnect: async () => {
|
|
5712
|
+
await neoChefConnectionStore().disconnect();
|
|
5713
|
+
},
|
|
5714
|
+
disconnectNote: "Desconectar congela o espelho: nenhum produto \xE9 apagado e o estoque para de ser atualizado.",
|
|
5715
|
+
...connectionRuns(neoChefConnectionStore),
|
|
5716
|
+
// The managed automation says a paid order goes to the kitchen; this says
|
|
5717
|
+
// whether it arrived.
|
|
5718
|
+
listDeliveries: (options) => listOrderDeliveries2(options?.limit ?? 10),
|
|
5719
|
+
syncActions: [
|
|
5720
|
+
{
|
|
5721
|
+
key: "reconcile",
|
|
5722
|
+
label: "Sincronizar agora",
|
|
5723
|
+
direction: "inbound",
|
|
5724
|
+
// The one that is right on an ordinary day; the rest fold away.
|
|
5725
|
+
primary: true,
|
|
5726
|
+
description: "Traz o que mudou no NeoChef e envia o que est\xE1 pendente daqui \u2014 card\xE1pio, pre\xE7o, estoque e pedidos pagos.",
|
|
5727
|
+
run: async () => {
|
|
5728
|
+
const result = await reconcile2();
|
|
5729
|
+
const legs = Object.values(result.push ?? {}).map((leg) => leg?.message ? String(leg.message) : null).filter(Boolean);
|
|
5730
|
+
return [`${result.pull?.fetched ?? 0} produto(s) lidos do NeoChef`, ...legs].join(" \xB7 ");
|
|
5731
|
+
}
|
|
5732
|
+
},
|
|
5733
|
+
{
|
|
5734
|
+
key: "pull_products",
|
|
5735
|
+
label: "Trazer card\xE1pio do NeoChef",
|
|
5736
|
+
direction: "inbound",
|
|
5737
|
+
description: "Pre\xE7o e estoque do NeoChef passam a valer aqui. Nada \xE9 apagado da loja.",
|
|
5738
|
+
run: async () => {
|
|
5739
|
+
const result = await pullCatalogue2("full");
|
|
5740
|
+
return `${result.fetched} produto(s) lidos, ${result.inserted} criado(s), ${result.updated} atualizado(s)`;
|
|
5741
|
+
}
|
|
5742
|
+
},
|
|
5743
|
+
{
|
|
5744
|
+
key: "push_orders",
|
|
5745
|
+
label: "Enviar pedidos para a cozinha",
|
|
5746
|
+
direction: "outbound",
|
|
5747
|
+
description: "Cada pedido pago vira um pedido na cozinha. Pedido cancelado n\xE3o sobe, e um j\xE1 enviado n\xE3o sobe duas vezes.",
|
|
5748
|
+
pending: pendingOrders2,
|
|
5749
|
+
run: async () => {
|
|
5750
|
+
const result = await pushOrders2();
|
|
5751
|
+
return { message: result.message, status: result.status };
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
]
|
|
5755
|
+
};
|
|
5756
|
+
|
|
5757
|
+
// src/integrations/neochef/mapping.ts
|
|
5758
|
+
function toNumber2(value) {
|
|
5759
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
5760
|
+
const n = typeof value === "number" ? value : Number(String(value).replace(",", "."));
|
|
5761
|
+
return Number.isFinite(n) ? n : null;
|
|
5762
|
+
}
|
|
5763
|
+
function toMirroredNeoChefProduct(raw) {
|
|
5764
|
+
if (!raw || raw.id === null || raw.id === void 0 || raw.id === "") return null;
|
|
5765
|
+
const inactive = raw.is_active === false || String(raw.status ?? "active").toLowerCase() === "archived";
|
|
5766
|
+
return {
|
|
5767
|
+
externalId: String(raw.id),
|
|
5768
|
+
sku: raw.sku ? String(raw.sku) : null,
|
|
5769
|
+
name: String(raw.name ?? "").trim() || `Produto ${raw.id}`,
|
|
5770
|
+
price: toNumber2(raw.price),
|
|
5771
|
+
// The kitchen owns this number. The store mirrors it; it does not decide it.
|
|
5772
|
+
inventoryCount: toNumber2(raw.stock),
|
|
5773
|
+
status: inactive ? "archived" : "active",
|
|
5774
|
+
imageUrl: raw.image_url ? String(raw.image_url) : null,
|
|
5775
|
+
// Unlike Bling, the far side DOES return its change stamp, so the cursor can
|
|
5776
|
+
// advance from the data instead of from the window we asked for.
|
|
5777
|
+
updatedAt: raw.updated_at ? String(raw.updated_at) : null
|
|
5778
|
+
};
|
|
5779
|
+
}
|
|
5780
|
+
function toNeoChefOrder(order) {
|
|
5781
|
+
return {
|
|
5782
|
+
kind: order.pickup === true ? "takeout" : "delivery",
|
|
5783
|
+
// The store's own number, so a human can match the two screens. Falls back
|
|
5784
|
+
// to the id rather than to empty: an order with no reference is unmatchable.
|
|
5785
|
+
reference_number: String(order.referenceNumber ?? order.id),
|
|
5786
|
+
// It arrives paid and confirmed — the store already took the money, and a
|
|
5787
|
+
// kitchen that has to re-confirm it is a kitchen doing the work twice.
|
|
5788
|
+
status: "confirmed",
|
|
5789
|
+
total: toNumber2(order.total),
|
|
5790
|
+
subtotal: toNumber2(order.subtotal),
|
|
5791
|
+
notes: order.notes ? String(order.notes) : null
|
|
5792
|
+
};
|
|
5793
|
+
}
|
|
5794
|
+
function advanceNeoChefCursor(products, overlapMs) {
|
|
5795
|
+
const stamps = products.map((product) => product.updatedAt ? Date.parse(product.updatedAt) : NaN).filter((value) => Number.isFinite(value));
|
|
5796
|
+
if (stamps.length === 0) return null;
|
|
5797
|
+
return new Date(Math.max(...stamps) - overlapMs).toISOString();
|
|
5798
|
+
}
|
|
5271
5799
|
var ORDERS = "plg_shop_rep_orders";
|
|
5272
5800
|
var ITEMS = "plg_shop_rep_order_items";
|
|
5273
5801
|
var CUSTOMERS = "plg_shop_rep_customers";
|
|
@@ -6095,7 +6623,7 @@ function buildShopAnalyticsCards() {
|
|
|
6095
6623
|
}
|
|
6096
6624
|
|
|
6097
6625
|
// src/index.ts
|
|
6098
|
-
var ShopPage = React8.lazy(() => import('./ShopPage-
|
|
6626
|
+
var ShopPage = React8.lazy(() => import('./ShopPage-VVLOH3RF.js').then((m) => ({ default: m.ShopPage })));
|
|
6099
6627
|
function createShopPlugin(options) {
|
|
6100
6628
|
const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
|
|
6101
6629
|
setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
|
|
@@ -6126,6 +6654,24 @@ function createShopPlugin(options) {
|
|
|
6126
6654
|
const ProductsCrudPage = createCrudPage(buildShopProductEntity(curr.code, resolveProvider), {
|
|
6127
6655
|
dataProvider: createShopProductDataProvider(resolveProvider),
|
|
6128
6656
|
feature: "shop",
|
|
6657
|
+
// O produto da loja não tem tabela própria — vive atrás do provider de
|
|
6658
|
+
// commerce —, então as primitivas de dado do agente não o alcançam. A porta
|
|
6659
|
+
// funciona porque a lista empresta o próprio store: ver CrudAgentSurface.
|
|
6660
|
+
aiCreate: {
|
|
6661
|
+
hint: "Descreva o produto\u2026",
|
|
6662
|
+
description: "Descreva o produto e eu cadastro.",
|
|
6663
|
+
intro: {
|
|
6664
|
+
role: "operations",
|
|
6665
|
+
persona: "Cadastro de produtos",
|
|
6666
|
+
pitch: "Diga o produto que voc\xEA quer cadastrar.",
|
|
6667
|
+
description: "Nome, pre\xE7o, descri\xE7\xE3o, estoque \u2014 diga o que souber, do jeito que falar, e eu preencho o resto com o que faz sentido. Nasce como rascunho e voc\xEA confere antes de publicar.",
|
|
6668
|
+
suggestions: [
|
|
6669
|
+
{ label: "Camiseta preta, R$ 79, estoque 20" },
|
|
6670
|
+
{ label: "Cadastre 5 produtos de exemplo para eu testar a loja" },
|
|
6671
|
+
{ label: "Um combo com os dois produtos mais vendidos" }
|
|
6672
|
+
]
|
|
6673
|
+
}
|
|
6674
|
+
},
|
|
6129
6675
|
stats: (items, total) => [
|
|
6130
6676
|
{ id: "total", label: "Produtos", value: total, icon: Package },
|
|
6131
6677
|
{ id: "active", label: "Ativos", value: items?.filter((p) => p.status === "active").length ?? 0, icon: Eye },
|
|
@@ -6390,6 +6936,7 @@ function createShopPlugin(options) {
|
|
|
6390
6936
|
const READ = { feature: "shop", action: "read" };
|
|
6391
6937
|
return {
|
|
6392
6938
|
id: "shop",
|
|
6939
|
+
defaultAgentRole: "operations",
|
|
6393
6940
|
name: options?.navLabel ?? "Shop",
|
|
6394
6941
|
icon: "ShoppingBag",
|
|
6395
6942
|
version: "1.0.0",
|
|
@@ -6527,19 +7074,22 @@ function createShopPlugin(options) {
|
|
|
6527
7074
|
// screens (commerce-stack.md §0). Melhor Envio, AppMax and AbacatePay are
|
|
6528
7075
|
// REQUEST-plane connectors: the buyer is waiting, so each declares a latency
|
|
6529
7076
|
// budget and, because the carrier and the gateways want opposite answers to
|
|
6530
|
-
// the same failure, its own `onFailure`. Bling
|
|
6531
|
-
// nobody is waiting on
|
|
6532
|
-
// a
|
|
7077
|
+
// the same failure, its own `onFailure`. Bling and NeoChef are the SYNC-plane
|
|
7078
|
+
// ones — nobody is waiting on them, and what they declare instead is a call
|
|
7079
|
+
// budget and a cursor.
|
|
6533
7080
|
//
|
|
6534
7081
|
// Two gateways on the shelf is the point: the store picks one, and the
|
|
6535
|
-
// second one arriving changed no contract — only how it authorizes.
|
|
7082
|
+
// second one arriving changed no contract — only how it authorizes. NeoChef
|
|
7083
|
+
// makes the same point about the back office, and one better: its provider
|
|
7084
|
+
// is another Fayz app, and it still fits the shelf without a special case.
|
|
6536
7085
|
connectors: [
|
|
6537
7086
|
melhorEnvioConnectorDef,
|
|
6538
7087
|
appmaxConnectorDef,
|
|
6539
7088
|
abacatePayConnectorDef,
|
|
6540
7089
|
stripeConnectConnectorDef,
|
|
6541
7090
|
vindiConnectorDef,
|
|
6542
|
-
blingConnectorDef
|
|
7091
|
+
blingConnectorDef,
|
|
7092
|
+
neoChefConnectorDef
|
|
6543
7093
|
],
|
|
6544
7094
|
// What having a store makes plugable — the four above plus the ones still
|
|
6545
7095
|
// to be built. Carried over from the Fayz plugin page's "Integra com" row,
|
|
@@ -6549,6 +7099,6 @@ function createShopPlugin(options) {
|
|
|
6549
7099
|
};
|
|
6550
7100
|
}
|
|
6551
7101
|
|
|
6552
|
-
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, STRIPE_CONNECT_CONNECTOR_ID, STRIPE_CONNECT_LATENCY_BUDGET_MS, VINDI_CONNECTOR_ID, VINDI_LATENCY_BUDGET_MS, abacatePayConnectorDef, acceptWebhook, appmaxConnectorDef, blingConnectionStore, blingConnectorDef, buildShopAnalyticsCards, createShopPlugin, firstWindow, melhorEnvioConnectorDef, orphanConnection, pullProducts, routeCallback, sampleProducts, stripeConnectConnectorDef, toMirroredProduct, vindiConnectorDef, windowFor };
|
|
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 };
|
|
6553
7103
|
//# sourceMappingURL=index.js.map
|
|
6554
7104
|
//# sourceMappingURL=index.js.map
|