@burdenoff/microfe-store 2026.716.1 → 2026.720.1
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/AdminDashboardPage.js.map +1 -1
- package/dist/pages/AdminSubmissionsQueuePage.js +110 -70
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/CartPage.js +6 -6
- package/dist/pages/CartPage.js.map +1 -1
- package/dist/pages/OrderConfirmationPage.js +137 -80
- package/dist/pages/OrderConfirmationPage.js.map +1 -1
- package/dist/pages/ProductDetailPage.js +571 -513
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -2,211 +2,268 @@ import { useStore as e } from "../providers/StoreProvider.js";
|
|
|
2
2
|
import { formatPrice as t } from "../utils/index.js";
|
|
3
3
|
import { nativeCopyText as n, nativeImpact as r, nativeNotify as i } from "../utils/nativeBridge.js";
|
|
4
4
|
import { useEffect as a, useState as o } from "react";
|
|
5
|
-
import { ArrowRight as s, CheckCircle2 as c, Copy as l, Download as u, KeyRound as d,
|
|
6
|
-
import { graphqlFetch as
|
|
7
|
-
import { jsx as
|
|
8
|
-
import { useNavigate as
|
|
9
|
-
import { EmphasisPanel as
|
|
5
|
+
import { ArrowRight as s, CheckCircle2 as c, Copy as l, Download as u, KeyRound as d, Package as f, Receipt as p, Truck as m } from "lucide-react";
|
|
6
|
+
import { graphqlFetch as h } from "@burdenoff/fe-libs/shared/graphql";
|
|
7
|
+
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
8
|
+
import { useNavigate as y, useParams as b } from "react-router-dom";
|
|
9
|
+
import { EmphasisPanel as x, NextSteps as S } from "@burdenoff/fe-libs/ui";
|
|
10
10
|
//#region src/pages/OrderConfirmationPage.tsx
|
|
11
|
-
var
|
|
12
|
-
let
|
|
11
|
+
var C = "\n query OrderConfirmation($id: ID!) {\n getStoreOrder(id: $id) {\n id\n status\n total\n currency\n purchasedAt\n lineItems\n context\n fulfillment {\n status\n trackingNumber\n trackingUrl\n carrier\n estimatedDeliveryDate\n }\n }\n myEntitlements {\n id\n productId\n licenseKey\n expiresAt\n features\n product {\n id\n name\n slug\n icon\n manifest\n }\n }\n }\n", w = () => {
|
|
12
|
+
let w = y(), { basePath: T, authToken: E } = e(), { orderId: D } = b(), [O, k] = o(null), [A, j] = o([]), [M, N] = o(!0), [P, F] = o(null), [I, L] = o(null);
|
|
13
13
|
a(() => {
|
|
14
|
-
if (!
|
|
14
|
+
if (!D) return;
|
|
15
15
|
let e = !1;
|
|
16
16
|
return (async () => {
|
|
17
|
-
|
|
17
|
+
N(!0), F(null);
|
|
18
18
|
try {
|
|
19
|
-
let t = await
|
|
19
|
+
let t = await h({
|
|
20
20
|
gateway: "global",
|
|
21
|
-
query:
|
|
22
|
-
variables: { id:
|
|
23
|
-
authToken:
|
|
21
|
+
query: C,
|
|
22
|
+
variables: { id: D },
|
|
23
|
+
authToken: E || void 0
|
|
24
24
|
});
|
|
25
25
|
if (e) return;
|
|
26
26
|
if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Failed to load order");
|
|
27
|
-
|
|
27
|
+
k(t.data?.getStoreOrder ?? null);
|
|
28
28
|
let n = new Set((t.data?.getStoreOrder?.lineItems ?? []).map((e) => e.productId));
|
|
29
|
-
|
|
29
|
+
j((t.data?.myEntitlements ?? []).filter((e) => n.has(e.productId)));
|
|
30
30
|
} catch (t) {
|
|
31
31
|
if (e) return;
|
|
32
|
-
|
|
32
|
+
F(t instanceof Error ? t.message : "Failed to load order");
|
|
33
33
|
} finally {
|
|
34
|
-
e ||
|
|
34
|
+
e || N(!1);
|
|
35
35
|
}
|
|
36
36
|
})(), () => {
|
|
37
37
|
e = !0;
|
|
38
38
|
};
|
|
39
|
-
}, [
|
|
40
|
-
let
|
|
39
|
+
}, [D, E]);
|
|
40
|
+
let R = async (e) => {
|
|
41
41
|
r("light");
|
|
42
42
|
try {
|
|
43
|
-
await n(e) ? (
|
|
43
|
+
await n(e) ? (L(e), i("success"), setTimeout(() => L(null), 1500)) : i("error");
|
|
44
44
|
} catch {
|
|
45
45
|
i("error");
|
|
46
46
|
}
|
|
47
|
-
},
|
|
47
|
+
}, z = (e, t) => {
|
|
48
48
|
let n = new URLSearchParams();
|
|
49
49
|
n.set("install", e), t && n.set("licenseKey", t), window.location.href = `/vibecontrols/agents?${n.toString()}`;
|
|
50
|
-
};
|
|
51
|
-
return /* @__PURE__ */
|
|
50
|
+
}, B = (e, n) => t(e, n || "USD"), V = O?.lineItems ?? [], H = V.some((e) => String(e.itemType ?? "").toLowerCase() === "physical"), U = V.some((e) => String(e.itemType ?? "").toLowerCase() !== "physical");
|
|
51
|
+
return /* @__PURE__ */ v("main", {
|
|
52
52
|
className: "mx-auto max-w-3xl p-6",
|
|
53
53
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ v(x, {
|
|
55
55
|
className: "rounded-2xl p-6 text-center",
|
|
56
56
|
children: [
|
|
57
|
-
/* @__PURE__ */
|
|
58
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ _(c, { className: "mx-auto size-12 text-status-success-text" }),
|
|
58
|
+
/* @__PURE__ */ _("h1", {
|
|
59
59
|
className: "mt-3 text-2xl font-bold text-text-primary",
|
|
60
60
|
children: "Order placed successfully"
|
|
61
61
|
}),
|
|
62
|
-
|
|
62
|
+
O && /* @__PURE__ */ v("p", {
|
|
63
63
|
className: "mt-1 text-sm tabular-nums text-text-secondary",
|
|
64
64
|
children: [
|
|
65
65
|
"Total: ",
|
|
66
|
-
(
|
|
67
|
-
|
|
66
|
+
B(O.total, O.currency),
|
|
67
|
+
O.purchasedAt && ` • ${new Date(O.purchasedAt).toLocaleString()}`
|
|
68
68
|
]
|
|
69
69
|
}),
|
|
70
|
-
|
|
70
|
+
D && /* @__PURE__ */ v("div", {
|
|
71
71
|
className: "mx-auto mt-4 max-w-sm rounded-lg border border-border-seam bg-surface-default px-4 py-2 text-left",
|
|
72
|
-
children: [/* @__PURE__ */
|
|
72
|
+
children: [/* @__PURE__ */ _("p", {
|
|
73
73
|
className: "text-xs uppercase tracking-wide text-text-tertiary",
|
|
74
74
|
children: "Order ID"
|
|
75
|
-
}), /* @__PURE__ */
|
|
75
|
+
}), /* @__PURE__ */ _("p", {
|
|
76
76
|
className: "mt-0.5 break-all font-mono text-xs text-text-primary",
|
|
77
|
-
children:
|
|
77
|
+
children: D
|
|
78
78
|
})]
|
|
79
79
|
})
|
|
80
80
|
]
|
|
81
81
|
}),
|
|
82
|
-
/* @__PURE__ */
|
|
82
|
+
/* @__PURE__ */ _("p", {
|
|
83
83
|
className: "mt-3 text-center text-xs text-text-tertiary",
|
|
84
|
-
children: "This purchase is final and non-refundable. Digital app orders cannot be cancelled once completed."
|
|
84
|
+
children: H ? "You'll receive shipping and tracking updates as your order is prepared and dispatched." : "This purchase is final and non-refundable. Digital app orders cannot be cancelled once completed."
|
|
85
85
|
}),
|
|
86
|
-
|
|
86
|
+
P && /* @__PURE__ */ _("div", {
|
|
87
87
|
role: "alert",
|
|
88
88
|
className: "mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
|
|
89
|
-
children:
|
|
89
|
+
children: P
|
|
90
90
|
}),
|
|
91
|
-
|
|
91
|
+
M ? /* @__PURE__ */ _("output", {
|
|
92
92
|
className: "mt-6 block py-12 text-center text-text-secondary",
|
|
93
|
-
children: "Loading
|
|
94
|
-
}) :
|
|
93
|
+
children: "Loading your order…"
|
|
94
|
+
}) : /* @__PURE__ */ v(g, { children: [H && /* @__PURE__ */ v("section", {
|
|
95
95
|
className: "mt-6",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
96
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
97
97
|
className: "mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
98
|
+
children: [/* @__PURE__ */ _(m, { className: "size-4" }), "Shipping"]
|
|
99
|
+
}), /* @__PURE__ */ v("div", {
|
|
100
|
+
className: "rounded-lg border border-border-seam bg-surface-raised p-4",
|
|
98
101
|
children: [
|
|
99
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ _("p", {
|
|
103
|
+
className: "text-sm text-text-secondary",
|
|
104
|
+
children: O?.fulfillment?.status && O.fulfillment.status !== "PENDING" ? `Status: ${((e) => e.replace(/_/g, " ").toLowerCase().replace(/^./, (e) => e.toUpperCase()))(O.fulfillment.status)}` : "Your order is being prepared for shipment. We'll share tracking details once it ships."
|
|
105
|
+
}),
|
|
106
|
+
O?.fulfillment?.estimatedDeliveryDate && /* @__PURE__ */ v("p", {
|
|
107
|
+
className: "mt-1 text-xs text-text-tertiary",
|
|
108
|
+
children: [
|
|
109
|
+
"Estimated delivery:",
|
|
110
|
+
" ",
|
|
111
|
+
new Date(O.fulfillment.estimatedDeliveryDate).toLocaleDateString()
|
|
112
|
+
]
|
|
113
|
+
}),
|
|
114
|
+
O?.fulfillment?.trackingUrl && /* @__PURE__ */ v("a", {
|
|
115
|
+
href: O.fulfillment.trackingUrl,
|
|
116
|
+
target: "_blank",
|
|
117
|
+
rel: "noreferrer",
|
|
118
|
+
className: "mt-3 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-xs font-semibold text-text-primary hover:bg-surface-hover",
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ _(f, { className: "size-3.5" }),
|
|
121
|
+
"Track package",
|
|
122
|
+
O.fulfillment.trackingNumber ? ` (${O.fulfillment.trackingNumber})` : ""
|
|
123
|
+
]
|
|
124
|
+
}),
|
|
125
|
+
/* @__PURE__ */ _("ul", {
|
|
126
|
+
className: "mt-4 space-y-2 border-t border-border-seam pt-3",
|
|
127
|
+
children: V.filter((e) => String(e.itemType ?? "").toLowerCase() === "physical").map((e, t) => /* @__PURE__ */ v("li", {
|
|
128
|
+
className: "flex items-center justify-between gap-3 text-sm",
|
|
129
|
+
children: [/* @__PURE__ */ v("span", {
|
|
130
|
+
className: "min-w-0 truncate text-text-primary",
|
|
131
|
+
children: [
|
|
132
|
+
e.name,
|
|
133
|
+
" × ",
|
|
134
|
+
e.quantity
|
|
135
|
+
]
|
|
136
|
+
}), /* @__PURE__ */ _("span", {
|
|
137
|
+
className: "tabular-nums text-text-secondary",
|
|
138
|
+
children: B(e.subtotal, O?.currency ?? "USD")
|
|
139
|
+
})]
|
|
140
|
+
}, e.productId ?? t))
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
})]
|
|
144
|
+
}), A.length > 0 && /* @__PURE__ */ v("div", {
|
|
145
|
+
className: "mt-6",
|
|
146
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
147
|
+
className: "mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ _(d, { className: "size-4" }),
|
|
100
150
|
"Your licenses (",
|
|
101
|
-
|
|
151
|
+
A.length,
|
|
102
152
|
")"
|
|
103
153
|
]
|
|
104
|
-
}), /* @__PURE__ */
|
|
154
|
+
}), /* @__PURE__ */ _("ul", {
|
|
105
155
|
className: "space-y-3",
|
|
106
|
-
children:
|
|
156
|
+
children: A.map((e) => /* @__PURE__ */ _("li", {
|
|
107
157
|
className: "rounded-lg border border-border-seam bg-surface-raised p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]",
|
|
108
|
-
children: /* @__PURE__ */
|
|
158
|
+
children: /* @__PURE__ */ v("div", {
|
|
109
159
|
className: "flex items-start gap-3",
|
|
110
160
|
children: [
|
|
111
|
-
e.product?.icon ? /* @__PURE__ */
|
|
161
|
+
e.product?.icon ? /* @__PURE__ */ _("img", {
|
|
112
162
|
src: e.product.icon,
|
|
113
163
|
alt: "",
|
|
114
164
|
className: "size-12 flex-shrink-0 rounded-md object-cover"
|
|
115
|
-
}) : /* @__PURE__ */
|
|
165
|
+
}) : /* @__PURE__ */ _("div", {
|
|
116
166
|
className: "flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary",
|
|
117
167
|
children: e.product?.name.slice(0, 2).toUpperCase() ?? "?"
|
|
118
168
|
}),
|
|
119
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ v("div", {
|
|
120
170
|
className: "min-w-0 flex-1",
|
|
121
171
|
children: [
|
|
122
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ _("h3", {
|
|
123
173
|
className: "text-base font-semibold text-text-primary",
|
|
124
174
|
children: e.product?.name ?? "Unknown product"
|
|
125
175
|
}),
|
|
126
|
-
e.expiresAt && /* @__PURE__ */
|
|
176
|
+
e.expiresAt && /* @__PURE__ */ v("p", {
|
|
127
177
|
className: "text-xs text-text-secondary",
|
|
128
178
|
children: ["Expires: ", new Date(e.expiresAt).toLocaleDateString()]
|
|
129
179
|
}),
|
|
130
|
-
e.licenseKey && /* @__PURE__ */
|
|
180
|
+
e.licenseKey && /* @__PURE__ */ v("div", {
|
|
131
181
|
className: "mt-2 flex items-center gap-2",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
182
|
+
children: [/* @__PURE__ */ _("code", {
|
|
133
183
|
className: "flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary",
|
|
134
184
|
children: e.licenseKey
|
|
135
|
-
}), /* @__PURE__ */
|
|
185
|
+
}), /* @__PURE__ */ _("button", {
|
|
136
186
|
type: "button",
|
|
137
|
-
onClick: () => void
|
|
187
|
+
onClick: () => void R(e.licenseKey),
|
|
138
188
|
className: "rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover",
|
|
139
189
|
title: "Copy license key",
|
|
140
|
-
children: /* @__PURE__ */
|
|
190
|
+
children: /* @__PURE__ */ _(l, { className: "size-3.5" })
|
|
141
191
|
})]
|
|
142
192
|
}),
|
|
143
|
-
|
|
193
|
+
I === e.licenseKey && /* @__PURE__ */ _("p", {
|
|
144
194
|
className: "mt-1 text-xs text-status-success-text",
|
|
145
195
|
children: "Copied to clipboard"
|
|
146
196
|
})
|
|
147
197
|
]
|
|
148
198
|
}),
|
|
149
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ v("button", {
|
|
150
200
|
type: "button",
|
|
151
|
-
onClick: () =>
|
|
201
|
+
onClick: () => z(e.productId, e.licenseKey),
|
|
152
202
|
className: "inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-semibold text-action-primary-text hover:opacity-90",
|
|
153
|
-
children: [/* @__PURE__ */
|
|
203
|
+
children: [/* @__PURE__ */ _(u, { className: "size-3.5" }), "Install to workspace"]
|
|
154
204
|
})
|
|
155
205
|
]
|
|
156
206
|
})
|
|
157
207
|
}, e.id))
|
|
158
208
|
})]
|
|
159
|
-
})
|
|
160
|
-
!
|
|
209
|
+
})] }),
|
|
210
|
+
!M && /* @__PURE__ */ _(S, {
|
|
161
211
|
className: "mt-6",
|
|
162
212
|
storageKey: "store-order-confirmation",
|
|
163
213
|
title: "What's next?",
|
|
164
214
|
steps: [
|
|
165
|
-
{
|
|
215
|
+
...U || A.length > 0 ? [{
|
|
166
216
|
id: "install",
|
|
167
217
|
label: "Install what you bought",
|
|
168
218
|
description: "Add each purchased app to a workspace using its license.",
|
|
169
|
-
icon: /* @__PURE__ */
|
|
219
|
+
icon: /* @__PURE__ */ _(u, { className: "size-4" }),
|
|
170
220
|
onClick: () => {
|
|
171
|
-
let e =
|
|
172
|
-
e &&
|
|
221
|
+
let e = A[0];
|
|
222
|
+
e && z(e.productId, e.licenseKey);
|
|
173
223
|
},
|
|
174
|
-
done:
|
|
175
|
-
},
|
|
224
|
+
done: A.length === 0
|
|
225
|
+
}] : [],
|
|
226
|
+
...H ? [{
|
|
227
|
+
id: "track",
|
|
228
|
+
label: "Track your order",
|
|
229
|
+
description: "Follow delivery status under My Orders.",
|
|
230
|
+
icon: /* @__PURE__ */ _(m, { className: "size-4" }),
|
|
231
|
+
onClick: () => w(`${T}/orders`)
|
|
232
|
+
}] : [],
|
|
176
233
|
{
|
|
177
234
|
id: "orders",
|
|
178
235
|
label: "Keep this receipt",
|
|
179
236
|
description: "Find this order any time under My Orders.",
|
|
180
|
-
icon: /* @__PURE__ */
|
|
181
|
-
onClick: () =>
|
|
237
|
+
icon: /* @__PURE__ */ _(p, { className: "size-4" }),
|
|
238
|
+
onClick: () => w(`${T}/orders`)
|
|
182
239
|
},
|
|
183
240
|
{
|
|
184
241
|
id: "shop",
|
|
185
242
|
label: "Discover more",
|
|
186
243
|
description: "Browse the marketplace for more apps and tools.",
|
|
187
|
-
icon: /* @__PURE__ */
|
|
188
|
-
onClick: () =>
|
|
244
|
+
icon: /* @__PURE__ */ _(s, { className: "size-4" }),
|
|
245
|
+
onClick: () => w(`${T}/marketplace`)
|
|
189
246
|
}
|
|
190
247
|
]
|
|
191
248
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ v("div", {
|
|
193
250
|
className: "mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
251
|
+
children: [/* @__PURE__ */ v("button", {
|
|
195
252
|
type: "button",
|
|
196
|
-
onClick: () =>
|
|
253
|
+
onClick: () => w(`${T}/orders`),
|
|
197
254
|
className: "cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text",
|
|
198
|
-
children: [/* @__PURE__ */
|
|
199
|
-
}), /* @__PURE__ */
|
|
255
|
+
children: [/* @__PURE__ */ _(p, { className: "size-4" }), "View My Orders"]
|
|
256
|
+
}), /* @__PURE__ */ v("button", {
|
|
200
257
|
type: "button",
|
|
201
|
-
onClick: () =>
|
|
258
|
+
onClick: () => w(`${T}/marketplace`),
|
|
202
259
|
className: "cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-semibold text-text-primary hover:bg-surface-hover",
|
|
203
|
-
children: ["Continue Shopping", /* @__PURE__ */
|
|
260
|
+
children: ["Continue Shopping", /* @__PURE__ */ _(s, { className: "size-4" })]
|
|
204
261
|
})]
|
|
205
262
|
})
|
|
206
263
|
]
|
|
207
264
|
});
|
|
208
265
|
};
|
|
209
266
|
//#endregion
|
|
210
|
-
export {
|
|
267
|
+
export { w as OrderConfirmationPage };
|
|
211
268
|
|
|
212
269
|
//# sourceMappingURL=OrderConfirmationPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderConfirmationPage.js","names":[],"sources":["../../src/pages/OrderConfirmationPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { CheckCircle2, Receipt, ArrowRight, Copy, Download, KeyRound } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeCopyText, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { formatPrice as formatCurrency } from '../utils';\n\ntype LineItem = {\n productId: string;\n name: string;\n quantity: number;\n unitPrice: number;\n subtotal: number;\n itemType?: string;\n};\n\ntype OrderDetails = {\n id: string;\n status: string;\n total: number;\n currency: string;\n purchasedAt?: string | null;\n lineItems: LineItem[];\n context?: Record<string, unknown> | null;\n};\n\ntype Entitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n expiresAt: string | null;\n features: string[];\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n};\n\nconst ORDER_QUERY = /* GraphQL */ `\n query OrderConfirmation($id: ID!) {\n getStoreOrder(id: $id) {\n id\n status\n total\n currency\n purchasedAt\n lineItems\n context\n }\n myEntitlements {\n id\n productId\n licenseKey\n expiresAt\n features\n product {\n id\n name\n slug\n icon\n manifest\n }\n }\n }\n`;\n\nexport const OrderConfirmationPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, authToken } = useStore();\n const { orderId } = useParams<{ orderId: string }>();\n const [order, setOrder] = useState<OrderDetails | null>(null);\n const [entitlements, setEntitlements] = useState<Entitlement[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n\n useEffect(() => {\n if (!orderId) return;\n let cancelled = false;\n const load = async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n getStoreOrder: OrderDetails | null;\n myEntitlements: Entitlement[];\n }>({\n gateway: 'global',\n query: ORDER_QUERY,\n variables: { id: orderId },\n authToken: authToken || undefined,\n });\n if (cancelled) return;\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load order');\n }\n setOrder(result.data?.getStoreOrder ?? null);\n // Filter entitlements to only those matching products in this order\n const productIds = new Set(\n (result.data?.getStoreOrder?.lineItems ?? []).map((item) => item.productId)\n );\n const orderEntitlements = (result.data?.myEntitlements ?? []).filter((e) =>\n productIds.has(e.productId)\n );\n setEntitlements(orderEntitlements);\n } catch (err) {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : 'Failed to load order');\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [orderId, authToken]);\n\n const copyKey = async (key: string) => {\n void nativeImpact('light');\n try {\n const ok = await nativeCopyText(key);\n if (ok) {\n setCopiedKey(key);\n void nativeNotify('success');\n setTimeout(() => setCopiedKey(null), 1500);\n } else {\n void nativeNotify('error');\n }\n } catch {\n void nativeNotify('error');\n }\n };\n\n const installInWorkspace = (productId: string, licenseKey: string | null) => {\n const params = new URLSearchParams();\n params.set('install', productId);\n if (licenseKey) params.set('licenseKey', licenseKey);\n // Navigate to the vibecontrols plugin install page via the top-level router.\n // Uses a plain anchor to avoid cross-MFE router imports.\n window.location.href = `/vibecontrols/agents?${params.toString()}`;\n };\n\n const formatPrice = (amount: number, currency: string) =>\n formatCurrency(amount, currency || 'USD');\n\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <EmphasisPanel className=\"rounded-2xl p-6 text-center\">\n <CheckCircle2 className=\"mx-auto size-12 text-status-success-text\" />\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">Order placed successfully</h1>\n {order && (\n <p className=\"mt-1 text-sm tabular-nums text-text-secondary\">\n Total: {formatPrice(order.total, order.currency)}\n {order.purchasedAt && ` • ${new Date(order.purchasedAt).toLocaleString()}`}\n </p>\n )}\n {orderId && (\n <div className=\"mx-auto mt-4 max-w-sm rounded-lg border border-border-seam bg-surface-default px-4 py-2 text-left\">\n <p className=\"text-xs uppercase tracking-wide text-text-tertiary\">Order ID</p>\n <p className=\"mt-0.5 break-all font-mono text-xs text-text-primary\">{orderId}</p>\n </div>\n )}\n </EmphasisPanel>\n\n <p className=\"mt-3 text-center text-xs text-text-tertiary\">\n This purchase is final and non-refundable. Digital app orders cannot be cancelled once\n completed.\n </p>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading ? (\n <output className=\"mt-6 block py-12 text-center text-text-secondary\">\n Loading licenses…\n </output>\n ) : entitlements.length > 0 ? (\n <div className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"size-4\" />\n Your licenses ({entitlements.length})\n </h2>\n <ul className=\"space-y-3\">\n {entitlements.map((ent) => (\n <li\n key={ent.id}\n className=\"rounded-lg border border-border-seam bg-surface-raised p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {ent.product?.name.slice(0, 2).toUpperCase() ?? '?'}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {ent.product?.name ?? 'Unknown product'}\n </h3>\n {ent.expiresAt && (\n <p className=\"text-xs text-text-secondary\">\n Expires: {new Date(ent.expiresAt).toLocaleDateString()}\n </p>\n )}\n {ent.licenseKey && (\n <div className=\"mt-2 flex items-center gap-2\">\n <code className=\"flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary\">\n {ent.licenseKey}\n </code>\n <button\n type=\"button\"\n onClick={() => void copyKey(ent.licenseKey!)}\n className=\"rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover\"\n title=\"Copy license key\"\n >\n <Copy className=\"size-3.5\" />\n </button>\n </div>\n )}\n {copiedKey === ent.licenseKey && (\n <p className=\"mt-1 text-xs text-status-success-text\">Copied to clipboard</p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => installInWorkspace(ent.productId, ent.licenseKey)}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-semibold text-action-primary-text hover:opacity-90\"\n >\n <Download className=\"size-3.5\" />\n Install to workspace\n </button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n\n {!loading && (\n <NextSteps\n className=\"mt-6\"\n storageKey=\"store-order-confirmation\"\n title=\"What's next?\"\n steps={[\n {\n id: 'install',\n label: 'Install what you bought',\n description: 'Add each purchased app to a workspace using its license.',\n icon: <Download className=\"size-4\" />,\n onClick: () => {\n const first = entitlements[0];\n if (first) installInWorkspace(first.productId, first.licenseKey);\n },\n done: entitlements.length === 0,\n },\n {\n id: 'orders',\n label: 'Keep this receipt',\n description: 'Find this order any time under My Orders.',\n icon: <Receipt className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n {\n id: 'shop',\n label: 'Discover more',\n description: 'Browse the marketplace for more apps and tools.',\n icon: <ArrowRight className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/marketplace`),\n },\n ]}\n />\n )}\n\n <div className=\"mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/orders`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Receipt className=\"size-4\" />\n View My Orders\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-semibold text-text-primary hover:bg-surface-hover\"\n >\n Continue Shopping\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OrderConfirmationPage.js","names":[],"sources":["../../src/pages/OrderConfirmationPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n CheckCircle2,\n Receipt,\n ArrowRight,\n Copy,\n Download,\n KeyRound,\n Truck,\n Package,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeCopyText, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { formatPrice as formatCurrency } from '../utils';\n\ntype LineItem = {\n productId: string;\n name: string;\n quantity: number;\n unitPrice: number;\n subtotal: number;\n itemType?: string;\n};\n\ntype OrderFulfillment = {\n status: string;\n trackingNumber: string | null;\n trackingUrl: string | null;\n carrier: string | null;\n estimatedDeliveryDate: string | null;\n};\n\ntype OrderDetails = {\n id: string;\n status: string;\n total: number;\n currency: string;\n purchasedAt?: string | null;\n lineItems: LineItem[];\n context?: Record<string, unknown> | null;\n fulfillment?: OrderFulfillment | null;\n};\n\ntype Entitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n expiresAt: string | null;\n features: string[];\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n};\n\nconst ORDER_QUERY = /* GraphQL */ `\n query OrderConfirmation($id: ID!) {\n getStoreOrder(id: $id) {\n id\n status\n total\n currency\n purchasedAt\n lineItems\n context\n fulfillment {\n status\n trackingNumber\n trackingUrl\n carrier\n estimatedDeliveryDate\n }\n }\n myEntitlements {\n id\n productId\n licenseKey\n expiresAt\n features\n product {\n id\n name\n slug\n icon\n manifest\n }\n }\n }\n`;\n\nexport const OrderConfirmationPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, authToken } = useStore();\n const { orderId } = useParams<{ orderId: string }>();\n const [order, setOrder] = useState<OrderDetails | null>(null);\n const [entitlements, setEntitlements] = useState<Entitlement[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n\n useEffect(() => {\n if (!orderId) return;\n let cancelled = false;\n const load = async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n getStoreOrder: OrderDetails | null;\n myEntitlements: Entitlement[];\n }>({\n gateway: 'global',\n query: ORDER_QUERY,\n variables: { id: orderId },\n authToken: authToken || undefined,\n });\n if (cancelled) return;\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load order');\n }\n setOrder(result.data?.getStoreOrder ?? null);\n // Filter entitlements to only those matching products in this order\n const productIds = new Set(\n (result.data?.getStoreOrder?.lineItems ?? []).map((item) => item.productId)\n );\n const orderEntitlements = (result.data?.myEntitlements ?? []).filter((e) =>\n productIds.has(e.productId)\n );\n setEntitlements(orderEntitlements);\n } catch (err) {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : 'Failed to load order');\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [orderId, authToken]);\n\n const copyKey = async (key: string) => {\n void nativeImpact('light');\n try {\n const ok = await nativeCopyText(key);\n if (ok) {\n setCopiedKey(key);\n void nativeNotify('success');\n setTimeout(() => setCopiedKey(null), 1500);\n } else {\n void nativeNotify('error');\n }\n } catch {\n void nativeNotify('error');\n }\n };\n\n const installInWorkspace = (productId: string, licenseKey: string | null) => {\n const params = new URLSearchParams();\n params.set('install', productId);\n if (licenseKey) params.set('licenseKey', licenseKey);\n // Navigate to the vibecontrols plugin install page via the top-level router.\n // Uses a plain anchor to avoid cross-MFE router imports.\n window.location.href = `/vibecontrols/agents?${params.toString()}`;\n };\n\n const formatPrice = (amount: number, currency: string) =>\n formatCurrency(amount, currency || 'USD');\n\n const lineItems = order?.lineItems ?? [];\n const hasPhysical = lineItems.some(\n (li) => String(li.itemType ?? '').toLowerCase() === 'physical'\n );\n const hasDigital = lineItems.some((li) => String(li.itemType ?? '').toLowerCase() !== 'physical');\n const formatFulfillmentStatus = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/^./, (c) => c.toUpperCase());\n\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <EmphasisPanel className=\"rounded-2xl p-6 text-center\">\n <CheckCircle2 className=\"mx-auto size-12 text-status-success-text\" />\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">Order placed successfully</h1>\n {order && (\n <p className=\"mt-1 text-sm tabular-nums text-text-secondary\">\n Total: {formatPrice(order.total, order.currency)}\n {order.purchasedAt && ` • ${new Date(order.purchasedAt).toLocaleString()}`}\n </p>\n )}\n {orderId && (\n <div className=\"mx-auto mt-4 max-w-sm rounded-lg border border-border-seam bg-surface-default px-4 py-2 text-left\">\n <p className=\"text-xs uppercase tracking-wide text-text-tertiary\">Order ID</p>\n <p className=\"mt-0.5 break-all font-mono text-xs text-text-primary\">{orderId}</p>\n </div>\n )}\n </EmphasisPanel>\n\n <p className=\"mt-3 text-center text-xs text-text-tertiary\">\n {hasPhysical\n ? \"You'll receive shipping and tracking updates as your order is prepared and dispatched.\"\n : 'This purchase is final and non-refundable. Digital app orders cannot be cancelled once completed.'}\n </p>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading ? (\n <output className=\"mt-6 block py-12 text-center text-text-secondary\">\n Loading your order…\n </output>\n ) : (\n <>\n {hasPhysical && (\n <section className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Truck className=\"size-4\" />\n Shipping\n </h2>\n <div className=\"rounded-lg border border-border-seam bg-surface-raised p-4\">\n <p className=\"text-sm text-text-secondary\">\n {order?.fulfillment?.status && order.fulfillment.status !== 'PENDING'\n ? `Status: ${formatFulfillmentStatus(order.fulfillment.status)}`\n : \"Your order is being prepared for shipment. We'll share tracking details once it ships.\"}\n </p>\n {order?.fulfillment?.estimatedDeliveryDate && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Estimated delivery:{' '}\n {new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString()}\n </p>\n )}\n {order?.fulfillment?.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-xs font-semibold text-text-primary hover:bg-surface-hover\"\n >\n <Package className=\"size-3.5\" />\n Track package\n {order.fulfillment.trackingNumber\n ? ` (${order.fulfillment.trackingNumber})`\n : ''}\n </a>\n )}\n <ul className=\"mt-4 space-y-2 border-t border-border-seam pt-3\">\n {lineItems\n .filter((li) => String(li.itemType ?? '').toLowerCase() === 'physical')\n .map((li, idx) => (\n <li\n key={li.productId ?? idx}\n className=\"flex items-center justify-between gap-3 text-sm\"\n >\n <span className=\"min-w-0 truncate text-text-primary\">\n {li.name} × {li.quantity}\n </span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatPrice(li.subtotal, order?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n </div>\n </section>\n )}\n {entitlements.length > 0 && (\n <div className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"size-4\" />\n Your licenses ({entitlements.length})\n </h2>\n <ul className=\"space-y-3\">\n {entitlements.map((ent) => (\n <li\n key={ent.id}\n className=\"rounded-lg border border-border-seam bg-surface-raised p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {ent.product?.name.slice(0, 2).toUpperCase() ?? '?'}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {ent.product?.name ?? 'Unknown product'}\n </h3>\n {ent.expiresAt && (\n <p className=\"text-xs text-text-secondary\">\n Expires: {new Date(ent.expiresAt).toLocaleDateString()}\n </p>\n )}\n {ent.licenseKey && (\n <div className=\"mt-2 flex items-center gap-2\">\n <code className=\"flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary\">\n {ent.licenseKey}\n </code>\n <button\n type=\"button\"\n onClick={() => void copyKey(ent.licenseKey!)}\n className=\"rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover\"\n title=\"Copy license key\"\n >\n <Copy className=\"size-3.5\" />\n </button>\n </div>\n )}\n {copiedKey === ent.licenseKey && (\n <p className=\"mt-1 text-xs text-status-success-text\">\n Copied to clipboard\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => installInWorkspace(ent.productId, ent.licenseKey)}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-semibold text-action-primary-text hover:opacity-90\"\n >\n <Download className=\"size-3.5\" />\n Install to workspace\n </button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n )}\n </>\n )}\n\n {!loading && (\n <NextSteps\n className=\"mt-6\"\n storageKey=\"store-order-confirmation\"\n title=\"What's next?\"\n steps={[\n ...(hasDigital || entitlements.length > 0\n ? [\n {\n id: 'install',\n label: 'Install what you bought',\n description: 'Add each purchased app to a workspace using its license.',\n icon: <Download className=\"size-4\" />,\n onClick: () => {\n const first = entitlements[0];\n if (first) installInWorkspace(first.productId, first.licenseKey);\n },\n done: entitlements.length === 0,\n },\n ]\n : []),\n ...(hasPhysical\n ? [\n {\n id: 'track',\n label: 'Track your order',\n description: 'Follow delivery status under My Orders.',\n icon: <Truck className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n ]\n : []),\n {\n id: 'orders',\n label: 'Keep this receipt',\n description: 'Find this order any time under My Orders.',\n icon: <Receipt className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n {\n id: 'shop',\n label: 'Discover more',\n description: 'Browse the marketplace for more apps and tools.',\n icon: <ArrowRight className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/marketplace`),\n },\n ]}\n />\n )}\n\n <div className=\"mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/orders`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Receipt className=\"size-4\" />\n View My Orders\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-semibold text-text-primary hover:bg-surface-hover\"\n >\n Continue Shopping\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
|