@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
|
@@ -1,135 +1,136 @@
|
|
|
1
1
|
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
2
|
import { useMyEntitlements as t } from "../hooks/useStoreGraphQL.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
3
|
+
import { useTr as n } from "../shared/hooks/useTr.js";
|
|
4
|
+
import { useMemo as r } from "react";
|
|
5
|
+
import { useNavigate as i } from "react-router";
|
|
6
|
+
import { QuotaMeter as a } from "@burdenoff/fe-libs/shared/components";
|
|
7
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { AlertCircle as s, ArrowLeft as c, Loader2 as l, ShieldCheck as u } from "lucide-react";
|
|
9
|
+
import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
10
|
+
import { EmphasisPanel as m, IllustratedEmptyState as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/pages/MyLicensesPage.tsx
|
|
11
|
-
var
|
|
12
|
-
let
|
|
13
|
-
return /* @__PURE__ */
|
|
12
|
+
var _ = (e) => e === "ACTIVE" ? "bg-status-success-bg-subtle text-status-success-text" : e === "SUSPENDED" || e === "PENDING" ? "bg-status-warning-bg-subtle text-status-warning-text" : e === "EXPIRED" || e === "REVOKED" ? "bg-status-error-bg-subtle text-status-error-text" : "bg-bg-sunken text-text-muted", v = () => {
|
|
13
|
+
let v = i(), { basePath: y } = e(), { t: b } = o(), x = n(), { entitlements: S, hasMore: C, loading: w, error: T, refetch: E, loadMore: D } = t(), O = r(() => S.slice().sort((e, t) => e.id.localeCompare(t.id)), [S]);
|
|
14
|
+
return /* @__PURE__ */ p("div", {
|
|
14
15
|
className: "flex h-full flex-col",
|
|
15
|
-
children: [/* @__PURE__ */
|
|
16
|
+
children: [/* @__PURE__ */ f("header", {
|
|
16
17
|
className: "sticky top-0 z-10 border-b border-border-seam bg-bg-surface px-6 py-4",
|
|
17
|
-
children: /* @__PURE__ */
|
|
18
|
+
children: /* @__PURE__ */ p("div", {
|
|
18
19
|
className: "mx-auto flex max-w-5xl items-center gap-4",
|
|
19
|
-
children: [/* @__PURE__ */
|
|
20
|
+
children: [/* @__PURE__ */ f("button", {
|
|
20
21
|
type: "button",
|
|
21
|
-
onClick: () =>
|
|
22
|
+
onClick: () => v(-1),
|
|
22
23
|
"aria-label": "Go back",
|
|
23
24
|
className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
|
|
24
|
-
children: /* @__PURE__ */
|
|
25
|
-
}), /* @__PURE__ */
|
|
25
|
+
children: /* @__PURE__ */ f(c, { className: "size-5" })
|
|
26
|
+
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
|
|
26
27
|
className: "text-2xl font-bold text-text-primary",
|
|
27
|
-
children:
|
|
28
|
-
}), /* @__PURE__ */
|
|
28
|
+
children: b("pages.licenses.title", { defaultValue: "My Licenses" })
|
|
29
|
+
}), /* @__PURE__ */ f("p", {
|
|
29
30
|
className: "text-sm text-text-muted",
|
|
30
|
-
children:
|
|
31
|
+
children: b("pages.licenses.subtitle", { defaultValue: "Review and manage your product entitlements" })
|
|
31
32
|
})] })]
|
|
32
33
|
})
|
|
33
|
-
}), /* @__PURE__ */
|
|
34
|
+
}), /* @__PURE__ */ f("main", {
|
|
34
35
|
className: "flex-1 overflow-y-auto p-6",
|
|
35
|
-
children: /* @__PURE__ */
|
|
36
|
+
children: /* @__PURE__ */ p("div", {
|
|
36
37
|
className: "mx-auto max-w-5xl",
|
|
37
|
-
children: [/* @__PURE__ */
|
|
38
|
+
children: [/* @__PURE__ */ f(g, {
|
|
38
39
|
className: "mb-6",
|
|
39
|
-
children: "Every paid app you own grants a license (entitlement) that proves your right to use it. Check here to confirm a license is active, find a license key to enter into a product, and see when paid access expires — so you're never caught out by a lapsed or suspended entitlement."
|
|
40
|
-
}),
|
|
41
|
-
"aria-label": "Loading licenses",
|
|
40
|
+
children: x("pages.licenses.purpose", "Every paid app you own grants a license (entitlement) that proves your right to use it. Check here to confirm a license is active, find a license key to enter into a product, and see when paid access expires — so you're never caught out by a lapsed or suspended entitlement.")
|
|
41
|
+
}), w ? /* @__PURE__ */ f("output", {
|
|
42
|
+
"aria-label": x("pages.licenses.loading", "Loading licenses"),
|
|
42
43
|
className: "flex items-center justify-center py-16",
|
|
43
|
-
children: /* @__PURE__ */
|
|
44
|
+
children: /* @__PURE__ */ f(l, {
|
|
44
45
|
className: "size-8 animate-spin text-text-muted",
|
|
45
46
|
"aria-hidden": "true"
|
|
46
47
|
})
|
|
47
|
-
}) :
|
|
48
|
+
}) : T ? /* @__PURE__ */ p("div", {
|
|
48
49
|
role: "alert",
|
|
49
50
|
className: "rounded-xl border border-status-error-border bg-status-error-bg-subtle p-8 text-center",
|
|
50
51
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ f(s, { className: "mx-auto mb-3 size-10 text-status-error-text" }),
|
|
53
|
+
/* @__PURE__ */ f("h3", {
|
|
53
54
|
className: "text-lg font-semibold text-status-error-text",
|
|
54
|
-
children:
|
|
55
|
+
children: b("pages.licenses.failedToLoad", { defaultValue: "Failed to load licenses" })
|
|
55
56
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ f("p", {
|
|
57
58
|
className: "mt-2 text-sm text-text-muted",
|
|
58
|
-
children:
|
|
59
|
+
children: T.message
|
|
59
60
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
+
/* @__PURE__ */ f("button", {
|
|
61
62
|
type: "button",
|
|
62
|
-
onClick: () =>
|
|
63
|
+
onClick: () => E(),
|
|
63
64
|
className: "mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
64
|
-
children:
|
|
65
|
+
children: b("common.tryAgain", { defaultValue: "Try Again" })
|
|
65
66
|
})
|
|
66
67
|
]
|
|
67
|
-
}) :
|
|
68
|
+
}) : O.length === 0 ? /* @__PURE__ */ f(h, {
|
|
68
69
|
illustration: "empty-data",
|
|
69
|
-
title:
|
|
70
|
-
description:
|
|
71
|
-
action: /* @__PURE__ */
|
|
70
|
+
title: b("pages.licenses.emptyTitle", { defaultValue: "No licenses yet" }),
|
|
71
|
+
description: b("pages.licenses.emptyDescription", { defaultValue: "Purchase apps from the marketplace to see your licenses here." }),
|
|
72
|
+
action: /* @__PURE__ */ f("button", {
|
|
72
73
|
type: "button",
|
|
73
|
-
onClick: () =>
|
|
74
|
+
onClick: () => v(`${y}/marketplace`),
|
|
74
75
|
className: "cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
75
|
-
children:
|
|
76
|
+
children: b("pages.licenses.browseMarketplace", { defaultValue: "Browse Marketplace" })
|
|
76
77
|
})
|
|
77
|
-
}) : /* @__PURE__ */
|
|
78
|
+
}) : /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(m, {
|
|
78
79
|
className: "mb-6",
|
|
79
|
-
children: /* @__PURE__ */
|
|
80
|
-
label:
|
|
81
|
-
used:
|
|
80
|
+
children: /* @__PURE__ */ f(a, {
|
|
81
|
+
label: b("pages.licenses.activeLicenses", { defaultValue: "Active licenses" }),
|
|
82
|
+
used: O.filter((e) => e.status === "ACTIVE").length,
|
|
82
83
|
limit: null,
|
|
83
|
-
unit:
|
|
84
|
+
unit: b("pages.licenses.licensesUnit", { defaultValue: "licenses" })
|
|
84
85
|
})
|
|
85
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ p("ul", {
|
|
86
87
|
className: "flex flex-col gap-y-4",
|
|
87
|
-
children: [
|
|
88
|
+
children: [O.map((e) => /* @__PURE__ */ f("li", {
|
|
88
89
|
className: "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)]",
|
|
89
|
-
children: /* @__PURE__ */
|
|
90
|
+
children: /* @__PURE__ */ p("div", {
|
|
90
91
|
className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
91
|
-
children: [/* @__PURE__ */
|
|
92
|
-
/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ p("div", { children: [
|
|
93
|
+
/* @__PURE__ */ p("div", {
|
|
93
94
|
className: "flex items-center gap-2",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
95
|
+
children: [/* @__PURE__ */ f(u, { className: "size-4 text-status-success-text" }), /* @__PURE__ */ f("h3", {
|
|
95
96
|
className: "font-semibold text-text-primary",
|
|
96
|
-
children:
|
|
97
|
+
children: x("pages.licenses.product", "Product {{id}}", { id: e.productId })
|
|
97
98
|
})]
|
|
98
99
|
}),
|
|
99
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ f("p", {
|
|
100
101
|
className: "mt-1 text-sm text-text-muted",
|
|
101
|
-
children:
|
|
102
|
+
children: b("pages.licenses.entitlementId", {
|
|
102
103
|
defaultValue: `Entitlement ID: ${e.id}`,
|
|
103
104
|
id: e.id
|
|
104
105
|
})
|
|
105
106
|
}),
|
|
106
|
-
e.licenseKey && /* @__PURE__ */
|
|
107
|
+
e.licenseKey && /* @__PURE__ */ f("p", {
|
|
107
108
|
className: "mt-1 text-sm text-text-muted",
|
|
108
|
-
children:
|
|
109
|
+
children: b("pages.licenses.licenseKey", {
|
|
109
110
|
defaultValue: `License key: ${e.licenseKey}`,
|
|
110
111
|
key: e.licenseKey
|
|
111
112
|
})
|
|
112
113
|
}),
|
|
113
|
-
e.expiresAt && /* @__PURE__ */
|
|
114
|
+
e.expiresAt && /* @__PURE__ */ f("p", {
|
|
114
115
|
className: "mt-1 text-sm text-text-muted",
|
|
115
|
-
children:
|
|
116
|
+
children: b("pages.licenses.expiresOn", {
|
|
116
117
|
defaultValue: `Expires on ${new Date(e.expiresAt).toLocaleDateString()}`,
|
|
117
118
|
date: new Date(e.expiresAt).toLocaleDateString()
|
|
118
119
|
})
|
|
119
120
|
})
|
|
120
|
-
] }), /* @__PURE__ */
|
|
121
|
-
className: `rounded-full px-3 py-1 text-xs font-semibold ${
|
|
121
|
+
] }), /* @__PURE__ */ f("span", {
|
|
122
|
+
className: `rounded-full px-3 py-1 text-xs font-semibold ${_(e.status)} ${e.status === "ACTIVE" ? "motion-safe:animate-vc-glow-pulse" : ""}`,
|
|
122
123
|
children: e.status
|
|
123
124
|
})]
|
|
124
125
|
})
|
|
125
|
-
}, e.id)),
|
|
126
|
+
}, e.id)), C && /* @__PURE__ */ f("li", {
|
|
126
127
|
className: "flex justify-center pt-4 list-none",
|
|
127
|
-
children: /* @__PURE__ */
|
|
128
|
+
children: /* @__PURE__ */ f("button", {
|
|
128
129
|
type: "button",
|
|
129
|
-
onClick:
|
|
130
|
-
disabled:
|
|
130
|
+
onClick: D,
|
|
131
|
+
disabled: w,
|
|
131
132
|
className: "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",
|
|
132
|
-
children:
|
|
133
|
+
children: w ? x("common.loading", "Loading…") : x("common.loadMore", "Load more")
|
|
133
134
|
})
|
|
134
135
|
})]
|
|
135
136
|
})] })]
|
|
@@ -138,6 +139,6 @@ var g = (e) => e === "ACTIVE" ? "bg-status-success-bg-subtle text-status-success
|
|
|
138
139
|
});
|
|
139
140
|
};
|
|
140
141
|
//#endregion
|
|
141
|
-
export {
|
|
142
|
+
export { v as MyLicensesPage };
|
|
142
143
|
|
|
143
144
|
//# sourceMappingURL=MyLicensesPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MyLicensesPage.js","names":[],"sources":["../../src/pages/MyLicensesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo } from 'react';\nimport { useNavigate } from 'react-router';\nimport { ArrowLeft, ShieldCheck, AlertCircle, Loader2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { QuotaMeter } from '@burdenoff/fe-libs/shared/components';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { useMyEntitlements } from '../hooks/useStoreGraphQL';\n\ninterface LicenseItem {\n id: string;\n productId: string;\n status: string;\n licenseKey?: string | null;\n expiresAt?: string | null;\n}\n\nconst statusClass = (status: string): string => {\n if (status === 'ACTIVE') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'SUSPENDED' || status === 'PENDING') {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === 'EXPIRED' || status === 'REVOKED') {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\nexport const MyLicensesPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const { entitlements, hasMore, loading, error, refetch, loadMore } = useMyEntitlements();\n\n const licenses = useMemo(\n () => (entitlements as LicenseItem[]).slice().sort((a, b) => a.id.localeCompare(b.id)),\n [entitlements]\n );\n\n return (\n <div className=\"flex h-full flex-col\">\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 flex max-w-5xl 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>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.licenses.title', { defaultValue: 'My Licenses' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.licenses.subtitle', {\n defaultValue: 'Review and manage your product entitlements',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-5xl\">\n <PagePurpose className=\"mb-6\">\n Every paid app you own grants a license (entitlement) that proves your right to use it.\n Check here to confirm a license is active, find a license key to enter into a product,\n and see when paid access expires — so you're never caught out by a lapsed or suspended\n entitlement.\n </PagePurpose>\n {loading ? (\n <output\n aria-label=\"Loading licenses\"\n className=\"flex items-center justify-center py-16\"\n >\n <Loader2 className=\"size-8 animate-spin text-text-muted\" aria-hidden=\"true\" />\n </output>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"rounded-xl border border-status-error-border bg-status-error-bg-subtle p-8 text-center\"\n >\n <AlertCircle className=\"mx-auto mb-3 size-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n {t('pages.licenses.failedToLoad', { defaultValue: 'Failed to load licenses' })}\n </h3>\n <p className=\"mt-2 text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {t('common.tryAgain', { defaultValue: 'Try Again' })}\n </button>\n </div>\n ) : licenses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.licenses.emptyTitle', { defaultValue: 'No licenses yet' })}\n description={t('pages.licenses.emptyDescription', {\n defaultValue: 'Purchase apps from the marketplace to see your licenses here.',\n })}\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {t('pages.licenses.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n }\n />\n ) : (\n <>\n {/*\n Inline quota meter showing active-license consumption vs the\n workspace plan limit. The limit is sourced from the billing\n summary when available; passing `null` renders an\n \"Unlimited\" indicator instead of misleading 0/0 progress.\n */}\n <EmphasisPanel className=\"mb-6\">\n <QuotaMeter\n label={t('pages.licenses.activeLicenses', {\n defaultValue: 'Active licenses',\n })}\n used={licenses.filter((l) => l.status === 'ACTIVE').length}\n limit={null}\n unit={t('pages.licenses.licensesUnit', {\n defaultValue: 'licenses',\n })}\n />\n </EmphasisPanel>\n <ul className=\"flex flex-col gap-y-4\">\n {licenses.map((license) => (\n <li\n key={license.id}\n className=\"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 <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" />\n <h3 className=\"font-semibold text-text-primary\">\n Product {license.productId}\n </h3>\n </div>\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.entitlementId', {\n defaultValue: `Entitlement ID: ${license.id}`,\n id: license.id,\n })}\n </p>\n {license.licenseKey && (\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.licenseKey', {\n defaultValue: `License key: ${license.licenseKey}`,\n key: license.licenseKey,\n })}\n </p>\n )}\n {license.expiresAt && (\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.expiresOn', {\n defaultValue: `Expires on ${new Date(license.expiresAt).toLocaleDateString()}`,\n date: new Date(license.expiresAt).toLocaleDateString(),\n })}\n </p>\n )}\n </div>\n <span\n className={`rounded-full px-3 py-1 text-xs font-semibold ${statusClass(license.status)} ${\n license.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n >\n {license.status}\n </span>\n </div>\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=\"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 >\n {loading ? 'Loading…' : 'Load more'}\n </button>\n </li>\n )}\n </ul>\n </>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MyLicensesPage.js","names":[],"sources":["../../src/pages/MyLicensesPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo } from 'react';\nimport { useNavigate } from 'react-router';\nimport { ArrowLeft, ShieldCheck, AlertCircle, Loader2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTr } from '../shared/hooks/useTr';\nimport { QuotaMeter } from '@burdenoff/fe-libs/shared/components';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { useMyEntitlements } from '../hooks/useStoreGraphQL';\n\ninterface LicenseItem {\n id: string;\n productId: string;\n status: string;\n licenseKey?: string | null;\n expiresAt?: string | null;\n}\n\nconst statusClass = (status: string): string => {\n if (status === 'ACTIVE') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'SUSPENDED' || status === 'PENDING') {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === 'EXPIRED' || status === 'REVOKED') {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\nexport const MyLicensesPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const tr = useTr();\n const { entitlements, hasMore, loading, error, refetch, loadMore } = useMyEntitlements();\n\n const licenses = useMemo(\n () => (entitlements as LicenseItem[]).slice().sort((a, b) => a.id.localeCompare(b.id)),\n [entitlements]\n );\n\n return (\n <div className=\"flex h-full flex-col\">\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 flex max-w-5xl 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>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.licenses.title', { defaultValue: 'My Licenses' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.licenses.subtitle', {\n defaultValue: 'Review and manage your product entitlements',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-5xl\">\n <PagePurpose className=\"mb-6\">\n {tr(\n 'pages.licenses.purpose',\n \"Every paid app you own grants a license (entitlement) that proves your right to use it. Check here to confirm a license is active, find a license key to enter into a product, and see when paid access expires — so you're never caught out by a lapsed or suspended entitlement.\"\n )}\n </PagePurpose>\n {loading ? (\n <output\n aria-label={tr('pages.licenses.loading', 'Loading licenses')}\n className=\"flex items-center justify-center py-16\"\n >\n <Loader2 className=\"size-8 animate-spin text-text-muted\" aria-hidden=\"true\" />\n </output>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"rounded-xl border border-status-error-border bg-status-error-bg-subtle p-8 text-center\"\n >\n <AlertCircle className=\"mx-auto mb-3 size-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n {t('pages.licenses.failedToLoad', { defaultValue: 'Failed to load licenses' })}\n </h3>\n <p className=\"mt-2 text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {t('common.tryAgain', { defaultValue: 'Try Again' })}\n </button>\n </div>\n ) : licenses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.licenses.emptyTitle', { defaultValue: 'No licenses yet' })}\n description={t('pages.licenses.emptyDescription', {\n defaultValue: 'Purchase apps from the marketplace to see your licenses here.',\n })}\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {t('pages.licenses.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n }\n />\n ) : (\n <>\n {/*\n Inline quota meter showing active-license consumption vs the\n workspace plan limit. The limit is sourced from the billing\n summary when available; passing `null` renders an\n \"Unlimited\" indicator instead of misleading 0/0 progress.\n */}\n <EmphasisPanel className=\"mb-6\">\n <QuotaMeter\n label={t('pages.licenses.activeLicenses', {\n defaultValue: 'Active licenses',\n })}\n used={licenses.filter((l) => l.status === 'ACTIVE').length}\n limit={null}\n unit={t('pages.licenses.licensesUnit', {\n defaultValue: 'licenses',\n })}\n />\n </EmphasisPanel>\n <ul className=\"flex flex-col gap-y-4\">\n {licenses.map((license) => (\n <li\n key={license.id}\n className=\"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 <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <ShieldCheck className=\"size-4 text-status-success-text\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('pages.licenses.product', 'Product {{id}}', {\n id: license.productId,\n })}\n </h3>\n </div>\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.entitlementId', {\n defaultValue: `Entitlement ID: ${license.id}`,\n id: license.id,\n })}\n </p>\n {license.licenseKey && (\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.licenseKey', {\n defaultValue: `License key: ${license.licenseKey}`,\n key: license.licenseKey,\n })}\n </p>\n )}\n {license.expiresAt && (\n <p className=\"mt-1 text-sm text-text-muted\">\n {t('pages.licenses.expiresOn', {\n defaultValue: `Expires on ${new Date(license.expiresAt).toLocaleDateString()}`,\n date: new Date(license.expiresAt).toLocaleDateString(),\n })}\n </p>\n )}\n </div>\n <span\n className={`rounded-full px-3 py-1 text-xs font-semibold ${statusClass(license.status)} ${\n license.status === 'ACTIVE' ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n >\n {license.status}\n </span>\n </div>\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=\"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 >\n {loading\n ? tr('common.loading', 'Loading…')\n : tr('common.loadMore', 'Load more')}\n </button>\n </li>\n )}\n </ul>\n </>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|