@burdenoff/microfe-store 2026.830.1 → 2026.831.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/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 +380 -376
- 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstalledAppsPage.js","names":[],"sources":["../../src/pages/InstalledAppsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useCallback } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Package,\n Plus,\n RefreshCw,\n Trash2,\n AlertCircle,\n Loader2,\n Search,\n ChevronLeft,\n ChevronRight,\n ExternalLink,\n Settings,\n} 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 {\n AlertDialog,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogCancel,\n AlertDialogAction,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\nimport { useApolloClient } from '@apollo/client/react';\nimport { gql } from '@apollo/client';\n\nconst PAGE_SIZE = 20;\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\nfunction getAppName(installation: InstalledApp): string {\n if (installation.productName) return installation.productName;\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.name && typeof manifest.name === 'string') return manifest.name;\n if (manifest?.slug && typeof manifest.slug === 'string') {\n return manifest.slug\n .split('-')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n }\n return installation.productId.slice(0, 8) + '…';\n}\n\nfunction getAppIcon(installation: InstalledApp): string | null {\n if (installation.productIcon) return installation.productIcon;\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.icon && typeof manifest.icon === 'string') return manifest.icon;\n return null;\n}\n\nfunction getAppType(installation: InstalledApp): string | null {\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.type && typeof manifest.type === 'string') return manifest.type;\n return null;\n}\n\nfunction getInstallationId(installation: InstalledApp): string | null {\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.installationId && typeof manifest.installationId === 'string')\n return manifest.installationId;\n return null;\n}\n\nfunction AppAvatar({ installation }: { installation: InstalledApp }) {\n const icon = getAppIcon(installation);\n const name = getAppName(installation);\n const [imgFailed, setImgFailed] = useState(false);\n\n if (icon && !imgFailed) {\n return (\n <img\n src={icon}\n alt={name}\n onError={() => setImgFailed(true)}\n className=\"size-8 rounded object-contain\"\n />\n );\n }\n return (\n <div className=\"flex size-8 items-center justify-center rounded bg-bg-sunken text-sm font-semibold text-text-muted\">\n {name.charAt(0).toUpperCase()}\n </div>\n );\n}\n\nconst UNINSTALL_APP_BY_APPLICATION = gql`\n mutation UninstallAppByApplicationFromStore(\n $applicationId: ID!\n $workspaceId: ID!\n $reason: String\n ) {\n uninstallAppByApplication(\n applicationId: $applicationId\n workspaceId: $workspaceId\n reason: $reason\n )\n }\n`;\n\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const apolloClient = useApolloClient();\n\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [search, setSearch] = useState('');\n const [page, setPage] = useState(1);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const statusFilter: 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 } = useInstallations({ filter: statusFilter, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE });\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 totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE));\n\n // Client-side search filter over the current page\n const filteredInstallations = useMemo(() => {\n if (!search.trim()) return installations;\n const term = search.toLowerCase();\n return installations.filter((inst) => {\n const name = getAppName(inst).toLowerCase();\n const ws = (inst.workspaceName ?? inst.workspaceId).toLowerCase();\n const type = (getAppType(inst) ?? '').toLowerCase();\n return name.includes(term) || ws.includes(term) || type.includes(term);\n });\n }, [installations, search]);\n\n const sortedInstallations = useMemo(\n () => [...filteredInstallations].sort((a, b) => b.installedAt.localeCompare(a.installedAt)),\n [filteredInstallations]\n );\n\n const handleStatusChange = useCallback((status: StoreInstallationStatus | 'all') => {\n setSelectedStatus(status);\n setPage(1);\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 } else {\n apolloClient\n .mutate({\n mutation: UNINSTALL_APP_BY_APPLICATION,\n variables: {\n applicationId: installation.productId,\n workspaceId: installation.workspaceId,\n reason: 'User uninstalled from Installed Apps',\n },\n })\n .catch(() => {});\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-seam 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 <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\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 <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-7xl\">\n <PagePurpose className=\"mb-4\">\n Every app you install into a workspace lives here. Check status, jump into details, or\n remove apps a workspace no longer needs.\n </PagePurpose>\n\n {/* Toolbar: status filters + search + refresh */}\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((f) => (\n <button\n type=\"button\"\n key={f.label}\n onClick={() => handleStatusChange(f.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === f.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {f.label}\n </button>\n ))}\n\n <div className=\"relative ml-auto\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-muted\" />\n <input\n type=\"search\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder=\"Search by name, workspace…\"\n className=\"h-9 rounded-lg border border-border-default bg-bg-surface pl-9 pr-3 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"inline-flex cursor-pointer 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 && totalCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={selectedStatus === 'all' ? 'No apps installed' : 'No apps match this filter'}\n description={\n selectedStatus === 'all'\n ? 'Browse the marketplace to install your first app.'\n : 'Try a different status filter, or browse the marketplace.'\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\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 }\n />\n ) : (\n <>\n <div className=\"mb-2 flex items-center justify-between text-sm text-text-muted\">\n <span aria-live=\"polite\">\n {search.trim()\n ? `${sortedInstallations.length} of ${totalCount} shown`\n : `${totalCount} installation${totalCount === 1 ? '' : 's'}`}\n </span>\n <span>\n Page {page} of {totalPages}\n </span>\n </div>\n\n {/* Table */}\n <div className=\"overflow-x-auto rounded-xl border border-border-seam\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken text-left text-xs font-semibold uppercase tracking-wide text-text-muted\">\n <th className=\"px-4 py-3\">App</th>\n <th className=\"px-4 py-3\">Workspace</th>\n <th className=\"px-4 py-3\">Status</th>\n <th className=\"px-4 py-3\">Version</th>\n <th className=\"px-4 py-3\">Installed</th>\n <th className=\"px-4 py-3 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-bg-surface\">\n {sortedInstallations.length === 0 ? (\n <tr>\n <td colSpan={6} className=\"py-12 text-center text-text-muted\">\n No results match your search.\n </td>\n </tr>\n ) : (\n sortedInstallations.map((installation) => {\n const appName = getAppName(installation);\n const appType = getAppType(installation);\n const installationId = getInstallationId(installation);\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n const isIntegration =\n appType === 'INTEGRATION' || appType === 'API_INTEGRATION';\n\n return (\n <tr\n key={installation.id}\n className=\"transition-colors hover:bg-bg-subtle\"\n >\n {/* App column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <AppAvatar installation={installation} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">\n {appName}\n </p>\n {appType && (\n <span className=\"mt-0.5 inline-block rounded bg-bg-sunken px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-muted\">\n {appType}\n </span>\n )}\n {isIntegration && installationId && (\n <p\n className=\"truncate text-[10px] text-text-muted\"\n title={installationId}\n >\n ID: {installationId.slice(0, 16)}…\n </p>\n )}\n </div>\n </div>\n </td>\n\n {/* Workspace column */}\n <td className=\"px-4 py-3\">\n <p className=\"truncate text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </p>\n {installation.workspaceType && (\n <p className=\"text-xs text-text-muted\">\n {installation.workspaceType}\n </p>\n )}\n </td>\n\n {/* Status column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex flex-wrap items-center gap-1\">\n <span\n className={`inline-block rounded-full px-2 py-1 text-xs font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.productStatus === 'UNLISTED' && (\n <span className=\"inline-block rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n Delisted\n </span>\n )}\n </div>\n </td>\n\n {/* Version column */}\n <td className=\"px-4 py-3 font-variant-numeric tabular-nums text-text-muted\">\n {installation.version ? `v${installation.version}` : '—'}\n </td>\n\n {/* Installed date column */}\n <td className=\"px-4 py-3 text-text-muted\">\n {new Date(installation.installedAt).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </td>\n\n {/* Actions column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-2\">\n {isIntegration ? (\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/installed/${installation.id}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n title=\"View installation details\"\n >\n <ExternalLink className=\"size-3.5\" />\n Details\n </button>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/installed/${installation.id}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Details\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n `${basePath}/installed/${installation.id}/settings`\n )\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Settings className=\"size-3.5\" />\n Configure\n </button>\n </>\n )}\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n `${basePath}/marketplace/product/${installation.productId}`\n )\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Product\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"size-3.5\" />\n )}\n Uninstall\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination controls */}\n {totalPages > 1 && (\n <div className=\"mt-4 flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page === 1 || loading}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"size-4\" />\n Previous\n </button>\n\n <div className=\"flex items-center gap-1\">\n {Array.from({ length: Math.min(totalPages, 7) }, (_, i) => {\n const p =\n totalPages <= 7\n ? i + 1\n : i < 3\n ? i + 1\n : i >= 4\n ? totalPages - (6 - i)\n : page;\n return (\n <button\n key={p}\n type=\"button\"\n onClick={() => setPage(p)}\n className={`size-8 rounded-lg text-sm font-medium transition-colors ${\n p === page\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken'\n }`}\n >\n {p}\n </button>\n );\n })}\n </div>\n\n <button\n type=\"button\"\n onClick={() => setPage((p) => (hasMore ? p + 1 : Math.min(totalPages, p + 1)))}\n disabled={page >= totalPages || loading}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </main>\n\n <AlertDialog\n open={pendingUninstall !== null}\n onOpenChange={(open) => !open && setPendingUninstall(null)}\n >\n <AlertDialogContent className=\"max-w-md\">\n <AlertDialogHeader>\n <AlertDialogTitle>Uninstall app</AlertDialogTitle>\n <AlertDialogDescription>\n {pendingUninstall && (\n <>\n Remove{' '}\n <span className=\"font-medium text-text-primary\">\n {getAppName(pendingUninstall)}\n </span>{' '}\n from{' '}\n <span className=\"font-medium text-text-primary\">\n {pendingUninstall.workspaceName ?? pendingUninstall.workspaceId}\n </span>\n ?{' '}\n {(getAppType(pendingUninstall) === 'INTEGRATION' ||\n getAppType(pendingUninstall) === 'API_INTEGRATION') && (\n <span className=\"mt-1 block text-xs text-text-muted\">\n This will also revoke the integration's access on the third-party side.\n </span>\n )}\n </>\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel onClick={() => setPendingUninstall(null)}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg-emphasis\"\n onClick={() => pendingUninstall && handleUninstall(pendingUninstall)}\n >\n Uninstall\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\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, useCallback } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Package,\n Plus,\n RefreshCw,\n Trash2,\n AlertCircle,\n Loader2,\n Search,\n ChevronLeft,\n ChevronRight,\n ExternalLink,\n Settings,\n} 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 { useTr } from '../shared/hooks/useTr';\nimport {\n AlertDialog,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogCancel,\n AlertDialogAction,\n PagePurpose,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { useInstallationStatusUpdatedSubscription } from '../generated/global-operations';\nimport { useApolloClient } from '@apollo/client/react';\nimport { gql } from '@apollo/client';\n\nconst PAGE_SIZE = 20;\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\nfunction getAppName(installation: InstalledApp): string {\n if (installation.productName) return installation.productName;\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.name && typeof manifest.name === 'string') return manifest.name;\n if (manifest?.slug && typeof manifest.slug === 'string') {\n return manifest.slug\n .split('-')\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ');\n }\n return installation.productId.slice(0, 8) + '…';\n}\n\nfunction getAppIcon(installation: InstalledApp): string | null {\n if (installation.productIcon) return installation.productIcon;\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.icon && typeof manifest.icon === 'string') return manifest.icon;\n return null;\n}\n\nfunction getAppType(installation: InstalledApp): string | null {\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.type && typeof manifest.type === 'string') return manifest.type;\n return null;\n}\n\nfunction getInstallationId(installation: InstalledApp): string | null {\n const manifest = installation.manifest as Record<string, unknown> | null;\n if (manifest?.installationId && typeof manifest.installationId === 'string')\n return manifest.installationId;\n return null;\n}\n\nfunction AppAvatar({ installation }: { installation: InstalledApp }) {\n const icon = getAppIcon(installation);\n const name = getAppName(installation);\n const [imgFailed, setImgFailed] = useState(false);\n\n if (icon && !imgFailed) {\n return (\n <img\n src={icon}\n alt={name}\n onError={() => setImgFailed(true)}\n className=\"size-8 rounded object-contain\"\n />\n );\n }\n return (\n <div className=\"flex size-8 items-center justify-center rounded bg-bg-sunken text-sm font-semibold text-text-muted\">\n {name.charAt(0).toUpperCase()}\n </div>\n );\n}\n\nconst UNINSTALL_APP_BY_APPLICATION = gql`\n mutation UninstallAppByApplicationFromStore(\n $applicationId: ID!\n $workspaceId: ID!\n $reason: String\n ) {\n uninstallAppByApplication(\n applicationId: $applicationId\n workspaceId: $workspaceId\n reason: $reason\n )\n }\n`;\n\nexport const InstalledAppsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, workspaceId } = useStore();\n const { t } = useI18n();\n const tr = useTr();\n const apolloClient = useApolloClient();\n\n const STATUS_FILTERS: Array<{ label: string; value: StoreInstallationStatus | 'all' }> = [\n { label: tr('pages.installed.statusAll', 'All'), value: 'all' },\n { label: tr('pages.installed.statusActive', 'Active'), value: StoreInstallationStatus.Active },\n {\n label: tr('pages.installed.statusInactive', 'Inactive'),\n value: StoreInstallationStatus.Inactive,\n },\n {\n label: tr('pages.installed.statusPending', 'Pending'),\n value: StoreInstallationStatus.Pending,\n },\n {\n label: tr('pages.installed.statusInstalling', 'Installing'),\n value: StoreInstallationStatus.Installing,\n },\n { label: tr('pages.installed.statusFailed', 'Failed'), value: StoreInstallationStatus.Failed },\n {\n label: tr('pages.installed.statusUninstalled', 'Uninstalled'),\n value: StoreInstallationStatus.Uninstalled,\n },\n ];\n\n const [selectedStatus, setSelectedStatus] = useState<StoreInstallationStatus | 'all'>('all');\n const [search, setSearch] = useState('');\n const [page, setPage] = useState(1);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n const [pendingUninstall, setPendingUninstall] = useState<InstalledApp | null>(null);\n\n const statusFilter: 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 } = useInstallations({ filter: statusFilter, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE });\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 totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE));\n\n // Client-side search filter over the current page\n const filteredInstallations = useMemo(() => {\n if (!search.trim()) return installations;\n const term = search.toLowerCase();\n return installations.filter((inst) => {\n const name = getAppName(inst).toLowerCase();\n const ws = (inst.workspaceName ?? inst.workspaceId).toLowerCase();\n const type = (getAppType(inst) ?? '').toLowerCase();\n return name.includes(term) || ws.includes(term) || type.includes(term);\n });\n }, [installations, search]);\n\n const sortedInstallations = useMemo(\n () => [...filteredInstallations].sort((a, b) => b.installedAt.localeCompare(a.installedAt)),\n [filteredInstallations]\n );\n\n const handleStatusChange = useCallback((status: StoreInstallationStatus | 'all') => {\n setSelectedStatus(status);\n setPage(1);\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(tr('pages.installed.uninstallError', 'Could not uninstall. Try again.'));\n } else {\n apolloClient\n .mutate({\n mutation: UNINSTALL_APP_BY_APPLICATION,\n variables: {\n applicationId: installation.productId,\n workspaceId: installation.workspaceId,\n reason: 'User uninstalled from Installed Apps',\n },\n })\n .catch(() => {});\n }\n setPendingUninstall(null);\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-seam 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\">\n {tr('pages.installed.subtitle', 'Manage your installed applications')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\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 <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-7xl\">\n <PagePurpose className=\"mb-4\">\n {tr(\n 'pages.installed.purpose',\n 'Every app you install into a workspace lives here. Check status, jump into details, or remove apps a workspace no longer needs.'\n )}\n </PagePurpose>\n\n {/* Toolbar: status filters + search + refresh */}\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n {STATUS_FILTERS.map((f) => (\n <button\n type=\"button\"\n key={f.label}\n onClick={() => handleStatusChange(f.value)}\n className={`cursor-pointer rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === f.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-surface'\n }`}\n >\n {f.label}\n </button>\n ))}\n\n <div className=\"relative ml-auto\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-muted\" />\n <input\n type=\"search\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={tr('pages.installed.searchPlaceholder', 'Search by name, workspace…')}\n className=\"h-9 rounded-lg border border-border-default bg-bg-surface pl-9 pr-3 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"inline-flex cursor-pointer 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 {tr('common.refresh', '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 {tr('pages.installed.failedToLoad', '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 {tr('common.tryAgain', 'Try Again')}\n </button>\n </div>\n ) : sortedInstallations.length === 0 && totalCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={\n selectedStatus === 'all'\n ? tr('pages.installed.emptyTitle', 'No apps installed')\n : tr('pages.installed.noMatchTitle', 'No apps match this filter')\n }\n description={\n selectedStatus === 'all'\n ? tr(\n 'pages.installed.emptyDescription',\n 'Browse the marketplace to install your first app.'\n )\n : tr(\n 'pages.installed.noMatchDescription',\n 'Try a different status filter, or browse the marketplace.'\n )\n }\n action={\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\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 {tr('pages.installed.browseMarketplace', 'Browse Marketplace')}\n </button>\n }\n />\n ) : (\n <>\n <div className=\"mb-2 flex items-center justify-between text-sm text-text-muted\">\n <span aria-live=\"polite\">\n {search.trim()\n ? `${sortedInstallations.length} of ${totalCount} shown`\n : `${totalCount} installation${totalCount === 1 ? '' : 's'}`}\n </span>\n <span>\n Page {page} of {totalPages}\n </span>\n </div>\n\n {/* Table */}\n <div className=\"overflow-x-auto rounded-xl border border-border-seam\">\n <table className=\"w-full min-w-[720px] text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken text-left text-xs font-semibold uppercase tracking-wide text-text-muted\">\n <th className=\"px-4 py-3\">{tr('pages.installed.colApp', 'App')}</th>\n <th className=\"px-4 py-3\">\n {tr('pages.installed.colWorkspace', 'Workspace')}\n </th>\n <th className=\"px-4 py-3\">{tr('pages.installed.colStatus', 'Status')}</th>\n <th className=\"px-4 py-3\">{tr('pages.installed.colVersion', 'Version')}</th>\n <th className=\"px-4 py-3\">\n {tr('pages.installed.colInstalled', 'Installed')}\n </th>\n <th className=\"px-4 py-3 text-right\">\n {tr('pages.installed.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-bg-surface\">\n {sortedInstallations.length === 0 ? (\n <tr>\n <td colSpan={6} className=\"py-12 text-center text-text-muted\">\n {tr('pages.installed.noSearchResults', 'No results match your search.')}\n </td>\n </tr>\n ) : (\n sortedInstallations.map((installation) => {\n const appName = getAppName(installation);\n const appType = getAppType(installation);\n const installationId = getInstallationId(installation);\n const uninstalling = uninstallingIds.has(\n `${installation.workspaceId}:${installation.productId}`\n );\n const isIntegration =\n appType === 'INTEGRATION' || appType === 'API_INTEGRATION';\n\n return (\n <tr\n key={installation.id}\n className=\"transition-colors hover:bg-bg-subtle\"\n >\n {/* App column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <AppAvatar installation={installation} />\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">\n {appName}\n </p>\n {appType && (\n <span className=\"mt-0.5 inline-block rounded bg-bg-sunken px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-muted\">\n {appType}\n </span>\n )}\n {isIntegration && installationId && (\n <p\n className=\"truncate text-[10px] text-text-muted\"\n title={installationId}\n >\n ID: {installationId.slice(0, 16)}…\n </p>\n )}\n </div>\n </div>\n </td>\n\n {/* Workspace column */}\n <td className=\"px-4 py-3\">\n <p className=\"truncate text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </p>\n {installation.workspaceType && (\n <p className=\"text-xs text-text-muted\">\n {installation.workspaceType}\n </p>\n )}\n </td>\n\n {/* Status column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex flex-wrap items-center gap-1\">\n <span\n className={`inline-block rounded-full px-2 py-1 text-xs font-medium ${statusBadgeClass(installation.status)}`}\n >\n {installation.status}\n </span>\n {installation.productStatus === 'UNLISTED' && (\n <span className=\"inline-block rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n {tr('pages.installed.delisted', 'Delisted')}\n </span>\n )}\n </div>\n </td>\n\n {/* Version column */}\n <td className=\"px-4 py-3 font-variant-numeric tabular-nums text-text-muted\">\n {installation.version\n ? `v${installation.version}`\n : tr('common.notApplicable', '—')}\n </td>\n\n {/* Installed date column */}\n <td className=\"px-4 py-3 text-text-muted\">\n {new Date(installation.installedAt).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </td>\n\n {/* Actions column */}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center justify-end gap-2\">\n {isIntegration ? (\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/installed/${installation.id}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n title={tr(\n 'pages.installed.viewDetails',\n 'View installation details'\n )}\n >\n <ExternalLink className=\"size-3.5\" />\n {tr('pages.installed.details', 'Details')}\n </button>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/installed/${installation.id}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {tr('pages.installed.details', 'Details')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n `${basePath}/installed/${installation.id}/settings`\n )\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Settings className=\"size-3.5\" />\n {tr('pages.installed.configure', 'Configure')}\n </button>\n </>\n )}\n <button\n type=\"button\"\n onClick={() =>\n navigate(\n `${basePath}/marketplace/product/${installation.productId}`\n )\n }\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {tr('pages.installed.product', 'Product')}\n </button>\n {installation.status !== StoreInstallationStatus.Uninstalled && (\n <button\n type=\"button\"\n onClick={() => setPendingUninstall(installation)}\n disabled={uninstalling}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-status-error-border px-3 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg-subtle disabled:opacity-60\"\n >\n {uninstalling ? (\n <Loader2 className=\"size-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"size-3.5\" />\n )}\n {tr('pages.installed.uninstall', 'Uninstall')}\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination controls */}\n {totalPages > 1 && (\n <div className=\"mt-4 flex items-center justify-between\">\n <button\n type=\"button\"\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page === 1 || loading}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"size-4\" />\n {tr('common.previous', 'Previous')}\n </button>\n\n <div className=\"flex items-center gap-1\">\n {Array.from({ length: Math.min(totalPages, 7) }, (_, i) => {\n const p =\n totalPages <= 7\n ? i + 1\n : i < 3\n ? i + 1\n : i >= 4\n ? totalPages - (6 - i)\n : page;\n return (\n <button\n key={p}\n type=\"button\"\n onClick={() => setPage(p)}\n className={`size-8 rounded-lg text-sm font-medium transition-colors ${\n p === page\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken'\n }`}\n >\n {p}\n </button>\n );\n })}\n </div>\n\n <button\n type=\"button\"\n onClick={() => setPage((p) => (hasMore ? p + 1 : Math.min(totalPages, p + 1)))}\n disabled={page >= totalPages || loading}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-border-default px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {tr('common.next', 'Next')}\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </main>\n\n <AlertDialog\n open={pendingUninstall !== null}\n onOpenChange={(open) => !open && setPendingUninstall(null)}\n >\n <AlertDialogContent className=\"max-w-md\">\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tr('pages.installed.uninstallTitle', 'Uninstall app')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {pendingUninstall && (\n <>\n {tr('pages.installed.uninstallPrompt', 'Remove {{app}} from {{workspace}}?', {\n app: getAppName(pendingUninstall),\n workspace: pendingUninstall.workspaceName ?? pendingUninstall.workspaceId,\n })}{' '}\n {(getAppType(pendingUninstall) === 'INTEGRATION' ||\n getAppType(pendingUninstall) === 'API_INTEGRATION') && (\n <span className=\"mt-1 block text-xs text-text-muted\">\n {tr(\n 'pages.installed.uninstallIntegrationHint',\n \"This will also revoke the integration's access on the third-party side.\"\n )}\n </span>\n )}\n </>\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel onClick={() => setPendingUninstall(null)}>\n {tr('common.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg-emphasis\"\n onClick={() => pendingUninstall && handleUninstall(pendingUninstall)}\n >\n {tr('pages.installed.uninstall', 'Uninstall')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n};\n"],"mappings":"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"}
|