@burdenoff/microfe-store 2026.1013.2 → 2026.1013.3

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.
@@ -445,7 +445,7 @@ var F = ({ isOpen: n, onClose: a, app: S, orderId: D, purchaseState: A = "free",
445
445
  type: "selectVersion",
446
446
  version: e.target.value
447
447
  }),
448
- className: "w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
448
+ className: "w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary scheme-normal dark:scheme-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] [&_option]:bg-bg-surface [&_option]:text-text-primary",
449
449
  children: [/* @__PURE__ */ h("option", {
450
450
  value: "",
451
451
  children: S.version ? L("installApp.latestWithVersion", "Latest ({{version}})", { version: S.version }) : L("installApp.latest", "Latest")
@@ -1 +1 @@
1
- {"version":3,"file":"InstallAppModal.js","names":[],"sources":["../../src/components/InstallAppModal.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useCallback, useReducer } from 'react';\nimport {\n Loader2,\n Building2,\n CheckCircle,\n AlertCircle,\n Info,\n ShieldCheck,\n ChevronRight,\n ArrowLeft,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { useTr } from '../shared/hooks/useTr';\n\n// Local type definitions for workspace operations\ninterface Workspace {\n id: string;\n name: string;\n slug: string;\n type?: string;\n status?: string;\n organizationId?: string;\n}\n\ninterface WorkspaceList {\n items: Workspace[];\n total: number;\n}\n\ninterface InstallWorkspaceAppsResult {\n successCount: number;\n failedCount: number;\n results: Array<{\n success: boolean;\n workspaceId: string;\n app?: unknown;\n error?: string;\n }>;\n}\n\n// String queries for graphqlFetch\nconst MY_WORKSPACES_STRING = `query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }`;\nconst INSTALL_WORKSPACE_APPS_STRING = `mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }`;\nconst RECORD_APP_INSTALLATION_STRING = `mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }`;\nconst INSTALL_PURCHASED_APP_STRING = `mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!, $version: String) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId, version: $version) { success alreadyInstalled installation { id productId workspaceId status version } error message } }`;\nconst CREATE_PRODUCT_ENTITLEMENT_STRING = `mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }`;\nconst INSTALL_APP_DEVPORTAL_STRING = `mutation InstallAppDevportal($input: InstallAppInput!) { installApp(input: $input) { id status workspaceId application { oauthAuthUrl } } }`;\nconst INSTALLABLE_APPLICATION_VERSIONS_STRING = `query InstallableApplicationVersions($applicationId: ID!) { installableApplicationVersions(applicationId: $applicationId) { version status isLatest } }`;\n\nconst STORE_DISTRIBUTED_TYPES = new Set([\n 'VIBEMODULE',\n 'NODE',\n 'CONSOLE',\n 'DASHBOARD',\n 'PARSER',\n 'DATASINK',\n]);\n\n// App types that require an explicit permissions consent step before install\nconst CONSENT_REQUIRED_TYPES = new Set(['INTEGRATION', 'API_INTEGRATION']);\n\nconst INSTALL_ERROR_MESSAGES: Record<string, string> = {\n ALREADY_INSTALLED: 'This app is already installed in the selected workspace.',\n QUOTA_EXCEEDED: 'Installation limit reached. Please upgrade your plan or contact support.',\n PERMISSION_DENIED: 'You do not have permission to install apps in this workspace.',\n NOT_FOUND: 'The app or workspace could not be found.',\n WORKSPACE_INACTIVE: 'The selected workspace is inactive.',\n ENTITLEMENT_EXPIRED: 'Your license for this app has expired.',\n UNSUPPORTED_TYPE: 'This product type cannot be installed in the selected workspace.',\n};\n\nfunction mapInstallError(raw: string): string {\n for (const [code, message] of Object.entries(INSTALL_ERROR_MESSAGES)) {\n if (raw.toUpperCase().includes(code)) return message;\n }\n return 'Installation failed. Please try again or contact support.';\n}\n\ntype InstallStep = 'workspace' | 'consent';\n\ninterface InstallState {\n step: InstallStep;\n selectedWorkspace: string | null;\n selectedVersion: string;\n installing: boolean;\n installError: string | null;\n installInfo: string | null;\n}\n\ntype InstallAction =\n | { type: 'reset' }\n | { type: 'selectWorkspace'; workspaceId: string }\n | { type: 'selectVersion'; version: string }\n | { type: 'advanceToConsent' }\n | { type: 'backToWorkspace' }\n | { type: 'startInstall' }\n | { type: 'finishInstall' }\n | { type: 'setError'; error: string }\n | { type: 'setInfo'; info: string };\n\nconst initialInstallState: InstallState = {\n step: 'workspace',\n selectedWorkspace: null,\n selectedVersion: '',\n installing: false,\n installError: null,\n installInfo: null,\n};\n\nfunction installReducer(state: InstallState, action: InstallAction): InstallState {\n switch (action.type) {\n case 'reset':\n return initialInstallState;\n case 'selectWorkspace':\n return { ...state, selectedWorkspace: action.workspaceId };\n case 'selectVersion':\n return { ...state, selectedVersion: action.version };\n case 'advanceToConsent':\n return { ...state, step: 'consent', installError: null };\n case 'backToWorkspace':\n return { ...state, step: 'workspace', installError: null };\n case 'startInstall':\n return { ...state, installing: true, installError: null, installInfo: null };\n case 'finishInstall':\n return { ...state, installing: false };\n case 'setError':\n return { ...state, installError: action.error };\n case 'setInfo':\n return { ...state, installInfo: action.info };\n default:\n return state;\n }\n}\n\nexport interface AppInfo {\n id: string;\n // devportal Application.id — equals StoreProduct.productId, used for installApp mutation\n applicationId?: string;\n name: string;\n slug?: string;\n icon?: string;\n type?: string;\n version?: string;\n permissions?: string[];\n}\n\ninterface InstallAppModalProps {\n isOpen: boolean;\n onClose: () => void;\n app: AppInfo;\n orderId?: string | null; // For purchased apps from orders page\n purchaseState?: 'free' | 'purchased';\n onInstallSuccess?: (workspaceId: string, workspaceName: string) => void;\n onInstallError?: (error: Error) => void;\n}\n\ninterface UseWorkspacesResult {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// NOTE: Inline Apollo clients removed. All GraphQL calls now use graphqlFetch\n// from fe-libs which automatically resolves gateway URLs and injects auth + locale headers.\n\n/**\n * Hook to fetch user's workspaces using graphqlFetch from fe-libs\n */\nfunction useWorkspaces(skip: boolean): UseWorkspacesResult {\n const { authToken } = useStore();\n const [workspaces, setWorkspaces] = useState<Workspace[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchWorkspaces = useCallback(async () => {\n if (skip) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<{ myWorkspaces: WorkspaceList }>({\n gateway: 'global',\n query: MY_WORKSPACES_STRING,\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'GraphQL error');\n }\n\n setWorkspaces(result.data?.myWorkspaces?.items || []);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch workspaces'));\n } finally {\n setLoading(false);\n }\n }, [authToken, skip]);\n\n useEffect(() => {\n fetchWorkspaces();\n }, [fetchWorkspaces]);\n\n return {\n workspaces,\n loading,\n error,\n refetch: fetchWorkspaces,\n };\n}\n\ninterface InstallableVersion {\n version: string;\n status: string;\n isLatest: boolean;\n}\n\n/**\n * Hook to fetch an app's installable (published/deprecated) versions, for\n * the optional version picker. Skipped entirely when there's no linked\n * devportal applicationId (e.g. sideloaded or legacy listings) — those\n * always install whatever the store listing's current version is.\n */\nfunction useInstallableVersions(\n applicationId: string | undefined,\n skip: boolean\n): { versions: InstallableVersion[]; loading: boolean } {\n const { authToken } = useStore();\n const [versions, setVersions] = useState<InstallableVersion[]>([]);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (skip || !applicationId) {\n setVersions([]);\n return;\n }\n\n let cancelled = false;\n setLoading(true);\n\n graphqlFetch<{ installableApplicationVersions: InstallableVersion[] }>({\n gateway: 'global',\n query: INSTALLABLE_APPLICATION_VERSIONS_STRING,\n variables: { applicationId },\n authToken: authToken || undefined,\n })\n .then((result) => {\n if (cancelled) return;\n if (result.errors?.length) {\n setVersions([]);\n return;\n }\n setVersions(result.data?.installableApplicationVersions ?? []);\n })\n .catch(() => {\n if (!cancelled) setVersions([]);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [applicationId, authToken, skip]);\n\n return { versions, loading };\n}\n\n/**\n * InstallAppModal component\n *\n * Modal for selecting a workspace to install an app into\n */\nexport const InstallAppModal: FC<InstallAppModalProps> = ({\n isOpen,\n onClose,\n app,\n orderId,\n purchaseState = 'free',\n onInstallSuccess,\n onInstallError,\n}) => {\n const tr = useTr();\n const { authToken, user } = useStore();\n const [installState, dispatchInstall] = useReducer(installReducer, initialInstallState);\n const { step, selectedWorkspace, selectedVersion, installing, installError, installInfo } =\n installState;\n\n const appType = app.type?.toUpperCase() ?? '';\n const requiresConsent = CONSENT_REQUIRED_TYPES.has(appType);\n const supportsVersionChoice = STORE_DISTRIBUTED_TYPES.has(appType);\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n const { versions: installableVersions } = useInstallableVersions(\n app.applicationId,\n !isOpen || !supportsVersionChoice\n );\n\n // Reset step and selection whenever the modal is closed/reopened\n useEffect(() => {\n if (!isOpen) {\n dispatchInstall({ type: 'reset' });\n }\n }, [isOpen]);\n\n // Filter workspaces by status (only show active)\n const filteredWorkspaces = workspaces.filter((ws) => ws.status === 'ACTIVE');\n\n /**\n * Install purchased app via Store Service (NEW FLOW)\n * Used when installing from orders page or any already-purchased product\n */\n const handleInstallFromStore = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n const purchasedBy = user?.id;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n if (!orderId && purchaseState !== 'purchased') {\n if (!purchasedBy) {\n throw new Error('Unable to identify the current user for license creation');\n }\n\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_PRODUCT_ENTITLEMENT_STRING,\n variables: {\n input: {\n productId: app.id,\n purchasedBy,\n workspaceId: workspace.id,\n metadata: {\n source: 'store-ui',\n productType: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n }\n\n // Call Store Service to install the purchased app\n const result = await graphqlFetch<{\n installPurchasedApp: {\n success: boolean;\n alreadyInstalled?: boolean;\n installation?: {\n id: string;\n productId: string;\n workspaceId: string;\n status: string;\n version: string;\n };\n error?: string;\n message?: string;\n };\n }>({\n gateway: 'global', // Store Service\n query: INSTALL_PURCHASED_APP_STRING,\n variables: {\n orderId: orderId ?? null,\n productId: app.id,\n workspaceId: workspace.id,\n version: selectedVersion || undefined,\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installPurchasedApp;\n\n if (installResult?.success) {\n if (installResult.alreadyInstalled) {\n dispatchInstall({\n type: 'setInfo',\n info: installResult.message || 'Already installed on this workspace',\n });\n } else {\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n }\n } else {\n const rawMsg = installResult?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Install app via Workspace Service (LEGACY FLOW)\n * Used for free apps or when installing without order\n */\n const handleInstallViaWorkspace = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n // Step 1: Install to workspace service via graphqlFetch\n const installVars = {\n workspaceIds: [workspace.id],\n input: {\n appName: app.slug || app.name.toLowerCase().replace(/\\s+/g, '-'),\n displayName: app.name,\n version: app.version || '1.0.0',\n type: app.type || 'app',\n manifest: {\n productId: app.id,\n icon: app.icon,\n },\n },\n };\n\n const result = await graphqlFetch<{\n installWorkspaceApps: InstallWorkspaceAppsResult;\n }>({\n gateway: 'workspace',\n query: INSTALL_WORKSPACE_APPS_STRING,\n variables: installVars,\n authToken: authToken || undefined,\n workspaceId: workspace.id,\n workspaceToken: true,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installWorkspaceApps;\n if (installResult?.successCount && installResult.successCount > 0) {\n // Step 2: Record installation in store service (for tracking)\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n name: app.name,\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal: workspace installation succeeded; store record will sync later.\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } else {\n const rawMsg = installResult?.results?.[0]?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Advance from workspace selection to consent step (for integration types),\n * or go straight to install (for other types).\n */\n const handleContinue = () => {\n if (requiresConsent) {\n dispatchInstall({ type: 'advanceToConsent' });\n } else {\n void handleInstall();\n }\n };\n\n /**\n * Install INTEGRATION / API_INTEGRATION apps via the devportal installApp mutation.\n * After a successful install, redirects the user to the app's callback URL with\n * ?installation_id=<id> appended so the integration server can complete the handshake.\n */\n const handleInstallIntegration = async () => {\n if (!selectedWorkspace) return;\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n try {\n const result = await graphqlFetch<{\n installApp: {\n id: string;\n status: string;\n workspaceId: string;\n application: { oauthAuthUrl?: string | null };\n };\n }>({\n gateway: 'global',\n query: INSTALL_APP_DEVPORTAL_STRING,\n variables: {\n input: {\n applicationId: app.applicationId ?? app.id,\n scope: 'WORKSPACE',\n workspaceId: workspace.id,\n consentedPermissions: app.permissions ?? [],\n },\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installApp;\n if (!installResult) throw new Error('Installation failed');\n\n // Record the integration install in the store service so the product page\n // shows \"Installed\" and it appears in the Installed Apps management page.\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n name: app.name,\n applicationId: app.applicationId,\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n installationId: installResult.id,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal — devportal installation succeeded; store record will sync later.\n }\n\n const callbackUrl = installResult.application?.oauthAuthUrl;\n if (callbackUrl) {\n window.open(`${callbackUrl}?installation_id=${installResult.id}`, '_blank');\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Main install handler - routes to appropriate install function\n */\n const handleInstall = async () => {\n if (CONSENT_REQUIRED_TYPES.has(appType)) {\n // Integration apps use the devportal installApp mutation with post-install redirect.\n await handleInstallIntegration();\n } else if (purchaseState === 'purchased' || orderId || STORE_DISTRIBUTED_TYPES.has(appType)) {\n // Purchased products install through the store service.\n await handleInstallFromStore();\n } else {\n // Free products install through the workspace service.\n await handleInstallViaWorkspace();\n }\n };\n\n const selectedWorkspaceObj = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"mx-4 w-full max-w-lg gap-0 p-0\">\n {/* Header */}\n <DialogHeader className=\"flex flex-row items-center justify-between space-y-0 border-b border-border-default px-6 py-4 text-left\">\n <div className=\"flex items-center gap-3\">\n {app.icon ? (\n <img src={app.icon} alt={app.name} className=\"size-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted\">\n {app.name.charAt(0)}\n </div>\n )}\n <div>\n <DialogTitle className=\"text-lg font-semibold text-text-primary\">\n {step === 'consent'\n ? tr('installApp.reviewPermissions', 'Review Permissions')\n : tr('installApp.title', 'Install App')}\n </DialogTitle>\n <DialogDescription className=\"text-sm text-text-muted\">{app.name}</DialogDescription>\n </div>\n </div>\n {/* Step indicator for consent-gated types */}\n {requiresConsent && (\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted shrink-0\">\n <span className={step === 'workspace' ? 'font-semibold text-text-primary' : ''}>\n 1\n </span>\n <ChevronRight className=\"size-3\" />\n <span className={step === 'consent' ? 'font-semibold text-text-primary' : ''}>2</span>\n </div>\n )}\n </DialogHeader>\n\n {/* ── Step 1: Workspace selection ── */}\n {step === 'workspace' && (\n <div className=\"px-6 py-4\">\n <p className=\"mb-4 text-text-secondary\">\n {tr(\n 'installApp.selectWorkspace',\n 'Select the workspace where you want to install this app:'\n )}\n </p>\n\n {/* Workspace List */}\n <div className=\"max-h-72 overflow-y-auto rounded-lg border border-border-seam\">\n {loading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 animate-spin text-text-muted\" />\n </div>\n ) : error ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle className=\"mb-2 size-8 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">\n {tr('installApp.failedToLoadWorkspaces', 'Failed to load workspaces')}\n </p>\n <p className=\"text-xs text-text-muted mt-1\">{error.message}</p>\n </div>\n ) : filteredWorkspaces.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Building2 className=\"mb-2 size-8 text-text-muted\" />\n <p className=\"text-sm text-text-muted\">\n {tr('installApp.noWorkspaces', 'No workspaces available')}\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredWorkspaces.map((workspace) => (\n <button\n type=\"button\"\n key={workspace.id}\n onClick={() =>\n dispatchInstall({ type: 'selectWorkspace', workspaceId: workspace.id })\n }\n className={`w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${\n selectedWorkspace === workspace.id\n ? 'bg-action-primary-bg/10'\n : 'hover:bg-bg-sunken'\n }`}\n >\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link\">\n <Building2 className=\"size-5\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{workspace.name}</p>\n {workspace.slug ? (\n <p className=\"text-xs text-text-muted truncate\">/{workspace.slug}</p>\n ) : null}\n </div>\n {selectedWorkspace === workspace.id && (\n <CheckCircle className=\"size-5 flex-shrink-0 text-action-primary-bg\" />\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Optional version picker — only shown when the app has more\n than one installable (published/deprecated) version. */}\n {supportsVersionChoice && installableVersions.length > 1 && (\n <div className=\"mt-4\">\n <label\n htmlFor=\"install-app-version\"\n className=\"mb-1.5 block text-xs font-medium text-text-secondary\"\n >\n {tr('installApp.version', 'Version')}\n </label>\n <select\n id=\"install-app-version\"\n value={selectedVersion}\n onChange={(event) =>\n dispatchInstall({ type: 'selectVersion', version: event.target.value })\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">\n {app.version\n ? tr('installApp.latestWithVersion', 'Latest ({{version}})', {\n version: app.version,\n })\n : tr('installApp.latest', 'Latest')}\n </option>\n {installableVersions\n .filter((v) => !v.isLatest)\n .map((v) => (\n <option key={v.version} value={v.version}>\n {tr('installApp.versionWithStatus', '{{version}} ({{status}})', {\n version: v.version,\n status: v.status,\n })}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n )}\n\n {/* ── Step 2: Permissions consent (integration types only) ── */}\n {step === 'consent' && (\n <div className=\"px-6 py-4\">\n {/* Selected workspace reminder */}\n {selectedWorkspaceObj && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\">\n <Building2 className=\"size-4 shrink-0 text-text-muted\" />\n <span className=\"text-sm text-text-secondary\">\n {tr('installApp.installingInto', 'Installing into')}{' '}\n <span className=\"font-medium text-text-primary\">{selectedWorkspaceObj.name}</span>\n </span>\n </div>\n )}\n\n {/* Shield header */}\n <div className=\"mb-4 flex items-center gap-2\">\n <ShieldCheck className=\"size-5 text-status-success-text\" />\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr('installApp.permissionsRequested', 'Permissions requested')}\n </p>\n </div>\n\n <p className=\"mb-4 text-sm text-text-secondary\">\n {tr(\n 'installApp.consentDescription',\n 'By clicking {{button}}, you grant {{appName}} access to the following in the selected workspace:',\n {\n button: tr('installApp.acceptAndInstall', 'Accept & Install'),\n appName: app.name,\n }\n )}\n </p>\n\n {/* Permission list — specific scopes if available, generic grant otherwise */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface px-4 py-3\">\n {app.permissions && app.permissions.length > 0 ? (\n <ul className=\"space-y-2\">\n {app.permissions.map((perm) => (\n <li key={perm} className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {perm}\n </li>\n ))}\n </ul>\n ) : (\n <ul className=\"space-y-2\">\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionReadWrite',\n 'Read and write data within this workspace on your behalf'\n )}\n </li>\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionWebhooks',\n 'Receive webhook events for workspace activity'\n )}\n </li>\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionAuthorized',\n 'Act as an authorized integration within this workspace'\n )}\n </li>\n </ul>\n )}\n </div>\n\n <p className=\"mt-3 text-xs text-text-muted\">\n {tr(\n 'installApp.revokeHint',\n 'You can revoke access at any time from your Installed Apps settings.'\n )}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <DialogFooter className=\"block border-t border-border-default px-6 py-4 sm:flex-col sm:items-stretch sm:space-x-0\">\n {installInfo && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-info-bg-subtle px-3 py-2 text-sm text-status-info-text\">\n <Info className=\"size-4 flex-shrink-0\" />\n <span>{installInfo}</span>\n </div>\n )}\n {installError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-error-bg-subtle/10 px-3 py-2 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n {step === 'consent' ? (\n <>\n <button\n type=\"button\"\n onClick={() => dispatchInstall({ type: 'backToWorkspace' })}\n disabled={installing}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('common.back', 'Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleInstall()}\n disabled={installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('installApp.installing', 'Installing…')}\n </>\n ) : (\n tr('installApp.acceptAndInstall', 'Accept & Install')\n )}\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={installing}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleContinue}\n disabled={!selectedWorkspace || installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('installApp.installing', 'Installing…')}\n </>\n ) : requiresConsent ? (\n <>\n {tr('common.continue', 'Continue')}\n <ChevronRight className=\"size-4\" />\n </>\n ) : (\n tr('installApp.installApp', 'Install App')\n )}\n </button>\n </>\n )}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"InstallAppModal.js","names":[],"sources":["../../src/components/InstallAppModal.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useCallback, useReducer } from 'react';\nimport {\n Loader2,\n Building2,\n CheckCircle,\n AlertCircle,\n Info,\n ShieldCheck,\n ChevronRight,\n ArrowLeft,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n DialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { useTr } from '../shared/hooks/useTr';\n\n// Local type definitions for workspace operations\ninterface Workspace {\n id: string;\n name: string;\n slug: string;\n type?: string;\n status?: string;\n organizationId?: string;\n}\n\ninterface WorkspaceList {\n items: Workspace[];\n total: number;\n}\n\ninterface InstallWorkspaceAppsResult {\n successCount: number;\n failedCount: number;\n results: Array<{\n success: boolean;\n workspaceId: string;\n app?: unknown;\n error?: string;\n }>;\n}\n\n// String queries for graphqlFetch\nconst MY_WORKSPACES_STRING = `query MyWorkspaces { myWorkspaces { items { id name slug type status organizationId } total } }`;\nconst INSTALL_WORKSPACE_APPS_STRING = `mutation InstallWorkspaceApps($workspaceIds: [ID!]!, $input: InstallWorkspaceAppInput!) { installWorkspaceApps(workspaceIds: $workspaceIds, input: $input) { successCount failedCount results { success workspaceId error } } }`;\nconst RECORD_APP_INSTALLATION_STRING = `mutation RecordAppInstallation($input: RecordAppInstallationInput!) { recordAppInstallation(input: $input) { id status workspaceId version } }`;\nconst INSTALL_PURCHASED_APP_STRING = `mutation InstallPurchasedApp($orderId: ID, $productId: String!, $workspaceId: String!, $version: String) { installPurchasedApp(orderId: $orderId, productId: $productId, workspaceId: $workspaceId, version: $version) { success alreadyInstalled installation { id productId workspaceId status version } error message } }`;\nconst CREATE_PRODUCT_ENTITLEMENT_STRING = `mutation CreateProductEntitlement($input: CreateProductEntitlementInput!) { createProductEntitlement(input: $input) { id status productId workspaceId } }`;\nconst INSTALL_APP_DEVPORTAL_STRING = `mutation InstallAppDevportal($input: InstallAppInput!) { installApp(input: $input) { id status workspaceId application { oauthAuthUrl } } }`;\nconst INSTALLABLE_APPLICATION_VERSIONS_STRING = `query InstallableApplicationVersions($applicationId: ID!) { installableApplicationVersions(applicationId: $applicationId) { version status isLatest } }`;\n\nconst STORE_DISTRIBUTED_TYPES = new Set([\n 'VIBEMODULE',\n 'NODE',\n 'CONSOLE',\n 'DASHBOARD',\n 'PARSER',\n 'DATASINK',\n]);\n\n// App types that require an explicit permissions consent step before install\nconst CONSENT_REQUIRED_TYPES = new Set(['INTEGRATION', 'API_INTEGRATION']);\n\nconst INSTALL_ERROR_MESSAGES: Record<string, string> = {\n ALREADY_INSTALLED: 'This app is already installed in the selected workspace.',\n QUOTA_EXCEEDED: 'Installation limit reached. Please upgrade your plan or contact support.',\n PERMISSION_DENIED: 'You do not have permission to install apps in this workspace.',\n NOT_FOUND: 'The app or workspace could not be found.',\n WORKSPACE_INACTIVE: 'The selected workspace is inactive.',\n ENTITLEMENT_EXPIRED: 'Your license for this app has expired.',\n UNSUPPORTED_TYPE: 'This product type cannot be installed in the selected workspace.',\n};\n\nfunction mapInstallError(raw: string): string {\n for (const [code, message] of Object.entries(INSTALL_ERROR_MESSAGES)) {\n if (raw.toUpperCase().includes(code)) return message;\n }\n return 'Installation failed. Please try again or contact support.';\n}\n\ntype InstallStep = 'workspace' | 'consent';\n\ninterface InstallState {\n step: InstallStep;\n selectedWorkspace: string | null;\n selectedVersion: string;\n installing: boolean;\n installError: string | null;\n installInfo: string | null;\n}\n\ntype InstallAction =\n | { type: 'reset' }\n | { type: 'selectWorkspace'; workspaceId: string }\n | { type: 'selectVersion'; version: string }\n | { type: 'advanceToConsent' }\n | { type: 'backToWorkspace' }\n | { type: 'startInstall' }\n | { type: 'finishInstall' }\n | { type: 'setError'; error: string }\n | { type: 'setInfo'; info: string };\n\nconst initialInstallState: InstallState = {\n step: 'workspace',\n selectedWorkspace: null,\n selectedVersion: '',\n installing: false,\n installError: null,\n installInfo: null,\n};\n\nfunction installReducer(state: InstallState, action: InstallAction): InstallState {\n switch (action.type) {\n case 'reset':\n return initialInstallState;\n case 'selectWorkspace':\n return { ...state, selectedWorkspace: action.workspaceId };\n case 'selectVersion':\n return { ...state, selectedVersion: action.version };\n case 'advanceToConsent':\n return { ...state, step: 'consent', installError: null };\n case 'backToWorkspace':\n return { ...state, step: 'workspace', installError: null };\n case 'startInstall':\n return { ...state, installing: true, installError: null, installInfo: null };\n case 'finishInstall':\n return { ...state, installing: false };\n case 'setError':\n return { ...state, installError: action.error };\n case 'setInfo':\n return { ...state, installInfo: action.info };\n default:\n return state;\n }\n}\n\nexport interface AppInfo {\n id: string;\n // devportal Application.id — equals StoreProduct.productId, used for installApp mutation\n applicationId?: string;\n name: string;\n slug?: string;\n icon?: string;\n type?: string;\n version?: string;\n permissions?: string[];\n}\n\ninterface InstallAppModalProps {\n isOpen: boolean;\n onClose: () => void;\n app: AppInfo;\n orderId?: string | null; // For purchased apps from orders page\n purchaseState?: 'free' | 'purchased';\n onInstallSuccess?: (workspaceId: string, workspaceName: string) => void;\n onInstallError?: (error: Error) => void;\n}\n\ninterface UseWorkspacesResult {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// NOTE: Inline Apollo clients removed. All GraphQL calls now use graphqlFetch\n// from fe-libs which automatically resolves gateway URLs and injects auth + locale headers.\n\n/**\n * Hook to fetch user's workspaces using graphqlFetch from fe-libs\n */\nfunction useWorkspaces(skip: boolean): UseWorkspacesResult {\n const { authToken } = useStore();\n const [workspaces, setWorkspaces] = useState<Workspace[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchWorkspaces = useCallback(async () => {\n if (skip) return;\n\n setLoading(true);\n setError(null);\n\n try {\n const result = await graphqlFetch<{ myWorkspaces: WorkspaceList }>({\n gateway: 'global',\n query: MY_WORKSPACES_STRING,\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'GraphQL error');\n }\n\n setWorkspaces(result.data?.myWorkspaces?.items || []);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch workspaces'));\n } finally {\n setLoading(false);\n }\n }, [authToken, skip]);\n\n useEffect(() => {\n fetchWorkspaces();\n }, [fetchWorkspaces]);\n\n return {\n workspaces,\n loading,\n error,\n refetch: fetchWorkspaces,\n };\n}\n\ninterface InstallableVersion {\n version: string;\n status: string;\n isLatest: boolean;\n}\n\n/**\n * Hook to fetch an app's installable (published/deprecated) versions, for\n * the optional version picker. Skipped entirely when there's no linked\n * devportal applicationId (e.g. sideloaded or legacy listings) — those\n * always install whatever the store listing's current version is.\n */\nfunction useInstallableVersions(\n applicationId: string | undefined,\n skip: boolean\n): { versions: InstallableVersion[]; loading: boolean } {\n const { authToken } = useStore();\n const [versions, setVersions] = useState<InstallableVersion[]>([]);\n const [loading, setLoading] = useState(false);\n\n useEffect(() => {\n if (skip || !applicationId) {\n setVersions([]);\n return;\n }\n\n let cancelled = false;\n setLoading(true);\n\n graphqlFetch<{ installableApplicationVersions: InstallableVersion[] }>({\n gateway: 'global',\n query: INSTALLABLE_APPLICATION_VERSIONS_STRING,\n variables: { applicationId },\n authToken: authToken || undefined,\n })\n .then((result) => {\n if (cancelled) return;\n if (result.errors?.length) {\n setVersions([]);\n return;\n }\n setVersions(result.data?.installableApplicationVersions ?? []);\n })\n .catch(() => {\n if (!cancelled) setVersions([]);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [applicationId, authToken, skip]);\n\n return { versions, loading };\n}\n\n/**\n * InstallAppModal component\n *\n * Modal for selecting a workspace to install an app into\n */\nexport const InstallAppModal: FC<InstallAppModalProps> = ({\n isOpen,\n onClose,\n app,\n orderId,\n purchaseState = 'free',\n onInstallSuccess,\n onInstallError,\n}) => {\n const tr = useTr();\n const { authToken, user } = useStore();\n const [installState, dispatchInstall] = useReducer(installReducer, initialInstallState);\n const { step, selectedWorkspace, selectedVersion, installing, installError, installInfo } =\n installState;\n\n const appType = app.type?.toUpperCase() ?? '';\n const requiresConsent = CONSENT_REQUIRED_TYPES.has(appType);\n const supportsVersionChoice = STORE_DISTRIBUTED_TYPES.has(appType);\n\n const { workspaces, loading, error } = useWorkspaces(!isOpen);\n const { versions: installableVersions } = useInstallableVersions(\n app.applicationId,\n !isOpen || !supportsVersionChoice\n );\n\n // Reset step and selection whenever the modal is closed/reopened\n useEffect(() => {\n if (!isOpen) {\n dispatchInstall({ type: 'reset' });\n }\n }, [isOpen]);\n\n // Filter workspaces by status (only show active)\n const filteredWorkspaces = workspaces.filter((ws) => ws.status === 'ACTIVE');\n\n /**\n * Install purchased app via Store Service (NEW FLOW)\n * Used when installing from orders page or any already-purchased product\n */\n const handleInstallFromStore = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n const purchasedBy = user?.id;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n if (!orderId && purchaseState !== 'purchased') {\n if (!purchasedBy) {\n throw new Error('Unable to identify the current user for license creation');\n }\n\n await graphqlFetch({\n gateway: 'global',\n query: CREATE_PRODUCT_ENTITLEMENT_STRING,\n variables: {\n input: {\n productId: app.id,\n purchasedBy,\n workspaceId: workspace.id,\n metadata: {\n source: 'store-ui',\n productType: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n }\n\n // Call Store Service to install the purchased app\n const result = await graphqlFetch<{\n installPurchasedApp: {\n success: boolean;\n alreadyInstalled?: boolean;\n installation?: {\n id: string;\n productId: string;\n workspaceId: string;\n status: string;\n version: string;\n };\n error?: string;\n message?: string;\n };\n }>({\n gateway: 'global', // Store Service\n query: INSTALL_PURCHASED_APP_STRING,\n variables: {\n orderId: orderId ?? null,\n productId: app.id,\n workspaceId: workspace.id,\n version: selectedVersion || undefined,\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installPurchasedApp;\n\n if (installResult?.success) {\n if (installResult.alreadyInstalled) {\n dispatchInstall({\n type: 'setInfo',\n info: installResult.message || 'Already installed on this workspace',\n });\n } else {\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n }\n } else {\n const rawMsg = installResult?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Install app via Workspace Service (LEGACY FLOW)\n * Used for free apps or when installing without order\n */\n const handleInstallViaWorkspace = async () => {\n if (!selectedWorkspace) return;\n\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n\n try {\n // Step 1: Install to workspace service via graphqlFetch\n const installVars = {\n workspaceIds: [workspace.id],\n input: {\n appName: app.slug || app.name.toLowerCase().replace(/\\s+/g, '-'),\n displayName: app.name,\n version: app.version || '1.0.0',\n type: app.type || 'app',\n manifest: {\n productId: app.id,\n icon: app.icon,\n },\n },\n };\n\n const result = await graphqlFetch<{\n installWorkspaceApps: InstallWorkspaceAppsResult;\n }>({\n gateway: 'workspace',\n query: INSTALL_WORKSPACE_APPS_STRING,\n variables: installVars,\n authToken: authToken || undefined,\n workspaceId: workspace.id,\n workspaceToken: true,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installWorkspaceApps;\n if (installResult?.successCount && installResult.successCount > 0) {\n // Step 2: Record installation in store service (for tracking)\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n name: app.name,\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal: workspace installation succeeded; store record will sync later.\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } else {\n const rawMsg = installResult?.results?.[0]?.error || 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n }\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Advance from workspace selection to consent step (for integration types),\n * or go straight to install (for other types).\n */\n const handleContinue = () => {\n if (requiresConsent) {\n dispatchInstall({ type: 'advanceToConsent' });\n } else {\n void handleInstall();\n }\n };\n\n /**\n * Install INTEGRATION / API_INTEGRATION apps via the devportal installApp mutation.\n * After a successful install, redirects the user to the app's callback URL with\n * ?installation_id=<id> appended so the integration server can complete the handshake.\n */\n const handleInstallIntegration = async () => {\n if (!selectedWorkspace) return;\n const workspace = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n if (!workspace) return;\n\n dispatchInstall({ type: 'startInstall' });\n try {\n const result = await graphqlFetch<{\n installApp: {\n id: string;\n status: string;\n workspaceId: string;\n application: { oauthAuthUrl?: string | null };\n };\n }>({\n gateway: 'global',\n query: INSTALL_APP_DEVPORTAL_STRING,\n variables: {\n input: {\n applicationId: app.applicationId ?? app.id,\n scope: 'WORKSPACE',\n workspaceId: workspace.id,\n consentedPermissions: app.permissions ?? [],\n },\n },\n authToken: authToken || undefined,\n });\n\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Installation failed');\n }\n\n const installResult = result.data?.installApp;\n if (!installResult) throw new Error('Installation failed');\n\n // Record the integration install in the store service so the product page\n // shows \"Installed\" and it appears in the Installed Apps management page.\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RECORD_APP_INSTALLATION_STRING,\n variables: {\n input: {\n productId: app.id,\n workspaceId: workspace.id,\n workspaceName: workspace.name,\n workspaceType: workspace.type,\n organizationId: workspace.organizationId,\n version: app.version || '1.0.0',\n manifest: {\n name: app.name,\n applicationId: app.applicationId,\n productId: app.id,\n icon: app.icon,\n slug: app.slug,\n type: app.type,\n installationId: installResult.id,\n },\n },\n },\n authToken: authToken || undefined,\n });\n } catch {\n // Non-fatal — devportal installation succeeded; store record will sync later.\n }\n\n const callbackUrl = installResult.application?.oauthAuthUrl;\n if (callbackUrl) {\n window.open(`${callbackUrl}?installation_id=${installResult.id}`, '_blank');\n }\n\n onInstallSuccess?.(workspace.id, workspace.name);\n onClose();\n } catch (err) {\n const rawMsg = err instanceof Error ? err.message : 'Installation failed';\n const errorMsg = mapInstallError(rawMsg);\n dispatchInstall({ type: 'setError', error: errorMsg });\n onInstallError?.(new Error(rawMsg));\n } finally {\n dispatchInstall({ type: 'finishInstall' });\n }\n };\n\n /**\n * Main install handler - routes to appropriate install function\n */\n const handleInstall = async () => {\n if (CONSENT_REQUIRED_TYPES.has(appType)) {\n // Integration apps use the devportal installApp mutation with post-install redirect.\n await handleInstallIntegration();\n } else if (purchaseState === 'purchased' || orderId || STORE_DISTRIBUTED_TYPES.has(appType)) {\n // Purchased products install through the store service.\n await handleInstallFromStore();\n } else {\n // Free products install through the workspace service.\n await handleInstallViaWorkspace();\n }\n };\n\n const selectedWorkspaceObj = filteredWorkspaces.find((ws) => ws.id === selectedWorkspace);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"mx-4 w-full max-w-lg gap-0 p-0\">\n {/* Header */}\n <DialogHeader className=\"flex flex-row items-center justify-between space-y-0 border-b border-border-default px-6 py-4 text-left\">\n <div className=\"flex items-center gap-3\">\n {app.icon ? (\n <img src={app.icon} alt={app.name} className=\"size-10 rounded-lg object-cover\" />\n ) : (\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-bg-sunken text-lg font-bold text-text-muted\">\n {app.name.charAt(0)}\n </div>\n )}\n <div>\n <DialogTitle className=\"text-lg font-semibold text-text-primary\">\n {step === 'consent'\n ? tr('installApp.reviewPermissions', 'Review Permissions')\n : tr('installApp.title', 'Install App')}\n </DialogTitle>\n <DialogDescription className=\"text-sm text-text-muted\">{app.name}</DialogDescription>\n </div>\n </div>\n {/* Step indicator for consent-gated types */}\n {requiresConsent && (\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted shrink-0\">\n <span className={step === 'workspace' ? 'font-semibold text-text-primary' : ''}>\n 1\n </span>\n <ChevronRight className=\"size-3\" />\n <span className={step === 'consent' ? 'font-semibold text-text-primary' : ''}>2</span>\n </div>\n )}\n </DialogHeader>\n\n {/* ── Step 1: Workspace selection ── */}\n {step === 'workspace' && (\n <div className=\"px-6 py-4\">\n <p className=\"mb-4 text-text-secondary\">\n {tr(\n 'installApp.selectWorkspace',\n 'Select the workspace where you want to install this app:'\n )}\n </p>\n\n {/* Workspace List */}\n <div className=\"max-h-72 overflow-y-auto rounded-lg border border-border-seam\">\n {loading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 animate-spin text-text-muted\" />\n </div>\n ) : error ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle className=\"mb-2 size-8 text-status-error-text\" />\n <p className=\"text-sm text-status-error-text\">\n {tr('installApp.failedToLoadWorkspaces', 'Failed to load workspaces')}\n </p>\n <p className=\"text-xs text-text-muted mt-1\">{error.message}</p>\n </div>\n ) : filteredWorkspaces.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Building2 className=\"mb-2 size-8 text-text-muted\" />\n <p className=\"text-sm text-text-muted\">\n {tr('installApp.noWorkspaces', 'No workspaces available')}\n </p>\n </div>\n ) : (\n <div className=\"divide-y divide-border-default\">\n {filteredWorkspaces.map((workspace) => (\n <button\n type=\"button\"\n key={workspace.id}\n onClick={() =>\n dispatchInstall({ type: 'selectWorkspace', workspaceId: workspace.id })\n }\n className={`w-full cursor-pointer flex items-center gap-3 px-4 py-3 text-left transition-colors ${\n selectedWorkspace === workspace.id\n ? 'bg-action-primary-bg/10'\n : 'hover:bg-bg-sunken'\n }`}\n >\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-text-link\">\n <Building2 className=\"size-5\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-text-primary truncate\">{workspace.name}</p>\n {workspace.slug ? (\n <p className=\"text-xs text-text-muted truncate\">/{workspace.slug}</p>\n ) : null}\n </div>\n {selectedWorkspace === workspace.id && (\n <CheckCircle className=\"size-5 flex-shrink-0 text-action-primary-bg\" />\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Optional version picker — only shown when the app has more\n than one installable (published/deprecated) version. */}\n {supportsVersionChoice && installableVersions.length > 1 && (\n <div className=\"mt-4\">\n <label\n htmlFor=\"install-app-version\"\n className=\"mb-1.5 block text-xs font-medium text-text-secondary\"\n >\n {tr('installApp.version', 'Version')}\n </label>\n <select\n id=\"install-app-version\"\n value={selectedVersion}\n onChange={(event) =>\n dispatchInstall({ type: 'selectVersion', version: event.target.value })\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary scheme-normal dark:scheme-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {app.version\n ? tr('installApp.latestWithVersion', 'Latest ({{version}})', {\n version: app.version,\n })\n : tr('installApp.latest', 'Latest')}\n </option>\n {installableVersions\n .filter((v) => !v.isLatest)\n .map((v) => (\n <option key={v.version} value={v.version}>\n {tr('installApp.versionWithStatus', '{{version}} ({{status}})', {\n version: v.version,\n status: v.status,\n })}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n )}\n\n {/* ── Step 2: Permissions consent (integration types only) ── */}\n {step === 'consent' && (\n <div className=\"px-6 py-4\">\n {/* Selected workspace reminder */}\n {selectedWorkspaceObj && (\n <div className=\"mb-4 flex items-center gap-2 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\">\n <Building2 className=\"size-4 shrink-0 text-text-muted\" />\n <span className=\"text-sm text-text-secondary\">\n {tr('installApp.installingInto', 'Installing into')}{' '}\n <span className=\"font-medium text-text-primary\">{selectedWorkspaceObj.name}</span>\n </span>\n </div>\n )}\n\n {/* Shield header */}\n <div className=\"mb-4 flex items-center gap-2\">\n <ShieldCheck className=\"size-5 text-status-success-text\" />\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr('installApp.permissionsRequested', 'Permissions requested')}\n </p>\n </div>\n\n <p className=\"mb-4 text-sm text-text-secondary\">\n {tr(\n 'installApp.consentDescription',\n 'By clicking {{button}}, you grant {{appName}} access to the following in the selected workspace:',\n {\n button: tr('installApp.acceptAndInstall', 'Accept & Install'),\n appName: app.name,\n }\n )}\n </p>\n\n {/* Permission list — specific scopes if available, generic grant otherwise */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface px-4 py-3\">\n {app.permissions && app.permissions.length > 0 ? (\n <ul className=\"space-y-2\">\n {app.permissions.map((perm) => (\n <li key={perm} className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {perm}\n </li>\n ))}\n </ul>\n ) : (\n <ul className=\"space-y-2\">\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionReadWrite',\n 'Read and write data within this workspace on your behalf'\n )}\n </li>\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionWebhooks',\n 'Receive webhook events for workspace activity'\n )}\n </li>\n <li className=\"flex items-center gap-2.5 text-sm text-text-primary\">\n <CheckCircle className=\"size-4 shrink-0 text-status-success-text\" />\n {tr(\n 'installApp.permissionAuthorized',\n 'Act as an authorized integration within this workspace'\n )}\n </li>\n </ul>\n )}\n </div>\n\n <p className=\"mt-3 text-xs text-text-muted\">\n {tr(\n 'installApp.revokeHint',\n 'You can revoke access at any time from your Installed Apps settings.'\n )}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <DialogFooter className=\"block border-t border-border-default px-6 py-4 sm:flex-col sm:items-stretch sm:space-x-0\">\n {installInfo && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-info-bg-subtle px-3 py-2 text-sm text-status-info-text\">\n <Info className=\"size-4 flex-shrink-0\" />\n <span>{installInfo}</span>\n </div>\n )}\n {installError && (\n <div className=\"mb-3 flex items-center gap-2 rounded-lg bg-status-error-bg-subtle/10 px-3 py-2 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <span>{installError}</span>\n </div>\n )}\n <div className=\"flex items-center justify-end gap-3\">\n {step === 'consent' ? (\n <>\n <button\n type=\"button\"\n onClick={() => dispatchInstall({ type: 'backToWorkspace' })}\n disabled={installing}\n className=\"cursor-pointer inline-flex items-center gap-1.5 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('common.back', 'Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleInstall()}\n disabled={installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('installApp.installing', 'Installing…')}\n </>\n ) : (\n tr('installApp.acceptAndInstall', 'Accept & Install')\n )}\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={installing}\n className=\"cursor-pointer rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleContinue}\n disabled={!selectedWorkspace || installing}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-2\"\n >\n {installing ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('installApp.installing', 'Installing…')}\n </>\n ) : requiresConsent ? (\n <>\n {tr('common.continue', 'Continue')}\n <ChevronRight className=\"size-4\" />\n </>\n ) : (\n tr('installApp.installApp', 'Install App')\n )}\n </button>\n </>\n )}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
@@ -523,7 +523,7 @@ var C = (e) => e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `${(e / 1e3).t
523
523
  "aria-label": e,
524
524
  value: t,
525
525
  onChange: (e) => r(e.target.value),
526
- className: "min-w-[8rem] appearance-none bg-transparent pr-6 text-sm font-medium text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
526
+ className: "min-w-[8rem] appearance-none bg-bg-surface pr-6 text-sm font-medium text-text-primary scheme-normal dark:scheme-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] [&_option]:bg-bg-surface [&_option]:text-text-primary",
527
527
  children: n.map((e) => /* @__PURE__ */ x("option", {
528
528
  value: e.value,
529
529
  children: e.label
@@ -1 +1 @@
1
- {"version":3,"file":"MarketplaceHomePage.js","names":[],"sources":["../../src/pages/MarketplaceHomePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useMemo, useId, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Search,\n Star,\n Download,\n ShoppingBag,\n ChevronRight,\n ChevronDown,\n Sparkles,\n TrendingUp,\n Clock,\n Bot,\n Brain,\n LayoutDashboard,\n GitBranch,\n Box,\n Puzzle,\n ShoppingCart,\n X,\n AlertCircle,\n Gift,\n Filter,\n SlidersHorizontal,\n Loader2,\n FileCode2,\n Database,\n type LucideIcon,\n} from 'lucide-react';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { CartDrawer } from '../components/cart/CartDrawer';\nimport {\n useHomeGroupedProducts,\n useBrowseStoreWithContext,\n type StoreProduct,\n} from '../hooks/useStoreGraphQL';\nimport { formatPrice } from '../utils';\nimport type { BrowseStoreInput } from '../generated/global-types';\nimport { StoreSortBy, PricingModel } from '../generated/global-types';\nimport { useGetMyStoreInstallationsQuery } from '../generated/global-operations';\nimport { useTr } from '../shared/hooks/useTr';\nimport {\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// ========================================\n// Helper Types and Functions\n// ========================================\n\ntype ProductTypeKey =\n | 'BOTAPP'\n | 'AGENT'\n | 'CONSOLE'\n | 'WORKFLOW'\n | 'NODE'\n | 'WIDGET'\n | 'VIBEMODULE'\n | 'DASHBOARD'\n | 'PARSER'\n | 'DATASINK'\n | 'APP'\n | 'TEMPLATE'\n | 'INTEGRATION'\n | 'EXTENSION'\n | 'THEME'\n | 'PLUGIN'\n | 'COMPONENT'\n | string;\n\nconst formatDownloads = (count: number): string => {\n if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;\n if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;\n return count.toString();\n};\n\nconst getPriceDisplay = (\n product: StoreProduct,\n tr?: (key: string, fallback: string) => string\n): string => {\n if (product.pricingModel === 'FREE') return tr ? tr('pages.marketplace.free', 'Free') : 'Free';\n if (product.price === 0) return tr ? tr('pages.marketplace.free', 'Free') : 'Free';\n return formatPrice(product.price, product.currency);\n};\n\nconst getProductColor = (type: ProductTypeKey): string => {\n const colors: Record<string, string> = {\n BOTAPP: 'var(--color-accent-blue)',\n AGENT: 'var(--color-accent-purple)',\n CONSOLE: 'var(--color-accent-teal)',\n WORKFLOW: 'var(--color-status-warning-bg)',\n NODE: 'var(--color-status-error-bg)',\n WIDGET: 'var(--color-accent-purple)',\n VIBEMODULE: 'var(--color-accent-blue)',\n DASHBOARD: 'var(--color-accent-blue)',\n PARSER: 'var(--color-status-warning-bg)',\n DATASINK: 'var(--color-status-success-bg)',\n APP: 'var(--color-accent-indigo)',\n TEMPLATE: 'var(--color-accent-teal)',\n INTEGRATION: 'var(--color-status-warning-bg)',\n EXTENSION: 'var(--color-accent-purple)',\n THEME: 'var(--color-accent-purple)',\n PLUGIN: 'var(--color-status-success-bg)',\n COMPONENT: 'var(--color-accent-blue)',\n };\n return colors[type] || 'var(--color-text-muted)';\n};\n\n// Helper to get translucent background color from a token\nconst getProductBgColor = (type: ProductTypeKey): string => {\n const color = getProductColor(type);\n return `color-mix(in srgb, ${color}, transparent 85%)`;\n};\n\n// Component wrapper to render type icons\ninterface ProductTypeIconProps {\n type: ProductTypeKey;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst TYPE_ICONS: Record<string, LucideIcon> = {\n BOTAPP: Bot,\n AGENT: Brain,\n CONSOLE: LayoutDashboard,\n WORKFLOW: GitBranch,\n NODE: Box,\n WIDGET: Puzzle,\n VIBEMODULE: Puzzle,\n DASHBOARD: LayoutDashboard,\n PARSER: FileCode2,\n DATASINK: Database,\n APP: Box,\n TEMPLATE: LayoutDashboard,\n INTEGRATION: GitBranch,\n EXTENSION: Puzzle,\n THEME: Puzzle,\n PLUGIN: Puzzle,\n COMPONENT: Box,\n};\n\nconst ProductTypeIcon: FC<ProductTypeIconProps> = ({ type, className, style }) => {\n const IconComponent = TYPE_ICONS[type] || Box;\n return <IconComponent className={className} style={style} />;\n};\n\n// ========================================\n// App Card Component\n// ========================================\n\ninterface AppCardProps {\n product: StoreProduct;\n onClick: () => void;\n variant?: 'default' | 'compact' | 'featured';\n}\n\nconst AppCard: FC<AppCardProps> = ({ product, onClick, variant = 'default' }) => {\n const tr = useTr();\n const productColor = getProductColor(product.type);\n\n if (variant === 'featured') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group relative flex size-full cursor-pointer flex-col overflow-hidden rounded-2xl border border-border-seam bg-gradient-to-br from-bg-surface to-bg-sunken p-6 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {product.featured && (\n <div className=\"absolute right-4 top-4 flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.featured', 'Featured')}\n </div>\n )}\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-16 flex-shrink-0 items-center justify-center rounded-2xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-10\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h3>\n <p className=\"flex items-center gap-2 text-sm text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3.5\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-4 line-clamp-2 flex-1 text-sm text-text-muted\">\n {product.description || tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n {product.nature === 'PHYSICAL' ? (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <ShoppingBag className=\"size-4\" />\n {formatDownloads(product.purchaseCount)}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {formatDownloads(product.downloads)}\n </span>\n )}\n </div>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-sm font-medium text-text-link\">\n {getPriceDisplay(product, tr)}\n </span>\n </div>\n </button>\n );\n }\n\n if (variant === 'compact') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-transparent p-3 text-left transition-[background-color,border-color] hover:border-border-seam hover:bg-bg-surface\"\n >\n <div\n className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-8 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-8\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-medium text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"truncate text-xs text-text-muted\">\n {product.category || product.type} · {(product.rating ?? 0).toFixed(1)} ★\n </p>\n </div>\n <span className=\"text-xs font-medium text-text-muted\">{getPriceDisplay(product, tr)}</span>\n </button>\n );\n }\n\n // Default variant\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface p-4 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-9 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-9\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-3 line-clamp-2 text-sm text-text-muted\">\n {product.description || tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mt-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-0.5 text-xs text-text-muted\">\n <Star className=\"size-3.5 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-xs text-text-muted\">·</span>\n <span className=\"text-xs text-text-muted\">\n {product.nature === 'PHYSICAL'\n ? tr('pages.marketplace.purchases', '{{count}} purchases', {\n count: formatDownloads(product.purchaseCount),\n })\n : tr('pages.marketplace.downloads', '{{count}} downloads', {\n count: formatDownloads(product.downloads),\n })}\n </span>\n </div>\n <span className=\"text-xs font-semibold text-text-link\">{getPriceDisplay(product, tr)}</span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Deprecated App Card Component\n// ========================================\n\ninterface DeprecatedProduct {\n id: string;\n name: string;\n icon?: string;\n}\n\nconst DeprecatedAppCard: FC<{ product: DeprecatedProduct; onClick: () => void }> = ({\n product,\n onClick,\n}) => {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-status-warning-border/30 bg-bg-surface p-4 text-left shadow-elevation-1 transition-all hover:border-status-warning-border/60 hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"relative flex size-14 flex-shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle\">\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-9 rounded-lg object-cover grayscale\"\n />\n ) : (\n <Box className=\"size-9 text-status-warning-text/50\" />\n )}\n <span className=\"absolute -right-1 -top-1 flex size-4 items-center justify-center rounded-full bg-status-warning-bg-subtle border border-status-warning-border/40\">\n <AlertCircle className=\"size-2.5 text-status-warning-text\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1\">\n <h4 className=\"truncate font-semibold text-text-secondary group-hover:text-text-link\">\n {product.name}\n </h4>\n <span\n title={tr(\n 'pages.marketplace.deprecatedHint',\n 'This node is no longer supported. We encourage you to migrate to newer alternatives.'\n )}\n className=\"mt-1 inline-flex cursor-help items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\"\n >\n <AlertCircle className=\"size-3\" />\n {tr('pages.marketplace.deprecated', 'No Longer Supported')}\n </span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Section Header Component\n// ========================================\n\ninterface SectionHeaderProps {\n icon: typeof Sparkles;\n title: string;\n subtitle?: string;\n onViewAll?: () => void;\n}\n\nconst SectionHeader: FC<SectionHeaderProps> = ({ icon: Icon, title, subtitle, onViewAll }) => {\n const tr = useTr();\n return (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Icon className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n {subtitle && <p className=\"text-sm text-text-muted\">{subtitle}</p>}\n </div>\n </div>\n {onViewAll && (\n <button\n type=\"button\"\n onClick={onViewAll}\n className=\"cursor-pointer flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('pages.marketplace.viewAll', 'View all')}\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n );\n};\n\n// ========================================\n// Category Chip Component\n// ========================================\n\ninterface CategoryChipProps {\n typeId: ProductTypeKey;\n name: string;\n count: number;\n active?: boolean;\n onClick: () => void;\n}\n\nconst CategoryChip: FC<CategoryChipProps> = ({ typeId, name, count, active, onClick }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex cursor-pointer items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all ${\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-bg-sunken'\n }`}\n >\n <ProductTypeIcon type={typeId} className=\"size-4\" />\n {name}\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${active ? 'bg-bg-surface/20' : 'bg-bg-sunken'}`}\n >\n {count}\n </span>\n </button>\n);\n\n// ========================================\n// Hero Section Component\n// ========================================\n\ninterface HeroSectionProps {\n featuredProducts: StoreProduct[];\n onProductClick: (product: StoreProduct) => void;\n /** Optional tour anchor placed on the hero root (avoids an extra wrapper div). */\n dataTour?: string;\n}\n\nconst HeroSection: FC<HeroSectionProps> = ({ featuredProducts, onProductClick, dataTour }) => {\n const tr = useTr();\n const [activeIndex, setActiveIndex] = useState(0);\n const heroProduct = featuredProducts[activeIndex] || featuredProducts[0];\n\n if (!heroProduct) return null;\n\n const productColor = getProductColor(heroProduct.type);\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow\n data-tour={dataTour}\n className=\"relative overflow-hidden rounded-2xl px-6 py-10 sm:px-10 sm:py-12\"\n >\n <div\n className=\"absolute inset-0 opacity-10\"\n style={{\n background: `linear-gradient(135deg, ${productColor} 0%, transparent 50%)`,\n }}\n />\n <div className=\"relative z-10 flex items-center gap-8\">\n <div className=\"flex-1\">\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-3 py-1 text-xs font-semibold text-status-warning-text motion-safe:animate-vc-glow-pulse\">\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.featuredApp', 'Featured App')}\n </span>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-text-link\">\n {heroProduct.category || heroProduct.type}\n </span>\n </div>\n <h1 className=\"mb-3 text-3xl font-bold text-text-primary\">{heroProduct.name}</h1>\n <p className=\"mb-6 max-w-xl text-text-muted\">\n {heroProduct.description ||\n tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mb-6 flex items-center gap-4\">\n <span className=\"flex items-center gap-1 text-sm\">\n <Star className=\"size-5 fill-current text-status-warning-text\" />\n <span className=\"font-semibold text-text-primary\">\n {(heroProduct.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-text-muted\">\n {tr('pages.productDetail.reviews_count', '({{count}} reviews)', {\n count: heroProduct.reviewCount,\n })}\n </span>\n </span>\n {heroProduct.nature === 'PHYSICAL' ? (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <ShoppingBag className=\"size-4\" />\n {tr('pages.marketplace.purchasesCount', '{{count}} purchases', {\n count: heroProduct.purchaseCount.toLocaleString(),\n })}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {tr('pages.marketplace.downloadsCount', '{{count}} downloads', {\n count: heroProduct.downloads.toLocaleString(),\n })}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => onProductClick(heroProduct)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n {tr('pages.marketplace.viewDetails', 'View Details')}\n </button>\n </div>\n <div className=\"hidden flex-shrink-0 lg:block\">\n <div\n className=\"flex size-48 items-center justify-center rounded-3xl shadow-lg\"\n style={{ backgroundColor: getProductBgColor(heroProduct.type) }}\n >\n {heroProduct.icon ? (\n <img\n src={heroProduct.icon}\n alt={heroProduct.name}\n className=\"size-32 rounded-2xl object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={heroProduct.type}\n className=\"size-32\"\n style={{ color: productColor }}\n />\n )}\n </div>\n </div>\n </div>\n {/* Featured App Selector */}\n {featuredProducts.length > 1 && (\n <div className=\"relative z-10 mt-6 flex gap-2\">\n {featuredProducts.map((product, index) => (\n <button\n type=\"button\"\n key={product.id}\n onClick={() => setActiveIndex(index)}\n className={`h-2 w-8 rounded-full transition-all ${\n index === activeIndex\n ? 'bg-action-primary-bg'\n : 'bg-border-default hover:bg-action-primary-border'\n }`}\n aria-label={tr('pages.marketplace.viewProduct', 'View {{name}}', {\n name: product.name,\n })}\n />\n ))}\n </div>\n )}\n </GlassCard>\n );\n};\n\n// ========================================\n// Loading Skeleton Components\n// ========================================\n\nconst ProductCardSkeleton: FC<{ variant?: 'default' | 'compact' | 'featured' }> = ({\n variant = 'default',\n}) => {\n if (variant === 'featured') {\n return (\n <div className=\"h-full animate-pulse rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-2xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-4 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n if (variant === 'compact') {\n return (\n <div className=\"flex animate-pulse items-center gap-3 p-3\">\n <div className=\"size-12 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"animate-pulse rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-3 w-full rounded bg-bg-sunken\" />\n <div className=\"h-3 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n};\n\n// Hero Section Skeleton\nconst HeroSkeleton: FC = () => (\n <div className=\"animate-pulse rounded-2xl bg-gradient-to-r from-bg-surface via-bg-sunken to-bg-surface p-8\">\n <div className=\"flex items-center gap-8\">\n <div className=\"flex-1 space-y-4\">\n <div className=\"flex gap-2\">\n <div className=\"h-6 w-24 rounded-full bg-bg-sunken\" />\n <div className=\"h-6 w-20 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"h-9 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-4\">\n <div className=\"h-5 w-24 rounded bg-bg-sunken\" />\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-36 rounded-lg bg-bg-sunken\" />\n </div>\n <div className=\"hidden size-48 rounded-3xl bg-bg-sunken lg:block\" />\n </div>\n <div className=\"mt-6 flex gap-2\">\n {[...Array(3)].map((_, i) => (\n <div key={i} className=\"h-2 w-8 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n);\n\n// Category Chips Skeleton (Browse by Type)\nconst CHIP_WIDTHS = [100, 120, 90, 110, 130, 95, 115, 105];\n\nconst CategoryChipsSkeleton: FC = () => (\n <div className=\"space-y-4\">\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex flex-wrap gap-2\">\n {CHIP_WIDTHS.map((width, i) => (\n <div\n key={i}\n className=\"h-9 animate-pulse rounded-full bg-bg-sunken\"\n style={{ width: `${width}px` }}\n />\n ))}\n </div>\n </div>\n);\n\n// Section Header Skeleton\nconst SectionHeaderSkeleton: FC = () => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 animate-pulse rounded-xl bg-bg-sunken\" />\n <div className=\"space-y-1\">\n <div className=\"h-5 w-40 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n);\n\n// Full Page Skeleton - shows complete loading state\nconst MarketplacePageSkeleton: FC = () => (\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n {/* Hero Section */}\n <HeroSkeleton />\n\n {/* Browse by Type */}\n <CategoryChipsSkeleton />\n\n {/* Most Downloaded Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* New Releases Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* Free Apps Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} variant=\"compact\" />\n ))}\n </div>\n </div>\n </div>\n);\n\n// ========================================\n// Filter Types\n// ========================================\n\ntype PricingFilter = 'all' | 'free' | 'paid' | 'subscription';\ntype RatingFilter = 'any' | '3' | '3.5' | '4' | '4.5';\ntype SortOption =\n | 'relevance'\n | 'rating'\n | 'downloads'\n | 'newest'\n | 'price-low'\n | 'price-high'\n | 'name-asc';\n\ninterface FilterDropdownProps {\n label: string;\n value: string;\n options: { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nconst FilterDropdown: FC<FilterDropdownProps> = ({ label, value, options, onChange }) => {\n const selectId = useId();\n\n return (\n <label\n htmlFor={selectId}\n className=\"relative 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 transition-colors hover:bg-bg-sunken focus-within:border-action-primary-border focus-within:ring-2 focus-within:ring-action-primary-border/20\"\n >\n <span className=\"sr-only\">{label}</span>\n <span className=\"text-text-muted\">{label}:</span>\n <select\n id={selectId}\n aria-label={label}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n className=\"min-w-[8rem] appearance-none bg-transparent pr-6 text-sm font-medium text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-3 size-4 text-text-muted\" />\n </label>\n );\n};\n\n// ========================================\n// Main Page Component\n// ========================================\n\nexport const MarketplaceHomePage: FC = () => {\n const navigate = useNavigate();\n const { basePath, user } = useStore();\n const tr = useTr();\n const searchInputId = useId();\n const bus = useEventBus();\n const {\n cart,\n cartOpen,\n closeCart,\n itemCount,\n cartItems,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n } = useCart();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedType, setSelectedType] = useState<string>('all');\n\n // Filter states\n const [pricingFilter, setPricingFilter] = useState<PricingFilter>('all');\n const [ratingFilter, setRatingFilter] = useState<RatingFilter>('any');\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [selectedLicense, setSelectedLicense] = useState<string>('all');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [sortOption, setSortOption] = useState<SortOption>('relevance');\n const [showFilters, setShowFilters] = useState(false);\n\n // Debounce search query - 1.5 seconds for better UX\n const debouncedSearchQuery = useDebounce(searchQuery, 1500);\n\n // Filter options\n const pricingOptions = [\n { value: 'all', label: tr('pages.marketplace.pricingAll', 'All Prices') },\n { value: 'free', label: tr('pages.marketplace.pricingFree', 'Free') },\n { value: 'paid', label: tr('pages.marketplace.pricingPaid', 'Paid (One-time)') },\n { value: 'subscription', label: tr('pages.marketplace.pricingSubscription', 'Subscription') },\n ];\n\n const ratingOptions = [\n { value: 'any', label: tr('pages.marketplace.ratingAny', 'Any Rating') },\n { value: '3', label: tr('pages.marketplace.rating3', '3+ Stars') },\n { value: '3.5', label: tr('pages.marketplace.rating3_5', '3.5+ Stars') },\n { value: '4', label: tr('pages.marketplace.rating4', '4+ Stars') },\n { value: '4.5', label: tr('pages.marketplace.rating4_5', '4.5+ Stars') },\n ];\n\n const sortOptions = [\n { value: 'relevance', label: tr('pages.marketplace.sortRelevance', 'Relevance') },\n { value: 'rating', label: tr('pages.marketplace.sortRating', 'Highest Rated') },\n { value: 'downloads', label: tr('pages.marketplace.sortDownloads', 'Most Downloads') },\n { value: 'newest', label: tr('pages.marketplace.sortNewest', 'Newest First') },\n { value: 'price-low', label: tr('pages.marketplace.sortPriceLow', 'Price: Low to High') },\n { value: 'price-high', label: tr('pages.marketplace.sortPriceHigh', 'Price: High to Low') },\n { value: 'name-asc', label: tr('pages.marketplace.sortNameAsc', 'Name A-Z') },\n ];\n\n // Map UI sort option to GraphQL StoreSortBy enum\n const mapSortToApi = (sort: SortOption): StoreSortBy => {\n const mapping: Record<SortOption, StoreSortBy> = {\n relevance: StoreSortBy.Relevance,\n rating: StoreSortBy.Rating,\n downloads: StoreSortBy.Downloads,\n newest: StoreSortBy.Newest,\n 'price-low': StoreSortBy.PriceLow,\n 'price-high': StoreSortBy.PriceHigh,\n 'name-asc': StoreSortBy.NameAsc,\n };\n return mapping[sort];\n };\n\n // Map UI pricing filter to GraphQL PricingModel enum\n const mapPricingToApi = (pricing: PricingFilter): PricingModel | undefined => {\n if (pricing === 'all') return undefined;\n const mapping: Record<string, PricingModel> = {\n free: PricingModel.Free,\n paid: PricingModel.PaidOnetime,\n subscription: PricingModel.Subscription,\n };\n return mapping[pricing];\n };\n\n // Check if any filter is active (using debounced search for API calls)\n const hasActiveFilters =\n debouncedSearchQuery.trim() !== '' ||\n pricingFilter !== 'all' ||\n ratingFilter !== 'any' ||\n selectedCategory !== 'all' ||\n selectedLicense !== 'all' ||\n featuredOnly ||\n selectedType !== 'all' ||\n sortOption !== 'relevance';\n\n // Check if user is currently typing (search not yet debounced)\n const isSearchPending = searchQuery.trim() !== '' && searchQuery !== debouncedSearchQuery;\n\n const clearAllFilters = () => {\n setSearchQuery('');\n setPricingFilter('all');\n setRatingFilter('any');\n setSelectedCategory('all');\n setSelectedLicense('all');\n setFeaturedOnly(false);\n setSortOption('relevance');\n setSelectedType('all');\n };\n\n // Count active filters (excluding search and sort)\n const activeFilterCount =\n (pricingFilter !== 'all' ? 1 : 0) +\n (ratingFilter !== 'any' ? 1 : 0) +\n (selectedCategory !== 'all' ? 1 : 0) +\n (selectedLicense !== 'all' ? 1 : 0) +\n (featuredOnly ? 1 : 0) +\n (selectedType !== 'all' ? 1 : 0);\n\n // Build BrowseStoreInput from filter states for API call\n const browseStoreInput: BrowseStoreInput = useMemo(() => {\n const input: BrowseStoreInput = {\n sortBy: mapSortToApi(sortOption),\n pagination: { limit: 50 },\n };\n\n // Only add filter if we have active filters\n if (hasActiveFilters) {\n input.filter = {};\n\n // Search query (debounced)\n if (debouncedSearchQuery.trim()) {\n input.filter.search = debouncedSearchQuery.trim();\n }\n\n // Type filter\n if (selectedType !== 'all') {\n input.filter.type = selectedType as BrowseStoreInput['filter'] extends { type?: infer T }\n ? T\n : never;\n }\n\n // Category filter\n if (selectedCategory !== 'all') {\n input.filter.category = selectedCategory;\n }\n\n // Pricing filter\n const pricingModel = mapPricingToApi(pricingFilter);\n if (pricingModel) {\n input.filter.pricingModel = pricingModel;\n }\n\n // Rating filter\n if (ratingFilter !== 'any') {\n input.filter.minRating = parseFloat(ratingFilter);\n }\n\n // Featured filter\n if (featuredOnly) {\n input.filter.featured = true;\n }\n\n // License filter\n if (selectedLicense !== 'all') {\n input.filter.license = selectedLicense;\n }\n }\n\n return input;\n }, [\n debouncedSearchQuery,\n selectedType,\n selectedCategory,\n pricingFilter,\n ratingFilter,\n featuredOnly,\n selectedLicense,\n sortOption,\n hasActiveFilters,\n ]);\n\n // Fetch grouped products for homepage (when no filters active)\n const {\n featured,\n mostDownloaded,\n recentlyAdded,\n freeItems,\n loading: homeLoading,\n error: homeError,\n refetch: refetchHome,\n } = useHomeGroupedProducts({ skip: hasActiveFilters });\n\n // Fetch filtered products from API when filters are active\n const {\n products: browseProducts,\n totalCount,\n facets,\n loading: browseLoading,\n error: browseError,\n refetch: refetchBrowse,\n } = useBrowseStoreWithContext(hasActiveFilters ? browseStoreInput : {}, {\n skip: !hasActiveFilters,\n });\n\n // Determine which data to show\n const loading = hasActiveFilters ? browseLoading : homeLoading;\n const error = hasActiveFilters ? browseError : homeError;\n const refetch = hasActiveFilters ? refetchBrowse : refetchHome;\n\n // Products to display when filters are active\n const filteredProducts = hasActiveFilters ? browseProducts : [];\n\n // Emit search event when debounced query resolves. Read totalCount via ref so\n // we don't re-emit when only the count changes.\n const totalCountRef = useRef(totalCount);\n totalCountRef.current = totalCount;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n if (!debouncedSearchQuery.trim() || browseLoading) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.marketplace.searched',\n {\n query: debouncedSearchQuery,\n resultCount: totalCountRef.current,\n }\n );\n }, [debouncedSearchQuery, browseLoading]);\n\n const handleProductClick = useCallback(\n (product: StoreProduct) => {\n navigate(`${basePath}/marketplace/product/${product.id}`);\n },\n [navigate, basePath]\n );\n\n const handleCheckout = () => {\n closeCart();\n navigate(`${basePath}/cart`);\n };\n\n // Get all unique products for the homepage \"All Apps\" section\n const allProducts = useMemo(\n () => [\n ...new Map(\n [...featured, ...mostDownloaded, ...recentlyAdded, ...freeItems].map((p) => [p.id, p])\n ).values(),\n ],\n [featured, mostDownloaded, recentlyAdded, freeItems]\n );\n\n // Fetch installations to surface deprecated (UNLISTED) apps the user has installed\n const isAuthenticated = !!user?.id;\n const { data: installationsData } = useGetMyStoreInstallationsQuery({\n variables: { limit: 100 },\n skip: !isAuthenticated,\n fetchPolicy: 'cache-and-network',\n });\n\n // Deduplicate by productId (user may have installed in multiple workspaces)\n const deprecatedInstalledProducts = useMemo<DeprecatedProduct[]>(() => {\n if (!installationsData?.myStoreAppInstallations?.items) return [];\n const seen = new Set<string>();\n const result: DeprecatedProduct[] = [];\n for (const item of installationsData.myStoreAppInstallations.items) {\n if (item.productStatus === 'UNLISTED' && !seen.has(item.productId)) {\n seen.add(item.productId);\n result.push({\n id: item.productId,\n name: item.productName ?? 'Unknown App',\n icon: item.productIcon ?? undefined,\n });\n }\n }\n return result;\n }, [installationsData]);\n\n // Build dynamic filter options from facets (when available) or from homepage products\n const typeCategories = useMemo(() => {\n // Use facets from API if available\n if (facets?.types && facets.types.length > 0) {\n return [\n { id: 'all', name: tr('pages.marketplace.typeAll', 'All Apps'), count: totalCount },\n ...facets.types.map((t) => ({\n id: t.value,\n name: t.value.charAt(0) + t.value.slice(1).toLowerCase().replace(/_/g, ' '),\n count: t.count,\n })),\n ];\n }\n // Fallback to homepage products: count each type in a single pass.\n const typeCounts = allProducts.reduce<Map<string, number>>((acc, p) => {\n acc.set(p.type, (acc.get(p.type) ?? 0) + 1);\n return acc;\n }, new Map());\n const typeOptions = Array.from(typeCounts.entries())\n .flatMap(([type, count]) =>\n count > 0\n ? [\n {\n id: type,\n name: type.charAt(0) + type.slice(1).toLowerCase().replace(/_/g, ' '),\n count,\n },\n ]\n : []\n )\n .sort((a, b) => b.count - a.count);\n return [\n { id: 'all', name: tr('pages.marketplace.typeAll', 'All Apps'), count: allProducts.length },\n ...typeOptions,\n ];\n }, [facets, allProducts, totalCount, tr]);\n\n // Build category options from facets or products\n const categoryOptions = useMemo(() => {\n if (facets?.categories && facets.categories.length > 0) {\n return [\n { value: 'all', label: tr('pages.marketplace.allCategories', 'All Categories') },\n ...facets.categories.map((c) => ({\n value: c.value,\n label: c.value,\n })),\n ];\n }\n return [\n { value: 'all', label: tr('pages.marketplace.allCategories', 'All Categories') },\n ...Array.from(new Set(allProducts.flatMap((p) => (p.category ? [p.category] : []))))\n .map((cat) => ({\n value: cat as string,\n label: cat as string,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n ];\n }, [facets, allProducts, tr]);\n\n // Build license options from facets or static list\n const licenseOptions = useMemo(() => {\n if (facets?.licenses && facets.licenses.length > 0) {\n return [\n { value: 'all', label: tr('pages.marketplace.allLicenses', 'All Licenses') },\n ...facets.licenses.map((l) => ({\n value: l.value,\n label: l.value,\n })),\n ];\n }\n return [{ value: 'all', label: tr('pages.marketplace.allLicenses', 'All Licenses') }];\n }, [facets, tr]);\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"relative overflow-hidden border-b border-border-seam bg-bg-surface px-6 py-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('pages.marketplace.appTitle', 'App')}{' '}\n <GradientText>{tr('pages.marketplace.title', 'Marketplace')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'pages.marketplace.subtitle',\n 'Discover apps, workflows, and integrations for your workspace'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n data-tour=\"store-marketplace-cart-button\"\n onClick={() => navigate(`${basePath}/cart`)}\n className=\"cursor-pointer relative flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ShoppingCart className=\"size-5\" />\n {tr('pages.marketplace.cart', 'Cart')}\n {itemCount > 0 && (\n <span className=\"absolute -right-2 -top-2 flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs font-bold text-action-primary-text\">\n {itemCount}\n </span>\n )}\n </button>\n </div>\n\n {/* Search Bar - Live filtering as you type */}\n <div className=\"mt-4\">\n <div className=\"relative\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {tr('pages.marketplace.search.label', 'Search marketplace products')}\n </label>\n <Search className=\"absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-muted\" />\n <input\n id={searchInputId}\n data-tour=\"store-marketplace-search-input\"\n aria-label={tr('pages.marketplace.search.label', 'Search marketplace products')}\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'pages.marketplace.search.placeholder',\n 'Search apps, workflows, integrations...'\n )}\n className=\"w-full rounded-xl border border-border-default bg-bg-sunken py-3 pl-12 pr-4 text-text-primary placeholder-text-muted focus:border-action-primary-border focus:bg-bg-surface focus:outline-none focus:ring-2 focus:ring-action-primary-border/20\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n aria-label={tr('pages.marketplace.search.clear', 'Clear search')}\n className=\"cursor-pointer absolute right-4 top-1/2 -translate-y-1/2 text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n {/* Search results count / pending indicator */}\n {searchQuery.trim() && (\n <div\n aria-live=\"polite\"\n className=\"mt-2 flex items-center gap-2 text-sm text-text-muted\"\n >\n {isSearchPending || browseLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span>{tr('pages.marketplace.search.searching', 'Searching...')}</span>\n </>\n ) : (\n <span>\n {tr(\n 'pages.marketplace.search.resultCount',\n '{{count}} result{{plural}} for \"{{query}}\"',\n {\n count: totalCount,\n plural: totalCount !== 1 ? 's' : '',\n query: debouncedSearchQuery,\n }\n )}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Filter Bar */}\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n {/* Filter Toggle Button */}\n <button\n type=\"button\"\n data-tour=\"store-marketplace-filters-button\"\n onClick={() => setShowFilters(!showFilters)}\n className={`cursor-pointer flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors ${\n showFilters || activeFilterCount > 0\n ? 'border-action-primary-border bg-action-primary-bg/10 text-text-link'\n : 'border-border-default bg-bg-surface text-text-primary transition-colors hover:bg-bg-sunken'\n }`}\n >\n <SlidersHorizontal className=\"size-4\" />\n {tr('pages.marketplace.filters', 'Filters')}\n {activeFilterCount > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs text-action-primary-text\">\n {activeFilterCount}\n </span>\n )}\n </button>\n\n {/* Quick Filter Chips - Always visible */}\n <div\n className=\"flex flex-wrap items-center gap-2\"\n data-tour=\"store-marketplace-quick-filters\"\n >\n <button\n type=\"button\"\n onClick={() => setPricingFilter(pricingFilter === 'free' ? 'all' : 'free')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n pricingFilter === 'free'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Gift className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterFree', 'Free')}\n </button>\n <button\n type=\"button\"\n onClick={() => setRatingFilter(ratingFilter === '4' ? 'any' : '4')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n ratingFilter === '4'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Star className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterRating', '4+ Stars')}\n </button>\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(!featuredOnly)}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n featuredOnly\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Sparkles className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterFeatured', 'Featured')}\n </button>\n </div>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n {tr('pages.marketplace.clearAll', 'Clear all')}\n </button>\n )}\n\n {/* Sort Dropdown - Right aligned */}\n <div className=\"ml-auto\" data-tour=\"store-marketplace-section-sort\">\n <FilterDropdown\n label={tr('pages.marketplace.sortLabel', 'Sort')}\n value={sortOption}\n options={sortOptions}\n onChange={(value) => setSortOption(value as SortOption)}\n />\n </div>\n </div>\n\n {/* Expanded Filters Panel */}\n {showFilters && (\n <div className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex flex-wrap items-start gap-6\">\n {/* Pricing Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterPricing', 'Pricing')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterPrice', 'Price')}\n value={pricingFilter}\n options={pricingOptions}\n onChange={(value) => setPricingFilter(value as PricingFilter)}\n />\n </div>\n\n {/* Rating Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterMinRating', 'Min Rating')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterRating', 'Rating')}\n value={ratingFilter}\n options={ratingOptions}\n onChange={(value) => setRatingFilter(value as RatingFilter)}\n />\n </div>\n\n {/* Category Filter */}\n {categoryOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterCategory', 'Category')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterCategory', 'Category')}\n value={selectedCategory}\n options={categoryOptions}\n onChange={setSelectedCategory}\n />\n </div>\n )}\n\n {/* License Filter */}\n {licenseOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterLicense', 'License')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterLicense', 'License')}\n value={selectedLicense}\n options={licenseOptions}\n onChange={setSelectedLicense}\n />\n </div>\n )}\n </div>\n\n {/* Active Filters Summary */}\n {activeFilterCount > 0 && (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border-default pt-4\">\n <span className=\"text-xs font-medium text-text-muted\">\n {tr('pages.marketplace.activeFilters', 'Active filters:')}\n </span>\n {selectedType !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedType('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterType', 'Type:')}\n </span>{' '}\n {selectedType}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {pricingFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setPricingFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterPrice', 'Price:')}\n </span>{' '}\n {pricingOptions.find((o) => o.value === pricingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {ratingFilter !== 'any' && (\n <button\n type=\"button\"\n onClick={() => setRatingFilter('any')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterRating', 'Rating:')}\n </span>{' '}\n {ratingOptions.find((o) => o.value === ratingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedCategory !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedCategory('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterCategory', 'Category:')}\n </span>{' '}\n {selectedCategory}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedLicense !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedLicense('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterLicense', 'License:')}\n </span>{' '}\n {licenseOptions.find((o) => o.value === selectedLicense)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {featuredOnly && (\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(false)}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.activeFilterFeatured', 'Featured Only')}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </header>\n\n {/* Main Content */}\n <main className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n <PagePurpose>\n {tr(\n 'pages.marketplace.pagePurpose',\n 'The marketplace is where you discover and install apps, agents, workflows, nodes and widgets that extend your workspaces. Browse featured and trending picks, filter by type or category, search for something specific, then open any product to view pricing and reviews before adding it to your cart.'\n )}\n </PagePurpose>\n {/* Error State */}\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 p-8 text-center\"\n >\n <AlertCircle className=\"mb-4 size-12 text-status-error-text\" />\n <h3 className=\"mb-2 text-lg font-semibold text-status-error-text-emphasis\">\n {tr('pages.marketplace.errorTitle', 'Failed to load marketplace')}\n </h3>\n <p className=\"mb-4 text-sm text-status-error-text\">\n {error.message ||\n tr(\n 'pages.marketplace.errorDescription',\n 'An error occurred while fetching products'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-emphasis\"\n >\n {tr('common.tryAgain', 'Try Again')}\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {loading && !error && <MarketplacePageSkeleton />}\n\n {/* Content - Only show when data is loaded */}\n {!loading && !error && (\n <>\n {/* Hero Section - Featured Products (only when no filters) */}\n {!hasActiveFilters && selectedType === 'all' && featured.length > 0 && (\n <HeroSection\n dataTour=\"store-marketplace-hero-section\"\n featuredProducts={featured}\n onProductClick={handleProductClick}\n />\n )}\n\n {/* Type Filter Categories */}\n {typeCategories.length > 1 && (\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {tr('pages.marketplace.browseByType', 'Browse by Type')}\n </h2>\n <div\n className=\"flex flex-wrap gap-2\"\n data-tour=\"store-marketplace-type-categories\"\n >\n {typeCategories.map((category) => (\n <CategoryChip\n key={category.id}\n typeId={category.id}\n name={category.name}\n count={category.count}\n active={selectedType === category.id}\n onClick={() => setSelectedType(category.id)}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Show Filtered Results when filters are active */}\n {hasActiveFilters && (\n <section>\n <SectionHeader\n icon={Filter}\n title={\n debouncedSearchQuery\n ? tr('pages.marketplace.searchResults', 'Search Results')\n : tr('pages.marketplace.filteredResults', 'Filtered Results')\n }\n subtitle={tr('pages.marketplace.appsFound', '{{count}} app{{plural}} found', {\n count: totalCount,\n plural: totalCount !== 1 ? 's' : '',\n })}\n />\n {filteredProducts.length > 0 ? (\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n data-tour=\"store-marketplace-product-grid\"\n >\n {filteredProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('pages.marketplace.noFilterMatch', 'No apps match your filters')}\n description={tr(\n 'pages.marketplace.noFilterMatchDescription',\n 'Try adjusting your filter criteria or clearing filters to see more results.'\n )}\n action={\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n {tr('pages.marketplace.clearAllFilters', 'Clear All Filters')}\n </button>\n }\n />\n )}\n </section>\n )}\n\n {/* Show grouped sections only when no filters are active */}\n {!hasActiveFilters && selectedType === 'all' && (\n <>\n {/* Most Downloaded / Trending Apps */}\n {mostDownloaded.length > 0 && (\n <section>\n <SectionHeader\n icon={TrendingUp}\n title={tr('pages.marketplace.mostDownloaded', 'Most Downloaded')}\n subtitle={tr(\n 'pages.marketplace.mostDownloadedSubtitle',\n 'Top picks by our community'\n )}\n />\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n data-tour=\"store-marketplace-most-downloaded\"\n >\n {mostDownloaded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Recently Added / New Releases */}\n {recentlyAdded.length > 0 && (\n <section>\n <SectionHeader\n icon={Clock}\n title={tr('pages.marketplace.newReleases', 'New Releases')}\n subtitle={tr(\n 'pages.marketplace.newReleasesSubtitle',\n 'Recently published apps'\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentlyAdded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Free Items */}\n {freeItems.length > 0 && (\n <section>\n <SectionHeader\n icon={Gift}\n title={tr('pages.marketplace.freeApps', 'Free Apps')}\n subtitle={tr(\n 'pages.marketplace.freeAppsSubtitle',\n 'No cost, full functionality'\n )}\n />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {freeItems.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n variant=\"compact\"\n />\n ))}\n </div>\n </section>\n )}\n\n {/* All Apps */}\n {allProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={Box}\n title={tr('pages.marketplace.allApps', 'All Apps')}\n subtitle={tr(\n 'pages.marketplace.allAppsSubtitle',\n '{{count}} apps available',\n { count: allProducts.length }\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {allProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* Deprecated Installed Apps — shown whenever user has UNLISTED products installed */}\n {isAuthenticated && deprecatedInstalledProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={AlertCircle}\n title={tr(\n 'pages.marketplace.deprecatedApps',\n \"Deprecated Apps You've Installed\"\n )}\n subtitle={tr(\n 'pages.marketplace.deprecatedAppsSubtitle',\n 'These apps are no longer supported — hover the badge for details'\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {deprecatedInstalledProducts.map((product) => (\n <DeprecatedAppCard\n key={product.id}\n product={product}\n onClick={() => navigate(`${basePath}/marketplace/product/${product.id}`)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Empty State - No products at all */}\n {allProducts.length === 0 && (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('pages.marketplace.noAppsTitle', 'No apps available yet')}\n description={tr(\n 'pages.marketplace.noAppsDescription',\n 'The marketplace is empty. Check back soon for new apps, workflows, and integrations.'\n )}\n />\n )}\n </>\n )}\n </div>\n </main>\n\n {/* Cart Drawer */}\n <CartDrawer\n isOpen={cartOpen}\n onClose={closeCart}\n onCheckout={handleCheckout}\n cartItems={cartItems}\n itemCount={itemCount}\n currency={cart?.currency || 'USD'}\n subtotal={subtotal}\n tax={tax}\n total={total}\n updateQuantity={updateQuantity}\n removeProduct={removeProduct}\n />\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarketplaceHomePage.js","names":[],"sources":["../../src/pages/MarketplaceHomePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useCallback, useMemo, useId, useEffect, useRef } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Search,\n Star,\n Download,\n ShoppingBag,\n ChevronRight,\n ChevronDown,\n Sparkles,\n TrendingUp,\n Clock,\n Bot,\n Brain,\n LayoutDashboard,\n GitBranch,\n Box,\n Puzzle,\n ShoppingCart,\n X,\n AlertCircle,\n Gift,\n Filter,\n SlidersHorizontal,\n Loader2,\n FileCode2,\n Database,\n type LucideIcon,\n} from 'lucide-react';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { CartDrawer } from '../components/cart/CartDrawer';\nimport {\n useHomeGroupedProducts,\n useBrowseStoreWithContext,\n type StoreProduct,\n} from '../hooks/useStoreGraphQL';\nimport { formatPrice } from '../utils';\nimport type { BrowseStoreInput } from '../generated/global-types';\nimport { StoreSortBy, PricingModel } from '../generated/global-types';\nimport { useGetMyStoreInstallationsQuery } from '../generated/global-operations';\nimport { useTr } from '../shared/hooks/useTr';\nimport {\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n GlassCard,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\n// ========================================\n// Helper Types and Functions\n// ========================================\n\ntype ProductTypeKey =\n | 'BOTAPP'\n | 'AGENT'\n | 'CONSOLE'\n | 'WORKFLOW'\n | 'NODE'\n | 'WIDGET'\n | 'VIBEMODULE'\n | 'DASHBOARD'\n | 'PARSER'\n | 'DATASINK'\n | 'APP'\n | 'TEMPLATE'\n | 'INTEGRATION'\n | 'EXTENSION'\n | 'THEME'\n | 'PLUGIN'\n | 'COMPONENT'\n | string;\n\nconst formatDownloads = (count: number): string => {\n if (count >= 1000000) return `${(count / 1000000).toFixed(1)}M`;\n if (count >= 1000) return `${(count / 1000).toFixed(1)}K`;\n return count.toString();\n};\n\nconst getPriceDisplay = (\n product: StoreProduct,\n tr?: (key: string, fallback: string) => string\n): string => {\n if (product.pricingModel === 'FREE') return tr ? tr('pages.marketplace.free', 'Free') : 'Free';\n if (product.price === 0) return tr ? tr('pages.marketplace.free', 'Free') : 'Free';\n return formatPrice(product.price, product.currency);\n};\n\nconst getProductColor = (type: ProductTypeKey): string => {\n const colors: Record<string, string> = {\n BOTAPP: 'var(--color-accent-blue)',\n AGENT: 'var(--color-accent-purple)',\n CONSOLE: 'var(--color-accent-teal)',\n WORKFLOW: 'var(--color-status-warning-bg)',\n NODE: 'var(--color-status-error-bg)',\n WIDGET: 'var(--color-accent-purple)',\n VIBEMODULE: 'var(--color-accent-blue)',\n DASHBOARD: 'var(--color-accent-blue)',\n PARSER: 'var(--color-status-warning-bg)',\n DATASINK: 'var(--color-status-success-bg)',\n APP: 'var(--color-accent-indigo)',\n TEMPLATE: 'var(--color-accent-teal)',\n INTEGRATION: 'var(--color-status-warning-bg)',\n EXTENSION: 'var(--color-accent-purple)',\n THEME: 'var(--color-accent-purple)',\n PLUGIN: 'var(--color-status-success-bg)',\n COMPONENT: 'var(--color-accent-blue)',\n };\n return colors[type] || 'var(--color-text-muted)';\n};\n\n// Helper to get translucent background color from a token\nconst getProductBgColor = (type: ProductTypeKey): string => {\n const color = getProductColor(type);\n return `color-mix(in srgb, ${color}, transparent 85%)`;\n};\n\n// Component wrapper to render type icons\ninterface ProductTypeIconProps {\n type: ProductTypeKey;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst TYPE_ICONS: Record<string, LucideIcon> = {\n BOTAPP: Bot,\n AGENT: Brain,\n CONSOLE: LayoutDashboard,\n WORKFLOW: GitBranch,\n NODE: Box,\n WIDGET: Puzzle,\n VIBEMODULE: Puzzle,\n DASHBOARD: LayoutDashboard,\n PARSER: FileCode2,\n DATASINK: Database,\n APP: Box,\n TEMPLATE: LayoutDashboard,\n INTEGRATION: GitBranch,\n EXTENSION: Puzzle,\n THEME: Puzzle,\n PLUGIN: Puzzle,\n COMPONENT: Box,\n};\n\nconst ProductTypeIcon: FC<ProductTypeIconProps> = ({ type, className, style }) => {\n const IconComponent = TYPE_ICONS[type] || Box;\n return <IconComponent className={className} style={style} />;\n};\n\n// ========================================\n// App Card Component\n// ========================================\n\ninterface AppCardProps {\n product: StoreProduct;\n onClick: () => void;\n variant?: 'default' | 'compact' | 'featured';\n}\n\nconst AppCard: FC<AppCardProps> = ({ product, onClick, variant = 'default' }) => {\n const tr = useTr();\n const productColor = getProductColor(product.type);\n\n if (variant === 'featured') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group relative flex size-full cursor-pointer flex-col overflow-hidden rounded-2xl border border-border-seam bg-gradient-to-br from-bg-surface to-bg-sunken p-6 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n {product.featured && (\n <div className=\"absolute right-4 top-4 flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-1 text-xs font-medium text-status-warning-text\">\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.featured', 'Featured')}\n </div>\n )}\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-16 flex-shrink-0 items-center justify-center rounded-2xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-10 rounded-lg object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-10\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h3 className=\"truncate text-lg font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h3>\n <p className=\"flex items-center gap-2 text-sm text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3.5\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-4 line-clamp-2 flex-1 text-sm text-text-muted\">\n {product.description || tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Star className=\"size-4 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n {product.nature === 'PHYSICAL' ? (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <ShoppingBag className=\"size-4\" />\n {formatDownloads(product.purchaseCount)}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {formatDownloads(product.downloads)}\n </span>\n )}\n </div>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-sm font-medium text-text-link\">\n {getPriceDisplay(product, tr)}\n </span>\n </div>\n </button>\n );\n }\n\n if (variant === 'compact') {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-transparent p-3 text-left transition-[background-color,border-color] hover:border-border-seam hover:bg-bg-surface\"\n >\n <div\n className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-8 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-8\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-medium text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"truncate text-xs text-text-muted\">\n {product.category || product.type} · {(product.rating ?? 0).toFixed(1)} ★\n </p>\n </div>\n <span className=\"text-xs font-medium text-text-muted\">{getPriceDisplay(product, tr)}</span>\n </button>\n );\n }\n\n // Default variant\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface p-4 text-left shadow-elevation-1 transition-[box-shadow,border-color,color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div\n className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-xl\"\n style={{ backgroundColor: getProductBgColor(product.type) }}\n >\n {product.icon ? (\n <img src={product.icon} alt={product.name} className=\"size-9 rounded-lg object-cover\" />\n ) : (\n <ProductTypeIcon\n type={product.type}\n className=\"size-9\"\n style={{ color: productColor }}\n />\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <h4 className=\"truncate font-semibold text-text-primary group-hover:text-text-link\">\n {product.name}\n </h4>\n <p className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <ProductTypeIcon type={product.type} className=\"size-3\" />\n {product.category || product.type}\n </p>\n </div>\n </div>\n <p className=\"mt-3 line-clamp-2 text-sm text-text-muted\">\n {product.description || tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mt-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex items-center gap-0.5 text-xs text-text-muted\">\n <Star className=\"size-3.5 fill-current text-status-warning-text\" />\n {(product.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-xs text-text-muted\">·</span>\n <span className=\"text-xs text-text-muted\">\n {product.nature === 'PHYSICAL'\n ? tr('pages.marketplace.purchases', '{{count}} purchases', {\n count: formatDownloads(product.purchaseCount),\n })\n : tr('pages.marketplace.downloads', '{{count}} downloads', {\n count: formatDownloads(product.downloads),\n })}\n </span>\n </div>\n <span className=\"text-xs font-semibold text-text-link\">{getPriceDisplay(product, tr)}</span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Deprecated App Card Component\n// ========================================\n\ninterface DeprecatedProduct {\n id: string;\n name: string;\n icon?: string;\n}\n\nconst DeprecatedAppCard: FC<{ product: DeprecatedProduct; onClick: () => void }> = ({\n product,\n onClick,\n}) => {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex cursor-pointer items-center gap-3 rounded-xl border border-status-warning-border/30 bg-bg-surface p-4 text-left shadow-elevation-1 transition-all hover:border-status-warning-border/60 hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"relative flex size-14 flex-shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle\">\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"size-9 rounded-lg object-cover grayscale\"\n />\n ) : (\n <Box className=\"size-9 text-status-warning-text/50\" />\n )}\n <span className=\"absolute -right-1 -top-1 flex size-4 items-center justify-center rounded-full bg-status-warning-bg-subtle border border-status-warning-border/40\">\n <AlertCircle className=\"size-2.5 text-status-warning-text\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1\">\n <h4 className=\"truncate font-semibold text-text-secondary group-hover:text-text-link\">\n {product.name}\n </h4>\n <span\n title={tr(\n 'pages.marketplace.deprecatedHint',\n 'This node is no longer supported. We encourage you to migrate to newer alternatives.'\n )}\n className=\"mt-1 inline-flex cursor-help items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text\"\n >\n <AlertCircle className=\"size-3\" />\n {tr('pages.marketplace.deprecated', 'No Longer Supported')}\n </span>\n </div>\n </button>\n );\n};\n\n// ========================================\n// Section Header Component\n// ========================================\n\ninterface SectionHeaderProps {\n icon: typeof Sparkles;\n title: string;\n subtitle?: string;\n onViewAll?: () => void;\n}\n\nconst SectionHeader: FC<SectionHeaderProps> = ({ icon: Icon, title, subtitle, onViewAll }) => {\n const tr = useTr();\n return (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-xl bg-action-primary-bg/10\">\n <Icon className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>\n {subtitle && <p className=\"text-sm text-text-muted\">{subtitle}</p>}\n </div>\n </div>\n {onViewAll && (\n <button\n type=\"button\"\n onClick={onViewAll}\n className=\"cursor-pointer flex items-center gap-1 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('pages.marketplace.viewAll', 'View all')}\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n );\n};\n\n// ========================================\n// Category Chip Component\n// ========================================\n\ninterface CategoryChipProps {\n typeId: ProductTypeKey;\n name: string;\n count: number;\n active?: boolean;\n onClick: () => void;\n}\n\nconst CategoryChip: FC<CategoryChipProps> = ({ typeId, name, count, active, onClick }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex cursor-pointer items-center gap-2 rounded-full px-4 py-2 text-sm font-medium transition-all ${\n active\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-surface text-text-primary hover:bg-bg-sunken'\n }`}\n >\n <ProductTypeIcon type={typeId} className=\"size-4\" />\n {name}\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${active ? 'bg-bg-surface/20' : 'bg-bg-sunken'}`}\n >\n {count}\n </span>\n </button>\n);\n\n// ========================================\n// Hero Section Component\n// ========================================\n\ninterface HeroSectionProps {\n featuredProducts: StoreProduct[];\n onProductClick: (product: StoreProduct) => void;\n /** Optional tour anchor placed on the hero root (avoids an extra wrapper div). */\n dataTour?: string;\n}\n\nconst HeroSection: FC<HeroSectionProps> = ({ featuredProducts, onProductClick, dataTour }) => {\n const tr = useTr();\n const [activeIndex, setActiveIndex] = useState(0);\n const heroProduct = featuredProducts[activeIndex] || featuredProducts[0];\n\n if (!heroProduct) return null;\n\n const productColor = getProductColor(heroProduct.type);\n\n return (\n <GlassCard\n treatment=\"glass\"\n glow\n data-tour={dataTour}\n className=\"relative overflow-hidden rounded-2xl px-6 py-10 sm:px-10 sm:py-12\"\n >\n <div\n className=\"absolute inset-0 opacity-10\"\n style={{\n background: `linear-gradient(135deg, ${productColor} 0%, transparent 50%)`,\n }}\n />\n <div className=\"relative z-10 flex items-center gap-8\">\n <div className=\"flex-1\">\n <div className=\"mb-4 flex items-center gap-2\">\n <span className=\"flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-3 py-1 text-xs font-semibold text-status-warning-text motion-safe:animate-vc-glow-pulse\">\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.featuredApp', 'Featured App')}\n </span>\n <span className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-text-link\">\n {heroProduct.category || heroProduct.type}\n </span>\n </div>\n <h1 className=\"mb-3 text-3xl font-bold text-text-primary\">{heroProduct.name}</h1>\n <p className=\"mb-6 max-w-xl text-text-muted\">\n {heroProduct.description ||\n tr('pages.productDetail.noDesc', 'No description available')}\n </p>\n <div className=\"mb-6 flex items-center gap-4\">\n <span className=\"flex items-center gap-1 text-sm\">\n <Star className=\"size-5 fill-current text-status-warning-text\" />\n <span className=\"font-semibold text-text-primary\">\n {(heroProduct.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-text-muted\">\n {tr('pages.productDetail.reviews_count', '({{count}} reviews)', {\n count: heroProduct.reviewCount,\n })}\n </span>\n </span>\n {heroProduct.nature === 'PHYSICAL' ? (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <ShoppingBag className=\"size-4\" />\n {tr('pages.marketplace.purchasesCount', '{{count}} purchases', {\n count: heroProduct.purchaseCount.toLocaleString(),\n })}\n </span>\n ) : (\n <span className=\"flex items-center gap-1 text-sm text-text-muted\">\n <Download className=\"size-4\" />\n {tr('pages.marketplace.downloadsCount', '{{count}} downloads', {\n count: heroProduct.downloads.toLocaleString(),\n })}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => onProductClick(heroProduct)}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n {tr('pages.marketplace.viewDetails', 'View Details')}\n </button>\n </div>\n <div className=\"hidden flex-shrink-0 lg:block\">\n <div\n className=\"flex size-48 items-center justify-center rounded-3xl shadow-lg\"\n style={{ backgroundColor: getProductBgColor(heroProduct.type) }}\n >\n {heroProduct.icon ? (\n <img\n src={heroProduct.icon}\n alt={heroProduct.name}\n className=\"size-32 rounded-2xl object-cover\"\n />\n ) : (\n <ProductTypeIcon\n type={heroProduct.type}\n className=\"size-32\"\n style={{ color: productColor }}\n />\n )}\n </div>\n </div>\n </div>\n {/* Featured App Selector */}\n {featuredProducts.length > 1 && (\n <div className=\"relative z-10 mt-6 flex gap-2\">\n {featuredProducts.map((product, index) => (\n <button\n type=\"button\"\n key={product.id}\n onClick={() => setActiveIndex(index)}\n className={`h-2 w-8 rounded-full transition-all ${\n index === activeIndex\n ? 'bg-action-primary-bg'\n : 'bg-border-default hover:bg-action-primary-border'\n }`}\n aria-label={tr('pages.marketplace.viewProduct', 'View {{name}}', {\n name: product.name,\n })}\n />\n ))}\n </div>\n )}\n </GlassCard>\n );\n};\n\n// ========================================\n// Loading Skeleton Components\n// ========================================\n\nconst ProductCardSkeleton: FC<{ variant?: 'default' | 'compact' | 'featured' }> = ({\n variant = 'default',\n}) => {\n if (variant === 'featured') {\n return (\n <div className=\"h-full animate-pulse rounded-2xl border border-border-seam bg-bg-surface p-6\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-16 rounded-2xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-4 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-4 space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n if (variant === 'compact') {\n return (\n <div className=\"flex animate-pulse items-center gap-3 p-3\">\n <div className=\"size-12 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"animate-pulse rounded-xl border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"size-14 rounded-xl bg-bg-sunken\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"h-3 w-1/2 rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"mt-3 space-y-2\">\n <div className=\"h-3 w-full rounded bg-bg-sunken\" />\n <div className=\"h-3 w-2/3 rounded bg-bg-sunken\" />\n </div>\n </div>\n );\n};\n\n// Hero Section Skeleton\nconst HeroSkeleton: FC = () => (\n <div className=\"animate-pulse rounded-2xl bg-gradient-to-r from-bg-surface via-bg-sunken to-bg-surface p-8\">\n <div className=\"flex items-center gap-8\">\n <div className=\"flex-1 space-y-4\">\n <div className=\"flex gap-2\">\n <div className=\"h-6 w-24 rounded-full bg-bg-sunken\" />\n <div className=\"h-6 w-20 rounded-full bg-bg-sunken\" />\n </div>\n <div className=\"h-9 w-3/4 rounded bg-bg-sunken\" />\n <div className=\"space-y-2\">\n <div className=\"h-4 w-full rounded bg-bg-sunken\" />\n <div className=\"h-4 w-5/6 rounded bg-bg-sunken\" />\n </div>\n <div className=\"flex gap-4\">\n <div className=\"h-5 w-24 rounded bg-bg-sunken\" />\n <div className=\"h-5 w-32 rounded bg-bg-sunken\" />\n </div>\n <div className=\"h-12 w-36 rounded-lg bg-bg-sunken\" />\n </div>\n <div className=\"hidden size-48 rounded-3xl bg-bg-sunken lg:block\" />\n </div>\n <div className=\"mt-6 flex gap-2\">\n {[...Array(3)].map((_, i) => (\n <div key={i} className=\"h-2 w-8 rounded-full bg-bg-sunken\" />\n ))}\n </div>\n </div>\n);\n\n// Category Chips Skeleton (Browse by Type)\nconst CHIP_WIDTHS = [100, 120, 90, 110, 130, 95, 115, 105];\n\nconst CategoryChipsSkeleton: FC = () => (\n <div className=\"space-y-4\">\n <div className=\"h-6 w-32 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"flex flex-wrap gap-2\">\n {CHIP_WIDTHS.map((width, i) => (\n <div\n key={i}\n className=\"h-9 animate-pulse rounded-full bg-bg-sunken\"\n style={{ width: `${width}px` }}\n />\n ))}\n </div>\n </div>\n);\n\n// Section Header Skeleton\nconst SectionHeaderSkeleton: FC = () => (\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 animate-pulse rounded-xl bg-bg-sunken\" />\n <div className=\"space-y-1\">\n <div className=\"h-5 w-40 animate-pulse rounded bg-bg-sunken\" />\n <div className=\"h-4 w-32 animate-pulse rounded bg-bg-sunken\" />\n </div>\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-bg-sunken\" />\n </div>\n);\n\n// Full Page Skeleton - shows complete loading state\nconst MarketplacePageSkeleton: FC = () => (\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n {/* Hero Section */}\n <HeroSkeleton />\n\n {/* Browse by Type */}\n <CategoryChipsSkeleton />\n\n {/* Most Downloaded Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* New Releases Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} />\n ))}\n </div>\n </div>\n\n {/* Free Apps Section */}\n <div>\n <SectionHeaderSkeleton />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {[...Array(6)].map((_, i) => (\n <ProductCardSkeleton key={i} variant=\"compact\" />\n ))}\n </div>\n </div>\n </div>\n);\n\n// ========================================\n// Filter Types\n// ========================================\n\ntype PricingFilter = 'all' | 'free' | 'paid' | 'subscription';\ntype RatingFilter = 'any' | '3' | '3.5' | '4' | '4.5';\ntype SortOption =\n | 'relevance'\n | 'rating'\n | 'downloads'\n | 'newest'\n | 'price-low'\n | 'price-high'\n | 'name-asc';\n\ninterface FilterDropdownProps {\n label: string;\n value: string;\n options: { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nconst FilterDropdown: FC<FilterDropdownProps> = ({ label, value, options, onChange }) => {\n const selectId = useId();\n\n return (\n <label\n htmlFor={selectId}\n className=\"relative 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 transition-colors hover:bg-bg-sunken focus-within:border-action-primary-border focus-within:ring-2 focus-within:ring-action-primary-border/20\"\n >\n <span className=\"sr-only\">{label}</span>\n <span className=\"text-text-muted\">{label}:</span>\n <select\n id={selectId}\n aria-label={label}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n className=\"min-w-[8rem] appearance-none bg-bg-surface pr-6 text-sm font-medium text-text-primary scheme-normal dark:scheme-dark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <ChevronDown className=\"pointer-events-none absolute right-3 size-4 text-text-muted\" />\n </label>\n );\n};\n\n// ========================================\n// Main Page Component\n// ========================================\n\nexport const MarketplaceHomePage: FC = () => {\n const navigate = useNavigate();\n const { basePath, user } = useStore();\n const tr = useTr();\n const searchInputId = useId();\n const bus = useEventBus();\n const {\n cart,\n cartOpen,\n closeCart,\n itemCount,\n cartItems,\n subtotal,\n tax,\n total,\n updateQuantity,\n removeProduct,\n } = useCart();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedType, setSelectedType] = useState<string>('all');\n\n // Filter states\n const [pricingFilter, setPricingFilter] = useState<PricingFilter>('all');\n const [ratingFilter, setRatingFilter] = useState<RatingFilter>('any');\n const [selectedCategory, setSelectedCategory] = useState<string>('all');\n const [selectedLicense, setSelectedLicense] = useState<string>('all');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [sortOption, setSortOption] = useState<SortOption>('relevance');\n const [showFilters, setShowFilters] = useState(false);\n\n // Debounce search query - 1.5 seconds for better UX\n const debouncedSearchQuery = useDebounce(searchQuery, 1500);\n\n // Filter options\n const pricingOptions = [\n { value: 'all', label: tr('pages.marketplace.pricingAll', 'All Prices') },\n { value: 'free', label: tr('pages.marketplace.pricingFree', 'Free') },\n { value: 'paid', label: tr('pages.marketplace.pricingPaid', 'Paid (One-time)') },\n { value: 'subscription', label: tr('pages.marketplace.pricingSubscription', 'Subscription') },\n ];\n\n const ratingOptions = [\n { value: 'any', label: tr('pages.marketplace.ratingAny', 'Any Rating') },\n { value: '3', label: tr('pages.marketplace.rating3', '3+ Stars') },\n { value: '3.5', label: tr('pages.marketplace.rating3_5', '3.5+ Stars') },\n { value: '4', label: tr('pages.marketplace.rating4', '4+ Stars') },\n { value: '4.5', label: tr('pages.marketplace.rating4_5', '4.5+ Stars') },\n ];\n\n const sortOptions = [\n { value: 'relevance', label: tr('pages.marketplace.sortRelevance', 'Relevance') },\n { value: 'rating', label: tr('pages.marketplace.sortRating', 'Highest Rated') },\n { value: 'downloads', label: tr('pages.marketplace.sortDownloads', 'Most Downloads') },\n { value: 'newest', label: tr('pages.marketplace.sortNewest', 'Newest First') },\n { value: 'price-low', label: tr('pages.marketplace.sortPriceLow', 'Price: Low to High') },\n { value: 'price-high', label: tr('pages.marketplace.sortPriceHigh', 'Price: High to Low') },\n { value: 'name-asc', label: tr('pages.marketplace.sortNameAsc', 'Name A-Z') },\n ];\n\n // Map UI sort option to GraphQL StoreSortBy enum\n const mapSortToApi = (sort: SortOption): StoreSortBy => {\n const mapping: Record<SortOption, StoreSortBy> = {\n relevance: StoreSortBy.Relevance,\n rating: StoreSortBy.Rating,\n downloads: StoreSortBy.Downloads,\n newest: StoreSortBy.Newest,\n 'price-low': StoreSortBy.PriceLow,\n 'price-high': StoreSortBy.PriceHigh,\n 'name-asc': StoreSortBy.NameAsc,\n };\n return mapping[sort];\n };\n\n // Map UI pricing filter to GraphQL PricingModel enum\n const mapPricingToApi = (pricing: PricingFilter): PricingModel | undefined => {\n if (pricing === 'all') return undefined;\n const mapping: Record<string, PricingModel> = {\n free: PricingModel.Free,\n paid: PricingModel.PaidOnetime,\n subscription: PricingModel.Subscription,\n };\n return mapping[pricing];\n };\n\n // Check if any filter is active (using debounced search for API calls)\n const hasActiveFilters =\n debouncedSearchQuery.trim() !== '' ||\n pricingFilter !== 'all' ||\n ratingFilter !== 'any' ||\n selectedCategory !== 'all' ||\n selectedLicense !== 'all' ||\n featuredOnly ||\n selectedType !== 'all' ||\n sortOption !== 'relevance';\n\n // Check if user is currently typing (search not yet debounced)\n const isSearchPending = searchQuery.trim() !== '' && searchQuery !== debouncedSearchQuery;\n\n const clearAllFilters = () => {\n setSearchQuery('');\n setPricingFilter('all');\n setRatingFilter('any');\n setSelectedCategory('all');\n setSelectedLicense('all');\n setFeaturedOnly(false);\n setSortOption('relevance');\n setSelectedType('all');\n };\n\n // Count active filters (excluding search and sort)\n const activeFilterCount =\n (pricingFilter !== 'all' ? 1 : 0) +\n (ratingFilter !== 'any' ? 1 : 0) +\n (selectedCategory !== 'all' ? 1 : 0) +\n (selectedLicense !== 'all' ? 1 : 0) +\n (featuredOnly ? 1 : 0) +\n (selectedType !== 'all' ? 1 : 0);\n\n // Build BrowseStoreInput from filter states for API call\n const browseStoreInput: BrowseStoreInput = useMemo(() => {\n const input: BrowseStoreInput = {\n sortBy: mapSortToApi(sortOption),\n pagination: { limit: 50 },\n };\n\n // Only add filter if we have active filters\n if (hasActiveFilters) {\n input.filter = {};\n\n // Search query (debounced)\n if (debouncedSearchQuery.trim()) {\n input.filter.search = debouncedSearchQuery.trim();\n }\n\n // Type filter\n if (selectedType !== 'all') {\n input.filter.type = selectedType as BrowseStoreInput['filter'] extends { type?: infer T }\n ? T\n : never;\n }\n\n // Category filter\n if (selectedCategory !== 'all') {\n input.filter.category = selectedCategory;\n }\n\n // Pricing filter\n const pricingModel = mapPricingToApi(pricingFilter);\n if (pricingModel) {\n input.filter.pricingModel = pricingModel;\n }\n\n // Rating filter\n if (ratingFilter !== 'any') {\n input.filter.minRating = parseFloat(ratingFilter);\n }\n\n // Featured filter\n if (featuredOnly) {\n input.filter.featured = true;\n }\n\n // License filter\n if (selectedLicense !== 'all') {\n input.filter.license = selectedLicense;\n }\n }\n\n return input;\n }, [\n debouncedSearchQuery,\n selectedType,\n selectedCategory,\n pricingFilter,\n ratingFilter,\n featuredOnly,\n selectedLicense,\n sortOption,\n hasActiveFilters,\n ]);\n\n // Fetch grouped products for homepage (when no filters active)\n const {\n featured,\n mostDownloaded,\n recentlyAdded,\n freeItems,\n loading: homeLoading,\n error: homeError,\n refetch: refetchHome,\n } = useHomeGroupedProducts({ skip: hasActiveFilters });\n\n // Fetch filtered products from API when filters are active\n const {\n products: browseProducts,\n totalCount,\n facets,\n loading: browseLoading,\n error: browseError,\n refetch: refetchBrowse,\n } = useBrowseStoreWithContext(hasActiveFilters ? browseStoreInput : {}, {\n skip: !hasActiveFilters,\n });\n\n // Determine which data to show\n const loading = hasActiveFilters ? browseLoading : homeLoading;\n const error = hasActiveFilters ? browseError : homeError;\n const refetch = hasActiveFilters ? refetchBrowse : refetchHome;\n\n // Products to display when filters are active\n const filteredProducts = hasActiveFilters ? browseProducts : [];\n\n // Emit search event when debounced query resolves. Read totalCount via ref so\n // we don't re-emit when only the count changes.\n const totalCountRef = useRef(totalCount);\n totalCountRef.current = totalCount;\n const busRef = useRef(bus);\n busRef.current = bus;\n useEffect(() => {\n if (!debouncedSearchQuery.trim() || browseLoading) return;\n (busRef.current as unknown as { emit: (name: string, payload: unknown) => void }).emit(\n 'store.marketplace.searched',\n {\n query: debouncedSearchQuery,\n resultCount: totalCountRef.current,\n }\n );\n }, [debouncedSearchQuery, browseLoading]);\n\n const handleProductClick = useCallback(\n (product: StoreProduct) => {\n navigate(`${basePath}/marketplace/product/${product.id}`);\n },\n [navigate, basePath]\n );\n\n const handleCheckout = () => {\n closeCart();\n navigate(`${basePath}/cart`);\n };\n\n // Get all unique products for the homepage \"All Apps\" section\n const allProducts = useMemo(\n () => [\n ...new Map(\n [...featured, ...mostDownloaded, ...recentlyAdded, ...freeItems].map((p) => [p.id, p])\n ).values(),\n ],\n [featured, mostDownloaded, recentlyAdded, freeItems]\n );\n\n // Fetch installations to surface deprecated (UNLISTED) apps the user has installed\n const isAuthenticated = !!user?.id;\n const { data: installationsData } = useGetMyStoreInstallationsQuery({\n variables: { limit: 100 },\n skip: !isAuthenticated,\n fetchPolicy: 'cache-and-network',\n });\n\n // Deduplicate by productId (user may have installed in multiple workspaces)\n const deprecatedInstalledProducts = useMemo<DeprecatedProduct[]>(() => {\n if (!installationsData?.myStoreAppInstallations?.items) return [];\n const seen = new Set<string>();\n const result: DeprecatedProduct[] = [];\n for (const item of installationsData.myStoreAppInstallations.items) {\n if (item.productStatus === 'UNLISTED' && !seen.has(item.productId)) {\n seen.add(item.productId);\n result.push({\n id: item.productId,\n name: item.productName ?? 'Unknown App',\n icon: item.productIcon ?? undefined,\n });\n }\n }\n return result;\n }, [installationsData]);\n\n // Build dynamic filter options from facets (when available) or from homepage products\n const typeCategories = useMemo(() => {\n // Use facets from API if available\n if (facets?.types && facets.types.length > 0) {\n return [\n { id: 'all', name: tr('pages.marketplace.typeAll', 'All Apps'), count: totalCount },\n ...facets.types.map((t) => ({\n id: t.value,\n name: t.value.charAt(0) + t.value.slice(1).toLowerCase().replace(/_/g, ' '),\n count: t.count,\n })),\n ];\n }\n // Fallback to homepage products: count each type in a single pass.\n const typeCounts = allProducts.reduce<Map<string, number>>((acc, p) => {\n acc.set(p.type, (acc.get(p.type) ?? 0) + 1);\n return acc;\n }, new Map());\n const typeOptions = Array.from(typeCounts.entries())\n .flatMap(([type, count]) =>\n count > 0\n ? [\n {\n id: type,\n name: type.charAt(0) + type.slice(1).toLowerCase().replace(/_/g, ' '),\n count,\n },\n ]\n : []\n )\n .sort((a, b) => b.count - a.count);\n return [\n { id: 'all', name: tr('pages.marketplace.typeAll', 'All Apps'), count: allProducts.length },\n ...typeOptions,\n ];\n }, [facets, allProducts, totalCount, tr]);\n\n // Build category options from facets or products\n const categoryOptions = useMemo(() => {\n if (facets?.categories && facets.categories.length > 0) {\n return [\n { value: 'all', label: tr('pages.marketplace.allCategories', 'All Categories') },\n ...facets.categories.map((c) => ({\n value: c.value,\n label: c.value,\n })),\n ];\n }\n return [\n { value: 'all', label: tr('pages.marketplace.allCategories', 'All Categories') },\n ...Array.from(new Set(allProducts.flatMap((p) => (p.category ? [p.category] : []))))\n .map((cat) => ({\n value: cat as string,\n label: cat as string,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n ];\n }, [facets, allProducts, tr]);\n\n // Build license options from facets or static list\n const licenseOptions = useMemo(() => {\n if (facets?.licenses && facets.licenses.length > 0) {\n return [\n { value: 'all', label: tr('pages.marketplace.allLicenses', 'All Licenses') },\n ...facets.licenses.map((l) => ({\n value: l.value,\n label: l.value,\n })),\n ];\n }\n return [{ value: 'all', label: tr('pages.marketplace.allLicenses', 'All Licenses') }];\n }, [facets, tr]);\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"relative overflow-hidden border-b border-border-seam bg-bg-surface px-6 py-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('pages.marketplace.appTitle', 'App')}{' '}\n <GradientText>{tr('pages.marketplace.title', 'Marketplace')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'pages.marketplace.subtitle',\n 'Discover apps, workflows, and integrations for your workspace'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n data-tour=\"store-marketplace-cart-button\"\n onClick={() => navigate(`${basePath}/cart`)}\n className=\"cursor-pointer relative flex items-center gap-2 rounded-lg border border-border-default bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ShoppingCart className=\"size-5\" />\n {tr('pages.marketplace.cart', 'Cart')}\n {itemCount > 0 && (\n <span className=\"absolute -right-2 -top-2 flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs font-bold text-action-primary-text\">\n {itemCount}\n </span>\n )}\n </button>\n </div>\n\n {/* Search Bar - Live filtering as you type */}\n <div className=\"mt-4\">\n <div className=\"relative\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {tr('pages.marketplace.search.label', 'Search marketplace products')}\n </label>\n <Search className=\"absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-muted\" />\n <input\n id={searchInputId}\n data-tour=\"store-marketplace-search-input\"\n aria-label={tr('pages.marketplace.search.label', 'Search marketplace products')}\n type=\"text\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={tr(\n 'pages.marketplace.search.placeholder',\n 'Search apps, workflows, integrations...'\n )}\n className=\"w-full rounded-xl border border-border-default bg-bg-sunken py-3 pl-12 pr-4 text-text-primary placeholder-text-muted focus:border-action-primary-border focus:bg-bg-surface focus:outline-none focus:ring-2 focus:ring-action-primary-border/20\"\n />\n {searchQuery && (\n <button\n type=\"button\"\n onClick={() => setSearchQuery('')}\n aria-label={tr('pages.marketplace.search.clear', 'Clear search')}\n className=\"cursor-pointer absolute right-4 top-1/2 -translate-y-1/2 text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n {/* Search results count / pending indicator */}\n {searchQuery.trim() && (\n <div\n aria-live=\"polite\"\n className=\"mt-2 flex items-center gap-2 text-sm text-text-muted\"\n >\n {isSearchPending || browseLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n <span>{tr('pages.marketplace.search.searching', 'Searching...')}</span>\n </>\n ) : (\n <span>\n {tr(\n 'pages.marketplace.search.resultCount',\n '{{count}} result{{plural}} for \"{{query}}\"',\n {\n count: totalCount,\n plural: totalCount !== 1 ? 's' : '',\n query: debouncedSearchQuery,\n }\n )}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Filter Bar */}\n <div className=\"mt-4 flex flex-wrap items-center gap-3\">\n {/* Filter Toggle Button */}\n <button\n type=\"button\"\n data-tour=\"store-marketplace-filters-button\"\n onClick={() => setShowFilters(!showFilters)}\n className={`cursor-pointer flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors ${\n showFilters || activeFilterCount > 0\n ? 'border-action-primary-border bg-action-primary-bg/10 text-text-link'\n : 'border-border-default bg-bg-surface text-text-primary transition-colors hover:bg-bg-sunken'\n }`}\n >\n <SlidersHorizontal className=\"size-4\" />\n {tr('pages.marketplace.filters', 'Filters')}\n {activeFilterCount > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-action-primary-bg text-xs text-action-primary-text\">\n {activeFilterCount}\n </span>\n )}\n </button>\n\n {/* Quick Filter Chips - Always visible */}\n <div\n className=\"flex flex-wrap items-center gap-2\"\n data-tour=\"store-marketplace-quick-filters\"\n >\n <button\n type=\"button\"\n onClick={() => setPricingFilter(pricingFilter === 'free' ? 'all' : 'free')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n pricingFilter === 'free'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Gift className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterFree', 'Free')}\n </button>\n <button\n type=\"button\"\n onClick={() => setRatingFilter(ratingFilter === '4' ? 'any' : '4')}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n ratingFilter === '4'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Star className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterRating', '4+ Stars')}\n </button>\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(!featuredOnly)}\n className={`cursor-pointer flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors ${\n featuredOnly\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary transition-colors hover:bg-bg-surface'\n }`}\n >\n <Sparkles className=\"size-3.5\" />\n {tr('pages.marketplace.quickFilterFeatured', 'Featured')}\n </button>\n </div>\n\n {/* Clear Filters */}\n {hasActiveFilters && (\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-text-primary\"\n >\n <X className=\"size-4\" aria-hidden=\"true\" />\n {tr('pages.marketplace.clearAll', 'Clear all')}\n </button>\n )}\n\n {/* Sort Dropdown - Right aligned */}\n <div className=\"ml-auto\" data-tour=\"store-marketplace-section-sort\">\n <FilterDropdown\n label={tr('pages.marketplace.sortLabel', 'Sort')}\n value={sortOption}\n options={sortOptions}\n onChange={(value) => setSortOption(value as SortOption)}\n />\n </div>\n </div>\n\n {/* Expanded Filters Panel */}\n {showFilters && (\n <div className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex flex-wrap items-start gap-6\">\n {/* Pricing Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterPricing', 'Pricing')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterPrice', 'Price')}\n value={pricingFilter}\n options={pricingOptions}\n onChange={(value) => setPricingFilter(value as PricingFilter)}\n />\n </div>\n\n {/* Rating Filter */}\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterMinRating', 'Min Rating')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterRating', 'Rating')}\n value={ratingFilter}\n options={ratingOptions}\n onChange={(value) => setRatingFilter(value as RatingFilter)}\n />\n </div>\n\n {/* Category Filter */}\n {categoryOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterCategory', 'Category')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterCategory', 'Category')}\n value={selectedCategory}\n options={categoryOptions}\n onChange={setSelectedCategory}\n />\n </div>\n )}\n\n {/* License Filter */}\n {licenseOptions.length > 1 && (\n <div className=\"flex flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wider text-text-muted\">\n {tr('pages.marketplace.filterLicense', 'License')}\n </span>\n <FilterDropdown\n label={tr('pages.marketplace.filterLicense', 'License')}\n value={selectedLicense}\n options={licenseOptions}\n onChange={setSelectedLicense}\n />\n </div>\n )}\n </div>\n\n {/* Active Filters Summary */}\n {activeFilterCount > 0 && (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border-default pt-4\">\n <span className=\"text-xs font-medium text-text-muted\">\n {tr('pages.marketplace.activeFilters', 'Active filters:')}\n </span>\n {selectedType !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedType('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterType', 'Type:')}\n </span>{' '}\n {selectedType}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {pricingFilter !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setPricingFilter('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterPrice', 'Price:')}\n </span>{' '}\n {pricingOptions.find((o) => o.value === pricingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {ratingFilter !== 'any' && (\n <button\n type=\"button\"\n onClick={() => setRatingFilter('any')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterRating', 'Rating:')}\n </span>{' '}\n {ratingOptions.find((o) => o.value === ratingFilter)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedCategory !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedCategory('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterCategory', 'Category:')}\n </span>{' '}\n {selectedCategory}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {selectedLicense !== 'all' && (\n <button\n type=\"button\"\n onClick={() => setSelectedLicense('all')}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <span className=\"text-text-muted\">\n {tr('pages.marketplace.activeFilterLicense', 'License:')}\n </span>{' '}\n {licenseOptions.find((o) => o.value === selectedLicense)?.label}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n {featuredOnly && (\n <button\n type=\"button\"\n onClick={() => setFeaturedOnly(false)}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-full border border-action-primary-border bg-action-primary-bg/5 px-3 py-1.5 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-action-primary-bg/15\"\n >\n <Sparkles className=\"size-3\" />\n {tr('pages.marketplace.activeFilterFeatured', 'Featured Only')}\n <X className=\"size-3\" aria-hidden=\"true\" />\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </header>\n\n {/* Main Content */}\n <main className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-7xl space-y-8 p-6\">\n <PagePurpose>\n {tr(\n 'pages.marketplace.pagePurpose',\n 'The marketplace is where you discover and install apps, agents, workflows, nodes and widgets that extend your workspaces. Browse featured and trending picks, filter by type or category, search for something specific, then open any product to view pricing and reviews before adding it to your cart.'\n )}\n </PagePurpose>\n {/* Error State */}\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 p-8 text-center\"\n >\n <AlertCircle className=\"mb-4 size-12 text-status-error-text\" />\n <h3 className=\"mb-2 text-lg font-semibold text-status-error-text-emphasis\">\n {tr('pages.marketplace.errorTitle', 'Failed to load marketplace')}\n </h3>\n <p className=\"mb-4 text-sm text-status-error-text\">\n {error.message ||\n tr(\n 'pages.marketplace.errorDescription',\n 'An error occurred while fetching products'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"cursor-pointer rounded-lg bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-emphasis\"\n >\n {tr('common.tryAgain', 'Try Again')}\n </button>\n </div>\n )}\n\n {/* Loading State */}\n {loading && !error && <MarketplacePageSkeleton />}\n\n {/* Content - Only show when data is loaded */}\n {!loading && !error && (\n <>\n {/* Hero Section - Featured Products (only when no filters) */}\n {!hasActiveFilters && selectedType === 'all' && featured.length > 0 && (\n <HeroSection\n dataTour=\"store-marketplace-hero-section\"\n featuredProducts={featured}\n onProductClick={handleProductClick}\n />\n )}\n\n {/* Type Filter Categories */}\n {typeCategories.length > 1 && (\n <div>\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {tr('pages.marketplace.browseByType', 'Browse by Type')}\n </h2>\n <div\n className=\"flex flex-wrap gap-2\"\n data-tour=\"store-marketplace-type-categories\"\n >\n {typeCategories.map((category) => (\n <CategoryChip\n key={category.id}\n typeId={category.id}\n name={category.name}\n count={category.count}\n active={selectedType === category.id}\n onClick={() => setSelectedType(category.id)}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Show Filtered Results when filters are active */}\n {hasActiveFilters && (\n <section>\n <SectionHeader\n icon={Filter}\n title={\n debouncedSearchQuery\n ? tr('pages.marketplace.searchResults', 'Search Results')\n : tr('pages.marketplace.filteredResults', 'Filtered Results')\n }\n subtitle={tr('pages.marketplace.appsFound', '{{count}} app{{plural}} found', {\n count: totalCount,\n plural: totalCount !== 1 ? 's' : '',\n })}\n />\n {filteredProducts.length > 0 ? (\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\"\n data-tour=\"store-marketplace-product-grid\"\n >\n {filteredProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tr('pages.marketplace.noFilterMatch', 'No apps match your filters')}\n description={tr(\n 'pages.marketplace.noFilterMatchDescription',\n 'Try adjusting your filter criteria or clearing filters to see more results.'\n )}\n action={\n <button\n type=\"button\"\n onClick={clearAllFilters}\n className=\"cursor-pointer rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n {tr('pages.marketplace.clearAllFilters', 'Clear All Filters')}\n </button>\n }\n />\n )}\n </section>\n )}\n\n {/* Show grouped sections only when no filters are active */}\n {!hasActiveFilters && selectedType === 'all' && (\n <>\n {/* Most Downloaded / Trending Apps */}\n {mostDownloaded.length > 0 && (\n <section>\n <SectionHeader\n icon={TrendingUp}\n title={tr('pages.marketplace.mostDownloaded', 'Most Downloaded')}\n subtitle={tr(\n 'pages.marketplace.mostDownloadedSubtitle',\n 'Top picks by our community'\n )}\n />\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n data-tour=\"store-marketplace-most-downloaded\"\n >\n {mostDownloaded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Recently Added / New Releases */}\n {recentlyAdded.length > 0 && (\n <section>\n <SectionHeader\n icon={Clock}\n title={tr('pages.marketplace.newReleases', 'New Releases')}\n subtitle={tr(\n 'pages.marketplace.newReleasesSubtitle',\n 'Recently published apps'\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {recentlyAdded.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Free Items */}\n {freeItems.length > 0 && (\n <section>\n <SectionHeader\n icon={Gift}\n title={tr('pages.marketplace.freeApps', 'Free Apps')}\n subtitle={tr(\n 'pages.marketplace.freeAppsSubtitle',\n 'No cost, full functionality'\n )}\n />\n <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6\">\n {freeItems.slice(0, 6).map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n variant=\"compact\"\n />\n ))}\n </div>\n </section>\n )}\n\n {/* All Apps */}\n {allProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={Box}\n title={tr('pages.marketplace.allApps', 'All Apps')}\n subtitle={tr(\n 'pages.marketplace.allAppsSubtitle',\n '{{count}} apps available',\n { count: allProducts.length }\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {allProducts.map((product) => (\n <AppCard\n key={product.id}\n product={product}\n onClick={() => handleProductClick(product)}\n />\n ))}\n </div>\n </section>\n )}\n </>\n )}\n\n {/* Deprecated Installed Apps — shown whenever user has UNLISTED products installed */}\n {isAuthenticated && deprecatedInstalledProducts.length > 0 && (\n <section>\n <SectionHeader\n icon={AlertCircle}\n title={tr(\n 'pages.marketplace.deprecatedApps',\n \"Deprecated Apps You've Installed\"\n )}\n subtitle={tr(\n 'pages.marketplace.deprecatedAppsSubtitle',\n 'These apps are no longer supported — hover the badge for details'\n )}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {deprecatedInstalledProducts.map((product) => (\n <DeprecatedAppCard\n key={product.id}\n product={product}\n onClick={() => navigate(`${basePath}/marketplace/product/${product.id}`)}\n />\n ))}\n </div>\n </section>\n )}\n\n {/* Empty State - No products at all */}\n {allProducts.length === 0 && (\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('pages.marketplace.noAppsTitle', 'No apps available yet')}\n description={tr(\n 'pages.marketplace.noAppsDescription',\n 'The marketplace is empty. Check back soon for new apps, workflows, and integrations.'\n )}\n />\n )}\n </>\n )}\n </div>\n </main>\n\n {/* Cart Drawer */}\n <CartDrawer\n isOpen={cartOpen}\n onClose={closeCart}\n onCheckout={handleCheckout}\n cartItems={cartItems}\n itemCount={itemCount}\n currency={cart?.currency || 'USD'}\n subtotal={subtotal}\n tax={tax}\n total={total}\n updateQuantity={updateQuantity}\n removeProduct={removeProduct}\n />\n </div>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-store",
3
- "version": "2026.1013.2",
3
+ "version": "2026.1013.3",
4
4
  "description": "Store microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [