@fayz-ai/plugin-shop 0.14.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{ShopPage-6IQ7WKQG.js → ShopPage-5IERN4E5.js} +339 -412
  2. package/dist/ShopPage-5IERN4E5.js.map +1 -0
  3. package/dist/{chunk-NFFJVQCY.js → chunk-RPBKGW24.js} +4 -18
  4. package/dist/chunk-RPBKGW24.js.map +1 -0
  5. package/dist/index.d.ts +6 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +392 -380
  8. package/dist/index.js.map +1 -1
  9. package/dist/integrations/abacatepay/connectorDef.d.ts +1 -1
  10. package/dist/integrations/abacatepay/sync.d.ts.map +1 -1
  11. package/dist/integrations/appmax/connectorDef.d.ts +1 -1
  12. package/dist/integrations/melhor-envio/connectorDef.d.ts +1 -1
  13. package/dist/integrations/mercadopago/connectorDef.d.ts.map +1 -1
  14. package/dist/lib/onboarding.d.ts +1 -1
  15. package/dist/lib/onboarding.d.ts.map +1 -1
  16. package/dist/migrations/index.d.ts +6 -0
  17. package/dist/migrations/index.d.ts.map +1 -0
  18. package/dist/settings/error-message.d.ts +8 -0
  19. package/dist/settings/error-message.d.ts.map +1 -1
  20. package/dist/views/NewOrderPage.d.ts.map +1 -1
  21. package/dist/views/OrderDetailView.d.ts.map +1 -1
  22. package/dist/views/ProductImagesTab.d.ts.map +1 -1
  23. package/dist/views/ProductPositionsTab.d.ts.map +1 -1
  24. package/dist/views/ProductVariantsTab.d.ts.map +1 -1
  25. package/dist/views/ShipOrderForm.d.ts.map +1 -1
  26. package/dist/views/ShopPage.d.ts.map +1 -1
  27. package/dist/views/productEntity.d.ts.map +1 -1
  28. package/dist/views/stock-agent.d.ts.map +1 -1
  29. package/package.json +7 -6
  30. package/dist/ShopPage-6IQ7WKQG.js.map +0 -1
  31. package/dist/chunk-NFFJVQCY.js.map +0 -1
  32. package/dist/views/ChannelsPage.d.ts +0 -7
  33. package/dist/views/ChannelsPage.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-NFFJVQCY.js';
1
+ import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_PROFILES_REGISTRY, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-RPBKGW24.js';
2
2
  import * as React13 from 'react';
3
3
  import React13__default, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
- import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, countByTenant, getActiveTenantId, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
4
+ import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, tl, getActiveTenantId, getSupabaseClientOptional, registerMessageDefaults, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, countByTenant, errorMessage as errorMessage$1, emitDataChanged, resolveDataProvider } from '@fayz-ai/core';
5
5
  import { getShopTenantId, setShopTenantResolver, buildShopMessageDefaults, registerShippingLabelProvider, getShopProvider, buildShopDomainEvents, axesFromOptions, planVariantMatrix, validateVariantAxes, hasOnlyDefaultVariant, MAX_VARIANT_AXES, leavesNothingSellable, productImageToMediaFile, formatVariantCombination, applyVariantMatrix } from '@fayz-ai/shop';
6
- import { fayzConnectorOAuth, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo, useAgentSurface, askAI, AskAIButton, CrudFormPage, SettingsGroup, SelectRow, ToggleRow, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, MultiSelectRow, AddConnectorButton, AddConnectorPanel, ConnectorDetail, RegistryCrudView, TextRow, ConnectorRow, ConnectorMark, StateChip } from '@fayz-ai/admin';
7
- import { Package, Eye, FileEdit, PackageX, UsersRound, Users, UserX, Filter, ShoppingBag, Repeat, CircleDollarSign, Tag, TicketCheck, Layers, CreditCard, ImageOff, Link2, MapPin, Music, Camera, MessageCircle, Globe, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, ExternalLink, Check, X, AlertTriangle, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, Car, Truck, Info, Sparkles, Ban, ListChecks, Search, Building2 } from 'lucide-react';
8
- import { PageHeaderActions, PageTabs, Card, Button, Thumbnail, PageHeader, toast, ConfirmDialog, Input, Hint, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, Skeleton } from '@fayz-ai/ui';
6
+ import { fayzConnectorOAuth, describeConnectorAccount, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo, DetailTabHeader, useAgentSurface, askAI, AskAIButton, CrudFormPage, SettingsGroup, SelectRow, ToggleRow, useConnectors, useConnectorsPanelAvailable, openConnectorsPanel, MultiSelectRow, AddConnectorButton, AddConnectorPanel, ConnectorDetail, RegistryCrudView, TextRow, ConnectorRow, ConnectorMark, StateChip } from '@fayz-ai/admin';
7
+ import { Package, Eye, FileEdit, PackageX, UsersRound, Users, UserX, Filter, ShoppingBag, Repeat, CircleDollarSign, Tag, TicketCheck, Layers, CreditCard, ImageOff, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, Upload, AlertTriangle, X, ExternalLink, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, MapPin, Car, Truck, Info, Check, Sparkles, Ban, ListChecks, Search, Building2 } from 'lucide-react';
8
+ import { PageHeaderActions, PageTabs, Card, Button, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalFooter, Thumbnail, PageHeader, toast, ConfirmDialog, Hint, ModalBody, Skeleton } from '@fayz-ai/ui';
9
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
10
  import { listStockReasonCodes, resolveStockStore, createSupabaseStockStore } from '@fayz-ai/shop/inventory';
11
11
  import { normalizeMovementType } from '@fayz-ai/plugin-inventory';
12
12
  import { advanceWindow, createRateLimiter, sliceWindow, paginate, reconcileMirror, serializeCursor } from '@fayz-ai/core/connector-runtime';
13
13
 
