@burdenoff/microfe-store 2026.912.3 → 2026.912.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pages/OrdersPage.js +167 -159
- package/dist/pages/OrdersPage.js.map +1 -1
- package/package.json +1 -1
package/dist/pages/OrdersPage.js
CHANGED
|
@@ -8,18 +8,18 @@ import { useOrders as o } from "../hooks/useOrders.js";
|
|
|
8
8
|
import { useCallback as s, useMemo as c, useState as l } from "react";
|
|
9
9
|
import { useNavigate as u } from "react-router";
|
|
10
10
|
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
-
import { ArrowLeft as f, CheckCircle as p, ChevronRight as m, Clock as h, Download as
|
|
12
|
-
import { jsx as
|
|
13
|
-
import { GlassCard as
|
|
11
|
+
import { ArrowLeft as f, CheckCircle as p, ChevronRight as m, Clock as h, Download as ee, Loader2 as g, Package as _, RefreshCw as v, ShoppingBag as y, XCircle as b } from "lucide-react";
|
|
12
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
13
|
+
import { GlassCard as C, IllustratedEmptyState as w, PagePurpose as te } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
//#region src/pages/OrdersPage.tsx
|
|
15
|
-
function
|
|
15
|
+
function T(e) {
|
|
16
16
|
return typeof e == "number" && Number.isFinite(e) ? e : null;
|
|
17
17
|
}
|
|
18
|
-
var
|
|
18
|
+
var E = [
|
|
19
19
|
"COMPLETED",
|
|
20
20
|
"FULFILLED",
|
|
21
21
|
"PROCESSED"
|
|
22
|
-
],
|
|
22
|
+
], D = (e, t) => {
|
|
23
23
|
switch (e) {
|
|
24
24
|
case "COMPLETED":
|
|
25
25
|
case "FULFILLED":
|
|
@@ -39,17 +39,17 @@ var D = [
|
|
|
39
39
|
label: t("pages.orders.statusPending", "Pending")
|
|
40
40
|
};
|
|
41
41
|
case "FAILED": return {
|
|
42
|
-
icon:
|
|
42
|
+
icon: b,
|
|
43
43
|
classes: "bg-status-error-bg-subtle/15 text-status-error-text border-status-error-text/20",
|
|
44
44
|
label: t("pages.orders.statusFailed", "Failed")
|
|
45
45
|
};
|
|
46
46
|
case "REFUNDED": return {
|
|
47
|
-
icon:
|
|
47
|
+
icon: v,
|
|
48
48
|
classes: "bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20",
|
|
49
49
|
label: t("pages.orders.statusRefunded", "Refunded")
|
|
50
50
|
};
|
|
51
51
|
case "CANCELLED": return {
|
|
52
|
-
icon:
|
|
52
|
+
icon: v,
|
|
53
53
|
classes: "bg-bg-sunken text-text-muted border-border-default",
|
|
54
54
|
label: t("pages.orders.statusCancelled", "Cancelled")
|
|
55
55
|
};
|
|
@@ -59,24 +59,24 @@ var D = [
|
|
|
59
59
|
label: e
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
},
|
|
63
|
-
let n =
|
|
64
|
-
return /* @__PURE__ */
|
|
62
|
+
}, O = ({ status: e, tr: t }) => {
|
|
63
|
+
let n = D(e, t), r = n.icon;
|
|
64
|
+
return /* @__PURE__ */ S("span", {
|
|
65
65
|
className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-semibold ${n.classes}`,
|
|
66
|
-
children: [/* @__PURE__ */
|
|
66
|
+
children: [/* @__PURE__ */ x(r, { className: "size-3.5" }), n.label]
|
|
67
67
|
});
|
|
68
68
|
}, k = () => {
|
|
69
|
-
let h = u(), { basePath:
|
|
69
|
+
let h = u(), { basePath: v } = e(), { t: D } = d(), k = r(), { orders: ne, loading: A, error: j, refetchOrders: M, hasMore: N, loadMore: P } = o(), { installations: F, refetch: I } = a();
|
|
70
70
|
t({
|
|
71
71
|
onData: () => {
|
|
72
|
-
|
|
72
|
+
M();
|
|
73
73
|
},
|
|
74
74
|
onError: (e) => {
|
|
75
75
|
console.warn("OrderStatusUpdated subscription error:", e.message);
|
|
76
76
|
}
|
|
77
77
|
});
|
|
78
|
-
let [re, ie] = l(/* @__PURE__ */ new Set()), [L, R] = l("completed"), [ae, z] = l(!1), [B, V] = l(null), [H, U] = l(null), [oe, W] = l(null), G = c(() => new Set(
|
|
79
|
-
let t = e.lineItems || [], n = t.reduce((e, t) => e + t.subtotal, 0), r = e.transaction?.metadata, i =
|
|
78
|
+
let [re, ie] = l(/* @__PURE__ */ new Set()), [L, R] = l("completed"), [ae, z] = l(!1), [B, V] = l(null), [H, U] = l(null), [oe, W] = l(null), G = c(() => new Set(F.flatMap((e) => e.status === "ACTIVE" ? [e.productId] : [])), [F]), K = ne.map((e) => {
|
|
79
|
+
let t = e.lineItems || [], n = t.reduce((e, t) => e + t.subtotal, 0), r = e.transaction?.metadata, i = T(r?.subtotal), a = T(r?.taxAmount), o = T(r?.total), s = i != null && o != null && e.transaction?.currency != null, c = s ? e.transaction.currency : e.currency, l = s ? i : n, u = s ? a ?? 0 : 0, d = s ? o : e.total, f = r?.paymentMethod === "CREDITS" ? T(r?.creditsUsed) : null, p = t.map((t, n) => ({
|
|
80
80
|
id: `${e.id}-${n}`,
|
|
81
81
|
productId: t.productId,
|
|
82
82
|
productName: t.name,
|
|
@@ -91,18 +91,19 @@ var D = [
|
|
|
91
91
|
return {
|
|
92
92
|
id: e.id,
|
|
93
93
|
orderNumber: `#${e.id.slice(-8).toUpperCase()}`,
|
|
94
|
-
items:
|
|
94
|
+
items: p,
|
|
95
95
|
catalogCurrency: e.currency,
|
|
96
96
|
currency: c,
|
|
97
97
|
subtotal: l,
|
|
98
98
|
tax: u,
|
|
99
99
|
total: d,
|
|
100
100
|
isActualCharge: s,
|
|
101
|
+
creditsUsed: f,
|
|
101
102
|
status: e.status,
|
|
102
103
|
createdAt: e.createdAt,
|
|
103
104
|
fulfillment: e.fulfillment ?? null
|
|
104
105
|
};
|
|
105
|
-
}), q = c(() => K.filter((e) =>
|
|
106
|
+
}), q = c(() => K.filter((e) => E.includes(e.status)), [K]), J = c(() => K.filter((e) => !E.includes(e.status)), [K]), Y = L === "completed" ? q : J, se = s((e) => {
|
|
106
107
|
ie((t) => {
|
|
107
108
|
let n = new Set(t);
|
|
108
109
|
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
@@ -118,36 +119,36 @@ var D = [
|
|
|
118
119
|
}, X = () => {
|
|
119
120
|
z(!1), V(null), U(null), W(null);
|
|
120
121
|
}, le = (e, t) => {
|
|
121
|
-
X(),
|
|
122
|
+
X(), I();
|
|
122
123
|
}, ue = (e) => {}, Z = () => {
|
|
123
|
-
h(`${
|
|
124
|
+
h(`${v}/marketplace`);
|
|
124
125
|
}, Q = "px-4 py-3 text-sm", $ = "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-text-muted";
|
|
125
|
-
return /* @__PURE__ */
|
|
126
|
+
return /* @__PURE__ */ S("div", {
|
|
126
127
|
className: "flex h-full flex-col",
|
|
127
128
|
children: [
|
|
128
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ x("header", {
|
|
129
130
|
className: "flex-shrink-0 border-b border-border-seam bg-bg-surface px-6 py-4",
|
|
130
|
-
children: /* @__PURE__ */
|
|
131
|
+
children: /* @__PURE__ */ x("div", {
|
|
131
132
|
className: "mx-auto max-w-6xl",
|
|
132
|
-
children: /* @__PURE__ */
|
|
133
|
+
children: /* @__PURE__ */ x("div", {
|
|
133
134
|
className: "flex items-center justify-between",
|
|
134
|
-
children: /* @__PURE__ */
|
|
135
|
+
children: /* @__PURE__ */ S("div", {
|
|
135
136
|
className: "flex items-center gap-4",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
137
|
+
children: [/* @__PURE__ */ x("button", {
|
|
137
138
|
type: "button",
|
|
138
139
|
onClick: () => h(-1),
|
|
139
140
|
"aria-label": "Go back",
|
|
140
141
|
className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
141
|
-
children: /* @__PURE__ */
|
|
142
|
-
}), /* @__PURE__ */
|
|
142
|
+
children: /* @__PURE__ */ x(f, { className: "size-5" })
|
|
143
|
+
}), /* @__PURE__ */ S("div", {
|
|
143
144
|
className: "flex items-center gap-3",
|
|
144
145
|
children: [
|
|
145
|
-
/* @__PURE__ */
|
|
146
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ x(_, { className: "size-6 text-text-primary" }),
|
|
147
|
+
/* @__PURE__ */ x("h1", {
|
|
147
148
|
className: "text-xl font-bold text-text-primary",
|
|
148
|
-
children:
|
|
149
|
+
children: D("pages.orders.title", { defaultValue: "My Orders" })
|
|
149
150
|
}),
|
|
150
|
-
K.length > 0 && /* @__PURE__ */
|
|
151
|
+
K.length > 0 && /* @__PURE__ */ x("span", {
|
|
151
152
|
className: "rounded-full bg-action-primary-bg/10 px-2.5 py-0.5 text-sm font-medium text-action-primary-bg",
|
|
152
153
|
children: K.length
|
|
153
154
|
})
|
|
@@ -157,115 +158,115 @@ var D = [
|
|
|
157
158
|
})
|
|
158
159
|
})
|
|
159
160
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ x("div", {
|
|
161
162
|
className: "flex-1 overflow-y-auto",
|
|
162
|
-
children: /* @__PURE__ */
|
|
163
|
+
children: /* @__PURE__ */ S("div", {
|
|
163
164
|
className: "mx-auto max-w-6xl p-6",
|
|
164
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ x(te, {
|
|
165
166
|
className: "mb-6",
|
|
166
167
|
children: k("pages.orders.purpose", "A record of everything you've purchased from the marketplace. Track each order's status, review what you paid, and — once an order is complete — install the apps you bought into a workspace right from here. Come back to re-install on a new workspace or check a past receipt.")
|
|
167
|
-
}),
|
|
168
|
+
}), A && K.length === 0 ? /* @__PURE__ */ S("div", {
|
|
168
169
|
className: "flex flex-col items-center justify-center py-24",
|
|
169
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ x(g, {
|
|
170
171
|
className: "size-12 animate-spin text-text-muted mb-4",
|
|
171
172
|
"aria-hidden": "true"
|
|
172
|
-
}), /* @__PURE__ */
|
|
173
|
+
}), /* @__PURE__ */ x("p", {
|
|
173
174
|
className: "text-text-muted",
|
|
174
175
|
children: k("pages.orders.loading", "Loading your orders…")
|
|
175
176
|
})]
|
|
176
|
-
}) :
|
|
177
|
+
}) : j ? /* @__PURE__ */ S("div", {
|
|
177
178
|
className: "flex flex-col items-center justify-center py-24 text-center",
|
|
178
179
|
children: [
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ x("div", {
|
|
180
181
|
className: "mb-6 rounded-full bg-status-error-bg-subtle/10 p-6",
|
|
181
|
-
children: /* @__PURE__ */
|
|
182
|
+
children: /* @__PURE__ */ x(b, { className: "size-16 text-status-error-text" })
|
|
182
183
|
}),
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ x("h2", {
|
|
184
185
|
className: "mb-2 text-2xl font-bold text-text-primary",
|
|
185
|
-
children:
|
|
186
|
+
children: D("pages.orders.failedToLoad", { defaultValue: "Failed to load orders" })
|
|
186
187
|
}),
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ x("p", {
|
|
188
189
|
className: "mb-6 max-w-md text-text-muted",
|
|
189
|
-
children:
|
|
190
|
+
children: j.message || k("pages.orders.errorDescription", "An error occurred while loading your orders. Please try again.")
|
|
190
191
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ S("button", {
|
|
192
193
|
type: "button",
|
|
193
194
|
onClick: Z,
|
|
194
195
|
className: "cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ x(y, { className: "size-5" }), k("pages.orders.browseMarketplace", "Browse Marketplace")]
|
|
196
197
|
})
|
|
197
198
|
]
|
|
198
|
-
}) : K.length === 0 ? /* @__PURE__ */
|
|
199
|
+
}) : K.length === 0 ? /* @__PURE__ */ x(w, {
|
|
199
200
|
illustration: "empty-data",
|
|
200
|
-
title:
|
|
201
|
+
title: D("pages.orders.emptyTitle", { defaultValue: "No orders yet" }),
|
|
201
202
|
description: k("pages.orders.emptyDescription", "You have not placed any orders yet. Browse the marketplace to discover apps and products!"),
|
|
202
|
-
action: /* @__PURE__ */
|
|
203
|
+
action: /* @__PURE__ */ S("button", {
|
|
203
204
|
type: "button",
|
|
204
205
|
onClick: Z,
|
|
205
206
|
className: "cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
207
|
+
children: [/* @__PURE__ */ x(y, { className: "size-5" }), k("pages.orders.browseMarketplace", "Browse Marketplace")]
|
|
207
208
|
})
|
|
208
|
-
}) : /* @__PURE__ */
|
|
209
|
+
}) : /* @__PURE__ */ S("div", {
|
|
209
210
|
className: "space-y-4",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
211
|
+
children: [/* @__PURE__ */ S("div", {
|
|
211
212
|
className: "inline-flex rounded-xl border border-border-seam bg-bg-surface p-1",
|
|
212
|
-
children: [/* @__PURE__ */
|
|
213
|
+
children: [/* @__PURE__ */ S("button", {
|
|
213
214
|
type: "button",
|
|
214
215
|
onClick: () => R("completed"),
|
|
215
216
|
className: `rounded-lg px-4 py-2 text-sm font-medium transition-colors ${L === "completed" ? "bg-action-primary-bg text-action-primary-text" : "text-text-muted hover:bg-bg-sunken hover:text-text-primary"}`,
|
|
216
|
-
children: [k("pages.orders.tabCompleted", "Completed"), /* @__PURE__ */
|
|
217
|
+
children: [k("pages.orders.tabCompleted", "Completed"), /* @__PURE__ */ x("span", {
|
|
217
218
|
className: "ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs",
|
|
218
219
|
children: q.length
|
|
219
220
|
})]
|
|
220
|
-
}), /* @__PURE__ */
|
|
221
|
+
}), /* @__PURE__ */ S("button", {
|
|
221
222
|
type: "button",
|
|
222
223
|
onClick: () => R("pending"),
|
|
223
224
|
className: `rounded-lg px-4 py-2 text-sm font-medium transition-colors ${L === "pending" ? "bg-action-primary-bg text-action-primary-text" : "text-text-muted hover:bg-bg-sunken hover:text-text-primary"}`,
|
|
224
|
-
children: [k("pages.orders.tabPending", "Pending"), /* @__PURE__ */
|
|
225
|
+
children: [k("pages.orders.tabPending", "Pending"), /* @__PURE__ */ x("span", {
|
|
225
226
|
className: "ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs",
|
|
226
227
|
children: J.length
|
|
227
228
|
})]
|
|
228
229
|
})]
|
|
229
|
-
}), Y.length === 0 ? /* @__PURE__ */ C
|
|
230
|
+
}), Y.length === 0 ? /* @__PURE__ */ S(C, {
|
|
230
231
|
className: "p-10 text-center",
|
|
231
232
|
children: [
|
|
232
|
-
/* @__PURE__ */
|
|
233
|
+
/* @__PURE__ */ x("div", {
|
|
233
234
|
className: "mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-bg-sunken text-text-muted",
|
|
234
|
-
children: /* @__PURE__ */
|
|
235
|
+
children: /* @__PURE__ */ x(_, { className: "size-6" })
|
|
235
236
|
}),
|
|
236
|
-
/* @__PURE__ */
|
|
237
|
+
/* @__PURE__ */ x("h2", {
|
|
237
238
|
className: "mb-2 text-lg font-semibold text-text-primary",
|
|
238
239
|
children: L === "completed" ? k("pages.orders.noCompletedTitle", "No completed orders yet") : k("pages.orders.noPendingTitle", "No pending orders")
|
|
239
240
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
+
/* @__PURE__ */ x("p", {
|
|
241
242
|
className: "text-sm text-text-muted",
|
|
242
243
|
children: L === "completed" ? k("pages.orders.noCompletedDescription", "Completed purchases will appear here once payment is successful.") : k("pages.orders.noPendingDescription", "Orders that are pending, processing, failed, refunded, or cancelled will appear here.")
|
|
243
244
|
})
|
|
244
245
|
]
|
|
245
|
-
}) : /* @__PURE__ */
|
|
246
|
+
}) : /* @__PURE__ */ S("div", {
|
|
246
247
|
className: "overflow-hidden rounded-xl border border-border-seam bg-bg-surface",
|
|
247
248
|
children: [
|
|
248
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ S("div", {
|
|
249
250
|
className: "hidden border-b border-border-seam bg-bg-sunken/50 md:grid md:grid-cols-[40px_130px_140px_1fr_120px_140px]",
|
|
250
251
|
children: [
|
|
251
|
-
/* @__PURE__ */
|
|
252
|
-
/* @__PURE__ */
|
|
252
|
+
/* @__PURE__ */ x("div", { className: $ }),
|
|
253
|
+
/* @__PURE__ */ x("div", {
|
|
253
254
|
className: $,
|
|
254
255
|
children: k("pages.orders.colOrder", "Order")
|
|
255
256
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ x("div", {
|
|
257
258
|
className: $,
|
|
258
259
|
children: k("pages.orders.colDate", "Date")
|
|
259
260
|
}),
|
|
260
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ x("div", {
|
|
261
262
|
className: $,
|
|
262
263
|
children: k("pages.orders.colItems", "Items")
|
|
263
264
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ x("div", {
|
|
265
266
|
className: `${$} text-right`,
|
|
266
267
|
children: k("pages.orders.colTotal", "Total")
|
|
267
268
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ x("div", {
|
|
269
270
|
className: `${$} text-center`,
|
|
270
271
|
children: k("pages.orders.colStatus", "Status")
|
|
271
272
|
})
|
|
@@ -273,22 +274,22 @@ var D = [
|
|
|
273
274
|
}),
|
|
274
275
|
Y.map((e) => {
|
|
275
276
|
let t = re.has(e.id), r = e.status === "COMPLETED" || e.status === "FULFILLED" || e.status === "PROCESSED";
|
|
276
|
-
return /* @__PURE__ */
|
|
277
|
+
return /* @__PURE__ */ S("div", {
|
|
277
278
|
className: "border-b border-border-seam last:border-b-0",
|
|
278
|
-
children: [/* @__PURE__ */
|
|
279
|
+
children: [/* @__PURE__ */ S("button", {
|
|
279
280
|
type: "button",
|
|
280
281
|
onClick: () => se(e.id),
|
|
281
282
|
className: "grid w-full cursor-pointer grid-cols-[40px_130px_140px_1fr_120px_140px] items-center transition-colors hover:bg-bg-sunken/50 md:grid",
|
|
282
283
|
children: [
|
|
283
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ x("div", {
|
|
284
285
|
className: `${Q} flex justify-center`,
|
|
285
|
-
children: /* @__PURE__ */
|
|
286
|
+
children: /* @__PURE__ */ x(m, { className: `size-4 text-text-muted transition-transform duration-200 ${t ? "rotate-90" : ""}` })
|
|
286
287
|
}),
|
|
287
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ x("div", {
|
|
288
289
|
className: `${Q} font-medium text-text-primary`,
|
|
289
290
|
children: e.orderNumber
|
|
290
291
|
}),
|
|
291
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ x("div", {
|
|
292
293
|
className: `${Q} text-text-muted`,
|
|
293
294
|
children: new Date(e.createdAt).toLocaleDateString("en-US", {
|
|
294
295
|
year: "numeric",
|
|
@@ -296,61 +297,61 @@ var D = [
|
|
|
296
297
|
day: "numeric"
|
|
297
298
|
})
|
|
298
299
|
}),
|
|
299
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ S("div", {
|
|
300
301
|
className: `${Q} text-text-muted`,
|
|
301
302
|
children: [
|
|
302
303
|
e.items.length,
|
|
303
304
|
" ",
|
|
304
305
|
e.items.length === 1 ? k("pages.orders.item", "item") : k("pages.orders.items", "items"),
|
|
305
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ x("span", {
|
|
306
307
|
className: "block text-xs text-text-muted/70",
|
|
307
308
|
children: e.items.map((e) => e.productName).join(", ")
|
|
308
309
|
})
|
|
309
310
|
]
|
|
310
311
|
}),
|
|
311
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ x("div", {
|
|
312
313
|
className: `${Q} text-right font-semibold tabular-nums text-text-primary`,
|
|
313
314
|
children: n(e.total, e.currency)
|
|
314
315
|
}),
|
|
315
|
-
/* @__PURE__ */
|
|
316
|
+
/* @__PURE__ */ x("div", {
|
|
316
317
|
className: `${Q} flex justify-center`,
|
|
317
|
-
children: /* @__PURE__ */
|
|
318
|
+
children: /* @__PURE__ */ x(O, {
|
|
318
319
|
status: e.status,
|
|
319
320
|
tr: k
|
|
320
321
|
})
|
|
321
322
|
})
|
|
322
323
|
]
|
|
323
|
-
}), t && /* @__PURE__ */
|
|
324
|
+
}), t && /* @__PURE__ */ S("div", {
|
|
324
325
|
className: "border-t border-border-seam bg-bg-sunken/30",
|
|
325
326
|
children: [
|
|
326
|
-
e.fulfillment && /* @__PURE__ */
|
|
327
|
+
e.fulfillment && /* @__PURE__ */ S("div", {
|
|
327
328
|
className: "mx-4 mt-4 rounded-xl border border-border-seam bg-bg-surface px-4 py-4 md:mx-12",
|
|
328
|
-
children: [/* @__PURE__ */
|
|
329
|
+
children: [/* @__PURE__ */ S("div", {
|
|
329
330
|
className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between",
|
|
330
|
-
children: [/* @__PURE__ */
|
|
331
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("div", {
|
|
331
332
|
className: "text-sm font-semibold text-text-primary",
|
|
332
333
|
children: k("pages.orders.orderTracking", "Order tracking")
|
|
333
|
-
}), /* @__PURE__ */
|
|
334
|
+
}), /* @__PURE__ */ S("div", {
|
|
334
335
|
className: "mt-1 text-xs text-text-muted",
|
|
335
336
|
children: [
|
|
336
337
|
e.fulfillment.status || k("pages.orders.statusPending", "Pending"),
|
|
337
338
|
e.fulfillment.carrier ? ` · ${e.fulfillment.carrier}` : "",
|
|
338
339
|
e.fulfillment.estimatedDeliveryDate ? ` · ${k("pages.orders.estimatedDelivery", "Est. delivery {{date}}", { date: new Date(e.fulfillment.estimatedDeliveryDate).toLocaleDateString() })}` : ""
|
|
339
340
|
]
|
|
340
|
-
})] }), /* @__PURE__ */
|
|
341
|
+
})] }), /* @__PURE__ */ S("div", {
|
|
341
342
|
className: "flex flex-col items-start gap-2 md:items-end",
|
|
342
|
-
children: [e.fulfillment.trackingNumber && /* @__PURE__ */
|
|
343
|
+
children: [e.fulfillment.trackingNumber && /* @__PURE__ */ x("div", {
|
|
343
344
|
className: "text-xs text-text-muted",
|
|
344
345
|
children: k("pages.orders.trackingNumber", "Tracking #: {{number}}", { number: e.fulfillment.trackingNumber })
|
|
345
|
-
}), e.fulfillment.trackingUrl && /* @__PURE__ */
|
|
346
|
+
}), e.fulfillment.trackingUrl && /* @__PURE__ */ S("a", {
|
|
346
347
|
href: e.fulfillment.trackingUrl,
|
|
347
348
|
target: "_blank",
|
|
348
349
|
rel: "noreferrer",
|
|
349
350
|
className: "inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken",
|
|
350
|
-
children: [k("pages.orders.trackPackage", "Track package"), /* @__PURE__ */
|
|
351
|
+
children: [k("pages.orders.trackPackage", "Track package"), /* @__PURE__ */ x(m, { className: "size-3.5" })]
|
|
351
352
|
})]
|
|
352
353
|
})]
|
|
353
|
-
}), /* @__PURE__ */
|
|
354
|
+
}), /* @__PURE__ */ x("div", {
|
|
354
355
|
className: "mt-4 grid gap-2 sm:grid-cols-4",
|
|
355
356
|
children: [
|
|
356
357
|
{
|
|
@@ -371,7 +372,7 @@ var D = [
|
|
|
371
372
|
}
|
|
372
373
|
].map((t) => {
|
|
373
374
|
let n = e.fulfillment?.status || "PENDING";
|
|
374
|
-
return /* @__PURE__ */
|
|
375
|
+
return /* @__PURE__ */ x("div", {
|
|
375
376
|
className: `rounded-lg border px-3 py-2 text-xs font-medium ${[
|
|
376
377
|
"PROCESSING",
|
|
377
378
|
"READY_TO_SHIP",
|
|
@@ -390,22 +391,22 @@ var D = [
|
|
|
390
391
|
})
|
|
391
392
|
})]
|
|
392
393
|
}),
|
|
393
|
-
/* @__PURE__ */
|
|
394
|
+
/* @__PURE__ */ S("div", {
|
|
394
395
|
className: "hidden grid-cols-[1fr_100px_120px_160px] gap-4 px-12 py-2 md:grid",
|
|
395
396
|
children: [
|
|
396
|
-
/* @__PURE__ */
|
|
397
|
+
/* @__PURE__ */ x("span", {
|
|
397
398
|
className: "text-xs font-medium text-text-muted",
|
|
398
399
|
children: k("pages.orders.colProduct", "Product")
|
|
399
400
|
}),
|
|
400
|
-
/* @__PURE__ */
|
|
401
|
+
/* @__PURE__ */ x("span", {
|
|
401
402
|
className: "text-xs font-medium text-text-muted text-right",
|
|
402
403
|
children: k("pages.orders.colQty", "Qty")
|
|
403
404
|
}),
|
|
404
|
-
/* @__PURE__ */
|
|
405
|
+
/* @__PURE__ */ x("span", {
|
|
405
406
|
className: "text-xs font-medium text-text-muted text-right",
|
|
406
407
|
children: k("pages.orders.colPrice", "Price")
|
|
407
408
|
}),
|
|
408
|
-
/* @__PURE__ */
|
|
409
|
+
/* @__PURE__ */ x("span", {
|
|
409
410
|
className: "text-xs font-medium text-text-muted text-center",
|
|
410
411
|
children: k("pages.orders.colAction", "Action")
|
|
411
412
|
})
|
|
@@ -413,43 +414,43 @@ var D = [
|
|
|
413
414
|
}),
|
|
414
415
|
e.items.map((t) => {
|
|
415
416
|
let i = t.productType !== "PHYSICAL" && t.productType !== "MERCHANDISE";
|
|
416
|
-
return /* @__PURE__ */
|
|
417
|
+
return /* @__PURE__ */ S("div", {
|
|
417
418
|
className: "grid grid-cols-[auto_1fr_auto] items-center gap-3 px-4 py-3 md:px-12 md:grid-cols-[1fr_100px_120px_160px]",
|
|
418
419
|
children: [
|
|
419
|
-
/* @__PURE__ */
|
|
420
|
+
/* @__PURE__ */ S("div", {
|
|
420
421
|
className: "flex items-center gap-3 min-w-0",
|
|
421
|
-
children: [/* @__PURE__ */
|
|
422
|
+
children: [/* @__PURE__ */ x("div", {
|
|
422
423
|
className: "size-10 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
|
|
423
|
-
children: t.productIcon ? /* @__PURE__ */
|
|
424
|
+
children: t.productIcon ? /* @__PURE__ */ x("img", {
|
|
424
425
|
src: t.productIcon,
|
|
425
426
|
alt: t.productName,
|
|
426
427
|
className: "size-full object-cover"
|
|
427
|
-
}) : /* @__PURE__ */
|
|
428
|
+
}) : /* @__PURE__ */ x("div", {
|
|
428
429
|
className: "flex size-full items-center justify-center text-sm font-bold text-text-muted",
|
|
429
430
|
children: t.productName.charAt(0)
|
|
430
431
|
})
|
|
431
|
-
}), /* @__PURE__ */
|
|
432
|
+
}), /* @__PURE__ */ S("div", {
|
|
432
433
|
className: "min-w-0",
|
|
433
|
-
children: [/* @__PURE__ */
|
|
434
|
+
children: [/* @__PURE__ */ x("p", {
|
|
434
435
|
className: "truncate text-sm font-medium text-text-primary",
|
|
435
436
|
children: t.productName
|
|
436
|
-
}), /* @__PURE__ */
|
|
437
|
+
}), /* @__PURE__ */ x("span", {
|
|
437
438
|
className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted",
|
|
438
439
|
children: t.productType
|
|
439
440
|
})]
|
|
440
441
|
})]
|
|
441
442
|
}),
|
|
442
|
-
/* @__PURE__ */
|
|
443
|
+
/* @__PURE__ */ x("div", {
|
|
443
444
|
className: "hidden text-right text-sm text-text-muted md:block",
|
|
444
445
|
children: t.quantity
|
|
445
446
|
}),
|
|
446
|
-
/* @__PURE__ */
|
|
447
|
+
/* @__PURE__ */ x("div", {
|
|
447
448
|
className: "hidden text-right text-sm font-semibold tabular-nums text-text-primary md:block",
|
|
448
449
|
children: n(t.totalPrice, e.catalogCurrency)
|
|
449
450
|
}),
|
|
450
|
-
/* @__PURE__ */
|
|
451
|
+
/* @__PURE__ */ x("div", {
|
|
451
452
|
className: "text-right text-sm text-text-muted md:hidden",
|
|
452
|
-
children: /* @__PURE__ */
|
|
453
|
+
children: /* @__PURE__ */ x("span", {
|
|
453
454
|
className: "tabular-nums",
|
|
454
455
|
children: k("pages.orders.qtyTimesPrice", "{{qty}} × {{price}}", {
|
|
455
456
|
qty: t.quantity,
|
|
@@ -457,80 +458,87 @@ var D = [
|
|
|
457
458
|
})
|
|
458
459
|
})
|
|
459
460
|
}),
|
|
460
|
-
/* @__PURE__ */
|
|
461
|
+
/* @__PURE__ */ x("div", {
|
|
461
462
|
className: "flex justify-end md:justify-center",
|
|
462
|
-
children: t.installed ? /* @__PURE__ */
|
|
463
|
+
children: t.installed ? /* @__PURE__ */ S("span", {
|
|
463
464
|
className: "inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs font-medium text-status-success-text",
|
|
464
|
-
children: [/* @__PURE__ */
|
|
465
|
-
}) : r && i ? /* @__PURE__ */
|
|
465
|
+
children: [/* @__PURE__ */ x(p, { className: "size-3.5" }), k("pages.orders.installed", "Installed")]
|
|
466
|
+
}) : r && i ? /* @__PURE__ */ S("button", {
|
|
466
467
|
type: "button",
|
|
467
468
|
onClick: (n) => {
|
|
468
469
|
n.stopPropagation(), ce(e.id, t);
|
|
469
470
|
},
|
|
470
471
|
className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-all hover:opacity-90 active:scale-95",
|
|
471
|
-
children: [/* @__PURE__ */
|
|
472
|
+
children: [/* @__PURE__ */ x(ee, { className: "size-3.5" }), k("pages.orders.installApp", "Install App")]
|
|
472
473
|
}) : null
|
|
473
474
|
})
|
|
474
475
|
]
|
|
475
476
|
}, t.id);
|
|
476
477
|
}),
|
|
477
|
-
/* @__PURE__ */
|
|
478
|
+
/* @__PURE__ */ S("div", {
|
|
478
479
|
className: "border-t border-border-seam/50 px-4 py-2 md:px-12",
|
|
479
|
-
children: [
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
className: "tabular-nums text-text-primary",
|
|
493
|
-
children: n(e.subtotal, e.currency)
|
|
494
|
-
})
|
|
495
|
-
] }),
|
|
496
|
-
/* @__PURE__ */ C("span", { children: [
|
|
497
|
-
k("pages.orders.tax", "Tax:"),
|
|
498
|
-
" ",
|
|
499
|
-
/* @__PURE__ */ S("span", {
|
|
500
|
-
className: "tabular-nums text-text-primary",
|
|
501
|
-
children: n(e.tax, e.currency)
|
|
502
|
-
})
|
|
503
|
-
] }),
|
|
504
|
-
/* @__PURE__ */ C("span", {
|
|
505
|
-
className: "font-semibold",
|
|
506
|
-
children: [
|
|
507
|
-
k("pages.orders.total", "Total:"),
|
|
480
|
+
children: [
|
|
481
|
+
e.isActualCharge && e.currency !== e.catalogCurrency && /* @__PURE__ */ x("p", {
|
|
482
|
+
className: "mb-1 text-right text-xs text-text-muted",
|
|
483
|
+
children: k("pages.orders.paidInCurrency", "Listed in {{catalogCurrency}}, actually charged in {{currency}}", {
|
|
484
|
+
catalogCurrency: e.catalogCurrency,
|
|
485
|
+
currency: e.currency
|
|
486
|
+
})
|
|
487
|
+
}),
|
|
488
|
+
/* @__PURE__ */ S("div", {
|
|
489
|
+
className: "flex justify-end gap-6 text-xs text-text-muted",
|
|
490
|
+
children: [
|
|
491
|
+
/* @__PURE__ */ S("span", { children: [
|
|
492
|
+
k("pages.orders.subtotal", "Subtotal:"),
|
|
508
493
|
" ",
|
|
509
|
-
/* @__PURE__ */
|
|
494
|
+
/* @__PURE__ */ x("span", {
|
|
510
495
|
className: "tabular-nums text-text-primary",
|
|
511
|
-
children: n(e.
|
|
496
|
+
children: n(e.subtotal, e.currency)
|
|
512
497
|
})
|
|
513
|
-
]
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ S("span", { children: [
|
|
500
|
+
k("pages.orders.tax", "Tax:"),
|
|
501
|
+
" ",
|
|
502
|
+
/* @__PURE__ */ x("span", {
|
|
503
|
+
className: "tabular-nums text-text-primary",
|
|
504
|
+
children: n(e.tax, e.currency)
|
|
505
|
+
})
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ S("span", {
|
|
508
|
+
className: "font-semibold",
|
|
509
|
+
children: [
|
|
510
|
+
k("pages.orders.total", "Total:"),
|
|
511
|
+
" ",
|
|
512
|
+
/* @__PURE__ */ x("span", {
|
|
513
|
+
className: "tabular-nums text-text-primary",
|
|
514
|
+
children: n(e.total, e.currency)
|
|
515
|
+
})
|
|
516
|
+
]
|
|
517
|
+
})
|
|
518
|
+
]
|
|
519
|
+
}),
|
|
520
|
+
e.creditsUsed != null && /* @__PURE__ */ x("p", {
|
|
521
|
+
className: "mt-1 text-right text-xs text-text-muted",
|
|
522
|
+
children: k("pages.orders.paidWithCredits", "Paid with {{credits}} credits", { credits: e.creditsUsed.toLocaleString() })
|
|
523
|
+
})
|
|
524
|
+
]
|
|
517
525
|
})
|
|
518
526
|
]
|
|
519
527
|
})]
|
|
520
528
|
}, e.id);
|
|
521
529
|
}),
|
|
522
|
-
|
|
530
|
+
N && /* @__PURE__ */ x("div", {
|
|
523
531
|
className: "flex justify-center px-4 py-5",
|
|
524
|
-
children: /* @__PURE__ */
|
|
532
|
+
children: /* @__PURE__ */ S("button", {
|
|
525
533
|
type: "button",
|
|
526
|
-
onClick:
|
|
527
|
-
disabled:
|
|
534
|
+
onClick: P,
|
|
535
|
+
disabled: A,
|
|
528
536
|
className: "flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-subtle disabled:opacity-50",
|
|
529
|
-
"aria-label":
|
|
530
|
-
children: [
|
|
537
|
+
"aria-label": D("common.loadMore", { defaultValue: "Load more" }),
|
|
538
|
+
children: [A ? /* @__PURE__ */ x(g, {
|
|
531
539
|
className: "size-4 animate-spin",
|
|
532
540
|
"aria-hidden": "true"
|
|
533
|
-
}) : null,
|
|
541
|
+
}) : null, D("common.loadMore", { defaultValue: "Load more" })]
|
|
534
542
|
})
|
|
535
543
|
})
|
|
536
544
|
]
|
|
@@ -538,7 +546,7 @@ var D = [
|
|
|
538
546
|
})]
|
|
539
547
|
})
|
|
540
548
|
}),
|
|
541
|
-
H && /* @__PURE__ */
|
|
549
|
+
H && /* @__PURE__ */ x(i, {
|
|
542
550
|
isOpen: ae,
|
|
543
551
|
onClose: X,
|
|
544
552
|
app: H,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useCallback } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\n ChevronRight,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { useOrders } from '../hooks/useOrders';\nimport { formatPrice } from '../utils';\nimport { InstallAppModal, type AppInfo } from '../components/InstallAppModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTr } from '../shared/hooks/useTr';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrderStatusUpdatedSubscription } from '../generated/global-operations';\n\n// Local types for UI\ninterface OrderItem {\n id: string;\n productId: string;\n productName: string;\n productIcon?: string;\n productType: string;\n version?: string;\n quantity: number;\n unitPrice: number;\n totalPrice: number;\n installed?: boolean;\n}\n\ninterface UIOrder {\n id: string;\n orderNumber: string;\n items: OrderItem[];\n /** The order's own catalog-native currency — what item unitPrice/totalPrice\n * below are denominated in (the listed price), regardless of what was\n * actually charged. */\n catalogCurrency: string;\n /** Currency of subtotal/tax/total below — the actually-charged currency\n * when a payment transaction exists, otherwise catalogCurrency (e.g. an\n * unpaid order, or one paid entirely with credits). */\n currency: string;\n subtotal: number;\n tax: number;\n total: number;\n /** True when subtotal/tax/total reflect the real gateway charge\n * (transaction.metadata) rather than the order's catalog-native amounts. */\n isActualCharge: boolean;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n fulfillment?: {\n status?: string | null;\n trackingNumber?: string | null;\n trackingUrl?: string | null;\n carrier?: string | null;\n estimatedDeliveryDate?: string | null;\n } | null;\n}\n\ntype OrdersTab = 'completed' | 'pending';\n\n/** Safely reads a finite number out of an untyped JSON metadata field. */\nfunction toFiniteNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\nconst SUCCESSFUL_ORDER_STATUSES: UIOrder['status'][] = ['COMPLETED', 'FULFILLED', 'PROCESSED'];\n\n/**\n * Get status badge config\n */\nconst getStatusDisplay = (\n status: UIOrder['status'],\n tr: (key: string, fallback: string) => string\n) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n classes:\n 'bg-status-success-bg-subtle/15 text-status-success-text border-status-success-text/20',\n label: tr('pages.orders.statusCompleted', 'Completed'),\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusProcessing', 'Processing'),\n };\n case 'PENDING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusPending', 'Pending'),\n };\n case 'FAILED':\n return {\n icon: XCircle,\n classes: 'bg-status-error-bg-subtle/15 text-status-error-text border-status-error-text/20',\n label: tr('pages.orders.statusFailed', 'Failed'),\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusRefunded', 'Refunded'),\n };\n case 'CANCELLED':\n return {\n icon: RefreshCw,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: tr('pages.orders.statusCancelled', 'Cancelled'),\n };\n default:\n return {\n icon: Clock,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: status,\n };\n }\n};\n\n/**\n * Status badge component\n */\nconst StatusBadge: FC<{\n status: UIOrder['status'];\n tr: (key: string, fallback: string) => string;\n}> = ({ status, tr }) => {\n const display = getStatusDisplay(status, tr);\n const Icon = display.icon;\n return (\n <span\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-semibold ${display.classes}`}\n >\n <Icon className=\"size-3.5\" />\n {display.label}\n </span>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a table of all orders with expandable rows showing line items\n * and Install App buttons for digital products.\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const tr = useTr();\n const { orders: backendOrders, loading, error, refetchOrders, hasMore, loadMore } = useOrders();\n const { installations, refetch: refetchInstallations } = useInstallations();\n\n useOrderStatusUpdatedSubscription({\n onData: () => {\n void refetchOrders();\n },\n onError: (err) => {\n console.warn('OrderStatusUpdated subscription error:', err.message);\n },\n });\n\n // Expanded rows tracking\n const [expandedOrders, setExpandedOrders] = useState<Set<string>>(new Set());\n const [activeTab, setActiveTab] = useState<OrdersTab>('completed');\n\n // Modal state\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);\n const [selectedApp, setSelectedApp] = useState<AppInfo | null>(null);\n const [selectedItemId, setSelectedItemId] = useState<string | null>(null);\n\n const installedProductIds = useMemo(\n () =>\n new Set(\n installations.flatMap((installation) =>\n installation.status === 'ACTIVE' ? [installation.productId] : []\n )\n ),\n [installations]\n );\n\n // Map backend orders to UI format\n const orders: UIOrder[] = backendOrders.map((order) => {\n const lineItems = order.lineItems || [];\n const catalogSubtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n\n // Prefer the real gateway charge (BOFF-7192): order.total/currency are\n // the order's own catalog-native bookkeeping, which can differ from\n // what was actually charged — e.g. a USD-listed item charged in INR via\n // Razorpay, with GST added. The actual subtotal/tax/total live in the\n // paired transaction's metadata (see webhook handlers for how it's\n // written); transaction.amount/currency alone is only the pre-tax\n // gateway amount, not the full charge.\n const gatewayMeta = order.transaction?.metadata;\n const gatewaySubtotal = toFiniteNumber(gatewayMeta?.subtotal);\n const gatewayTax = toFiniteNumber(gatewayMeta?.taxAmount);\n const gatewayTotal = toFiniteNumber(gatewayMeta?.total);\n const hasActualCharge =\n gatewaySubtotal != null && gatewayTotal != null && order.transaction?.currency != null;\n\n const currency = hasActualCharge ? order.transaction!.currency : order.currency;\n const subtotal = hasActualCharge ? gatewaySubtotal! : catalogSubtotal;\n const tax = hasActualCharge ? (gatewayTax ?? 0) : 0;\n const total = hasActualCharge ? gatewayTotal! : order.total;\n\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`,\n productId: item.productId,\n productName: item.name,\n productIcon: item.productIcon,\n productType:\n item.productType ||\n (typeof item.itemType === 'string' ? item.itemType.toUpperCase() : 'APP'),\n version: item.version,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.subtotal,\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`,\n items,\n catalogCurrency: order.currency,\n currency,\n subtotal,\n tax,\n total,\n isActualCharge: hasActualCharge,\n status: order.status as UIOrder['status'],\n createdAt: order.createdAt,\n fulfillment: order.fulfillment ?? null,\n };\n });\n\n const completedOrders = useMemo(\n () => orders.filter((order) => SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const pendingOrders = useMemo(\n () => orders.filter((order) => !SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const visibleOrders = activeTab === 'completed' ? completedOrders : pendingOrders;\n\n const toggleExpand = useCallback((orderId: string) => {\n setExpandedOrders((prev) => {\n const next = new Set(prev);\n if (next.has(orderId)) {\n next.delete(orderId);\n } else {\n next.add(orderId);\n }\n return next;\n });\n }, []);\n\n const handleOpenInstallModal = (orderId: string, item: OrderItem) => {\n setSelectedOrderId(orderId);\n setSelectedItemId(item.id);\n setSelectedApp({\n id: item.productId,\n name: item.productName,\n icon: item.productIcon,\n type: item.productType,\n version: item.version,\n });\n setIsModalOpen(true);\n };\n\n const handleCloseModal = () => {\n setIsModalOpen(false);\n setSelectedOrderId(null);\n setSelectedApp(null);\n setSelectedItemId(null);\n };\n\n const handleInstallSuccess = (_workspaceId: string, _workspaceName: string) => {\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (_error: Error) => {\n // Error is displayed in modal\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n // ──── Shared table cell classes ────\n const cellCls = 'px-4 py-3 text-sm';\n const headerCls =\n 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-text-muted';\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"flex-shrink-0 border-b border-border-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('pages.orders.title', { defaultValue: 'My Orders' })}\n </h1>\n {orders.length > 0 && (\n <span className=\"rounded-full bg-action-primary-bg/10 px-2.5 py-0.5 text-sm font-medium text-action-primary-bg\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </header>\n\n {/* Scrollable content area */}\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-6xl p-6\">\n <PagePurpose className=\"mb-6\">\n {tr(\n 'pages.orders.purpose',\n \"A record of everything you've purchased from the marketplace. Track each order's status, review what you paid, and — once an order is complete — install the apps you bought into a workspace right from here. Come back to re-install on a new workspace or check a past receipt.\"\n )}\n </PagePurpose>\n\n {loading && orders.length === 0 ? (\n // ── Loading state ──\n <div className=\"flex flex-col items-center justify-center py-24\">\n <Loader2 className=\"size-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">\n {tr('pages.orders.loading', 'Loading your orders…')}\n </p>\n </div>\n ) : error ? (\n // ── Error state ──\n <div className=\"flex flex-col items-center justify-center py-24 text-center\">\n <div className=\"mb-6 rounded-full bg-status-error-bg-subtle/10 p-6\">\n <XCircle className=\"size-16 text-status-error-text\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.failedToLoad', { defaultValue: 'Failed to load orders' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {error.message ||\n tr(\n 'pages.orders.errorDescription',\n 'An error occurred while loading your orders. Please try again.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n {tr('pages.orders.browseMarketplace', 'Browse Marketplace')}\n </button>\n </div>\n ) : orders.length === 0 ? (\n // ── Empty state ──\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n description={tr(\n 'pages.orders.emptyDescription',\n 'You have not placed any orders yet. Browse the marketplace to discover apps and products!'\n )}\n action={\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n {tr('pages.orders.browseMarketplace', 'Browse Marketplace')}\n </button>\n }\n />\n ) : (\n // ── Table ──\n <div className=\"space-y-4\">\n <div className=\"inline-flex rounded-xl border border-border-seam bg-bg-surface p-1\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('completed')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'completed'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr('pages.orders.tabCompleted', 'Completed')}\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {completedOrders.length}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('pending')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'pending'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr('pages.orders.tabPending', 'Pending')}\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {pendingOrders.length}\n </span>\n </button>\n </div>\n\n {visibleOrders.length === 0 ? (\n <GlassCard className=\"p-10 text-center\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <h2 className=\"mb-2 text-lg font-semibold text-text-primary\">\n {activeTab === 'completed'\n ? tr('pages.orders.noCompletedTitle', 'No completed orders yet')\n : tr('pages.orders.noPendingTitle', 'No pending orders')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {activeTab === 'completed'\n ? tr(\n 'pages.orders.noCompletedDescription',\n 'Completed purchases will appear here once payment is successful.'\n )\n : tr(\n 'pages.orders.noPendingDescription',\n 'Orders that are pending, processing, failed, refunded, or cancelled will appear here.'\n )}\n </p>\n </GlassCard>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n {/* Table Header */}\n <div className=\"hidden border-b border-border-seam bg-bg-sunken/50 md:grid md:grid-cols-[40px_130px_140px_1fr_120px_140px]\">\n <div className={headerCls} />\n <div className={headerCls}>{tr('pages.orders.colOrder', 'Order')}</div>\n <div className={headerCls}>{tr('pages.orders.colDate', 'Date')}</div>\n <div className={headerCls}>{tr('pages.orders.colItems', 'Items')}</div>\n <div className={`${headerCls} text-right`}>\n {tr('pages.orders.colTotal', 'Total')}\n </div>\n <div className={`${headerCls} text-center`}>\n {tr('pages.orders.colStatus', 'Status')}\n </div>\n </div>\n\n {/* Table Body */}\n {visibleOrders.map((order) => {\n const isExpanded = expandedOrders.has(order.id);\n const isSuccessful =\n order.status === 'COMPLETED' ||\n order.status === 'FULFILLED' ||\n order.status === 'PROCESSED';\n\n return (\n <div key={order.id} className=\"border-b border-border-seam last:border-b-0\">\n {/* Order Row */}\n <button\n type=\"button\"\n onClick={() => toggleExpand(order.id)}\n className=\"grid w-full cursor-pointer grid-cols-[40px_130px_140px_1fr_120px_140px] items-center transition-colors hover:bg-bg-sunken/50 md:grid\"\n >\n {/* Expand/collapse chevron */}\n <div className={`${cellCls} flex justify-center`}>\n <ChevronRight\n className={`size-4 text-text-muted transition-transform duration-200 ${\n isExpanded ? 'rotate-90' : ''\n }`}\n />\n </div>\n\n {/* Order number */}\n <div className={`${cellCls} font-medium text-text-primary`}>\n {order.orderNumber}\n </div>\n\n {/* Date */}\n <div className={`${cellCls} text-text-muted`}>\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </div>\n\n {/* Items summary */}\n <div className={`${cellCls} text-text-muted`}>\n {order.items.length}{' '}\n {order.items.length === 1\n ? tr('pages.orders.item', 'item')\n : tr('pages.orders.items', 'items')}\n <span className=\"block text-xs text-text-muted/70\">\n {order.items.map((i) => i.productName).join(', ')}\n </span>\n </div>\n\n {/* Total */}\n <div\n className={`${cellCls} text-right font-semibold tabular-nums text-text-primary`}\n >\n {formatPrice(order.total, order.currency)}\n </div>\n\n {/* Status */}\n <div className={`${cellCls} flex justify-center`}>\n <StatusBadge status={order.status} tr={tr} />\n </div>\n </button>\n\n {/* Expanded: Line items */}\n {isExpanded && (\n <div className=\"border-t border-border-seam bg-bg-sunken/30\">\n {order.fulfillment && (\n <div className=\"mx-4 mt-4 rounded-xl border border-border-seam bg-bg-surface px-4 py-4 md:mx-12\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">\n {tr('pages.orders.orderTracking', 'Order tracking')}\n </div>\n <div className=\"mt-1 text-xs text-text-muted\">\n {order.fulfillment.status ||\n tr('pages.orders.statusPending', 'Pending')}\n {order.fulfillment.carrier\n ? ` · ${order.fulfillment.carrier}`\n : ''}\n {order.fulfillment.estimatedDeliveryDate\n ? ` · ${tr('pages.orders.estimatedDelivery', 'Est. delivery {{date}}', { date: new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString() })}`\n : ''}\n </div>\n </div>\n\n <div className=\"flex flex-col items-start gap-2 md:items-end\">\n {order.fulfillment.trackingNumber && (\n <div className=\"text-xs text-text-muted\">\n {tr(\n 'pages.orders.trackingNumber',\n 'Tracking #: {{number}}',\n { number: order.fulfillment.trackingNumber }\n )}\n </div>\n )}\n {order.fulfillment.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {tr('pages.orders.trackPackage', 'Track package')}\n <ChevronRight className=\"size-3.5\" />\n </a>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-2 sm:grid-cols-4\">\n {[\n {\n key: 'PROCESSING',\n label: tr('pages.orders.stepProcessing', 'Processing'),\n },\n {\n key: 'SHIPPED',\n label: tr('pages.orders.stepShipped', 'Shipped'),\n },\n {\n key: 'OUT_FOR_DELIVERY',\n label: tr(\n 'pages.orders.stepOutForDelivery',\n 'Out for delivery'\n ),\n },\n {\n key: 'DELIVERED',\n label: tr('pages.orders.stepDelivered', 'Delivered'),\n },\n ].map((step) => {\n const current = order.fulfillment?.status || 'PENDING';\n const reached =\n [\n 'PROCESSING',\n 'READY_TO_SHIP',\n 'SHIPPED',\n 'IN_TRANSIT',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(current) >=\n [\n 'PROCESSING',\n 'SHIPPED',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(step.key);\n return (\n <div\n key={step.key}\n className={`rounded-lg border px-3 py-2 text-xs font-medium ${\n reached\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-seam bg-bg-sunken/40 text-text-muted'\n }`}\n >\n {step.label}\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Column headers for items */}\n <div className=\"hidden grid-cols-[1fr_100px_120px_160px] gap-4 px-12 py-2 md:grid\">\n <span className=\"text-xs font-medium text-text-muted\">\n {tr('pages.orders.colProduct', 'Product')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n {tr('pages.orders.colQty', 'Qty')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n {tr('pages.orders.colPrice', 'Price')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-center\">\n {tr('pages.orders.colAction', 'Action')}\n </span>\n </div>\n\n {order.items.map((item) => {\n const isInstallable =\n item.productType !== 'PHYSICAL' &&\n item.productType !== 'MERCHANDISE';\n\n return (\n <div\n key={item.id}\n className=\"grid grid-cols-[auto_1fr_auto] items-center gap-3 px-4 py-3 md:px-12 md:grid-cols-[1fr_100px_120px_160px]\"\n >\n {/* Product info */}\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.productIcon ? (\n <img\n src={item.productIcon}\n alt={item.productName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-sm font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {item.productName}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.productType}\n </span>\n </div>\n </div>\n\n {/* Quantity */}\n <div className=\"hidden text-right text-sm text-text-muted md:block\">\n {item.quantity}\n </div>\n\n {/* Price */}\n <div className=\"hidden text-right text-sm font-semibold tabular-nums text-text-primary md:block\">\n {formatPrice(item.totalPrice, order.catalogCurrency)}\n </div>\n\n {/* Mobile price + qty */}\n <div className=\"text-right text-sm text-text-muted md:hidden\">\n <span className=\"tabular-nums\">\n {tr('pages.orders.qtyTimesPrice', '{{qty}} × {{price}}', {\n qty: item.quantity,\n price: formatPrice(item.unitPrice, order.catalogCurrency),\n })}\n </span>\n </div>\n\n {/* Install button */}\n <div className=\"flex justify-end md:justify-center\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <CheckCircle className=\"size-3.5\" />\n {tr('pages.orders.installed', 'Installed')}\n </span>\n ) : isSuccessful && isInstallable ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleOpenInstallModal(order.id, item);\n }}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-all hover:opacity-90 active:scale-95\"\n >\n <Download className=\"size-3.5\" />\n {tr('pages.orders.installApp', 'Install App')}\n </button>\n ) : null}\n </div>\n </div>\n );\n })}\n\n {/* Order summary footer */}\n <div className=\"border-t border-border-seam/50 px-4 py-2 md:px-12\">\n {order.isActualCharge && order.currency !== order.catalogCurrency && (\n <p className=\"mb-1 text-right text-xs text-text-muted\">\n {tr(\n 'pages.orders.paidInCurrency',\n 'Listed in {{catalogCurrency}}, actually charged in {{currency}}',\n {\n catalogCurrency: order.catalogCurrency,\n currency: order.currency,\n }\n )}\n </p>\n )}\n <div className=\"flex justify-end gap-6 text-xs text-text-muted\">\n <span>\n {tr('pages.orders.subtotal', 'Subtotal:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.subtotal, order.currency)}\n </span>\n </span>\n <span>\n {tr('pages.orders.tax', 'Tax:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.tax, order.currency)}\n </span>\n </span>\n <span className=\"font-semibold\">\n {tr('pages.orders.total', 'Total:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.total, order.currency)}\n </span>\n </span>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {/* Load More */}\n {hasMore && (\n <div className=\"flex justify-center px-4 py-5\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Install App Modal */}\n {selectedApp && (\n <InstallAppModal\n isOpen={isModalOpen}\n onClose={handleCloseModal}\n app={selectedApp}\n orderId={selectedOrderId}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AA+EA,SAAS,EAAe,GAA+B;AACrD,QAAO,OAAO,KAAU,YAAY,OAAO,SAAS,EAAM,GAAG,IAAQ;;AAGvE,IAAM,IAAiD;CAAC;CAAa;CAAa;CAAY,EAKxF,KACJ,GACA,MACG;AACH,SAAQ,GAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK,YACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO,EAAG,gCAAgC,YAAY;GACvD;EACH,KAAK,aACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO,EAAG,iCAAiC,aAAa;GACzD;EACH,KAAK,UACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO,EAAG,8BAA8B,UAAU;GACnD;EACH,KAAK,SACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO,EAAG,6BAA6B,SAAS;GACjD;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO,EAAG,+BAA+B,WAAW;GACrD;EACH,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO,EAAG,gCAAgC,YAAY;GACvD;EACH,QACE,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO;GACR;;GAOD,MAGA,EAAE,WAAQ,YAAS;CACvB,IAAM,IAAU,EAAiB,GAAQ,EAAG,EACtC,IAAO,EAAQ;AACrB,QACE,kBAAC,QAAD;EACE,WAAW,0FAA0F,EAAQ;YAD/G,CAGE,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA,EAC5B,EAAQ,MACJ;;GAUE,UAAuB;CAClC,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAU,EACzB,EAAE,SAAM,GAAS,EACjB,IAAK,GAAO,EACZ,EAAE,QAAQ,GAAe,YAAS,UAAO,kBAAe,YAAS,gBAAa,GAAW,EACzF,EAAE,kBAAe,SAAS,OAAyB,GAAkB;AAE3E,GAAkC;EAChC,cAAc;AACP,MAAe;;EAEtB,UAAU,MAAQ;AAChB,WAAQ,KAAK,0CAA0C,EAAI,QAAQ;;EAEtE,CAAC;CAGF,IAAM,CAAC,IAAgB,MAAqB,kBAAsB,IAAI,KAAK,CAAC,EACtE,CAAC,GAAW,KAAgB,EAAoB,YAAY,EAG5D,CAAC,IAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAiB,KAAsB,EAAwB,KAAK,EACrE,CAAC,GAAa,KAAkB,EAAyB,KAAK,EAC9D,CAAC,IAAgB,KAAqB,EAAwB,KAAK,EAEnE,IAAsB,QAExB,IAAI,IACF,EAAc,SAAS,MACrB,EAAa,WAAW,WAAW,CAAC,EAAa,UAAU,GAAG,EAAE,CACjE,CACF,EACH,CAAC,EAAc,CAChB,EAGK,IAAoB,EAAc,KAAK,MAAU;EACrD,IAAM,IAAY,EAAM,aAAa,EAAE,EACjC,IAAkB,EAAU,QAAQ,GAAK,MAAS,IAAM,EAAK,UAAU,EAAE,EASzE,IAAc,EAAM,aAAa,UACjC,IAAkB,EAAe,GAAa,SAAS,EACvD,IAAa,EAAe,GAAa,UAAU,EACnD,IAAe,EAAe,GAAa,MAAM,EACjD,IACJ,KAAmB,QAAQ,KAAgB,QAAQ,EAAM,aAAa,YAAY,MAE9E,IAAW,IAAkB,EAAM,YAAa,WAAW,EAAM,UACjE,IAAW,IAAkB,IAAmB,GAChD,IAAM,IAAmB,KAAc,IAAK,GAC5C,IAAQ,IAAkB,IAAgB,EAAM,OAEhD,IAAqB,EAAU,KAAK,GAAM,OAAW;GACzD,IAAI,GAAG,EAAM,GAAG,GAAG;GACnB,WAAW,EAAK;GAChB,aAAa,EAAK;GAClB,aAAa,EAAK;GAClB,aACE,EAAK,gBACJ,OAAO,EAAK,YAAa,WAAW,EAAK,SAAS,aAAa,GAAG;GACrE,SAAS,EAAK;GACd,UAAU,EAAK;GACf,WAAW,EAAK;GAChB,YAAY,EAAK;GACjB,WAAW,EAAoB,IAAI,EAAK,UAAU;GACnD,EAAE;AAEH,SAAO;GACL,IAAI,EAAM;GACV,aAAa,IAAI,EAAM,GAAG,MAAM,GAAG,CAAC,aAAa;GACjD;GACA,iBAAiB,EAAM;GACvB;GACA;GACA;GACA;GACA,gBAAgB;GAChB,QAAQ,EAAM;GACd,WAAW,EAAM;GACjB,aAAa,EAAM,eAAe;GACnC;GACD,EAEI,IAAkB,QAChB,EAAO,QAAQ,MAAU,EAA0B,SAAS,EAAM,OAAO,CAAC,EAChF,CAAC,EAAO,CACT,EAEK,IAAgB,QACd,EAAO,QAAQ,MAAU,CAAC,EAA0B,SAAS,EAAM,OAAO,CAAC,EACjF,CAAC,EAAO,CACT,EAEK,IAAgB,MAAc,cAAc,IAAkB,GAE9D,KAAe,GAAa,MAAoB;AACpD,MAAmB,MAAS;GAC1B,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,EAAK,IAAI,EAAQ,GACnB,EAAK,OAAO,EAAQ,GAEpB,EAAK,IAAI,EAAQ,EAEZ;IACP;IACD,EAAE,CAAC,EAEA,MAA0B,GAAiB,MAAoB;AAUnE,EATA,EAAmB,EAAQ,EAC3B,EAAkB,EAAK,GAAG,EAC1B,EAAe;GACb,IAAI,EAAK;GACT,MAAM,EAAK;GACX,MAAM,EAAK;GACX,MAAM,EAAK;GACX,SAAS,EAAK;GACf,CAAC,EACF,EAAe,GAAK;IAGhB,UAAyB;AAI7B,EAHA,EAAe,GAAM,EACrB,EAAmB,KAAK,EACxB,EAAe,KAAK,EACpB,EAAkB,KAAK;IAGnB,MAAwB,GAAsB,MAA2B;AAExE,EADL,GAAkB,EACb,IAAsB;IAGvB,MAAsB,MAAkB,IAIxC,UAAgC;AACpC,IAAS,GAAG,EAAS,cAAc;IAI/B,IAAU,qBACV,IACJ;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAChB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,GAAG;QAC3B,cAAW;QACX,WAAU;kBAEV,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;QACzB,CAAA,EACT,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA;SAChD,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,sBAAsB,EAAE,cAAc,aAAa,CAAC;UACpD,CAAA;SACJ,EAAO,SAAS,KACf,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAO;UACH,CAAA;SAEL;UACF;;MACF,CAAA;KACF,CAAA;IACC,CAAA;GAGT,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAa,WAAU;gBACpB,EACC,wBACA,qRACD;MACW,CAAA,EAEb,KAAW,EAAO,WAAW,IAE5B,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAS,WAAU;OAA4C,eAAY;OAAS,CAAA,EACpF,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,wBAAwB,uBAAuB;OACjD,CAAA,CACA;UACJ,IAEF,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAS,WAAU,kCAAmC,CAAA;QAClD,CAAA;OACN,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,6BAA6B,EAAE,cAAc,yBAAyB,CAAC;QACvE,CAAA;OACL,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,WACL,EACE,iCACA,iEACD;QACD,CAAA;OACJ,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EACjC,EAAG,kCAAkC,qBAAqB,CACpD;;OACL;UACJ,EAAO,WAAW,IAEpB,kBAAC,GAAD;MACE,cAAa;MACb,OAAO,EAAE,2BAA2B,EAAE,cAAc,iBAAiB,CAAC;MACtE,aAAa,EACX,iCACA,4FACD;MACD,QACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EACjC,EAAG,kCAAkC,qBAAqB,CACpD;;MAEX,CAAA,GAGF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAa,YAAY;QACxC,WAAW,8DACT,MAAc,cACV,kDACA;kBANR,CASG,EAAG,6BAA6B,YAAY,EAC7C,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAgB;SACZ,CAAA,CACA;WACT,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAa,UAAU;QACtC,WAAW,8DACT,MAAc,YACV,kDACA;kBANR,CASG,EAAG,2BAA2B,UAAU,EACzC,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAc;SACV,CAAA,CACA;UACL;UAEL,EAAc,WAAW,IACxB,kBAAC,IAAD;OAAW,WAAU;iBAArB;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;SAC1B,CAAA;QACN,kBAAC,MAAD;SAAI,WAAU;mBACX,MAAc,cACX,EAAG,iCAAiC,0BAA0B,GAC9D,EAAG,+BAA+B,oBAAoB;SACvD,CAAA;QACL,kBAAC,KAAD;SAAG,WAAU;mBACV,MAAc,cACX,EACE,uCACA,mEACD,GACD,EACE,qCACA,wFACD;SACH,CAAA;QACM;WAEZ,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD,EAAK,WAAW,GAAa,CAAA;UAC7B,kBAAC,OAAD;WAAK,WAAW;qBAAY,EAAG,yBAAyB,QAAQ;WAAO,CAAA;UACvE,kBAAC,OAAD;WAAK,WAAW;qBAAY,EAAG,wBAAwB,OAAO;WAAO,CAAA;UACrE,kBAAC,OAAD;WAAK,WAAW;qBAAY,EAAG,yBAAyB,QAAQ;WAAO,CAAA;UACvE,kBAAC,OAAD;WAAK,WAAW,GAAG,EAAU;qBAC1B,EAAG,yBAAyB,QAAQ;WACjC,CAAA;UACN,kBAAC,OAAD;WAAK,WAAW,GAAG,EAAU;qBAC1B,EAAG,0BAA0B,SAAS;WACnC,CAAA;UACF;;QAGL,EAAc,KAAK,MAAU;SAC5B,IAAM,IAAa,GAAe,IAAI,EAAM,GAAG,EACzC,IACJ,EAAM,WAAW,eACjB,EAAM,WAAW,eACjB,EAAM,WAAW;AAEnB,gBACE,kBAAC,OAAD;UAAoB,WAAU;oBAA9B,CAEE,kBAAC,UAAD;WACE,MAAK;WACL,eAAe,GAAa,EAAM,GAAG;WACrC,WAAU;qBAHZ;YAME,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACzB,kBAAC,GAAD,EACE,WAAW,4DACT,IAAa,cAAc,MAE7B,CAAA;aACE,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACxB,EAAM;aACH,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACxB,IAAI,KAAK,EAAM,UAAU,CAAC,mBAAmB,SAAS;cACrD,MAAM;cACN,OAAO;cACP,KAAK;cACN,CAAC;aACE,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBAA3B;cACG,EAAM,MAAM;cAAQ;cACpB,EAAM,MAAM,WAAW,IACpB,EAAG,qBAAqB,OAAO,GAC/B,EAAG,sBAAsB,QAAQ;cACrC,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAM,MAAM,KAAK,MAAM,EAAE,YAAY,CAAC,KAAK,KAAK;eAC5C,CAAA;cACH;;YAGN,kBAAC,OAAD;aACE,WAAW,GAAG,EAAQ;uBAErB,EAAY,EAAM,OAAO,EAAM,SAAS;aACrC,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACzB,kBAAC,IAAD;cAAa,QAAQ,EAAM;cAAY;cAAM,CAAA;aACzC,CAAA;YACC;cAGR,KACC,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACG,EAAM,eACL,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,OAAD;cAAK,WAAU;wBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;eAAK,WAAU;yBACZ,EAAG,8BAA8B,iBAAiB;eAC/C,CAAA,EACN,kBAAC,OAAD;eAAK,WAAU;yBAAf;gBACG,EAAM,YAAY,UACjB,EAAG,8BAA8B,UAAU;gBAC5C,EAAM,YAAY,UACf,MAAM,EAAM,YAAY,YACxB;gBACH,EAAM,YAAY,wBACf,MAAM,EAAG,kCAAkC,0BAA0B,EAAE,MAAM,IAAI,KAAK,EAAM,YAAY,sBAAsB,CAAC,oBAAoB,EAAE,CAAC,KACtJ;gBACA;iBACF,EAAA,CAAA,EAEN,kBAAC,OAAD;eAAK,WAAU;yBAAf,CACG,EAAM,YAAY,kBACjB,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EACC,+BACA,0BACA,EAAE,QAAQ,EAAM,YAAY,gBAAgB,CAC7C;gBACG,CAAA,EAEP,EAAM,YAAY,eACjB,kBAAC,KAAD;gBACE,MAAM,EAAM,YAAY;gBACxB,QAAO;gBACP,KAAI;gBACJ,WAAU;0BAJZ,CAMG,EAAG,6BAA6B,gBAAgB,EACjD,kBAAC,GAAD,EAAc,WAAU,YAAa,CAAA,CACnC;kBAEF;iBACF;iBAEN,kBAAC,OAAD;cAAK,WAAU;wBACZ;eACC;gBACE,KAAK;gBACL,OAAO,EAAG,+BAA+B,aAAa;gBACvD;eACD;gBACE,KAAK;gBACL,OAAO,EAAG,4BAA4B,UAAU;gBACjD;eACD;gBACE,KAAK;gBACL,OAAO,EACL,mCACA,mBACD;gBACF;eACD;gBACE,KAAK;gBACL,OAAO,EAAG,8BAA8B,YAAY;gBACrD;eACF,CAAC,KAAK,MAAS;eACd,IAAM,IAAU,EAAM,aAAa,UAAU;AAgB7C,sBACE,kBAAC,OAAD;gBAEE,WAAW,mDAjBb;iBACE;iBACA;iBACA;iBACA;iBACA;iBACA;iBACD,CAAC,QAAQ,EAAQ,IAClB;iBACE;iBACA;iBACA;iBACA;iBACD,CAAC,QAAQ,EAAK,IAAI,GAMX,sFACA;0BAGL,EAAK;gBACF,EARC,EAAK,IAQN;gBAER;cACE,CAAA,CACF;;YAIR,kBAAC,OAAD;aAAK,WAAU;uBAAf;cACE,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAG,2BAA2B,UAAU;eACpC,CAAA;cACP,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAG,uBAAuB,MAAM;eAC5B,CAAA;cACP,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAG,yBAAyB,QAAQ;eAChC,CAAA;cACP,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAG,0BAA0B,SAAS;eAClC,CAAA;cACH;;YAEL,EAAM,MAAM,KAAK,MAAS;aACzB,IAAM,IACJ,EAAK,gBAAgB,cACrB,EAAK,gBAAgB;AAEvB,oBACE,kBAAC,OAAD;cAEE,WAAU;wBAFZ;eAKE,kBAAC,OAAD;gBAAK,WAAU;0BAAf,CACE,kBAAC,OAAD;iBAAK,WAAU;2BACZ,EAAK,cACJ,kBAAC,OAAD;kBACE,KAAK,EAAK;kBACV,KAAK,EAAK;kBACV,WAAU;kBACV,CAAA,GAEF,kBAAC,OAAD;kBAAK,WAAU;4BACZ,EAAK,YAAY,OAAO,EAAE;kBACvB,CAAA;iBAEJ,CAAA,EACN,kBAAC,OAAD;iBAAK,WAAU;2BAAf,CACE,kBAAC,KAAD;kBAAG,WAAU;4BACV,EAAK;kBACJ,CAAA,EACJ,kBAAC,QAAD;kBAAM,WAAU;4BACb,EAAK;kBACD,CAAA,CACH;mBACF;;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAK;gBACF,CAAA;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAY,EAAK,YAAY,EAAM,gBAAgB;gBAChD,CAAA;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACb,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAG,8BAA8B,uBAAuB;kBACvD,KAAK,EAAK;kBACV,OAAO,EAAY,EAAK,WAAW,EAAM,gBAAgB;kBAC1D,CAAC;iBACG,CAAA;gBACH,CAAA;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAK,YACJ,kBAAC,QAAD;iBAAM,WAAU;2BAAhB,CACE,kBAAC,GAAD,EAAa,WAAU,YAAa,CAAA,EACnC,EAAG,0BAA0B,YAAY,CACrC;qBACL,KAAgB,IAClB,kBAAC,UAAD;iBACE,MAAK;iBACL,UAAU,MAAM;AAEd,kBADA,EAAE,iBAAiB,EACnB,GAAuB,EAAM,IAAI,EAAK;;iBAExC,WAAU;2BANZ,CAQE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAChC,EAAG,2BAA2B,cAAc,CACtC;qBACP;gBACA,CAAA;eACF;gBArEC,EAAK,GAqEN;cAER;YAGF,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACG,EAAM,kBAAkB,EAAM,aAAa,EAAM,mBAChD,kBAAC,KAAD;cAAG,WAAU;wBACV,EACC,+BACA,mEACA;eACE,iBAAiB,EAAM;eACvB,UAAU,EAAM;eACjB,CACF;cACC,CAAA,EAEN,kBAAC,OAAD;cAAK,WAAU;wBAAf;eACE,kBAAC,QAAD,EAAA,UAAA;gBACG,EAAG,yBAAyB,YAAY;gBAAE;gBAC3C,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAY,EAAM,UAAU,EAAM,SAAS;iBACvC,CAAA;gBACF,EAAA,CAAA;eACP,kBAAC,QAAD,EAAA,UAAA;gBACG,EAAG,oBAAoB,OAAO;gBAAE;gBACjC,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAY,EAAM,KAAK,EAAM,SAAS;iBAClC,CAAA;gBACF,EAAA,CAAA;eACP,kBAAC,QAAD;gBAAM,WAAU;0BAAhB;iBACG,EAAG,sBAAsB,SAAS;iBAAE;iBACrC,kBAAC,QAAD;kBAAM,WAAU;4BACb,EAAY,EAAM,OAAO,EAAM,SAAS;kBACpC,CAAA;iBACF;;eACH;gBACF;;YACF;aAEJ;YAhSI,EAAM,GAgSV;UAER;QAGD,KACC,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,UAAD;UACE,MAAK;UACL,SAAS;UACT,UAAU;UACV,WAAU;UACV,cAAY,EAAE,mBAAmB,EAAE,cAAc,aAAa,CAAC;oBALjE,CAOG,IACC,kBAAC,GAAD;WAAS,WAAU;WAAsB,eAAY;WAAS,CAAA,GAC5D,MACH,EAAE,mBAAmB,EAAE,cAAc,aAAa,CAAC,CAC7C;;SACL,CAAA;QAEJ;SAEJ;QAEJ;;IACF,CAAA;GAGL,KACC,kBAAC,GAAD;IACE,QAAQ;IACR,SAAS;IACT,KAAK;IACL,SAAS;IACT,kBAAkB;IAClB,gBAAgB;IAChB,CAAA;GAEA"}
|
|
1
|
+
{"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useCallback } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\n ChevronRight,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { useOrders } from '../hooks/useOrders';\nimport { formatPrice } from '../utils';\nimport { InstallAppModal, type AppInfo } from '../components/InstallAppModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTr } from '../shared/hooks/useTr';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrderStatusUpdatedSubscription } from '../generated/global-operations';\n\n// Local types for UI\ninterface OrderItem {\n id: string;\n productId: string;\n productName: string;\n productIcon?: string;\n productType: string;\n version?: string;\n quantity: number;\n unitPrice: number;\n totalPrice: number;\n installed?: boolean;\n}\n\ninterface UIOrder {\n id: string;\n orderNumber: string;\n items: OrderItem[];\n /** The order's own catalog-native currency — what item unitPrice/totalPrice\n * below are denominated in (the listed price), regardless of what was\n * actually charged. */\n catalogCurrency: string;\n /** Currency of subtotal/tax/total below — the actually-charged currency\n * when a payment transaction exists, otherwise catalogCurrency (e.g. an\n * unpaid order, or one paid entirely with credits). */\n currency: string;\n subtotal: number;\n tax: number;\n total: number;\n /** True when subtotal/tax/total reflect the real gateway charge\n * (transaction.metadata) rather than the order's catalog-native amounts. */\n isActualCharge: boolean;\n /** Credits actually spent, when this order was paid with credits\n * (transaction.metadata.paymentMethod === 'CREDITS'). null otherwise. */\n creditsUsed: number | null;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n fulfillment?: {\n status?: string | null;\n trackingNumber?: string | null;\n trackingUrl?: string | null;\n carrier?: string | null;\n estimatedDeliveryDate?: string | null;\n } | null;\n}\n\ntype OrdersTab = 'completed' | 'pending';\n\n/** Safely reads a finite number out of an untyped JSON metadata field. */\nfunction toFiniteNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\nconst SUCCESSFUL_ORDER_STATUSES: UIOrder['status'][] = ['COMPLETED', 'FULFILLED', 'PROCESSED'];\n\n/**\n * Get status badge config\n */\nconst getStatusDisplay = (\n status: UIOrder['status'],\n tr: (key: string, fallback: string) => string\n) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n classes:\n 'bg-status-success-bg-subtle/15 text-status-success-text border-status-success-text/20',\n label: tr('pages.orders.statusCompleted', 'Completed'),\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusProcessing', 'Processing'),\n };\n case 'PENDING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusPending', 'Pending'),\n };\n case 'FAILED':\n return {\n icon: XCircle,\n classes: 'bg-status-error-bg-subtle/15 text-status-error-text border-status-error-text/20',\n label: tr('pages.orders.statusFailed', 'Failed'),\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: tr('pages.orders.statusRefunded', 'Refunded'),\n };\n case 'CANCELLED':\n return {\n icon: RefreshCw,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: tr('pages.orders.statusCancelled', 'Cancelled'),\n };\n default:\n return {\n icon: Clock,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: status,\n };\n }\n};\n\n/**\n * Status badge component\n */\nconst StatusBadge: FC<{\n status: UIOrder['status'];\n tr: (key: string, fallback: string) => string;\n}> = ({ status, tr }) => {\n const display = getStatusDisplay(status, tr);\n const Icon = display.icon;\n return (\n <span\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-semibold ${display.classes}`}\n >\n <Icon className=\"size-3.5\" />\n {display.label}\n </span>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a table of all orders with expandable rows showing line items\n * and Install App buttons for digital products.\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const tr = useTr();\n const { orders: backendOrders, loading, error, refetchOrders, hasMore, loadMore } = useOrders();\n const { installations, refetch: refetchInstallations } = useInstallations();\n\n useOrderStatusUpdatedSubscription({\n onData: () => {\n void refetchOrders();\n },\n onError: (err) => {\n console.warn('OrderStatusUpdated subscription error:', err.message);\n },\n });\n\n // Expanded rows tracking\n const [expandedOrders, setExpandedOrders] = useState<Set<string>>(new Set());\n const [activeTab, setActiveTab] = useState<OrdersTab>('completed');\n\n // Modal state\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);\n const [selectedApp, setSelectedApp] = useState<AppInfo | null>(null);\n const [selectedItemId, setSelectedItemId] = useState<string | null>(null);\n\n const installedProductIds = useMemo(\n () =>\n new Set(\n installations.flatMap((installation) =>\n installation.status === 'ACTIVE' ? [installation.productId] : []\n )\n ),\n [installations]\n );\n\n // Map backend orders to UI format\n const orders: UIOrder[] = backendOrders.map((order) => {\n const lineItems = order.lineItems || [];\n const catalogSubtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n\n // Prefer the real gateway charge (BOFF-7192): order.total/currency are\n // the order's own catalog-native bookkeeping, which can differ from\n // what was actually charged — e.g. a USD-listed item charged in INR via\n // Razorpay, with GST added. The actual subtotal/tax/total live in the\n // paired transaction's metadata (see webhook handlers for how it's\n // written); transaction.amount/currency alone is only the pre-tax\n // gateway amount, not the full charge.\n const gatewayMeta = order.transaction?.metadata;\n const gatewaySubtotal = toFiniteNumber(gatewayMeta?.subtotal);\n const gatewayTax = toFiniteNumber(gatewayMeta?.taxAmount);\n const gatewayTotal = toFiniteNumber(gatewayMeta?.total);\n const hasActualCharge =\n gatewaySubtotal != null && gatewayTotal != null && order.transaction?.currency != null;\n\n const currency = hasActualCharge ? order.transaction!.currency : order.currency;\n const subtotal = hasActualCharge ? gatewaySubtotal! : catalogSubtotal;\n const tax = hasActualCharge ? (gatewayTax ?? 0) : 0;\n const total = hasActualCharge ? gatewayTotal! : order.total;\n\n const creditsUsed =\n gatewayMeta?.paymentMethod === 'CREDITS' ? toFiniteNumber(gatewayMeta?.creditsUsed) : null;\n\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`,\n productId: item.productId,\n productName: item.name,\n productIcon: item.productIcon,\n productType:\n item.productType ||\n (typeof item.itemType === 'string' ? item.itemType.toUpperCase() : 'APP'),\n version: item.version,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.subtotal,\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`,\n items,\n catalogCurrency: order.currency,\n currency,\n subtotal,\n tax,\n total,\n isActualCharge: hasActualCharge,\n creditsUsed,\n status: order.status as UIOrder['status'],\n createdAt: order.createdAt,\n fulfillment: order.fulfillment ?? null,\n };\n });\n\n const completedOrders = useMemo(\n () => orders.filter((order) => SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const pendingOrders = useMemo(\n () => orders.filter((order) => !SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const visibleOrders = activeTab === 'completed' ? completedOrders : pendingOrders;\n\n const toggleExpand = useCallback((orderId: string) => {\n setExpandedOrders((prev) => {\n const next = new Set(prev);\n if (next.has(orderId)) {\n next.delete(orderId);\n } else {\n next.add(orderId);\n }\n return next;\n });\n }, []);\n\n const handleOpenInstallModal = (orderId: string, item: OrderItem) => {\n setSelectedOrderId(orderId);\n setSelectedItemId(item.id);\n setSelectedApp({\n id: item.productId,\n name: item.productName,\n icon: item.productIcon,\n type: item.productType,\n version: item.version,\n });\n setIsModalOpen(true);\n };\n\n const handleCloseModal = () => {\n setIsModalOpen(false);\n setSelectedOrderId(null);\n setSelectedApp(null);\n setSelectedItemId(null);\n };\n\n const handleInstallSuccess = (_workspaceId: string, _workspaceName: string) => {\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (_error: Error) => {\n // Error is displayed in modal\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n // ──── Shared table cell classes ────\n const cellCls = 'px-4 py-3 text-sm';\n const headerCls =\n 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-text-muted';\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"flex-shrink-0 border-b border-border-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('pages.orders.title', { defaultValue: 'My Orders' })}\n </h1>\n {orders.length > 0 && (\n <span className=\"rounded-full bg-action-primary-bg/10 px-2.5 py-0.5 text-sm font-medium text-action-primary-bg\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </header>\n\n {/* Scrollable content area */}\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-6xl p-6\">\n <PagePurpose className=\"mb-6\">\n {tr(\n 'pages.orders.purpose',\n \"A record of everything you've purchased from the marketplace. Track each order's status, review what you paid, and — once an order is complete — install the apps you bought into a workspace right from here. Come back to re-install on a new workspace or check a past receipt.\"\n )}\n </PagePurpose>\n\n {loading && orders.length === 0 ? (\n // ── Loading state ──\n <div className=\"flex flex-col items-center justify-center py-24\">\n <Loader2 className=\"size-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">\n {tr('pages.orders.loading', 'Loading your orders…')}\n </p>\n </div>\n ) : error ? (\n // ── Error state ──\n <div className=\"flex flex-col items-center justify-center py-24 text-center\">\n <div className=\"mb-6 rounded-full bg-status-error-bg-subtle/10 p-6\">\n <XCircle className=\"size-16 text-status-error-text\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.failedToLoad', { defaultValue: 'Failed to load orders' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {error.message ||\n tr(\n 'pages.orders.errorDescription',\n 'An error occurred while loading your orders. Please try again.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n {tr('pages.orders.browseMarketplace', 'Browse Marketplace')}\n </button>\n </div>\n ) : orders.length === 0 ? (\n // ── Empty state ──\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n description={tr(\n 'pages.orders.emptyDescription',\n 'You have not placed any orders yet. Browse the marketplace to discover apps and products!'\n )}\n action={\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n {tr('pages.orders.browseMarketplace', 'Browse Marketplace')}\n </button>\n }\n />\n ) : (\n // ── Table ──\n <div className=\"space-y-4\">\n <div className=\"inline-flex rounded-xl border border-border-seam bg-bg-surface p-1\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('completed')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'completed'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr('pages.orders.tabCompleted', 'Completed')}\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {completedOrders.length}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('pending')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'pending'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tr('pages.orders.tabPending', 'Pending')}\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {pendingOrders.length}\n </span>\n </button>\n </div>\n\n {visibleOrders.length === 0 ? (\n <GlassCard className=\"p-10 text-center\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <h2 className=\"mb-2 text-lg font-semibold text-text-primary\">\n {activeTab === 'completed'\n ? tr('pages.orders.noCompletedTitle', 'No completed orders yet')\n : tr('pages.orders.noPendingTitle', 'No pending orders')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {activeTab === 'completed'\n ? tr(\n 'pages.orders.noCompletedDescription',\n 'Completed purchases will appear here once payment is successful.'\n )\n : tr(\n 'pages.orders.noPendingDescription',\n 'Orders that are pending, processing, failed, refunded, or cancelled will appear here.'\n )}\n </p>\n </GlassCard>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n {/* Table Header */}\n <div className=\"hidden border-b border-border-seam bg-bg-sunken/50 md:grid md:grid-cols-[40px_130px_140px_1fr_120px_140px]\">\n <div className={headerCls} />\n <div className={headerCls}>{tr('pages.orders.colOrder', 'Order')}</div>\n <div className={headerCls}>{tr('pages.orders.colDate', 'Date')}</div>\n <div className={headerCls}>{tr('pages.orders.colItems', 'Items')}</div>\n <div className={`${headerCls} text-right`}>\n {tr('pages.orders.colTotal', 'Total')}\n </div>\n <div className={`${headerCls} text-center`}>\n {tr('pages.orders.colStatus', 'Status')}\n </div>\n </div>\n\n {/* Table Body */}\n {visibleOrders.map((order) => {\n const isExpanded = expandedOrders.has(order.id);\n const isSuccessful =\n order.status === 'COMPLETED' ||\n order.status === 'FULFILLED' ||\n order.status === 'PROCESSED';\n\n return (\n <div key={order.id} className=\"border-b border-border-seam last:border-b-0\">\n {/* Order Row */}\n <button\n type=\"button\"\n onClick={() => toggleExpand(order.id)}\n className=\"grid w-full cursor-pointer grid-cols-[40px_130px_140px_1fr_120px_140px] items-center transition-colors hover:bg-bg-sunken/50 md:grid\"\n >\n {/* Expand/collapse chevron */}\n <div className={`${cellCls} flex justify-center`}>\n <ChevronRight\n className={`size-4 text-text-muted transition-transform duration-200 ${\n isExpanded ? 'rotate-90' : ''\n }`}\n />\n </div>\n\n {/* Order number */}\n <div className={`${cellCls} font-medium text-text-primary`}>\n {order.orderNumber}\n </div>\n\n {/* Date */}\n <div className={`${cellCls} text-text-muted`}>\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </div>\n\n {/* Items summary */}\n <div className={`${cellCls} text-text-muted`}>\n {order.items.length}{' '}\n {order.items.length === 1\n ? tr('pages.orders.item', 'item')\n : tr('pages.orders.items', 'items')}\n <span className=\"block text-xs text-text-muted/70\">\n {order.items.map((i) => i.productName).join(', ')}\n </span>\n </div>\n\n {/* Total */}\n <div\n className={`${cellCls} text-right font-semibold tabular-nums text-text-primary`}\n >\n {formatPrice(order.total, order.currency)}\n </div>\n\n {/* Status */}\n <div className={`${cellCls} flex justify-center`}>\n <StatusBadge status={order.status} tr={tr} />\n </div>\n </button>\n\n {/* Expanded: Line items */}\n {isExpanded && (\n <div className=\"border-t border-border-seam bg-bg-sunken/30\">\n {order.fulfillment && (\n <div className=\"mx-4 mt-4 rounded-xl border border-border-seam bg-bg-surface px-4 py-4 md:mx-12\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">\n {tr('pages.orders.orderTracking', 'Order tracking')}\n </div>\n <div className=\"mt-1 text-xs text-text-muted\">\n {order.fulfillment.status ||\n tr('pages.orders.statusPending', 'Pending')}\n {order.fulfillment.carrier\n ? ` · ${order.fulfillment.carrier}`\n : ''}\n {order.fulfillment.estimatedDeliveryDate\n ? ` · ${tr('pages.orders.estimatedDelivery', 'Est. delivery {{date}}', { date: new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString() })}`\n : ''}\n </div>\n </div>\n\n <div className=\"flex flex-col items-start gap-2 md:items-end\">\n {order.fulfillment.trackingNumber && (\n <div className=\"text-xs text-text-muted\">\n {tr(\n 'pages.orders.trackingNumber',\n 'Tracking #: {{number}}',\n { number: order.fulfillment.trackingNumber }\n )}\n </div>\n )}\n {order.fulfillment.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {tr('pages.orders.trackPackage', 'Track package')}\n <ChevronRight className=\"size-3.5\" />\n </a>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-2 sm:grid-cols-4\">\n {[\n {\n key: 'PROCESSING',\n label: tr('pages.orders.stepProcessing', 'Processing'),\n },\n {\n key: 'SHIPPED',\n label: tr('pages.orders.stepShipped', 'Shipped'),\n },\n {\n key: 'OUT_FOR_DELIVERY',\n label: tr(\n 'pages.orders.stepOutForDelivery',\n 'Out for delivery'\n ),\n },\n {\n key: 'DELIVERED',\n label: tr('pages.orders.stepDelivered', 'Delivered'),\n },\n ].map((step) => {\n const current = order.fulfillment?.status || 'PENDING';\n const reached =\n [\n 'PROCESSING',\n 'READY_TO_SHIP',\n 'SHIPPED',\n 'IN_TRANSIT',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(current) >=\n [\n 'PROCESSING',\n 'SHIPPED',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(step.key);\n return (\n <div\n key={step.key}\n className={`rounded-lg border px-3 py-2 text-xs font-medium ${\n reached\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-seam bg-bg-sunken/40 text-text-muted'\n }`}\n >\n {step.label}\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Column headers for items */}\n <div className=\"hidden grid-cols-[1fr_100px_120px_160px] gap-4 px-12 py-2 md:grid\">\n <span className=\"text-xs font-medium text-text-muted\">\n {tr('pages.orders.colProduct', 'Product')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n {tr('pages.orders.colQty', 'Qty')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n {tr('pages.orders.colPrice', 'Price')}\n </span>\n <span className=\"text-xs font-medium text-text-muted text-center\">\n {tr('pages.orders.colAction', 'Action')}\n </span>\n </div>\n\n {order.items.map((item) => {\n const isInstallable =\n item.productType !== 'PHYSICAL' &&\n item.productType !== 'MERCHANDISE';\n\n return (\n <div\n key={item.id}\n className=\"grid grid-cols-[auto_1fr_auto] items-center gap-3 px-4 py-3 md:px-12 md:grid-cols-[1fr_100px_120px_160px]\"\n >\n {/* Product info */}\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.productIcon ? (\n <img\n src={item.productIcon}\n alt={item.productName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-sm font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {item.productName}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.productType}\n </span>\n </div>\n </div>\n\n {/* Quantity */}\n <div className=\"hidden text-right text-sm text-text-muted md:block\">\n {item.quantity}\n </div>\n\n {/* Price */}\n <div className=\"hidden text-right text-sm font-semibold tabular-nums text-text-primary md:block\">\n {formatPrice(item.totalPrice, order.catalogCurrency)}\n </div>\n\n {/* Mobile price + qty */}\n <div className=\"text-right text-sm text-text-muted md:hidden\">\n <span className=\"tabular-nums\">\n {tr('pages.orders.qtyTimesPrice', '{{qty}} × {{price}}', {\n qty: item.quantity,\n price: formatPrice(item.unitPrice, order.catalogCurrency),\n })}\n </span>\n </div>\n\n {/* Install button */}\n <div className=\"flex justify-end md:justify-center\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <CheckCircle className=\"size-3.5\" />\n {tr('pages.orders.installed', 'Installed')}\n </span>\n ) : isSuccessful && isInstallable ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleOpenInstallModal(order.id, item);\n }}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-all hover:opacity-90 active:scale-95\"\n >\n <Download className=\"size-3.5\" />\n {tr('pages.orders.installApp', 'Install App')}\n </button>\n ) : null}\n </div>\n </div>\n );\n })}\n\n {/* Order summary footer */}\n <div className=\"border-t border-border-seam/50 px-4 py-2 md:px-12\">\n {order.isActualCharge && order.currency !== order.catalogCurrency && (\n <p className=\"mb-1 text-right text-xs text-text-muted\">\n {tr(\n 'pages.orders.paidInCurrency',\n 'Listed in {{catalogCurrency}}, actually charged in {{currency}}',\n {\n catalogCurrency: order.catalogCurrency,\n currency: order.currency,\n }\n )}\n </p>\n )}\n <div className=\"flex justify-end gap-6 text-xs text-text-muted\">\n <span>\n {tr('pages.orders.subtotal', 'Subtotal:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.subtotal, order.currency)}\n </span>\n </span>\n <span>\n {tr('pages.orders.tax', 'Tax:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.tax, order.currency)}\n </span>\n </span>\n <span className=\"font-semibold\">\n {tr('pages.orders.total', 'Total:')}{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.total, order.currency)}\n </span>\n </span>\n </div>\n {order.creditsUsed != null && (\n <p className=\"mt-1 text-right text-xs text-text-muted\">\n {tr(\n 'pages.orders.paidWithCredits',\n 'Paid with {{credits}} credits',\n { credits: order.creditsUsed.toLocaleString() }\n )}\n </p>\n )}\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {/* Load More */}\n {hasMore && (\n <div className=\"flex justify-center px-4 py-5\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Install App Modal */}\n {selectedApp && (\n <InstallAppModal\n isOpen={isModalOpen}\n onClose={handleCloseModal}\n app={selectedApp}\n orderId={selectedOrderId}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|