@fayz-ai/plugin-shop 0.16.2 → 0.16.4

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.
@@ -1,10 +1,10 @@
1
- import { errorMessage, listProductsDetailed, invalidateProductLists, shopCustomerLookup, buildShopCustomerEntity, buildStockLocationsRegistry, listProductsCached, resolveTerm, candidateMeta } from './chunk-RPBKGW24.js';
1
+ import { errorMessage, listProductsDetailed, invalidateProductLists, shopCustomerLookup, buildStockLocationsRegistry, listProductsCached, resolveTerm, candidateMeta } from './chunk-6DRDPROL.js';
2
2
  import * as React2 from 'react';
3
3
  import React2__default, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
4
4
  import { Button, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalFooter, formatPhone, toast, DataTable, ConfirmDialog, SearchSelect, ModalPortal, ModalOverlay, ModalBody, Skeleton } from '@fayz-ai/ui';
5
5
  import { RotateCcw, QrCode, Pencil, Archive, Ban, CreditCard, XCircle, ShoppingBag, Package, Wallet, Plus, WandSparkles, X, MapPin, Link2, HandCoins, Check, Copy, Share2, MessageCircle, CheckCircle2, Printer, RefreshCw, ExternalLink, Truck, AlertTriangle, Loader2 } from 'lucide-react';
6
6
  import { CHARGEABLE_PROVIDERS, supportsPaymentLink, getShopProvider, formatDocument, resolveShippingLabelProvider, isValidDocument, documentDigits } from '@fayz-ai/shop';
7
- import { useAdminPath, adminNavigateTo, OrderFormBlock, OrderCreateScreen, useOrderReceivable, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailRow, toOrderDetailReceivable, PersonLink, OrderDetailView as OrderDetailView$1, OrderListView, askAI, useConnectors, CrudRecordModal, useAgentSurface, duplicateWrite } from '@fayz-ai/admin';
7
+ import { useAdminPath, adminNavigateTo, OrderFormBlock, OrderCreateScreen, useOrderReceivable, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailRow, toOrderDetailReceivable, PersonLink, OrderDetailView as OrderDetailView$1, OrderListView, askAI, useConnectors, CrudRecordModal, useAgentSurface, ClientOrdersTab, createClientOrdersProvider, duplicateWrite } from '@fayz-ai/admin';
8
8
  import { ReceivePaymentModal } from '@fayz-ai/plugin-financial';
9
9
  import { usePluginRuntimeOptional, getSupabaseClientOptional } from '@fayz-ai/core';
10
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -887,7 +887,7 @@ function NewOrderCharge({
887
887
  const [checking, setChecking] = useState(false);
888
888
  const settled = useRef(false);
889
889
  const inFlight = useRef(false);
890
- const money4 = new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(total);
890
+ const money5 = new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(total);
891
891
  const check = useCallback(async () => {
892
892
  if (settled.current || !sessionId || inFlight.current) return;
893
893
  inFlight.current = true;
@@ -944,7 +944,7 @@ function NewOrderCharge({
944
944
  });
945
945
  return;
946
946
  }
947
- const file = new File([blob], `pix-${money4.replace(/\D/gu, "")}.${blob.type.includes("svg") ? "svg" : "png"}`, { type: blob.type || "image/png" });
947
+ const file = new File([blob], `pix-${money5.replace(/\D/gu, "")}.${blob.type.includes("svg") ? "svg" : "png"}`, { type: blob.type || "image/png" });
948
948
  const share = navigator;
949
949
  try {
950
950
  if (share.canShare?.({ files: [file] })) {
@@ -962,11 +962,11 @@ function NewOrderCharge({
962
962
  }
963
963
  }
964
964
  const greeting = customerName ? ` ${customerName.split(" ")[0]}` : "";
965
- const message = isLink ? `Ol\xE1${greeting}! Aqui est\xE1 o link para pagar seu pedido de ${money4}:
965
+ const message = isLink ? `Ol\xE1${greeting}! Aqui est\xE1 o link para pagar seu pedido de ${money5}:
966
966
 
967
967
  ${payable}
968
968
 
969
- Voc\xEA escolhe a forma de pagamento na p\xE1gina.` : `Ol\xE1${greeting}! Segue o Pix de ${money4} para o seu pedido:
969
+ Voc\xEA escolhe a forma de pagamento na p\xE1gina.` : `Ol\xE1${greeting}! Segue o Pix de ${money5} para o seu pedido:
970
970
 
971
971
  ${payable}`;
972
972
  const whatsapp = customerPhone && payable ? `https://wa.me/${customerPhone.length > 11 ? customerPhone : `55${customerPhone}`}?text=${encodeURIComponent(message)}` : null;
@@ -974,7 +974,7 @@ ${payable}`;
974
974
  if (!next) onClose();
975
975
  }, children: /* @__PURE__ */ jsx(ModalPortal, { children: /* @__PURE__ */ jsx(ModalOverlay, { children: /* @__PURE__ */ jsxs(ModalContent, { size: "lg", "data-testid": "new-order-charge-modal", children: [
976
976
  /* @__PURE__ */ jsxs(ModalHeader, { className: "mb-4", children: [
977
- /* @__PURE__ */ jsx(ModalTitle, { children: choosing || opening ? `Cobrar ${money4}` : isLink ? `Link de ${money4}` : `Pix de ${money4}` }),
977
+ /* @__PURE__ */ jsx(ModalTitle, { children: choosing || opening ? `Cobrar ${money5}` : isLink ? `Link de ${money5}` : `Pix de ${money5}` }),
978
978
  choosing && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Como o cliente vai pagar?" }),
979
979
  opening && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Abrindo a cobran\xE7a no adquirente\u2026" }),
980
980
  ready && state === "waiting" && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: isLink ? "Mande para o cliente. O pedido \xE9 criado quando o pagamento cair \u2014 pode fechar esta janela." : "Vale por 15 minutos. O pedido \xE9 criado quando o pagamento cair." })
@@ -1124,6 +1124,148 @@ ${payable}`;
1124
1124
  ] })
1125
1125
  ] }) }) }) });
