@fayz-ai/plugin-shop 0.16.2 → 0.16.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { shopCustomerLookup, buildShopCustomerEntity, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, SHIPPING_PROFILES_REGISTRY, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-RPBKGW24.js';
1
+ import { shopCustomerLookup, resolve, invalidateProductLists, listProductsDetailed, applyQuery, errorMessage, listProductsCached, resolveTerm, candidateMeta, SHIPPING_PROFILES_REGISTRY, packageMeasureLabel, PACKAGES_REGISTRY, packagesRegistry, SHIPPING_ZONES_REGISTRY, buildShopRegistries, locationSummary, LOCATIONS_REGISTRY } from './chunk-6DRDPROL.js';
2
2
  import * as React13 from 'react';
3
3
  import React13__default, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
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, describeConnectorAccount, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, adminNavigateTo, PageActions, 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, Upload, ImageOff, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, 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';
6
+ import { fayzConnectorOAuth, describeConnectorAccount, useOrganizationStore, registerEntityPath, registerPersonLookup, createCrudPage, registerCrudNavTabs, adminNavigateTo, PageActions, 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, Users, UsersRound, Filter, UserX, Tag, TicketCheck, Layers, CreditCard, CircleDollarSign, Upload, ImageOff, Plus, Loader2, WandSparkles, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, AlertTriangle, X, ExternalLink, Box, ChevronDown, Film, FileText, Warehouse, ArrowRight, MapPin, Car, Truck, Info, Check, Sparkles, Ban, ListChecks, Search, Building2 } from 'lucide-react';
8
+ import { PageTabs, PageHeaderActions, 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';
@@ -2971,28 +2971,6 @@ function createShopProductDataProvider(provider) {
2971
2971
  };
2972
2972
  }
2973
2973
 
2974
- // src/data/customerDataProvider.ts
2975
- function createShopCustomerDataProvider(provider) {
2976
- return {
2977
- async list(query) {
2978
- const rows = await resolve(provider).listCustomers(
2979
- query.search ? { search: query.search } : void 0
2980
- );
2981
- return applyQuery(rows, query);
2982
- },
2983
- async create(data) {
2984
- return resolve(provider).createCustomer(data);
2985
- },
2986
- async update(id, data) {
2987
- const { ordersCount: _o, totalSpent: _t, ...patch } = data;
2988
- return resolve(provider).updateCustomer(id, patch);
2989
- },
2990
- async remove(id) {
2991
- await resolve(provider).deleteCustomer(id);
2992
- }
2993
- };
2994
- }
2995
-
2996
2974
  // src/data/discountDataProvider.ts
2997
2975
  function createShopDiscountDataProvider(provider) {
2998
2976
  return {
@@ -6575,11 +6553,12 @@ function ContentPage({ resolveProvider }) {
6575
6553
  }
6576
6554
  const attached = visible.filter((f) => f.reference);
6577
6555
  const loose = visible.filter((f) => !f.reference);
6578
- const productCount = (files ?? []).filter((f) => f.reference).length;
6579
- const caption = files === null ? "" : [
6580
- `${files.length} ${files.length === 1 ? "arquivo" : "arquivos"}`,
6581
- productCount > 0 ? `${productCount} ${productCount === 1 ? "de produto" : "de produtos"}` : ""
6582
- ].filter(Boolean).join(" \xB7 ");
6556
+ const countOf = (t) => {
6557
+ if (files === null) return void 0;
6558
+ if (t === "all") return files.length;
6559
+ return files.filter((f) => f.kind === t).length;
6560
+ };
6561
+ const tabs = TABS.map((t) => ({ ...t, count: countOf(t.value) }));
6583
6562
  const uploadActions = [{
6584
6563
  id: "upload",
6585
6564
  label: uploading ? "Enviando\u2026" : "Enviar arquivos",
@@ -6601,9 +6580,53 @@ function ContentPage({ resolveProvider }) {
6601
6580
  },
6602
6581
  file.id
6603
6582
  )) });
6604
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
6605
- supported && provider.uploadMediaFile && /* @__PURE__ */ jsxs(Fragment, { children: [
6583
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6584
+ /* @__PURE__ */ jsx(
6585
+ PageTabs,
6586
+ {
6587
+ tabs,
6588
+ value: tab,
6589
+ onChange: setTab,
6590
+ "aria-label": "Arquivos"
6591
+ }
6592
+ ),
6593
+ supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: uploadActions }) }),
6594
+ /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
6595
+ !supported ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
6596
+ /* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
6597
+ /* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Armazenamento n\xE3o configurado" }),
6598
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "A biblioteca de arquivos precisa do storage da loja, que n\xE3o est\xE1 dispon\xEDvel neste modo." })
6599
+ ] }) : files === null ? /* @__PURE__ */ jsx("div", { className: "fz-cards gap-4", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs(Card, { className: "p-3", children: [
6600
+ /* @__PURE__ */ jsx("div", { className: "aspect-square animate-pulse rounded-md bg-muted" }),
6601
+ /* @__PURE__ */ jsx("div", { className: "mt-2 h-3.5 w-2/3 animate-pulse rounded bg-muted" })
6602
+ ] }, i)) }) : visible.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
6603
+ /* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
6604
+ /* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Nenhum arquivo ainda" }),
6605
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "As fotos dos seus produtos e o que voc\xEA enviar aqui aparecem nesta biblioteca." })
6606
+ ] }) : attached.length && loose.length ? (
6607
+ // Só separa quando há os dois: um título sozinho em cima de tudo é
6608
+ // barulho, e a etiqueta em cada cartão já diz de quem é a foto.
6609
+ /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
6610
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
6611
+ /* @__PURE__ */ jsx(SectionTitle, { title: "Fotos de produtos", count: attached.length }),
6612
+ renderGrid(attached)
6613
+ ] }),
6614
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
6615
+ /* @__PURE__ */ jsx(SectionTitle, { title: "Biblioteca", count: loose.length }),
6616
+ renderGrid(loose)
6617
+ ] })
6618
+ ] })
6619
+ ) : renderGrid(visible),
6606
6620
  /* @__PURE__ */ jsx(
6621
+ DeleteConfirm,
6622
+ {
6623
+ file: pendingDelete,
6624
+ loading: deleting,
6625
+ onConfirm: () => void confirmDelete(),
6626
+ onCancel: () => setPendingDelete(null)
6627
+ }
6628
+ ),
6629
+ supported && provider.uploadMediaFile && /* @__PURE__ */ jsx(
6607
6630
  "input",
6608
6631
  {
6609
6632
  ref: uploadInputRef,
@@ -6616,54 +6639,8 @@ function ContentPage({ resolveProvider }) {
6616
6639
  });
6617
6640
  }
6618
6641
  }
6619
- ),
6620
- /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: uploadActions }) })
6621
- ] }),
6622
- /* @__PURE__ */ jsx(
6623
- PageTabs,
6624
- {
6625
- className: "mb-0",
6626
- tabs: TABS,
6627
- value: tab,
6628
- onChange: setTab,
6629
- "aria-label": "Arquivos",
6630
- actions: caption ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: caption }) : void 0
6631
- }
6632
- ),
6633
- !supported ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
6634
- /* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
6635
- /* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Armazenamento n\xE3o configurado" }),
6636
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "A biblioteca de arquivos precisa do storage da loja, que n\xE3o est\xE1 dispon\xEDvel neste modo." })
6637
- ] }) : files === null ? /* @__PURE__ */ jsx("div", { className: "fz-cards gap-4", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs(Card, { className: "p-3", children: [
6638
- /* @__PURE__ */ jsx("div", { className: "aspect-square animate-pulse rounded-md bg-muted" }),
6639
- /* @__PURE__ */ jsx("div", { className: "mt-2 h-3.5 w-2/3 animate-pulse rounded bg-muted" })
6640
- ] }, i)) }) : visible.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-12 text-center", children: [
6641
- /* @__PURE__ */ jsx(ImageOff, { className: "mx-auto h-8 w-8 text-muted-foreground" }),
6642
- /* @__PURE__ */ jsx("p", { className: "mt-3 font-medium", children: "Nenhum arquivo ainda" }),
6643
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "As fotos dos seus produtos e o que voc\xEA enviar aqui aparecem nesta biblioteca." })
6644
- ] }) : attached.length && loose.length ? (
6645
- // Só separa quando há os dois: um título sozinho em cima de tudo é
6646
- // barulho, e a etiqueta em cada cartão já diz de quem é a foto.
6647
- /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
6648
- /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
6649
- /* @__PURE__ */ jsx(SectionTitle, { title: "Fotos de produtos", count: attached.length }),
6650
- renderGrid(attached)
6651
- ] }),
6652
- /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
6653
- /* @__PURE__ */ jsx(SectionTitle, { title: "Biblioteca", count: loose.length }),
6654
- renderGrid(loose)
6655
- ] })
6656
- ] })
6657
- ) : renderGrid(visible),
6658
- /* @__PURE__ */ jsx(
6659
- DeleteConfirm,
6660
- {
6661
- file: pendingDelete,
6662
- loading: deleting,
6663
- onConfirm: () => void confirmDelete(),
6664
- onCancel: () => setPendingDelete(null)
6665
- }
6666
- )
6642
+ )
6643
+ ] })
6667
6644
  ] });
6668
6645
  }
6669
6646
  function DeleteConfirm({ file, loading, onConfirm, onCancel }) {
@@ -8607,13 +8584,13 @@ function buildShopAnalyticsCards() {
8607
8584
  }
8608
8585
 
8609
8586
  // src/index.ts
8610
- var ShopPage = React13__default.lazy(() => import('./ShopPage-5IERN4E5.js').then((m) => ({ default: m.ShopPage })));
8587
+ var ShopPage = React13__default.lazy(() => import('./ShopPage-V65TI34Q.js').then((m) => ({ default: m.ShopPage })));
8611
8588
  function createShopPlugin(options) {
8612
8589
  const curr = { code: "BRL", locale: "pt-BR", symbol: "R$", ...options?.currency };
8613
8590
  setShopTenantResolver(() => useOrganizationStore.getState().currentOrg?.id);
8614
8591
  registerMessageDefaults(buildShopMessageDefaults());
8615
8592
  registerShippingLabelProvider(createMelhorEnvioLabelProvider(() => typeof options?.provider === "function" ? options.provider() : options?.provider ?? getShopProvider()));
8616
- registerEntityPath("shop_customer", "/orders/customers");
8593
+ registerEntityPath("shop_customer", "/customers");
8617
8594
  registerPersonLookup("shop_customer", shopCustomerLookup(() => resolveProvider()));
8618
8595
  const pageFor = (section) => {
8619
8596
  const Wrapped = () => React13__default.createElement(
@@ -8668,21 +8645,22 @@ function createShopPlugin(options) {
8668
8645
  id: "customers",
8669
8646
  label: label("customers", "Clientes"),
8670
8647
  icon: Users,
8671
- onSelect: () => adminNavigateTo("/orders/customers")
8648
+ onSelect: () => adminNavigateTo("/customers")
8672
8649
  },
8673
8650
  {
8674
8651
  id: "groups",
8675
8652
  label: label("customerGroups", "Grupos"),
8676
8653
  icon: UsersRound,
8677
- onSelect: () => adminNavigateTo("/orders/customers/groups")
8654
+ onSelect: () => adminNavigateTo("/customers/groups")
8678
8655
  },
8679
8656
  {
8680
8657
  id: "segments",
8681
8658
  label: label("customerSegments", "Segmentos"),
8682
8659
  icon: Filter,
8683
- onSelect: () => adminNavigateTo("/orders/customers/segments")
8660
+ onSelect: () => adminNavigateTo("/customers/segments")
8684
8661
  }
8685
8662
  ];
8663
+ registerCrudNavTabs("/customers", customerTabs);
8686
8664
  const CustomerGroupsCrudPage = createCrudPage(buildShopCustomerGroupEntity(), {
8687
8665
  dataProvider: createShopCustomerGroupDataProvider(resolveProvider),
8688
8666
  feature: "shop",
@@ -8729,19 +8707,6 @@ function createShopPlugin(options) {
8729
8707
  { id: "groups", label: "Grupos", pending: "Ver aba", icon: UsersRound }
8730
8708
  ]
8731
8709
  });
8732
- const CustomersCrudPage = createCrudPage(buildShopCustomerEntity(), {
8733
- dataProvider: createShopCustomerDataProvider(resolveProvider),
8734
- feature: "shop",
8735
- navTabs: customerTabs,
8736
- activeNavTab: "customers",
8737
- stats: (items, total) => [
8738
- { id: "total", label: "Total de clientes", value: total, icon: Users },
8739
- { id: "with-orders", label: "Com pedidos", value: items?.filter((c) => (c.ordersCount ?? 0) > 0).length ?? 0, icon: ShoppingBag },
8740
- // Não computados ainda: um zero confiante aqui seria lido como medição.
8741
- { id: "repeat", label: "Taxa de recompra", pending: "Em breve", icon: Repeat },
8742
- { id: "aov", label: "Ticket m\xE9dio", pending: "Em breve", icon: CircleDollarSign }
8743
- ]
8744
- });
8745
8710
  const DiscountsCrudPage = createCrudPage(buildShopDiscountEntity(), {
8746
8711
  dataProvider: createShopDiscountDataProvider(resolveProvider),
8747
8712
  feature: "shop",
@@ -8834,13 +8799,10 @@ function createShopPlugin(options) {
8834
8799
  position: navPosition,
8835
8800
  component: pageFor("orders")
8836
8801
  },
8837
- {
8838
- route: "/orders/customers",
8839
- label: label("customers", "Clientes"),
8840
- icon: "Users",
8841
- position: navPosition + 0.1,
8842
- component: CustomersCrudPage
8843
- },
8802
+ // Sem Clientes aqui: o cadastro padrão já tem linha própria no rail, e uma
8803
+ // segunda porta para o mesmo assunto dentro da Loja era a duplicação que a
8804
+ // faixa de abas existe para evitar. Grupos e Segmentos continuam da loja,
8805
+ // agora pendurados em `/customers`.
8844
8806
  // Produtos ▸ Produtos · Coleções · Categorias
8845
8807
  {
8846
8808
  route: "/products",
@@ -8891,21 +8853,24 @@ function createShopPlugin(options) {
8891
8853
  const HIDDEN_ROUTES = [
8892
8854
  // Reached from the Clientes tab strip, deliberately not from the menu:
8893
8855
  // three sidebar entries for one destination is the duplication the tab
8894
- // strip exists to avoid. Nesting under /orders/customers also keeps
8895
- // "Clientes" highlighted in the rail while you are on them.
8896
- // Detalhe do modelo de e-mail. Sem entrada no menu: é uma linha da lista,
8897
- // não um destino. Ganha do curinga /emails/* por especificidade de rota.
8856
+ // strip exists to avoid.
8857
+ //
8858
+ // Penduradas em `/customers`, e não numa Clientes própria da loja: a ficha
8859
+ // de quem compra é UMA — `plg_shop_customers` pendura em `people` por
8860
+ // `person_id`, e o cadastro padrão já é a lista canônica. Grupo e segmento
8861
+ // são recortes DAQUELAS pessoas, então moram ao lado delas; a loja continua
8862
+ // dona das duas telas, só não abre uma segunda porta para o mesmo assunto.
8898
8863
  {
8899
- path: "/orders/customers/groups",
8864
+ path: "/customers/groups",
8900
8865
  component: CustomerGroupsCrudPage,
8901
8866
  title: label("customerGroups", "Grupos"),
8902
- parentRoute: "/orders/customers"
8867
+ parentRoute: "/customers"
8903
8868
  },
8904
8869
  {
8905
- path: "/orders/customers/segments",
8870
+ path: "/customers/segments",
8906
8871
  component: CustomerSegmentsCrudPage,
8907
8872
  title: label("customerSegments", "Segmentos"),
8908
- parentRoute: "/orders/customers"
8873
+ parentRoute: "/customers"
8909
8874
  }
8910
8875
  ];
8911
8876
  const redirectTo = (target) => {
@@ -8933,7 +8898,10 @@ function createShopPlugin(options) {
8933
8898
  { path: "/store/shipping", component: redirectTo(shippingRegistryPath(SHIPPING_ZONES_REGISTRY)) },
8934
8899
  { path: "/store", component: redirectTo(siteRoute) },
8935
8900
  { path: "/shop/channels", component: redirectTo(siteRoute) },
8936
- { path: "/customers", component: redirectTo("/orders/customers") },
8901
+ // A seta virou: `/customers` é do cadastro padrão, que toda conta tem com
8902
+ // ou sem loja. Era a loja que sequestrava a linha Clientes do rail — quem
8903
+ // clicava no cadastro de fora aterrissava dentro da loja.
8904
+ { path: "/orders/customers", component: redirectTo("/customers") },
8937
8905
  // A mensageria da loja virou parte de Automações — mesmos eventos, agora ao
8938
8906
  // lado dos do CRM e da agenda, e com WhatsApp além do e-mail.
8939
8907
  { path: "/emails", component: redirectTo("/automations") },