@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/actions/ActionCard.js +2 -2
- package/dist/components/actions/ActionCard.js.map +1 -1
- package/dist/components/actions/ExecutionHistory.js +1 -1
- package/dist/components/actions/ExecutionHistory.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js +2 -2
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js +4 -4
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js +1 -1
- package/dist/components/agent-manager/AgentManagerLayout.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js +1 -1
- package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerSidebar.js +5 -5
- package/dist/components/agent-manager/AgentManagerSidebar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js +1 -1
- package/dist/components/agent-manager/AgentManagerTab.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerTabBar.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js +23 -23
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js +1 -1
- package/dist/components/agent-manager/PluginInstallModal.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js +5 -5
- package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionConfigPanel.js +6 -6
- package/dist/components/agent-manager/detail/SessionConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionLogsPanel.js +5 -5
- package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js +1 -1
- package/dist/components/agent-manager/detail/SessionToolsPanel.js.map +1 -1
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js +2 -2
- package/dist/components/agent-manager/dialogs/NewSessionDialog.js.map +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js +1 -1
- package/dist/components/agent-manager/dialogs/RenameSessionDialog.js.map +1 -1
- package/dist/components/agents/AgentCard.js +14 -11
- package/dist/components/agents/AgentCard.js.map +1 -1
- package/dist/components/ai/AIContextsTab.js +1 -1
- package/dist/components/ai/AIContextsTab.js.map +1 -1
- package/dist/components/ai/AIOverviewTab.js +4 -4
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPlanPanel.js +4 -4
- package/dist/components/ai/AIPlanPanel.js.map +1 -1
- package/dist/components/ai/AIPlaygroundTab.js +1 -1
- package/dist/components/ai/AIPlaygroundTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js +2 -2
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIProviderCredentialsCard.js +4 -4
- package/dist/components/ai/AIProviderCredentialsCard.js.map +1 -1
- package/dist/components/ai/AIQueueTab.js +4 -4
- package/dist/components/ai/AIQueueTab.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js +4 -4
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/ai/AITasksTab.js +1 -1
- package/dist/components/ai/AITasksTab.js.map +1 -1
- package/dist/components/ai/ClaudeCredentialsCard.js +3 -3
- package/dist/components/ai/ClaudeCredentialsCard.js.map +1 -1
- package/dist/components/ai/PluginHarnessPicker.js +3 -3
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/analytics/AnalyticsBreakdownChart.js +8 -8
- package/dist/components/analytics/AnalyticsBreakdownChart.js.map +1 -1
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js +5 -5
- package/dist/components/analytics/AnalyticsTimeSeriesChart.js.map +1 -1
- package/dist/components/audit/AuditDeleteDialog.js +2 -2
- package/dist/components/audit/AuditDeleteDialog.js.map +1 -1
- package/dist/components/audit/AuditExportButton.js +5 -5
- package/dist/components/audit/AuditExportButton.js.map +1 -1
- package/dist/components/audit/AuditLogDetail.js +9 -9
- package/dist/components/audit/AuditLogDetail.js.map +1 -1
- package/dist/components/audit/AuditLogFilters.js +1 -1
- package/dist/components/audit/AuditLogFilters.js.map +1 -1
- package/dist/components/audit/AuditLogStats.js +1 -1
- package/dist/components/audit/AuditLogStats.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js +28 -28
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/catalog/CatalogCard.js +3 -3
- package/dist/components/catalog/CatalogCard.js.map +1 -1
- package/dist/components/catalog/CatalogForm.js +1 -1
- package/dist/components/catalog/CatalogForm.js.map +1 -1
- package/dist/components/common/ActionAgentTargetNotice.js +6 -6
- package/dist/components/common/ActionAgentTargetNotice.js.map +1 -1
- package/dist/components/common/CommandEditor.js +1 -1
- package/dist/components/common/CommandEditor.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsPreviewPanel.js +1 -1
- package/dist/components/docs/DocsPreviewPanel.js.map +1 -1
- package/dist/components/docs/DocsRevisionHistory.js +4 -4
- package/dist/components/docs/DocsRevisionHistory.js.map +1 -1
- package/dist/components/docs/DocsSettingsPanel.js +7 -7
- package/dist/components/docs/DocsSettingsPanel.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +1 -1
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
- package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
- package/dist/components/graph/MeshDeployWizard.js +3 -3
- package/dist/components/graph/MeshDeployWizard.js.map +1 -1
- package/dist/components/graph/MeshRequestPanel.js +2 -2
- package/dist/components/graph/MeshRequestPanel.js.map +1 -1
- package/dist/components/idp-components/ComponentCard.js +5 -5
- package/dist/components/idp-components/ComponentCard.js.map +1 -1
- package/dist/components/idp-components/ComponentForm.js +1 -1
- package/dist/components/idp-components/ComponentForm.js.map +1 -1
- package/dist/components/idp-components/DependencyGraphTab.js +7 -7
- package/dist/components/idp-components/DependencyGraphTab.js.map +1 -1
- package/dist/components/logs/LogViewer.js +14 -14
- package/dist/components/logs/LogViewer.js.map +1 -1
- package/dist/components/notes/NoteCard.js +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +2 -2
- package/dist/components/notes/NoteEditor.js.map +1 -1
- package/dist/components/notes/NoteList.js +1 -1
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +1 -1
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js +1 -1
- package/dist/components/scorecard/ScorecardEmptyState.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js +3 -3
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/security/SecurityScanList.js +11 -11
- package/dist/components/security/SecurityScanList.js.map +1 -1
- package/dist/components/sessions/SessionCard.js +26 -19
- package/dist/components/sessions/SessionCard.js.map +1 -1
- package/dist/components/sessions/SessionStatusBadge.js +7 -7
- package/dist/components/sessions/SessionStatusBadge.js.map +1 -1
- package/dist/components/shared/AgentSelector.js +4 -4
- package/dist/components/shared/AgentSelector.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js +3 -3
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/Card.js +4 -4
- package/dist/components/shared/Card.js.map +1 -1
- package/dist/components/shared/ErrorState.js +15 -9
- package/dist/components/shared/ErrorState.js.map +1 -1
- package/dist/components/shared/FilterDropdown.js +2 -2
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/PageSkeleton.js +6 -6
- package/dist/components/shared/PageSkeleton.js.map +1 -1
- package/dist/components/shared/SearchInput.js +1 -1
- package/dist/components/shared/SearchInput.js.map +1 -1
- package/dist/components/shared/StatCard.js +7 -7
- package/dist/components/shared/StatCard.js.map +1 -1
- package/dist/components/templates/PublishTemplateForm.js +2 -2
- package/dist/components/templates/PublishTemplateForm.js.map +1 -1
- package/dist/components/templates/TemplateCard.js +5 -5
- package/dist/components/templates/TemplateCard.js.map +1 -1
- package/dist/components/templates/TemplateWizard.js +2 -2
- package/dist/components/templates/TemplateWizard.js.map +1 -1
- package/dist/components/tunnels/DomainVerificationBadge.js +4 -4
- package/dist/components/tunnels/DomainVerificationBadge.js.map +1 -1
- package/dist/components/tunnels/ProviderBadge.js +1 -1
- package/dist/components/tunnels/ProviderBadge.js.map +1 -1
- package/dist/components/tunnels/TunnelCard.js +16 -16
- package/dist/components/tunnels/TunnelCard.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js +1 -1
- package/dist/components/tunnels/TunnelPublicAccessPanel.js.map +1 -1
- package/dist/components/tunnels/TunnelStatusBadge.js +4 -4
- package/dist/components/tunnels/TunnelStatusBadge.js.map +1 -1
- package/dist/components/vibecalendar/LogsDrawer.js +2 -2
- package/dist/components/vibecalendar/LogsDrawer.js.map +1 -1
- package/dist/components/vibecalendar/ScheduleTaskModal.js +5 -5
- package/dist/components/vibecalendar/ScheduleTaskModal.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalAgendaView.js +2 -2
- package/dist/components/vibecalendar/VibeCalAgendaView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalDayView.js +3 -3
- package/dist/components/vibecalendar/VibeCalDayView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js +1 -1
- package/dist/components/vibecalendar/VibeCalMonthView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalWeekView.js +4 -4
- package/dist/components/vibecalendar/VibeCalWeekView.js.map +1 -1
- package/dist/components/vibecalendar/VibeCalendarHeader.js +4 -4
- package/dist/components/vibecalendar/VibeCalendarHeader.js.map +1 -1
- package/dist/components/vibecalendar/VibeTaskBlock.js +2 -2
- package/dist/components/vibecalendar/VibeTaskBlock.js.map +1 -1
- package/dist/components/vibecalendar/types.js +6 -6
- package/dist/components/vibecalendar/types.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js +9 -9
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonTile.js +6 -6
- package/dist/components/vibedeck/VibeDeckButtonTile.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js +10 -10
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js +8 -8
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +2 -2
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +2 -2
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckSwitcher.js +8 -8
- package/dist/components/vibedeck/VibeDeckSwitcher.js.map +1 -1
- package/dist/components/vibes/VibeCard.js +22 -18
- package/dist/components/vibes/VibeCard.js.map +1 -1
- package/dist/components/vibes/VibeList.js +16 -12
- package/dist/components/vibes/VibeList.js.map +1 -1
- package/dist/pages/ActionsPage.js +4 -3
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +9 -9
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsLandingPage.js +4 -4
- package/dist/pages/AgentsLandingPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +14 -14
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/AnalyticsPage.js +8 -8
- package/dist/pages/AnalyticsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +11 -11
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +12 -9
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +7 -7
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +12 -9
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/EnvTopologyPage.js +2 -2
- package/dist/pages/EnvTopologyPage.js.map +1 -1
- package/dist/pages/GettingStartedPage.js +12 -12
- package/dist/pages/GettingStartedPage.js.map +1 -1
- package/dist/pages/HealthPage.js +12 -12
- package/dist/pages/HealthPage.js.map +1 -1
- package/dist/pages/LogsPage.js +4 -4
- package/dist/pages/LogsPage.js.map +1 -1
- package/dist/pages/NoteDetailsPage.js +5 -5
- package/dist/pages/NoteDetailsPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +74 -41
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +6 -3
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/ScheduledJobDetailPage.js +7 -7
- package/dist/pages/ScheduledJobDetailPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +21 -21
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +20 -20
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SettingsPage.js +64 -52
- package/dist/pages/SettingsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js +21 -21
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/SharedVibeDeckPage.js +5 -5
- package/dist/pages/SharedVibeDeckPage.js.map +1 -1
- package/dist/pages/TargetDetailsPage.js +56 -65
- package/dist/pages/TargetDetailsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +4 -4
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +10 -10
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/TemplatesPage.js +6 -5
- package/dist/pages/TemplatesPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +3 -3
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeCalendarPage.js +3 -3
- package/dist/pages/VibeCalendarPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +3 -3
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +15 -15
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibeTaskDetailPage.js +5 -5
- package/dist/pages/VibeTaskDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js +18 -18
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +11 -11
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AIBookmarksPage.js +1 -1
- package/dist/pages/ai/AIBookmarksPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +6 -6
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/dist/pages/tunnels/AuditTab.js +1 -1
- package/dist/pages/tunnels/AuditTab.js.map +1 -1
- package/dist/pages/tunnels/DomainsTab.js +1 -1
- package/dist/pages/tunnels/DomainsTab.js.map +1 -1
- package/dist/pages/tunnels/ProvidersTab.js +1 -1
- package/dist/pages/tunnels/ProvidersTab.js.map +1 -1
- package/dist/pages/tunnels/SessionsTab.js +1 -1
- package/dist/pages/tunnels/SessionsTab.js.map +1 -1
- package/dist/pages/tunnels/TunnelDetailsPage.js +3 -3
- package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
- package/dist/pages/tunnels/UsageTab.js +1 -1
- package/dist/pages/tunnels/UsageTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js +16 -16
- package/dist/pages/tunnels/tabs/TunnelDomainsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +1 -1
- package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js +52 -49
- package/dist/pages/tunnels/tabs/TunnelSessionsTab.js.map +1 -1
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js +9 -9
- package/dist/pages/tunnels/tabs/TunnelSettingsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogDetailPage.js","names":[],"sources":["../../src/pages/CatalogDetailPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo, useEffect } from 'react';\nimport '@xyflow/react/dist/style.css';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\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 ArrowRight,\n Package,\n Trash2,\n Tag,\n Box,\n} from 'lucide-react';\nimport {\n ReactFlow,\n Background,\n Controls,\n Handle,\n Position,\n addEdge,\n useNodesState,\n useEdgesState,\n type Node,\n type Edge,\n type Connection,\n type NodeMouseHandler,\n MarkerType,\n BackgroundVariant,\n} from '@xyflow/react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\n\nimport { DocsTabPanel } from '@/components/docs';\nimport { ScorecardPanel } from '@/components/scorecard';\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 { EntityShareButton } from '@/components/sharing';\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 useListAllComponentsForCatalogQuery,\n useUpdateComponentMutation,\n type GetCatalogDependencyGraphQuery,\n type ListAllComponentsForCatalogQuery,\n} from '@/generated/wspace-operations';\n\ntype CatalogTab =\n | 'overview'\n | 'components'\n | 'dependency-graph'\n | 'scorecard'\n | 'docs'\n | 'settings';\n\nconst CATALOG_TABS: readonly CatalogTab[] = [\n 'overview',\n 'components',\n 'dependency-graph',\n 'scorecard',\n 'docs',\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 const canReadCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_READ);\n const canUpdateCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_UPDATE);\n const canDeleteCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_DELETE);\n const canReadComponents = hasPermission(VIBE_PERMISSIONS.COMPONENT_READ);\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 || permissionsLoading || !canReadCatalog,\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 (!canReadCatalog)\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 || !canUpdateCatalog) 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: 'scorecard', label: tr('catalog.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'docs', label: tr('catalog.docs', 'Docs'), icon: FileText },\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 type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <PageSkeleton variant=\"detail\" />\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 type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-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 type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <EmptyState\n icon={<BookOpen className=\"size-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 type=\"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 type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-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 <EntityShareButton\n entity=\"catalog\"\n resource={{ id: catalog.id, name: catalog.name }}\n label={tr('catalog.share', 'Share')}\n variant=\"button\"\n />\n <button\n type=\"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={`size-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=\"size-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.vibes', 'Vibes')}\n value={catalog.vibeCount ?? 0}\n icon={<Zap className=\"size-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.ownerName', 'Owner')}\n value={catalog.ownerNameName || 'Unassigned'}\n icon={<BookOpen className=\"size-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"size-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('catalog.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_CATALOG\" entityId={catalog.id} />\n </div>\n </Card>\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 type=\"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=\"size-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 {canUpdateCatalog && canReadComponents && (\n <button\n type=\"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=\"size-4\" />\n {tr('catalog.addComponent', 'Add Component')}\n </button>\n )}\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=\"size-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 === '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 className=\"space-y-6\"\n >\n <ScorecardPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\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 === '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 {canUpdateCatalog && (\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 type=\"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 )}\n {canDeleteCatalog && (\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 type=\"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=\"size-4\" aria-hidden=\"true\" />\n {tr('catalog.deleteCatalog', 'Delete Catalog')}\n </button>\n </Card>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n {canUpdateCatalog && (\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\n {/* Add Component Dialog */}\n {showAddComponent && canUpdateCatalog && canReadComponents && (\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\n// ---------------------------------------------------------------------------\n// Catalog Dependency Graph — React Flow canvas\n// ---------------------------------------------------------------------------\n\ninterface CatalogGraphNode {\n id: string;\n name: string;\n type: string;\n version?: string | null;\n}\ninterface CatalogGraphEdge {\n source: string;\n target: string;\n version?: string | null;\n dependencyType?: string | null;\n}\n\n// Custom node renders source/target handles for drag-to-connect\nfunction CatalogComponentNode({ data }: { data: { label: React.ReactNode } }) {\n return (\n <>\n <Handle\n type=\"target\"\n position={Position.Left}\n style={{ width: 8, height: 8, background: 'var(--color-border-strong)' }}\n />\n <div>{data.label}</div>\n <Handle\n type=\"source\"\n position={Position.Right}\n style={{ width: 8, height: 8, background: 'var(--color-action-primary-bg)' }}\n />\n </>\n );\n}\n\n// Must be defined outside component to avoid remount on every render\nconst catalogNodeTypes = { catalog: CatalogComponentNode };\n\nfunction buildCatalogFlowElements(\n nodes: CatalogGraphNode[],\n edges: CatalogGraphEdge[]\n): { nodes: Node[]; edges: Edge[] } {\n const COLS = Math.max(1, Math.ceil(Math.sqrt(nodes.length)));\n const COL_W = 220;\n const ROW_H = 120;\n\n // Map dep name → component ID for resolving cross-catalog edges\n const nameToId = new Map(nodes.map((n) => [n.name, n.id]));\n\n // Only edges where the target dep name resolves to a catalog component\n const internalEdges = edges.filter((e) => nameToId.has(e.target));\n\n const flowNodes: Node[] = nodes.map((n, i) => {\n const col = i % COLS;\n const row = Math.floor(i / COLS);\n const outDegree = internalEdges.filter((e) => e.source === n.id).length;\n return {\n id: n.id,\n type: 'catalog',\n position: { x: col * COL_W + 40, y: row * ROW_H + 40 },\n data: {\n label: (\n <div style={{ lineHeight: 1.4, textAlign: 'center' }}>\n <div\n style={{\n fontWeight: 700,\n fontSize: 12,\n maxWidth: 150,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {n.name}\n </div>\n <div\n style={{\n fontSize: 10,\n color: outDegree > 0 ? 'rgba(255,255,255,0.75)' : 'var(--color-text-muted)',\n marginTop: 2,\n }}\n >\n {n.type}\n {n.version ? ` v${n.version}` : ''}\n </div>\n {outDegree > 0 && (\n <div style={{ fontSize: 10, marginTop: 3, color: 'rgba(255,255,255,0.85)' }}>\n {outDegree} dep{outDegree !== 1 ? 's' : ''}\n </div>\n )}\n </div>\n ),\n },\n style: {\n background: outDegree > 0 ? 'var(--color-action-primary-bg)' : 'var(--color-bg-sunken)',\n color: outDegree > 0 ? '#fff' : 'var(--color-text-primary)',\n border: `1px solid ${outDegree > 0 ? 'transparent' : 'var(--color-border-default)'}`,\n borderRadius: 10,\n padding: '10px 14px',\n width: 180,\n fontSize: 12,\n boxShadow: outDegree > 0 ? '0 2px 8px rgba(0,0,0,0.15)' : 'none',\n textAlign: 'center' as const,\n cursor: 'pointer',\n },\n };\n });\n\n const flowEdges: Edge[] = internalEdges.map((e, i) => ({\n id: `e-${i}`,\n source: e.source,\n target: nameToId.get(e.target)!,\n label: e.version || undefined,\n animated: e.dependencyType !== 'dev',\n markerEnd: { type: MarkerType.ArrowClosed },\n style: {\n stroke: 'var(--color-border-strong)',\n strokeWidth: 1.5,\n ...(e.dependencyType === 'dev' ? { strokeDasharray: '4 2' } : {}),\n },\n labelStyle: { fontSize: 10, fill: 'var(--color-text-muted)' },\n labelBgStyle: { fill: 'var(--color-bg-surface)', fillOpacity: 0.85 },\n }));\n\n return { nodes: flowNodes, edges: flowEdges };\n}\n\ninterface CatalogFlowProps {\n graphNodes: CatalogGraphNode[];\n graphEdges: CatalogGraphEdge[];\n onComponentClick?: (nodeId: string) => void;\n onConnect?: (sourceId: string, targetId: string) => void;\n}\n\nfunction CatalogFlow({ graphNodes, graphEdges, onComponentClick, onConnect }: CatalogFlowProps) {\n const { nodes: initNodes, edges: initEdges } = useMemo(\n () => buildCatalogFlowElements(graphNodes, graphEdges),\n [graphNodes, graphEdges]\n );\n const [nodes, setNodes, onNodesChange] = useNodesState(initNodes);\n const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);\n\n useEffect(() => {\n const { nodes: n, edges: e } = buildCatalogFlowElements(graphNodes, graphEdges);\n setNodes(n);\n setEdges(e);\n }, [graphNodes, graphEdges, setNodes, setEdges]);\n\n const rows = Math.ceil(graphNodes.length / Math.max(1, Math.ceil(Math.sqrt(graphNodes.length))));\n const canvasHeight = Math.max(380, Math.min(rows * 140 + 120, 600));\n\n const handleNodeClick: NodeMouseHandler = useCallback(\n (_event, node) => {\n onComponentClick?.(node.id);\n },\n [onComponentClick]\n );\n\n const handleConnect = useCallback(\n (connection: Connection) => {\n if (!connection.source || !connection.target) return;\n setEdges((eds) =>\n addEdge(\n {\n ...connection,\n animated: true,\n markerEnd: { type: MarkerType.ArrowClosed },\n style: { stroke: 'var(--color-border-strong)', strokeWidth: 1.5 },\n },\n eds\n )\n );\n onConnect?.(connection.source, connection.target);\n },\n [onConnect, setEdges]\n );\n\n return (\n <div\n className=\"w-full rounded-lg border border-border-default overflow-hidden\"\n style={{ height: canvasHeight, background: 'var(--color-bg-secondary)' }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={handleNodeClick}\n onConnect={handleConnect}\n nodeTypes={catalogNodeTypes}\n fitView\n fitViewOptions={{ padding: 0.25 }}\n minZoom={0.15}\n maxZoom={3}\n panOnDrag\n panOnScroll={false}\n zoomOnScroll\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!fill-text-tertiary/10\"\n />\n <Controls\n showInteractive={false}\n style={{\n background: 'var(--color-bg-primary)',\n border: '1px solid var(--color-border-default)',\n borderRadius: 8,\n }}\n className=\"[&_button]:!bg-[var(--color-bg-primary)] [&_button]:!border-[var(--color-border-default)] [&_button]:!fill-[var(--color-text-secondary)] [&_button:hover]:!bg-[var(--color-bg-sunken)]\"\n />\n </ReactFlow>\n </div>\n );\n}\n\nfunction DependencyGraphTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const navigate = useVibeNavigate();\n const { data, loading, error, refetch } = useGetCatalogDependencyGraphQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n const [updateComponent] = useUpdateComponentMutation();\n\n const graph: GetCatalogDependencyGraphQuery['vibecontrolsCatalogDependencyGraph'] | undefined =\n data?.vibecontrolsCatalogDependencyGraph;\n\n const handleConnect = useCallback(\n async (sourceId: string, targetId: string) => {\n if (!graph) return;\n const targetNode = graph.nodes.find((n) => n.id === targetId);\n if (!targetNode) return;\n\n // Reconstruct source's full dep list from graph edges (service returns all, not just catalog-internal)\n const currentDeps = graph.edges\n .filter((e) => e.source === sourceId)\n .map((e) => ({\n name: e.target,\n version: e.version ?? null,\n type: e.dependencyType ?? null,\n }));\n\n // Skip if already connected\n if (currentDeps.some((d) => d.name === targetNode.name)) return;\n\n const newDeps = [\n ...currentDeps,\n { name: targetNode.name, version: targetNode.version ?? null, type: 'runtime' },\n ];\n\n try {\n await updateComponent({ variables: { id: sourceId, input: { dependencies: newDeps } } });\n await refetch();\n } catch {\n // Edge was added optimistically; refetch will restore server state\n await refetch();\n }\n },\n [graph, updateComponent, refetch]\n );\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=\"size-5\" />\n {tr('catalog.dependencyGraph', 'Dependency Graph')}\n </h3>\n <button\n type=\"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={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {loading && !graph && (\n <LoadingState message={tr('catalog.loadingGraph', 'Loading dependency graph...')} />\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=\"size-8 text-text-secondary\" />}\n title={tr('catalog.noGraphNodes', 'No components in catalog')}\n description={tr(\n 'catalog.noGraphNodesDesc',\n 'Add components to this catalog to visualize the dependency graph'\n )}\n />\n )}\n\n {graph && graph.nodes.length > 0 && (\n <>\n {/* Stats */}\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 {/* React Flow canvas */}\n <CatalogFlow\n graphNodes={graph.nodes}\n graphEdges={graph.edges}\n onComponentClick={(nodeId) => navigate(`/components/${nodeId}`)}\n onConnect={handleConnect}\n />\n\n {graph.edges.length === 0 && (\n <p className=\"text-xs text-text-muted text-center\">\n No inter-component dependencies yet. Drag from a node's right handle to another node\n to create a dependency.\n </p>\n )}\n\n {/* Edge table — only when edges exist */}\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', 'Dependency Edges')}\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=\"size-4 text-text-muted flex-shrink-0\" />\n <span className=\"text-text-primary truncate max-w-[120px]\">\n {graph.nodes.find((n) => n.id === edge.target)?.name || edge.target}\n </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/* ── 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 aria-label=\"control\"\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 type=\"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} · {comp.lifecycle}\n </p>\n </div>\n <Plus className=\"size-4 text-text-secondary\" />\n </button>\n ))}\n </div>\n <div className=\"p-4 border-t border-border-default\">\n <button\n type=\"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, useEffect } from 'react';\nimport '@xyflow/react/dist/style.css';\nimport { useParams } from 'react-router-dom';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\n\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 ArrowRight,\n Package,\n Trash2,\n Tag,\n Box,\n} from 'lucide-react';\nimport {\n ReactFlow,\n Background,\n Controls,\n Handle,\n Position,\n addEdge,\n useNodesState,\n useEdgesState,\n type Node,\n type Edge,\n type Connection,\n type NodeMouseHandler,\n MarkerType,\n BackgroundVariant,\n} from '@xyflow/react';\nimport {\n PageHeader,\n Card,\n StatCard,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n} from '@/components/shared';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\n\nimport { DocsTabPanel } from '@/components/docs';\nimport { ScorecardPanel } from '@/components/scorecard';\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 { EntityShareButton } from '@/components/sharing';\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 useListAllComponentsForCatalogQuery,\n useUpdateComponentMutation,\n type GetCatalogDependencyGraphQuery,\n type ListAllComponentsForCatalogQuery,\n} from '@/generated/wspace-operations';\n\ntype CatalogTab =\n | 'overview'\n | 'components'\n | 'dependency-graph'\n | 'scorecard'\n | 'docs'\n | 'settings';\n\nconst CATALOG_TABS: readonly CatalogTab[] = [\n 'overview',\n 'components',\n 'dependency-graph',\n 'scorecard',\n 'docs',\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 const canReadCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_READ);\n const canUpdateCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_UPDATE);\n const canDeleteCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_DELETE);\n const canReadComponents = hasPermission(VIBE_PERMISSIONS.COMPONENT_READ);\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 || permissionsLoading || !canReadCatalog,\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 (!canReadCatalog)\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-text-primary\">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 || !canUpdateCatalog) 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: 'scorecard', label: tr('catalog.scorecard', 'Scorecard'), icon: BarChart3 },\n { id: 'docs', label: tr('catalog.docs', 'Docs'), icon: FileText },\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 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !catalog) {\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-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 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('catalog.backToCatalogs', 'Back to Catalogs')}\n </button>\n <EmptyState\n icon={<BookOpen className=\"size-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 type=\"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 px-6 lg:px-8 py-6 bg-bg-canvas 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 type=\"button\"\n onClick={handleBack}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-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 <EntityShareButton\n entity=\"catalog\"\n resource={{ id: catalog.id, name: catalog.name }}\n label={tr('catalog.share', 'Share')}\n variant=\"button\"\n />\n <button\n type=\"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-action-secondary-bg-hover transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-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=\"size-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.vibes', 'Vibes')}\n value={catalog.vibeCount ?? 0}\n icon={<Zap className=\"size-4\" />}\n />\n </MotionItem>\n <MotionItem>\n <StatCard\n title={tr('catalog.ownerName', 'Owner')}\n value={catalog.ownerNameName || 'Unassigned'}\n icon={<BookOpen className=\"size-4\" />}\n />\n </MotionItem>\n </MotionList>\n\n {/* Universal tags */}\n <Card>\n <div className=\"p-4 border-b border-border-default flex items-center gap-2\">\n <Tag className=\"size-5\" />\n <h3 className=\"font-semibold text-text-primary\">{tr('catalog.tags', 'Tags')}</h3>\n </div>\n <div className=\"p-4\">\n <EntityTagPicker entityType=\"VIBECONTROLS_CATALOG\" entityId={catalog.id} />\n </div>\n </Card>\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 type=\"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=\"size-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-[var(--color-accent-soft)] 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 {canUpdateCatalog && canReadComponents && (\n <button\n type=\"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=\"size-4\" />\n {tr('catalog.addComponent', 'Add Component')}\n </button>\n )}\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=\"size-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 === '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 className=\"space-y-6\"\n >\n <ScorecardPanel ownerType=\"CATALOG\" ownerId={id || ''} />\n </motion.div>\n )}\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 === '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 {canUpdateCatalog && (\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 type=\"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 )}\n {canDeleteCatalog && (\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 type=\"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=\"size-4\" aria-hidden=\"true\" />\n {tr('catalog.deleteCatalog', 'Delete Catalog')}\n </button>\n </Card>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Edit Form */}\n {canUpdateCatalog && (\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\n {/* Add Component Dialog */}\n {showAddComponent && canUpdateCatalog && canReadComponents && (\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\n// ---------------------------------------------------------------------------\n// Catalog Dependency Graph — React Flow canvas\n// ---------------------------------------------------------------------------\n\ninterface CatalogGraphNode {\n id: string;\n name: string;\n type: string;\n version?: string | null;\n}\ninterface CatalogGraphEdge {\n source: string;\n target: string;\n version?: string | null;\n dependencyType?: string | null;\n}\n\n// Custom node renders source/target handles for drag-to-connect\nfunction CatalogComponentNode({ data }: { data: { label: React.ReactNode } }) {\n return (\n <>\n <Handle\n type=\"target\"\n position={Position.Left}\n style={{ width: 8, height: 8, background: 'var(--color-border-strong)' }}\n />\n <div>{data.label}</div>\n <Handle\n type=\"source\"\n position={Position.Right}\n style={{ width: 8, height: 8, background: 'var(--color-action-primary-bg)' }}\n />\n </>\n );\n}\n\n// Must be defined outside component to avoid remount on every render\nconst catalogNodeTypes = { catalog: CatalogComponentNode };\n\nfunction buildCatalogFlowElements(\n nodes: CatalogGraphNode[],\n edges: CatalogGraphEdge[]\n): { nodes: Node[]; edges: Edge[] } {\n const COLS = Math.max(1, Math.ceil(Math.sqrt(nodes.length)));\n const COL_W = 220;\n const ROW_H = 120;\n\n // Map dep name → component ID for resolving cross-catalog edges\n const nameToId = new Map(nodes.map((n) => [n.name, n.id]));\n\n // Only edges where the target dep name resolves to a catalog component\n const internalEdges = edges.filter((e) => nameToId.has(e.target));\n\n const flowNodes: Node[] = nodes.map((n, i) => {\n const col = i % COLS;\n const row = Math.floor(i / COLS);\n const outDegree = internalEdges.filter((e) => e.source === n.id).length;\n return {\n id: n.id,\n type: 'catalog',\n position: { x: col * COL_W + 40, y: row * ROW_H + 40 },\n data: {\n label: (\n <div style={{ lineHeight: 1.4, textAlign: 'center' }}>\n <div\n style={{\n fontWeight: 700,\n fontSize: 12,\n maxWidth: 150,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {n.name}\n </div>\n <div\n style={{\n fontSize: 10,\n color:\n outDegree > 0 ? 'var(--color-action-primary-text)' : 'var(--color-text-muted)',\n marginTop: 2,\n }}\n >\n {n.type}\n {n.version ? ` v${n.version}` : ''}\n </div>\n {outDegree > 0 && (\n <div\n style={{ fontSize: 10, marginTop: 3, color: 'var(--color-action-primary-text)' }}\n >\n {outDegree} dep{outDegree !== 1 ? 's' : ''}\n </div>\n )}\n </div>\n ),\n },\n style: {\n background: outDegree > 0 ? 'var(--color-action-primary-bg)' : 'var(--color-bg-sunken)',\n color: outDegree > 0 ? 'var(--color-action-primary-text)' : 'var(--color-text-primary)',\n border: `1px solid ${outDegree > 0 ? 'transparent' : 'var(--color-border-default)'}`,\n borderRadius: 10,\n padding: '10px 14px',\n width: 180,\n fontSize: 12,\n boxShadow: outDegree > 0 ? 'var(--shadow-elevation-2)' : 'none',\n textAlign: 'center' as const,\n cursor: 'pointer',\n },\n };\n });\n\n const flowEdges: Edge[] = internalEdges.map((e, i) => ({\n id: `e-${i}`,\n source: e.source,\n target: nameToId.get(e.target)!,\n label: e.version || undefined,\n animated: e.dependencyType !== 'dev',\n markerEnd: { type: MarkerType.ArrowClosed },\n style: {\n stroke: 'var(--color-border-strong)',\n strokeWidth: 1.5,\n ...(e.dependencyType === 'dev' ? { strokeDasharray: '4 2' } : {}),\n },\n labelStyle: { fontSize: 10, fill: 'var(--color-text-muted)' },\n labelBgStyle: { fill: 'var(--color-bg-surface)', fillOpacity: 0.85 },\n }));\n\n return { nodes: flowNodes, edges: flowEdges };\n}\n\ninterface CatalogFlowProps {\n graphNodes: CatalogGraphNode[];\n graphEdges: CatalogGraphEdge[];\n onComponentClick?: (nodeId: string) => void;\n onConnect?: (sourceId: string, targetId: string) => void;\n}\n\nfunction CatalogFlow({ graphNodes, graphEdges, onComponentClick, onConnect }: CatalogFlowProps) {\n const { nodes: initNodes, edges: initEdges } = useMemo(\n () => buildCatalogFlowElements(graphNodes, graphEdges),\n [graphNodes, graphEdges]\n );\n const [nodes, setNodes, onNodesChange] = useNodesState(initNodes);\n const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);\n\n useEffect(() => {\n const { nodes: n, edges: e } = buildCatalogFlowElements(graphNodes, graphEdges);\n setNodes(n);\n setEdges(e);\n }, [graphNodes, graphEdges, setNodes, setEdges]);\n\n const rows = Math.ceil(graphNodes.length / Math.max(1, Math.ceil(Math.sqrt(graphNodes.length))));\n const canvasHeight = Math.max(380, Math.min(rows * 140 + 120, 600));\n\n const handleNodeClick: NodeMouseHandler = useCallback(\n (_event, node) => {\n onComponentClick?.(node.id);\n },\n [onComponentClick]\n );\n\n const handleConnect = useCallback(\n (connection: Connection) => {\n if (!connection.source || !connection.target) return;\n setEdges((eds) =>\n addEdge(\n {\n ...connection,\n animated: true,\n markerEnd: { type: MarkerType.ArrowClosed },\n style: { stroke: 'var(--color-border-strong)', strokeWidth: 1.5 },\n },\n eds\n )\n );\n onConnect?.(connection.source, connection.target);\n },\n [onConnect, setEdges]\n );\n\n return (\n <div\n className=\"w-full rounded-lg border border-border-default overflow-hidden\"\n style={{ height: canvasHeight, background: 'var(--color-bg-secondary)' }}\n >\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={handleNodeClick}\n onConnect={handleConnect}\n nodeTypes={catalogNodeTypes}\n fitView\n fitViewOptions={{ padding: 0.25 }}\n minZoom={0.15}\n maxZoom={3}\n panOnDrag\n panOnScroll={false}\n zoomOnScroll\n proOptions={{ hideAttribution: true }}\n >\n <Background\n variant={BackgroundVariant.Dots}\n gap={20}\n size={1}\n className=\"!fill-text-tertiary/10\"\n />\n <Controls\n showInteractive={false}\n style={{\n background: 'var(--color-bg-primary)',\n border: '1px solid var(--color-border-default)',\n borderRadius: 8,\n }}\n className=\"[&_button]:!bg-[var(--color-bg-primary)] [&_button]:!border-[var(--color-border-default)] [&_button]:!fill-[var(--color-text-secondary)] [&_button:hover]:!bg-[var(--color-bg-sunken)]\"\n />\n </ReactFlow>\n </div>\n );\n}\n\nfunction DependencyGraphTab({\n catalogId,\n tr,\n}: {\n catalogId: string;\n tr: (key: string, fallback: string) => string;\n}) {\n const navigate = useVibeNavigate();\n const { data, loading, error, refetch } = useGetCatalogDependencyGraphQuery({\n variables: { catalogId },\n skip: !catalogId,\n fetchPolicy: 'cache-and-network',\n });\n const [updateComponent] = useUpdateComponentMutation();\n\n const graph: GetCatalogDependencyGraphQuery['vibecontrolsCatalogDependencyGraph'] | undefined =\n data?.vibecontrolsCatalogDependencyGraph;\n\n const handleConnect = useCallback(\n async (sourceId: string, targetId: string) => {\n if (!graph) return;\n const targetNode = graph.nodes.find((n) => n.id === targetId);\n if (!targetNode) return;\n\n // Reconstruct source's full dep list from graph edges (service returns all, not just catalog-internal)\n const currentDeps = graph.edges\n .filter((e) => e.source === sourceId)\n .map((e) => ({\n name: e.target,\n version: e.version ?? null,\n type: e.dependencyType ?? null,\n }));\n\n // Skip if already connected\n if (currentDeps.some((d) => d.name === targetNode.name)) return;\n\n const newDeps = [\n ...currentDeps,\n { name: targetNode.name, version: targetNode.version ?? null, type: 'runtime' },\n ];\n\n try {\n await updateComponent({ variables: { id: sourceId, input: { dependencies: newDeps } } });\n await refetch();\n } catch {\n // Edge was added optimistically; refetch will restore server state\n await refetch();\n }\n },\n [graph, updateComponent, refetch]\n );\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=\"size-5\" />\n {tr('catalog.dependencyGraph', 'Dependency Graph')}\n </h3>\n <button\n type=\"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={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n\n {loading && !graph && (\n <LoadingState message={tr('catalog.loadingGraph', 'Loading dependency graph...')} />\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=\"size-8 text-text-secondary\" />}\n title={tr('catalog.noGraphNodes', 'No components in catalog')}\n description={tr(\n 'catalog.noGraphNodesDesc',\n 'Add components to this catalog to visualize the dependency graph'\n )}\n />\n )}\n\n {graph && graph.nodes.length > 0 && (\n <>\n {/* Stats */}\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 {/* React Flow canvas */}\n <CatalogFlow\n graphNodes={graph.nodes}\n graphEdges={graph.edges}\n onComponentClick={(nodeId) => navigate(`/components/${nodeId}`)}\n onConnect={handleConnect}\n />\n\n {graph.edges.length === 0 && (\n <p className=\"text-xs text-text-muted text-center\">\n No inter-component dependencies yet. Drag from a node's right handle to another node\n to create a dependency.\n </p>\n )}\n\n {/* Edge table — only when edges exist */}\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', 'Dependency Edges')}\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=\"size-4 text-text-muted flex-shrink-0\" />\n <span className=\"text-text-primary truncate max-w-[120px]\">\n {graph.nodes.find((n) => n.id === edge.target)?.name || edge.target}\n </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/* ── 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 aria-label=\"control\"\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 type=\"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} · {comp.lifecycle}\n </p>\n </div>\n <Plus className=\"size-4 text-text-secondary\" />\n </button>\n ))}\n </div>\n <div className=\"p-4 border-t border-border-default\">\n <button\n type=\"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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"}
|
|
@@ -118,7 +118,7 @@ function S() {
|
|
|
118
118
|
children: /* @__PURE__ */ v("div", {
|
|
119
119
|
className: "text-center space-y-2",
|
|
120
120
|
children: [/* @__PURE__ */ _("h2", {
|
|
121
|
-
className: "text-lg font-semibold text-
|
|
121
|
+
className: "text-lg font-semibold text-text-primary tracking-tight",
|
|
122
122
|
children: "Access Denied"
|
|
123
123
|
}), /* @__PURE__ */ _("p", {
|
|
124
124
|
className: "text-sm text-text-secondary",
|
|
@@ -130,14 +130,14 @@ function S() {
|
|
|
130
130
|
S(`/catalogs/${e.id}`);
|
|
131
131
|
};
|
|
132
132
|
return G && $.length === 0 ? /* @__PURE__ */ _(x, {
|
|
133
|
-
className: "space-y-6
|
|
133
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
134
134
|
children: /* @__PURE__ */ _(te, {
|
|
135
135
|
title: O("catalog.failedLoad", "Failed to load catalogs"),
|
|
136
136
|
message: G.message,
|
|
137
137
|
onRetry: () => K()
|
|
138
138
|
})
|
|
139
139
|
}) : /* @__PURE__ */ v(x, {
|
|
140
|
-
className: "space-y-6
|
|
140
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
141
141
|
children: [
|
|
142
142
|
/* @__PURE__ */ _(ee, {
|
|
143
143
|
title: O("catalog.title", "Catalogs"),
|
|
@@ -148,13 +148,13 @@ function S() {
|
|
|
148
148
|
type: "button",
|
|
149
149
|
onClick: () => K(),
|
|
150
150
|
disabled: W,
|
|
151
|
-
className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-
|
|
151
|
+
className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
152
152
|
title: O("common.refresh", "Refresh"),
|
|
153
153
|
children: /* @__PURE__ */ _(oe, { className: `size-4 ${W ? "animate-spin" : ""}` })
|
|
154
154
|
}), E && /* @__PURE__ */ v("button", {
|
|
155
155
|
type: "button",
|
|
156
156
|
onClick: () => P(!0),
|
|
157
|
-
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-
|
|
157
|
+
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
158
158
|
children: [/* @__PURE__ */ _(g, { className: "size-4" }), O("catalog.createCatalog", "Create Catalog")]
|
|
159
159
|
})]
|
|
160
160
|
})
|
|
@@ -205,7 +205,10 @@ function S() {
|
|
|
205
205
|
}) }, e.id))
|
|
206
206
|
}, "catalog-grid")
|
|
207
207
|
}) : /* @__PURE__ */ _(re, {
|
|
208
|
-
icon: /* @__PURE__ */ _(h, {
|
|
208
|
+
icon: /* @__PURE__ */ _(h, {
|
|
209
|
+
className: "size-6 text-action-primary-bg",
|
|
210
|
+
"aria-hidden": "true"
|
|
211
|
+
}),
|
|
209
212
|
title: k || j !== "all" ? O("catalog.emptyNoResults", "No catalogs found") : O("catalog.emptyNoCatalogs", "No catalogs yet"),
|
|
210
213
|
description: k || j !== "all" ? O("catalog.emptyAdjustSearch", "Try adjusting your search or filters") : O("catalog.emptyCreate", "Create your first catalog to organize components"),
|
|
211
214
|
action: !k && j === "all" ? /* @__PURE__ */ v("div", {
|
|
@@ -213,7 +216,7 @@ function S() {
|
|
|
213
216
|
children: [E && /* @__PURE__ */ v("button", {
|
|
214
217
|
type: "button",
|
|
215
218
|
onClick: () => P(!0),
|
|
216
|
-
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-
|
|
219
|
+
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
217
220
|
children: [/* @__PURE__ */ _(g, { className: "size-4" }), O("catalog.createCatalog", "Create Catalog")]
|
|
218
221
|
}), /* @__PURE__ */ _(c, { prompt: "What is a Catalog in VibeControls and how do I create one?" })]
|
|
219
222
|
}) : void 0
|
|
@@ -224,12 +227,12 @@ function S() {
|
|
|
224
227
|
type: "button",
|
|
225
228
|
onClick: fe,
|
|
226
229
|
disabled: V,
|
|
227
|
-
className: "flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-
|
|
230
|
+
className: "flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-button border border-border-subtle hover:bg-bg-surface hover:border-border-strong transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
228
231
|
children: V ? O("common.loading", "Loading...") : O("common.loadMore", "Load more")
|
|
229
232
|
})
|
|
230
233
|
}),
|
|
231
234
|
L && /* @__PURE__ */ _("div", {
|
|
232
|
-
className: "fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-
|
|
235
|
+
className: "fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg-subtle text-status-success-text border border-status-success-border rounded-card shadow-elevation-3 text-sm",
|
|
233
236
|
children: L
|
|
234
237
|
}),
|
|
235
238
|
/* @__PURE__ */ _(n, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogPage.js","names":[],"sources":["../../src/pages/CatalogPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, BookOpen, RefreshCw } from 'lucide-react';\nimport { CatalogCard } from '@/components/catalog';\nimport type { CatalogCardData } from '@/components/catalog';\nimport { CatalogForm } from '@/components/catalog';\nimport type { CatalogFormData } from '@/components/catalog';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ErrorState,\n GetHelpButton,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListCatalogsQuery, useCreateCatalogMutation } from '@/generated/wspace-operations';\n\nexport function CatalogPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canCreateCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_CREATE);\n const canReadCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_READ);\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const visibilityOptions = [\n { value: 'all', label: tr('catalog.allVisibility', 'All Visibility') },\n { value: 'PRIVATE', label: tr('catalog.visibilityPrivate', 'Private') },\n { value: 'INTERNAL', label: tr('catalog.visibilityInternal', 'Internal') },\n { value: 'PUBLIC', label: tr('catalog.visibilityPublic', 'Public') },\n ];\n\n const PAGE_SIZE = 50;\n const [searchQuery, setSearchQuery] = useState('');\n const [visibilityFilter, setVisibilityFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [accumulated, setAccumulated] = useState<CatalogCardData[]>([]);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, refetch, fetchMore } = useListCatalogsQuery({\n variables: {\n filter: {\n visibility:\n visibilityFilter !== 'all'\n ? (visibilityFilter as 'PRIVATE' | 'INTERNAL' | 'PUBLIC')\n : undefined,\n search: searchQuery || undefined,\n },\n pagination: { limit: PAGE_SIZE, offset: 0 },\n },\n skip: permissionsLoading || !canReadCatalog,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const [createCatalog] = useCreateCatalogMutation({ refetchQueries: ['ListCatalogs'] });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_CATALOG');\n\n const mapItems = useCallback(\n (items: ReadonlyArray<Record<string, unknown>>): CatalogCardData[] =>\n items.map((item) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n visibility: item.visibility as CatalogCardData['visibility'],\n componentCount: (item.componentCount as number) || 0,\n vibeCount: (item.vibeCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n })),\n []\n );\n\n // Mirror the watched query's items into local state. fetchMore below uses\n // updateQuery to append new pages into the watched data, so this single\n // effect handles both the initial load and load-more cases.\n useEffect(() => {\n const items = data?.vibecontrolsCatalogs?.items ?? [];\n setAccumulated(mapItems(items as ReadonlyArray<Record<string, unknown>>));\n }, [data?.vibecontrolsCatalogs?.items, mapItems]);\n\n const total = data?.vibecontrolsCatalogs?.total ?? 0;\n const hasMore = accumulated.length < total;\n const filteredCatalogs = accumulated;\n const catalogs = accumulated;\n\n const handleLoadMore = useCallback(async () => {\n if (loadingMore || !hasMore) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { limit: PAGE_SIZE, offset: accumulated.length } },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult?.vibecontrolsCatalogs) return prev;\n const prevList = prev.vibecontrolsCatalogs;\n const nextList = fetchMoreResult.vibecontrolsCatalogs;\n return {\n ...prev,\n vibecontrolsCatalogs: {\n ...nextList,\n items: [...(prevList?.items ?? []), ...nextList.items],\n total: nextList.total,\n hasMore: nextList.hasMore,\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n }, [fetchMore, accumulated.length, loadingMore, hasMore]);\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 (!canReadCatalog)\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 handleCatalogSelect = (catalog: CatalogCardData) => {\n navigate(`/catalogs/${catalog.id}`);\n };\n\n const handleCreateCatalog = async (formData: CatalogFormData) => {\n try {\n const res = await createCatalog({\n variables: {\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 if (res.error && isQuotaExhaustedError(res.error)) {\n setShowCreateForm(false);\n setShowQuotaExhausted(true);\n return;\n }\n const newCatalogId = res.data?.createVibecontrolsCatalog?.id;\n if (!newCatalogId) throw new Error(tr('catalog.failedCreate', 'Failed to create catalog'));\n await flushTags(newCatalogId);\n setShowCreateForm(false);\n setSuccessMessage(tr('catalog.createdSuccess', 'Catalog created successfully'));\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n if (error && catalogs.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('catalog.failedLoad', 'Failed to load catalogs')}\n message={error.message}\n onRetry={() => refetch()}\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 <PageHeader\n title={tr('catalog.title', 'Catalogs')}\n description={tr('catalog.description', '{{total}} catalogs', {\n total: catalogs.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-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={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n {canCreateCatalog && (\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n )}\n </div>\n }\n />\n\n {/* Filters — SearchInput / FilterDropdown are custom components that don't\n forward rest props onto a DOM node, so the tour anchors live on\n wrapper <div>s. */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <div data-tour=\"vibecontrols-catalog-search\" className=\"flex-1 sm:w-64 sm:flex-none\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('catalog.searchPlaceholder', 'Search catalogs...')}\n className=\"w-full\"\n />\n </div>\n <div data-tour=\"vibecontrols-catalog-visibility-filter\">\n <FilterDropdown\n label={tr('catalog.filterVisibility', 'Visibility')}\n value={visibilityFilter}\n options={visibilityOptions}\n onChange={setVisibilityFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.showingResults', 'Showing {{filtered}} of {{total}} catalogs', {\n filtered: filteredCatalogs.length,\n total,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">{tr('common.refreshing', '(refreshing...)')}</span>\n )}\n </p>\n\n {/* Grid */}\n {loading && catalogs.length === 0 ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredCatalogs.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"catalog-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n data-tour=\"vibecontrols-catalog-grid\"\n >\n {filteredCatalogs.map((catalog) => (\n <MotionItem key={catalog.id}>\n <CatalogCard catalog={catalog} onOpen={handleCatalogSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<BookOpen className=\"size-8 text-text-secondary\" />}\n title={\n searchQuery || visibilityFilter !== 'all'\n ? tr('catalog.emptyNoResults', 'No catalogs found')\n : tr('catalog.emptyNoCatalogs', 'No catalogs yet')\n }\n description={\n searchQuery || visibilityFilter !== 'all'\n ? tr('catalog.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr('catalog.emptyCreate', 'Create your first catalog to organize components')\n }\n action={\n !searchQuery && visibilityFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n {canCreateCatalog && (\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n )}\n <GetHelpButton prompt=\"What is a Catalog in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Load more */}\n {hasMore && filteredCatalogs.length > 0 && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-sunken-hover transition-colors disabled:opacity-50\"\n >\n {loadingMore ? tr('common.loading', 'Loading...') : tr('common.loadMore', 'Load more')}\n </button>\n </div>\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Catalog Quota Reached\"\n message=\"You've reached the maximum number of catalogs on your current plan. Upgrade your plan or purchase an add-on to create more catalogs.\"\n />\n\n {/* Create Form */}\n <CatalogForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateCatalog}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('catalog.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_CATALOG\" onDraftChange={setDraftTagIds} />\n </div>\n }\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CatalogPage.js","names":[],"sources":["../../src/pages/CatalogPage.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, BookOpen, RefreshCw } from 'lucide-react';\nimport { CatalogCard } from '@/components/catalog';\nimport type { CatalogCardData } from '@/components/catalog';\nimport { CatalogForm } from '@/components/catalog';\nimport type { CatalogFormData } from '@/components/catalog';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n ErrorState,\n GetHelpButton,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListCatalogsQuery, useCreateCatalogMutation } from '@/generated/wspace-operations';\n\nexport function CatalogPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canCreateCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_CREATE);\n const canReadCatalog = hasPermission(VIBE_PERMISSIONS.CATALOG_READ);\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const visibilityOptions = [\n { value: 'all', label: tr('catalog.allVisibility', 'All Visibility') },\n { value: 'PRIVATE', label: tr('catalog.visibilityPrivate', 'Private') },\n { value: 'INTERNAL', label: tr('catalog.visibilityInternal', 'Internal') },\n { value: 'PUBLIC', label: tr('catalog.visibilityPublic', 'Public') },\n ];\n\n const PAGE_SIZE = 50;\n const [searchQuery, setSearchQuery] = useState('');\n const [visibilityFilter, setVisibilityFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [accumulated, setAccumulated] = useState<CatalogCardData[]>([]);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const { data, loading, error, refetch, fetchMore } = useListCatalogsQuery({\n variables: {\n filter: {\n visibility:\n visibilityFilter !== 'all'\n ? (visibilityFilter as 'PRIVATE' | 'INTERNAL' | 'PUBLIC')\n : undefined,\n search: searchQuery || undefined,\n },\n pagination: { limit: PAGE_SIZE, offset: 0 },\n },\n skip: permissionsLoading || !canReadCatalog,\n fetchPolicy: 'cache-and-network',\n notifyOnNetworkStatusChange: true,\n });\n\n const [createCatalog] = useCreateCatalogMutation({ refetchQueries: ['ListCatalogs'] });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_CATALOG');\n\n const mapItems = useCallback(\n (items: ReadonlyArray<Record<string, unknown>>): CatalogCardData[] =>\n items.map((item) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n visibility: item.visibility as CatalogCardData['visibility'],\n componentCount: (item.componentCount as number) || 0,\n vibeCount: (item.vibeCount as number) || 0,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n })),\n []\n );\n\n // Mirror the watched query's items into local state. fetchMore below uses\n // updateQuery to append new pages into the watched data, so this single\n // effect handles both the initial load and load-more cases.\n useEffect(() => {\n const items = data?.vibecontrolsCatalogs?.items ?? [];\n setAccumulated(mapItems(items as ReadonlyArray<Record<string, unknown>>));\n }, [data?.vibecontrolsCatalogs?.items, mapItems]);\n\n const total = data?.vibecontrolsCatalogs?.total ?? 0;\n const hasMore = accumulated.length < total;\n const filteredCatalogs = accumulated;\n const catalogs = accumulated;\n\n const handleLoadMore = useCallback(async () => {\n if (loadingMore || !hasMore) return;\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { pagination: { limit: PAGE_SIZE, offset: accumulated.length } },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult?.vibecontrolsCatalogs) return prev;\n const prevList = prev.vibecontrolsCatalogs;\n const nextList = fetchMoreResult.vibecontrolsCatalogs;\n return {\n ...prev,\n vibecontrolsCatalogs: {\n ...nextList,\n items: [...(prevList?.items ?? []), ...nextList.items],\n total: nextList.total,\n hasMore: nextList.hasMore,\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n }, [fetchMore, accumulated.length, loadingMore, hasMore]);\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 (!canReadCatalog)\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-text-primary tracking-tight\">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 handleCatalogSelect = (catalog: CatalogCardData) => {\n navigate(`/catalogs/${catalog.id}`);\n };\n\n const handleCreateCatalog = async (formData: CatalogFormData) => {\n try {\n const res = await createCatalog({\n variables: {\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 if (res.error && isQuotaExhaustedError(res.error)) {\n setShowCreateForm(false);\n setShowQuotaExhausted(true);\n return;\n }\n const newCatalogId = res.data?.createVibecontrolsCatalog?.id;\n if (!newCatalogId) throw new Error(tr('catalog.failedCreate', 'Failed to create catalog'));\n await flushTags(newCatalogId);\n setShowCreateForm(false);\n setSuccessMessage(tr('catalog.createdSuccess', 'Catalog created successfully'));\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setShowQuotaExhausted(true);\n return;\n }\n throw err;\n }\n };\n\n if (error && catalogs.length === 0) {\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <ErrorState\n title={tr('catalog.failedLoad', 'Failed to load catalogs')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen\">\n <PageHeader\n title={tr('catalog.title', 'Catalogs')}\n description={tr('catalog.description', '{{total}} catalogs', {\n total: catalogs.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n {canCreateCatalog && (\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Plus className=\"size-4\" />\n {tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n )}\n </div>\n }\n />\n\n {/* Filters — SearchInput / FilterDropdown are custom components that don't\n forward rest props onto a DOM node, so the tour anchors live on\n wrapper <div>s. */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <div data-tour=\"vibecontrols-catalog-search\" className=\"flex-1 sm:w-64 sm:flex-none\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('catalog.searchPlaceholder', 'Search catalogs...')}\n className=\"w-full\"\n />\n </div>\n <div data-tour=\"vibecontrols-catalog-visibility-filter\">\n <FilterDropdown\n label={tr('catalog.filterVisibility', 'Visibility')}\n value={visibilityFilter}\n options={visibilityOptions}\n onChange={setVisibilityFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {tr('catalog.showingResults', 'Showing {{filtered}} of {{total}} catalogs', {\n filtered: filteredCatalogs.length,\n total,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">{tr('common.refreshing', '(refreshing...)')}</span>\n )}\n </p>\n\n {/* Grid */}\n {loading && catalogs.length === 0 ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredCatalogs.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"catalog-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n data-tour=\"vibecontrols-catalog-grid\"\n >\n {filteredCatalogs.map((catalog) => (\n <MotionItem key={catalog.id}>\n <CatalogCard catalog={catalog} onOpen={handleCatalogSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<BookOpen className=\"size-6 text-action-primary-bg\" aria-hidden=\"true\" />}\n title={\n searchQuery || visibilityFilter !== 'all'\n ? tr('catalog.emptyNoResults', 'No catalogs found')\n : tr('catalog.emptyNoCatalogs', 'No catalogs yet')\n }\n description={\n searchQuery || visibilityFilter !== 'all'\n ? tr('catalog.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr('catalog.emptyCreate', 'Create your first catalog to organize components')\n }\n action={\n !searchQuery && visibilityFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n {canCreateCatalog && (\n <button\n type=\"button\"\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <Plus className=\"size-4\" />\n {tr('catalog.createCatalog', 'Create Catalog')}\n </button>\n )}\n <GetHelpButton prompt=\"What is a Catalog in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Load more */}\n {hasMore && filteredCatalogs.length > 0 && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"flex items-center gap-2 px-4 py-2 bg-bg-sunken text-text-primary rounded-button border border-border-subtle hover:bg-bg-surface hover:border-border-strong transition-colors disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {loadingMore ? tr('common.loading', 'Loading...') : tr('common.loadMore', 'Load more')}\n </button>\n </div>\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg-subtle text-status-success-text border border-status-success-border rounded-card shadow-elevation-3 text-sm\">\n {successMessage}\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Catalog Quota Reached\"\n message=\"You've reached the maximum number of catalogs on your current plan. Upgrade your plan or purchase an add-on to create more catalogs.\"\n />\n\n {/* Create Form */}\n <CatalogForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateCatalog}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('catalog.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_CATALOG\" onDraftChange={setDraftTagIds} />\n </div>\n }\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -85,7 +85,7 @@ function T() {
|
|
|
85
85
|
children: /* @__PURE__ */ x("div", {
|
|
86
86
|
className: "text-center space-y-2",
|
|
87
87
|
children: [/* @__PURE__ */ b("h2", {
|
|
88
|
-
className: "text-lg font-semibold text-
|
|
88
|
+
className: "text-lg font-semibold text-text-primary",
|
|
89
89
|
children: "Access Denied"
|
|
90
90
|
}), /* @__PURE__ */ b("p", {
|
|
91
91
|
className: "text-sm text-text-secondary",
|
|
@@ -196,7 +196,7 @@ function T() {
|
|
|
196
196
|
}
|
|
197
197
|
];
|
|
198
198
|
return G && !Y ? /* @__PURE__ */ x(w, {
|
|
199
|
-
className: "space-y-6
|
|
199
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
200
200
|
children: [/* @__PURE__ */ x("button", {
|
|
201
201
|
type: "button",
|
|
202
202
|
onClick: Z,
|
|
@@ -204,7 +204,7 @@ function T() {
|
|
|
204
204
|
children: [/* @__PURE__ */ b(p, { className: "size-4" }), P("component.backToComponents", "Back to Components")]
|
|
205
205
|
}), /* @__PURE__ */ b(u, { variant: "detail" })]
|
|
206
206
|
}) : K && !Y ? /* @__PURE__ */ x(w, {
|
|
207
|
-
className: "space-y-6
|
|
207
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
208
208
|
children: [/* @__PURE__ */ x("button", {
|
|
209
209
|
type: "button",
|
|
210
210
|
onClick: Z,
|
|
@@ -216,7 +216,7 @@ function T() {
|
|
|
216
216
|
onRetry: () => q()
|
|
217
217
|
})]
|
|
218
218
|
}) : Y ? /* @__PURE__ */ x(w, {
|
|
219
|
-
className: "space-y-6
|
|
219
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
220
220
|
children: [
|
|
221
221
|
z && /* @__PURE__ */ b("div", {
|
|
222
222
|
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",
|
|
@@ -247,7 +247,7 @@ function T() {
|
|
|
247
247
|
type: "button",
|
|
248
248
|
onClick: () => q(),
|
|
249
249
|
disabled: G,
|
|
250
|
-
className: "flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-bg-
|
|
250
|
+
className: "flex items-center justify-center p-2 bg-bg-sunken text-text-primary rounded-md hover:bg-action-secondary-bg-hover transition-colors disabled:opacity-50",
|
|
251
251
|
title: P("common.refresh", "Refresh"),
|
|
252
252
|
children: /* @__PURE__ */ b(xe, { className: `size-4 ${G ? "animate-spin" : ""}` })
|
|
253
253
|
}),
|
|
@@ -319,7 +319,7 @@ function T() {
|
|
|
319
319
|
/* @__PURE__ */ b(t, { className: "size-4" }),
|
|
320
320
|
e.label,
|
|
321
321
|
e.badge && /* @__PURE__ */ b("span", {
|
|
322
|
-
className: `hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${n ? "bg-
|
|
322
|
+
className: `hidden rounded-full px-1.5 py-0.5 text-[10px] sm:inline-flex sm:text-xs ${n ? "bg-[var(--color-accent-soft)] text-action-primary-bg" : "bg-bg-sunken text-text-muted"}`,
|
|
323
323
|
children: e.badge
|
|
324
324
|
})
|
|
325
325
|
]
|
|
@@ -791,7 +791,7 @@ function T() {
|
|
|
791
791
|
})
|
|
792
792
|
]
|
|
793
793
|
}) : /* @__PURE__ */ x(w, {
|
|
794
|
-
className: "space-y-6
|
|
794
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
795
795
|
children: [/* @__PURE__ */ x("button", {
|
|
796
796
|
type: "button",
|
|
797
797
|
onClick: Z,
|