@fayz-ai/plugin-shop 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{ShopPage-YB5ZWECJ.js → ShopPage-VVLOH3RF.js} +67 -28
  2. package/dist/ShopPage-VVLOH3RF.js.map +1 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +684 -141
  6. package/dist/index.js.map +1 -1
  7. package/dist/integrations/abacatepay/connectorDef.d.ts.map +1 -1
  8. package/dist/integrations/abacatepay/data.d.ts +40 -0
  9. package/dist/integrations/abacatepay/data.d.ts.map +1 -0
  10. package/dist/integrations/abacatepay/limits.d.ts +26 -0
  11. package/dist/integrations/abacatepay/limits.d.ts.map +1 -0
  12. package/dist/integrations/abacatepay/sync.d.ts +88 -0
  13. package/dist/integrations/abacatepay/sync.d.ts.map +1 -0
  14. package/dist/integrations/neochef/connectorDef.d.ts +12 -0
  15. package/dist/integrations/neochef/connectorDef.d.ts.map +1 -0
  16. package/dist/integrations/neochef/data.d.ts +54 -0
  17. package/dist/integrations/neochef/data.d.ts.map +1 -0
  18. package/dist/integrations/neochef/index.d.ts +8 -0
  19. package/dist/integrations/neochef/index.d.ts.map +1 -0
  20. package/dist/integrations/neochef/limits.d.ts +26 -0
  21. package/dist/integrations/neochef/limits.d.ts.map +1 -0
  22. package/dist/integrations/neochef/mapping.d.ts +31 -0
  23. package/dist/integrations/neochef/mapping.d.ts.map +1 -0
  24. package/dist/integrations/neochef/mark.d.ts +21 -0
  25. package/dist/integrations/neochef/mark.d.ts.map +1 -0
  26. package/dist/integrations/neochef/types.d.ts +71 -0
  27. package/dist/integrations/neochef/types.d.ts.map +1 -0
  28. package/dist/integrations/vindi/connectorDef.d.ts +25 -6
  29. package/dist/integrations/vindi/connectorDef.d.ts.map +1 -1
  30. package/dist/views/ContentPage.d.ts.map +1 -1
  31. package/dist/views/NewOrderCharge.d.ts +14 -2
  32. package/dist/views/NewOrderCharge.d.ts.map +1 -1
  33. package/dist/views/NewOrderPage.d.ts.map +1 -1
  34. package/dist/views/new-order-rules.d.ts +10 -1
  35. package/dist/views/new-order-rules.d.ts.map +1 -1
  36. package/package.json +5 -5
  37. package/dist/ShopPage-YB5ZWECJ.js.map +0 -1
@@ -2,7 +2,7 @@ import { errorMessage, shopCustomerLookup, listProductsDetailed, invalidateProdu
2
2
  import React2, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
3
3
  import { Thumbnail, Button, DataTable, toast, ConfirmDialog, formatPhone, SearchSelect, Modal, ModalPortal, ModalOverlay, ModalContent, ModalHeader, ModalTitle, ModalBody, ModalFooter, Skeleton, PhoneField } from '@fayz-ai/ui';
4
4
  import { Minus, Plus, Trash2, CreditCard, XCircle, ShoppingBag, Package, Wallet, X, Pencil, MapPin, Search, Check, Copy, MessageCircle, CheckCircle2, Printer, RefreshCw, ExternalLink, Truck, AlertTriangle, Loader2 } from 'lucide-react';
5
- import { getShopProvider, formatDocument, resolveShippingLabelProvider, documentDigits, isValidDocument } from '@fayz-ai/shop';
5
+ import { CHARGEABLE_PROVIDERS, supportsPaymentLink, getShopProvider, formatDocument, resolveShippingLabelProvider, documentDigits, isValidDocument } from '@fayz-ai/shop';
6
6
  import { useAdminPath, adminNavigateTo, OrderFormBlock, OrderFormView, OrderDetailSkeleton, OrderDetailBlock, OrderDetailRow, PersonLink, OrderDetailView as OrderDetailView$1, OrderListView, RecordPickerModal, useConnectors, CrudRecordModal } from '@fayz-ai/admin';
7
7
  import { usePluginRuntimeOptional, getSupabaseClientOptional } from '@fayz-ai/core';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -187,6 +187,15 @@ function validateNewOrder(draft, ending) {
187
187
  return `Para gerar a cobran\xE7a o adquirente exige ${missing.join(", ")} no cadastro do cliente.`;
188
188
  }
189
189
  }
