@fayz-ai/plugin-shop 0.8.1 → 0.8.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,7 +1,7 @@
1
1
  import { useState, useEffect, useCallback, useMemo } from 'react';
2
- import { DataTable, Skeleton, Card, CardContent } from '@fayz-ai/ui';
2
+ import { DataTable } from '@fayz-ai/ui';
3
3
  import { getShopProvider } from '@fayz-ai/shop';
4
- import { useAdminPath, adminNavigateTo, PersonLink } from '@fayz-ai/saas';
4
+ import { useAdminPath, adminNavigateTo, OrderDetailSkeleton, OrderDetailBlock, OrderDetailRow, PersonLink, OrderDetailView as OrderDetailView$1 } from '@fayz-ai/saas';
5
5
  import { getSupabaseClientOptional } from '@fayz-ai/core';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
@@ -32,49 +32,9 @@ function shopCustomerLookup(provider) {
32
32
  }
33
33
  };
34
34
  }
35
- var RECEIVABLE_LABEL = {
36
- open: "Em aberto",
37
- partial: "Parcialmente recebido",
38
- paid: "Recebido",
39
- overdue: "Vencido",
40
- cancelled: "Cancelado"
41
- };
42
- var RECEIVABLE_TONE = {
43
- open: "bg-blue-100 text-blue-800",
44
- partial: "bg-yellow-100 text-yellow-800",
45
- paid: "bg-emerald-100 text-emerald-800",
46
- overdue: "bg-red-100 text-red-700",
47
- cancelled: "bg-gray-100 text-gray-700"
48
- };
49
- var SHIPMENT_LABEL = {
50
- pending: "Aguardando coleta",
51
- in_transit: "Em tr\xE2nsito",
52
- delivered: "Entregue",
53
- failed: "Falhou",
54
- returned: "Devolvido",
55
- cancelled: "Cancelado"
56
- };
57
35
  var money = (v, currency2 = "BRL") => new Intl.NumberFormat("pt-BR", { style: "currency", currency: currency2 }).format(Number(v) || 0);
58
- var dateTime = (iso) => new Intl.DateTimeFormat("pt-BR", {
59
- day: "2-digit",
60
- month: "2-digit",
61
- year: "numeric",
62
- hour: "2-digit",
63
- minute: "2-digit"
64
- }).format(new Date(iso));
65
- var TONE = {
66
- paid: "bg-emerald-100 text-emerald-800",
67
- pending: "bg-yellow-100 text-yellow-800",
68
- refunded: "bg-orange-100 text-orange-700",
69
- partially_refunded: "bg-orange-100 text-orange-700",
70
- voided: "bg-gray-100 text-gray-700",
71
- fulfilled: "bg-emerald-100 text-emerald-800",
72
- unfulfilled: "bg-gray-100 text-gray-700",
73
- partially_fulfilled: "bg-blue-100 text-blue-800",
74
- open: "bg-blue-100 text-blue-800",
75
- cancelled: "bg-red-100 text-red-700",
76
- archived: "bg-gray-100 text-gray-700"
77
- };
36
+ var dateTime = (iso) => new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
37
+ var dateOnly = (iso) => new Date(iso).toLocaleDateString("pt-BR");
78
38
  var PAYMENT_LABEL = {
79
39
  pix: "Pix",
80
40
  credit_card: "Cart\xE3o de cr\xE9dito",
@@ -96,85 +56,89 @@ var LABEL = {
96
56
  cancelled: "Cancelado",
97
57
  archived: "Arquivado"
98
58
  };
99
- function Badge({ value }) {
100
- return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${TONE[value] ?? TONE.open}`, children: LABEL[value] ?? value });
101
- }
102
- function Block({ title, hint, children }) {
103
- return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "pt-4", children: [
104
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: title }),
105
- hint && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: hint }),
106
- /* @__PURE__ */ jsx("div", { className: "mt-3", children })
107
- ] }) });
59
+ var RECEIVABLE_LABEL = {
60
+ open: "Em aberto",
61
+ partial: "Parcialmente recebido",
62
+ paid: "Recebido",
63
+ overdue: "Vencido",
64
+ cancelled: "Cancelado"
65
+ };
66
+ var SHIPMENT_LABEL = {
67
+ pending: "Aguardando coleta",
68
+ in_transit: "Em tr\xE2nsito",
69
+ delivered: "Entregue",
70
+ failed: "Falhou",
71
+ returned: "Devolvido",
72
+ cancelled: "Cancelado"
73
+ };
74
+ function toneOf(value) {
75
+ switch (value) {
76
+ case "paid":
77
+ case "fulfilled":
78
+ case "delivered":
79
+ return "success";
80
+ case "pending":
81
+ case "partial":
82
+ case "partially_fulfilled":
83
+ case "open":
84
+ case "in_transit":
85
+ return "warning";
86
+ case "refunded":
87
+ case "partially_refunded":
88
+ case "voided":
89
+ case "cancelled":
90
+ case "overdue":
91
+ case "failed":
92
+ case "returned":
93
+ return "destructive";
94
+ default:
95
+ return "muted";
96
+ }
108
97
  }
109
- function Row({ label, value, strong }) {
110
- return /* @__PURE__ */ jsxs("div", { className: `flex items-baseline justify-between gap-4 py-1 ${strong ? "font-semibold" : ""}`, children: [
111
- /* @__PURE__ */ jsx("span", { className: strong ? "" : "text-muted-foreground", children: label }),
112
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: value })
113
- ] });
98
+ function InlineBadge({ value }) {
99
+ const tone = toneOf(value);
100
+ const cls = {
101
+ success: "bg-emerald-100 text-emerald-800",
102
+ warning: "bg-amber-100 text-amber-800",
103
+ destructive: "bg-red-100 text-red-700",
104
+ info: "bg-sky-100 text-sky-800",
105
+ muted: "bg-muted text-muted-foreground"
106
+ };
107
+ return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${cls[tone]}`, children: LABEL[value] ?? value });
114
108
  }
