@burdenoff/microfe-vibecontrols 2026.518.2 → 2026.519.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogDetailPage.js","names":[],"sources":["../../src/pages/CatalogDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n ArrowLeft,\n BookOpen,\n Layers,\n Zap,\n BarChart3,\n FileText,\n Settings,\n RefreshCw,\n Plus,\n Network,\n Shield,\n Download,\n ArrowRight,\n Package,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { ScorecardPanel } from '@/components/scorecard';\nimport { DocsTabPanel } from '@/components/docs';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { CatalogForm } from '@/components/catalog';\nimport type { CatalogFormData } from '@/components/catalog';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetCatalogQuery,\n useUpdateCatalogMutation,\n useAddComponentToCatalogMutation,\n useRemoveComponentFromCatalogMutation,\n useGetCatalogDependencyGraphQuery,\n useGetCatalogSbomQuery,\n useListAllComponentsForCatalogQuery,\n type GetCatalogDependencyGraphQuery,\n type GetCatalogSbomQuery,\n type ListAllComponentsForCatalogQuery,\n} from '@/generated/wspace-operations';\n\ntype CatalogTab =\n | 'overview'\n | 'components'\n | 'dependency-graph'\n | 'sbom'\n | 'docs'\n | 'scorecard'\n | 'settings';\n\nconst CATALOG_TABS: readonly CatalogTab[] = [\n 'overview',\n 'components',\n 'dependency-graph',\n 'sbom',\n 'docs',\n 'scorecard',\n 'settings',\n];\n\nexport function CatalogDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [activeTab, setActiveTab] = useQueryBackedTab<CatalogTab>(CATALOG_TABS, 'overview');\n const [showEditForm, setShowEditForm] = useState(false);\n const [showAddComponent, setShowAddComponent] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useGetCatalogQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateCatalog] = useUpdateCatalogMutation({ refetchQueries: ['GetCatalog'] });\n const [addComponentToCatalog] = useAddComponentToCatalogMutation({\n refetchQueries: ['GetCatalog'],\n });\n const [removeComponentFromCatalog] = useRemoveComponentFromCatalogMutation({\n refetchQueries: ['GetCatalog'],\n });\n\n const catalog = data?.vibecontrolsCatalog ?? null;\n\n const components: ComponentCardData[] = useMemo(() => {\n return (catalog?.components ?? []).map((entry: any) => {\n const c = entry.component || {};\n return {\n id: c.id as string,\n name: c.name as string,\n description: (c.description as string) || null,\n type: c.type as ComponentCardData['type'],\n lifecycle: c.lifecycle as ComponentCardData['lifecycle'],\n version: (c.version as string) || null,\n language: (c.language as string) || null,\n framework: (c.framework as string) || null,\n tags: (c.tags as string[]) || [],\n createdAt: c.createdAt as string,\n updatedAt: c.updatedAt as string,\n };\n });\n }, [catalog?.components]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/catalogs');\n };\n\n const handleComponentClick = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleUpdateCatalog = async (formData: CatalogFormData) => {\n if (!id) return;\n await updateCatalog({\n variables: {\n id,\n input: {\n name: formData.name,\n description: formData.description || undefined,\n visibility: formData.visibility,\n ownerName: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n setSuccessMessage(tr('catalog.updatedSuccess', 'Catalog updated successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n const tabs: { id: CatalogTab; label: string; icon: typeof BookOpen; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: BookOpen },\n {\n id: 'components',\n label: tr('catalog.components', 'Components'),\n icon: Layers,\n badge: `${catalog?.componentCount ?? 0}`,\n },\n {\n id: 'dependency-graph',\n label: tr('catalog.dependencyGraph', 'Dependency Graph'),\n icon: Network,\n },\n { id: 'sbom', label: tr('catalog.sbom', 'SBOM'), icon: Shield },\n { id: 'docs', label: tr('catalog.docs', 'Docs'), icon: FileText },\n { id: 'scorecard', label: tr('catalog.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'settings', label: tr('common.settings', 'Settings'), icon: Settings },\n ];\n\n // Guard states\n if (loading && !catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <LoadingState message={tr('catalog.loadingDetails', 'Loading catalog details...')} />\n </MotionPage>\n );\n }\n\n if (error && !catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <ErrorState\n title={tr('catalog.failedLoad', 'Failed to load catalog')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <EmptyState\n icon={<BookOpen className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.notFound', 'Catalog not found')}\n description={`No catalog with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={catalog.name}\n description={catalog.description || tr('catalog.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <span\n className={`inline-flex items-center px-2.5 py-1 text-xs font-medium rounded ${\n catalog.visibility === 'PUBLIC'\n ? 'bg-status-success-bg text-status-success-text'\n : catalog.visibility === 'INTERNAL'\n ? 'bg-status-warning-bg text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {catalog.visibility}\n </span>\n </div>\n }\n />\n\n {/* Stat Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n <MotionItem>\n <StatCard\n title={tr('catalog.components', 'Components')}\n value={catalog.componentCount ?? 0}\n icon={<Layers className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.vibes', 'Vibes')}\n value={catalog.vibeCount ?? 0}\n icon={<Zap className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.ownerName', 'Owner')}\n value={catalog.ownerNameName || 'Unassigned'}\n icon={<BookOpen className=\"w-4 h-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Tab Navigation */}\n <div className=\"border-b border-border-default\">\n <nav\n className=\"flex gap-1 -mb-px overflow-x-auto pb-1\"\n aria-label={tr('catalog.tabsAriaLabel', 'Catalog tabs')}\n >\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActiveTab = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 px-3 py-2.5 text-xs sm:gap-2 sm:px-4 sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActiveTab\n ? 'border-action-primary-bg text-action-primary-text'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n aria-selected={isActiveTab}\n role=\"tab\"\n >\n <Icon className=\"w-4 h-4\" />\n {tab.label}\n {tab.badge && (\n <span\n className={`hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${\n isActiveTab\n ? 'bg-action-primary-bg/10 text-action-primary-bg'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.badge}\n </span>\n )}\n </button>\n );\n })}\n </nav>\n </div>\n\n {/* Tab Panels */}\n <AnimatePresence mode=\"wait\">\n {activeTab === 'overview' && (\n <motion.div\n key=\"overview\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n {catalog.description && (\n <Card>\n <div className=\"p-4\">\n <p className=\"text-text-primary\">{catalog.description}</p>\n </div>\n </Card>\n )}\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('catalog.metadata', 'Metadata')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">{tr('catalog.ownerNameLabel', 'Owner')}</p>\n <p className=\"font-medium text-text-primary\">\n {catalog.ownerName || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('catalog.visibility', 'Visibility')}</p>\n <p className=\"font-medium text-text-primary\">{catalog.visibility}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(catalog.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(catalog.updatedAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n </div>\n </Card>\n\n {catalog.tags?.length > 0 && (\n <Card>\n <div className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('catalog.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {catalog.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n </Card>\n )}\n </motion.div>\n )}\n\n {activeTab === 'components' && (\n <motion.div\n key=\"components\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-secondary\">\n {components.length}{' '}\n {tr('catalog.componentsInCatalog', 'components in this catalog')}\n </p>\n <button\n onClick={() => setShowAddComponent(true)}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('catalog.addComponent', 'Add Component')}\n </button>\n </div>\n {components.length > 0 ? (\n <MotionList\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {components.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentClick} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <EmptyState\n icon={<Layers className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noComponents', 'No components')}\n description={tr(\n 'catalog.noComponentsDescription',\n 'Add components to this catalog to track them'\n )}\n />\n )}\n </motion.div>\n )}\n\n {activeTab === 'dependency-graph' && <DependencyGraphTab catalogId={id || ''} tr={tr} />}\n\n {activeTab === 'sbom' && <SBOMTab catalogId={id || ''} tr={tr} />}\n\n {activeTab === 'docs' && (\n <motion.div\n key=\"docs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n <DocsTabPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'scorecard' && (\n <motion.div\n key=\"scorecard\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <ScorecardPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'settings' && (\n <motion.div\n key=\"settings\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('catalog.editCatalog', 'Edit Catalog')}\n </h3>\n <button\n onClick={() => setShowEditForm(true)}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('catalog.editDetails', 'Edit Details')}\n </button>\n </Card>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n <CatalogForm\n isOpen={showEditForm}\n onClose={() => setShowEditForm(false)}\n onSubmit={handleUpdateCatalog}\n initialData={{\n name: catalog.name,\n description: catalog.description || '',\n visibility: catalog.visibility,\n owner: catalog.ownerName || '',\n tags: catalog.tags || [],\n }}\n title={tr('catalog.editCatalog', 'Edit Catalog')}\n />\n\n {/* Add Component Dialog */}\n {showAddComponent && (\n <AddComponentDialog\n catalogId={id || ''}\n existingComponentIds={components.map((c) => c.id)}\n onAdd={async (componentId) => {\n await addComponentToCatalog({ variables: { catalogId: id, componentId } });\n setSuccessMessage(tr('catalog.componentAdded', 'Component added to catalog'));\n setTimeout(() => setSuccessMessage(null), 3000);\n setShowAddComponent(false);\n }}\n onClose={() => setShowAddComponent(false)}\n tr={tr}\n />\n )}\n </MotionPage>\n );\n}\n\n/* ── Dependency Graph Tab ──────────────────────────────────────────────── */\n\ninterface DependencyGraphNode {\n id: string;\n name: string;\n type: string;\n version: string | null;\n}\ninterface DependencyGraphEdge {\n source: string;\n target: string;\n version: string | null;\n dependencyType: string | null;\n}\n\nfunction DependencyGraphTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const { data, loading, error, refetch } = useGetCatalogDependencyGraphQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n\n const graph: GetCatalogDependencyGraphQuery['vibecontrolsCatalogDependencyGraph'] | undefined =\n data?.vibecontrolsCatalogDependencyGraph;\n\n return (\n <motion.div\n key=\"dependency-graph\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Network className=\"w-5 h-5\" />\n {tr('catalog.dependencyGraph', 'Dependency Graph')}\n </h3>\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"p-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {loading && !graph && (\n <LoadingState message={tr('catalog.loadingGraph', 'Loading dependency graph...')} />\n )}\n\n {error && !graph && (\n <ErrorState\n title={tr('catalog.graphError', 'Failed to load graph')}\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {graph && graph.nodes.length === 0 && (\n <EmptyState\n icon={<Network className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noGraphNodes', 'No dependency data')}\n description={tr(\n 'catalog.noGraphNodesDesc',\n 'Add components with dependencies to this catalog to visualize the dependency graph'\n )}\n />\n )}\n\n {graph && graph.nodes.length > 0 && (\n <>\n <div className=\"grid grid-cols-2 gap-4\">\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.graphNodes', 'Components')}\n </p>\n <p className=\"text-2xl font-bold text-text-primary\">{graph.nodes.length}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.graphEdges', 'Dependencies')}\n </p>\n <p className=\"text-2xl font-bold text-text-primary\">{graph.edges.length}</p>\n </Card>\n </div>\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h4 className=\"font-medium text-text-primary\">\n {tr('catalog.graphNodesList', 'Components')}\n </h4>\n </div>\n <div className=\"divide-y divide-border-default\">\n {graph.nodes.map((node) => (\n <div key={node.id} className=\"p-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Package className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"font-medium text-text-primary text-sm\">{node.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {node.type}\n {node.version ? ` v${node.version}` : ''}\n </p>\n </div>\n </div>\n <span className=\"text-xs text-text-muted\">\n {graph.edges.filter((e) => e.source === node.id).length} deps\n </span>\n </div>\n ))}\n </div>\n </Card>\n\n {graph.edges.length > 0 && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h4 className=\"font-medium text-text-primary\">\n {tr('catalog.graphEdgesList', 'Dependencies')}\n </h4>\n </div>\n <div className=\"divide-y divide-border-default\">\n {graph.edges.map((edge, idx) => (\n <div key={idx} className=\"p-3 flex items-center gap-3 text-sm\">\n <span className=\"font-medium text-text-primary truncate max-w-[120px]\">\n {graph.nodes.find((n) => n.id === edge.source)?.name || edge.source}\n </span>\n <ArrowRight className=\"w-4 h-4 text-text-muted flex-shrink-0\" />\n <span className=\"text-text-primary truncate max-w-[120px]\">{edge.target}</span>\n {edge.version && (\n <span className=\"text-xs text-text-muted ml-auto\">{edge.version}</span>\n )}\n {edge.dependencyType && (\n <span className=\"text-xs bg-bg-sunken text-text-secondary px-1.5 py-0.5 rounded\">\n {edge.dependencyType}\n </span>\n )}\n </div>\n ))}\n </div>\n </Card>\n )}\n </>\n )}\n </motion.div>\n );\n}\n\n/* ── SBOM Tab ──────────────────────────────────────────────────────────── */\n\ninterface SBOMEntry {\n name: string;\n version: string | null;\n type: string | null;\n license: string | null;\n purl: string | null;\n supplier: string | null;\n}\n\nfunction SBOMTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const { data, loading, error, refetch } = useGetCatalogSbomQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n\n const sbom: GetCatalogSbomQuery['vibecontrolsCatalogSBOM'] | undefined =\n data?.vibecontrolsCatalogSBOM;\n\n const handleDownloadJSON = () => {\n if (!sbom) return;\n const blob = new Blob([JSON.stringify(sbom, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `sbom-${catalogId}.json`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n return (\n <motion.div\n key=\"sbom\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Shield className=\"w-5 h-5\" />\n {tr('catalog.sbom', 'Software Bill of Materials')}\n </h3>\n <div className=\"flex items-center gap-2\">\n {sbom && sbom.components.length > 0 && (\n <button\n onClick={handleDownloadJSON}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Download className=\"w-4 h-4\" />\n {tr('catalog.downloadSBOM', 'Download JSON')}\n </button>\n )}\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"p-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n </div>\n\n {loading && !sbom && (\n <LoadingState message={tr('catalog.loadingSBOM', 'Generating SBOM...')} />\n )}\n\n {error && !sbom && (\n <ErrorState\n title={tr('catalog.sbomError', 'Failed to load SBOM')}\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {sbom && (\n <>\n <div className=\"grid grid-cols-3 gap-4\">\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">{tr('catalog.sbomFormat', 'Format')}</p>\n <p className=\"text-lg font-bold text-text-primary\">{sbom.format}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">{tr('catalog.sbomEntries', 'Entries')}</p>\n <p className=\"text-lg font-bold text-text-primary\">{sbom.totalCount}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.sbomGenerated', 'Generated')}\n </p>\n <p className=\"text-lg font-bold text-text-primary\">\n {new Date(sbom.generatedAt).toLocaleTimeString()}\n </p>\n </Card>\n </div>\n\n {sbom.components.length > 0 ? (\n <Card>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-default\">\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomName', 'Name')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomVersion', 'Version')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomType', 'Type')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomLicense', 'License')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomSupplier', 'Supplier')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-default\">\n {sbom.components.map((entry, idx) => (\n <tr key={idx} className=\"hover:bg-bg-sunken/50 transition-colors\">\n <td className=\"p-3 font-medium text-text-primary\">{entry.name}</td>\n <td className=\"p-3 text-text-secondary\">{entry.version || '-'}</td>\n <td className=\"p-3 text-text-secondary\">{entry.type || '-'}</td>\n <td className=\"p-3\">\n {entry.license ? (\n <span className=\"inline-flex px-2 py-0.5 text-xs bg-status-success-bg text-status-success-text rounded\">\n {entry.license}\n </span>\n ) : (\n '-'\n )}\n </td>\n <td className=\"p-3 text-text-secondary\">{entry.supplier || '-'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </Card>\n ) : (\n <EmptyState\n icon={<Shield className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noSBOMEntries', 'No SBOM entries')}\n description={tr(\n 'catalog.noSBOMEntriesDesc',\n 'Add components with SBOM data or dependencies to this catalog to generate an aggregated SBOM'\n )}\n />\n )}\n </>\n )}\n </motion.div>\n );\n}\n\n/* ── Add Component Dialog ──────────────────────────────────────────────── */\n\nfunction AddComponentDialog({\n catalogId,\n existingComponentIds,\n onAdd,\n onClose,\n tr,\n}: {\n catalogId: string;\n existingComponentIds: string[];\n onAdd: (componentId: string) => Promise<void>;\n onClose: () => void;\n tr: (key: string, fallback: string) => string;\n}) {\n const [adding, setAdding] = useState(false);\n const [search, setSearch] = useState('');\n\n const { data, loading } = useListAllComponentsForCatalogQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n type CatalogComponent =\n ListAllComponentsForCatalogQuery['vibecontrolsComponents']['items'][number];\n const available: Array<CatalogComponent> = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n const filtered = items.filter((c: CatalogComponent) => !existingComponentIds.includes(c.id));\n if (!search) return filtered;\n const q = search.toLowerCase();\n return filtered.filter(\n (c: CatalogComponent) =>\n c.name.toLowerCase().includes(q) || c.type.toString().toLowerCase().includes(q)\n );\n }, [data, existingComponentIds, search]);\n\n const handleAdd = async (componentId: string) => {\n setAdding(true);\n try {\n await onAdd(componentId);\n } finally {\n setAdding(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg w-full max-w-md max-h-[70vh] flex flex-col\"\n >\n <div className=\"p-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('catalog.addComponentTitle', 'Add Component to Catalog')}\n </h2>\n <div className=\"mt-3\">\n <input\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={tr('catalog.searchComponents', 'Search components...')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder text-sm\"\n />\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2\">\n {loading && <LoadingState message={tr('common.loading', 'Loading...')} />}\n {!loading && available.length === 0 && (\n <div className=\"p-4 text-center text-text-secondary text-sm\">\n {tr('catalog.noAvailableComponents', 'No available components to add')}\n </div>\n )}\n {available.map((comp) => (\n <button\n key={comp.id}\n onClick={() => handleAdd(comp.id)}\n disabled={adding}\n className=\"w-full flex items-center justify-between p-3 rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50 text-left\"\n >\n <div>\n <p className=\"font-medium text-text-primary text-sm\">{comp.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {comp.type} &middot; {comp.lifecycle}\n </p>\n </div>\n <Plus className=\"w-4 h-4 text-text-secondary\" />\n </button>\n ))}\n </div>\n <div className=\"p-4 border-t border-border-default\">\n <button\n onClick={onClose}\n className=\"w-full px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('common.close', 'Close')}\n </button>\n </div>\n </motion.div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CatalogDetailPage.js","names":[],"sources":["../../src/pages/CatalogDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { AnimatePresence, motion } from 'framer-motion';\nimport {\n ArrowLeft,\n BookOpen,\n Layers,\n Zap,\n BarChart3,\n FileText,\n Settings,\n RefreshCw,\n Plus,\n Network,\n Shield,\n Download,\n ArrowRight,\n Package,\n Trash2,\n} from 'lucide-react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { ScorecardPanel } from '@/components/scorecard';\nimport { DocsTabPanel } from '@/components/docs';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { CatalogForm } from '@/components/catalog';\nimport type { CatalogFormData } from '@/components/catalog';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useQueryBackedTab } from '@/hooks/useQueryBackedTab';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetCatalogQuery,\n useUpdateCatalogMutation,\n useDeleteCatalogMutation,\n useAddComponentToCatalogMutation,\n useRemoveComponentFromCatalogMutation,\n useGetCatalogDependencyGraphQuery,\n useGetCatalogSbomQuery,\n useListAllComponentsForCatalogQuery,\n type GetCatalogDependencyGraphQuery,\n type GetCatalogSbomQuery,\n type ListAllComponentsForCatalogQuery,\n} from '@/generated/wspace-operations';\n\ntype CatalogTab =\n | 'overview'\n | 'components'\n | 'dependency-graph'\n | 'sbom'\n | 'docs'\n | 'scorecard'\n | 'settings';\n\nconst CATALOG_TABS: readonly CatalogTab[] = [\n 'overview',\n 'components',\n 'dependency-graph',\n 'sbom',\n 'docs',\n 'scorecard',\n 'settings',\n];\n\nexport function CatalogDetailPage() {\n const { t } = useI18n();\n const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const [activeTab, setActiveTab] = useQueryBackedTab<CatalogTab>(CATALOG_TABS, 'overview');\n const [showEditForm, setShowEditForm] = useState(false);\n const [showAddComponent, setShowAddComponent] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useGetCatalogQuery({\n variables: { id: id || '' },\n skip: !id,\n fetchPolicy: 'cache-and-network',\n });\n\n const [updateCatalog] = useUpdateCatalogMutation({ refetchQueries: ['GetCatalog'] });\n const [deleteCatalog] = useDeleteCatalogMutation({ refetchQueries: ['ListCatalogs'] });\n const [addComponentToCatalog] = useAddComponentToCatalogMutation({\n refetchQueries: ['GetCatalog'],\n });\n const [removeComponentFromCatalog] = useRemoveComponentFromCatalogMutation({\n refetchQueries: ['GetCatalog'],\n });\n\n const catalog = data?.vibecontrolsCatalog ?? null;\n\n const components: ComponentCardData[] = useMemo(() => {\n return (catalog?.components ?? []).map((entry: any) => {\n const c = entry.component || {};\n return {\n id: c.id as string,\n name: c.name as string,\n description: (c.description as string) || null,\n type: c.type as ComponentCardData['type'],\n lifecycle: c.lifecycle as ComponentCardData['lifecycle'],\n version: (c.version as string) || null,\n language: (c.language as string) || null,\n framework: (c.framework as string) || null,\n tags: (c.tags as string[]) || [],\n createdAt: c.createdAt as string,\n updatedAt: c.updatedAt as string,\n };\n });\n }, [catalog?.components]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_READ))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleBack = () => {\n navigate('/catalogs');\n };\n\n const handleComponentClick = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleUpdateCatalog = async (formData: CatalogFormData) => {\n if (!id) return;\n await updateCatalog({\n variables: {\n id,\n input: {\n name: formData.name,\n description: formData.description || undefined,\n visibility: formData.visibility,\n ownerName: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n setSuccessMessage(tr('catalog.updatedSuccess', 'Catalog updated successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n const handleDeleteCatalog = async () => {\n if (!id) return;\n setIsDeleting(true);\n try {\n await deleteCatalog({ variables: { id } });\n setShowDeleteConfirm(false);\n navigate('/catalogs');\n } finally {\n setIsDeleting(false);\n }\n };\n\n const tabs: { id: CatalogTab; label: string; icon: typeof BookOpen; badge?: string }[] = [\n { id: 'overview', label: tr('nav.overview', 'Overview'), icon: BookOpen },\n {\n id: 'components',\n label: tr('catalog.components', 'Components'),\n icon: Layers,\n badge: `${catalog?.componentCount ?? 0}`,\n },\n {\n id: 'dependency-graph',\n label: tr('catalog.dependencyGraph', 'Dependency Graph'),\n icon: Network,\n },\n { id: 'sbom', label: tr('catalog.sbom', 'SBOM'), icon: Shield },\n { id: 'docs', label: tr('catalog.docs', 'Docs'), icon: FileText },\n { id: 'scorecard', label: tr('catalog.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'settings', label: tr('common.settings', 'Settings'), icon: Settings },\n ];\n\n // Guard states\n if (loading && !catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <LoadingState message={tr('catalog.loadingDetails', 'Loading catalog details...')} />\n </MotionPage>\n );\n }\n\n if (error && !catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <ErrorState\n title={tr('catalog.failedLoad', 'Failed to load catalog')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!catalog) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <EmptyState\n icon={<BookOpen className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.notFound', 'Catalog not found')}\n description={`No catalog with ID \"${id}\" exists`}\n action={\n <button\n onClick={handleBack}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n }\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed top-4 left-4 right-4 sm:left-auto z-50 bg-status-success-bg text-status-success-text px-4 py-3 rounded-md shadow-lg\">\n {successMessage}\n </div>\n )}\n\n {/* Back Button */}\n <button\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n\n {/* Header */}\n <PageHeader\n title={catalog.name}\n description={catalog.description || tr('catalog.noDescription', 'No description')}\n actions={\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <span\n className={`inline-flex items-center px-2.5 py-1 text-xs font-medium rounded ${\n catalog.visibility === 'PUBLIC'\n ? 'bg-status-success-bg text-status-success-text'\n : catalog.visibility === 'INTERNAL'\n ? 'bg-status-warning-bg text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {catalog.visibility}\n </span>\n </div>\n }\n />\n\n {/* Stat Cards */}\n <MotionList stagger={0.04} className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n <MotionItem>\n <StatCard\n title={tr('catalog.components', 'Components')}\n value={catalog.componentCount ?? 0}\n icon={<Layers className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.vibes', 'Vibes')}\n value={catalog.vibeCount ?? 0}\n icon={<Zap className=\"w-4 h-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.ownerName', 'Owner')}\n value={catalog.ownerNameName || 'Unassigned'}\n icon={<BookOpen className=\"w-4 h-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Tab Navigation */}\n <div className=\"border-b border-border-default\">\n <nav\n className=\"flex gap-1 -mb-px overflow-x-auto pb-1\"\n aria-label={tr('catalog.tabsAriaLabel', 'Catalog tabs')}\n >\n {tabs.map((tab) => {\n const Icon = tab.icon;\n const isActiveTab = activeTab === tab.id;\n return (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={`flex shrink-0 items-center gap-1.5 px-3 py-2.5 text-xs sm:gap-2 sm:px-4 sm:text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActiveTab\n ? 'border-action-primary-bg text-action-primary-text'\n : 'border-transparent text-text-secondary hover:text-text-primary hover:border-border-default'\n }`}\n aria-selected={isActiveTab}\n role=\"tab\"\n >\n <Icon className=\"w-4 h-4\" />\n {tab.label}\n {tab.badge && (\n <span\n className={`hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${\n isActiveTab\n ? 'bg-action-primary-bg/10 text-action-primary-bg'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.badge}\n </span>\n )}\n </button>\n );\n })}\n </nav>\n </div>\n\n {/* Tab Panels */}\n <AnimatePresence mode=\"wait\">\n {activeTab === 'overview' && (\n <motion.div\n key=\"overview\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n {catalog.description && (\n <Card>\n <div className=\"p-4\">\n <p className=\"text-text-primary\">{catalog.description}</p>\n </div>\n </Card>\n )}\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary\">\n {tr('catalog.metadata', 'Metadata')}\n </h3>\n </div>\n <div className=\"p-4\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <p className=\"text-text-secondary\">{tr('catalog.ownerNameLabel', 'Owner')}</p>\n <p className=\"font-medium text-text-primary\">\n {catalog.ownerName || tr('common.unset', 'Unset')}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('catalog.visibility', 'Visibility')}</p>\n <p className=\"font-medium text-text-primary\">{catalog.visibility}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.created', 'Created')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(catalog.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div>\n <p className=\"text-text-secondary\">{tr('common.updated', 'Updated')}</p>\n <p className=\"font-medium text-text-primary\">\n {new Date(catalog.updatedAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n </div>\n </Card>\n\n {catalog.tags?.length > 0 && (\n <Card>\n <div className=\"p-4\">\n <h3 className=\"font-semibold text-text-primary mb-3\">\n {tr('catalog.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-2\">\n {catalog.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"px-2.5 py-1 text-sm bg-bg-sunken text-text-secondary rounded-md\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n </Card>\n )}\n </motion.div>\n )}\n\n {activeTab === 'components' && (\n <motion.div\n key=\"components\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-secondary\">\n {components.length}{' '}\n {tr('catalog.componentsInCatalog', 'components in this catalog')}\n </p>\n <button\n onClick={() => setShowAddComponent(true)}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('catalog.addComponent', 'Add Component')}\n </button>\n </div>\n {components.length > 0 ? (\n <MotionList\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {components.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentClick} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <EmptyState\n icon={<Layers className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noComponents', 'No components')}\n description={tr(\n 'catalog.noComponentsDescription',\n 'Add components to this catalog to track them'\n )}\n />\n )}\n </motion.div>\n )}\n\n {activeTab === 'dependency-graph' && <DependencyGraphTab catalogId={id || ''} tr={tr} />}\n\n {activeTab === 'sbom' && <SBOMTab catalogId={id || ''} tr={tr} />}\n\n {activeTab === 'docs' && (\n <motion.div\n key=\"docs\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-6\"\n >\n <DocsTabPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'scorecard' && (\n <motion.div\n key=\"scorecard\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <ScorecardPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\n\n {activeTab === 'settings' && (\n <motion.div\n key=\"settings\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"space-y-4\">\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('catalog.editCatalog', 'Edit Catalog')}\n </h3>\n <button\n onClick={() => setShowEditForm(true)}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n {tr('catalog.editDetails', 'Edit Details')}\n </button>\n </Card>\n <Card padding=\"lg\">\n <h3 className=\"font-semibold text-status-error-text mb-2\">\n {tr('catalog.dangerZone', 'Danger Zone')}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n 'catalog.deleteWarning',\n 'Deleting this catalog will detach all components and vibes. This cannot be undone.'\n )}\n </p>\n <button\n data-testid=\"catalog-delete-button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 bg-status-error-bg text-status-error-text rounded-md hover:opacity-90 transition-opacity\"\n >\n <Trash2 className=\"w-4 h-4\" aria-hidden=\"true\" />\n {tr('catalog.deleteCatalog', 'Delete Catalog')}\n </button>\n </Card>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n <CatalogForm\n isOpen={showEditForm}\n onClose={() => setShowEditForm(false)}\n onSubmit={handleUpdateCatalog}\n initialData={{\n name: catalog.name,\n description: catalog.description || '',\n visibility: catalog.visibility,\n owner: catalog.ownerName || '',\n tags: catalog.tags || [],\n }}\n title={tr('catalog.editCatalog', 'Edit Catalog')}\n />\n\n {/* Add Component Dialog */}\n {showAddComponent && (\n <AddComponentDialog\n catalogId={id || ''}\n existingComponentIds={components.map((c) => c.id)}\n onAdd={async (componentId) => {\n await addComponentToCatalog({ variables: { catalogId: id, componentId } });\n setSuccessMessage(tr('catalog.componentAdded', 'Component added to catalog'));\n setTimeout(() => setSuccessMessage(null), 3000);\n setShowAddComponent(false);\n }}\n onClose={() => setShowAddComponent(false)}\n tr={tr}\n />\n )}\n\n <ConfirmDialog\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDeleteCatalog}\n title={tr('catalog.deleteCatalog', 'Delete Catalog')}\n message={tr(\n 'catalog.deleteConfirmMessage',\n `Are you sure you want to delete \"${catalog.name}\"? This action cannot be undone.`\n )}\n confirmLabel={tr('common.delete', 'Delete')}\n variant=\"danger\"\n isLoading={isDeleting}\n />\n </MotionPage>\n );\n}\n\n/* ── Dependency Graph Tab ──────────────────────────────────────────────── */\n\ninterface DependencyGraphNode {\n id: string;\n name: string;\n type: string;\n version: string | null;\n}\ninterface DependencyGraphEdge {\n source: string;\n target: string;\n version: string | null;\n dependencyType: string | null;\n}\n\nfunction DependencyGraphTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const { data, loading, error, refetch } = useGetCatalogDependencyGraphQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n\n const graph: GetCatalogDependencyGraphQuery['vibecontrolsCatalogDependencyGraph'] | undefined =\n data?.vibecontrolsCatalogDependencyGraph;\n\n return (\n <motion.div\n key=\"dependency-graph\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Network className=\"w-5 h-5\" />\n {tr('catalog.dependencyGraph', 'Dependency Graph')}\n </h3>\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"p-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {loading && !graph && (\n <LoadingState message={tr('catalog.loadingGraph', 'Loading dependency graph...')} />\n )}\n\n {error && !graph && (\n <ErrorState\n title={tr('catalog.graphError', 'Failed to load graph')}\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {graph && graph.nodes.length === 0 && (\n <EmptyState\n icon={<Network className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noGraphNodes', 'No dependency data')}\n description={tr(\n 'catalog.noGraphNodesDesc',\n 'Add components with dependencies to this catalog to visualize the dependency graph'\n )}\n />\n )}\n\n {graph && graph.nodes.length > 0 && (\n <>\n <div className=\"grid grid-cols-2 gap-4\">\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.graphNodes', 'Components')}\n </p>\n <p className=\"text-2xl font-bold text-text-primary\">{graph.nodes.length}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.graphEdges', 'Dependencies')}\n </p>\n <p className=\"text-2xl font-bold text-text-primary\">{graph.edges.length}</p>\n </Card>\n </div>\n\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h4 className=\"font-medium text-text-primary\">\n {tr('catalog.graphNodesList', 'Components')}\n </h4>\n </div>\n <div className=\"divide-y divide-border-default\">\n {graph.nodes.map((node) => (\n <div key={node.id} className=\"p-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Package className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"font-medium text-text-primary text-sm\">{node.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {node.type}\n {node.version ? ` v${node.version}` : ''}\n </p>\n </div>\n </div>\n <span className=\"text-xs text-text-muted\">\n {graph.edges.filter((e) => e.source === node.id).length} deps\n </span>\n </div>\n ))}\n </div>\n </Card>\n\n {graph.edges.length > 0 && (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h4 className=\"font-medium text-text-primary\">\n {tr('catalog.graphEdgesList', 'Dependencies')}\n </h4>\n </div>\n <div className=\"divide-y divide-border-default\">\n {graph.edges.map((edge, idx) => (\n <div key={idx} className=\"p-3 flex items-center gap-3 text-sm\">\n <span className=\"font-medium text-text-primary truncate max-w-[120px]\">\n {graph.nodes.find((n) => n.id === edge.source)?.name || edge.source}\n </span>\n <ArrowRight className=\"w-4 h-4 text-text-muted flex-shrink-0\" />\n <span className=\"text-text-primary truncate max-w-[120px]\">{edge.target}</span>\n {edge.version && (\n <span className=\"text-xs text-text-muted ml-auto\">{edge.version}</span>\n )}\n {edge.dependencyType && (\n <span className=\"text-xs bg-bg-sunken text-text-secondary px-1.5 py-0.5 rounded\">\n {edge.dependencyType}\n </span>\n )}\n </div>\n ))}\n </div>\n </Card>\n )}\n </>\n )}\n </motion.div>\n );\n}\n\n/* ── SBOM Tab ──────────────────────────────────────────────────────────── */\n\ninterface SBOMEntry {\n name: string;\n version: string | null;\n type: string | null;\n license: string | null;\n purl: string | null;\n supplier: string | null;\n}\n\nfunction SBOMTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const { data, loading, error, refetch } = useGetCatalogSbomQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n\n const sbom: GetCatalogSbomQuery['vibecontrolsCatalogSBOM'] | undefined =\n data?.vibecontrolsCatalogSBOM;\n\n const handleDownloadJSON = () => {\n if (!sbom) return;\n const blob = new Blob([JSON.stringify(sbom, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `sbom-${catalogId}.json`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n return (\n <motion.div\n key=\"sbom\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Shield className=\"w-5 h-5\" />\n {tr('catalog.sbom', 'Software Bill of Materials')}\n </h3>\n <div className=\"flex items-center gap-2\">\n {sbom && sbom.components.length > 0 && (\n <button\n onClick={handleDownloadJSON}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken/80 transition-colors\"\n >\n <Download className=\"w-4 h-4\" />\n {tr('catalog.downloadSBOM', 'Download JSON')}\n </button>\n )}\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"p-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n </div>\n\n {loading && !sbom && (\n <LoadingState message={tr('catalog.loadingSBOM', 'Generating SBOM...')} />\n )}\n\n {error && !sbom && (\n <ErrorState\n title={tr('catalog.sbomError', 'Failed to load SBOM')}\n message={error.message}\n onRetry={() => refetch()}\n />\n )}\n\n {sbom && (\n <>\n <div className=\"grid grid-cols-3 gap-4\">\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">{tr('catalog.sbomFormat', 'Format')}</p>\n <p className=\"text-lg font-bold text-text-primary\">{sbom.format}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">{tr('catalog.sbomEntries', 'Entries')}</p>\n <p className=\"text-lg font-bold text-text-primary\">{sbom.totalCount}</p>\n </Card>\n <Card padding=\"md\">\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.sbomGenerated', 'Generated')}\n </p>\n <p className=\"text-lg font-bold text-text-primary\">\n {new Date(sbom.generatedAt).toLocaleTimeString()}\n </p>\n </Card>\n </div>\n\n {sbom.components.length > 0 ? (\n <Card>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-default\">\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomName', 'Name')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomVersion', 'Version')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomType', 'Type')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomLicense', 'License')}\n </th>\n <th className=\"text-left p-3 font-medium text-text-secondary\">\n {tr('catalog.sbomSupplier', 'Supplier')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-default\">\n {sbom.components.map((entry, idx) => (\n <tr key={idx} className=\"hover:bg-bg-sunken/50 transition-colors\">\n <td className=\"p-3 font-medium text-text-primary\">{entry.name}</td>\n <td className=\"p-3 text-text-secondary\">{entry.version || '-'}</td>\n <td className=\"p-3 text-text-secondary\">{entry.type || '-'}</td>\n <td className=\"p-3\">\n {entry.license ? (\n <span className=\"inline-flex px-2 py-0.5 text-xs bg-status-success-bg text-status-success-text rounded\">\n {entry.license}\n </span>\n ) : (\n '-'\n )}\n </td>\n <td className=\"p-3 text-text-secondary\">{entry.supplier || '-'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </Card>\n ) : (\n <EmptyState\n icon={<Shield className=\"w-8 h-8 text-text-secondary\" />}\n title={tr('catalog.noSBOMEntries', 'No SBOM entries')}\n description={tr(\n 'catalog.noSBOMEntriesDesc',\n 'Add components with SBOM data or dependencies to this catalog to generate an aggregated SBOM'\n )}\n />\n )}\n </>\n )}\n </motion.div>\n );\n}\n\n/* ── Add Component Dialog ──────────────────────────────────────────────── */\n\nfunction AddComponentDialog({\n catalogId,\n existingComponentIds,\n onAdd,\n onClose,\n tr,\n}: {\n catalogId: string;\n existingComponentIds: string[];\n onAdd: (componentId: string) => Promise<void>;\n onClose: () => void;\n tr: (key: string, fallback: string) => string;\n}) {\n const [adding, setAdding] = useState(false);\n const [search, setSearch] = useState('');\n\n const { data, loading } = useListAllComponentsForCatalogQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n type CatalogComponent =\n ListAllComponentsForCatalogQuery['vibecontrolsComponents']['items'][number];\n const available: Array<CatalogComponent> = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n const filtered = items.filter((c: CatalogComponent) => !existingComponentIds.includes(c.id));\n if (!search) return filtered;\n const q = search.toLowerCase();\n return filtered.filter(\n (c: CatalogComponent) =>\n c.name.toLowerCase().includes(q) || c.type.toString().toLowerCase().includes(q)\n );\n }, [data, existingComponentIds, search]);\n\n const handleAdd = async (componentId: string) => {\n setAdding(true);\n try {\n await onAdd(componentId);\n } finally {\n setAdding(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n className=\"bg-bg-surface rounded-lg w-full max-w-md max-h-[70vh] flex flex-col\"\n >\n <div className=\"p-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('catalog.addComponentTitle', 'Add Component to Catalog')}\n </h2>\n <div className=\"mt-3\">\n <input\n type=\"text\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={tr('catalog.searchComponents', 'Search components...')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder text-sm\"\n />\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2\">\n {loading && <LoadingState message={tr('common.loading', 'Loading...')} />}\n {!loading && available.length === 0 && (\n <div className=\"p-4 text-center text-text-secondary text-sm\">\n {tr('catalog.noAvailableComponents', 'No available components to add')}\n </div>\n )}\n {available.map((comp) => (\n <button\n key={comp.id}\n onClick={() => handleAdd(comp.id)}\n disabled={adding}\n className=\"w-full flex items-center justify-between p-3 rounded-md hover:bg-bg-sunken transition-colors disabled:opacity-50 text-left\"\n >\n <div>\n <p className=\"font-medium text-text-primary text-sm\">{comp.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {comp.type} &middot; {comp.lifecycle}\n </p>\n </div>\n <Plus className=\"w-4 h-4 text-text-secondary\" />\n </button>\n ))}\n </div>\n <div className=\"p-4 border-t border-border-default\">\n <button\n onClick={onClose}\n className=\"w-full px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n {tr('common.close', 'Close')}\n </button>\n </div>\n </motion.div>\n </div>\n );\n}\n"],"mappings":"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"}