1126
1126
  }
1127
+ function money2(value) {
1128
+ const n = Number(value);
1129
+ if (!Number.isFinite(n)) return "R$ 0,00";
1130
+ return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(n);
1131
+ }
1132
+ function buildShopCustomerEntity() {
1133
+ const fields = [
1134
+ // — Contato —
1135
+ {
1136
+ key: "firstName",
1137
+ label: "Nome",
1138
+ type: "text",
1139
+ required: true,
1140
+ group: "contact",
1141
+ placeholder: "Maria",
1142
+ searchable: true,
1143
+ showInTable: true,
1144
+ sortable: true,
1145
+ renderCell: (_v, row) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1146
+ /* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: [row.firstName, row.lastName].filter(Boolean).join(" ") }),
1147
+ row.email && /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: row.email })
1148
+ ] })
1149
+ },
1150
+ { key: "lastName", label: "Sobrenome", type: "text", group: "contact", placeholder: "Silva", searchable: true, showInTable: false },
1151
+ {
1152
+ key: "email",
1153
+ label: "E-mail",
1154
+ type: "email",
1155
+ group: "contact",
1156
+ placeholder: "maria@exemplo.com",
1157
+ searchable: true,
1158
+ hint: "\xC9 por ele que o pedido \xE9 vinculado ao cliente no checkout.",
1159
+ showInTable: false
1160
+ },
1161
+ {
1162
+ key: "phone",
1163
+ label: "Telefone",
1164
+ type: "phone",
1165
+ group: "contact",
1166
+ placeholder: "(21) 99999-0000",
1167
+ searchable: true,
1168
+ showInTable: true,
1169
+ sortable: false,
1170
+ renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: v || "\u2014" })
1171
+ },
1172
+ // The column, the type and the provider all carried `document` already; the
1173
+ // form was the only place it could not be typed. Which meant a store could
1174
+ // see "cliente sem CPF/CNPJ" on every Bling run and have nowhere to fix it.
1175
+ {
1176
+ // Masked while typing, digits stored — the same rule the phone field
1177
+ // follows. A formatted CPF in the column means no search for the bare
1178
+ // number ever finds it.
1179
+ key: "document",
1180
+ label: "CPF / CNPJ",
1181
+ type: "custom",
1182
+ group: "contact",
1183
+ placeholder: "000.000.000-00",
1184
+ searchable: true,
1185
+ hint: "Necess\xE1rio para emitir nota \u2014 sem ele o cliente n\xE3o sobe para o ERP.",
1186
+ showInTable: false,
1187
+ renderInput: ({ value, onChange }) => /* @__PURE__ */ jsx(
1188
+ "input",
1189
+ {
1190
+ value: formatDocument(String(value ?? "")),
1191
+ onChange: (e) => onChange(documentDigits(e.target.value)),
1192
+ placeholder: "000.000.000-00",
1193
+ inputMode: "numeric",
1194
+ className: "flex h-9 w-full rounded-input border border-input bg-background px-3 py-1.5 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1195
+ }
1196
+ )
1197
+ },
1198
+ // — Histórico (somente leitura) —
1199
+ {
1200
+ key: "ordersCount",
1201
+ label: "Pedidos",
1202
+ type: "number",
1203
+ showInForm: false,
1204
+ showInTable: true,
1205
+ sortable: true,
1206
+ renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right", children: Number(v) || 0 })
1207
+ },
1208
+ {
1209
+ key: "totalSpent",
1210
+ label: "Total gasto",
1211
+ type: "number",
1212
+ showInForm: false,
1213
+ showInTable: true,
1214
+ sortable: true,
1215
+ renderCell: (v) => /* @__PURE__ */ jsx("span", { className: "block text-right font-medium", children: money2(v) })
1216
+ },
1217
+ // — Interno —
1218
+ {
1219
+ key: "notes",
1220
+ label: "Observa\xE7\xF5es",
1221
+ type: "textarea",
1222
+ group: "internal",
1223
+ span: 2,
1224
+ placeholder: "Prefer\xEAncias, restri\xE7\xF5es, combinados de entrega\u2026",
1225
+ hint: "Vis\xEDvel s\xF3 no admin. O cliente nunca v\xEA.",
1226
+ showInTable: false
1227
+ }
1228
+ ];
1229
+ return {
1230
+ name: "Cliente",
1231
+ namePlural: "Clientes",
1232
+ icon: "Users",
1233
+ displayField: "firstName",
1234
+ // `person` layout brings the shared archetype tabs — including Endereços,
1235
+ // which reads the core addresses table and therefore works in every app.
1236
+ layout: "person",
1237
+ // Without archetypeKind the shell shows every person tab, including the
1238
+ // staff-only Agenda (its visibleFor filter only runs when a kind is set).
1239
+ data: { table: "plg_shop_customers", archetypeKind: "customer" },
1240
+ fields,
1241
+ // The SAME tab and the SAME provider beauty-saas uses. Since the shop's
1242
+ // orders now live in public.orders with party_id set, the canonical provider
1243
+ // already returns them — a shop-specific adapter would be a second
1244
+ // implementation of "list this person's orders", which is how two apps end
1245
+ // up with two different answers to the same question.
1246
+ detailTabs: [{
1247
+ id: "orders",
1248
+ label: "Pedidos",
1249
+ icon: "ShoppingBag",
1250
+ aliases: ["appointments", "quotes"],
1251
+ component: ClientOrdersTab,
1252
+ props: {
1253
+ provider: createClientOrdersProvider(),
1254
+ currency: { code: "BRL", locale: "pt-BR" },
1255
+ // The one part that legitimately differs per app: where a document
1256
+ // leads. Beauty sends a booking to the agenda and an invoice to
1257
+ // receivables; a shop order goes to the order screen.
1258
+ navigator: {
1259
+ onNavigate: (doc) => adminNavigateTo(`/shop/orders/${doc.id}`)
1260
+ }
1261
+ }
1262
+ }],
1263
+ fieldGroups: [
1264
+ { id: "contact", label: "Contato", columns: 2 },
1265
+ { id: "internal", label: "Notas internas", columns: 1 }
1266
+ ]
1267
+ };
1268
+ }
1127
1269
  var onlyDigits = (value) => value.replace(/\D/gu, "");