115
109
  function deliveryFromNotes(notes) {
116
110
  if (!notes) return null;
117
111
  const match = notes.match(/entrega:\s*(.+)/i);
118
112
  return (match?.[1] ?? "").trim() || null;
119
113
  }
120
- function BlockSkeleton({ rows }) {
121
- return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "pt-4", children: [
122
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-28" }),
123
- /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2.5", children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: `h-3 ${i % 3 === 2 ? "w-1/2" : i % 3 === 1 ? "w-2/3" : "w-full"}` }, i)) })
124
- ] }) });
125
- }
126
- function OrderDetailSkeleton() {
127
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", "aria-busy": "true", "aria-live": "polite", children: [
128
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" }),
129
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
130
- /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-48" }),
131
- /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-20 rounded-full" }),
132
- /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-24 rounded-full" }),
133
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" })
134
- ] }),
135
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-3", children: [
136
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 lg:col-span-2", children: [
137
- /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "pt-4", children: [
138
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16" }),
139
- /* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-24" }),
140
- /* @__PURE__ */ jsx("div", { className: "mt-3 divide-y", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-2", children: [
141
- /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-10 shrink-0 rounded" }),
142
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1.5", children: [
143
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-2/5" }),
144
- /* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-20" })
145
- ] }),
146
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }),
147
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" })
148
- ] }, i)) })
149
- ] }) }),
150
- /* @__PURE__ */ jsx(BlockSkeleton, { rows: 6 })
151
- ] }),
152
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
153
- /* @__PURE__ */ jsx(BlockSkeleton, { rows: 2 }),
154
- /* @__PURE__ */ jsx(BlockSkeleton, { rows: 4 }),
155
- /* @__PURE__ */ jsx(BlockSkeleton, { rows: 3 })
156
- ] })
157
- ] })
158
- ] });
159
- }
160
- function OrderDetailView({
161
- orderId,
162
- provider,
163
- onBack
164
- }) {
165
- const [order, setOrder] = useState(null);
166
- const [shipments, setShipments] = useState([]);
167
- const [loading, setLoading] = useState(true);
114
+ var shopOrderCache = /* @__PURE__ */ new Map();
115
+ var shopShipmentsCache = /* @__PURE__ */ new Map();
116
+ var shopReceivableCache = /* @__PURE__ */ new Map();
117
+ function OrderDetailView({ orderId, provider, onBack }) {
118
+ const [order, setOrder] = useState(() => shopOrderCache.get(orderId) ?? null);
119
+ const [shipments, setShipments] = useState(() => shopShipmentsCache.get(orderId) ?? []);
120
+ const [loading, setLoading] = useState(() => !shopOrderCache.has(orderId));
168
121
  const [error, setError] = useState(null);
169
122
  const [confirming, setConfirming] = useState(false);
170
123
  const [confirmError, setConfirmError] = useState(null);
171
- const [receivable, setReceivable] = useState(null);
124
+ const [receivable, setReceivable] = useState(() => shopReceivableCache.get(orderId) ?? null);
172
125
  useEffect(() => {
173
126
  let cancelled = false;
127
+ const cached = shopOrderCache.get(orderId);
128
+ if (cached) {
129
+ setOrder(cached);
130
+ setLoading(false);
131
+ return () => {
132
+ cancelled = true;
133
+ };
134
+ }
174
135
  setLoading(true);
175
136
  setError(null);
176
137
  provider().getOrder(orderId).then((o) => {
177
- if (!cancelled) setOrder(o);
138
+ if (!cancelled) {
139
+ if (o) shopOrderCache.set(orderId, o);
140
+ setOrder(o);
141
+ }
178
142
  }).catch((err) => {
179
143
  if (!cancelled) setError(err instanceof Error ? err.message : String(err));
180
144
  }).finally(() => {
@@ -188,219 +152,202 @@ function OrderDetailView({
188
152
  let cancelled = false;
189
153
  const db = getSupabaseClientOptional();
190
154
  if (!db) return;
191
- db.from("fulfillments").select("*").eq("order_id", orderId).order("created_at", { ascending: false }).then(({ data }) => {
192
- if (!cancelled) setShipments(data ?? []);
155
+ if (shopShipmentsCache.has(orderId)) return () => {
156
+ cancelled = true;
157
+ };
158
+ db.from("fulfillments").select("*").eq("order_id", orderId).order("created_at", { ascending: false }).then(({ data: data2 }) => {
159
+ if (!cancelled) {
160
+ shopShipmentsCache.set(orderId, data2 ?? []);
161
+ setShipments(data2 ?? []);
162
+ }
193
163
  });
194
164
  return () => {
195
165
  cancelled = true;
196
166
  };
197
167
  }, [orderId]);
198
- if (loading) return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
199
- if (error) {
200
- return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800", children: [
201
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: "N\xE3o foi poss\xEDvel carregar o pedido." }),
202
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-red-700", children: error })
203
- ] });
204
- }
205
- if (!order) return /* @__PURE__ */ jsx("div", { className: "py-12 text-center text-sm text-muted-foreground", children: "Pedido n\xE3o encontrado." });
206
- const delivery = deliveryFromNotes(order.notes);
207
- const addr = order.shippingAddress;
208
168
  useEffect(() => {
209
169
  let cancelled = false;
210
170
  const db = getSupabaseClientOptional();
211
171
  if (!db) return;
172
+ if (shopReceivableCache.has(orderId)) {
173
+ setReceivable(shopReceivableCache.get(orderId) ?? null);
174
+ return () => {
175
+ cancelled = true;
176
+ };
177
+ }
212
178
  void (async () => {
213
179
  const [balance, bills] = await Promise.all([
214
180
  db.from("v_invoice_balances").select("*").eq("invoice_id", orderId).maybeSingle(),
215
181
  db.from("plg_financial_movements").select("*").eq("invoice_id", orderId).eq("movement_kind", "bill").order("installment_number", { ascending: true })
216
182
  ]);
217
183
  if (cancelled || !balance?.data) return;
218
- setReceivable({ balance: balance.data, bills: bills?.data ?? [] });
184
+ const rec = { balance: balance.data, bills: bills?.data ?? [] };
185
+ shopReceivableCache.set(orderId, rec);
186
+ setReceivable(rec);
219
187
  })();
220
188
  return () => {
221
189
  cancelled = true;
222
190
  };
223
191
  }, [orderId, confirming]);
192
+ if (loading) return /* @__PURE__ */ jsx(OrderDetailSkeleton, {});
193
+ if (error) {
194
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800", children: [
195
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "N\xE3o foi poss\xEDvel carregar o pedido." }),
196
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-red-700", children: error })
197
+ ] });
198
+ }
199
+ if (!order) return /* @__PURE__ */ jsx("div", { className: "py-12 text-center text-sm text-muted-foreground", children: "Pedido n\xE3o encontrado." });
200
+ const cur = order.currency;
201
+ const delivery = deliveryFromNotes(order.notes);
202
+ const addr = order.shippingAddress;
224
203
  async function registerReceipt() {
225
204
  if (!order) return;
205
+ shopReceivableCache.delete(order.id);
226
206
  setConfirming(true);
227
207
  setConfirmError(null);
228
208
  try {
229
- const updated = await provider().confirmPayment?.(order.id);
230
- if (updated) setOrder(updated);
231
- else setOrder(await provider().getOrder(order.id));
209
+ const fresh = await provider().confirmPayment?.(order.id) ?? await provider().getOrder(order.id);
210
+ if (fresh) {
211
+ shopOrderCache.set(order.id, fresh);
212
+ setOrder(fresh);
213
+ }
232
214
  } catch (err) {
233
215
  setConfirmError(err instanceof Error ? err.message : String(err));
234
216
  } finally {
235
217
  setConfirming(false);
236
218
  }
237
219
  }
