@fayz-ai/plugin-shop 0.16.1 → 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.
- package/dist/{ShopPage-5IERN4E5.js → ShopPage-V65TI34Q.js} +164 -22
- package/dist/ShopPage-V65TI34Q.js.map +1 -0
- package/dist/{chunk-RPBKGW24.js → chunk-6DRDPROL.js} +10 -152
- package/dist/chunk-6DRDPROL.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -115
- package/dist/index.js.map +1 -1
- package/dist/views/ContentPage.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/ShopPage-5IERN4E5.js.map +0 -1
- package/dist/chunk-RPBKGW24.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { errorMessage, listProductsDetailed, invalidateProductLists, shopCustomerLookup,
|
|
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
|
|
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-${
|
|
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 ${
|
|
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 ${
|
|
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 ${
|
|
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
|
|
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:
|
|
1697
|
-
total:
|
|
1838
|
+
unitPrice: money3(it.unitPrice, cur),
|
|
1839
|
+
total: money3(it.total, cur)
|
|
1698
1840
|
})),
|
|
1699
1841
|
totals: [
|
|
1700
|
-
{ label: "Subtotal", value:
|
|
1701
|
-
...order.discountTotal > 0 ? [{ label: `Desconto${order.discountCode ? ` (${order.discountCode})` : ""}`, value: `\u2212 ${
|
|
1702
|
-
{ label: "Entrega", value:
|
|
1703
|
-
{ label: "Impostos", value:
|
|
1704
|
-
{ label: "Total", value:
|
|
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) =>
|
|
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
|
|
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 ?
|
|
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:
|
|
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) =>
|
|
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-
|
|
3963
|
-
//# sourceMappingURL=ShopPage-
|
|
4104
|
+
//# sourceMappingURL=ShopPage-V65TI34Q.js.map
|
|
4105
|
+
//# sourceMappingURL=ShopPage-V65TI34Q.js.map
|