14
- function buildShopOnboarding() {
14
+ function buildShopOnboarding(siteRoute = "/site") {
15
15
  return {
16
16
  id: "shop",
17
17
  title: "Sua loja no ar",
@@ -87,22 +87,22 @@ function buildShopOnboarding() {
87
87
  {
88
88
  key: "storefront",
89
89
  title: "Configure sua loja online",
90
- description: "O endere\xE7o que voc\xEA manda para o cliente \u2014 e os canais que apontam para ela.",
90
+ description: "O endere\xE7o que voc\xEA manda para o cliente \u2014 e como a vitrine est\xE1 hoje.",
91
91
  icon: "Store",
92
- route: "/store",
92
+ route: siteRoute,
93
93
  priority: "high",
94
94
  // A plataforma chama de "publique seu site"; numa loja é a vitrine, e
95
95
  // quem sabe configurá-la é este módulo.
96
96
  replaces: "publish",
97
- agentHint: "Confira o endere\xE7o da vitrine e os canais ativos. Publicar sem frete configurado gera pedido que ningu\xE9m consegue entregar.",
98
- howTo: 'Canais de venda (o item "Sua loja" na barra lateral). A Loja Online \xE9 a sua vitrine: confira o endere\xE7o, veja como est\xE1 e publique. Esse link \xE9 o que vai para a bio do Instagram e para o WhatsApp.',
97
+ agentHint: "Confira como a vitrine est\xE1 e o endere\xE7o dela. Publicar sem frete configurado gera pedido que ningu\xE9m consegue entregar.",
98
+ howTo: 'O construtor do site (o "Editar" no item "Sua loja", na barra lateral). Veja como a vitrine est\xE1, ajuste o que precisar conversando com o assistente e publique. Esse link \xE9 o que vai para a bio do Instagram e para o WhatsApp.',
99
99
  celebrate: "Vitrine pronta \u2728",
100
100
  script: [
101
101
  { kind: "say", text: "Agora a vitrine \u2014 o endere\xE7o que voc\xEA manda para o cliente." },
102
- { kind: "goto", route: "/store" },
102
+ { kind: "goto", route: siteRoute },
103
103
  {
104
104
  kind: "ask",
105
- text: "Estou na tela de canais de venda. Confira comigo o endere\xE7o da Loja Online e me diga o que falta para publicar."
105
+ text: "Estou vendo o site da loja. Confira comigo como ele est\xE1 e me diga o que falta para publicar."
106
106
  }
107
107
  ]
108
108
  },
@@ -175,7 +175,7 @@ function buildShopOnboarding() {
175
175
  waitForUser: true
176
176
  }
177
177
  ],
178
- check: async () => await countByTenant("plg_shop_orders") > 0,
178
+ check: async () => await countByTenant("v_shop_orders") > 0,
179
179
  tour: {
180
180
  id: "shop.orders",
181
181
  stops: [
@@ -194,17 +194,17 @@ function buildShopOnboarding() {
194
194
 
195
195
  // src/integrations/melhor-envio/labelProvider.ts
196
196
  var MELHOR_ENVIO_CARRIER = "melhor-envio";
197
- function createMelhorEnvioLabelProvider(resolve4) {
197
+ function createMelhorEnvioLabelProvider(resolve3) {
198
198
  const unsupported = () => {
199
199
  throw new Error("Este painel n\xE3o sabe comprar etiquetas nesta loja.");
200
200
  };
201
201
  return {
202
202
  id: MELHOR_ENVIO_CARRIER,
203
203
  label: "Melhor Envio",
204
- quote: async (orderId) => await resolve4().quoteShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
205
- buy: async (orderId, serviceId) => await resolve4().buyShippingLabel?.(orderId, serviceId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
206
- print: async (orderId) => await resolve4().printShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null,
207
- track: async (orderId) => await resolve4().trackShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null
204
+ quote: async (orderId) => await resolve3().quoteShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
205
+ buy: async (orderId, serviceId) => await resolve3().buyShippingLabel?.(orderId, serviceId, MELHOR_ENVIO_CARRIER) ?? unsupported(),
206
+ print: async (orderId) => await resolve3().printShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null,
207
+ track: async (orderId) => await resolve3().trackShippingLabel?.(orderId, MELHOR_ENVIO_CARRIER) ?? null
208
208
  };
209
209
  }
210
210
  function ProductImagesTab({ item, getProvider }) {
@@ -256,27 +256,34 @@ function ProductImagesTab({ item, getProvider }) {
256
256
  }
257
257
  const update = getProvider().updateProductImage;
258
258
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
259
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
260
- /* @__PURE__ */ jsxs("div", { children: [
261
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Fotos do produto" }),
262
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "A primeira foto \xE9 a capa \u2014 aparece no cat\xE1logo, no carrinho e no pedido. As demais viram o carrossel na p\xE1gina do produto." })
263
- ] }),
264
- /* @__PURE__ */ jsxs("label", { className: "inline-flex cursor-pointer items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition hover:opacity-90", children: [
265
- busy ? "Enviando\u2026" : "Adicionar fotos",
266
- /* @__PURE__ */ jsx(
267
- "input",
268
- {
269
- ref: fileRef,
270
- type: "file",
271
- accept: "image/*",
272
- multiple: true,
273
- disabled: busy,
274
- className: "hidden",
275
- onChange: (event) => void upload(event.target.files)
276
- }
259
+ /* @__PURE__ */ jsx(
260
+ DetailTabHeader,
261
+ {
262
+ title: "Fotos do produto",
263
+ description: "A primeira foto \xE9 a capa \u2014 aparece no cat\xE1logo, no carrinho e no pedido. As demais viram o carrossel na p\xE1gina do produto.",
264
+ actions: (
265
+ // The same Button as every other tab. It used to be a <label> hand-
266
+ // painted to look like one, which drifted the moment the real button
267
+ // changed — different height, no icon, no disabled state.
268
+ /* @__PURE__ */ jsxs(Button, { disabled: busy, onClick: () => fileRef.current?.click(), children: [
269
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
270
+ busy ? "Enviando\u2026" : "Adicionar fotos"
271
+ ] })
277
272
  )
278
- ] })
279
- ] }),
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsx(
276
+ "input",
277
+ {
278
+ ref: fileRef,
279
+ type: "file",
280
+ accept: "image/*",
281
+ multiple: true,
282
+ disabled: busy,
283
+ className: "hidden",
284
+ onChange: (event) => void upload(event.target.files)
285
+ }
286
+ ),
280
287
  error && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800", children: error }),
281
288
  images.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-dashed px-4 py-10 text-center text-sm text-muted-foreground", children: "Nenhuma foto ainda. Produtos com mais de uma foto vendem melhor \u2014 mostre a embalagem, o produto pronto e uma refer\xEAncia de tamanho." }) : /* @__PURE__ */ jsx("ul", { className: "fz-cards gap-3", children: images.map((image, index) => /* @__PURE__ */ jsxs("li", { className: "overflow-hidden rounded-lg border", children: [
282
289
  /* @__PURE__ */ jsxs("div", { className: "relative aspect-square bg-muted", children: [
@@ -357,6 +364,7 @@ function ProductVariantsTab({ item, getProvider }) {
357
364
  const [axes, setAxes] = React13__default.useState([]);
358
365
  const [variants, setVariants] = React13__default.useState([]);
359
366
  const [busy, setBusy] = React13__default.useState(false);
367
+ const [adding, setAdding] = React13__default.useState(false);
360
368
  const [error, setError] = React13__default.useState(null);
361
369
  const [selected, setSelected] = React13__default.useState(/* @__PURE__ */ new Set());
362
370
  const [dirty, setDirty] = React13__default.useState(false);
@@ -397,25 +405,40 @@ function ProductVariantsTab({ item, getProvider }) {
397
405
  }
398
406
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
399
407
  /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
400
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [
401
- /* @__PURE__ */ jsxs("div", { children: [
402
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Op\xE7\xF5es" }),
403
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Tamanho, cor, sabor. Cada combina\xE7\xE3o vira uma variante com pre\xE7o, SKU e estoque pr\xF3prios \u2014 \xE9 assim que P e GG deixam de dividir o mesmo contador." })
404
- ] }),
405
- /* @__PURE__ */ jsxs(
406
- "button",
407
- {
408
- type: "button",
409
- disabled: busy || axes.length >= MAX_VARIANT_AXES,
410
- onClick: () => patchAxes([...axes, { name: "", values: [] }]),
411
- className: "inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs font-medium transition hover:bg-muted disabled:opacity-40",
412
- children: [
413
- /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
414
- " Adicionar op\xE7\xE3o"
415
- ]
408
+ /* @__PURE__ */ jsx(
409
+ DetailTabHeader,
410
+ {
411
+ title: "Op\xE7\xF5es",
412
+ description: "Tamanho, cor, sabor. Cada combina\xE7\xE3o vira uma variante com pre\xE7o, SKU e estoque pr\xF3prios \u2014 \xE9 assim que P e GG deixam de dividir o mesmo contador.",
413
+ actions: (
414
+ // Same Button as the other two tabs. It used to be a bare <button>
415
+ // at text-xs with its own border, which read as a lesser control
416
+ // than "Dar entrada" one tab over, for no reason anybody chose.
417
+ /* @__PURE__ */ jsxs(
418
+ Button,
419
+ {
420
+ disabled: busy || axes.length >= MAX_VARIANT_AXES,
421
+ onClick: () => setAdding(true),
422
+ children: [
423
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
424
+ " Adicionar op\xE7\xE3o"
425
+ ]
426
+ }
427
+ )
428
+ )
429
+ }
430
+ ),
431
+ /* @__PURE__ */ jsx(
432
+ NewAxisModal,
433
+ {
434
+ open: adding,
435
+ onClose: () => setAdding(false),
436
+ onCreate: (axis) => {
437
+ patchAxes([...axes, axis]);
438
+ setAdding(false);
416
439
  }
417
- )
418
- ] }),
440
+ }
441
+ ),
419
442
  axes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: "Este produto \xE9 vendido de um jeito s\xF3. Adicione uma op\xE7\xE3o se ele tem tamanhos, cores ou sabores." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-3", children: axes.map((axis, index) => /* @__PURE__ */ jsx(
420
443
  AxisEditor,
421
444
  {
@@ -568,14 +591,17 @@ function VariantGrid({ rows, axes, product, busy, dirty, selected, onSelect, onS
568
591
  onSelect(next);
569
592
  }
570
593
  return /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
571
- /* @__PURE__ */ jsxs("header", { children: [
572
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Variantes" }),
573
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
574
- "Pre\xE7o vazio herda o pre\xE7o do produto \u2014 o cat\xE1logo em que todo tamanho custa igual n\xE3o precisa repetir o n\xFAmero ",
575
- rows.length,
576
- " vezes. Medida vazia herda a do produto, do mesmo jeito."
577
- ] })
578
- ] }),
594
+ /* @__PURE__ */ jsx(
595
+ DetailTabHeader,
596
+ {
597
+ title: "Variantes",
598
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
599
+ "Pre\xE7o vazio herda o pre\xE7o do produto \u2014 o cat\xE1logo em que todo tamanho custa igual n\xE3o precisa repetir o n\xFAmero ",
600
+ rows.length,
601
+ " vezes. Medida vazia herda a do produto, do mesmo jeito."
602
+ ] })
603
+ }
604
+ ),
579
605
  dirty && /* @__PURE__ */ jsx(Notice, { children: "As linhas abaixo ainda refletem o que est\xE1 salvo. Salve as op\xE7\xF5es para gerar as novas combina\xE7\xF5es." }),
580
606
  selected.size > 0 && /* @__PURE__ */ jsx(
581
607
  BulkBar,
@@ -904,7 +930,96 @@ function describePlan(plan) {
904
930
  return `${parts.join(", ")}. Combina\xE7\xF5es removidas s\xE3o arquivadas, nunca exclu\xEDdas \u2014 o estoque delas continua no hist\xF3rico.`;
905
931
  }
906
932
  function message(error) {
907
- return error instanceof Error ? error.message : String(error);
933
+ return errorMessage$1(error);
934
+ }
935
+ function NewAxisModal({ open, onClose, onCreate }) {
936
+ const [name, setName] = React13__default.useState("");
937
+ const [values, setValues] = React13__default.useState([""]);
938
+ React13__default.useEffect(() => {
939
+ if (open) {
940
+ setName("");
941
+ setValues([""]);
942
+ }
943
+ }, [open]);
944
+ function setValue(index, next) {
945
+ setValues((prev) => {
946
+ const out = [...prev];
947
+ out[index] = next;
948
+ if (index === out.length - 1 && next.trim()) out.push("");
949
+ return out;
950
+ });
951
+ }
952
+ function removeValue(index) {
953
+ setValues((prev) => {
954
+ const out = prev.filter((_, i) => i !== index);
955
+ return out.length === 0 || out[out.length - 1].trim() ? [...out, ""] : out;
956
+ });
957
+ }
958
+ const filled = values.map((v) => v.trim()).filter(Boolean);
959
+ const unique = [...new Set(filled)];
960
+ const hasDuplicate = unique.length !== filled.length;
961
+ const ready = name.trim().length > 0 && unique.length > 0 && !hasDuplicate;
962
+ return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (o) => {
963
+ if (!o) onClose();
964
+ }, children: /* @__PURE__ */ jsxs(ModalContent, { className: "sm:max-w-md", children: [
965
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
966
+ /* @__PURE__ */ jsx(ModalTitle, { children: "Nova op\xE7\xE3o" }),
967
+ /* @__PURE__ */ jsx(ModalDescription, { children: "Cada combina\xE7\xE3o de op\xE7\xF5es vira uma variante com pre\xE7o, SKU e estoque pr\xF3prios." })
968
+ ] }),
969
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 py-2", children: [
970
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
971
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Nome" }),
972
+ /* @__PURE__ */ jsx(
973
+ "input",
974
+ {
975
+ autoFocus: true,
976
+ value: name,
977
+ onChange: (e) => setName(e.target.value),
978
+ placeholder: "Tamanho",
979
+ className: "mt-1 h-9 w-full rounded-md border bg-background px-2.5 text-sm outline-none focus:border-primary"
980
+ }
981
+ )
982
+ ] }),
983
+ /* @__PURE__ */ jsxs("div", { children: [
984
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Valores" }),
985
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-1.5", children: values.map((value, index) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1.5", children: [
986
+ /* @__PURE__ */ jsx(
987
+ "input",
988
+ {
989
+ value,
990
+ onChange: (e) => setValue(index, e.target.value),
991
+ onKeyDown: (e) => {
992
+ if (e.key === "Enter") {
993
+ e.preventDefault();
994
+ if (index === values.length - 1 && value.trim()) setValue(index, value);
995
+ const rows = e.currentTarget.closest("ul")?.querySelectorAll("input");
996
+ window.setTimeout(() => rows?.[index + 1]?.focus(), 0);
997
+ }
998
+ },
999
+ placeholder: index === values.length - 1 ? "Adicionar valor" : "",
1000
+ "aria-label": `Valor ${index + 1}`,
1001
+ className: "h-9 w-full rounded-md border bg-background px-2.5 text-sm outline-none focus:border-primary"
1002
+ }
1003
+ ),
1004
+ index < values.length - 1 && /* @__PURE__ */ jsx(
1005
+ "button",
1006
+ {
1007
+ type: "button",
1008
+ onClick: () => removeValue(index),
1009
+ "aria-label": `Remover valor ${index + 1}`,
1010
+ className: "fz-tap shrink-0 rounded-md p-1.5 text-muted-foreground transition hover:bg-muted hover:text-destructive",
1011
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1012
+ }
1013
+ )
1014
+ ] }, index)) }),
1015
+ hasDuplicate && /* @__PURE__ */ jsx("span", { className: "mt-1 block text-[11px] text-destructive", children: "H\xE1 valores repetidos \u2014 cada um vira uma variante, ent\xE3o dois iguais viram uma s\xF3." })
1016
+ ] })
1017
+ ] }),
1018
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
1019
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onClose, children: "Cancelar" }),
1020
+ /* @__PURE__ */ jsx(Button, { disabled: !ready, onClick: () => onCreate({ name: name.trim(), values: unique }), children: "Adicionar" })
1021
+ ] })
1022
+ ] }) });
908
1023
  }
909
1024
  var KIND_OF = {
910
1025
  // `sign: 0` = o razão aceita os dois lados (ajuste corrige para cima e para baixo).
@@ -1018,8 +1133,14 @@ async function commitMovement(args) {
1018
1133
  // a Lista, e é a que o usuário estava vendo
1019
1134
  "plg_shop_product_variants",
1020
1135
  // a matriz de variantes
1021
- "plg_shop_stock_balances",
1136
+ // O saldo mudou de livro: quem move o número agora é o razão do
1137
+ // plugin-inventory, e o saldo compartilhado é `public.stock_balances`.
1138
+ // Invalidar a tabela antiga deixava as Posições mostrando o valor anterior
1139
+ // até alguém recarregar — o cache apontando para onde ninguém mais escreve.
1140
+ "stock_balances",
1022
1141
  // as Posições
1142
+ "plg_inventory_stock_movements",
1143
+ // o histórico de movimentos
1023
1144
  "products"
1024
1145
  // o arquétipo, que as telas de Estoque leem
1025
1146
  ]) {
@@ -1349,10 +1470,11 @@ function ProductPositionsTab({ item, getProvider }) {
1349
1470
  return /* @__PURE__ */ jsx("p", { className: missing ? "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground" : "rounded-lg border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive", children: error });
1350
1471
  }
1351
1472
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1352
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
1353
- /* @__PURE__ */ jsxs("div", { children: [
1354
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Estoque do produto" }),
1355
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
1473
+ /* @__PURE__ */ jsx(
1474
+ DetailTabHeader,
1475
+ {
1476
+ title: "Estoque do produto",
1477
+ description: /* @__PURE__ */ jsxs(Fragment, { children: [
1356
1478
  /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.onHand }),
1357
1479
  " em m\xE3os \xB7",
1358
1480
  " ",
@@ -1361,31 +1483,31 @@ function ProductPositionsTab({ item, getProvider }) {
1361
1483
  " ",
1362
1484
  /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums text-foreground", children: total.available }),
1363
1485
  " dispon\xEDvel. O raz\xE3o n\xE3o se edita: um erro se corrige com o movimento contr\xE1rio."
1486
+ ] }),
1487
+ actions: variantId && /* @__PURE__ */ jsxs(Fragment, { children: [
1488
+ /* @__PURE__ */ jsxs(
1489
+ Button,
1490
+ {
1491
+ variant: "ghost",
1492
+ size: "sm",
1493
+ onClick: () => askAI({
1494
+ intro: STOCK_INTRO,
1495
+ hint: "O que entrou ou saiu\u2026",
1496
+ draft: `Entrada de unidades de ${product.name}`
1497
+ }),
1498
+ children: [
1499
+ /* @__PURE__ */ jsx(WandSparkles, { className: "mr-1.5 h-3.5 w-3.5" }),
1500
+ "Com IA"
1501
+ ]
1502
+ }
1503
+ ),
1504
+ /* @__PURE__ */ jsxs(Button, { onClick: () => open("entrada"), children: [
1505
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
1506
+ "Dar entrada"
1507
+ ] })
1364
1508
  ] })
1365
- ] }),
1366
- variantId && type === null && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1367
- /* @__PURE__ */ jsxs(
1368
- Button,
1369
- {
1370
- variant: "ghost",
1371
- size: "sm",
1372
- onClick: () => askAI({
1373
- intro: STOCK_INTRO,
1374
- hint: "O que entrou ou saiu\u2026",
1375
- draft: `Entrada de unidades de ${product.name}`
1376
- }),
1377
- children: [
1378
- /* @__PURE__ */ jsx(WandSparkles, { className: "mr-1.5 h-3.5 w-3.5" }),
1379
- "Com IA"
1380
- ]
1381
- }
1382
- ),
1383
- /* @__PURE__ */ jsxs(Button, { onClick: () => open("entrada"), children: [
1384
- /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
1385
- "Dar entrada"
1386
- ] })
1387
- ] })
1388
- ] }),
1509
+ }
1510
+ ),
1389
1511
  !variantId && // Nunca desabilitar um botão sem dizer por quê. Sem variante o razão não
1390
1512
  // tem onde postar, e um "Lançar" apagado sem explicação faz o lojista
1391
1513
  // clicar três vezes e concluir que a tela está quebrada.
@@ -1398,103 +1520,110 @@ function ProductPositionsTab({ item, getProvider }) {
1398
1520
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Variante" }),
1399
1521
  /* @__PURE__ */ jsx("select", { value: variantId, onChange: (e) => setVariantId(e.target.value), className: field, children: variants.map((v) => /* @__PURE__ */ jsx("option", { value: v.id, children: Object.values(v.options ?? {}).join(" \xB7 ") || v.sku || v.id.slice(0, 8) }, v.id)) })
1400
1522
  ] }),
1401
- type !== null && /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-4 motion-safe:animate-zoom-in", children: [
1402
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: MOVEMENT_TYPES.map((t) => /* @__PURE__ */ jsx(
1403
- "button",
1404
- {
1405
- type: "button",
1406
- onClick: () => setType(t.value),
1407
- className: `fz-tap rounded-button px-3 py-1.5 text-xs font-medium transition-colors ${type === t.value ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
1408
- children: t.label
1409
- },
1410
- t.value
1411
- )) }),
1412
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-4", children: [
1413
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
1414
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Quantas?" }),
1415
- /* @__PURE__ */ jsx(
1416
- "input",
1417
- {
1418
- ref: quantityRef,
1419
- type: "number",
1420
- inputMode: "numeric",
1421
- min: 1,
1422
- value: quantity,
1423
- onChange: (e) => setQuantity(e.target.value),
1424
- onKeyDown: (e) => {
1425
- if (e.key === "Enter") {
1426
- e.preventDefault();
1427
- void submit();
1428
- }
1429
- if (e.key === "Escape") {
1430
- e.preventDefault();
1431
- close();
1432
- }
1433
- },
1434
- className: "mt-1 h-12 w-28 rounded-md border bg-background px-3 text-2xl font-semibold tabular-nums outline-none focus:border-primary"
1435
- }
1436
- )
1437
- ] }),
1438
- /* @__PURE__ */ jsx("p", { "aria-live": "polite", className: "pb-2 text-sm text-muted-foreground", children: nextBalance === null ? /* @__PURE__ */ jsxs(Fragment, { children: [
1439
- "Fica em ",
1440
- /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums text-foreground", children: total.available })
1441
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1442
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.available }),
1443
- /* @__PURE__ */ jsx("span", { className: "mx-1.5", children: "\u2192" }),
1444
- /* @__PURE__ */ jsx("span", { className: `text-base font-semibold tabular-nums ${nextBalance < 0 ? "text-destructive" : "text-foreground"}`, children: nextBalance }),
1445
- nextBalance < 0 && /* @__PURE__ */ jsx("span", { className: "ml-2 text-destructive", children: "n\xE3o h\xE1 tudo isso" })
1446
- ] }) }),
1447
- /* @__PURE__ */ jsx("span", { className: "flex-1" }),
1448
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
1449
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Motivo" }),
1450
- /* @__PURE__ */ jsxs("select", { value: reasonCode, onChange: (e) => setReasonCode(e.target.value), className: field, children: [
1451
- /* @__PURE__ */ jsx("option", { value: "", children: "Padr\xE3o" }),
1452
- reasons.map((r) => /* @__PURE__ */ jsx("option", { value: r.key, children: r.label }, r.key))
1523
+ /* @__PURE__ */ jsx(Modal, { open: type !== null, onOpenChange: (o) => {
1524
+ if (!o) close();
1525
+ }, children: /* @__PURE__ */ jsxs(ModalContent, { className: "sm:max-w-lg", children: [
1526
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
1527
+ /* @__PURE__ */ jsx(ModalTitle, { children: "Lan\xE7ar movimento" }),
1528
+ /* @__PURE__ */ jsx(ModalDescription, { children: "O raz\xE3o n\xE3o se edita: um erro se corrige com o movimento contr\xE1rio." })
1529
+ ] }),
1530
+ /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
1531
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: MOVEMENT_TYPES.map((t) => /* @__PURE__ */ jsx(
1532
+ "button",
1533
+ {
1534
+ type: "button",
1535
+ onClick: () => setType(t.value),
1536
+ className: `fz-tap rounded-button px-3 py-1.5 text-xs font-medium transition-colors ${type === t.value ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
1537
+ children: t.label
1538
+ },
1539
+ t.value
1540
+ )) }),
1541
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-3", children: [
1542
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
1543
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Quantidade" }),
1544
+ /* @__PURE__ */ jsx(
1545
+ "input",
1546
+ {
1547
+ ref: quantityRef,
1548
+ type: "number",
1549
+ inputMode: "numeric",
1550
+ min: 1,
1551
+ value: quantity,
1552
+ onChange: (e) => setQuantity(e.target.value),
1553
+ onKeyDown: (e) => {
1554
+ if (e.key === "Enter") {
1555
+ e.preventDefault();
1556
+ void submit();
1557
+ }
1558
+ if (e.key === "Escape") {
1559
+ e.preventDefault();
1560
+ close();
1561
+ }
1562
+ },
1563
+ placeholder: "0",
1564
+ className: `${field} font-semibold tabular-nums`
1565
+ }
1566
+ ),
1567
+ /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "mt-1 block text-xs text-muted-foreground", children: nextBalance === null ? /* @__PURE__ */ jsxs(Fragment, { children: [
1568
+ "Fica em ",
1569
+ /* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums text-foreground", children: total.available })
1570
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1571
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: total.available }),
1572
+ /* @__PURE__ */ jsx("span", { className: "mx-1.5", children: "\u2192" }),
1573
+ /* @__PURE__ */ jsx("span", { className: `text-base font-semibold tabular-nums ${nextBalance < 0 ? "text-destructive" : "text-foreground"}`, children: nextBalance }),
1574
+ nextBalance < 0 && /* @__PURE__ */ jsx("span", { className: "ml-2 text-destructive", children: "n\xE3o h\xE1 tudo isso" })
1575
+ ] }) })
1576
+ ] }),
1577
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
1578
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Motivo" }),
1579
+ /* @__PURE__ */ jsxs("select", { value: reasonCode, onChange: (e) => setReasonCode(e.target.value), className: field, children: [
1580
+ /* @__PURE__ */ jsx("option", { value: "", children: "Padr\xE3o" }),
1581
+ reasons.map((r) => /* @__PURE__ */ jsx("option", { value: r.key, children: r.label }, r.key))
1582
+ ] })
1583
+ ] }),
1584
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
1585
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: type === "entrada" ? "Chegou em" : "Quando" }),
1586
+ /* @__PURE__ */ jsx(
1587
+ "input",
1588
+ {
1589
+ type: "date",
1590
+ value: occurredOn,
1591
+ max: todayISO(),
1592
+ onChange: (e) => setOccurredOn(e.target.value || todayISO()),
1593
+ className: field
1594
+ }
1595
+ )
1453
1596
  ] })