238
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
239
- /* @__PURE__ */ jsx("button", { onClick: onBack, className: "text-sm text-muted-foreground hover:text-foreground", children: "\u2190 Pedidos" }),
240
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
241
- /* @__PURE__ */ jsxs("h1", { className: "text-2xl font-semibold", children: [
242
- "Pedido #",
243
- order.orderNumber
244
- ] }),
245
- /* @__PURE__ */ jsx(Badge, { value: order.financialStatus }),
246
- /* @__PURE__ */ jsx(Badge, { value: order.fulfillmentStatus }),
247
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: dateTime(order.createdAt) })
248
- ] }),
249
- confirmError && /* @__PURE__ */ jsxs("p", { className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800", children: [
250
- "N\xE3o foi poss\xEDvel confirmar o pagamento: ",
251
- confirmError
220
+ const installments = receivable && receivable.bills.length > 1 ? receivable.bills.map((b) => ({
221
+ number: b.installment_number ?? 0,
222
+ amount: `${money(Number(b.paid_amount ?? 0), cur)} / ${money(Number(b.amount), cur)}`,
223
+ status: RECEIVABLE_LABEL[b.status] ?? b.status,
224
+ statusTone: toneOf(b.status),
225
+ dueDate: `vence ${dateOnly(b.due_date)}`
226
+ })) : [];
227
+ const data = {
228
+ title: `Pedido #${order.orderNumber}`,
229
+ subtitle: dateTime(order.createdAt),
230
+ badges: [
231
+ { label: LABEL[order.financialStatus] ?? order.financialStatus, tone: toneOf(order.financialStatus) },
232
+ { label: LABEL[order.fulfillmentStatus] ?? order.fulfillmentStatus, tone: toneOf(order.fulfillmentStatus) }
233
+ ],
234
+ customer: { name: order.customerName, email: order.customerEmail },
235
+ customerNode: /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
236
+ order.customerId ? /* @__PURE__ */ jsx(
237
+ PersonLink,
238
+ {
239
+ personId: order.customerId,
240
+ name: order.customerName ?? order.customerEmail ?? "Cliente",
241
+ lookup: shopCustomerLookup(provider),
242
+ profileHref: `#/customers/${order.customerId}`,
243
+ className: "font-medium"
244
+ }
245
+ ) : /* @__PURE__ */ jsx("p", { className: "font-medium", children: order.customerName ?? "\u2014" }),
246
+ order.customerEmail && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: order.customerEmail }),
247
+ !order.customerId && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Compra sem cadastro \u2014 n\xE3o h\xE1 ficha de cliente para abrir." })
252
248
  ] }),
