@fayz-ai/storefront 0.84.0 → 0.84.2
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/CheckoutPage-DDY4WLFJ.js +12 -0
- package/dist/CheckoutPage-DDY4WLFJ.js.map +1 -0
- package/dist/MyPurchasesPage-IHCB5QRR.js +11 -0
- package/dist/MyPurchasesPage-IHCB5QRR.js.map +1 -0
- package/dist/OrderConfirmationPage-SAKAGOCC.js +10 -0
- package/dist/OrderConfirmationPage-SAKAGOCC.js.map +1 -0
- package/dist/analytics/meta-pixel.d.ts +13 -0
- package/dist/analytics/meta-pixel.d.ts.map +1 -1
- package/dist/app/createStorefrontApp.d.ts.map +1 -1
- package/dist/chunk-45R7CIEA.js +234 -0
- package/dist/chunk-45R7CIEA.js.map +1 -0
- package/dist/{chunk-QU5O4CKY.js → chunk-6NUBVJJY.js} +5 -4
- package/dist/chunk-6NUBVJJY.js.map +1 -0
- package/dist/chunk-BKO2AQ3Q.js +329 -0
- package/dist/chunk-BKO2AQ3Q.js.map +1 -0
- package/dist/chunk-CV6RYJIC.js +454 -0
- package/dist/chunk-CV6RYJIC.js.map +1 -0
- package/dist/chunk-D5C5U2SG.js +267 -0
- package/dist/chunk-D5C5U2SG.js.map +1 -0
- package/dist/chunk-DRMDBXMT.js +2123 -0
- package/dist/chunk-DRMDBXMT.js.map +1 -0
- package/dist/chunk-DVQHKNNX.js +29 -0
- package/dist/chunk-DVQHKNNX.js.map +1 -0
- package/dist/chunk-FCPOPWG3.js +31 -0
- package/dist/chunk-FCPOPWG3.js.map +1 -0
- package/dist/chunk-GQ3TCJVX.js +216 -0
- package/dist/chunk-GQ3TCJVX.js.map +1 -0
- package/dist/{chunk-7N5HKQBM.js → chunk-GVZ2SAOI.js} +5 -552
- package/dist/chunk-GVZ2SAOI.js.map +1 -0
- package/dist/{chunk-7FR6KOW2.js → chunk-JLCF7CPP.js} +1256 -6590
- package/dist/chunk-JLCF7CPP.js.map +1 -0
- package/dist/chunk-K27LYMSF.js +435 -0
- package/dist/chunk-K27LYMSF.js.map +1 -0
- package/dist/chunk-MFAU3AWK.js +817 -0
- package/dist/chunk-MFAU3AWK.js.map +1 -0
- package/dist/chunk-OHWX6BEY.js +436 -0
- package/dist/chunk-OHWX6BEY.js.map +1 -0
- package/dist/chunk-PATS4DIF.js +484 -0
- package/dist/chunk-PATS4DIF.js.map +1 -0
- package/dist/chunk-WDQFNHYO.js +41 -0
- package/dist/chunk-WDQFNHYO.js.map +1 -0
- package/dist/chunk-WPWNYBNG.js +68 -0
- package/dist/chunk-WPWNYBNG.js.map +1 -0
- package/dist/chunk-WXXYJZE6.js +99 -0
- package/dist/chunk-WXXYJZE6.js.map +1 -0
- package/dist/host.js +4 -2
- package/dist/host.js.map +1 -1
- package/dist/index.js +26 -6
- package/dist/index.js.map +1 -1
- package/dist/render.js +11 -3
- package/dist/render.js.map +1 -1
- package/dist/tanstack.js +10 -2
- package/dist/tanstack.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-7FR6KOW2.js.map +0 -1
- package/dist/chunk-7N5HKQBM.js.map +0 -1
- package/dist/chunk-QU5O4CKY.js.map +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { accountSectionPath } from './chunk-DVQHKNNX.js';
|
|
2
|
+
import { useSessionStore, TID, signOutCustomer } from './chunk-PATS4DIF.js';
|
|
3
|
+
import { useStorefrontConfig, Link } from './chunk-GVZ2SAOI.js';
|
|
4
|
+
import { User, Package, MapPin, CreditCard, ShieldCheck, LogOut, Check, XCircle, RotateCcw, Clock, PackageCheck, Truck, ChefHat, Store, Bike } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var menuItems = [
|
|
8
|
+
{ id: "profile", label: "Perfil", icon: User },
|
|
9
|
+
{ id: "orders", label: "Meus pedidos", icon: Package },
|
|
10
|
+
{ id: "addresses", label: "Endere\xE7os", icon: MapPin },
|
|
11
|
+
{ id: "payments", label: "Pagamentos", icon: CreditCard }
|
|
12
|
+
];
|
|
13
|
+
function CustomerAccountShell({ title, subtitle, active, onSelect, children }) {
|
|
14
|
+
const config = useStorefrontConfig();
|
|
15
|
+
const session = useSessionStore();
|
|
16
|
+
const displayName = session.name || session.email?.split("@")[0] || "Cliente";
|
|
17
|
+
const displayEmail = session.email || "Conta criada no checkout";
|
|
18
|
+
return /* @__PURE__ */ jsxs("main", { "data-testid": TID.customerAccountShell, className: "mx-auto max-w-6xl px-4 py-8 sm:px-6", children: [
|
|
19
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-4", children: [
|
|
20
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
21
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-muted-foreground", children: config.name }),
|
|
22
|
+
/* @__PURE__ */ jsx("h1", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
|
|
23
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: subtitle })
|
|
24
|
+
] }),
|
|
25
|
+
/* @__PURE__ */ jsx(Link, { to: config.catalogPath, className: "rounded-lg border px-4 py-2 text-sm font-semibold hover:bg-muted", children: "Voltar \xE0 loja" })
|
|
26
|
+
] }),
|
|
27
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-[260px_minmax(0,1fr)]", children: [
|
|
28
|
+
/* @__PURE__ */ jsxs("aside", { className: "h-fit rounded-xl border bg-card p-4", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 border-b pb-4", children: [
|
|
30
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 font-bold text-primary", children: displayName.slice(0, 1).toUpperCase() }),
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-semibold", children: displayName }),
|
|
33
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: displayEmail })
|
|
34
|
+
] })
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsx("nav", { "data-testid": TID.customerAccountMenu, className: "mt-4 space-y-1", "aria-label": "\xC1rea do cliente", children: menuItems.map((item) => {
|
|
37
|
+
const Icon = item.icon;
|
|
38
|
+
const selected = item.id === active;
|
|
39
|
+
return /* @__PURE__ */ jsxs(
|
|
40
|
+
Link,
|
|
41
|
+
{
|
|
42
|
+
to: accountSectionPath(item.id),
|
|
43
|
+
"data-section": item.id,
|
|
44
|
+
"aria-current": selected ? "page" : void 0,
|
|
45
|
+
onClick: (event) => {
|
|
46
|
+
if (!onSelect) return;
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
onSelect(item.id);
|
|
49
|
+
},
|
|
50
|
+
className: `flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold transition ${selected ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
53
|
+
item.label
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
item.id
|
|
57
|
+
);
|
|
58
|
+
}) }),
|
|
59
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg bg-muted/40 p-3 text-xs text-muted-foreground", children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 font-semibold text-foreground", children: [
|
|
61
|
+
/* @__PURE__ */ jsx(ShieldCheck, { className: "h-4 w-4 text-primary" }),
|
|
62
|
+
"Conta protegida"
|
|
63
|
+
] }),
|
|
64
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1", children: "Pedidos, pagamentos e endere\xE7os ficam organizados para pr\xF3ximas compras." })
|
|
65
|
+
] }),
|
|
66
|
+
/* @__PURE__ */ jsxs(
|
|
67
|
+
"button",
|
|
68
|
+
{
|
|
69
|
+
type: "button",
|
|
70
|
+
"data-testid": TID.signout,
|
|
71
|
+
onClick: () => void signOutCustomer(),
|
|
72
|
+
className: "mt-4 flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-muted-foreground transition hover:bg-muted hover:text-foreground",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" }),
|
|
75
|
+
"Sair"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ jsx("section", { className: "min-w-0", children })
|
|
81
|
+
] })
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/data/order-status.ts
|
|
86
|
+
var FINANCIAL = {
|
|
87
|
+
pending: { label: "Pendente", tone: "pending" },
|
|
88
|
+
paid: { label: "Pago", tone: "positive" },
|
|
89
|
+
partially_paid: { label: "Parcialmente pago", tone: "pending" },
|
|
90
|
+
partially_refunded: { label: "Parcialmente reembolsado", tone: "negative" },
|
|
91
|
+
refunded: { label: "Reembolsado", tone: "negative" },
|
|
92
|
+
voided: { label: "Cancelado", tone: "negative" }
|
|
93
|
+
};
|
|
94
|
+
var TONE_CLASS = {
|
|
95
|
+
positive: "bg-emerald-500/15 text-emerald-600 ring-1 ring-inset ring-emerald-500/30",
|
|
96
|
+
pending: "bg-amber-500/15 text-amber-600 ring-1 ring-inset ring-amber-500/30",
|
|
97
|
+
negative: "bg-destructive/15 text-destructive ring-1 ring-inset ring-destructive/30"
|
|
98
|
+
};
|
|
99
|
+
function financialStatusBadge(status) {
|
|
100
|
+
const entry = FINANCIAL[status] ?? { label: status, tone: "pending" };
|
|
101
|
+
return { label: entry.label, className: TONE_CLASS[entry.tone] };
|
|
102
|
+
}
|
|
103
|
+
function isPaid(status) {
|
|
104
|
+
return status === "paid" || status === "partially_paid";
|
|
105
|
+
}
|
|
106
|
+
function isRefunded(status) {
|
|
107
|
+
return status === "refunded" || status === "partially_refunded";
|
|
108
|
+
}
|
|
109
|
+
function deliverySteps(order) {
|
|
110
|
+
const stage = order.deliveryStage;
|
|
111
|
+
const pickup = order.fulfillmentType === "pickup";
|
|
112
|
+
const received = {
|
|
113
|
+
id: "received",
|
|
114
|
+
label: "Pedido recebido",
|
|
115
|
+
description: `#${order.orderNumber}`,
|
|
116
|
+
state: "complete",
|
|
117
|
+
icon: Check
|
|
118
|
+
};
|
|
119
|
+
if (stage === "rejected" || stage === "cancelled") {
|
|
120
|
+
const refunded = isRefunded(order.financialStatus) || order.financialStatus === "voided";
|
|
121
|
+
return [
|
|
122
|
+
received,
|
|
123
|
+
{
|
|
124
|
+
id: "closed",
|
|
125
|
+
label: stage === "rejected" ? "Pedido recusado" : "Pedido cancelado",
|
|
126
|
+
description: [order.rejectReason, refunded ? "Valor estornado" : null].filter(Boolean).join(" \xB7 ") || "A loja cancelou este pedido",
|
|
127
|
+
state: "cancelled",
|
|
128
|
+
icon: XCircle
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
const rank = { new: 0, accepted: 1, out_for_delivery: 2, delivered: 3 }[stage] ?? 0;
|
|
133
|
+
const state = (step) => rank > step ? "complete" : rank === step ? "current" : "pending";
|
|
134
|
+
return [
|
|
135
|
+
{ ...received, state: rank === 0 ? "current" : "complete", description: rank === 0 ? "Aguardando a loja aceitar" : received.description },
|
|
136
|
+
{ id: "accepted", label: "Em preparo", description: "A loja aceitou seu pedido", state: state(1), icon: ChefHat },
|
|
137
|
+
{
|
|
138
|
+
id: "out",
|
|
139
|
+
label: pickup ? "Pronto para retirada" : "Saiu para entrega",
|
|
140
|
+
description: pickup ? "Pode vir buscar" : "A caminho de voc\xEA",
|
|
141
|
+
state: state(2),
|
|
142
|
+
icon: pickup ? Store : Bike
|
|
143
|
+
},
|
|
144
|
+
{ id: "delivered", label: pickup ? "Retirado" : "Entregue", description: "Bom apetite!", state: rank === 3 ? "complete" : "pending", icon: PackageCheck }
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
function orderTrackingSteps(order) {
|
|
148
|
+
if (order.deliveryStage) return deliverySteps(order);
|
|
149
|
+
const received = {
|
|
150
|
+
id: "received",
|
|
151
|
+
label: "Pedido recebido",
|
|
152
|
+
description: `#${order.orderNumber}`,
|
|
153
|
+
state: "complete",
|
|
154
|
+
icon: Check
|
|
155
|
+
};
|
|
156
|
+
if (order.status === "cancelled" || order.financialStatus === "voided") {
|
|
157
|
+
const refunded = isRefunded(order.financialStatus);
|
|
158
|
+
return [
|
|
159
|
+
received,
|
|
160
|
+
{
|
|
161
|
+
id: "payment",
|
|
162
|
+
label: "Pedido cancelado",
|
|
163
|
+
description: [order.rejectReason, refunded ? "Valor estornado" : null].filter(Boolean).join(" \xB7 ") || "Este pedido foi cancelado",
|
|
164
|
+
state: "cancelled",
|
|
165
|
+
icon: XCircle
|
|
166
|
+
}
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
if (isRefunded(order.financialStatus)) {
|
|
170
|
+
return [
|
|
171
|
+
received,
|
|
172
|
+
{ id: "payment", label: "Pagamento aprovado", description: "Compra confirmada", state: "complete", icon: Check },
|
|
173
|
+
{
|
|
174
|
+
id: "refund",
|
|
175
|
+
label: order.financialStatus === "refunded" ? "Reembolsado" : "Reembolso parcial",
|
|
176
|
+
description: "Valor devolvido ao cliente",
|
|
177
|
+
state: "cancelled",
|
|
178
|
+
icon: RotateCcw
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
const paid = isPaid(order.financialStatus);
|
|
183
|
+
const partiallyFulfilled = order.fulfillmentStatus === "partially_fulfilled";
|
|
184
|
+
const fulfilled = order.fulfillmentStatus === "fulfilled";
|
|
185
|
+
return [
|
|
186
|
+
received,
|
|
187
|
+
{
|
|
188
|
+
id: "payment",
|
|
189
|
+
label: paid ? "Pagamento aprovado" : "Aguardando pagamento",
|
|
190
|
+
// "Processando" descrevia um pagamento que ninguém iniciou.
|
|
191
|
+
description: paid ? "Compra confirmada" : "Ainda n\xE3o recebemos",
|
|
192
|
+
state: paid ? "complete" : "current",
|
|
193
|
+
icon: paid ? Check : Clock
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: "preparing",
|
|
197
|
+
label: "Preparando envio",
|
|
198
|
+
description: paid ? "Separando produtos" : "Come\xE7a ap\xF3s o pagamento",
|
|
199
|
+
state: fulfilled || partiallyFulfilled ? "complete" : paid ? "current" : "pending",
|
|
200
|
+
icon: PackageCheck
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "delivery",
|
|
204
|
+
label: fulfilled ? "Pedido enviado" : partiallyFulfilled ? "Envio parcial" : "Entrega",
|
|
205
|
+
description: fulfilled ? "A caminho do cliente" : partiallyFulfilled ? "Parte do pedido saiu" : "Pr\xF3xima etapa",
|
|
206
|
+
state: fulfilled ? "complete" : partiallyFulfilled ? "current" : "pending",
|
|
207
|
+
icon: fulfilled || partiallyFulfilled ? Truck : Clock
|
|
208
|
+
}
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
var GRID_COLS = {
|
|
212
|
+
2: "sm:grid-cols-2",
|
|
213
|
+
3: "sm:grid-cols-3",
|
|
214
|
+
4: "sm:grid-cols-4"
|
|
215
|
+
};
|
|
216
|
+
function OrderTrackingTimeline({ order, compact = false }) {
|
|
217
|
+
const steps = orderTrackingSteps(order);
|
|
218
|
+
return /* @__PURE__ */ jsx(
|
|
219
|
+
"ol",
|
|
220
|
+
{
|
|
221
|
+
"data-testid": TID.orderTracking,
|
|
222
|
+
className: `grid ${compact ? "gap-2" : "gap-3"} ${GRID_COLS[steps.length] ?? "sm:grid-cols-4"}`,
|
|
223
|
+
"aria-label": "Acompanhamento do pedido",
|
|
224
|
+
children: steps.map((step) => {
|
|
225
|
+
const Icon = step.icon;
|
|
226
|
+
const cancelled = step.state === "cancelled";
|
|
227
|
+
const done = step.state === "complete";
|
|
228
|
+
const current = step.state === "current";
|
|
229
|
+
const active = done || current;
|
|
230
|
+
return /* @__PURE__ */ jsx(
|
|
231
|
+
"li",
|
|
232
|
+
{
|
|
233
|
+
"data-testid": TID.orderTrackingStep(step.id),
|
|
234
|
+
"data-state": step.state,
|
|
235
|
+
className: [
|
|
236
|
+
"relative min-w-0 rounded-xl border p-3",
|
|
237
|
+
cancelled ? "border-destructive/40 bg-destructive/10" : active ? "border-primary/35 bg-primary/5" : "bg-muted/20 text-muted-foreground",
|
|
238
|
+
compact ? "text-xs" : "text-sm"
|
|
239
|
+
].join(" "),
|
|
240
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
241
|
+
/* @__PURE__ */ jsx(
|
|
242
|
+
"span",
|
|
243
|
+
{
|
|
244
|
+
className: [
|
|
245
|
+
"grid h-7 w-7 shrink-0 place-items-center rounded-full border",
|
|
246
|
+
cancelled ? "border-destructive bg-destructive text-destructive-foreground" : done ? "border-primary bg-primary text-primary-foreground" : current ? "border-primary bg-background text-primary" : "border-muted-foreground/30 bg-background"
|
|
247
|
+
].join(" "),
|
|
248
|
+
"aria-hidden": true,
|
|
249
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" })
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
253
|
+
/* @__PURE__ */ jsx("span", { className: "block font-semibold leading-snug text-foreground", children: step.label }),
|
|
254
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block leading-snug text-muted-foreground", children: step.description })
|
|
255
|
+
] })
|
|
256
|
+
] })
|
|
257
|
+
},
|
|
258
|
+
step.id
|
|
259
|
+
);
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export { CustomerAccountShell, OrderTrackingTimeline, financialStatusBadge, isPaid };
|
|
266
|
+
//# sourceMappingURL=chunk-D5C5U2SG.js.map
|
|
267
|
+
//# sourceMappingURL=chunk-D5C5U2SG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/account/CustomerAccountShell.tsx","../src/data/order-status.ts","../src/components/order/OrderTrackingTimeline.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-D5C5U2SG.js","sourcesContent":["import React from 'react'\nimport { CreditCard, LogOut, MapPin, Package, ShieldCheck, User } from 'lucide-react'\nimport { signOutCustomer } from '../../data/auth'\nimport { useStorefrontConfig } from '../../document/config'\nimport { Link } from '../../app/router'\nimport { useSessionStore } from '../../stores/session.store'\nimport { TID } from '../../lib/testids'\nimport { accountSectionPath, type AccountSection } from './section'\n\nexport type { AccountSection }\n\ninterface CustomerAccountShellProps {\n title: string\n subtitle?: string\n active: AccountSection\n /**\n * Switch the active section in-place instead of navigating. Optional, and\n * omitting it is the normal case: the menu is made of real links to\n * `/account?section=…`, so it works on any page that mounts this shell.\n */\n onSelect?: (section: AccountSection) => void\n children: React.ReactNode\n}\n\nconst menuItems = [\n { id: 'profile', label: 'Perfil', icon: User },\n { id: 'orders', label: 'Meus pedidos', icon: Package },\n { id: 'addresses', label: 'Endereços', icon: MapPin },\n { id: 'payments', label: 'Pagamentos', icon: CreditCard },\n] as const\n\nexport function CustomerAccountShell({ title, subtitle, active, onSelect, children }: CustomerAccountShellProps) {\n const config = useStorefrontConfig()\n const session = useSessionStore()\n const displayName = session.name || session.email?.split('@')[0] || 'Cliente'\n const displayEmail = session.email || 'Conta criada no checkout'\n\n return (\n <main data-testid={TID.customerAccountShell} className=\"mx-auto max-w-6xl px-4 py-8 sm:px-6\">\n <div className=\"mb-6 flex flex-wrap items-center justify-between gap-4\">\n <div>\n <p className=\"text-sm font-medium text-muted-foreground\">{config.name}</p>\n <h1 className=\"sf-heading text-3xl font-bold tracking-tight\">{title}</h1>\n {subtitle && <p className=\"mt-1 text-sm text-muted-foreground\">{subtitle}</p>}\n </div>\n <Link to={config.catalogPath} className=\"rounded-lg border px-4 py-2 text-sm font-semibold hover:bg-muted\">\n Voltar à loja\n </Link>\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-[260px_minmax(0,1fr)]\">\n <aside className=\"h-fit rounded-xl border bg-card p-4\">\n <div className=\"flex items-center gap-3 border-b pb-4\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 font-bold text-primary\">\n {displayName.slice(0, 1).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold\">{displayName}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{displayEmail}</p>\n </div>\n </div>\n\n <nav data-testid={TID.customerAccountMenu} className=\"mt-4 space-y-1\" aria-label=\"Área do cliente\">\n {menuItems.map((item) => {\n const Icon = item.icon\n const selected = item.id === active\n return (\n <Link\n key={item.id}\n to={accountSectionPath(item.id)}\n data-section={item.id}\n aria-current={selected ? 'page' : undefined}\n // A real href always, so the menu still works when the page\n // that mounted the shell has no in-place handler to offer.\n onClick={(event) => {\n if (!onSelect) return\n event.preventDefault()\n onSelect(item.id)\n }}\n className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold transition ${\n selected ? 'bg-primary/10 text-primary' : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {item.label}\n </Link>\n )\n })}\n </nav>\n\n <div className=\"mt-4 rounded-lg bg-muted/40 p-3 text-xs text-muted-foreground\">\n <div className=\"flex items-center gap-2 font-semibold text-foreground\">\n <ShieldCheck className=\"h-4 w-4 text-primary\" />\n Conta protegida\n </div>\n <p className=\"mt-1\">Pedidos, pagamentos e endereços ficam organizados para próximas compras.</p>\n </div>\n\n <button\n type=\"button\"\n data-testid={TID.signout}\n onClick={() => void signOutCustomer()}\n className=\"mt-4 flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-muted-foreground transition hover:bg-muted hover:text-foreground\"\n >\n <LogOut className=\"h-4 w-4\" />\n Sair\n </button>\n </aside>\n\n <section className=\"min-w-0\">{children}</section>\n </div>\n </main>\n )\n}\n","import type { FinancialStatus } from '@fayz-ai/shop/types'\n\ntype Tone = 'positive' | 'pending' | 'negative'\n\nconst FINANCIAL: Record<FinancialStatus, { label: string; tone: Tone }> = {\n pending: { label: 'Pendente', tone: 'pending' },\n paid: { label: 'Pago', tone: 'positive' },\n partially_paid: { label: 'Parcialmente pago', tone: 'pending' },\n partially_refunded: { label: 'Parcialmente reembolsado', tone: 'negative' },\n refunded: { label: 'Reembolsado', tone: 'negative' },\n voided: { label: 'Cancelado', tone: 'negative' },\n}\n\n// Tinted, not pastel: a -100 pill with -800 text read as a sticker on a dark\n// store. A low-alpha fill takes the card's own surface, and the -500/-600 text\n// holds up on light and dark alike.\nconst TONE_CLASS: Record<Tone, string> = {\n positive: 'bg-emerald-500/15 text-emerald-600 ring-1 ring-inset ring-emerald-500/30',\n pending: 'bg-amber-500/15 text-amber-600 ring-1 ring-inset ring-amber-500/30',\n negative: 'bg-destructive/15 text-destructive ring-1 ring-inset ring-destructive/30',\n}\n\n/** One source of truth for a financial-status pill — label + color by status,\n * so a pending or refunded order never renders inside a green \"success\" pill. */\nexport function financialStatusBadge(status: FinancialStatus): { label: string; className: string } {\n const entry = FINANCIAL[status] ?? { label: status, tone: 'pending' as Tone }\n return { label: entry.label, className: TONE_CLASS[entry.tone] }\n}\n\nexport function isPaid(status: FinancialStatus): boolean {\n return status === 'paid' || status === 'partially_paid'\n}\n\nexport function isRefunded(status: FinancialStatus): boolean {\n return status === 'refunded' || status === 'partially_refunded'\n}\n","import React from 'react'\nimport { Bike, Check, ChefHat, Clock, PackageCheck, RotateCcw, Store, Truck, XCircle } from 'lucide-react'\nimport type { Order } from '@fayz-ai/shop/types'\nimport { isPaid, isRefunded } from '../../data/order-status'\nimport { TID } from '../../lib/testids'\n\ntype TrackingState = 'complete' | 'current' | 'pending' | 'cancelled'\n\ninterface TrackingStep {\n id: string\n label: string\n description: string\n state: TrackingState\n icon: React.ComponentType<{ className?: string }>\n}\n\n/**\n * A delivery order (shop 0125) is followed by the step the STORE moves it\n * through, not by payment and shipment: «aceito», «saiu para entrega» and\n * «entregue» are what the kitchen says and what the WhatsApp/e-mail already\n * told the buyer. Same four boxes, so the page reads the same either way.\n */\nfunction deliverySteps(order: Order): TrackingStep[] {\n const stage = order.deliveryStage!\n const pickup = order.fulfillmentType === 'pickup'\n const received: TrackingStep = {\n id: 'received', label: 'Pedido recebido', description: `#${order.orderNumber}`, state: 'complete', icon: Check,\n }\n if (stage === 'rejected' || stage === 'cancelled') {\n const refunded = isRefunded(order.financialStatus) || order.financialStatus === 'voided'\n return [\n received,\n {\n id: 'closed',\n label: stage === 'rejected' ? 'Pedido recusado' : 'Pedido cancelado',\n description: [order.rejectReason, refunded ? 'Valor estornado' : null].filter(Boolean).join(' · ')\n || 'A loja cancelou este pedido',\n state: 'cancelled',\n icon: XCircle,\n },\n ]\n }\n const rank = { new: 0, accepted: 1, out_for_delivery: 2, delivered: 3 }[stage] ?? 0\n const state = (step: number): TrackingState => (rank > step ? 'complete' : rank === step ? 'current' : 'pending')\n return [\n { ...received, state: rank === 0 ? 'current' : 'complete', description: rank === 0 ? 'Aguardando a loja aceitar' : received.description },\n { id: 'accepted', label: 'Em preparo', description: 'A loja aceitou seu pedido', state: state(1), icon: ChefHat },\n {\n id: 'out',\n label: pickup ? 'Pronto para retirada' : 'Saiu para entrega',\n description: pickup ? 'Pode vir buscar' : 'A caminho de você',\n state: state(2),\n icon: pickup ? Store : Bike,\n },\n { id: 'delivered', label: pickup ? 'Retirado' : 'Entregue', description: 'Bom apetite!', state: rank === 3 ? 'complete' : 'pending', icon: PackageCheck },\n ]\n}\n\nfunction orderTrackingSteps(order: Order): TrackingStep[] {\n if (order.deliveryStage) return deliverySteps(order)\n const received: TrackingStep = {\n id: 'received',\n label: 'Pedido recebido',\n description: `#${order.orderNumber}`,\n state: 'complete',\n icon: Check,\n }\n\n // Terminal states short-circuit the happy path.\n if (order.status === 'cancelled' || order.financialStatus === 'voided') {\n const refunded = isRefunded(order.financialStatus)\n return [\n received,\n {\n id: 'payment',\n label: 'Pedido cancelado',\n description: [order.rejectReason, refunded ? 'Valor estornado' : null].filter(Boolean).join(' · ')\n || 'Este pedido foi cancelado',\n state: 'cancelled',\n icon: XCircle,\n },\n ]\n }\n if (isRefunded(order.financialStatus)) {\n return [\n received,\n { id: 'payment', label: 'Pagamento aprovado', description: 'Compra confirmada', state: 'complete', icon: Check },\n {\n id: 'refund',\n label: order.financialStatus === 'refunded' ? 'Reembolsado' : 'Reembolso parcial',\n description: 'Valor devolvido ao cliente',\n state: 'cancelled',\n icon: RotateCcw,\n },\n ]\n }\n\n const paid = isPaid(order.financialStatus)\n const partiallyFulfilled = order.fulfillmentStatus === 'partially_fulfilled'\n const fulfilled = order.fulfillmentStatus === 'fulfilled'\n\n return [\n received,\n {\n id: 'payment',\n label: paid ? 'Pagamento aprovado' : 'Aguardando pagamento',\n // \"Processando\" descrevia um pagamento que ninguém iniciou.\n description: paid ? 'Compra confirmada' : 'Ainda não recebemos',\n state: paid ? 'complete' : 'current',\n icon: paid ? Check : Clock,\n },\n {\n id: 'preparing',\n label: 'Preparando envio',\n description: paid ? 'Separando produtos' : 'Começa após o pagamento',\n state: fulfilled || partiallyFulfilled ? 'complete' : paid ? 'current' : 'pending',\n icon: PackageCheck,\n },\n {\n id: 'delivery',\n label: fulfilled ? 'Pedido enviado' : partiallyFulfilled ? 'Envio parcial' : 'Entrega',\n description: fulfilled ? 'A caminho do cliente' : partiallyFulfilled ? 'Parte do pedido saiu' : 'Próxima etapa',\n state: fulfilled ? 'complete' : partiallyFulfilled ? 'current' : 'pending',\n icon: fulfilled || partiallyFulfilled ? Truck : Clock,\n },\n ]\n}\n\nconst GRID_COLS: Record<number, string> = {\n 2: 'sm:grid-cols-2',\n 3: 'sm:grid-cols-3',\n 4: 'sm:grid-cols-4',\n}\n\nexport { orderTrackingSteps }\n\nexport function OrderTrackingTimeline({ order, compact = false }: { order: Order; compact?: boolean }) {\n const steps = orderTrackingSteps(order)\n\n return (\n <ol\n data-testid={TID.orderTracking}\n className={`grid ${compact ? 'gap-2' : 'gap-3'} ${GRID_COLS[steps.length] ?? 'sm:grid-cols-4'}`}\n aria-label=\"Acompanhamento do pedido\"\n >\n {steps.map((step) => {\n const Icon = step.icon\n const cancelled = step.state === 'cancelled'\n const done = step.state === 'complete'\n // `current` is IN PROGRESS, not finished. Both used to render as the\n // same solid badge with a checkmark, so an unpaid order showed\n // \"Aguardando pagamento ✓\" — the tick said done about the one thing\n // that had not happened.\n const current = step.state === 'current'\n const active = done || current\n return (\n <li\n key={step.id}\n data-testid={TID.orderTrackingStep(step.id)}\n data-state={step.state}\n className={[\n 'relative min-w-0 rounded-xl border p-3',\n cancelled\n // Theme tokens, not rose-50: a light pink box on a dark store\n // put the step's own title out of sight.\n ? 'border-destructive/40 bg-destructive/10'\n : active\n ? 'border-primary/35 bg-primary/5'\n : 'bg-muted/20 text-muted-foreground',\n compact ? 'text-xs' : 'text-sm',\n ].join(' ')}\n >\n <div className=\"flex items-start gap-2\">\n <span\n className={[\n 'grid h-7 w-7 shrink-0 place-items-center rounded-full border',\n cancelled\n ? 'border-destructive bg-destructive text-destructive-foreground'\n : done\n ? 'border-primary bg-primary text-primary-foreground'\n : current\n ? 'border-primary bg-background text-primary'\n : 'border-muted-foreground/30 bg-background',\n ].join(' ')}\n aria-hidden\n >\n <Icon className=\"h-3.5 w-3.5\" />\n </span>\n <span className=\"min-w-0\">\n {/* Wrap instead of truncate: \"Aguardando pagamento\" became\n \"Aguardando pag…\", which is the one label a buyer must read\n in full. */}\n <span className=\"block font-semibold leading-snug text-foreground\">{step.label}</span>\n <span className=\"mt-0.5 block leading-snug text-muted-foreground\">{step.description}</span>\n </span>\n </div>\n </li>\n )\n })}\n </ol>\n )\n}\n"]}
|