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