253
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-3", children: [
254
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 lg:col-span-2", children: [
255
- /* @__PURE__ */ jsx(Block, { title: "Itens", hint: `${order.items.length} item(ns)`, children: /* @__PURE__ */ jsx("div", { className: "divide-y", children: order.items.map((item) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-2", children: [
256
- item.imageUrl ? /* @__PURE__ */ jsx("img", { src: item.imageUrl, alt: "", className: "h-10 w-10 shrink-0 rounded object-cover" }) : /* @__PURE__ */ jsx("div", { className: "h-10 w-10 shrink-0 rounded bg-muted" }),
257
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
258
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: item.name }),
259
- item.sku && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: item.sku })
260
- ] }),
261
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
262
- item.quantity,
263
- " \xD7 ",
264
- money(item.unitPrice, order.currency)
265
- ] }),
266
- /* @__PURE__ */ jsx("span", { className: "w-24 text-right text-sm font-medium tabular-nums", children: money(item.total, order.currency) })
267
- ] }, item.id)) }) }),
268
- /* @__PURE__ */ jsx(Block, { title: "Pagamento", hint: "O que o financeiro precisa conciliar", children: /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
269
- /* @__PURE__ */ jsx(Row, { label: "Subtotal", value: money(order.subtotal, order.currency) }),
270
- order.discountTotal > 0 && /* @__PURE__ */ jsx(
271
- Row,
272
- {
273
- label: `Desconto${order.discountCode ? ` (${order.discountCode})` : ""}`,
274
- value: `\u2212 ${money(order.discountTotal, order.currency)}`
275
- }
276
- ),
277
- /* @__PURE__ */ jsx(Row, { label: "Entrega", value: money(order.shippingTotal, order.currency) }),
278
- /* @__PURE__ */ jsx(Row, { label: "Impostos", value: money(order.taxTotal, order.currency) }),
279
- /* @__PURE__ */ jsx("div", { className: "mt-2 border-t pt-2", children: /* @__PURE__ */ jsx(Row, { label: "Total", value: money(order.total, order.currency), strong: true }) }),
280
- /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t pt-2", children: [
281
- /* @__PURE__ */ jsx(Row, { label: "Forma de pagamento", value: PAYMENT_LABEL[order.paymentMethodKind ?? ""] ?? "\u2014" }),
282
- order.paidAt && /* @__PURE__ */ jsx(Row, { label: "Pago em", value: dateTime(order.paidAt) })
283
- ] }),
284
- !order.paymentMethodKind && /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Este pedido \xE9 anterior ao registro de forma de pagamento." })
285
- ] }) }),
249
+ items: order.items.map((it) => ({
250
+ id: it.id,
251
+ name: it.name,
252
+ description: it.sku,
253
+ imageUrl: it.imageUrl,
254
+ quantity: it.quantity,
255
+ unitPrice: money(it.unitPrice, cur),
256
+ total: money(it.total, cur)
257
+ })),
258
+ totals: [
259
+ { label: "Subtotal", value: money(order.subtotal, cur) },
260
+ ...order.discountTotal > 0 ? [{ label: `Desconto${order.discountCode ? ` (${order.discountCode})` : ""}`, value: `\u2212 ${money(order.discountTotal, cur)}`, muted: true }] : [],
261
+ { label: "Entrega", value: money(order.shippingTotal, cur) },
262
+ { label: "Impostos", value: money(order.taxTotal, cur) },
263
+ { label: "Total", value: money(order.total, cur), strong: true }
264
+ ],
265
+ paymentMethod: order.paymentMethodKind ? PAYMENT_LABEL[order.paymentMethodKind] ?? order.paymentMethodKind : void 0,
266
+ paidAt: order.paidAt ? dateTime(order.paidAt) : void 0,
267
+ receivable: receivable ? {
268
+ referenceNumber: receivable.balance.reference_number ?? void 0,
269
+ statusBadge: { label: RECEIVABLE_LABEL[receivable.balance.status] ?? receivable.balance.status, tone: toneOf(receivable.balance.status) },
270
+ rows: [
271
+ { label: "Faturado", value: money(receivable.balance.amount, cur) },
272
+ { label: "Recebido", value: money(receivable.balance.paid, cur) },
273
+ { label: "Saldo", value: money(receivable.balance.balance, cur), strong: true }
274
+ ],
275
+ installments,
276
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
277
+ receivable.balance.balance > 0 && receivable.balance.status !== "cancelled" && /* @__PURE__ */ jsx(
278
+ "button",
279
+ {
280
+ type: "button",
281
+ onClick: registerReceipt,
282
+ disabled: confirming,
283
+ "data-testid": "order-register-receipt",
284
+ className: "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
285
+ children: confirming ? "Registrando\u2026" : "Registrar recebimento"
286
+ }
287
+ ),
286
288
  /* @__PURE__ */ jsx(
287
- Block,
289
+ "button",
288
290
  {
289
- title: "Contas a receber",
290
- hint: "Criado automaticamente quando o pedido foi feito \u2014 o mesmo registro que o Financeiro l\xEA",
291
- children: !receivable ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Sem conta a receber para este pedido." }) : /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
292
- /* @__PURE__ */ jsxs("div", { className: "mb-3 flex flex-wrap items-center gap-2", children: [
293
- /* @__PURE__ */ jsx("span", { className: "font-mono font-semibold", children: receivable.balance.reference_number ?? "\u2014" }),
294
- /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${RECEIVABLE_TONE[receivable.balance.status] ?? RECEIVABLE_TONE.open}`, children: RECEIVABLE_LABEL[receivable.balance.status] ?? receivable.balance.status })
295
- ] }),
296
- /* @__PURE__ */ jsx(Row, { label: "Faturado", value: money(receivable.balance.amount, order.currency) }),
297
- /* @__PURE__ */ jsx(Row, { label: "Recebido", value: money(receivable.balance.paid, order.currency) }),
298
- /* @__PURE__ */ jsx("div", { className: "mt-2 border-t pt-2", children: /* @__PURE__ */ jsx(Row, { label: "Saldo", value: money(receivable.balance.balance, order.currency), strong: true }) }),
299
- receivable.bills.length > 1 && /* @__PURE__ */ jsx("ul", { className: "mt-3 space-y-1 border-t pt-2 text-xs", children: receivable.bills.map((bill) => /* @__PURE__ */ jsxs("li", { className: "flex items-baseline justify-between gap-3", children: [
300
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
301
- "Parcela ",
302
- bill.installment_number,
303
- " \xB7 vence ",
304
- new Date(bill.due_date).toLocaleDateString("pt-BR")
305
- ] }),
306
- /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
307
- money(Number(bill.paid_amount ?? 0), order.currency),
308
- " / ",
309
- money(Number(bill.amount), order.currency)
310
- ] })
311
- ] }, bill.id)) }),
312
- /* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap gap-2", children: [
313
- receivable.balance.balance > 0 && receivable.balance.status !== "cancelled" && /* @__PURE__ */ jsx(
314
- "button",
315
- {
316
- type: "button",
317
- onClick: registerReceipt,
318
- disabled: confirming,
319
- "data-testid": "order-register-receipt",
320
- className: "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
321
- children: confirming ? "Registrando\u2026" : "Registrar recebimento"
322
- }
323
- ),
324
- /* @__PURE__ */ jsx(
325
- "button",
326
- {
327
- type: "button",
328
- onClick: () => adminNavigateTo("/financial"),
329
- className: "rounded-md border px-3 py-1.5 text-sm font-medium transition hover:bg-muted",
330
- children: "Abrir no Financeiro"
331
- }
332
- )
333
- ] }),
334
- confirmError && /* @__PURE__ */ jsx("p", { className: "mt-2 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-800", children: confirmError })
335
- ] })
291
+ type: "button",
292
+ onClick: () => adminNavigateTo("/financial"),
293
+ className: "rounded-md border px-3 py-1.5 text-sm font-medium transition hover:bg-muted",
294
+ children: "Abrir no Financeiro"
336
295
  }
337
- )
296
+ ),
297
+ confirmError && /* @__PURE__ */ jsx("p", { className: "mt-2 w-full rounded-md border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-800", children: confirmError })
338
298
  ] }),
339
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
340
- /* @__PURE__ */ jsx(Block, { title: "Cliente", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
341
- order.customerId ? /* @__PURE__ */ jsx(
342
- PersonLink,
343
- {
344
- personId: order.customerId,
345
- name: order.customerName ?? order.customerEmail ?? "Cliente",
346
- lookup: shopCustomerLookup(provider),
347
- profileHref: `#/shop/customers/${order.customerId}`,
348
- className: "font-medium"
349
- }
350
- ) : /* @__PURE__ */ jsx("p", { className: "font-medium", children: order.customerName ?? "\u2014" }),
351
- order.customerEmail && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: order.customerEmail }),
352
- !order.customerId && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Compra sem cadastro \u2014 n\xE3o h\xE1 ficha de cliente para abrir." })
353
- ] }) }),
354
- /* @__PURE__ */ jsx(Block, { title: "Entrega", hint: "O que a log\xEDstica precisa", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2 text-sm", children: [
355
- addr ? /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
356
- /* @__PURE__ */ jsxs("p", { children: [
357
- [addr.street, addr.number].filter(Boolean).join(", "),
358
- addr.complement ? ` \u2014 ${addr.complement}` : ""
359
- ] }),
360
- addr.district && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: addr.district }),
361
- /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
362
- [addr.city, addr.state].filter((v) => v && v !== "\u2014").join("/"),
363
- addr.postal_code ? ` \xB7 ${addr.postal_code}` : ""
364
- ] }),
365
- (addr.recipient || addr.phone) && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: [addr.recipient, addr.phone].filter(Boolean).join(" \xB7 ") })
366
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
367
- /* @__PURE__ */ jsx("p", { className: delivery ? "" : "text-muted-foreground", children: delivery ?? "Nenhum endere\xE7o registrado neste pedido." }),
368
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Pedido anterior ao endere\xE7o estruturado \u2014 s\xF3 existe o texto livre das observa\xE7\xF5es, sem CEP, n\xFAmero ou bairro separ\xE1veis." })
299
+ emptyLabel: "Sem conta a receber para este pedido."
300
+ } : { rows: [], installments: [], emptyLabel: "Sem conta a receber para este pedido." },
301
+ extraSections: /* @__PURE__ */ jsxs(Fragment, { children: [
302
+ /* @__PURE__ */ jsx(OrderDetailBlock, { title: "Entrega", hint: "O que a log\xEDstica precisa", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2 text-sm", children: [
303
+ addr ? /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
304
+ /* @__PURE__ */ jsxs("p", { children: [
305
+ [addr.street, addr.number].filter(Boolean).join(", "),
306
+ addr.complement ? ` \u2014 ${addr.complement}` : ""
307
+ ] }),
308
+ addr.district && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: addr.district }),
309
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
310
+ [addr.city, addr.state].filter((v) => v && v !== "\u2014").join("/"),
311
+ addr.postal_code ? ` \xB7 ${addr.postal_code}` : ""
369
312
  ] }),