1454
1597
  ] }),
1455
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
1456
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: type === "entrada" ? "Chegou em" : "Quando" }),
1598
+ noteOpen ? /* @__PURE__ */ jsxs("label", { className: "mt-3 block", children: [
1599
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Observa\xE7\xE3o" }),
1457
1600
  /* @__PURE__ */ jsx(
1458
1601
  "input",
1459
1602
  {
1460
- type: "date",
1461
- value: occurredOn,
1462
- max: todayISO(),
1463
- onChange: (e) => setOccurredOn(e.target.value || todayISO()),
1464
- className: field
1603
+ autoFocus: true,
1604
+ value: note,
1605
+ onChange: (e) => setNote(e.target.value),
1606
+ className: field,
1607
+ placeholder: "Nota fiscal, quem entregou\u2026"
1465
1608
  }
1466
1609
  )
1467
- ] })
1468
- ] }),
1469
- noteOpen ? /* @__PURE__ */ jsxs("label", { className: "mt-3 block", children: [
1470
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Observa\xE7\xE3o" }),
1471
- /* @__PURE__ */ jsx(
1472
- "input",
1610
+ ] }) : /* @__PURE__ */ jsx(
1611
+ "button",
1473
1612
  {
1474
- autoFocus: true,
1475
- value: note,
1476
- onChange: (e) => setNote(e.target.value),
1477
- className: field,
1478
- placeholder: "Nota fiscal, quem entregou\u2026"
1613
+ type: "button",
1614
+ onClick: () => setNoteOpen(true),
1615
+ className: "mt-2 text-xs font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
1616
+ children: "+ observa\xE7\xE3o"
1479
1617
  }
1480
1618
  )
1481
- ] }) : /* @__PURE__ */ jsx(
1482
- "button",
1483
- {
1484
- type: "button",
1485
- onClick: () => setNoteOpen(true),
1486
- className: "mt-2 text-xs font-medium text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
1487
- children: "+ observa\xE7\xE3o"
1488
- }
1489
- ),
1490
- /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2", children: [
1491
- /* @__PURE__ */ jsx(Button, { onClick: () => void submit(), disabled: saving || amount === 0, children: saving ? "Lan\xE7ando\u2026" : "Lan\xE7ar" }),
1492
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: close, disabled: saving, children: "Cancelar" }),
1493
- /* @__PURE__ */ jsx("span", { className: "flex-1" }),
1619
+ ] }),
1620
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
1494
1621
  amount === 0 && // O motivo de o botão estar apagado, ao lado dele.
1495
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Diga quantas unidades" })
1622
+ /* @__PURE__ */ jsx("span", { className: "mr-auto text-xs text-muted-foreground", children: "Diga quantas unidades" }),
1623
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: close, disabled: saving, children: "Cancelar" }),
1624
+ /* @__PURE__ */ jsx(Button, { onClick: () => void submit(), disabled: saving || amount === 0, children: saving ? "Lan\xE7ando\u2026" : "Lan\xE7ar" })
1496
1625
  ] })
