@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.
Files changed (39) hide show
  1. package/dist/{ShopPage-YB5ZWECJ.js → ShopPage-VVLOH3RF.js} +67 -28
  2. package/dist/ShopPage-VVLOH3RF.js.map +1 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +702 -152
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/abacatepay/connectorDef.d.ts.map +1 -1
  8. package/dist/integrations/abacatepay/data.d.ts +40 -0
  9. package/dist/integrations/abacatepay/data.d.ts.map +1 -0
  10. package/dist/integrations/abacatepay/limits.d.ts +26 -0
  11. package/dist/integrations/abacatepay/limits.d.ts.map +1 -0
  12. package/dist/integrations/abacatepay/sync.d.ts +88 -0
  13. package/dist/integrations/abacatepay/sync.d.ts.map +1 -0
  14. package/dist/integrations/neochef/connectorDef.d.ts +12 -0
  15. package/dist/integrations/neochef/connectorDef.d.ts.map +1 -0
  16. package/dist/integrations/neochef/data.d.ts +54 -0
  17. package/dist/integrations/neochef/data.d.ts.map +1 -0
  18. package/dist/integrations/neochef/index.d.ts +8 -0
  19. package/dist/integrations/neochef/index.d.ts.map +1 -0
  20. package/dist/integrations/neochef/limits.d.ts +26 -0
  21. package/dist/integrations/neochef/limits.d.ts.map +1 -0
  22. package/dist/integrations/neochef/mapping.d.ts +31 -0
  23. package/dist/integrations/neochef/mapping.d.ts.map +1 -0
  24. package/dist/integrations/neochef/mark.d.ts +21 -0
  25. package/dist/integrations/neochef/mark.d.ts.map +1 -0
  26. package/dist/integrations/neochef/types.d.ts +71 -0
  27. package/dist/integrations/neochef/types.d.ts.map +1 -0
  28. package/dist/integrations/vindi/connectorDef.d.ts +25 -6
  29. package/dist/integrations/vindi/connectorDef.d.ts.map +1 -1
  30. package/dist/settings/PackagesSettings.d.ts.map +1 -1
  31. package/dist/views/ContentPage.d.ts.map +1 -1
  32. package/dist/views/NewOrderCharge.d.ts +14 -2
  33. package/dist/views/NewOrderCharge.d.ts.map +1 -1
  34. package/dist/views/NewOrderPage.d.ts.map +1 -1
  35. package/dist/views/new-order-rules.d.ts +10 -1
  36. package/dist/views/new-order-rules.d.ts.map +1 -1
  37. package/dist/views/productEntity.d.ts.map +1 -1
  38. package/package.json +12 -11
  39. 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, Sparkles, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info } from 'lucide-react';