370
- /* @__PURE__ */ jsxs("div", { className: "border-t pt-2", children: [
371
- /* @__PURE__ */ jsx(Row, { label: "Status", value: /* @__PURE__ */ jsx(Badge, { value: order.fulfillmentStatus }) }),
372
- shipments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Nenhum envio registrado. Ao despachar, informe transportadora e c\xF3digo de rastreio \u2014 o status do pedido \xE9 calculado a partir dos envios." }) : /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-2", children: shipments.map((s) => /* @__PURE__ */ jsxs("div", { className: "rounded-md border px-2 py-1.5", children: [
373
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
374
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: [s.carrier, s.service].filter(Boolean).join(" \xB7 ") || "Envio" }),
375
- /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: SHIPMENT_LABEL[s.status] ?? s.status })
313
+ (addr.recipient || addr.phone) && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: [addr.recipient, addr.phone].filter(Boolean).join(" \xB7 ") })
314
+ ] }) : /* @__PURE__ */ jsx("p", { className: delivery ? "" : "text-muted-foreground", children: delivery ?? "Nenhum endere\xE7o registrado neste pedido." }),
315
+ /* @__PURE__ */ jsxs("div", { className: "border-t pt-2", children: [
316
+ /* @__PURE__ */ jsx(OrderDetailRow, { label: "Status", value: /* @__PURE__ */ jsx(InlineBadge, { value: order.fulfillmentStatus }) }),
317
+ shipments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Nenhum envio registrado." }) : /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-2", children: shipments.map((s) => /* @__PURE__ */ jsxs("div", { className: "rounded-md border px-2 py-1.5", children: [
318
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
319
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: [s.carrier, s.service].filter(Boolean).join(" \xB7 ") || "Envio" }),
320
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: SHIPMENT_LABEL[s.status] ?? s.status })
321
+ ] }),
322
+ s.tracking_code && /* @__PURE__ */ jsx("p", { className: "mt-0.5 font-mono text-xs", children: s.tracking_url ? /* @__PURE__ */ jsx("a", { href: s.tracking_url, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: s.tracking_code }) : s.tracking_code }),
323
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5 text-[11px] text-muted-foreground", children: [
324
+ s.shipped_at && /* @__PURE__ */ jsxs("p", { children: [
325
+ "Despachado em ",
326
+ dateTime(s.shipped_at)
376
327
  ] }),