190
+ if (ending === "link") {
191
+ const missing = [
192
+ !draft.customerEmail.trim() && "e-mail",
193
+ !draft.customerPhone.trim() && "telefone"
194
+ ].filter(Boolean);
195
+ if (missing.length) {
196
+ return `Para mandar o link o cadastro do cliente precisa de ${missing.join(" e ")}.`;
197
+ }
198
+ }
190
199
  return null;
191
200
  }
192
201
  function isDuplicateEmail(error) {
@@ -1014,7 +1023,7 @@ function OrderDetailView({ orderId, provider, onBack }) {
1014
1023
  useEffect(() => {
1015
1024
  void loadReceivable();
1016
1025
  }, [loadReceivable]);
1017
- if (loading) return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
1026
+ if (loading) return /* @__PURE__ */ jsx(OrderDetailSkeleton, { parentLabel: "Pedidos", onBack });
1018
1027
  if (error) {
1019
1028
  return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800", children: [
1020
1029
  /* @__PURE__ */ jsx("p", { className: "font-medium", children: "N\xE3o foi poss\xEDvel carregar o pedido." }),
@@ -1318,6 +1327,8 @@ function NewOrderCharge({
1318
1327
  sessionId,
1319
1328
  emv,
1320
1329
  qrcodeBase64,
1330
+ paymentUrl,
1331
+ acquirer,
1321
1332
  total,
1322
1333
  currency: currency2,
1323
1334
  customerName,
@@ -1334,7 +1345,7 @@ function NewOrderCharge({
1334
1345
  if (settled.current) return;
1335
1346
  setChecking(true);
1336
1347
  try {
1337
- const result = await provider().settleCheckoutSession?.(sessionId);
1348
+ const result = await provider().settleCheckoutSession?.(sessionId, acquirer);
1338
1349
  if (!result) return;
1339
1350
  if (result.state === "paid") {
1340
1351
  settled.current = true;
@@ -1350,7 +1361,7 @@ function NewOrderCharge({
1350
1361
  } finally {
1351
1362
  setChecking(false);
1352
1363
  }
1353
- }, [provider, sessionId, onPaid]);
1364
+ }, [provider, sessionId, acquirer, onPaid]);
1354
1365
  useEffect(() => {
1355
1366
  if (!open) return;
1356
1367
  const timer = setInterval(() => {
@@ -1358,28 +1369,32 @@ function NewOrderCharge({
1358
1369
  }, POLL_MS);
1359
1370
  return () => clearInterval(timer);
1360
1371
  }, [open, check]);
1372
+ const isLink = Boolean(paymentUrl && !emv);
1373
+ const payable = paymentUrl ?? emv ?? "";
1361
1374
  async function copy() {
1375
+ if (!payable) return;
1362
1376
  try {
1363
- await navigator.clipboard.writeText(emv);
1377
+ await navigator.clipboard.writeText(payable);
1364
1378
  setCopied(true);
1365
1379
  setTimeout(() => setCopied(false), 2400);
1366
1380
  } catch {
1367
1381
  }
1368
1382
  }
1369
- const whatsapp = customerPhone ? `https://wa.me/${customerPhone.length > 11 ? customerPhone : `55${customerPhone}`}?text=${encodeURIComponent(
1370
- `Ol\xE1${customerName ? ` ${customerName.split(" ")[0]}` : ""}! Segue o Pix de ${money4} para o seu pedido:
1383
+ const greeting = customerName ? ` ${customerName.split(" ")[0]}` : "";
1384
+ const message = isLink ? `Ol\xE1${greeting}! Aqui est\xE1 o link para pagar seu pedido de ${money4}:
1385
+
1386
+ ${payable}
1371
1387
 
1372
- ${emv}`
1373
- )}` : null;
1388
+ Voc\xEA escolhe a forma de pagamento na p\xE1gina.` : `Ol\xE1${greeting}! Segue o Pix de ${money4} para o seu pedido:
1389
+
1390
+ ${payable}`;
1391
+ const whatsapp = customerPhone && payable ? `https://wa.me/${customerPhone.length > 11 ? customerPhone : `55${customerPhone}`}?text=${encodeURIComponent(message)}` : null;
1374
1392
  return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (next) => {
1375
1393
  if (!next) onClose();
1376
1394
  }, children: /* @__PURE__ */ jsx(ModalPortal, { children: /* @__PURE__ */ jsx(ModalOverlay, { children: /* @__PURE__ */ jsxs(ModalContent, { size: "lg", "data-testid": "new-order-charge-modal", children: [
1377
1395
  /* @__PURE__ */ jsxs(ModalHeader, { className: "mb-4", children: [
1378
- /* @__PURE__ */ jsxs(ModalTitle, { children: [
1379
- "Pix de ",
1380
- money4
1381
- ] }),
1382
- state === "waiting" && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Vale por 15 minutos. O pedido \xE9 criado quando o pagamento cair." })
1396
+ /* @__PURE__ */ jsx(ModalTitle, { children: isLink ? `Link de ${money4}` : `Pix de ${money4}` }),
1397
+ 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." })
1383
1398
  ] }),
1384
1399
  /* @__PURE__ */ jsxs(ModalBody, { children: [
1385
1400
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 sm:flex-row sm:items-start", children: [
@@ -1394,20 +1409,21 @@ ${emv}`
1394
1409
  ),
1395
1410
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-3", children: [
1396
1411
  /* @__PURE__ */ jsxs("div", { children: [
1397
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Pix copia e cola" }),
1412
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: isLink ? "Link de pagamento" : "Pix copia e cola" }),
1398
1413
  /* @__PURE__ */ jsx(
1399
1414
  "p",
1400
1415
  {
1401
- "data-testid": "new-order-emv",
1416
+ "data-testid": isLink ? "new-order-payment-link" : "new-order-emv",
1402
1417
  className: "mt-1 max-h-20 overflow-auto break-all rounded-md border bg-muted/30 p-2 font-mono text-[11px]",
1403
- children: emv
1418
+ children: payable
1404
1419
  }
1405
- )
1420
+ ),
1421
+ isLink && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "O cliente escolhe Pix, boleto ou cart\xE3o na p\xE1gina do adquirente." })
1406
1422
  ] }),
1407
1423
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
1408
1424
  /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: copy, "data-testid": "new-order-copy", children: [
1409
1425
  copied ? /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "mr-1.5 h-3.5 w-3.5" }),
1410
- copied ? "Copiado" : "Copiar c\xF3digo"
1426
+ copied ? "Copiado" : isLink ? "Copiar link" : "Copiar c\xF3digo"
1411
1427
  ] }),
1412
1428
  whatsapp && /* @__PURE__ */ jsxs(
1413
1429
  "a",
@@ -1435,7 +1451,7 @@ ${emv}`
1435
1451
  /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
1436
1452
  " Pagamento confirmado \u2014 abrindo o pedido\u2026"
1437
1453
  ] }),
1438
- (state === "expired" || state === "failed") && /* @__PURE__ */ jsx("p", { className: "text-red-700", "data-testid": "new-order-dead", children: state === "expired" ? "O c\xF3digo expirou sem pagamento. O pedido continua aqui \u2014 d\xE1 para cobrar de outro jeito." : "O pagamento n\xE3o foi conclu\xEDdo. O pedido continua aqui \u2014 d\xE1 para cobrar de outro jeito." })
1454
+ (state === "expired" || state === "failed") && /* @__PURE__ */ jsx("p", { className: "text-red-700", "data-testid": "new-order-dead", children: state === "expired" ? `${isLink ? "O link expirou" : "O c\xF3digo expirou"} sem pagamento. O pedido continua aqui \u2014 d\xE1 para cobrar de outro jeito.` : "O pagamento n\xE3o foi conclu\xEDdo. O pedido continua aqui \u2014 d\xE1 para cobrar de outro jeito." })
1439
1455
  ] })
1440
1456
  ] }),
1441
1457
  /* @__PURE__ */ jsxs(ModalFooter, { className: "mt-4 gap-2", children: [
@@ -1470,7 +1486,6 @@ var PAYMENT_METHODS = [
1470
1486
  { id: "boleto", label: "Boleto" },
1471
1487
  { id: "other", label: "Outro" }
1472
1488
  ];
1473
- var CHARGEABLE_PROVIDERS = ["abacatepay"];
1474
1489
  var money3 = (value, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(value) || 0);
1475
1490
  var onlyDigits = (value) => value.replace(/\D/gu, "");
1476
1491
  var maskPostal = (value) => {
@@ -1514,6 +1529,7 @@ function NewOrderPage({ provider, onDone, onCancel }) {
1514
1529
  }, [provider]);
1515
1530
  const acquirer = settings?.paymentProvider ?? null;
1516
1531
  const canCharge = Boolean(acquirer && CHARGEABLE_PROVIDERS.includes(acquirer));
1532
+ const canLink = canCharge && supportsPaymentLink(acquirer);
1517
1533
  const currency2 = settings?.currency ?? "BRL";
1518
1534
  const subtotal = useMemo(
1519
1535
  () => lines.reduce((sum, line) => sum + line.unitPrice * line.quantity, 0),
@@ -1624,7 +1640,7 @@ function NewOrderPage({ provider, onDone, onCancel }) {
1624
1640
  setSaving(true);
1625
1641
  setError(null);
1626
1642
  try {
1627
- if (ending === "charge") {
1643
+ if (ending === "charge" || ending === "link") {
1628
1644
  const opened = await provider().openCheckoutSession?.({
1629
1645
  items: lines.map((line) => ({
1630
1646
  productId: line.productId,
@@ -1634,13 +1650,24 @@ function NewOrderPage({ provider, onDone, onCancel }) {
1634
1650
  })),
1635
1651
  customer: { name: customer.name.trim(), email: customer.email.trim().toLowerCase() },
1636
1652
  shippingAddress,
1637
- discountCode: discountCode.trim() || null
1653
+ discountCode: discountCode.trim() || null,
1654
+ // The one difference between the two endings, all the way down: same
1655
+ // repricing, same frozen session, same settlement by webhook. What
1656
+ // changes is whether the acquirer answers with a code to read out or
1657
+ // an address to send.
1658
+ asLink: ending === "link"
1638
1659
  });
1639
- if (!opened?.charge?.emv) throw new Error("A cobran\xE7a foi aberta sem c\xF3digo Pix. Tente de novo.");
1660
+ if (ending === "link" && !opened?.paymentUrl) {
1661
+ throw new Error("O adquirente n\xE3o devolveu o link de pagamento. Tente de novo.");
1662
+ }
1663
+ if (ending === "charge" && !opened?.charge?.emv) {
1664
+ throw new Error("A cobran\xE7a foi aberta sem c\xF3digo Pix. Tente de novo.");
1665
+ }
1640
1666
  setSession({
1641
1667
  sessionId: opened.sessionId,
1642
- emv: opened.charge.emv,
1643
- qrcode: opened.charge.qrcodeBase64,
1668
+ emv: opened.charge?.emv ?? null,
1669
+ qrcode: opened.charge?.qrcodeBase64 ?? null,
1670
+ paymentUrl: opened.paymentUrl ?? null,
1644
1671
  total: opened.total
1645
1672
  });
1646
1673
  setChargeOpen(true);
@@ -1882,6 +1909,16 @@ function NewOrderPage({ provider, onDone, onCancel }) {
1882
1909
  children: saving ? "Abrindo\u2026" : session ? "Ver cobran\xE7a" : "Gerar cobran\xE7a Pix"
1883
1910
  }
1884
1911
  ),
1912
+ canLink && !session && /* @__PURE__ */ jsx(
1913
+ Button,
1914
+ {
1915
+ variant: "outline",
1916
+ onClick: () => void finish("link"),
1917
+ disabled: saving,
1918
+ "data-testid": "new-order-link",
1919
+ children: saving ? "Gerando\u2026" : "Gerar link de pagamento"
1920
+ }
1921
+ ),
1885
1922
  /* @__PURE__ */ jsx(
1886
1923
  Button,
1887
1924
  {
@@ -1914,6 +1951,8 @@ function NewOrderPage({ provider, onDone, onCancel }) {
1914
1951
  sessionId: session.sessionId,
1915
1952
  emv: session.emv,
1916
1953
  qrcodeBase64: session.qrcode,
1954
+ paymentUrl: session.paymentUrl,
1955
+ acquirer,
1917
1956
  total: session.total,
1918
1957
  currency: currency2,
1919
1958
  customerPhone: onlyDigits(customer.phone),
@@ -2922,5 +2961,5 @@ var ShopPage = ({ provider, section }) => {
2922
2961
  ShopPage.displayName = "ShopPage";
2923
2962
 
2924
2963
  export { ShopPage };
2925
- //# sourceMappingURL=ShopPage-YB5ZWECJ.js.map
2926
- //# sourceMappingURL=ShopPage-YB5ZWECJ.js.map
2964
+ //# sourceMappingURL=ShopPage-VVLOH3RF.js.map
2965
+ //# sourceMappingURL=ShopPage-VVLOH3RF.js.map