1497
- ] }),
1626
+ ] }) }),
1498
1627
  variantId && (rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: "Nenhuma posi\xE7\xE3o ainda \u2014 a primeira entrada abre o saldo." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: rows.map((r) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-card border bg-card p-3", children: [
1499
1628
  /* @__PURE__ */ jsx("span", { className: "flex-1 text-sm font-medium", children: r.location }),
1500
1629
  /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
@@ -1925,6 +2054,30 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
1925
2054
  hint: "Menor aparece primeiro.",
1926
2055
  showInTable: false
1927
2056
  },
2057
+ // — Busca (SEO) —
2058
+ // A coleção já tinha estes dois; o produto — que é a página onde alguém
2059
+ // CAI vindo do Google — não tinha nenhum, então cada página herdava o que
2060
+ // a vitrine inventasse a partir do nome.
2061
+ {
2062
+ key: "seoTitle",
2063
+ label: "T\xEDtulo da p\xE1gina",
2064
+ type: "text",
2065
+ group: "seo",
2066
+ span: 2,
2067
+ placeholder: "T\xEDtulo exibido nos resultados de busca",
2068
+ hint: "Vazio usa o nome do produto.",
2069
+ showInTable: false
2070
+ },
2071
+ {
2072
+ key: "seoDescription",
2073
+ label: "Meta descri\xE7\xE3o",
2074
+ type: "textarea",
2075
+ group: "seo",
2076
+ span: 2,
2077
+ placeholder: "O trecho que aparece embaixo do t\xEDtulo no Google",
2078
+ hint: "Vazio usa a descri\xE7\xE3o do produto.",
2079
+ showInTable: false
2080
+ },
1928
2081
  // — Envio —
1929
2082
  // O desenho é o do Shopify e a ordem importa: o interruptor primeiro, e o
1930
2083
  // resto da seção só existe depois dele. Perguntar altura de um e-book não é
@@ -2060,6 +2213,7 @@ function buildShopProductEntity(currencyCode = "BRL", getProvider) {
2060
2213
  { id: "pricing", label: "Pre\xE7o", description: 'O pre\xE7o comparativo vira o "de" riscado na vitrine.', columns: 3 },
2061
2214
  { id: "inventory", label: "Estoque", columns: 2 },
2062
2215
  { id: "publishing", label: "Publica\xE7\xE3o", description: "Controla se o produto aparece na loja.", columns: 2 },
2216
+ { id: "seo", label: "Busca (SEO)", description: "Como este produto aparece nos resultados de busca.", columns: 2 },
2063
2217
  {
2064
2218
  id: "shipping",
2065
2219
  label: "Envio",
@@ -5116,7 +5270,7 @@ var melhorEnvioConnectorDef = {
5116
5270
  direction: "outbound",
5117
5271
  docsUrl: "https://docs.melhorenvio.com.br",
5118
5272
  // A quote WRITES NOTHING — `createShippingQuoter` says so in as many words,
5119
- // and 029_connections.sql is why: one row per quote would make plg_sync_runs
5273
+ // and 029_connections.sql is why: one row per quote would make sync_runs
5120
5274
  // the largest table in the pool within weeks. So `order` is read-only here,
5121
5275
  // and the only write is the label, which is an effect with an idempotency key
5122
5276
  // (`createLabelBuyer`) because a label bought twice is money spent twice.
@@ -5187,7 +5341,7 @@ var melhorEnvioConnectorDef = {
5187
5341
  ),
5188
5342
  ...connectionSettings(store),
5189
5343
  // This used to say the platform's redirect route writes `active = true`. It
5190
- // does not, and says so itself: "It does not touch `plg_connections` in the
5344
+ // does not, and says so itself: "It does not touch `connections` in the
5191
5345
  // tenant's pool. Per FAY-1388 ... the app writes its own." Bling shipped with
5192
5346
  // the same belief and looped — authorize, come back, still "not connected",
5193
5347
  // click again, forever.
@@ -5338,7 +5492,7 @@ var appmaxConnectorDef = {
5338
5492
  }
5339
5493
  ],
5340
5494
  // Read from the row the list already batched, so a screen with N connectors
5341
- // costs one plg_connections request rather than N.
5495
+ // costs one connections request rather than N.
5342
5496
  ...connectionStatus(
5343
5497
  APPMAX_CONNECTOR_ID,
5344
5498
  (connection) => ({
@@ -5614,7 +5768,7 @@ var abacatePayConnectorDef = {
5614
5768
  }
5615
5769
  ],
5616
5770
  // Read from the row the list already batched, so a screen with N connectors
5617
- // costs one plg_connections request rather than N.
5771
+ // costs one connections request rather than N.
5618
5772
  ...connectionStatus(
5619
5773
  ABACATEPAY_CONNECTOR_ID,
5620
5774
  (connection) => ({
@@ -5759,7 +5913,7 @@ var stripeConnectConnectorDef = {
5759
5913
  // Reached with no values when the browser returns marked `connected`. It is
5760
5914
  // required even though nothing is collected here: the platform callback stores
5761
5915
  // the credential but deliberately never touches the pool, so this is the only
5762
- // thing that writes the tenant's `plg_connections` row.
5916
+ // thing that writes the tenant's `connections` row.
5763
5917
  saveConnection: async () => {
5764
5918
  await store4.save({ active: true });
5765
5919
  },
@@ -6033,7 +6187,7 @@ var vindiConnectorDef = {
6033
6187
  }
6034
6188
  ],
6035
6189
  // Read from the row the list already batched, so a screen with N connectors
6036
- // costs one plg_connections request rather than N.
6190
+ // costs one connections request rather than N.
6037
6191
  ...connectionStatus(
6038
6192
  VINDI_CONNECTOR_ID,
6039
6193
  (connection) => ({
@@ -6065,16 +6219,23 @@ var vindiConnectorDef = {
6065
6219
  var MERCADOPAGO_CONNECTOR_ID = "mercadopago";
6066
6220
  var MERCADOPAGO_LATENCY_BUDGET_MS = 6e3;
6067
6221
  var store6 = createConnectionStore(MERCADOPAGO_CONNECTOR_ID);
6068
- function connectionDetail2() {
6069
- return "Pix e cart\xE3o";
6222
+ function connectionDetail2(connection) {
6223
+ const account = String(connection?.displayName ?? "").trim();
6224
+ return account ? `${account} \xB7 Pix e cart\xE3o` : "Pix e cart\xE3o";
6070
6225
  }
6071
6226
  function MercadoPagoExtraPanel() {
6072
6227
  const [connected, setConnected] = useState(false);
6228
+ const [account, setAccount] = useState({ name: null, id: null });
6073
6229
  const [loaded, setLoaded] = useState(false);
6074
6230
  useEffect(() => {
6075
6231
  let cancelled = false;
6076
6232
  void store6.get().then((c) => {
6077
- if (!cancelled) setConnected(!!c?.active);
6233
+ if (cancelled) return;
6234
+ setConnected(!!c?.active);
6235
+ setAccount({
6236
+ name: String(c?.displayName ?? "").trim() || null,
6237
+ id: String(c?.providerAccountKey ?? "").trim() || null
6238
+ });
6078
6239
  }).catch(() => {
6079
6240
  }).finally(() => {
6080
6241
  if (!cancelled) setLoaded(true);
@@ -6085,6 +6246,29 @@ function MercadoPagoExtraPanel() {
6085
6246
  }, []);
6086
6247
  if (!loaded) return null;
6087
6248
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t pt-4 text-xs text-muted-foreground", children: [
6249
+ connected && (account.name || account.id) && /* Named before anything else is explained. "Conectado" is not an answer
6250
+ to "connected to WHAT" — and a store whose money is going to the
6251
+ wrong Mercado Pago account looks identical to one whose money is not. */
6252
+ /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-muted/40 p-3", children: [
6253
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: "Conta conectada" }),
6254
+ account.name && /* @__PURE__ */ jsx("p", { className: "mt-1 break-words", children: account.name }),
6255
+ account.id && /* @__PURE__ */ jsxs("p", { className: "mt-0.5", children: [
6256
+ "ID no Mercado Pago: ",
6257
+ /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5", children: account.id })
6258
+ ] }),
6259
+ /* @__PURE__ */ jsxs("p", { className: "mt-1", children: [
6260
+ "N\xE3o \xE9 esta a conta? Clique em ",
6261
+ /* @__PURE__ */ jsx("strong", { children: "Desconectar" }),
6262
+ " e conecte de novo, logado na conta certa do Mercado Pago."
6263
+ ] })
6264
+ ] }),
6265
+ connected && !account.name && !account.id && /* An older connection, made before the account was recorded. Saying so
6266
+ beats showing nothing and letting it read as "no account". */
6267
+ /* @__PURE__ */ jsxs("p", { className: "rounded-md border border-dashed p-3", children: [
6268
+ "Esta conex\xE3o foi feita antes de a loja passar a guardar qual conta foi autorizada. Para ver o nome da conta aqui, clique em ",
6269
+ /* @__PURE__ */ jsx("strong", { children: "Desconectar" }),
6270
+ " e conecte novamente."
6271
+ ] }),
6088
6272
  connected ? /* @__PURE__ */ jsxs(Fragment, { children: [
6089
6273
  /* @__PURE__ */ jsxs("p", { children: [
6090
6274
  "Pix e cart\xE3o acontecem ",
@@ -6205,7 +6389,7 @@ var mercadoPagoConnectorDef = {
6205
6389
  }
6206
6390
  ],
6207
6391
  // Read from the row the list already batched, so a screen with N connectors
6208
- // costs one plg_connections request rather than N.
6392
+ // costs one connections request rather than N.
6209
6393
  //
6210
6394
  // No `settingsStatus`: this connector has nothing to configure, so a
6211
6395
  // connection either has its credential pair or does not. "Conectado, falta
@@ -6214,7 +6398,7 @@ var mercadoPagoConnectorDef = {
6214
6398
  MERCADOPAGO_CONNECTOR_ID,
6215
6399
  (connection) => ({
6216
6400
  connected: !!connection?.active,
6217
- detail: connectionDetail2(),
6401
+ detail: connectionDetail2(connection),
6218
6402
  plane: "request"
6219
6403
  }),
6220
6404
  { store: () => store6 }
@@ -6229,190 +6413,19 @@ var mercadoPagoConnectorDef = {
6229
6413
  // key alone cannot open an order. Testing belongs to the edge function that
6230
6414
  // holds the credential.
6231
6415
  saveConnection: async () => {
6232
- await store6.save({ active: true });
6416
+ const account = await describeConnectorAccount({
6417
+ connectorId: MERCADOPAGO_CONNECTOR_ID,
6418
+ tenantId: getActiveTenantId()
6419
+ });
6420
+ await store6.save({
6421
+ active: true,
6422
+ ...account?.displayName ? { displayName: account.displayName } : {},
6423
+ ...account?.providerAccountKey ? { providerAccountKey: account.providerAccountKey } : {}
6424
+ });
6233
6425
  },
6234
6426
  disconnect: () => store6.disconnect(),
6235
6427
  ExtraPanel: MercadoPagoExtraPanel
6236
6428
  };
6237
- var KIND_META = {
6238
- online_store: { label: "Loja Online", icon: Globe, hint: "Seu storefront fayz \u2014 a vitrine p\xFAblica da loja.", placeholder: "https://minhaloja.com.br/" },
6239
- whatsapp: { label: "WhatsApp", icon: MessageCircle, hint: "Link direto para o WhatsApp Business.", placeholder: "https://wa.me/5521999999999" },
6240
- instagram: { label: "Instagram", icon: Camera, hint: "Perfil ou loja no Instagram.", placeholder: "https://instagram.com/sualoja" },
6241
- tiktok_shop: { label: "TikTok Shop", icon: Music, hint: "Sua vitrine no TikTok Shop.", placeholder: "https://shop.tiktok.com/@sualoja" },
6242
- marketplace: { label: "Marketplace", icon: ShoppingBag, hint: "Mercado Livre, Amazon, Shopee\u2026", placeholder: "https://\u2026" },
6243
- physical: { label: "Loja F\xEDsica", icon: MapPin, hint: "Endere\xE7o ou ponto de venda f\xEDsico.", placeholder: "https://maps.google.com/\u2026" },
6244
- custom: { label: "Outro canal", icon: Link2, hint: "Qualquer outro ponto de venda com um link.", placeholder: "https://\u2026" }
6245
- };
6246
- var ADDABLE_KINDS = ["whatsapp", "instagram", "tiktok_shop", "marketplace", "physical", "custom"];
6247
- function resolve2(resolver) {
6248
- return typeof resolver === "function" ? resolver() : resolver ?? getShopProvider();
6249
- }
6250
- function notifyChannelsChanged() {
6251
- if (typeof window !== "undefined") window.dispatchEvent(new Event("fayz:channels-changed"));
6252
- }
6253
- function ChannelsPage({ resolveProvider }) {
6254
- const provider = React13__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
6255
- const supported = typeof provider.listSalesChannels === "function";
6256
- const [channels, setChannels] = React13__default.useState([]);
6257
- const [loading, setLoading] = React13__default.useState(true);
6258
- const [adding, setAdding] = React13__default.useState(null);
6259
- const reload = React13__default.useCallback(async () => {
6260
- if (!provider.listSalesChannels) {
6261
- setLoading(false);
6262
- return;
6263
- }
6264
- setLoading(true);
6265
- try {
6266
- const rows = await provider.listSalesChannels();
6267
- if (!rows.some((c) => c.kind === "online_store") && provider.createSalesChannel) {
6268
- await provider.createSalesChannel({ kind: "online_store", label: "Loja Online", enabled: true, position: 0 });
6269
- setChannels(await provider.listSalesChannels());
6270
- } else {
6271
- setChannels(rows);
6272
- }
6273
- } finally {
6274
- setLoading(false);
6275
- }
6276
- }, [provider]);
6277
- React13__default.useEffect(() => {
6278
- void reload();
6279
- }, [reload]);
6280
- const patch = async (id, input) => {
6281
- if (!provider.updateSalesChannel) return;
6282
- const updated = await provider.updateSalesChannel(id, input);
6283
- setChannels((prev) => prev.map((c) => c.id === id ? updated : c));
6284
- notifyChannelsChanged();
6285
- };
6286
- const remove = async (id) => {
6287
- if (!provider.deleteSalesChannel) return;
6288
- await provider.deleteSalesChannel(id);
6289
- setChannels((prev) => prev.filter((c) => c.id !== id));
6290
- notifyChannelsChanged();
6291
- };
6292
- const add = async (kind, label, url) => {
6293
- if (!provider.createSalesChannel) return;
6294
- const created = await provider.createSalesChannel({ kind, label: label || KIND_META[kind].label, url: url || null, enabled: true, position: channels.length });
6295
- setChannels((prev) => [...prev, created]);
6296
- setAdding(null);
6297
- notifyChannelsChanged();
6298
- };
6299
- if (!supported) {
6300
- return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl p-6", children: [
6301
- /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-foreground", children: "Canais de venda" }),
6302
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Este provedor de loja ainda n\xE3o exp\xF5e canais de venda." })
6303
- ] });
6304
- }
6305
- const online = channels.find((c) => c.kind === "online_store");
6306
- const others = channels.filter((c) => c.kind !== "online_store");
6307
- return /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl p-6", children: [
6308
- /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
6309
- /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-foreground", children: "Canais de venda" }),
6310
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Onde sua loja vende. A Loja Online \xE9 o seu storefront fayz; os demais s\xE3o links que aparecem no menu lateral." })
6311
- ] }),
6312
- loading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Carregando\u2026" }) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
6313
- online && /* @__PURE__ */ jsx(OnlineStoreCard, { channel: online, onSave: (url) => patch(online.id, { url }), onToggle: (enabled) => patch(online.id, { enabled }) }),
6314
- /* @__PURE__ */ jsxs("div", { children: [
6315
- /* @__PURE__ */ jsx("div", { className: "mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Outros canais" }) }),
6316
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
6317
- others.map((c) => /* @__PURE__ */ jsx(ChannelRow, { channel: c, onSave: (url) => patch(c.id, { url }), onToggle: (enabled) => patch(c.id, { enabled }), onRemove: () => remove(c.id) }, c.id)),
6318
- others.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-card border border-dashed border-border p-4 text-sm text-muted-foreground", children: "Nenhum canal adicional ainda. Adicione WhatsApp, Instagram, TikTok Shop e outros pontos de venda." })
6319
- ] }),
6320
- adding ? /* @__PURE__ */ jsx(AddChannelForm, { kind: adding, onCancel: () => setAdding(null), onAdd: (label, url) => add(adding, label, url), onKind: setAdding }) : /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: ADDABLE_KINDS.map((k) => {
6321
- const Icon = KIND_META[k].icon;
6322
- return /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setAdding(k), children: [
6323
- /* @__PURE__ */ jsx(Icon, { className: "mr-1.5 h-3.5 w-3.5" }),
6324
- " ",
6325
- KIND_META[k].label
6326
- ] }, k);
6327
- }) })
6328
- ] })
6329
- ] })
6330
- ] });
6331
- }
6332
- function OnlineStoreCard({ channel, onSave, onToggle }) {
6333
- const [url, setUrl] = React13__default.useState(channel.url ?? "");
6334
- const dirty = (channel.url ?? "") !== url;
6335
- const meta = KIND_META.online_store;
6336
- return /* @__PURE__ */ jsx("div", { className: "rounded-card border border-border bg-card p-5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
6337
- /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(meta.icon, { className: "h-5 w-5 text-primary" }) }),
6338
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
6339
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
6340
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Loja Online" }),
6341
- /* @__PURE__ */ jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${channel.enabled ? "bg-emerald-100 text-emerald-800" : "bg-muted text-muted-foreground"}`, children: channel.enabled ? "Ativa" : "Inativa" })
6342
- ] }),
6343
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-sm text-muted-foreground", children: meta.hint }),
6344
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-2 sm:flex-row sm:items-center", children: [
6345
- /* @__PURE__ */ jsx(Input, { value: url, placeholder: meta.placeholder, onChange: (e) => setUrl(e.target.value), className: "flex-1" }),
6346
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
6347
- /* @__PURE__ */ jsx(Button, { size: "sm", disabled: !dirty, onClick: () => onSave(url.trim() || null), children: "Salvar" }),
6348
- /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", disabled: !channel.url, onClick: () => channel.url && window.open(channel.url, "_blank", "noopener,noreferrer"), children: [
6349
- /* @__PURE__ */ jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }),
6350
- " Ver loja"
6351
- ] })
6352
- ] })
6353
- ] }),
6354
- /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onToggle(!channel.enabled), className: "text-xs font-medium text-muted-foreground hover:text-foreground", children: channel.enabled ? "Desativar canal" : "Ativar canal" }) })
6355
- ] })
6356
- ] }) });
6357
- }
6358
- function ChannelRow({ channel, onSave, onToggle, onRemove }) {
6359
- const [url, setUrl] = React13__default.useState(channel.url ?? "");
6360
- const [editing, setEditing] = React13__default.useState(false);
6361
- const meta = KIND_META[channel.kind] ?? KIND_META.custom;
6362
- const Icon = meta.icon;
6363
- return /* @__PURE__ */ jsx("div", { className: "rounded-card border border-border bg-card p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
6364
- /* @__PURE__ */ jsx("div", { className: `flex h-9 w-9 shrink-0 items-center justify-center rounded-full ${channel.enabled ? "bg-primary/10" : "bg-muted"}`, children: /* @__PURE__ */ jsx(Icon, { className: `h-4 w-4 ${channel.enabled ? "text-primary" : "text-muted-foreground"}` }) }),
6365
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
6366
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: channel.label }),
6367
- editing ? /* @__PURE__ */ jsx(Input, { value: url, placeholder: meta.placeholder, onChange: (e) => setUrl(e.target.value), className: "mt-1 h-8" }) : /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: channel.url || "Sem link configurado" })
6368
- ] }),
6369
- /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1", children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
6370
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
6371
- onSave(url.trim() || null);
6372
- setEditing(false);
6373
- }, children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }),
6374
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
6375
- setUrl(channel.url ?? "");
6376
- setEditing(false);
6377
- }, children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
6378
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6379
- channel.url && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => window.open(channel.url, "_blank", "noopener,noreferrer"), children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" }) }),
6380
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onToggle(!channel.enabled), title: channel.enabled ? "Desativar" : "Ativar", className: `rounded-full px-2 py-0.5 text-xs font-medium ${channel.enabled ? "bg-emerald-100 text-emerald-800" : "bg-muted text-muted-foreground"}`, children: channel.enabled ? "Ativo" : "Inativo" }),
6381
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(true), children: "Editar" }),
6382
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: onRemove, children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 text-red-500" }) })
6383
- ] }) })
6384
- ] }) });
6385
- }
6386
- function AddChannelForm({ kind, onKind, onCancel, onAdd }) {
6387
- const meta = KIND_META[kind];
6388
- const [label, setLabel] = React13__default.useState(meta.label);
6389
- const [url, setUrl] = React13__default.useState("");
6390
- React13__default.useEffect(() => {
6391
- setLabel(KIND_META[kind].label);
6392
- }, [kind]);
6393
- return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-card border border-border bg-card p-4", children: [
6394
- /* @__PURE__ */ jsx("div", { className: "mb-3 flex flex-wrap gap-2", children: ADDABLE_KINDS.map((k) => {
6395
- const Icon = KIND_META[k].icon;
6396
- return /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => onKind(k), className: `flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium ${k === kind ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`, children: [
6397
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
6398
- " ",
6399
- KIND_META[k].label
6400
- ] }, k);
6401
- }) }),
6402
- /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs text-muted-foreground", children: meta.hint }),
6403
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [
6404
- /* @__PURE__ */ jsx(Input, { value: label, placeholder: "Nome do canal", onChange: (e) => setLabel(e.target.value), className: "sm:w-48" }),
6405
- /* @__PURE__ */ jsx(Input, { value: url, placeholder: meta.placeholder, onChange: (e) => setUrl(e.target.value), className: "flex-1" })
6406
- ] }),
6407
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex gap-2", children: [
6408
- /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => onAdd(label, url), children: [
6409
- /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-3.5 w-3.5" }),
6410
- " Adicionar"
6411
- ] }),
6412
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: onCancel, children: "Cancelar" })
6413
- ] })
6414
- ] });
6415
- }
6416
6429
 
6417
6430
  // src/views/media-links.ts
6418
6431
  function recordRoute(reference) {
@@ -6429,7 +6442,7 @@ var TABS = [
6429
6442
  { value: "video", label: "V\xEDdeos" }
6430
6443
  ];
6431
6444
  var LOW_RESOLUTION_PX = 800;
6432
- function resolve3(resolver) {
6445
+ function resolve2(resolver) {
6433
6446
  return typeof resolver === "function" ? resolver() : resolver ?? getShopProvider();
6434
6447
  }
6435
6448
  function formatSize(bytes) {
@@ -6439,7 +6452,7 @@ function formatSize(bytes) {
6439
6452
  return `${(bytes / 1024 ** i).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
6440
6453
  }
6441
6454
  function ContentPage({ resolveProvider }) {
6442
- const provider = React13__default.useMemo(() => resolve3(resolveProvider), [resolveProvider]);
6455
+ const provider = React13__default.useMemo(() => resolve2(resolveProvider), [resolveProvider]);
6443
6456
  const supported = typeof provider.listMediaFiles === "function";
6444
6457
  const [files, setFiles] = React13__default.useState(null);
6445
6458
  const [tab, setTab] = React13__default.useState("all");
@@ -7196,7 +7209,7 @@ var blingConnectorDef = {
7196
7209
  // where the question is actually asked. It is the only thing in that panel
7197
7210
  // that was neither status, nor history, nor an act.
7198
7211
  disconnectNote: "Desconectar congela o espelho: nenhum produto \xE9 apagado e o estoque para de ser atualizado.",
7199
- // No panel of its own. The history is `plg_sync_runs` read through the shared
7212
+ // No panel of its own. The history is `sync_runs` read through the shared
7200
7213
  // store, and the shared surface renders it — including the "first load brings
7201
7214
  // a sample" sentence, which it derives from `firstLoad` above rather than
7202
7215
  // repeating the number in prose.
@@ -7767,7 +7780,7 @@ var ORDERS = "plg_shop_rep_orders";
7767
7780
  var ITEMS = "plg_shop_rep_order_items";
7768
7781
  var CUSTOMERS = "plg_shop_rep_customers";
7769
7782
  var PRODUCTS = "plg_shop_rep_products";
7770
- var CANONICAL = "public.plg_shop_orders";
7783
+ var CANONICAL = "public.v_shop_orders";
7771
7784
  var CAT = {
7772
7785
  sales: tl("Sales", "Vendas"),
7773
7786
  orders: tl("Orders", "Pedidos"),
@@ -8590,7 +8603,7 @@ function buildShopAnalyticsCards() {
8590
8603
  }
8591
8604
 
8592
8605
  // src/index.ts
8593
- var ShopPage = React13__default.lazy(() => import('./ShopPage-6IQ7WKQG.js').then((m) => ({ default: m.ShopPage })));
8606
+ var ShopPage = React13__default.lazy(() => import('./ShopPage-5IERN4E5.js').then((m) => ({ default: m.ShopPage })));
8594
8607
  function createShopPlugin(options) {
8595
8608
  const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
8596
8609
  setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
@@ -8805,10 +8818,9 @@ function createShopPlugin(options) {
8805
8818
  });
8806
8819
  const ContentPageWrapped = () => React13__default.createElement(ContentPage, { resolveProvider: options?.provider });
8807
8820
  ContentPageWrapped.displayName = "ContentPage";
8808
- const ChannelsPageWrapped = () => React13__default.createElement(ChannelsPage, { resolveProvider: options?.provider });
8809
- ChannelsPageWrapped.displayName = "ChannelsPage";
8810
8821
  const navSection = options?.navSection ?? "main";
8811
8822
  const navPosition = options?.navPosition ?? 6;
8823
+ const siteRoute = options?.siteRoute ?? "/site";
8812
8824
  const NAV = [
8813
8825
  // Pedidos ▸ Pedidos · Clientes
8814
8826
  {
@@ -8873,7 +8885,6 @@ function createShopPlugin(options) {
8873
8885
  }
8874
8886
  ];
8875
8887
  const HIDDEN_ROUTES = [
8876
- { path: "/store", component: ChannelsPageWrapped, title: label("store", "Canais de venda") },
8877
8888
  // Reached from the Clientes tab strip, deliberately not from the menu:
8878
8889
  // three sidebar entries for one destination is the duplication the tab
8879
8890
  // strip exists to avoid. Nesting under /orders/customers also keeps
@@ -8916,7 +8927,8 @@ function createShopPlugin(options) {
8916
8927
  // rota vem do mesmo lugar que a tela usa.
8917
8928
  { path: "/shop/shipping", component: redirectTo(shippingRegistryPath(SHIPPING_ZONES_REGISTRY)) },
8918
8929
  { path: "/store/shipping", component: redirectTo(shippingRegistryPath(SHIPPING_ZONES_REGISTRY)) },
8919
- { path: "/shop/channels", component: redirectTo("/store") },
8930
+ { path: "/store", component: redirectTo(siteRoute) },
8931
+ { path: "/shop/channels", component: redirectTo(siteRoute) },
8920
8932
  { path: "/customers", component: redirectTo("/orders/customers") },
8921
8933
  // A mensageria da loja virou parte de Automações — mesmos eventos, agora ao
8922
8934
  // lado dos do CRM e da agenda, e com WhatsApp além do e-mail.
@@ -8930,7 +8942,7 @@ function createShopPlugin(options) {
8930
8942
  // O que a loja pede de um tenant novo, na ordem em que faz diferença.
8931
8943
  // Vira linha de tarefa, aparece no checklist da trilha e é o roteiro que o
8932
8944
  // assistente segue — ver ./lib/onboarding.ts.
8933
- onboarding: { program: buildShopOnboarding() },
8945
+ onboarding: { program: buildShopOnboarding(siteRoute) },
8934
8946
  name: options?.navLabel ?? "Shop",
8935
8947
  icon: "ShoppingBag",
8936
8948
  version: "1.0.0",