377
- s.tracking_code && /* @__PURE__ */ jsx("p", { className: "mt-0.5 font-mono text-xs", children: s.tracking_url ? /* @__PURE__ */ jsx("a", { href: s.tracking_url, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: s.tracking_code }) : s.tracking_code }),
378
- /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5 text-[11px] text-muted-foreground", children: [
379
- s.shipped_at && /* @__PURE__ */ jsxs("p", { children: [
380
- "Despachado em ",
381
- dateTime(s.shipped_at)
382
- ] }),
383
- s.estimated_delivery_at && /* @__PURE__ */ jsxs("p", { children: [
384
- "Previs\xE3o: ",
385
- dateTime(s.estimated_delivery_at)
386
- ] }),
387
- s.delivered_at && /* @__PURE__ */ jsxs("p", { children: [
388
- "Entregue em ",
389
- dateTime(s.delivered_at)
390
- ] })
328
+ s.estimated_delivery_at && /* @__PURE__ */ jsxs("p", { children: [
329
+ "Previs\xE3o: ",
330
+ dateTime(s.estimated_delivery_at)
331
+ ] }),
332
+ s.delivered_at && /* @__PURE__ */ jsxs("p", { children: [
333
+ "Entregue em ",
334
+ dateTime(s.delivered_at)
391
335
  ] })