1128
1270
  function CustomerModal({ open, initialName, customerId, initial, provider, onClose, onSaved }) {
1129
1271
  const entityDef = useMemo(() => buildShopCustomerEntity(), []);
@@ -1245,7 +1387,7 @@ function CustomerModal({ open, initialName, customerId, initial, provider, onClo
1245
1387
  }
1246
1388
  );
1247
1389
  }
1248
- var money2 = (v, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(v) || 0);
1390
+ var money3 = (v, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(v) || 0);
1249
1391
  var dateTime2 = (iso) => new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
1250
1392
  var dateOnly = (iso) => new Date(iso).toLocaleDateString("pt-BR");
1251
1393
  var paidAtLabel = (iso) => {
@@ -1693,15 +1835,15 @@ function OrderDetailView({ orderId, provider, onBack }) {
1693
1835
  description: it.sku,
1694
1836
  imageUrl: it.imageUrl,
1695
1837
  quantity: it.quantity,
1696
- unitPrice: money2(it.unitPrice, cur),
1697
- total: money2(it.total, cur)
1838
+ unitPrice: money3(it.unitPrice, cur),
1839
+ total: money3(it.total, cur)
1698
1840
  })),
1699
1841
  totals: [
1700
- { label: "Subtotal", value: money2(order.subtotal, cur) },
1701
- ...order.discountTotal > 0 ? [{ label: `Desconto${order.discountCode ? ` (${order.discountCode})` : ""}`, value: `\u2212 ${money2(order.discountTotal, cur)}`, muted: true }] : [],
1702
- { label: "Entrega", value: money2(order.shippingTotal, cur) },
1703
- { label: "Impostos", value: money2(order.taxTotal, cur) },
1704
- { label: "Total", value: money2(order.total, cur), strong: true }
1842
+ { label: "Subtotal", value: money3(order.subtotal, cur) },
1843
+ ...order.discountTotal > 0 ? [{ label: `Desconto${order.discountCode ? ` (${order.discountCode})` : ""}`, value: `\u2212 ${money3(order.discountTotal, cur)}`, muted: true }] : [],
1844
+ { label: "Entrega", value: money3(order.shippingTotal, cur) },
1845
+ { label: "Impostos", value: money3(order.taxTotal, cur) },
1846
+ { label: "Total", value: money3(order.total, cur), strong: true }
1705
1847
  ],
1706
1848
  paymentMethod: order.paymentMethodKind ? PAYMENT_LABEL[order.paymentMethodKind] ?? order.paymentMethodKind : void 0,
1707
1849
  paidAt: order.paidAt ? paidAtLabel(order.paidAt) : void 0,
@@ -1711,7 +1853,7 @@ function OrderDetailView({ orderId, provider, onBack }) {
1711
1853
  // here is the only part that is the shop's: the verbs, which need a writer.
1712
1854
  receivable: toOrderDetailReceivable(
1713
1855
  receivable,
1714
- (value) => money2(value, cur),
1856
+ (value) => money3(value, cur),
1715
1857
  {
1716
1858
  dateOnly: (value) => value ? dateOnly(value) : "",
1717
1859
  actions: /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2038,7 +2180,7 @@ var PAYMENT_METHODS = [
2038
2180
  { id: "boleto", label: "Boleto" },
2039
2181
  { id: "other", label: "Outro" }
2040
2182
  ];
2041
- var money3 = (value, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(value) || 0);
2183
+ var money4 = (value, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(value) || 0);
2042
2184
  var onlyDigits2 = (value) => value.replace(/\D/gu, "");
2043
2185
  var maskPostal = (value) => {
2044
2186
  const digits2 = onlyDigits2(value).slice(0, 8);
@@ -2388,7 +2530,7 @@ function NewOrderPage({ provider, onDone, onCancel, orderId }) {
2388
2530
  key: "shipping",
2389
2531
  label: delivers ? `Entrega${freight ? ` \xB7 ${freight.label}` : ""}` : "Entrega",
2390
2532
  muted: !delivers,
2391
- value: delivers ? money3(shipping, currency2) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "sem entrega" })
2533
+ value: delivers ? money4(shipping, currency2) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "sem entrega" })
2392
2534
  }
2393
2535
  ], [couponOpen, discountCode, delivers, freight, shipping, currency2]);
2394
2536
  const errorActions = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2557,7 +2699,7 @@ function NewOrderPage({ provider, onDone, onCancel, orderId }) {
2557
2699
  title: product.name,
2558
2700
  subtitle: product.sku ?? void 0,
2559
2701
  meta: product.inventoryCount ?? 0,
2560
- value: money3(product.price, currency2)
2702
+ value: money4(product.price, currency2)
2561
2703
  }),
2562
2704
  toLines,
2563
2705
  onRefresh: invalidateProductLists,
@@ -2565,7 +2707,7 @@ function NewOrderPage({ provider, onDone, onCancel, orderId }) {
2565
2707
  },
2566
2708
  lines,
2567
2709
  onLinesChange: setLines,
2568
- formatMoney: (value) => money3(value, currency2),
2710
+ formatMoney: (value) => money4(value, currency2),
2569
2711
  totalsTitle: "Pagamento",
2570
2712
  totalRows,
2571
2713
  extraTotal: shipping,
@@ -3959,5 +4101,5 @@ var ShopPage = ({ provider, section }) => {
3959
4101
  ShopPage.displayName = "ShopPage";
3960
4102
 
3961
4103
  export { ShopPage };
3962
- //# sourceMappingURL=ShopPage-5IERN4E5.js.map
3963
- //# sourceMappingURL=ShopPage-5IERN4E5.js.map
4104
+ //# sourceMappingURL=ShopPage-V65TI34Q.js.map
4105
+ //# sourceMappingURL=ShopPage-V65TI34Q.js.map