@burdenoff/microfe-store 2026.525.3 → 2026.527.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/StoreAdminRoutes.js +10 -9
- package/dist/StoreAdminRoutes.js.map +1 -1
- package/dist/StoreRoutes.js +2 -1
- package/dist/StoreRoutes.js.map +1 -1
- package/dist/components/AccessDenied.js +23 -0
- package/dist/components/AccessDenied.js.map +1 -0
- package/dist/components/InstallAppModal.js +183 -129
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/components/PermissionGuard.js +8 -0
- package/dist/components/PermissionGuard.js.map +1 -0
- package/dist/components/RouteGuards.js +7 -21
- package/dist/components/RouteGuards.js.map +1 -1
- package/dist/components/cart/CartDrawer.js +20 -14
- package/dist/components/cart/CartDrawer.js.map +1 -1
- package/dist/hooks/useStoreGraphQL.js +22 -20
- package/dist/hooks/useStoreGraphQL.js.map +1 -1
- package/dist/hooks/useStoreGraphQLWithContext.js.map +1 -1
- package/dist/hooks/useStorePermissions.js +4 -1
- package/dist/hooks/useStorePermissions.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/pages/AdminDashboardPage.js +182 -183
- package/dist/pages/AdminDashboardPage.js.map +1 -1
- package/dist/pages/AdminReviewModerationPage.js +7 -6
- package/dist/pages/AdminReviewModerationPage.js.map +1 -1
- package/dist/pages/AdminStoresPage.js +193 -185
- package/dist/pages/AdminStoresPage.js.map +1 -1
- package/dist/pages/AdminSubmissionsQueuePage.js +140 -142
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/AppDetailPage.js +6 -3
- package/dist/pages/AppDetailPage.js.map +1 -1
- package/dist/pages/AppSettingsPage.js +6 -3
- package/dist/pages/AppSettingsPage.js.map +1 -1
- package/dist/pages/CartPage.js +23 -15
- package/dist/pages/CartPage.js.map +1 -1
- package/dist/pages/InstalledAppsPage.js +26 -15
- package/dist/pages/InstalledAppsPage.js.map +1 -1
- package/dist/pages/MarketplaceHomePage.js +275 -247
- package/dist/pages/MarketplaceHomePage.js.map +1 -1
- package/dist/pages/MyLicensesPage.js +10 -8
- package/dist/pages/MyLicensesPage.js.map +1 -1
- package/dist/pages/OrderConfirmationPage.js +72 -75
- package/dist/pages/OrderConfirmationPage.js.map +1 -1
- package/dist/pages/OrdersPage.js +22 -20
- package/dist/pages/OrdersPage.js.map +1 -1
- package/dist/pages/ProductCategoryPage.js +3 -1
- package/dist/pages/ProductCategoryPage.js.map +1 -1
- package/dist/pages/ProductDetailPage.js +458 -443
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/dist/pages/ProductReviewsPage.js +4 -3
- package/dist/pages/ProductReviewsPage.js.map +1 -1
- package/dist/pages/PublisherDashboardPage.js +13 -7
- package/dist/pages/PublisherDashboardPage.js.map +1 -1
- package/dist/pages/PublisherRevenuePage.js +7 -5
- package/dist/pages/PublisherRevenuePage.js.map +1 -1
- package/dist/pages/PublisherSubmissionsPage.js +12 -7
- package/dist/pages/PublisherSubmissionsPage.js.map +1 -1
- package/dist/pages/PublisherSubmitPage.js +6 -0
- package/dist/pages/PublisherSubmitPage.js.map +1 -1
- package/dist/pages/SearchResultsPage.js +6 -3
- package/dist/pages/SearchResultsPage.js.map +1 -1
- package/dist/pages/StoresPage.js +73 -74
- package/dist/pages/StoresPage.js.map +1 -1
- package/dist/providers/StoreProvider.js +25 -25
- package/dist/providers/StoreProvider.js.map +1 -1
- package/dist/utils/index.js +29 -19
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/index.js +0 -3
- package/dist/components/marketplace/ProductCard.js +0 -2
|
@@ -68,9 +68,10 @@ var g = [
|
|
|
68
68
|
className: "text-sm text-text-muted",
|
|
69
69
|
children: "Manage your installed applications"
|
|
70
70
|
})] }), /* @__PURE__ */ m("button", {
|
|
71
|
+
type: "button",
|
|
71
72
|
onClick: R,
|
|
72
73
|
className: "flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90",
|
|
73
|
-
children: [/* @__PURE__ */ p(u, { className: "
|
|
74
|
+
children: [/* @__PURE__ */ p(u, { className: "size-5" }), /* @__PURE__ */ p("span", { children: x("pages.installed.installNew", { defaultValue: "Install New App" }) })]
|
|
74
75
|
})]
|
|
75
76
|
})
|
|
76
77
|
}),
|
|
@@ -82,13 +83,15 @@ var g = [
|
|
|
82
83
|
/* @__PURE__ */ m("div", {
|
|
83
84
|
className: "mb-4 flex flex-wrap items-center gap-2",
|
|
84
85
|
children: [g.map((e) => /* @__PURE__ */ p("button", {
|
|
86
|
+
type: "button",
|
|
85
87
|
onClick: () => C(e.value),
|
|
86
88
|
className: `cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${S === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-primary hover:bg-bg-surface"}`,
|
|
87
89
|
children: e.label
|
|
88
90
|
}, e.label)), /* @__PURE__ */ m("button", {
|
|
91
|
+
type: "button",
|
|
89
92
|
onClick: () => F(),
|
|
90
93
|
className: "ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
91
|
-
children: [/* @__PURE__ */ p(d, { className: "
|
|
94
|
+
children: [/* @__PURE__ */ p(d, { className: "size-4" }), "Refresh"]
|
|
92
95
|
})]
|
|
93
96
|
}),
|
|
94
97
|
w && /* @__PURE__ */ p("div", {
|
|
@@ -96,19 +99,18 @@ var g = [
|
|
|
96
99
|
className: "mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
|
|
97
100
|
children: w
|
|
98
101
|
}),
|
|
99
|
-
j ? /* @__PURE__ */ p("
|
|
100
|
-
role: "status",
|
|
102
|
+
j ? /* @__PURE__ */ p("output", {
|
|
101
103
|
"aria-label": "Loading installed apps",
|
|
102
104
|
className: "flex items-center justify-center py-16",
|
|
103
105
|
children: /* @__PURE__ */ p(c, {
|
|
104
|
-
className: "
|
|
106
|
+
className: "size-8 animate-spin text-text-muted",
|
|
105
107
|
"aria-hidden": "true"
|
|
106
108
|
})
|
|
107
109
|
}) : M ? /* @__PURE__ */ m("div", {
|
|
108
110
|
role: "alert",
|
|
109
111
|
className: "flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center",
|
|
110
112
|
children: [
|
|
111
|
-
/* @__PURE__ */ p(s, { className: "mb-3
|
|
113
|
+
/* @__PURE__ */ p(s, { className: "mb-3 size-10 text-status-error-text" }),
|
|
112
114
|
/* @__PURE__ */ p("h3", {
|
|
113
115
|
className: "text-lg font-semibold text-status-error-text",
|
|
114
116
|
children: "Failed to load installations"
|
|
@@ -118,6 +120,7 @@ var g = [
|
|
|
118
120
|
children: M.message
|
|
119
121
|
}),
|
|
120
122
|
/* @__PURE__ */ p("button", {
|
|
123
|
+
type: "button",
|
|
121
124
|
onClick: () => F(),
|
|
122
125
|
className: "mt-4 cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
123
126
|
children: "Try Again"
|
|
@@ -128,7 +131,7 @@ var g = [
|
|
|
128
131
|
children: /* @__PURE__ */ m("div", {
|
|
129
132
|
className: "text-center",
|
|
130
133
|
children: [
|
|
131
|
-
/* @__PURE__ */ p(l, { className: "mx-auto mb-4
|
|
134
|
+
/* @__PURE__ */ p(l, { className: "mx-auto mb-4 size-16 text-text-muted" }),
|
|
132
135
|
/* @__PURE__ */ p("h3", {
|
|
133
136
|
className: "mb-2 text-lg font-semibold text-text-primary",
|
|
134
137
|
children: "No apps installed"
|
|
@@ -138,6 +141,7 @@ var g = [
|
|
|
138
141
|
children: "Browse the marketplace to install your first app"
|
|
139
142
|
}),
|
|
140
143
|
/* @__PURE__ */ p("button", {
|
|
144
|
+
type: "button",
|
|
141
145
|
onClick: R,
|
|
142
146
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90",
|
|
143
147
|
children: "Browse Marketplace"
|
|
@@ -145,7 +149,7 @@ var g = [
|
|
|
145
149
|
]
|
|
146
150
|
})
|
|
147
151
|
}) : /* @__PURE__ */ m("div", {
|
|
148
|
-
className: "
|
|
152
|
+
className: "flex flex-col gap-y-4",
|
|
149
153
|
children: [
|
|
150
154
|
/* @__PURE__ */ m("p", {
|
|
151
155
|
"aria-live": "polite",
|
|
@@ -158,7 +162,7 @@ var g = [
|
|
|
158
162
|
]
|
|
159
163
|
}),
|
|
160
164
|
/* @__PURE__ */ p("ul", {
|
|
161
|
-
className: "
|
|
165
|
+
className: "flex flex-col gap-y-4",
|
|
162
166
|
children: L.map((e) => {
|
|
163
167
|
let t = P.has(`${e.workspaceId}:${e.productId}`);
|
|
164
168
|
return /* @__PURE__ */ p("li", {
|
|
@@ -168,8 +172,8 @@ var g = [
|
|
|
168
172
|
children: [/* @__PURE__ */ m("div", {
|
|
169
173
|
className: "flex items-start gap-3",
|
|
170
174
|
children: [/* @__PURE__ */ p("div", {
|
|
171
|
-
className: "flex
|
|
172
|
-
children: /* @__PURE__ */ p(l, { className: "
|
|
175
|
+
className: "flex size-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted",
|
|
176
|
+
children: /* @__PURE__ */ p(l, { className: "size-6" })
|
|
173
177
|
}), /* @__PURE__ */ m("div", { children: [
|
|
174
178
|
/* @__PURE__ */ p("h3", {
|
|
175
179
|
className: "font-semibold text-text-primary",
|
|
@@ -195,20 +199,23 @@ var g = [
|
|
|
195
199
|
className: "flex items-center gap-2",
|
|
196
200
|
children: [
|
|
197
201
|
/* @__PURE__ */ p("button", {
|
|
202
|
+
type: "button",
|
|
198
203
|
onClick: () => v(`${y}/installed/${e.id}`),
|
|
199
204
|
className: "cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
200
205
|
children: "Details"
|
|
201
206
|
}),
|
|
202
207
|
/* @__PURE__ */ p("button", {
|
|
208
|
+
type: "button",
|
|
203
209
|
onClick: () => v(`${y}/marketplace/product/${e.productId}`),
|
|
204
210
|
className: "cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
205
211
|
children: "View Product"
|
|
206
212
|
}),
|
|
207
213
|
e.status !== r.Uninstalled && /* @__PURE__ */ m("button", {
|
|
214
|
+
type: "button",
|
|
208
215
|
onClick: () => D(e),
|
|
209
216
|
disabled: t,
|
|
210
217
|
className: "cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60",
|
|
211
|
-
children: [t ? /* @__PURE__ */ p(c, { className: "
|
|
218
|
+
children: [t ? /* @__PURE__ */ p(c, { className: "size-4 animate-spin" }) : /* @__PURE__ */ p(f, { className: "size-4" }), "Uninstall"]
|
|
212
219
|
})
|
|
213
220
|
]
|
|
214
221
|
})]
|
|
@@ -233,11 +240,13 @@ var g = [
|
|
|
233
240
|
}),
|
|
234
241
|
E && /* @__PURE__ */ m("div", {
|
|
235
242
|
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
236
|
-
children: [/* @__PURE__ */ p("
|
|
243
|
+
children: [/* @__PURE__ */ p("button", {
|
|
244
|
+
type: "button",
|
|
245
|
+
"aria-label": "Cancel uninstall",
|
|
237
246
|
className: "absolute inset-0 bg-bg-overlay/50",
|
|
238
247
|
onClick: () => D(null)
|
|
239
|
-
}), /* @__PURE__ */ m("
|
|
240
|
-
|
|
248
|
+
}), /* @__PURE__ */ m("dialog", {
|
|
249
|
+
open: !0,
|
|
241
250
|
"aria-modal": "true",
|
|
242
251
|
"aria-labelledby": "installed-apps-uninstall-title",
|
|
243
252
|
className: "relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl",
|
|
@@ -269,10 +278,12 @@ var g = [
|
|
|
269
278
|
/* @__PURE__ */ m("div", {
|
|
270
279
|
className: "mt-6 flex flex-wrap justify-end gap-3",
|
|
271
280
|
children: [/* @__PURE__ */ p("button", {
|
|
281
|
+
type: "button",
|
|
272
282
|
onClick: () => D(null),
|
|
273
283
|
className: "cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
274
284
|
children: "Cancel"
|
|
275
285
|
}), /* @__PURE__ */ p("button", {
|
|
286
|
+
type: "button",
|
|
276
287
|
onClick: () => z(E),
|
|
277
288
|
className: "cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis",
|
|
278
289
|
children: "Uninstall"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstalledAppsPage.js","names":[],"sources":["../../src/pages/InstalledAppsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Package, Plus, RefreshCw, Trash2, AlertCircle, Loader2 } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { type InstalledApp, useInstallations } from '../hooks/useInstallations';\nimport { StoreInstallationStatus } from '../generated/global-types';\nimport type { InstallationFilterInput } from '../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\n\nconst STATUS_FILTERS: Array<{ label: string; value: StoreInstallationStatus | 'all' }> = [\n { label: 'All', value: 'all' },\n { label: 'Active', value: StoreInstallationStatus.Active },\n { label: 'Inactive', value: StoreInstallationStatus.Inactive },\n { label: 'Pending', value: StoreInstallationStatus.Pending },\n { label: 'Installing', value: StoreInstallationStatus.Installing },\n { label: 'Failed', value: StoreInstallationStatus.Failed },\n { label: 'Uninstalled', value: StoreInstallationStatus.Uninstalled },\n];\n\nconst statusBadgeClass = (status: StoreInstallationStatus): string => {\n if (status === StoreInstallationStatus.Active) {\n return 'bg-status-success-bg-subtle text-status-success-text';\n }\n if (\n status === StoreInstallationStatus.Pending ||\n status === StoreInstallationStatus.Installing ||\n status === StoreInstallationStatus.Inactive\n ) {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === StoreInstallationStatus.Failed) {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\n/**\n * InstalledAppsPage component\n *\n * Displays installed applications for the current actor\n */\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const filter: InstallationFilterInput | undefined = useMemo(\n () => (selectedStatus === 'all' ? undefined : { status: selectedStatus }),\n [selectedStatus]\n );\n\n const {\n installations,\n totalCount,\n hasMore,\n loading,\n error,\n uninstall,\n uninstallingIds,\n refetch,\n loadMore,\n } = useInstallations({ filter });\n\n useInstallationStatusUpdatedSubscription({\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n onData: () => {\n void refetch();\n },\n onError: (err) => {\n console.warn('InstallationStatusUpdated subscription error:', err.message);\n },\n });\n\n const sortedInstallations = useMemo(\n () => [...installations].sort((a, b) => b.installedAt.localeCompare(a.installedAt)),\n [installations]\n );\n\n const handleInstallNew = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n const handleUninstall = async (installation: InstalledApp) => {\n setErrorMessage(null);\n const ok = await uninstall(installation, 'User requested uninstall from store UI');\n if (!ok) {\n setErrorMessage(`Could not uninstall. Try again.`);\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.installed.title', { defaultValue: 'Installed Apps' })}\n </h1>\n <p className=\"text-sm text-text-muted\">Manage your installed applications</p>\n </div>\n\n <button\n onClick={handleInstallNew}\n className=\"flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"h-5 w-5\" />\n <span>{t('pages.installed.installNew', { defaultValue: 'Install New App' })}</span>\n </button>\n </div>\n </header>\n\n {/* Content */}\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((filter) => (\n <button\n key={filter.label}\n onClick={() => setSelectedStatus(filter.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === filter.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {filter.label}\n </button>\n ))}\n <button\n onClick={() => refetch()}\n className=\"ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n Refresh\n </button>\n </div>\n\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {errorMessage}\n </div>\n )}\n\n {loading ? (\n <div\n role=\"status\"\n aria-label=\"Loading installed apps\"\n className=\"flex items-center justify-center py-16\"\n >\n <Loader2 className=\"h-8 w-8 animate-spin text-text-muted\" aria-hidden=\"true\" />\n </div>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center\"\n >\n <AlertCircle className=\"mb-3 h-10 w-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n Failed to load installations\n </h3>\n <p className=\"mt-2 text-sm text-text-muted\">{error.message}</p>\n <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 Try Again\n </button>\n </div>\n ) : sortedInstallations.length === 0 ? (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"text-center\">\n <Package className=\"mx-auto mb-4 h-16 w-16 text-text-muted\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">No apps installed</h3>\n <p className=\"mb-4 text-sm text-text-muted\">\n Browse the marketplace to install your first app\n </p>\n <button\n onClick={handleInstallNew}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n Browse Marketplace\n </button>\n </div>\n </div>\n ) : (\n <div className=\"space-y-4 flex flex-col\">\n <p aria-live=\"polite\" className=\"text-sm text-text-muted\">\n {totalCount} installation{totalCount === 1 ? '' : 's'} found\n </p>\n <ul className=\"space-y-4 flex flex-col\">\n {sortedInstallations.map((installation) => {\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n return (\n <li\n key={installation.id}\n className=\"rounded-xl border border-border-default bg-bg-surface p-4\"\n >\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted\">\n <Package className=\"h-6 w-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h3>\n <p className=\"text-sm text-text-muted\">\n Product: {installation.productId}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span\n className={`rounded-full px-2 py-1 font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.version && <span>v{installation.version}</span>}\n {installation.workspaceType && (\n <span>{installation.workspaceType}</span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Details\n </button>\n <button\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n View Product\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Uninstall\n </button>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-6\">\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 </div>\n )}\n </div>\n )}\n </div>\n </main>\n\n {pendingUninstall && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div\n className=\"absolute inset-0 bg-bg-overlay/50\"\n onClick={() => setPendingUninstall(null)}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"installed-apps-uninstall-title\"\n className=\"relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl\"\n >\n <h2\n id=\"installed-apps-uninstall-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Uninstall app\n </h2>\n <p className=\"mt-2 text-sm text-text-muted\">\n Remove product{' '}\n <span className=\"font-medium text-text-primary\">{pendingUninstall.productId}</span>{' '}\n from workspace{' '}\n <span className=\"font-medium text-text-primary\">\n {pendingUninstall.workspaceName ?? pendingUninstall.workspaceId}\n </span>\n ?\n </p>\n\n <div className=\"mt-6 flex flex-wrap justify-end gap-3\">\n <button\n onClick={() => setPendingUninstall(null)}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n onClick={() => handleUninstall(pendingUninstall)}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis\"\n >\n Uninstall\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InstalledAppsPage.js","names":[],"sources":["../../src/pages/InstalledAppsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Package, Plus, RefreshCw, Trash2, AlertCircle, Loader2 } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { type InstalledApp, useInstallations } from '../hooks/useInstallations';\nimport { StoreInstallationStatus } from '../generated/global-types';\nimport type { InstallationFilterInput } from '../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\n\nconst STATUS_FILTERS: Array<{ label: string; value: StoreInstallationStatus | 'all' }> = [\n { label: 'All', value: 'all' },\n { label: 'Active', value: StoreInstallationStatus.Active },\n { label: 'Inactive', value: StoreInstallationStatus.Inactive },\n { label: 'Pending', value: StoreInstallationStatus.Pending },\n { label: 'Installing', value: StoreInstallationStatus.Installing },\n { label: 'Failed', value: StoreInstallationStatus.Failed },\n { label: 'Uninstalled', value: StoreInstallationStatus.Uninstalled },\n];\n\nconst statusBadgeClass = (status: StoreInstallationStatus): string => {\n if (status === StoreInstallationStatus.Active) {\n return 'bg-status-success-bg-subtle text-status-success-text';\n }\n if (\n status === StoreInstallationStatus.Pending ||\n status === StoreInstallationStatus.Installing ||\n status === StoreInstallationStatus.Inactive\n ) {\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n }\n if (status === StoreInstallationStatus.Failed) {\n return 'bg-status-error-bg-subtle text-status-error-text';\n }\n return 'bg-bg-sunken text-text-muted';\n};\n\n/**\n * InstalledAppsPage component\n *\n * Displays installed applications for the current actor\n */\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const filter: InstallationFilterInput | undefined = useMemo(\n () => (selectedStatus === 'all' ? undefined : { status: selectedStatus }),\n [selectedStatus]\n );\n\n const {\n installations,\n totalCount,\n hasMore,\n loading,\n error,\n uninstall,\n uninstallingIds,\n refetch,\n loadMore,\n } = useInstallations({ filter });\n\n useInstallationStatusUpdatedSubscription({\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n onData: () => {\n void refetch();\n },\n onError: (err) => {\n console.warn('InstallationStatusUpdated subscription error:', err.message);\n },\n });\n\n const sortedInstallations = useMemo(\n () =>\n [...installations].sort((a, b) =>\n b.installedAt.localeCompare(a.installedAt)\n ),\n [installations]\n );\n\n const handleInstallNew = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n const handleUninstall = async (installation: InstalledApp) => {\n setErrorMessage(null);\n const ok = await uninstall(installation, 'User requested uninstall from store UI');\n if (!ok) {\n setErrorMessage(`Could not uninstall. Try again.`);\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.installed.title', { defaultValue: 'Installed Apps' })}\n </h1>\n <p className=\"text-sm text-text-muted\">Manage your installed applications</p>\n </div>\n\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"size-5\" />\n <span>{t('pages.installed.installNew', { defaultValue: 'Install New App' })}</span>\n </button>\n </div>\n </header>\n\n {/* Content */}\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((filter) => (\n <button\n type=\"button\"\n key={filter.label}\n onClick={() => setSelectedStatus(filter.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === filter.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {filter.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"ml-auto cursor-pointer inline-flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n </div>\n\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\"\n >\n {errorMessage}\n </div>\n )}\n\n {loading ? (\n <output\n aria-label=\"Loading installed apps\"\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=\"flex flex-col items-center justify-center rounded-xl border border-status-error-border bg-status-error-bg-subtle py-16 text-center\"\n >\n <AlertCircle className=\"mb-3 size-10 text-status-error-text\" />\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n Failed to load installations\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 Try Again\n </button>\n </div>\n ) : sortedInstallations.length === 0 ? (\n <div className=\"flex items-center justify-center py-16\">\n <div className=\"text-center\">\n <Package className=\"mx-auto mb-4 size-16 text-text-muted\" />\n <h3 className=\"mb-2 text-lg font-semibold text-text-primary\">No apps installed</h3>\n <p className=\"mb-4 text-sm text-text-muted\">\n Browse the marketplace to install your first app\n </p>\n <button\n type=\"button\"\n onClick={handleInstallNew}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 font-semibold text-action-primary-text transition-colors hover:opacity-90\"\n >\n Browse Marketplace\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-y-4\">\n <p aria-live=\"polite\" className=\"text-sm text-text-muted\">\n {totalCount} installation{totalCount === 1 ? '' : 's'} found\n </p>\n <ul className=\"flex flex-col gap-y-4\">\n {sortedInstallations.map((installation) => {\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n return (\n <li\n key={installation.id}\n className=\"rounded-xl border border-border-default bg-bg-surface p-4\"\n >\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex size-12 items-center justify-center rounded-lg bg-bg-sunken text-lg font-semibold text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h3>\n <p className=\"text-sm text-text-muted\">\n Product: {installation.productId}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span\n className={`rounded-full px-2 py-1 font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.version && <span>v{installation.version}</span>}\n {installation.workspaceType && (\n <span>{installation.workspaceType}</span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Details\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"cursor-pointer rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n View Product\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n Uninstall\n </button>\n )}\n </div>\n </div>\n </li>\n );\n })}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-6\">\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 </div>\n )}\n </div>\n )}\n </div>\n </main>\n\n {pendingUninstall && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <button\n type=\"button\"\n aria-label=\"Cancel uninstall\"\n className=\"absolute inset-0 bg-bg-overlay/50\"\n onClick={() => setPendingUninstall(null)}\n />\n <dialog\n open\n aria-modal=\"true\"\n aria-labelledby=\"installed-apps-uninstall-title\"\n className=\"relative z-10 w-full max-w-md rounded-2xl border border-border-default bg-bg-surface p-6 shadow-2xl\"\n >\n <h2\n id=\"installed-apps-uninstall-title\"\n className=\"text-lg font-semibold text-text-primary\"\n >\n Uninstall app\n </h2>\n <p className=\"mt-2 text-sm text-text-muted\">\n Remove product{' '}\n <span className=\"font-medium text-text-primary\">{pendingUninstall.productId}</span>{' '}\n from workspace{' '}\n <span className=\"font-medium text-text-primary\">\n {pendingUninstall.workspaceName ?? pendingUninstall.workspaceId}\n </span>\n ?\n </p>\n\n <div className=\"mt-6 flex flex-wrap justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(null)}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => handleUninstall(pendingUninstall)}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg-emphasis\"\n >\n Uninstall\n </button>\n </div>\n </dialog>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|