392
- ] }, s.id)) })
393
- ] })
394
- ] }) }),
395
- /* @__PURE__ */ jsx(Block, { title: "Hist\xF3rico", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
396
- /* @__PURE__ */ jsx(Row, { label: "Criado", value: dateTime(order.createdAt) }),
397
- /* @__PURE__ */ jsx(Row, { label: "Atualizado", value: dateTime(order.updatedAt) }),
398
- /* @__PURE__ */ jsx(Row, { label: "Situa\xE7\xE3o", value: /* @__PURE__ */ jsx(Badge, { value: order.status }) })
399
- ] }) }),
400
- order.notes && /* @__PURE__ */ jsx(Block, { title: "Observa\xE7\xF5es", children: /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: order.notes }) })
401
- ] })
402
- ] })
403
- ] });
336
+ ] })
337
+ ] }, s.id)) })
338
+ ] })
339
+ ] }) }),
340
+ /* @__PURE__ */ jsx(OrderDetailBlock, { title: "Hist\xF3rico", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm", children: [
341
+ /* @__PURE__ */ jsx(OrderDetailRow, { label: "Criado", value: dateTime(order.createdAt) }),
342
+ /* @__PURE__ */ jsx(OrderDetailRow, { label: "Atualizado", value: dateTime(order.updatedAt) }),
343
+ /* @__PURE__ */ jsx(OrderDetailRow, { label: "Situa\xE7\xE3o", value: /* @__PURE__ */ jsx(InlineBadge, { value: order.status }) })
344
+ ] }) }),
345
+ order.notes && /* @__PURE__ */ jsx(OrderDetailBlock, { title: "Observa\xE7\xF5es", children: /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: order.notes }) })
346
+ ] }),
347
+ onBack,
348
+ backLabel: "Pedidos"
349
+ };
350
+ return /* @__PURE__ */ jsx(OrderDetailView$1, { ...data });
404
351
  }
405
352
  function currency(value, code = "BRL", locale = "pt-BR") {
406
353
  return new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(value);
@@ -555,7 +502,7 @@ function OrdersTab({ provider }) {
555
502
  personId: id,
556
503
  name: label,
557
504
  lookup: shopCustomerLookup(() => resolveShopProvider(provider)),
558
- profileHref: `#/shop/customers/${id}`
505
+ profileHref: `#/customers/${id}`
559
506
  }
560
507
  );
561
508
  }
@@ -739,5 +686,5 @@ var ShopPage = ({ provider, section }) => {
739
686
  ShopPage.displayName = "ShopPage";
740
687
 
741
688
  export { ShopPage };
742
- //# sourceMappingURL=ShopPage-HCLXMN7Q.js.map
743
- //# sourceMappingURL=ShopPage-HCLXMN7Q.js.map
689
+ //# sourceMappingURL=ShopPage-JP27MZS2.js.map
690
+ //# sourceMappingURL=ShopPage-JP27MZS2.js.map