@burdenoff/microfe-store 2026.830.1 → 2026.830.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/InstallAppModal.js +145 -150
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/components/MarkdownEditor.js +71 -70
- package/dist/components/MarkdownEditor.js.map +1 -1
- package/dist/components/cart/CartDrawer.js +179 -175
- package/dist/components/cart/CartDrawer.js.map +1 -1
- package/dist/pages/AppDetailPage.js +110 -119
- package/dist/pages/AppDetailPage.js.map +1 -1
- package/dist/pages/AppSettingsPage.js +60 -59
- package/dist/pages/AppSettingsPage.js.map +1 -1
- package/dist/pages/CartPage.js +122 -121
- package/dist/pages/CartPage.js.map +1 -1
- package/dist/pages/InstalledAppsPage.js +188 -197
- package/dist/pages/InstalledAppsPage.js.map +1 -1
- package/dist/pages/MarketplaceHomePage.js +559 -556
- package/dist/pages/MarketplaceHomePage.js.map +1 -1
- package/dist/pages/MyLicensesPage.js +68 -67
- package/dist/pages/MyLicensesPage.js.map +1 -1
- package/dist/pages/OrderConfirmationPage.js +117 -129
- package/dist/pages/OrderConfirmationPage.js.map +1 -1
- package/dist/pages/OrdersPage.js +87 -90
- package/dist/pages/OrdersPage.js.map +1 -1
- package/dist/pages/ProductDetailPage.js +600 -606
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/dist/shared/hooks/useTr.d.ts +16 -0
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/dist/translations/en.d.ts +327 -10
- package/dist/translations/en.js +343 -26
- package/dist/translations/en.js.map +1 -1
- package/package.json +1 -1
package/dist/pages/CartPage.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
2
|
import { useCart as t } from "../hooks/useCart.js";
|
|
3
3
|
import { formatPrice as n } from "../utils/index.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
4
|
+
import { useTr as r } from "../shared/hooks/useTr.js";
|
|
5
|
+
import { useState as i } from "react";
|
|
6
|
+
import { useNavigate as a } from "react-router";
|
|
7
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { ArrowLeft as s, Loader2 as c, Minus as l, Plus as u, ShoppingBag as d, ShoppingCart as f, Tag as p, Trash2 as m, Zap as h } from "lucide-react";
|
|
9
|
+
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { isQuotaExhaustedError as y } from "@burdenoff/fe-libs/shared/utils";
|
|
11
|
+
import { EmphasisPanel as b, IllustratedEmptyState as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
import { useEventBus as C } from "@burdenoff/fe-libs/shared/events";
|
|
13
|
+
import { useQuotaErrorToast as w } from "@burdenoff/fe-libs/shared/hooks";
|
|
13
14
|
//#region src/pages/CartPage.tsx
|
|
14
|
-
var
|
|
15
|
-
let
|
|
15
|
+
var T = () => {
|
|
16
|
+
let T = a(), { basePath: E } = e(), { cartItems: D, itemCount: O, total: k, updateQuantity: A, removeProduct: j, clearCart: M, createOrder: N } = t(), [P, F] = i(!1), [I, L] = i(null), { t: R } = o(), z = r(), B = C(), { report: V } = w(), H = D.some((e) => e.itemType === "physical"), U = async () => {
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
+
F(!0), L(null), B.emit("store.cart.checkout_started", {
|
|
18
19
|
cartId: "",
|
|
19
|
-
itemCount:
|
|
20
|
-
total:
|
|
20
|
+
itemCount: O,
|
|
21
|
+
total: k
|
|
21
22
|
});
|
|
22
|
-
let e = await
|
|
23
|
-
total:
|
|
24
|
-
lineItems:
|
|
23
|
+
let e = await N({
|
|
24
|
+
total: k,
|
|
25
|
+
lineItems: D.map((e) => ({
|
|
25
26
|
productId: e.productId,
|
|
26
27
|
name: e.product?.displayName || "Product",
|
|
27
28
|
quantity: e.quantity,
|
|
@@ -32,54 +33,54 @@ var w = () => {
|
|
|
32
33
|
pricingModel: e.product?.pricingModel || "PAID_ONETIME"
|
|
33
34
|
}))
|
|
34
35
|
});
|
|
35
|
-
e?.id ? (
|
|
36
|
+
e?.id ? (B.emit("store.order.placed", {
|
|
36
37
|
orderId: e.id,
|
|
37
|
-
total:
|
|
38
|
-
itemCount:
|
|
39
|
-
}),
|
|
38
|
+
total: k,
|
|
39
|
+
itemCount: O
|
|
40
|
+
}), M().catch(() => {}), T(`/billing/checkout/store/${e.id}`)) : (L(R("pages.cart.failedToCreate", { defaultValue: "Failed to create order. Please try again." })), F(!1));
|
|
40
41
|
} catch (e) {
|
|
41
|
-
if (console.error("Checkout error:", e),
|
|
42
|
-
|
|
42
|
+
if (console.error("Checkout error:", e), y(e)) {
|
|
43
|
+
V(e), F(!1);
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
+
L(e instanceof Error ? e.message : z("pages.cart.checkoutError", "An error occurred during checkout")), F(!1);
|
|
46
47
|
}
|
|
47
|
-
},
|
|
48
|
-
|
|
48
|
+
}, W = () => {
|
|
49
|
+
T(`${E}/marketplace`);
|
|
49
50
|
};
|
|
50
|
-
return /* @__PURE__ */
|
|
51
|
+
return /* @__PURE__ */ v("div", {
|
|
51
52
|
className: "h-full overflow-y-auto",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
53
|
+
children: [/* @__PURE__ */ _("header", {
|
|
53
54
|
className: "sticky top-0 z-10 border-b border-border-seam bg-bg-surface px-6 py-4",
|
|
54
|
-
children: /* @__PURE__ */
|
|
55
|
+
children: /* @__PURE__ */ _("div", {
|
|
55
56
|
className: "mx-auto max-w-6xl",
|
|
56
|
-
children: /* @__PURE__ */
|
|
57
|
+
children: /* @__PURE__ */ v("div", {
|
|
57
58
|
className: "flex items-center gap-4",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
59
|
+
children: [/* @__PURE__ */ _("button", {
|
|
59
60
|
type: "button",
|
|
60
|
-
onClick: () =>
|
|
61
|
-
"aria-label": "Go back",
|
|
61
|
+
onClick: () => T(-1),
|
|
62
|
+
"aria-label": z("common.goBack", "Go back"),
|
|
62
63
|
className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
63
|
-
children: /* @__PURE__ */
|
|
64
|
-
}), /* @__PURE__ */
|
|
64
|
+
children: /* @__PURE__ */ _(s, { className: "size-5" })
|
|
65
|
+
}), /* @__PURE__ */ v("div", {
|
|
65
66
|
className: "flex items-center gap-3",
|
|
66
67
|
children: [
|
|
67
|
-
/* @__PURE__ */
|
|
68
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ _(f, { className: "size-6 text-text-primary" }),
|
|
69
|
+
/* @__PURE__ */ _("h1", {
|
|
69
70
|
className: "text-xl font-bold text-text-primary",
|
|
70
|
-
children:
|
|
71
|
+
children: R("cart.title", { defaultValue: "Shopping Cart" })
|
|
71
72
|
}),
|
|
72
|
-
|
|
73
|
+
O > 0 && /* @__PURE__ */ v("span", {
|
|
73
74
|
className: "rounded-full bg-action-primary-bg px-2.5 py-0.5 text-sm font-medium text-action-primary-text",
|
|
74
75
|
children: [
|
|
75
|
-
|
|
76
|
+
O,
|
|
76
77
|
" ",
|
|
77
|
-
|
|
78
|
-
defaultValue: `${
|
|
79
|
-
count:
|
|
80
|
-
}) :
|
|
81
|
-
defaultValue: `${
|
|
82
|
-
count:
|
|
78
|
+
O === 1 ? R("pages.cart.item", {
|
|
79
|
+
defaultValue: `${O} item`,
|
|
80
|
+
count: O
|
|
81
|
+
}) : R("pages.cart.items", {
|
|
82
|
+
defaultValue: `${O} items`,
|
|
83
|
+
count: O
|
|
83
84
|
})
|
|
84
85
|
]
|
|
85
86
|
})
|
|
@@ -87,130 +88,130 @@ var w = () => {
|
|
|
87
88
|
})]
|
|
88
89
|
})
|
|
89
90
|
})
|
|
90
|
-
}), /* @__PURE__ */
|
|
91
|
+
}), /* @__PURE__ */ _("main", {
|
|
91
92
|
className: "mx-auto max-w-6xl p-6",
|
|
92
|
-
children:
|
|
93
|
+
children: D.length === 0 ? /* @__PURE__ */ _(x, {
|
|
93
94
|
illustration: "empty-generic",
|
|
94
|
-
title:
|
|
95
|
-
description:
|
|
96
|
-
action: /* @__PURE__ */
|
|
95
|
+
title: R("cart.empty", { defaultValue: "Your cart is empty" }),
|
|
96
|
+
description: R("pages.cart.emptyDescription", { defaultValue: "Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!" }),
|
|
97
|
+
action: /* @__PURE__ */ v("button", {
|
|
97
98
|
type: "button",
|
|
98
|
-
onClick:
|
|
99
|
+
onClick: W,
|
|
99
100
|
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",
|
|
100
|
-
children: [/* @__PURE__ */
|
|
101
|
+
children: [/* @__PURE__ */ _(d, { className: "size-5" }), R("pages.cart.browseMarketplace", { defaultValue: "Browse Marketplace" })]
|
|
101
102
|
})
|
|
102
|
-
}) : /* @__PURE__ */
|
|
103
|
+
}) : /* @__PURE__ */ v("div", {
|
|
103
104
|
className: "mx-auto max-w-3xl",
|
|
104
|
-
children: [/* @__PURE__ */
|
|
105
|
+
children: [/* @__PURE__ */ _(S, {
|
|
105
106
|
className: "mb-6",
|
|
106
|
-
children: "Review the apps and products you're about to buy, adjust quantities for physical items, and confirm the total before checkout. Nothing is charged until you proceed — once you do, your purchases become licenses you can install into a workspace."
|
|
107
|
-
}), /* @__PURE__ */
|
|
108
|
-
/* @__PURE__ */
|
|
107
|
+
children: z("pages.cart.purpose", "Review the apps and products you're about to buy, adjust quantities for physical items, and confirm the total before checkout. Nothing is charged until you proceed — once you do, your purchases become licenses you can install into a workspace.")
|
|
108
|
+
}), /* @__PURE__ */ v("div", { children: [
|
|
109
|
+
/* @__PURE__ */ v("div", {
|
|
109
110
|
className: "mb-4 flex items-center justify-between",
|
|
110
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
111
112
|
className: "text-lg font-semibold text-text-primary",
|
|
112
|
-
children:
|
|
113
|
-
defaultValue: `Cart Items (${
|
|
114
|
-
count:
|
|
113
|
+
children: R("pages.cart.cartItems", {
|
|
114
|
+
defaultValue: `Cart Items (${O})`,
|
|
115
|
+
count: O
|
|
115
116
|
})
|
|
116
|
-
}), /* @__PURE__ */
|
|
117
|
+
}), /* @__PURE__ */ v("button", {
|
|
117
118
|
type: "button",
|
|
118
|
-
onClick:
|
|
119
|
+
onClick: M,
|
|
119
120
|
className: "cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ _(m, { className: "size-4" }), R("cart.clearCart", { defaultValue: "Clear Cart" })]
|
|
121
122
|
})]
|
|
122
123
|
}),
|
|
123
|
-
/* @__PURE__ */
|
|
124
|
+
/* @__PURE__ */ _("ul", {
|
|
124
125
|
className: "space-y-4",
|
|
125
|
-
children:
|
|
126
|
+
children: D.map((e) => /* @__PURE__ */ v("li", {
|
|
126
127
|
className: "flex gap-4 rounded-xl border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]",
|
|
127
|
-
children: [/* @__PURE__ */
|
|
128
|
+
children: [/* @__PURE__ */ _("div", {
|
|
128
129
|
className: "size-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
|
|
129
|
-
children: e.product?.iconUrl ? /* @__PURE__ */
|
|
130
|
+
children: e.product?.iconUrl ? /* @__PURE__ */ _("img", {
|
|
130
131
|
src: e.product.iconUrl,
|
|
131
132
|
alt: e.product.displayName,
|
|
132
133
|
className: "size-full object-cover"
|
|
133
|
-
}) : /* @__PURE__ */
|
|
134
|
+
}) : /* @__PURE__ */ _("div", {
|
|
134
135
|
className: "flex size-full items-center justify-center text-2xl font-bold text-text-muted",
|
|
135
136
|
children: e.product?.displayName?.charAt(0) || "P"
|
|
136
137
|
})
|
|
137
|
-
}), /* @__PURE__ */
|
|
138
|
+
}), /* @__PURE__ */ v("div", {
|
|
138
139
|
className: "flex flex-1 flex-col",
|
|
139
140
|
children: [
|
|
140
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ v("div", {
|
|
141
142
|
className: "flex items-start justify-between",
|
|
142
|
-
children: [/* @__PURE__ */
|
|
143
|
-
/* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ v("div", { children: [
|
|
144
|
+
/* @__PURE__ */ _("h3", {
|
|
144
145
|
className: "font-semibold text-text-primary",
|
|
145
146
|
children: e.product?.displayName || "Product"
|
|
146
147
|
}),
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ _("p", {
|
|
148
149
|
className: "text-sm text-text-muted",
|
|
149
150
|
children: e.product?.publisher?.displayName || "Unknown Publisher"
|
|
150
151
|
}),
|
|
151
|
-
e.variant && /* @__PURE__ */
|
|
152
|
+
e.variant && /* @__PURE__ */ v("p", {
|
|
152
153
|
className: "mt-1 text-xs text-text-muted",
|
|
153
154
|
children: ["Version ", e.variant.version]
|
|
154
155
|
})
|
|
155
|
-
] }), /* @__PURE__ */
|
|
156
|
+
] }), /* @__PURE__ */ _("button", {
|
|
156
157
|
type: "button",
|
|
157
|
-
onClick: () =>
|
|
158
|
+
onClick: () => j(e.id),
|
|
158
159
|
className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text",
|
|
159
|
-
"aria-label":
|
|
160
|
-
title: "Remove item",
|
|
161
|
-
children: /* @__PURE__ */
|
|
160
|
+
"aria-label": z("cart.removeItem", "Remove {{name}} from cart", { name: e.product?.displayName ?? z("common.item", "item") }),
|
|
161
|
+
title: z("cart.removeItemTitle", "Remove item"),
|
|
162
|
+
children: /* @__PURE__ */ _(m, { className: "size-5" })
|
|
162
163
|
})]
|
|
163
164
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ v("div", {
|
|
165
166
|
className: "mt-2 flex items-center gap-2",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ _("span", {
|
|
167
168
|
className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted",
|
|
168
|
-
children: e.itemType === "digital" ? /* @__PURE__ */
|
|
169
|
-
}), e.product?.pricingModel === "SUBSCRIPTION" && /* @__PURE__ */
|
|
169
|
+
children: e.itemType === "digital" ? /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(h, { className: "size-3" }), R("pages.cart.digital", { defaultValue: "Digital" })] }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(p, { className: "size-3" }), R("pages.cart.physical", { defaultValue: "Physical" })] })
|
|
170
|
+
}), e.product?.pricingModel === "SUBSCRIPTION" && /* @__PURE__ */ _("span", {
|
|
170
171
|
className: "inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link",
|
|
171
|
-
children:
|
|
172
|
+
children: R("store.pricingModels.subscription", { defaultValue: "Subscription" })
|
|
172
173
|
})]
|
|
173
174
|
}),
|
|
174
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ v("div", {
|
|
175
176
|
className: "mt-auto flex items-center justify-between pt-3",
|
|
176
|
-
children: [e.itemType === "physical" ? /* @__PURE__ */
|
|
177
|
+
children: [e.itemType === "physical" ? /* @__PURE__ */ v("div", {
|
|
177
178
|
className: "flex items-center gap-3",
|
|
178
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ _("span", {
|
|
179
180
|
className: "text-sm text-text-muted",
|
|
180
|
-
children: "Qty:"
|
|
181
|
-
}), /* @__PURE__ */
|
|
181
|
+
children: z("pages.cart.qty", "Qty:")
|
|
182
|
+
}), /* @__PURE__ */ v("div", {
|
|
182
183
|
className: "flex items-center rounded-lg border border-border-default",
|
|
183
184
|
children: [
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ _("button", {
|
|
185
186
|
type: "button",
|
|
186
|
-
onClick: () =>
|
|
187
|
+
onClick: () => A(e.id, e.quantity - 1),
|
|
187
188
|
className: "cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50",
|
|
188
189
|
disabled: e.quantity <= 1,
|
|
189
|
-
children: /* @__PURE__ */
|
|
190
|
+
children: /* @__PURE__ */ _(l, { className: "size-4" })
|
|
190
191
|
}),
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ _("span", {
|
|
192
193
|
className: "min-w-[3rem] text-center font-medium",
|
|
193
194
|
children: e.quantity
|
|
194
195
|
}),
|
|
195
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ _("button", {
|
|
196
197
|
type: "button",
|
|
197
|
-
onClick: () =>
|
|
198
|
+
onClick: () => A(e.id, e.quantity + 1),
|
|
198
199
|
className: "cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken",
|
|
199
|
-
children: /* @__PURE__ */
|
|
200
|
+
children: /* @__PURE__ */ _(u, { className: "size-4" })
|
|
200
201
|
})
|
|
201
202
|
]
|
|
202
203
|
})]
|
|
203
|
-
}) : /* @__PURE__ */
|
|
204
|
+
}) : /* @__PURE__ */ _("div", {
|
|
204
205
|
className: "text-sm text-text-muted",
|
|
205
|
-
children:
|
|
206
|
-
}), /* @__PURE__ */
|
|
206
|
+
children: R("pages.cart.singleLicense", { defaultValue: "Single license" })
|
|
207
|
+
}), /* @__PURE__ */ v("div", {
|
|
207
208
|
className: "text-right",
|
|
208
|
-
children: [/* @__PURE__ */
|
|
209
|
+
children: [/* @__PURE__ */ _("p", {
|
|
209
210
|
className: "text-lg font-bold text-text-primary",
|
|
210
211
|
children: n(e.totalPrice)
|
|
211
212
|
}), e.quantity > 1 && e.itemType === "physical" && /* @__PURE__ */ _("p", {
|
|
212
213
|
className: "text-xs text-text-muted",
|
|
213
|
-
children:
|
|
214
|
+
children: z("pages.cart.each", "{{price}} each", { price: n(e.unitPrice) })
|
|
214
215
|
})]
|
|
215
216
|
})]
|
|
216
217
|
})
|
|
@@ -218,41 +219,41 @@ var w = () => {
|
|
|
218
219
|
})]
|
|
219
220
|
}, e.id))
|
|
220
221
|
}),
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ v(b, {
|
|
222
223
|
className: "mt-8",
|
|
223
224
|
children: [
|
|
224
|
-
|
|
225
|
+
I && /* @__PURE__ */ _("div", {
|
|
225
226
|
role: "alert",
|
|
226
227
|
className: "mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
227
|
-
children:
|
|
228
|
+
children: I
|
|
228
229
|
}),
|
|
229
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ _("p", {
|
|
230
231
|
className: "mb-3 text-xs text-text-muted",
|
|
231
|
-
children:
|
|
232
|
+
children: H ? z("pages.cart.physicalNotice", "Physical orders ship to your delivery address. Returns and refunds are handled by the seller per their store policy.") : z("pages.cart.digitalNotice", "All digital app purchases are final and non-refundable. Once an order is completed it cannot be cancelled or reversed.")
|
|
232
233
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ v("div", {
|
|
234
235
|
className: "flex items-center justify-between",
|
|
235
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
236
237
|
className: "text-sm text-text-muted",
|
|
237
|
-
children: "Total"
|
|
238
|
-
}), /* @__PURE__ */
|
|
238
|
+
children: z("cart.total", "Total")
|
|
239
|
+
}), /* @__PURE__ */ _("p", {
|
|
239
240
|
className: "text-2xl font-bold tabular-nums text-text-primary",
|
|
240
|
-
children: n(
|
|
241
|
-
})] }), /* @__PURE__ */
|
|
241
|
+
children: n(k)
|
|
242
|
+
})] }), /* @__PURE__ */ _("button", {
|
|
242
243
|
type: "button",
|
|
243
|
-
onClick:
|
|
244
|
-
disabled:
|
|
244
|
+
onClick: U,
|
|
245
|
+
disabled: P,
|
|
245
246
|
className: "flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70",
|
|
246
|
-
children:
|
|
247
|
+
children: P ? /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(c, { className: "size-4 animate-spin" }), R("pages.cart.processing", { defaultValue: "Processing..." })] }) : /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(h, { className: "size-4" }), R("pages.cart.proceedToCheckout", { defaultValue: "Proceed to Checkout" })] })
|
|
247
248
|
})]
|
|
248
249
|
})
|
|
249
250
|
]
|
|
250
251
|
}),
|
|
251
|
-
/* @__PURE__ */
|
|
252
|
+
/* @__PURE__ */ v("button", {
|
|
252
253
|
type: "button",
|
|
253
|
-
onClick:
|
|
254
|
+
onClick: W,
|
|
254
255
|
className: "cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline",
|
|
255
|
-
children: [/* @__PURE__ */
|
|
256
|
+
children: [/* @__PURE__ */ _(s, { className: "size-4" }), R("cart.continueShopping", { defaultValue: "Continue Shopping" })]
|
|
256
257
|
})
|
|
257
258
|
] })]
|
|
258
259
|
})
|
|
@@ -260,6 +261,6 @@ var w = () => {
|
|
|
260
261
|
});
|
|
261
262
|
};
|
|
262
263
|
//#endregion
|
|
263
|
-
export {
|
|
264
|
+
export { T as CartPage };
|
|
264
265
|
|
|
265
266
|
//# sourceMappingURL=CartPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartPage.js","names":[],"sources":["../../src/pages/CartPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useQuotaErrorToast } from '@burdenoff/fe-libs/shared/hooks';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport {\n ShoppingCart,\n Trash2,\n Plus,\n Minus,\n ArrowLeft,\n ShoppingBag,\n Zap,\n Tag,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { formatPrice } from '../utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n/**\n * CartPage component\n *\n * Full page cart view with items, summary, and checkout\n */\nexport const CartPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { cartItems, itemCount, total, updateQuantity, removeProduct, clearCart, createOrder } =\n useCart();\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n const [checkoutError, setCheckoutError] = useState<string | null>(null);\n const { t } = useI18n();\n const bus = useEventBus();\n // Surface QUOTA_EXHAUSTED gateway errors as a toast (e.g. when the\n // workspace has hit its store-purchases-per-month limit). The hook also\n // listens for the global `burdenoff:quota-exhausted` event so any other\n // mutation in the page that triggers it will toast as well.\n const { report: reportQuotaError } = useQuotaErrorToast();\n\n // Cart can hold physical (produce) items alongside digital apps. The page\n // compares item.itemType lowercase (see quantity controls below), so match\n // that casing here to avoid claiming physical/mixed orders \"cannot be\n // cancelled\" — those support returns/refunds via the seller's store policy.\n const hasPhysical = cartItems.some((i) => i.itemType === 'physical');\n\n const handleCheckout = async () => {\n try {\n setIsCheckingOut(true);\n setCheckoutError(null);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.cart.checkout_started' as any, {\n cartId: '',\n itemCount,\n total,\n });\n\n // Build line items from cart\n const lineItems = cartItems.map((item) => ({\n productId: item.productId,\n name: item.product?.displayName || 'Product',\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n subtotal: item.totalPrice,\n itemType: item.itemType.toLowerCase(),\n variantId: item.variantId,\n pricingModel: item.product?.pricingModel || 'PAID_ONETIME',\n }));\n\n // Create order input\n const orderInput = {\n total,\n lineItems,\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.order.placed' as any, {\n orderId: order.id,\n total,\n itemCount,\n });\n // Clear cart now that the order is created — non-blocking so a\n // clear failure never prevents the user from reaching billing.\n clearCart().catch(() => {});\n // Navigate to billing page with the order ID\n navigate(`/billing/checkout/store/${order.id}`);\n } else {\n setCheckoutError(\n t('pages.cart.failedToCreate', {\n defaultValue: 'Failed to create order. Please try again.',\n })\n );\n setIsCheckingOut(false);\n }\n } catch (error) {\n console.error('Checkout error:', error);\n // Quota-exhausted errors surface a global toast via the shell's\n // GlobalUiProvider; suppress the inline checkout error so we don't\n // stack two error UIs on top of each other.\n if (isQuotaExhaustedError(error)) {\n reportQuotaError(error);\n setIsCheckingOut(false);\n return;\n }\n setCheckoutError(\n error instanceof Error ? error.message : 'An error occurred during checkout'\n );\n setIsCheckingOut(false);\n }\n };\n\n const handleContinueShopping = () => {\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-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\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 <ShoppingCart className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('cart.title', { defaultValue: 'Shopping Cart' })}\n </h1>\n {itemCount > 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 {itemCount}{' '}\n {itemCount === 1\n ? t('pages.cart.item', { defaultValue: `${itemCount} item`, count: itemCount })\n : t('pages.cart.items', {\n defaultValue: `${itemCount} items`,\n count: itemCount,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl p-6\">\n {cartItems.length === 0 ? (\n // Empty Cart State\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={t('cart.empty', { defaultValue: 'Your cart is empty' })}\n description={t('pages.cart.emptyDescription', {\n defaultValue:\n 'Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!',\n })}\n action={\n <button\n type=\"button\"\n onClick={handleContinueShopping}\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 {t('pages.cart.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n }\n />\n ) : (\n // Cart with Items\n <div className=\"mx-auto max-w-3xl\">\n <PagePurpose className=\"mb-6\">\n Review the apps and products you're about to buy, adjust quantities for physical\n items, and confirm the total before checkout. Nothing is charged until you proceed —\n once you do, your purchases become licenses you can install into a workspace.\n </PagePurpose>\n {/* Cart Items */}\n <div>\n {/* Cart Actions */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.cart.cartItems', {\n defaultValue: `Cart Items (${itemCount})`,\n count: itemCount,\n })}\n </h2>\n <button\n type=\"button\"\n onClick={clearCart}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text\"\n >\n <Trash2 className=\"size-4\" />\n {t('cart.clearCart', { defaultValue: 'Clear Cart' })}\n </button>\n </div>\n\n {/* Items List */}\n <ul className=\"space-y-4\">\n {cartItems.map((item) => (\n <li\n key={item.id}\n className=\"flex gap-4 rounded-xl border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {/* Product Image */}\n <div className=\"size-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.product?.iconUrl ? (\n <img\n src={item.product.iconUrl}\n alt={item.product.displayName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-2xl font-bold text-text-muted\">\n {item.product?.displayName?.charAt(0) || 'P'}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {item.product?.displayName || 'Product'}\n </h3>\n <p className=\"text-sm text-text-muted\">\n {item.product?.publisher?.displayName || 'Unknown Publisher'}\n </p>\n {item.variant && (\n <p className=\"mt-1 text-xs text-text-muted\">\n Version {item.variant.version}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => removeProduct(item.id)}\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text\"\n aria-label={`Remove ${item.product?.displayName ?? 'item'} from cart`}\n title=\"Remove item\"\n >\n <Trash2 className=\"size-5\" />\n </button>\n </div>\n\n {/* Item Type Badge */}\n <div className=\"mt-2 flex items-center gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.itemType === 'digital' ? (\n <>\n <Zap className=\"size-3\" />\n {t('pages.cart.digital', { defaultValue: 'Digital' })}\n </>\n ) : (\n <>\n <Tag className=\"size-3\" />\n {t('pages.cart.physical', { defaultValue: 'Physical' })}\n </>\n )}\n </span>\n {item.product?.pricingModel === 'SUBSCRIPTION' && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link\">\n {t('store.pricingModels.subscription', {\n defaultValue: 'Subscription',\n })}\n </span>\n )}\n </div>\n\n {/* Quantity and Price Row */}\n <div className=\"mt-auto flex items-center justify-between pt-3\">\n {/* Quantity Controls - Only show for physical items */}\n {item.itemType === 'physical' ? (\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm text-text-muted\">Qty:</span>\n <div className=\"flex items-center rounded-lg border border-border-default\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n className=\"cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"min-w-[3rem] text-center font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n className=\"cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n </div>\n ) : (\n <div className=\"text-sm text-text-muted\">\n {t('pages.cart.singleLicense', { defaultValue: 'Single license' })}\n </div>\n )}\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"text-lg font-bold text-text-primary\">\n {formatPrice(item.totalPrice)}\n </p>\n {item.quantity > 1 && item.itemType === 'physical' && (\n <p className=\"text-xs text-text-muted\">\n {formatPrice(item.unitPrice)} each\n </p>\n )}\n </div>\n </div>\n </div>\n </li>\n ))}\n </ul>\n\n {/* Checkout Footer */}\n <EmphasisPanel className=\"mt-8\">\n {checkoutError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {checkoutError}\n </div>\n )}\n <p className=\"mb-3 text-xs text-text-muted\">\n {hasPhysical\n ? 'Physical orders ship to your delivery address. Returns and refunds are handled by the seller per their store policy.'\n : 'All digital app purchases are final and non-refundable. Once an order is completed it cannot be cancelled or reversed.'}\n </p>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-text-muted\">Total</p>\n <p className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {formatPrice(total)}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleCheckout}\n disabled={isCheckingOut}\n className=\"flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {isCheckingOut ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {t('pages.cart.processing', { defaultValue: 'Processing...' })}\n </>\n ) : (\n <>\n <Zap className=\"size-4\" />\n {t('pages.cart.proceedToCheckout', { defaultValue: 'Proceed to Checkout' })}\n </>\n )}\n </button>\n </div>\n </EmphasisPanel>\n\n {/* Continue Shopping */}\n <button\n type=\"button\"\n onClick={handleContinueShopping}\n className=\"cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline\"\n >\n <ArrowLeft className=\"size-4\" />\n {t('cart.continueShopping', { defaultValue: 'Continue Shopping' })}\n </button>\n </div>\n </div>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CartPage.js","names":[],"sources":["../../src/pages/CartPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useQuotaErrorToast } from '@burdenoff/fe-libs/shared/hooks';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport {\n ShoppingCart,\n Trash2,\n Plus,\n Minus,\n ArrowLeft,\n ShoppingBag,\n Zap,\n Tag,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { formatPrice } from '../utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTr } from '../shared/hooks/useTr';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n/**\n * CartPage component\n *\n * Full page cart view with items, summary, and checkout\n */\nexport const CartPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { cartItems, itemCount, total, updateQuantity, removeProduct, clearCart, createOrder } =\n useCart();\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n const [checkoutError, setCheckoutError] = useState<string | null>(null);\n const { t } = useI18n();\n const tr = useTr();\n const bus = useEventBus();\n // Surface QUOTA_EXHAUSTED gateway errors as a toast (e.g. when the\n // workspace has hit its store-purchases-per-month limit). The hook also\n // listens for the global `burdenoff:quota-exhausted` event so any other\n // mutation in the page that triggers it will toast as well.\n const { report: reportQuotaError } = useQuotaErrorToast();\n\n // Cart can hold physical (produce) items alongside digital apps. The page\n // compares item.itemType lowercase (see quantity controls below), so match\n // that casing here to avoid claiming physical/mixed orders \"cannot be\n // cancelled\" — those support returns/refunds via the seller's store policy.\n const hasPhysical = cartItems.some((i) => i.itemType === 'physical');\n\n const handleCheckout = async () => {\n try {\n setIsCheckingOut(true);\n setCheckoutError(null);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.cart.checkout_started' as any, {\n cartId: '',\n itemCount,\n total,\n });\n\n // Build line items from cart\n const lineItems = cartItems.map((item) => ({\n productId: item.productId,\n name: item.product?.displayName || 'Product',\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n subtotal: item.totalPrice,\n itemType: item.itemType.toLowerCase(),\n variantId: item.variantId,\n pricingModel: item.product?.pricingModel || 'PAID_ONETIME',\n }));\n\n // Create order input\n const orderInput = {\n total,\n lineItems,\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.order.placed' as any, {\n orderId: order.id,\n total,\n itemCount,\n });\n // Clear cart now that the order is created — non-blocking so a\n // clear failure never prevents the user from reaching billing.\n clearCart().catch(() => {});\n // Navigate to billing page with the order ID\n navigate(`/billing/checkout/store/${order.id}`);\n } else {\n setCheckoutError(\n t('pages.cart.failedToCreate', {\n defaultValue: 'Failed to create order. Please try again.',\n })\n );\n setIsCheckingOut(false);\n }\n } catch (error) {\n console.error('Checkout error:', error);\n // Quota-exhausted errors surface a global toast via the shell's\n // GlobalUiProvider; suppress the inline checkout error so we don't\n // stack two error UIs on top of each other.\n if (isQuotaExhaustedError(error)) {\n reportQuotaError(error);\n setIsCheckingOut(false);\n return;\n }\n setCheckoutError(\n error instanceof Error\n ? error.message\n : tr('pages.cart.checkoutError', 'An error occurred during checkout')\n );\n setIsCheckingOut(false);\n }\n };\n\n const handleContinueShopping = () => {\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-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label={tr('common.goBack', '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 <ShoppingCart className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('cart.title', { defaultValue: 'Shopping Cart' })}\n </h1>\n {itemCount > 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 {itemCount}{' '}\n {itemCount === 1\n ? t('pages.cart.item', { defaultValue: `${itemCount} item`, count: itemCount })\n : t('pages.cart.items', {\n defaultValue: `${itemCount} items`,\n count: itemCount,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl p-6\">\n {cartItems.length === 0 ? (\n // Empty Cart State\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={t('cart.empty', { defaultValue: 'Your cart is empty' })}\n description={t('pages.cart.emptyDescription', {\n defaultValue:\n 'Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!',\n })}\n action={\n <button\n type=\"button\"\n onClick={handleContinueShopping}\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 {t('pages.cart.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n }\n />\n ) : (\n // Cart with Items\n <div className=\"mx-auto max-w-3xl\">\n <PagePurpose className=\"mb-6\">\n {tr(\n 'pages.cart.purpose',\n \"Review the apps and products you're about to buy, adjust quantities for physical items, and confirm the total before checkout. Nothing is charged until you proceed — once you do, your purchases become licenses you can install into a workspace.\"\n )}\n </PagePurpose>\n {/* Cart Items */}\n <div>\n {/* Cart Actions */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.cart.cartItems', {\n defaultValue: `Cart Items (${itemCount})`,\n count: itemCount,\n })}\n </h2>\n <button\n type=\"button\"\n onClick={clearCart}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text\"\n >\n <Trash2 className=\"size-4\" />\n {t('cart.clearCart', { defaultValue: 'Clear Cart' })}\n </button>\n </div>\n\n {/* Items List */}\n <ul className=\"space-y-4\">\n {cartItems.map((item) => (\n <li\n key={item.id}\n className=\"flex gap-4 rounded-xl border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {/* Product Image */}\n <div className=\"size-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.product?.iconUrl ? (\n <img\n src={item.product.iconUrl}\n alt={item.product.displayName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-2xl font-bold text-text-muted\">\n {item.product?.displayName?.charAt(0) || 'P'}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {item.product?.displayName || 'Product'}\n </h3>\n <p className=\"text-sm text-text-muted\">\n {item.product?.publisher?.displayName || 'Unknown Publisher'}\n </p>\n {item.variant && (\n <p className=\"mt-1 text-xs text-text-muted\">\n Version {item.variant.version}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => removeProduct(item.id)}\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text\"\n aria-label={tr('cart.removeItem', 'Remove {{name}} from cart', {\n name: item.product?.displayName ?? tr('common.item', 'item'),\n })}\n title={tr('cart.removeItemTitle', 'Remove item')}\n >\n <Trash2 className=\"size-5\" />\n </button>\n </div>\n\n {/* Item Type Badge */}\n <div className=\"mt-2 flex items-center gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.itemType === 'digital' ? (\n <>\n <Zap className=\"size-3\" />\n {t('pages.cart.digital', { defaultValue: 'Digital' })}\n </>\n ) : (\n <>\n <Tag className=\"size-3\" />\n {t('pages.cart.physical', { defaultValue: 'Physical' })}\n </>\n )}\n </span>\n {item.product?.pricingModel === 'SUBSCRIPTION' && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link\">\n {t('store.pricingModels.subscription', {\n defaultValue: 'Subscription',\n })}\n </span>\n )}\n </div>\n\n {/* Quantity and Price Row */}\n <div className=\"mt-auto flex items-center justify-between pt-3\">\n {/* Quantity Controls - Only show for physical items */}\n {item.itemType === 'physical' ? (\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm text-text-muted\">\n {tr('pages.cart.qty', 'Qty:')}\n </span>\n <div className=\"flex items-center rounded-lg border border-border-default\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n className=\"cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"min-w-[3rem] text-center font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n className=\"cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n </div>\n ) : (\n <div className=\"text-sm text-text-muted\">\n {t('pages.cart.singleLicense', { defaultValue: 'Single license' })}\n </div>\n )}\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"text-lg font-bold text-text-primary\">\n {formatPrice(item.totalPrice)}\n </p>\n {item.quantity > 1 && item.itemType === 'physical' && (\n <p className=\"text-xs text-text-muted\">\n {tr('pages.cart.each', '{{price}} each', {\n price: formatPrice(item.unitPrice),\n })}\n </p>\n )}\n </div>\n </div>\n </div>\n </li>\n ))}\n </ul>\n\n {/* Checkout Footer */}\n <EmphasisPanel className=\"mt-8\">\n {checkoutError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {checkoutError}\n </div>\n )}\n <p className=\"mb-3 text-xs text-text-muted\">\n {hasPhysical\n ? tr(\n 'pages.cart.physicalNotice',\n 'Physical orders ship to your delivery address. Returns and refunds are handled by the seller per their store policy.'\n )\n : tr(\n 'pages.cart.digitalNotice',\n 'All digital app purchases are final and non-refundable. Once an order is completed it cannot be cancelled or reversed.'\n )}\n </p>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-text-muted\">{tr('cart.total', 'Total')}</p>\n <p className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {formatPrice(total)}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={handleCheckout}\n disabled={isCheckingOut}\n className=\"flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {isCheckingOut ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {t('pages.cart.processing', { defaultValue: 'Processing...' })}\n </>\n ) : (\n <>\n <Zap className=\"size-4\" />\n {t('pages.cart.proceedToCheckout', { defaultValue: 'Proceed to Checkout' })}\n </>\n )}\n </button>\n </div>\n </EmphasisPanel>\n\n {/* Continue Shopping */}\n <button\n type=\"button\"\n onClick={handleContinueShopping}\n className=\"cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline\"\n >\n <ArrowLeft className=\"size-4\" />\n {t('cart.continueShopping', { defaultValue: 'Continue Shopping' })}\n </button>\n </div>\n </div>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|