@burdenoff/microfe-store 2026.525.3 → 2026.528.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.
Files changed (69) hide show
  1. package/dist/StoreAdminRoutes.js +10 -9
  2. package/dist/StoreAdminRoutes.js.map +1 -1
  3. package/dist/StoreRoutes.js +2 -1
  4. package/dist/StoreRoutes.js.map +1 -1
  5. package/dist/components/AccessDenied.js +23 -0
  6. package/dist/components/AccessDenied.js.map +1 -0
  7. package/dist/components/InstallAppModal.js +183 -129
  8. package/dist/components/InstallAppModal.js.map +1 -1
  9. package/dist/components/PermissionGuard.js +8 -0
  10. package/dist/components/PermissionGuard.js.map +1 -0
  11. package/dist/components/RouteGuards.js +7 -21
  12. package/dist/components/RouteGuards.js.map +1 -1
  13. package/dist/components/cart/CartDrawer.js +20 -14
  14. package/dist/components/cart/CartDrawer.js.map +1 -1
  15. package/dist/hooks/useStoreGraphQL.js +22 -20
  16. package/dist/hooks/useStoreGraphQL.js.map +1 -1
  17. package/dist/hooks/useStoreGraphQLWithContext.js.map +1 -1
  18. package/dist/hooks/useStorePermissions.js +4 -1
  19. package/dist/hooks/useStorePermissions.js.map +1 -1
  20. package/dist/index.js +4 -4
  21. package/dist/pages/AdminDashboardPage.js +182 -183
  22. package/dist/pages/AdminDashboardPage.js.map +1 -1
  23. package/dist/pages/AdminReviewModerationPage.js +7 -6
  24. package/dist/pages/AdminReviewModerationPage.js.map +1 -1
  25. package/dist/pages/AdminStoresPage.js +193 -185
  26. package/dist/pages/AdminStoresPage.js.map +1 -1
  27. package/dist/pages/AdminSubmissionsQueuePage.js +140 -142
  28. package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
  29. package/dist/pages/AppDetailPage.js +6 -3
  30. package/dist/pages/AppDetailPage.js.map +1 -1
  31. package/dist/pages/AppSettingsPage.js +6 -3
  32. package/dist/pages/AppSettingsPage.js.map +1 -1
  33. package/dist/pages/CartPage.js +23 -15
  34. package/dist/pages/CartPage.js.map +1 -1
  35. package/dist/pages/InstalledAppsPage.js +26 -15
  36. package/dist/pages/InstalledAppsPage.js.map +1 -1
  37. package/dist/pages/MarketplaceHomePage.js +275 -247
  38. package/dist/pages/MarketplaceHomePage.js.map +1 -1
  39. package/dist/pages/MyLicensesPage.js +10 -8
  40. package/dist/pages/MyLicensesPage.js.map +1 -1
  41. package/dist/pages/OrderConfirmationPage.js +72 -75
  42. package/dist/pages/OrderConfirmationPage.js.map +1 -1
  43. package/dist/pages/OrdersPage.js +22 -20
  44. package/dist/pages/OrdersPage.js.map +1 -1
  45. package/dist/pages/ProductCategoryPage.js +3 -1
  46. package/dist/pages/ProductCategoryPage.js.map +1 -1
  47. package/dist/pages/ProductDetailPage.js +458 -443
  48. package/dist/pages/ProductDetailPage.js.map +1 -1
  49. package/dist/pages/ProductReviewsPage.js +4 -3
  50. package/dist/pages/ProductReviewsPage.js.map +1 -1
  51. package/dist/pages/PublisherDashboardPage.js +13 -7
  52. package/dist/pages/PublisherDashboardPage.js.map +1 -1
  53. package/dist/pages/PublisherRevenuePage.js +7 -5
  54. package/dist/pages/PublisherRevenuePage.js.map +1 -1
  55. package/dist/pages/PublisherSubmissionsPage.js +12 -7
  56. package/dist/pages/PublisherSubmissionsPage.js.map +1 -1
  57. package/dist/pages/PublisherSubmitPage.js +6 -0
  58. package/dist/pages/PublisherSubmitPage.js.map +1 -1
  59. package/dist/pages/SearchResultsPage.js +6 -3
  60. package/dist/pages/SearchResultsPage.js.map +1 -1
  61. package/dist/pages/StoresPage.js +73 -74
  62. package/dist/pages/StoresPage.js.map +1 -1
  63. package/dist/providers/StoreProvider.js +25 -25
  64. package/dist/providers/StoreProvider.js.map +1 -1
  65. package/dist/utils/index.js +29 -19
  66. package/dist/utils/index.js.map +1 -1
  67. package/package.json +1 -1
  68. package/dist/components/index.js +0 -3
  69. package/dist/components/marketplace/ProductCard.js +0 -2
@@ -1,178 +1,175 @@
1
1
  import { useStore as e } from "../providers/StoreProvider.js";
2
- import { nativeCopyText as t, nativeImpact as n, nativeNotify as r } from "../utils/nativeBridge.js";
3
- import { useEffect as i, useState as a } from "react";
4
- import { ArrowRight as o, CheckCircle2 as s, Copy as c, Download as l, KeyRound as u, Receipt as d } from "lucide-react";
5
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
6
- import { useNavigate as m, useParams as h } from "react-router-dom";
7
- import { graphqlFetch as g } from "@burdenoff/fe-libs/shared/graphql";
2
+ import { formatPrice as t } from "../utils/index.js";
3
+ import { nativeCopyText as n, nativeImpact as r, nativeNotify as i } from "../utils/nativeBridge.js";
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, Receipt as f } from "lucide-react";
6
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
7
+ import { useNavigate as h, useParams as g } from "react-router-dom";
8
+ import { graphqlFetch as _ } from "@burdenoff/fe-libs/shared/graphql";
8
9
  //#region src/pages/OrderConfirmationPage.tsx