7
- import { HeaderTabs, Card, Button, Thumbnail, PageHeader, toast, ConfirmDialog, Input, Hint, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Skeleton } from '@fayz-ai/ui';
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-lg border bg-card p-3", children: [
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
- renderCell: (v, row) => /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
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: "ml-2 text-xs text-muted-foreground line-through", children: money(row.compareAtPrice, row.currency) })
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-lg 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: [
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-lg border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
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-lg border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
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-lg border bg-card px-5 shadow-sm", children: /* @__PURE__ */ jsxs(
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-lg border bg-card shadow-sm", children: /* @__PURE__ */ jsx("ul", { className: "divide-y", children: carriers.map((connector) => /* @__PURE__ */ jsx(ConnectorRow, { connector, onOpen: openJourney }, connector.id)) }) })
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
- { value: "CARD", label: "Cart\xE3o de cr\xE9dito" }
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-2 border-t pt-4 text-xs text-muted-foreground", children: [
3683
- /* @__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 vai para a p\xE1gina segura da AbacatePay e volta com o resultado \u2014 assim nenhum n\xFAmero de cart\xE3o passa pelo seu site." }),
3684
- /* @__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." }),
3685
- /* @__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.' })
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, boleto e cart\xE3o com uma chave s\xF3, sem contrato de adquirente e sem tirar o cliente da loja.",
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, boleto ou cart\xE3o)",
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 no checkout.",
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 = { "27": "Pix", "28": "Bolepix", "6": "Boleto" };
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. Cart\xE3o ainda n\xE3o \u2014 ele vai chegar pela p\xE1gina segura da Vindi, para que nenhum n\xFAmero de cart\xE3o passe pelo seu site." }),
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 boleto pela Vindi. A loja recebe na conta dela, com o cliente pagando sem sair da p\xE1gina.",
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 boleto)",
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
- ...settingsStatus(VINDI_SETTINGS, connection)
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__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
4419
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
4420
- /* @__PURE__ */ jsx(HeaderTabs, { tabs: TABS, value: tab, onChange: setTab, "aria-label": "Arquivos" }),
4421
- caption && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: caption })
4422
- ] }),
4423
- supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(UploadButton, { uploading, onPick: handleUpload })
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 /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4579
- /* @__PURE__ */ jsx(PageHeader, { title: file.name, parentLabel: "Conte\xFAdo", onBack }),
4580
- 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: [
4581
- /* @__PURE__ */ jsx(
4582
- "button",
4583
- {
4584
- type: "button",
4585
- "aria-label": "Arquivo anterior",
4586
- disabled: position.index === 0,
4587
- onClick: () => onNavigate(-1),
4588
- className: "rounded-md border p-1 transition hover:bg-muted disabled:opacity-40",
4589
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
4590
- }
4591
- ),
4592
- /* @__PURE__ */ jsxs("span", { className: "px-1 tabular-nums", children: [
4593
- position.index + 1,
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
- file.url
4622
- ) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 p-12 text-muted-foreground", children: [
4623
- /* @__PURE__ */ jsx(KindIcon, { kind: file.kind, className: "h-10 w-10" }),
4624
- /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Pr\xE9-visualiza\xE7\xE3o n\xE3o dispon\xEDvel" })
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__ */ jsxs("div", { className: "space-y-2", children: [
4642
- /* @__PURE__ */ jsxs(Button, { disabled: true, className: "w-full", title: "Em breve", children: [
4643
- /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" }),
4644
- "Melhorar com IA"
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__ */ jsx(Button, { asChild: true, variant: "outline", className: "w-full", children: /* @__PURE__ */ jsxs("a", { href: downloadUrlOf(file), download: file.name, children: [
4647
- /* @__PURE__ */ jsx(Download, { className: "mr-2 h-4 w-4" }),
4648
- "Baixar"
4649
- ] }) }),
4650
- onReplace && /* @__PURE__ */ jsxs(Fragment, { children: [
4651
- /* @__PURE__ */ jsx(
4652
- "input",
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
- disabled: replacing,
4671
- onClick: () => replaceRef.current?.click(),
4672
- children: [
4673
- /* @__PURE__ */ jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${replacing ? "animate-spin" : ""}` }),
4674
- replacing ? "Trocando\u2026" : "Trocar arquivo"
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
- 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." })
4679
- ] }),
4680
- onDelete && /* @__PURE__ */ jsxs(Button, { variant: "ghost", className: "w-full text-destructive hover:text-destructive", onClick: onDelete, children: [
4681
- /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
4682
- "Excluir"
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 client() {
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 invoke(body) {
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 client().functions.invoke(BLING_FUNCTION, {
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 invoke({ action: "pull_products", ...{ mode } });
4966
+ return invoke2({ action: "pull_products", ...{ mode } });
4744
4967
  }
4745
- function pushCatalogue() {
4746
- return invoke({ action: "push_products" });
4968
+ function pushCatalogue2() {
4969
+ return invoke2({ action: "push_products" });
4747
4970
  }
4748
4971
  function pushCustomers() {
4749
- return invoke({ action: "push_customers" });
4972
+ return invoke2({ action: "push_customers" });
4750
4973
  }
4751
4974
  function pushOrders() {
4752
- return invoke({ action: "push_orders" });
4975
+ return invoke2({ action: "push_orders" });
4753
4976
  }
4754
4977
  function pushStock() {
4755
- return invoke({ action: "push_stock" });
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 invoke({ action: "reconcile" });
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 pushCatalogue();
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-YB5ZWECJ.js').then((m) => ({ default: m.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 is the SYNC-plane one —
6531
- // nobody is waiting on it, and what it declares instead is a call budget and
6532
- // a windowed cursor.
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