9
- var _ = "\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", v = () => {
10
- let v = m(), { basePath: y, authToken: b } = e(), { orderId: x } = h(), [S, C] = a(null), [w, T] = a([]), [E, D] = a(!0), [O, k] = a(null), [A, j] = a(null);
11
- i(() => {
12
- if (!x) return;
10
+ var v = "\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", y = () => {
11
+ let y = h(), { basePath: b, authToken: x } = e(), { orderId: S } = g(), [C, w] = o(null), [T, E] = o([]), [D, O] = o(!0), [k, A] = o(null), [j, M] = o(null);
12
+ a(() => {
13
+ if (!S) return;
13
14
  let e = !1;
14
15
  return (async () => {
15
- D(!0), k(null);
16
+ O(!0), A(null);
16
17
  try {
17
- let t = await g({
18
+ let t = await _({
18
19
  gateway: "global",
19
- query: _,
20
- variables: { id: x },
21
- authToken: b || void 0
20
+ query: v,
21
+ variables: { id: S },
22
+ authToken: x || void 0
22
23
  });
23
24
  if (e) return;
24
25
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Failed to load order");
25
- C(t.data?.getStoreOrder ?? null);
26
+ w(t.data?.getStoreOrder ?? null);
26
27
  let n = new Set((t.data?.getStoreOrder?.lineItems ?? []).map((e) => e.productId));
27
- T((t.data?.myEntitlements ?? []).filter((e) => n.has(e.productId)));
28
+ E((t.data?.myEntitlements ?? []).filter((e) => n.has(e.productId)));
28
29
  } catch (t) {
29
30
  if (e) return;
30
- k(t instanceof Error ? t.message : "Failed to load order");
31
+ A(t instanceof Error ? t.message : "Failed to load order");
31
32
  } finally {
32
- e || D(!1);
33
+ e || O(!1);
33
34
  }
34
35
  })(), () => {
35
36
  e = !0;
36
37
  };
37
- }, [x, b]);
38
- let M = async (e) => {
39
- n("light");
38
+ }, [S, x]);
39
+ let N = async (e) => {
40
+ r("light");
40
41
  try {
41
- await t(e) ? (j(e), r("success"), setTimeout(() => j(null), 1500)) : r("error");
42
+ await n(e) ? (M(e), i("success"), setTimeout(() => M(null), 1500)) : i("error");
42
43
  } catch {
43
- r("error");
44
+ i("error");
44
45
  }
45
- }, N = (e, t) => {
46
+ }, P = (e, t) => {
46
47
  let n = new URLSearchParams();
47
48
  n.set("install", e), t && n.set("licenseKey", t), window.location.href = `/vibecontrols/agents?${n.toString()}`;
48
49
  };
49
- return /* @__PURE__ */ p("main", {
50
+ return /* @__PURE__ */ m("main", {
50
51
  className: "mx-auto max-w-3xl p-6",
51
52
  children: [
52
- /* @__PURE__ */ p("div", {
53
+ /* @__PURE__ */ m("div", {
53
54
  className: "rounded-2xl border border-border-default bg-surface-raised p-6 text-center",
54
55
  children: [
55
- /* @__PURE__ */ f(s, { className: "mx-auto h-12 w-12 text-status-success-text" }),
56
- /* @__PURE__ */ f("h1", {
56
+ /* @__PURE__ */ p(c, { className: "mx-auto size-12 text-status-success-text" }),
57
+ /* @__PURE__ */ p("h1", {
57
58
  className: "mt-3 text-2xl font-bold text-text-primary",
58
59
  children: "Order placed successfully"
59
60
  }),
60
- S && /* @__PURE__ */ p("p", {
61
+ C && /* @__PURE__ */ m("p", {
61
62
  className: "mt-1 text-sm text-text-secondary",
62
63
  children: [
63
64
  "Total: ",
64
- ((e, t) => new Intl.NumberFormat(void 0, {
65
- style: "currency",
66
- currency: t || "USD"
67
- }).format(e))(S.total, S.currency),
68
- S.purchasedAt && ` • ${new Date(S.purchasedAt).toLocaleString()}`
65
+ ((e, n) => t(e, n || "USD"))(C.total, C.currency),
66
+ C.purchasedAt && ` • ${new Date(C.purchasedAt).toLocaleString()}`
69
67
  ]
70
68
  }),
71
- x && /* @__PURE__ */ p("div", {
69
+ S && /* @__PURE__ */ m("div", {
72
70
  className: "mx-auto mt-4 max-w-sm rounded-lg border border-border-default bg-surface-default px-4 py-2 text-left",
73
- children: [/* @__PURE__ */ f("p", {
71
+ children: [/* @__PURE__ */ p("p", {
74
72
  className: "text-xs uppercase tracking-wide text-text-tertiary",
75
73
  children: "Order ID"
76
- }), /* @__PURE__ */ f("p", {
74
+ }), /* @__PURE__ */ p("p", {
77
75
  className: "mt-0.5 break-all font-mono text-xs text-text-primary",
78
- children: x
76
+ children: S
79
77
  })]
80
78
  })
81
79
  ]
82
80
  }),
83
- O && /* @__PURE__ */ f("div", {
81
+ k && /* @__PURE__ */ p("div", {
84
82
  role: "alert",
85
83
  className: "mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
86
- children: O
84
+ children: k
87
85
  }),
88
- E ? /* @__PURE__ */ f("div", {
89
- role: "status",
90
- className: "mt-6 py-12 text-center text-text-secondary",
86
+ D ? /* @__PURE__ */ p("output", {
87
+ className: "mt-6 block py-12 text-center text-text-secondary",
91
88
  children: "Loading licenses…"
92
- }) : w.length > 0 ? /* @__PURE__ */ p("div", {
89
+ }) : T.length > 0 ? /* @__PURE__ */ m("div", {
93
90
  className: "mt-6",
94
- children: [/* @__PURE__ */ p("h2", {
91
+ children: [/* @__PURE__ */ m("h2", {
95
92
  className: "mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary",
96
93
  children: [
97
- /* @__PURE__ */ f(u, { className: "h-4 w-4" }),
94
+ /* @__PURE__ */ p(d, { className: "size-4" }),
98
95
  "Your licenses (",
99
- w.length,
96
+ T.length,
100
97
  ")"
101
98
  ]
102
- }), /* @__PURE__ */ f("ul", {
99
+ }), /* @__PURE__ */ p("ul", {
103
100
  className: "space-y-3",
104
- children: w.map((e) => /* @__PURE__ */ f("li", {
101
+ children: T.map((e) => /* @__PURE__ */ p("li", {
105
102
  className: "rounded-lg border border-border-default bg-surface-raised p-4",
106
- children: /* @__PURE__ */ p("div", {
103
+ children: /* @__PURE__ */ m("div", {
107
104
  className: "flex items-start gap-3",
108
105
  children: [
109
- e.product?.icon ? /* @__PURE__ */ f("img", {
106
+ e.product?.icon ? /* @__PURE__ */ p("img", {
110
107
  src: e.product.icon,
111
108
  alt: "",
112
- className: "h-12 w-12 flex-shrink-0 rounded-md object-cover"
113
- }) : /* @__PURE__ */ f("div", {
114
- className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary",
109
+ className: "size-12 flex-shrink-0 rounded-md object-cover"
110
+ }) : /* @__PURE__ */ p("div", {
111
+ className: "flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary",
115
112
  children: e.product?.name.slice(0, 2).toUpperCase() ?? "?"
116
113
  }),
117
- /* @__PURE__ */ p("div", {
114
+ /* @__PURE__ */ m("div", {
118
115
  className: "min-w-0 flex-1",
119
116
  children: [
120
- /* @__PURE__ */ f("h3", {
117
+ /* @__PURE__ */ p("h3", {
121
118
  className: "text-base font-semibold text-text-primary",
122
119
  children: e.product?.name ?? "Unknown product"
123
120
  }),
124
- e.expiresAt && /* @__PURE__ */ p("p", {
121
+ e.expiresAt && /* @__PURE__ */ m("p", {
125
122
  className: "text-xs text-text-secondary",
126
123
  children: ["Expires: ", new Date(e.expiresAt).toLocaleDateString()]
127
124
  }),
128
- e.licenseKey && /* @__PURE__ */ p("div", {
125
+ e.licenseKey && /* @__PURE__ */ m("div", {
129
126
  className: "mt-2 flex items-center gap-2",
130
- children: [/* @__PURE__ */ f("code", {
127
+ children: [/* @__PURE__ */ p("code", {
131
128
  className: "flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary",
132
129
  children: e.licenseKey
133
- }), /* @__PURE__ */ f("button", {
130
+ }), /* @__PURE__ */ p("button", {
134
131
  type: "button",
135
- onClick: () => void M(e.licenseKey),
132
+ onClick: () => void N(e.licenseKey),
136
133
  className: "rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover",
137
134
  title: "Copy license key",
138
- children: /* @__PURE__ */ f(c, { className: "h-3.5 w-3.5" })
135
+ children: /* @__PURE__ */ p(l, { className: "size-3.5" })
139
136
  })]
140
137
  }),
141
- A === e.licenseKey && /* @__PURE__ */ f("p", {
138
+ j === e.licenseKey && /* @__PURE__ */ p("p", {
142
139
  className: "mt-1 text-xs text-status-success-text",
143
140
  children: "Copied to clipboard"
144
141
  })
145
142
  ]
146
143
  }),
147
- /* @__PURE__ */ p("button", {
144
+ /* @__PURE__ */ m("button", {
148
145
  type: "button",
149
- onClick: () => N(e.productId, e.licenseKey),
146
+ onClick: () => P(e.productId, e.licenseKey),
150
147
  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",
151
- children: [/* @__PURE__ */ f(l, { className: "h-3.5 w-3.5" }), "Install to workspace"]
148
+ children: [/* @__PURE__ */ p(u, { className: "size-3.5" }), "Install to workspace"]
152
149
  })
153
150
  ]
154
151
  })
155
152
  }, e.id))
156
153
  })]
157
154
  }) : null,
158
- /* @__PURE__ */ p("div", {
155
+ /* @__PURE__ */ m("div", {
159
156
  className: "mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center",
160
- children: [/* @__PURE__ */ p("button", {
157
+ children: [/* @__PURE__ */ m("button", {
161
158
  type: "button",
162
- onClick: () => v(`${y}/orders`),
159
+ onClick: () => y(`${b}/orders`),
163
160
  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",
164
- children: [/* @__PURE__ */ f(d, { className: "h-4 w-4" }), "View My Orders"]
165
- }), /* @__PURE__ */ p("button", {
161
+ children: [/* @__PURE__ */ p(f, { className: "size-4" }), "View My Orders"]
162
+ }), /* @__PURE__ */ m("button", {
166
163
  type: "button",
167
- onClick: () => v(`${y}/marketplace`),
164
+ onClick: () => y(`${b}/marketplace`),
168
165
  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",
169
- children: ["Continue Shopping", /* @__PURE__ */ f(o, { className: "h-4 w-4" })]
166
+ children: ["Continue Shopping", /* @__PURE__ */ p(s, { className: "size-4" })]
170
167
  })]
171
168
  })
172
169
  ]
173
170
  });
174
171
  };
175
172
  //#endregion
176
- export { v as OrderConfirmationPage };
173
+ export { y as OrderConfirmationPage };
177
174
 
178
175
  //# 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 { useStore } from '../providers/StoreProvider';\nimport { nativeCopyText, nativeImpact, nativeNotify } from '../utils/nativeBridge';\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 new Intl.NumberFormat(undefined, { style: 'currency', currency: currency || 'USD' }).format(\n amount\n );\n\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <div className=\"rounded-2xl border border-border-default bg-surface-raised p-6 text-center\">\n <CheckCircle2 className=\"mx-auto h-12 w-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 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-default 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 </div>\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 <div role=\"status\" className=\"mt-6 py-12 text-center text-text-secondary\">\n Loading licenses…\n </div>\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=\"h-4 w-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-default bg-surface-raised p-4\"\n >\n <div className=\"flex items-start gap-3\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"h-12 w-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex h-12 w-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=\"h-3.5 w-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=\"h-3.5 w-3.5\" />\n Install to workspace\n </button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n ) : null}\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=\"h-4 w-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=\"h-4 w-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 { CheckCircle2, Receipt, ArrowRight, Copy, Download, KeyRound } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\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 <div className=\"rounded-2xl border border-border-default bg-surface-raised 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 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-default 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 </div>\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-default bg-surface-raised p-4\"\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 <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":";;;;;;;;;AA2CA,IAAM,IAA4B,4YA4BrB,UAAkC;CAC7C,IAAM,IAAW,GAAa,EACxB,EAAE,aAAU,iBAAc,GAAU,EACpC,EAAE,eAAY,GAAgC,EAC9C,CAAC,GAAO,KAAY,EAA8B,KAAK,EACvD,CAAC,GAAc,KAAmB,EAAwB,EAAE,CAAC,EAC7D,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAW,KAAgB,EAAwB,KAAK;AAE/D,SAAgB;AACd,MAAI,CAAC,EAAS;EACd,IAAI,IAAY;AAmChB,UAlCa,YAAY;AAEvB,GADA,EAAW,GAAK,EAChB,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAS,MAAM,EAGlB;KACD,SAAS;KACT,OAAO;KACP,WAAW,EAAE,IAAI,GAAS;KAC1B,WAAW,KAAa,KAAA;KACzB,CAAC;AACF,QAAI,EAAW;AACf,QAAI,EAAO,QAAQ,OACjB,OAAU,MAAM,EAAO,OAAO,IAAI,WAAW,uBAAuB;AAEtE,MAAS,EAAO,MAAM,iBAAiB,KAAK;IAE5C,IAAM,IAAa,IAAI,KACpB,EAAO,MAAM,eAAe,aAAa,EAAE,EAAE,KAAK,MAAS,EAAK,UAAU,CAC5E;AAID,OAH2B,EAAO,MAAM,kBAAkB,EAAE,EAAE,QAAQ,MACpE,EAAW,IAAI,EAAE,UAAU,CAC5B,CACiC;YAC3B,GAAK;AACZ,QAAI,EAAW;AACf,MAAS,aAAe,QAAQ,EAAI,UAAU,uBAAuB;aAC7D;AACR,IAAK,KAAW,EAAW,GAAM;;MAG1B,QACE;AACX,OAAY;;IAEb,CAAC,GAAS,EAAU,CAAC;CAExB,IAAM,IAAU,OAAO,MAAgB;AAChC,IAAa,QAAQ;AAC1B,MAAI;AAEF,GADW,MAAM,EAAe,EAAI,IAElC,EAAa,EAAI,EACZ,EAAa,UAAU,EAC5B,iBAAiB,EAAa,KAAK,EAAE,KAAK,IAErC,EAAa,QAAQ;UAEtB;AACD,KAAa,QAAQ;;IAIxB,KAAsB,GAAmB,MAA8B;EAC3E,IAAM,IAAS,IAAI,iBAAiB;AAKpC,EAJA,EAAO,IAAI,WAAW,EAAU,EAC5B,KAAY,EAAO,IAAI,cAAc,EAAW,EAGpD,OAAO,SAAS,OAAO,wBAAwB,EAAO,UAAU;;AAMlE,QACE,kBAAC,QAAD;EAAM,WAAU;YAAhB;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAc,WAAU,4CAA6C,CAAA;KACrE,kBAAC,MAAD;MAAI,WAAU;gBAA4C;MAA8B,CAAA;KACvF,KACC,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAAgD;SATnC,GAAgB,MACnC,EAAe,GAAQ,KAAY,MAAM,EASb,EAAM,OAAO,EAAM,SAAS;OAC/C,EAAM,eAAe,MAAM,IAAI,KAAK,EAAM,YAAY,CAAC,gBAAgB;OACtE;;KAEL,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAqD;OAAY,CAAA,EAC9E,kBAAC,KAAD;OAAG,WAAU;iBAAwD;OAAY,CAAA,CAC7E;;KAEJ;;GAEL,KACC,kBAAC,OAAD;IACE,MAAK;IACL,WAAU;cAET;IACG,CAAA;GAGP,IACC,kBAAC,UAAD;IAAQ,WAAU;cAAmD;IAE5D,CAAA,GACP,EAAa,SAAS,IACxB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;;MACf,EAAa;MAAO;MACjC;QACL,kBAAC,MAAD;KAAI,WAAU;eACX,EAAa,KAAK,MACjB,kBAAC,MAAD;MAEE,WAAU;gBAEV,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,EAAI,SAAS,OACZ,kBAAC,OAAD;SACE,KAAK,EAAI,QAAQ;SACjB,KAAI;SACJ,WAAU;SACV,CAAA,GAEF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAI,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,aAAa,IAAI;SAC5C,CAAA;QAER,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAI,SAAS,QAAQ;WACnB,CAAA;UACJ,EAAI,aACH,kBAAC,KAAD;WAAG,WAAU;qBAAb,CAA2C,aAC/B,IAAI,KAAK,EAAI,UAAU,CAAC,oBAAoB,CACpD;;UAEL,EAAI,cACH,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAI;YACA,CAAA,EACP,kBAAC,UAAD;YACE,MAAK;YACL,eAAe,KAAK,EAAQ,EAAI,WAAY;YAC5C,WAAU;YACV,OAAM;sBAEN,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA;YACtB,CAAA,CACL;;UAEP,MAAc,EAAI,cACjB,kBAAC,KAAD;WAAG,WAAU;qBAAwC;WAAuB,CAAA;UAE1E;;QACN,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAmB,EAAI,WAAW,EAAI,WAAW;SAChE,WAAU;mBAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAAA,uBAE1B;;QACL;;MACH,EApDE,EAAI,GAoDN,CACL;KACC,CAAA,CACD;QACJ;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,GAAG,EAAS,SAAS;KAC7C,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA,EAAA,iBAEvB;QACT,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,GAAG,EAAS,cAAc;KAClD,WAAU;eAHZ,CAIC,qBAEC,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA,CAC1B;OACL;;GACD"}
@@ -4,7 +4,6 @@ import { formatPrice as n } from "../utils/index.js";
4
4
  import { useInstallations as r } from "../hooks/useInstallations.js";
5
5
  import { useOrders as i } from "../hooks/useOrders.js";
6
6
  import { InstallAppModal as a } from "../components/InstallAppModal.js";
7
- import "../components/index.js";
8
7
  import { useMemo as o, useState as s } from "react";
9
8
  import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
9
  import { ArrowLeft as l, CheckCircle as u, Clock as d, Download as f, Loader2 as p, Package as m, RefreshCw as h, ShoppingBag as g, XCircle as _ } from "lucide-react";
@@ -57,13 +56,13 @@ var S = (e) => {
57
56
  className: "flex items-center gap-4 rounded-lg border border-border-default bg-bg-sunken/50 p-4",
58
57
  children: [
59
58
  /* @__PURE__ */ y("div", {
60
- className: "h-14 w-14 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
59
+ className: "size-14 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
61
60
  children: e.productIcon ? /* @__PURE__ */ y("img", {
62
61
  src: e.productIcon,
63
62
  alt: e.productName,
64
- className: "h-full w-full object-cover"
63
+ className: "size-full object-cover"
65
64
  }) : /* @__PURE__ */ y("div", {
66
- className: "flex h-full w-full items-center justify-center text-lg font-bold text-text-muted",
65
+ className: "flex size-full items-center justify-center text-lg font-bold text-text-muted",
67
66
  children: e.productName.charAt(0)
68
67
  })
69
68
  }),
@@ -98,12 +97,13 @@ var S = (e) => {
98
97
  className: "flex-shrink-0",
99
98
  children: e.installed ? /* @__PURE__ */ b("span", {
100
99
  className: "inline-flex items-center gap-1.5 rounded-lg bg-status-success-bg-subtle/10 px-4 py-2 text-sm font-medium text-status-success-text",
101
- children: [/* @__PURE__ */ y(u, { className: "h-4 w-4" }), "Installed"]
100
+ children: [/* @__PURE__ */ y(u, { className: "size-4" }), "Installed"]
102
101
  }) : o && s ? /* @__PURE__ */ y("button", {
102
+ type: "button",
103
103
  onClick: () => i(t, e),
104
104
  disabled: a,
105
105
  className: "inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50",
106
- children: a ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "h-4 w-4 animate-spin" }), "Installing..."] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(f, { className: "h-4 w-4" }), "Install App"] })
106
+ children: a ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "size-4 animate-spin" }), "Installing"] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(f, { className: "size-4" }), "Install App"] })
107
107
  }) : null
108
108
  })
109
109
  ]
@@ -122,7 +122,7 @@ var S = (e) => {
122
122
  children: e.orderNumber
123
123
  }), /* @__PURE__ */ b("span", {
124
124
  className: `inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold ${i.color}`,
125
- children: [/* @__PURE__ */ y(a, { className: "h-4 w-4" }), i.label]
125
+ children: [/* @__PURE__ */ y(a, { className: "size-4" }), i.label]
126
126
  })]
127
127
  }), /* @__PURE__ */ y("p", {
128
128
  className: "text-sm text-text-muted mt-1",
@@ -192,7 +192,7 @@ var S = (e) => {
192
192
  console.warn("OrderStatusUpdated subscription error:", e.message);
193
193
  }
194
194
  });
195
- let [O, k] = s(!1), [A, j] = s(null), [M, N] = s(null), [P, F] = s(null), I = o(() => new Set(E.filter((e) => e.status === "ACTIVE").map((e) => e.productId)), [E]), L = f.map((e) => {
195
+ let [O, k] = s(!1), [A, j] = s(null), [M, N] = s(null), [P, F] = s(null), I = o(() => new Set(E.flatMap((e) => e.status === "ACTIVE" ? [e.productId] : [])), [E]), L = f.map((e) => {
196
196
  let t = e.lineItems || [], n = t.reduce((e, t) => e + t.subtotal, 0), r = e.total - n, i = t.map((t, n) => ({
197
197
  id: `${e.id}-${n}`,
198
198
  productId: t.productId,
@@ -242,14 +242,15 @@ var S = (e) => {
242
242
  children: /* @__PURE__ */ b("div", {
243
243
  className: "flex items-center gap-4",
244
244
  children: [/* @__PURE__ */ y("button", {
245
+ type: "button",
245
246
  onClick: () => n(-1),
246
247
  "aria-label": "Go back",
247
248
  className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
248
- children: /* @__PURE__ */ y(l, { className: "h-5 w-5" })
249
+ children: /* @__PURE__ */ y(l, { className: "size-5" })
249
250
  }), /* @__PURE__ */ b("div", {
250
251
  className: "flex items-center gap-3",
251
252
  children: [
252
- /* @__PURE__ */ y(m, { className: "h-6 w-6 text-text-primary" }),
253
+ /* @__PURE__ */ y(m, { className: "size-6 text-text-primary" }),
253
254
  /* @__PURE__ */ y("h1", {
254
255
  className: "text-xl font-bold text-text-primary",
255
256
  children: d("pages.orders.title", { defaultValue: "My Orders" })
@@ -264,17 +265,16 @@ var S = (e) => {
264
265
  })
265
266
  }),
266
267
  /* @__PURE__ */ y("main", {
267
- className: "mx-auto max-w-4xl px-6 py-6",
268
- children: h ? /* @__PURE__ */ b("div", {
269
- role: "status",
268
+ className: "mx-auto max-w-4xl p-6",
269
+ children: h ? /* @__PURE__ */ b("output", {
270
270
  "aria-label": "Loading orders",
271
271
  className: "flex flex-col items-center justify-center py-16",
272
272
  children: [/* @__PURE__ */ y(p, {
273
- className: "h-12 w-12 animate-spin text-text-muted mb-4",
273
+ className: "size-12 animate-spin text-text-muted mb-4",
274
274
  "aria-hidden": "true"
275
275
  }), /* @__PURE__ */ y("p", {
276
276
  className: "text-text-muted",
277
- children: "Loading your orders..."
277
+ children: "Loading your orders"
278
278
  })]
279
279
  }) : v ? /* @__PURE__ */ b("div", {
280
280
  role: "alert",
@@ -282,7 +282,7 @@ var S = (e) => {
282
282
  children: [
283
283
  /* @__PURE__ */ y("div", {
284
284
  className: "mb-6 rounded-full bg-status-error-bg-subtle/10 p-6",
285
- children: /* @__PURE__ */ y(_, { className: "h-16 w-16 text-status-error-text" })
285
+ children: /* @__PURE__ */ y(_, { className: "size-16 text-status-error-text" })
286
286
  }),
287
287
  /* @__PURE__ */ y("h2", {
288
288
  className: "mb-2 text-2xl font-bold text-text-primary",
@@ -293,9 +293,10 @@ var S = (e) => {
293
293
  children: v.message || "An error occurred while loading your orders. Please try again."
294
294
  }),
295
295
  /* @__PURE__ */ b("button", {
296
+ type: "button",
296
297
  onClick: H,
297
298
  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",
298
- children: [/* @__PURE__ */ y(g, { className: "h-5 w-5" }), "Browse Marketplace"]
299
+ children: [/* @__PURE__ */ y(g, { className: "size-5" }), "Browse Marketplace"]
299
300
  })
300
301
  ]
301
302
  }) : L.length === 0 ? /* @__PURE__ */ b("div", {
@@ -303,7 +304,7 @@ var S = (e) => {
303
304
  children: [
304
305
  /* @__PURE__ */ y("div", {
305
306
  className: "mb-6 rounded-full bg-bg-sunken p-6",
306
- children: /* @__PURE__ */ y(g, { className: "h-16 w-16 text-text-muted" })
307
+ children: /* @__PURE__ */ y(g, { className: "size-16 text-text-muted" })
307
308
  }),
308
309
  /* @__PURE__ */ y("h2", {
309
310
  className: "mb-2 text-2xl font-bold text-text-primary",
@@ -314,9 +315,10 @@ var S = (e) => {
314
315
  children: "You have not placed any orders yet. Browse the marketplace to discover apps and products!"
315
316
  }),
316
317
  /* @__PURE__ */ b("button", {
318
+ type: "button",
317
319
  onClick: H,
318
320
  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",
319
- children: [/* @__PURE__ */ y(g, { className: "h-5 w-5" }), "Browse Marketplace"]
321
+ children: [/* @__PURE__ */ y(g, { className: "size-5" }), "Browse Marketplace"]
320
322
  })
321
323
  ]
322
324
  }) : /* @__PURE__ */ b("ul", {
@@ -334,7 +336,7 @@ var S = (e) => {
334
336
  className: "flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50",
335
337
  "aria-label": d("common.loadMore", { defaultValue: "Load more" }),
336
338
  children: [h ? /* @__PURE__ */ y(p, {
337
- className: "h-4 w-4 animate-spin",
339
+ className: "size-4 animate-spin",
338
340
  "aria-hidden": "true"
339
341
  }) : null, d("common.loadMore", { defaultValue: "Load more" })]
340
342
  })
@@ -1 +1 @@
1
- {"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\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';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\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 subtotal: number;\n tax: number;\n total: number;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n}\n\n/**\n * Get status icon and color\n */\nconst getStatusDisplay = (status: UIOrder['status']) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n color: 'text-status-success-text bg-status-success-bg-subtle/10',\n label: 'Completed',\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Processing',\n };\n case 'PENDING':\n return {\n icon: Clock,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Pending',\n };\n case 'FAILED':\n return {\n icon: XCircle,\n color: 'text-status-error-text bg-status-error-bg-subtle/10',\n label: 'Failed',\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Refunded',\n };\n case 'CANCELLED':\n return { icon: RefreshCw, color: 'text-text-muted bg-bg-sunken', label: 'Cancelled' };\n default:\n return { icon: Clock, color: 'text-text-muted', label: status };\n }\n};\n\n/**\n * Order item card with Install App button\n */\nconst OrderItemCard: FC<{\n item: OrderItem;\n orderId: string;\n orderStatus: UIOrder['status'];\n onOpenInstallModal: (orderId: string, item: OrderItem) => void;\n installing: boolean;\n}> = ({ item, orderId, orderStatus, onOpenInstallModal, installing }) => {\n const isOrderSuccessful =\n orderStatus === 'COMPLETED' || orderStatus === 'FULFILLED' || orderStatus === 'PROCESSED';\n const isInstallable = item.productType !== 'PHYSICAL' && item.productType !== 'MERCHANDISE';\n return (\n <div className=\"flex items-center gap-4 rounded-lg border border-border-default bg-bg-sunken/50 p-4\">\n {/* Product Icon */}\n <div className=\"h-14 w-14 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=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center text-lg font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-text-primary truncate\">{item.productName}</h4>\n <div className=\"flex items-center gap-2 mt-1\">\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 <span className=\"text-sm text-text-muted\">\n {item.quantity} x {formatPrice(item.unitPrice)}\n </span>\n </div>\n </div>\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"font-semibold text-text-primary\">{formatPrice(item.totalPrice)}</p>\n </div>\n\n {/* Install Button - only show when order is successfully processed */}\n <div className=\"flex-shrink-0\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg bg-status-success-bg-subtle/10 px-4 py-2 text-sm font-medium text-status-success-text\">\n <CheckCircle className=\"h-4 w-4\" />\n Installed\n </span>\n ) : isOrderSuccessful && isInstallable ? (\n <button\n onClick={() => onOpenInstallModal(orderId, item)}\n disabled={installing}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Installing...\n </>\n ) : (\n <>\n <Download className=\"h-4 w-4\" />\n Install App\n </>\n )}\n </button>\n ) : null}\n </div>\n </div>\n );\n};\n\n/**\n * Order card component\n */\nconst OrderCard: FC<{\n order: UIOrder;\n onOpenInstallModal: (orderId: string, item: OrderItem) => void;\n installingItem: string | null;\n}> = ({ order, onOpenInstallModal, installingItem }) => {\n const statusDisplay = getStatusDisplay(order.status);\n const StatusIcon = statusDisplay.icon;\n\n return (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-6 transition-shadow hover:shadow-md\">\n {/* Order Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div>\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{order.orderNumber}</h3>\n <span\n className={`inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold ${statusDisplay.color}`}\n >\n <StatusIcon className=\"h-4 w-4\" />\n {statusDisplay.label}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xl font-bold text-text-primary\">{formatPrice(order.total)}</p>\n <p className=\"text-xs text-text-muted\">\n {order.items.length} {order.items.length === 1 ? 'item' : 'items'}\n </p>\n </div>\n </div>\n\n {/* Order Items */}\n <div className=\"space-y-3\">\n {order.items.map((item) => (\n <OrderItemCard\n key={item.id}\n item={item}\n orderId={order.id}\n orderStatus={order.status}\n onOpenInstallModal={onOpenInstallModal}\n installing={installingItem === item.id}\n />\n ))}\n </div>\n\n {/* Order Summary */}\n <div className=\"mt-4 pt-4 border-t border-border-default\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Subtotal</span>\n <span className=\"text-text-primary\">{formatPrice(order.subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-sm mt-1\">\n <span className=\"text-text-muted\">Tax</span>\n <span className=\"text-text-primary\">{formatPrice(order.tax)}</span>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a list of all orders with their items and Install App buttons\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\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 // 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\n .filter((installation) => installation.status === 'ACTIVE')\n .map((installation) => installation.productId)\n ),\n [installations]\n );\n\n // Map backend orders to UI format\n const orders: UIOrder[] = backendOrders.map((order) => {\n // Calculate subtotal from line items (with safety check)\n const lineItems = order.lineItems || [];\n const subtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n // Estimate tax (backend doesn't store separate tax amount)\n const tax = order.total - subtotal;\n\n // Map lineItems to items with unique IDs\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`, // Generate unique ID for each line item\n productId: item.productId,\n productName: item.name, // Backend uses 'name', UI expects 'productName'\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, // Backend uses 'subtotal', UI expects 'totalPrice'\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`, // Generate order number from ID\n items,\n subtotal,\n tax,\n total: order.total,\n status: order.status as UIOrder['status'], // Cast to union type\n createdAt: order.createdAt,\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 if (!selectedOrderId || !selectedItemId) return;\n\n console.log(\n `Successfully installed ${selectedApp?.name} to workspace ${workspaceName} (${workspaceId})`\n );\n\n // Reset modal state\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (error: Error) => {\n console.error('Installation failed:', error.message);\n // Error is displayed in modal, no need to close it\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n return (\n <div className=\"h-full overflow-y-auto\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-4xl\">\n <div className=\"flex items-center gap-4\">\n <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=\"h-5 w-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <Package className=\"h-6 w-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 px-2.5 py-0.5 text-sm font-medium text-action-primary-text\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-4xl px-6 py-6\">\n {loading ? (\n // Loading State\n <div\n role=\"status\"\n aria-label=\"Loading orders\"\n className=\"flex flex-col items-center justify-center py-16\"\n >\n <Loader2 className=\"h-12 w-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">Loading your orders...</p>\n </div>\n ) : error ? (\n // Error State\n <div role=\"alert\" className=\"flex flex-col items-center justify-center py-16 text-center\">\n <div className=\"mb-6 rounded-full bg-status-error-bg-subtle/10 p-6\">\n <XCircle className=\"h-16 w-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 || 'An error occurred while loading your orders. Please try again.'}\n </p>\n <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=\"h-5 w-5\" />\n Browse Marketplace\n </button>\n </div>\n ) : orders.length === 0 ? (\n // Empty State\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <div className=\"mb-6 rounded-full bg-bg-sunken p-6\">\n <ShoppingBag className=\"h-16 w-16 text-text-muted\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n You have not placed any orders yet. Browse the marketplace to discover apps and\n products!\n </p>\n <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=\"h-5 w-5\" />\n Browse Marketplace\n </button>\n </div>\n ) : (\n // Orders List\n <ul className=\"space-y-6\">\n {orders.map((order) => (\n <li key={order.id}>\n <OrderCard\n order={order}\n onOpenInstallModal={handleOpenInstallModal}\n installingItem={isModalOpen ? selectedItemId : null}\n />\n </li>\n ))}\n {hasMore && (\n <li className=\"flex justify-center pt-4 list-none\">\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 hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" /> : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </li>\n )}\n </ul>\n )}\n </main>\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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"}
1
+ {"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\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 { 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 subtotal: number;\n tax: number;\n total: number;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n}\n\n/**\n * Get status icon and color\n */\nconst getStatusDisplay = (status: UIOrder['status']) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n color: 'text-status-success-text bg-status-success-bg-subtle/10',\n label: 'Completed',\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Processing',\n };\n case 'PENDING':\n return {\n icon: Clock,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Pending',\n };\n case 'FAILED':\n return {\n icon: XCircle,\n color: 'text-status-error-text bg-status-error-bg-subtle/10',\n label: 'Failed',\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n color: 'text-status-warning-text bg-status-warning-bg-subtle/10',\n label: 'Refunded',\n };\n case 'CANCELLED':\n return { icon: RefreshCw, color: 'text-text-muted bg-bg-sunken', label: 'Cancelled' };\n default:\n return { icon: Clock, color: 'text-text-muted', label: status };\n }\n};\n\n/**\n * Order item card with Install App button\n */\nconst OrderItemCard: FC<{\n item: OrderItem;\n orderId: string;\n orderStatus: UIOrder['status'];\n onOpenInstallModal: (orderId: string, item: OrderItem) => void;\n installing: boolean;\n}> = ({ item, orderId, orderStatus, onOpenInstallModal, installing }) => {\n const isOrderSuccessful =\n orderStatus === 'COMPLETED' || orderStatus === 'FULFILLED' || orderStatus === 'PROCESSED';\n const isInstallable = item.productType !== 'PHYSICAL' && item.productType !== 'MERCHANDISE';\n return (\n <div className=\"flex items-center gap-4 rounded-lg border border-border-default bg-bg-sunken/50 p-4\">\n {/* Product Icon */}\n <div className=\"size-14 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.productIcon ? (\n <img src={item.productIcon} alt={item.productName} className=\"size-full object-cover\" />\n ) : (\n <div className=\"flex size-full items-center justify-center text-lg font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"font-medium text-text-primary truncate\">{item.productName}</h4>\n <div className=\"flex items-center gap-2 mt-1\">\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 <span className=\"text-sm text-text-muted\">\n {item.quantity} x {formatPrice(item.unitPrice)}\n </span>\n </div>\n </div>\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"font-semibold text-text-primary\">{formatPrice(item.totalPrice)}</p>\n </div>\n\n {/* Install Button - only show when order is successfully processed */}\n <div className=\"flex-shrink-0\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg bg-status-success-bg-subtle/10 px-4 py-2 text-sm font-medium text-status-success-text\">\n <CheckCircle className=\"size-4\" />\n Installed\n </span>\n ) : isOrderSuccessful && isInstallable ? (\n <button\n type=\"button\"\n onClick={() => onOpenInstallModal(orderId, item)}\n disabled={installing}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Installing…\n </>\n ) : (\n <>\n <Download className=\"size-4\" />\n Install App\n </>\n )}\n </button>\n ) : null}\n </div>\n </div>\n );\n};\n\n/**\n * Order card component\n */\nconst OrderCard: FC<{\n order: UIOrder;\n onOpenInstallModal: (orderId: string, item: OrderItem) => void;\n installingItem: string | null;\n}> = ({ order, onOpenInstallModal, installingItem }) => {\n const statusDisplay = getStatusDisplay(order.status);\n const StatusIcon = statusDisplay.icon;\n\n return (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-6 transition-shadow hover:shadow-md\">\n {/* Order Header */}\n <div className=\"flex items-start justify-between mb-4\">\n <div>\n <div className=\"flex items-center gap-3\">\n <h3 className=\"text-lg font-semibold text-text-primary\">{order.orderNumber}</h3>\n <span\n className={`inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold ${statusDisplay.color}`}\n >\n <StatusIcon className=\"size-4\" />\n {statusDisplay.label}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-xl font-bold text-text-primary\">{formatPrice(order.total)}</p>\n <p className=\"text-xs text-text-muted\">\n {order.items.length} {order.items.length === 1 ? 'item' : 'items'}\n </p>\n </div>\n </div>\n\n {/* Order Items */}\n <div className=\"space-y-3\">\n {order.items.map((item) => (\n <OrderItemCard\n key={item.id}\n item={item}\n orderId={order.id}\n orderStatus={order.status}\n onOpenInstallModal={onOpenInstallModal}\n installing={installingItem === item.id}\n />\n ))}\n </div>\n\n {/* Order Summary */}\n {/* TODO(ux-sweep): no order-details modal exists; tax + shipping live inline. When a modal lands, wrap \"Tax + shipping advanced\" in OptionalFields. */}\n <div className=\"mt-4 pt-4 border-t border-border-default\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-muted\">Subtotal</span>\n <span className=\"text-text-primary\">{formatPrice(order.subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-sm mt-1\">\n <span className=\"text-text-muted\">Tax</span>\n <span className=\"text-text-primary\">{formatPrice(order.tax)}</span>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a list of all orders with their items and Install App buttons\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\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 // 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 // Calculate subtotal from line items (with safety check)\n const lineItems = order.lineItems || [];\n const subtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n // Estimate tax (backend doesn't store separate tax amount)\n const tax = order.total - subtotal;\n\n // Map lineItems to items with unique IDs\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`, // Generate unique ID for each line item\n productId: item.productId,\n productName: item.name, // Backend uses 'name', UI expects 'productName'\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, // Backend uses 'subtotal', UI expects 'totalPrice'\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`, // Generate order number from ID\n items,\n subtotal,\n tax,\n total: order.total,\n status: order.status as UIOrder['status'], // Cast to union type\n createdAt: order.createdAt,\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 if (!selectedOrderId || !selectedItemId) return;\n\n console.log(\n `Successfully installed ${selectedApp?.name} to workspace ${workspaceName} (${workspaceId})`\n );\n\n // Reset modal state\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (error: Error) => {\n console.error('Installation failed:', error.message);\n // Error is displayed in modal, no need to close it\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n return (\n <div className=\"h-full overflow-y-auto\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-4xl\">\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 px-2.5 py-0.5 text-sm font-medium text-action-primary-text\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-4xl p-6\">\n {loading ? (\n // Loading State\n <output\n aria-label=\"Loading orders\"\n className=\"flex flex-col items-center justify-center py-16\"\n >\n <Loader2 className=\"size-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">Loading your orders…</p>\n </output>\n ) : error ? (\n // Error State\n <div role=\"alert\" className=\"flex flex-col items-center justify-center py-16 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 || 'An error occurred while loading your orders. Please try again.'}\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 Browse Marketplace\n </button>\n </div>\n ) : orders.length === 0 ? (\n // Empty State\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <div className=\"mb-6 rounded-full bg-bg-sunken p-6\">\n <ShoppingBag className=\"size-16 text-text-muted\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n You have not placed any orders yet. Browse the marketplace to discover apps and\n products!\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 Browse Marketplace\n </button>\n </div>\n ) : (\n // Orders List\n <ul className=\"space-y-6\">\n {orders.map((order) => (\n <li key={order.id}>\n <OrderCard\n order={order}\n onOpenInstallModal={handleOpenInstallModal}\n installingItem={isModalOpen ? selectedItemId : null}\n />\n </li>\n ))}\n {hasMore && (\n <li className=\"flex justify-center pt-4 list-none\">\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 hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" /> : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </li>\n )}\n </ul>\n )}\n </main>\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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"}
@@ -7,6 +7,7 @@ import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
7
  import { useNavigate as s, useParams as c } from "react-router-dom";
8
8
  //#region src/pages/ProductCategoryPage.tsx
9
9
  var l = ({ product: e, onOpen: t, noDesc: n }) => /* @__PURE__ */ o("button", {
10
+ type: "button",
10
11
  onClick: () => t(e.id),
11
12
  className: "cursor-pointer rounded-xl border border-border-default bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken",
12
13
  children: [
@@ -35,10 +36,11 @@ var l = ({ product: e, onOpen: t, noDesc: n }) => /* @__PURE__ */ o("button", {
35
36
  children: /* @__PURE__ */ o("div", {
36
37
  className: "mx-auto flex max-w-6xl items-center gap-3",
37
38
  children: [/* @__PURE__ */ a("button", {
39
+ type: "button",
38
40
  onClick: () => u(-1),
39
41
  "aria-label": "Go back",
40
42
  className: "cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary",
41
- children: /* @__PURE__ */ a(i, { className: "h-5 w-5" })
43
+ children: /* @__PURE__ */ a(i, { className: "size-5" })
42
44
  }), /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h1", {
43
45
  className: "text-2xl font-bold text-text-primary",
44
46
  children: p("pages.category.title", {