@burdenoff/microfe-vibecontrols 2026.623.1 → 2026.623.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/actions/ActionCard.js +2 -2
- package/dist/components/actions/ActionCard.js.map +1 -1
- package/dist/components/actions/ActionTypeBadge.js +1 -1
- package/dist/components/actions/ActionTypeBadge.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 +24 -24
- 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 +4 -4
- 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 +7 -7
- 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 +76 -43
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +7 -4
- 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":"LogsPage.js","names":[],"sources":["../../src/pages/LogsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Bot } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { EmptyState, GetHelpButton, PageHeader } from '@/components/shared';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { SearchableSelect } from '@/components/shared/SearchableSelect';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport function LogsPage() {\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { agents: cachedAgents, loading } = useCachedInitialData();\n const [selectedAgentId, setSelectedAgentId] = useState('');\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Phase 5B — store-backed active-profile lookup keyed by agent id. The\n // hook can only be called once at the top level, so the store map is\n // the only safe way to attach a per-row `profile` inside the .map()\n // below. Each agent row will fall back to the literal \"default\" string\n // until the user opens that agent's Profiles tab and the AgentProfile\n // rows for it land in the store.\n const profileMap = useVibeControlsStore((s) => s.activeProfilePerAgent);\n\n // Pre-warm the store for the currently-selected agent so its row shows\n // the correct profile once profiles are loaded.\n useActiveAgentProfile(selectedAgentId || null);\n\n const agents = useMemo(\n () =>\n cachedAgents\n .filter((agent) => agent.tunnelUrl)\n .map((agent) => ({\n id: agent.id,\n name: agent.name || agent.hostname || agent.id,\n isActive: agent.isActive,\n hostname: agent.hostname,\n platform: agent.platform,\n profile: profileMap[agent.id] ?? 'default',\n })),\n [cachedAgents, profileMap]\n );\n\n const selectedAgent = agents.find((agent) => agent.id === selectedAgentId);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.LOG_LIST))\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-
|
|
1
|
+
{"version":3,"file":"LogsPage.js","names":[],"sources":["../../src/pages/LogsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Bot } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { EmptyState, GetHelpButton, PageHeader } from '@/components/shared';\nimport { LogViewer } from '@/components/logs/LogViewer';\nimport { SearchableSelect } from '@/components/shared/SearchableSelect';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useActiveAgentProfile } from '@/hooks/useActiveAgentProfile';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\nexport function LogsPage() {\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const { agents: cachedAgents, loading } = useCachedInitialData();\n const [selectedAgentId, setSelectedAgentId] = useState('');\n\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Phase 5B — store-backed active-profile lookup keyed by agent id. The\n // hook can only be called once at the top level, so the store map is\n // the only safe way to attach a per-row `profile` inside the .map()\n // below. Each agent row will fall back to the literal \"default\" string\n // until the user opens that agent's Profiles tab and the AgentProfile\n // rows for it land in the store.\n const profileMap = useVibeControlsStore((s) => s.activeProfilePerAgent);\n\n // Pre-warm the store for the currently-selected agent so its row shows\n // the correct profile once profiles are loaded.\n useActiveAgentProfile(selectedAgentId || null);\n\n const agents = useMemo(\n () =>\n cachedAgents\n .filter((agent) => agent.tunnelUrl)\n .map((agent) => ({\n id: agent.id,\n name: agent.name || agent.hostname || agent.id,\n isActive: agent.isActive,\n hostname: agent.hostname,\n platform: agent.platform,\n profile: profileMap[agent.id] ?? 'default',\n })),\n [cachedAgents, profileMap]\n );\n\n const selectedAgent = agents.find((agent) => agent.id === selectedAgentId);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions…</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.LOG_LIST))\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 if (loading && agents.length === 0) {\n return (\n <MotionPage className=\"flex h-full flex-col gap-6 px-6 lg:px-8 py-6\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n <PageSkeleton variant=\"rows\" count={6} />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"flex h-full flex-col gap-6 px-6 lg:px-8 py-6\">\n <PageHeader\n title={tr('nav.logs', 'Logs')}\n description={tr('logs.description', 'Gateway-proxied log streaming from your agents')}\n />\n\n <div className=\"flex flex-col items-start gap-3 rounded-card border border-border-subtle bg-bg-surface p-4 shadow-elevation-1 sm:flex-row sm:items-center\">\n <label className=\"text-sm font-medium text-text-secondary\">\n {tr('logs.agentLabel', 'Agent:')}\n </label>\n\n {agents.length > 0 ? (\n <div className=\"w-full sm:w-80\">\n <SearchableSelect\n options={agents.map((agent) => ({\n value: agent.id,\n label: agent.name,\n description: `${agent.platform ?? ''} · ${agent.hostname ?? ''}`,\n }))}\n value={selectedAgentId}\n onChange={(value) => setSelectedAgentId(value)}\n placeholder={tr('logs.selectAgent', 'Select an agent...')}\n />\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">\n {tr('logs.noAgentsAvailable', 'No agents with tunnel access available')}\n </span>\n )}\n\n {selectedAgent ? (\n <div className=\"flex items-center gap-1.5 text-xs text-text-tertiary\">\n <div\n className={`size-2 rounded-full ${selectedAgent.isActive ? 'bg-status-success-bg' : 'bg-bg-sunken'}`}\n />\n {selectedAgent.hostname}\n </div>\n ) : null}\n </div>\n\n {selectedAgent ? (\n <LogViewer agentId={selectedAgent.id} profile={selectedAgent.profile} className=\"flex-1\" />\n ) : (\n <EmptyState\n icon={<Bot size={48} />}\n title={tr('logs.noAgentSelected', 'Select an agent to view logs')}\n description={tr(\n 'logs.noAgentSelectedDescription',\n 'Logs are fetched on demand. Choose an agent above to start streaming its gateway-proxied logs.'\n )}\n action={\n <GetHelpButton prompt=\"What are Logs in VibeControls and how do I investigate them?\" />\n }\n />\n )}\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,SAAgB,IAAW;CACzB,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,kBAAe,WAAW,MAAuB,GAAgB,EACnE,EAAE,QAAQ,GAAc,eAAY,GAAsB,EAC1D,CAAC,GAAiB,KAAsB,EAAS,GAAG,EAEpD,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAS9B,IAAa,GAAsB,MAAM,EAAE,sBAAsB;AAIvE,GAAsB,KAAmB,KAAK;CAE9C,IAAM,IAAS,QAEX,EACG,QAAQ,MAAU,EAAM,UAAU,CAClC,KAAK,OAAW;EACf,IAAI,EAAM;EACV,MAAM,EAAM,QAAQ,EAAM,YAAY,EAAM;EAC5C,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,UAAU,EAAM;EAChB,SAAS,EAAW,EAAM,OAAO;EAClC,EAAE,EACP,CAAC,GAAc,EAAW,CAC3B,EAEK,IAAgB,EAAO,MAAM,MAAU,EAAM,OAAO,EAAgB;AAgC1E,QA9BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,KAAD;GAAG,WAAU;aAAsB;GAAwB,CAAA;EACvD,CAAA,GAEL,EAAc,EAAiB,SAAS,GAYzC,KAAW,EAAO,WAAW,IAE7B,kBAAC,GAAD;EAAY,WAAU;YAAtB,CACE,kBAAC,GAAD;GACE,OAAO,EAAG,YAAY,OAAO;GAC7B,aAAa,EAAG,oBAAoB,iDAAiD;GACrF,CAAA,EACF,kBAAC,GAAD;GAAc,SAAQ;GAAO,OAAO;GAAK,CAAA,CAC9B;MAKf,kBAAC,GAAD;EAAY,WAAU;YAAtB;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,YAAY,OAAO;IAC7B,aAAa,EAAG,oBAAoB,iDAAiD;IACrF,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,SAAD;MAAO,WAAU;gBACd,EAAG,mBAAmB,SAAS;MAC1B,CAAA;KAEP,EAAO,SAAS,IACf,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,SAAS,EAAO,KAAK,OAAW;QAC9B,OAAO,EAAM;QACb,OAAO,EAAM;QACb,aAAa,GAAG,EAAM,YAAY,GAAG,KAAK,EAAM,YAAY;QAC7D,EAAE;OACH,OAAO;OACP,WAAW,MAAU,EAAmB,EAAM;OAC9C,aAAa,EAAG,oBAAoB,qBAAqB;OACzD,CAAA;MACE,CAAA,GAEN,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,0BAA0B,yCAAyC;MAClE,CAAA;KAGR,IACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD,EACE,WAAW,uBAAuB,EAAc,WAAW,yBAAyB,kBACpF,CAAA,EACD,EAAc,SACX;UACJ;KACA;;GAEL,IACC,kBAAC,GAAD;IAAW,SAAS,EAAc;IAAI,SAAS,EAAc;IAAS,WAAU;IAAW,CAAA,GAE3F,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;IACvB,OAAO,EAAG,wBAAwB,+BAA+B;IACjE,aAAa,EACX,mCACA,iGACD;IACD,QACE,kBAAC,GAAD,EAAe,QAAO,gEAAiE,CAAA;IAEzF,CAAA;GAEO;MA9EX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAA0C;IAAkB,CAAA,EAC1E,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,CACA;;EACF,CAAA"}
|
|
@@ -31,7 +31,7 @@ function b() {
|
|
|
31
31
|
children: x("noteDetails.loadingPermissions", "Loading permissions...")
|
|
32
32
|
})
|
|
33
33
|
}) : E ? O && !j ? /* @__PURE__ */ v(y, {
|
|
34
|
-
className: "space-y-6
|
|
34
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
35
35
|
children: [/* @__PURE__ */ v("button", {
|
|
36
36
|
type: "button",
|
|
37
37
|
onClick: () => C("/vibes"),
|
|
@@ -39,7 +39,7 @@ function b() {
|
|
|
39
39
|
children: [/* @__PURE__ */ _(u, { className: "size-4" }), x("noteDetails.back", "Back")]
|
|
40
40
|
}), /* @__PURE__ */ _(s, { variant: "detail" })]
|
|
41
41
|
}) : k && !j ? /* @__PURE__ */ v(y, {
|
|
42
|
-
className: "space-y-6
|
|
42
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
43
43
|
children: [/* @__PURE__ */ v("button", {
|
|
44
44
|
type: "button",
|
|
45
45
|
onClick: () => C("/vibes"),
|
|
@@ -51,7 +51,7 @@ function b() {
|
|
|
51
51
|
onRetry: () => A()
|
|
52
52
|
})]
|
|
53
53
|
}) : j ? /* @__PURE__ */ v(y, {
|
|
54
|
-
className: "space-y-6
|
|
54
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
55
55
|
children: [/* @__PURE__ */ _(n, {
|
|
56
56
|
title: j.title,
|
|
57
57
|
description: j.vibe ? `${x("noteDetails.attachedTo", "Attached to")} ${j.vibe.name}` : x("noteDetails.workspaceNote", "Workspace note"),
|
|
@@ -137,7 +137,7 @@ function b() {
|
|
|
137
137
|
})]
|
|
138
138
|
})]
|
|
139
139
|
}) : /* @__PURE__ */ v(y, {
|
|
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: [/* @__PURE__ */ v("button", {
|
|
142
142
|
type: "button",
|
|
143
143
|
onClick: () => C("/vibes"),
|
|
@@ -153,7 +153,7 @@ function b() {
|
|
|
153
153
|
children: /* @__PURE__ */ v("div", {
|
|
154
154
|
className: "text-center space-y-2",
|
|
155
155
|
children: [/* @__PURE__ */ _("h2", {
|
|
156
|
-
className: "text-lg font-semibold text-
|
|
156
|
+
className: "text-lg font-semibold text-text-primary",
|
|
157
157
|
children: x("noteDetails.accessDenied", "Access Denied")
|
|
158
158
|
}), /* @__PURE__ */ _("p", {
|
|
159
159
|
className: "text-sm text-text-secondary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteDetailsPage.js","names":[],"sources":["../../src/pages/NoteDetailsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { ArrowLeft, ExternalLink, FileText, Pin, Tag } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Card, EmptyState, ErrorState, PageHeader } from '@/components/shared';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useGetNoteQuery } from '@/generated/wspace-operations';\nimport { useParams } from 'react-router-dom';\n\nexport function NoteDetailsPage() {\n const { t } = useI18n();\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 const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const canReadNote = hasPermission(VIBE_PERMISSIONS.NOTE_READ);\n const { data, loading, error, refetch } = useGetNoteQuery({\n variables: { id: id || '' },\n skip: !id || permissionsLoading || !canReadNote,\n fetchPolicy: 'cache-and-network',\n });\n\n const note = data?.note;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('noteDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!canReadNote)\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\">\n {tr('noteDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('noteDetails.accessDeniedDesc', \"You don't have permission to access this page.\")}\n </p>\n </div>\n </div>\n );\n\n if (loading && !note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('noteDetails.back', 'Back')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <ErrorState\n title={tr('noteDetails.failedLoad', 'Failed to load note')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('noteDetails.notFound', 'Note not found')}\n description={tr(\n 'noteDetails.notFoundDesc',\n 'This note may have been deleted or you may no longer have access to it.'\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 <PageHeader\n title={note.title}\n description={\n note.vibe\n ? `${tr('noteDetails.attachedTo', 'Attached to')} ${note.vibe.name}`\n : tr('noteDetails.workspaceNote', 'Workspace note')\n }\n actions={\n <div className=\"flex items-center gap-2\">\n {note.vibe && (\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-md border border-border-default bg-bg-surface hover:bg-bg-muted text-sm text-text-primary transition-colors\"\n >\n <ExternalLink className=\"size-4\" />\n {tr('noteDetails.openVibe', 'Open Vibe')}\n </button>\n )}\n </div>\n }\n />\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-tertiary\">\n {note.isPinned && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary-default/10 px-2 py-1 text-primary-default\">\n <Pin className=\"size-3.5\" />\n {tr('noteDetails.pinned', 'Pinned')}\n </span>\n )}\n <span>\n {tr('noteDetails.created', 'Created')} {new Date(note.createdAt).toLocaleString()}\n </span>\n <span>\n {tr('noteDetails.updated', 'Updated')} {new Date(note.updatedAt).toLocaleString()}\n </span>\n </div>\n\n <div className=\"rounded-lg border border-border-default bg-bg-canvas p-4\">\n <pre className=\"whitespace-pre-wrap break-words text-sm text-text-primary font-sans leading-6\">\n {note.content}\n </pre>\n </div>\n </div>\n </Card>\n\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.details', 'Details')}\n </h2>\n <div className=\"space-y-3 text-sm\">\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.noteId', 'Note ID')}</div>\n <div className=\"font-mono text-text-primary break-all\">{note.id}</div>\n </div>\n {note.vibe && (\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.vibe', 'Vibe')}</div>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"text-primary-default hover:underline\"\n >\n {note.vibe.name}\n </button>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.tags', 'Tags')}\n </h2>\n {note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {note.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-muted px-2 py-1 text-xs text-text-secondary\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">\n {tr('noteDetails.noTags', 'No tags assigned.')}\n </p>\n )}\n </div>\n </Card>\n </div>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NoteDetailsPage.js","names":[],"sources":["../../src/pages/NoteDetailsPage.tsx"],"sourcesContent":["import { useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { ArrowLeft, ExternalLink, FileText, Pin, Tag } from 'lucide-react';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Card, EmptyState, ErrorState, PageHeader } from '@/components/shared';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useGetNoteQuery } from '@/generated/wspace-operations';\nimport { useParams } from 'react-router-dom';\n\nexport function NoteDetailsPage() {\n const { t } = useI18n();\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 const { id } = useParams<{ id: string }>();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const canReadNote = hasPermission(VIBE_PERMISSIONS.NOTE_READ);\n const { data, loading, error, refetch } = useGetNoteQuery({\n variables: { id: id || '' },\n skip: !id || permissionsLoading || !canReadNote,\n fetchPolicy: 'cache-and-network',\n });\n\n const note = data?.note;\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">\n {tr('noteDetails.loadingPermissions', 'Loading permissions...')}\n </p>\n </div>\n );\n if (!canReadNote)\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\">\n {tr('noteDetails.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr('noteDetails.accessDeniedDesc', \"You don't have permission to access this page.\")}\n </p>\n </div>\n </div>\n );\n\n if (loading && !note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('noteDetails.back', 'Back')}\n </button>\n <PageSkeleton variant=\"detail\" />\n </MotionPage>\n );\n }\n\n if (error && !note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <ErrorState\n title={tr('noteDetails.failedLoad', 'Failed to load note')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n if (!note) {\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={() => navigate('/vibes')}\n className=\"flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={tr('noteDetails.notFound', 'Note not found')}\n description={tr(\n 'noteDetails.notFoundDesc',\n 'This note may have been deleted or you may no longer have access to it.'\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 <PageHeader\n title={note.title}\n description={\n note.vibe\n ? `${tr('noteDetails.attachedTo', 'Attached to')} ${note.vibe.name}`\n : tr('noteDetails.workspaceNote', 'Workspace note')\n }\n actions={\n <div className=\"flex items-center gap-2\">\n {note.vibe && (\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"inline-flex items-center gap-2 px-3 py-2 rounded-md border border-border-default bg-bg-surface hover:bg-bg-muted text-sm text-text-primary transition-colors\"\n >\n <ExternalLink className=\"size-4\" />\n {tr('noteDetails.openVibe', 'Open Vibe')}\n </button>\n )}\n </div>\n }\n />\n\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-tertiary\">\n {note.isPinned && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-primary-default/10 px-2 py-1 text-primary-default\">\n <Pin className=\"size-3.5\" />\n {tr('noteDetails.pinned', 'Pinned')}\n </span>\n )}\n <span>\n {tr('noteDetails.created', 'Created')} {new Date(note.createdAt).toLocaleString()}\n </span>\n <span>\n {tr('noteDetails.updated', 'Updated')} {new Date(note.updatedAt).toLocaleString()}\n </span>\n </div>\n\n <div className=\"rounded-lg border border-border-default bg-bg-canvas p-4\">\n <pre className=\"whitespace-pre-wrap break-words text-sm text-text-primary font-sans leading-6\">\n {note.content}\n </pre>\n </div>\n </div>\n </Card>\n\n <div className=\"space-y-6\">\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.details', 'Details')}\n </h2>\n <div className=\"space-y-3 text-sm\">\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.noteId', 'Note ID')}</div>\n <div className=\"font-mono text-text-primary break-all\">{note.id}</div>\n </div>\n {note.vibe && (\n <div>\n <div className=\"text-text-tertiary\">{tr('noteDetails.vibe', 'Vibe')}</div>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibes/${note.vibe?.id}`)}\n className=\"text-primary-default hover:underline\"\n >\n {note.vibe.name}\n </button>\n </div>\n )}\n </div>\n </div>\n </Card>\n\n <Card>\n <div className=\"p-5 space-y-4\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('noteDetails.tags', 'Tags')}\n </h2>\n {note.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {note.tags.map((tag: string) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-muted px-2 py-1 text-xs text-text-secondary\"\n >\n <Tag className=\"size-3\" />\n {tag}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-tertiary\">\n {tr('noteDetails.noTags', 'No tags assigned.')}\n </p>\n )}\n </div>\n </Card>\n </div>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -139,11 +139,11 @@ function D() {
|
|
|
139
139
|
children: /* @__PURE__ */ S(a, { message: A("overviewPage.loadingPermissions", "Loading permissions...") })
|
|
140
140
|
});
|
|
141
141
|
if (!k(u.VIBE_LIST)) return /* @__PURE__ */ S("div", {
|
|
142
|
-
className: "flex items-center justify-center min-h-[400px]",
|
|
142
|
+
className: "flex items-center justify-center min-h-[400px] px-4",
|
|
143
143
|
children: /* @__PURE__ */ C("div", {
|
|
144
|
-
className: "text-center space-y-2",
|
|
144
|
+
className: "text-center space-y-2 max-w-sm",
|
|
145
145
|
children: [/* @__PURE__ */ S("h2", {
|
|
146
|
-
className: "text-lg font-semibold text-
|
|
146
|
+
className: "text-lg font-semibold tracking-tight text-text-primary",
|
|
147
147
|
children: A("common.accessDeniedTitle", "Access Denied")
|
|
148
148
|
}), /* @__PURE__ */ S("p", {
|
|
149
149
|
className: "text-sm text-text-secondary",
|
|
@@ -162,16 +162,16 @@ function D() {
|
|
|
162
162
|
note_created: te,
|
|
163
163
|
module_installed: g
|
|
164
164
|
}[e] || g, { className: "size-4" }), ce = (e) => ({
|
|
165
|
-
green: "bg-status-success-bg text-status-success-text",
|
|
166
|
-
blue: "bg-
|
|
167
|
-
purple: "bg-accent-purple-subtle text-
|
|
168
|
-
orange: "bg-status-warning-bg text-status-warning-text",
|
|
165
|
+
green: "bg-status-success-bg-subtle text-status-success-text",
|
|
166
|
+
blue: "bg-[var(--color-accent-soft)] text-text-primary",
|
|
167
|
+
purple: "bg-accent-purple-subtle text-text-primary",
|
|
168
|
+
orange: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
169
169
|
cyan: "bg-accent-teal-subtle text-accent-teal",
|
|
170
|
-
yellow: "bg-status-warning-bg text-status-warning-text",
|
|
171
|
-
pink: "bg-accent-purple-subtle text-
|
|
170
|
+
yellow: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
171
|
+
pink: "bg-accent-purple-subtle text-text-primary"
|
|
172
172
|
})[e] || "bg-bg-sunken text-text-secondary";
|
|
173
173
|
return /* @__PURE__ */ C(E, {
|
|
174
|
-
className: "space-y-6
|
|
174
|
+
className: "space-y-6 px-6 lg:px-8 py-6 bg-bg-canvas min-h-screen",
|
|
175
175
|
children: [
|
|
176
176
|
/* @__PURE__ */ S(i, {
|
|
177
177
|
title: A("overviewPage.title", "VibeControls Overview"),
|
|
@@ -180,8 +180,11 @@ function D() {
|
|
|
180
180
|
type: "button",
|
|
181
181
|
onClick: V,
|
|
182
182
|
disabled: P,
|
|
183
|
-
className: "flex items-center gap-2 px-3 py-2 text-sm bg-bg-
|
|
184
|
-
children: [/* @__PURE__ */ S(ie, {
|
|
183
|
+
className: "inline-flex items-center gap-2 px-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
184
|
+
children: [/* @__PURE__ */ S(ie, {
|
|
185
|
+
className: `size-4 ${P ? "animate-spin" : ""}`,
|
|
186
|
+
"aria-hidden": "true"
|
|
187
|
+
}), A("overviewPage.refresh", "Refresh")]
|
|
185
188
|
})
|
|
186
189
|
}),
|
|
187
190
|
F && !P && /* @__PURE__ */ S(o, {
|
|
@@ -251,31 +254,40 @@ function D() {
|
|
|
251
254
|
children: /* @__PURE__ */ C(s, {
|
|
252
255
|
className: "flex h-full flex-col",
|
|
253
256
|
children: [/* @__PURE__ */ C("div", {
|
|
254
|
-
className: "flex items-center justify-between mb-
|
|
257
|
+
className: "flex items-center justify-between mb-5",
|
|
255
258
|
children: [/* @__PURE__ */ C("h2", {
|
|
256
|
-
className: "text-lg font-semibold text-text-primary flex items-center gap-2",
|
|
257
|
-
children: [/* @__PURE__ */ S(
|
|
259
|
+
className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
|
|
260
|
+
children: [/* @__PURE__ */ S("span", {
|
|
261
|
+
className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
262
|
+
children: /* @__PURE__ */ S(_, { className: "size-5" })
|
|
263
|
+
}), A("overviewPage.sections.activeAgents", "Active Agents")]
|
|
258
264
|
}), /* @__PURE__ */ S("button", {
|
|
259
265
|
type: "button",
|
|
260
266
|
onClick: () => D("/agents"),
|
|
261
|
-
className: "text-sm text-text-link hover:underline",
|
|
267
|
+
className: "text-sm font-medium text-text-link hover:underline",
|
|
262
268
|
"aria-label": A("overviewPage.sections.viewAllAgents", "View all agents"),
|
|
263
269
|
children: A("overviewPage.sections.viewAll", "View all")
|
|
264
270
|
})]
|
|
265
271
|
}), Y.length > 0 ? /* @__PURE__ */ S(T, {
|
|
266
|
-
className: "grid grid-cols-1 sm:grid-cols-2 gap-
|
|
272
|
+
className: "grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1 content-start",
|
|
267
273
|
stagger: .04,
|
|
268
274
|
children: Y.slice(0, 4).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(d, {
|
|
269
275
|
agent: e,
|
|
270
276
|
onSelect: (e) => D(`/agents/${e.id}`)
|
|
271
277
|
}) }, e.id))
|
|
272
278
|
}) : /* @__PURE__ */ C("div", {
|
|
273
|
-
className: "flex flex-1 flex-col items-center justify-center text-center py-
|
|
279
|
+
className: "flex flex-1 flex-col items-center justify-center text-center py-8 text-text-secondary",
|
|
274
280
|
children: [
|
|
275
|
-
/* @__PURE__ */ S(
|
|
276
|
-
|
|
281
|
+
/* @__PURE__ */ S("span", {
|
|
282
|
+
className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
|
|
283
|
+
children: /* @__PURE__ */ S(_, { className: "size-6" })
|
|
284
|
+
}),
|
|
285
|
+
/* @__PURE__ */ S("p", {
|
|
286
|
+
className: "text-base font-semibold text-text-primary",
|
|
287
|
+
children: A("overviewPage.empty.noActiveAgents", "No active agents")
|
|
288
|
+
}),
|
|
277
289
|
/* @__PURE__ */ S("p", {
|
|
278
|
-
className: "text-xs text-text-muted mt-1 mb-
|
|
290
|
+
className: "text-xs text-text-muted mt-1 mb-4",
|
|
279
291
|
children: A("overviewPage.empty.installCliHint", "Install the agent CLI to get started")
|
|
280
292
|
}),
|
|
281
293
|
/* @__PURE__ */ C("div", {
|
|
@@ -307,14 +319,17 @@ function D() {
|
|
|
307
319
|
children: /* @__PURE__ */ C(s, {
|
|
308
320
|
className: "flex h-full flex-col",
|
|
309
321
|
children: [/* @__PURE__ */ C("div", {
|
|
310
|
-
className: "flex items-center justify-between mb-
|
|
322
|
+
className: "flex items-center justify-between mb-5",
|
|
311
323
|
children: [/* @__PURE__ */ C("h2", {
|
|
312
|
-
className: "text-lg font-semibold text-text-primary flex items-center gap-2",
|
|
313
|
-
children: [/* @__PURE__ */ S(
|
|
324
|
+
className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
|
|
325
|
+
children: [/* @__PURE__ */ S("span", {
|
|
326
|
+
className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
327
|
+
children: /* @__PURE__ */ S(g, { className: "size-5" })
|
|
328
|
+
}), A("overviewPage.sections.recentActivity", "Recent Activity")]
|
|
314
329
|
}), /* @__PURE__ */ S("button", {
|
|
315
330
|
type: "button",
|
|
316
331
|
onClick: () => D("/audit"),
|
|
317
|
-
className: "text-sm text-text-link hover:underline",
|
|
332
|
+
className: "text-sm font-medium text-text-link hover:underline",
|
|
318
333
|
"aria-label": A("overviewPage.sections.viewAllActivity", "View all activity"),
|
|
319
334
|
children: "View all"
|
|
320
335
|
})]
|
|
@@ -360,63 +375,81 @@ function D() {
|
|
|
360
375
|
})]
|
|
361
376
|
}),
|
|
362
377
|
/* @__PURE__ */ S(w, { children: /* @__PURE__ */ C(s, { children: [/* @__PURE__ */ C("div", {
|
|
363
|
-
className: "flex items-center justify-between mb-
|
|
378
|
+
className: "flex items-center justify-between mb-5",
|
|
364
379
|
children: [/* @__PURE__ */ C("h2", {
|
|
365
|
-
className: "text-lg font-semibold text-text-primary flex items-center gap-2",
|
|
366
|
-
children: [/* @__PURE__ */ S(
|
|
380
|
+
className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
|
|
381
|
+
children: [/* @__PURE__ */ S("span", {
|
|
382
|
+
className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
383
|
+
children: /* @__PURE__ */ S(y, { className: "size-5" })
|
|
384
|
+
}), A("overviewPage.sections.runningSessions", "Running Sessions")]
|
|
367
385
|
}), /* @__PURE__ */ S("button", {
|
|
368
386
|
type: "button",
|
|
369
387
|
onClick: () => D("/sessions"),
|
|
370
|
-
className: "text-sm text-text-link hover:underline",
|
|
388
|
+
className: "text-sm font-medium text-text-link hover:underline",
|
|
371
389
|
children: A("overviewPage.sections.viewAll", "View all")
|
|
372
390
|
})]
|
|
373
391
|
}), X.length > 0 ? /* @__PURE__ */ S(T, {
|
|
374
|
-
className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-
|
|
392
|
+
className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4",
|
|
375
393
|
stagger: .04,
|
|
376
394
|
children: X.slice(0, 6).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(ee, {
|
|
377
395
|
session: e,
|
|
378
396
|
onOpen: (e) => D(`/sessions/${e.id}`)
|
|
379
397
|
}) }, e.id))
|
|
380
398
|
}) : /* @__PURE__ */ C("div", {
|
|
381
|
-
className: "text-center py-
|
|
399
|
+
className: "flex flex-col items-center text-center py-10 text-text-secondary",
|
|
382
400
|
children: [
|
|
383
|
-
/* @__PURE__ */ S(
|
|
384
|
-
|
|
401
|
+
/* @__PURE__ */ S("span", {
|
|
402
|
+
className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
|
|
403
|
+
children: /* @__PURE__ */ S(y, { className: "size-6" })
|
|
404
|
+
}),
|
|
405
|
+
/* @__PURE__ */ S("p", {
|
|
406
|
+
className: "text-base font-semibold text-text-primary",
|
|
407
|
+
children: A("overviewPage.empty.noRunningSessions", "No running sessions")
|
|
408
|
+
}),
|
|
385
409
|
/* @__PURE__ */ S("button", {
|
|
386
410
|
type: "button",
|
|
387
411
|
onClick: () => D("/sessions"),
|
|
388
|
-
className: "mt-
|
|
412
|
+
className: "mt-3 text-sm font-medium text-text-link hover:underline",
|
|
389
413
|
children: A("overviewPage.empty.startSession", "Start a session")
|
|
390
414
|
})
|
|
391
415
|
]
|
|
392
416
|
})] }) }),
|
|
393
417
|
/* @__PURE__ */ S(w, { children: /* @__PURE__ */ C(s, { children: [/* @__PURE__ */ C("div", {
|
|
394
|
-
className: "flex items-center justify-between mb-
|
|
418
|
+
className: "flex items-center justify-between mb-5",
|
|
395
419
|
children: [/* @__PURE__ */ C("h2", {
|
|
396
|
-
className: "text-lg font-semibold text-text-primary flex items-center gap-2",
|
|
397
|
-
children: [/* @__PURE__ */ S(
|
|
420
|
+
className: "text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5",
|
|
421
|
+
children: [/* @__PURE__ */ S("span", {
|
|
422
|
+
className: "inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
423
|
+
children: /* @__PURE__ */ S(b, { className: "size-5" })
|
|
424
|
+
}), A("overviewPage.sections.activeVibes", "Active Vibes")]
|
|
398
425
|
}), /* @__PURE__ */ S("button", {
|
|
399
426
|
type: "button",
|
|
400
427
|
onClick: () => D("/vibes"),
|
|
401
|
-
className: "text-sm text-text-link hover:underline",
|
|
428
|
+
className: "text-sm font-medium text-text-link hover:underline",
|
|
402
429
|
children: A("overviewPage.sections.viewAll", "View all")
|
|
403
430
|
})]
|
|
404
431
|
}), Z.length > 0 ? /* @__PURE__ */ S(T, {
|
|
405
|
-
className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-
|
|
432
|
+
className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4",
|
|
406
433
|
stagger: .04,
|
|
407
434
|
children: Z.slice(0, 6).map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(f, {
|
|
408
435
|
vibe: e,
|
|
409
436
|
onOpen: (e) => D(`/vibes/${e.id}`)
|
|
410
437
|
}) }, e.id))
|
|
411
438
|
}) : /* @__PURE__ */ C("div", {
|
|
412
|
-
className: "text-center py-
|
|
439
|
+
className: "flex flex-col items-center text-center py-10 text-text-secondary",
|
|
413
440
|
children: [
|
|
414
|
-
/* @__PURE__ */ S(
|
|
415
|
-
|
|
441
|
+
/* @__PURE__ */ S("span", {
|
|
442
|
+
className: "inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3",
|
|
443
|
+
children: /* @__PURE__ */ S(b, { className: "size-6" })
|
|
444
|
+
}),
|
|
445
|
+
/* @__PURE__ */ S("p", {
|
|
446
|
+
className: "text-base font-semibold text-text-primary",
|
|
447
|
+
children: A("overviewPage.empty.noActiveVibes", "No active vibes")
|
|
448
|
+
}),
|
|
416
449
|
/* @__PURE__ */ S("button", {
|
|
417
450
|
type: "button",
|
|
418
451
|
onClick: () => D("/vibes"),
|
|
419
|
-
className: "mt-
|
|
452
|
+
className: "mt-3 text-sm font-medium text-text-link hover:underline",
|
|
420
453
|
children: A("overviewPage.empty.addVibe", "Add a vibe")
|
|
421
454
|
})
|
|
422
455
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../src/pages/OverviewPage.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport {\n Bot,\n Zap,\n Terminal,\n FileText,\n Activity,\n Server,\n Play,\n GitBranch,\n RefreshCw,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AgentCard } from '@/components/agents';\nimport { SessionCard } from '@/components/sessions';\nimport { VibeCard } from '@/components/vibes';\nimport {\n PageHeader,\n StatCard,\n Card,\n AgentSetupBanner,\n ErrorState,\n LoadingState,\n} from '@/components/shared';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useListTargetsQuery } from '@/generated/wspace-operations';\nimport type { Vibe } from '@/components/vibes/types';\nimport type { Session } from '@/components/sessions/types';\n\n/**\n * Overview page showing summary dashboard with stats, active resources, and recent activity\n *\n * This page now uses cached initial data from Apollo instead of independent queries,\n * reducing GraphQL calls from 5 to 0 for the initial render.\n * The refresh button triggers a refetch of the initial data.\n */\nexport function OverviewPage() {\n const handleNavigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n };\n\n // Use cached initial data - reads from Apollo cache, no network request\n // Loading state reflects whether initial data has been loaded\n const {\n agents: cachedAgents,\n vibes: cachedVibes,\n sessions: cachedSessions,\n loading,\n error: dataError,\n totals,\n refetch,\n } = useCachedInitialData();\n\n // Targets is intentionally NOT part of VibeControlsInitialData (it caused a 25s\n // timeout there), so fetch the count with a lightweight standalone query — the\n // same pattern HealthPage uses.\n const { workspaceId } = useVibeControls();\n const targetsCountQuery = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50 },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const refetchTargets = targetsCountQuery.refetch;\n // The header Refresh button must refetch BOTH the cached initial data and the\n // standalone targets query — otherwise the Targets stat card stays stale after\n // a manual refresh.\n const handleRefresh = useCallback(() => {\n void refetch();\n void refetchTargets();\n }, [refetch, refetchTargets]);\n\n const targetItems: Array<{ status: string }> = targetsCountQuery.data?.targets.items ?? [];\n const targetsTotal = targetsCountQuery.data?.targets.total ?? 0;\n // `targetsConnected` is derived from `targetItems`, which is capped by the\n // query `limit`. When the workspace has more targets than the page size the\n // connected count would only reflect the first page and contradict the\n // server-side `targetsTotal`. Track truncation so we only surface the\n // connected detail when it is accurate for the full set.\n const targetsTruncated = targetsTotal > targetItems.length;\n const targetsConnected = targetItems.filter(\n (t) => t.status === 'connected' || t.status === 'active'\n ).length;\n\n if (dataError) {\n console.error('[overview] failed to load initial workspace data', dataError);\n }\n\n // Map cached data to component types\n // Using type assertion since we know the structure from the GraphQL query\n const agents = useMemo(() => {\n return cachedAgents.map((agent) => ({\n id: agent.id,\n workspaceId: '',\n name: agent.name,\n hostname: agent.hostname,\n platform: agent.platform,\n architecture: agent.architecture || '',\n version: agent.version || '',\n isActive: agent.isActive,\n status: agent.isActive ? ('ACTIVE' as const) : ('OFFLINE' as const),\n lastHeartbeat: agent.lastHeartbeat || null,\n tunnelUrl: agent.tunnelUrl ?? null,\n tunnelStatus: agent.tunnelStatus ?? null,\n agentApiKey: null,\n aiContext: null,\n configuration: {},\n capabilities: [],\n metadata: {},\n createdAt: agent.createdAt || '',\n updatedAt: '',\n }));\n }, [cachedAgents]);\n\n const vibes = useMemo(() => {\n return cachedVibes.map((vibe) => ({\n id: vibe.id,\n workspaceId: '',\n name: vibe.name,\n description: '',\n type: (vibe.type || 'PROJECT') as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: '',\n parentId: null,\n gitBranch: null,\n gitRemote: null,\n gitStatus: null,\n environmentVariables: {},\n tags: [],\n metadata: {},\n createdAt: '',\n updatedAt: '',\n archivedAt: null,\n // Multi-agent: surface attached agents so Overview cards can render a\n // count badge (\"+N agents\") or single name. Empty when none.\n agents: vibe.agents ?? [],\n }));\n }, [cachedVibes]);\n\n const sessions: Session[] = useMemo(() => {\n return cachedSessions.map((session) => ({\n id: session.id,\n workspaceId: '',\n agentId: session.agentId ?? null,\n vibeId: session.vibeId ?? null,\n name: session.name,\n type: (session.type || 'TERMINAL') as Session['type'],\n status: session.status as Session['status'],\n command: null,\n workingDirectory: null,\n environmentVariables: {},\n autoStart: false,\n pid: null,\n exitCode: null,\n lastOutput: null,\n metadata: {},\n createdAt: session.createdAt || '',\n updatedAt: '',\n agent: undefined,\n vibe: undefined,\n }));\n }, [cachedSessions]);\n\n // Calculate stats from cached totals\n const activeAgents = agents.filter((a) => a.isActive);\n const runningSessions = sessions.filter((s) => s.status === 'RUNNING');\n const activeVibes = vibes.filter((v) => v.status === 'ACTIVE');\n\n const stats = {\n agents: {\n total: totals.agents,\n active: activeAgents.length,\n offline: agents.filter((a) => !a.isActive).length,\n },\n vibes: {\n total: totals.vibes,\n active: activeVibes.length,\n archived: vibes.filter((v) => v.status === 'ARCHIVED').length,\n },\n sessions: {\n total: totals.sessions,\n running: runningSessions.length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n },\n };\n\n // Recent activity - derive from session/agent data (simplified for now)\n const recentActivity = useMemo(() => {\n const activities: Array<{\n id: string;\n type: string;\n title: string;\n description: string;\n timestamp: string;\n color: string;\n }> = [];\n\n // Add recent sessions as activity\n sessions.slice(0, 3).forEach((session) => {\n activities.push({\n id: `session-${session.id}`,\n type: 'session_started',\n title:\n session.status === 'RUNNING'\n ? tr('overviewPage.activity.sessionRunning', 'Session Running')\n : tr('overviewPage.activity.sessionStatus', 'Session {{status}}', {\n status: session.status.toLowerCase(),\n }),\n description: `${session.name}`,\n timestamp: session.createdAt,\n color: session.status === 'RUNNING' ? 'green' : 'blue',\n });\n });\n\n // Add active agents as activity\n activeAgents.slice(0, 2).forEach((agent) => {\n activities.push({\n id: `agent-${agent.id}`,\n type: 'agent_connected',\n title: tr('overviewPage.activity.agentOnline', 'Agent Online'),\n description: tr('overviewPage.activity.agentOnlineDescription', '{{name}} is active', {\n name: agent.name,\n }),\n timestamp: agent.lastHeartbeat || agent.updatedAt,\n color: 'blue',\n });\n });\n\n // Sort by timestamp descending\n return activities\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 6);\n }, [sessions, activeAgents]);\n\n if (\n permissionsLoading ||\n (loading &&\n cachedAgents.length === 0 &&\n cachedVibes.length === 0 &&\n cachedSessions.length === 0)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <LoadingState message={tr('overviewPage.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\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\">\n {tr('common.accessDeniedTitle', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'overviewPage.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const formatTimestamp = (timestamp: string) => {\n if (!timestamp) return tr('overviewPage.time.unknown', 'Unknown');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('overviewPage.time.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('overviewPage.time.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('overviewPage.time.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return tr('overviewPage.time.daysAgo', '{{count}}d ago', {\n count: Math.floor(diff / 86400000),\n });\n };\n\n const getActivityIcon = (type: string) => {\n const icons: Record<string, typeof Activity> = {\n session_started: Play,\n agent_connected: Server,\n vibe_updated: GitBranch,\n note_created: FileText,\n module_installed: Activity,\n };\n const Icon = icons[type] || Activity;\n return <Icon className=\"size-4\" />;\n };\n\n const getActivityColor = (color: string) => {\n const colors: Record<string, string> = {\n green: 'bg-status-success-bg text-status-success-text',\n blue: 'bg-status-info-bg text-status-info-text',\n purple: 'bg-accent-purple-subtle text-accent-purple',\n orange: 'bg-status-warning-bg text-status-warning-text',\n cyan: 'bg-accent-teal-subtle text-accent-teal',\n yellow: 'bg-status-warning-bg text-status-warning-text',\n pink: 'bg-accent-purple-subtle text-accent-purple',\n };\n return colors[color] || 'bg-bg-sunken text-text-secondary';\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('overviewPage.title', 'VibeControls Overview')}\n description={tr(\n 'overviewPage.description',\n 'Monitor and manage your development environments'\n )}\n actions={\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary hover:bg-bg-sunken/80 transition-colors disabled:opacity-50\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n {tr('overviewPage.refresh', 'Refresh')}\n </button>\n }\n />\n\n {/* Friendly fallback when the initial workspace load fails — keeps the\n page header / refresh button rendered above so the user can recover\n without a hard reload. */}\n {dataError && !loading && (\n <ErrorState\n title={tr('overviewPage.loadFailed.title', 'Could not load workspace data')}\n message={tr(\n 'overviewPage.loadFailed.message',\n \"We couldn't load your VibeControls overview. Please try again in a moment.\"\n )}\n onRetry={handleRefresh}\n />\n )}\n\n {/* Agent CLI Setup Banner — shown when no agents exist */}\n {!dataError && agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-overview-setup-banner\" />\n )}\n\n {/* Stats Grid */}\n <MotionList\n data-tour=\"overview-stats\"\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\"\n stagger={0.06}\n >\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.targets', 'Targets')}\n value={loading ? '-' : targetsTotal}\n subtitle={\n loading || targetsTruncated\n ? ''\n : tr('overviewPage.stats.connected', '{{count}} connected', {\n count: targetsConnected,\n })\n }\n icon={<Server className=\"size-6\" />}\n variant={!loading && targetsTotal > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/targets')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.agents', 'Agents')}\n value={loading ? '-' : stats.agents.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.agents.active,\n })\n }\n icon={<Bot className=\"size-6\" />}\n variant={!loading && stats.agents.active > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/agents')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.vibes', 'Vibes')}\n value={loading ? '-' : stats.vibes.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.vibes.active,\n })\n }\n icon={<Zap className=\"size-6\" />}\n onClick={() => handleNavigate('/vibes')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.sessions', 'Sessions')}\n value={loading ? '-' : stats.sessions.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.running', '{{count}} running', {\n count: stats.sessions.running,\n })\n }\n icon={<Terminal className=\"size-6\" />}\n variant={!loading && stats.sessions.running > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/sessions')}\n />\n </MotionItem>\n </MotionList>\n\n {/* Main Content Grid */}\n <MotionList className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\" stagger={0.08}>\n {/* Active Agents */}\n <MotionItem className=\"xl:col-span-2 h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Bot className=\"size-5\" />\n {tr('overviewPage.sections.activeAgents', 'Active Agents')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllAgents', 'View all agents')}\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeAgents.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 gap-3 flex-1 content-start\"\n stagger={0.04}\n >\n {activeAgents.slice(0, 4).map((agent) => (\n <MotionItem key={agent.id}>\n <AgentCard agent={agent} onSelect={(a) => handleNavigate(`/agents/${a.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center text-center py-6 text-text-secondary\">\n <Bot className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noActiveAgents', 'No active agents')}</p>\n <p className=\"text-xs text-text-muted mt-1 mb-3\">\n {tr('overviewPage.empty.installCliHint', 'Install the agent CLI to get started')}\n </p>\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.setupGuide', 'Setup guide')}\n </button>\n <span className=\"text-text-muted\">·</span>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addAgent', 'Add agent')}\n </button>\n </div>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Recent Activity */}\n <MotionItem className=\"h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Activity className=\"size-5\" />\n {tr('overviewPage.sections.recentActivity', 'Recent Activity')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"text-sm text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllActivity', 'View all activity')}\n >\n View all\n </button>\n </div>\n <MotionList className=\"space-y-3 flex-1\" stagger={0.04}>\n {recentActivity.length > 0 ? (\n recentActivity.map((activity) => (\n <MotionItem key={activity.id} variant=\"fadeRight\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-8 rounded-full flex items-center justify-center ${getActivityColor(activity.color)}`}\n >\n {getActivityIcon(activity.type)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {activity.title}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {activity.description}\n </p>\n </div>\n <span className=\"text-xs text-text-secondary flex-shrink-0\">\n {formatTimestamp(activity.timestamp)}\n </span>\n </div>\n </MotionItem>\n ))\n ) : (\n <div className=\"flex h-full flex-col items-center justify-center text-center py-4 text-text-secondary text-sm\">\n <p>{tr('overviewPage.empty.noRecentActivity', 'No recent activity')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.viewAuditLog', 'View audit log')}\n </button>\n </div>\n )}\n </MotionList>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Running Sessions */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Terminal className=\"size-5\" />\n {tr('overviewPage.sections.runningSessions', 'Running Sessions')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {runningSessions.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3\"\n stagger={0.04}\n >\n {runningSessions.slice(0, 6).map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={(s) => handleNavigate(`/sessions/${s.id}`)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Terminal className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noRunningSessions', 'No running sessions')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.startSession', 'Start a session')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Active Vibes */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary flex items-center gap-2\">\n <Zap className=\"size-5\" />\n {tr('overviewPage.sections.activeVibes', 'Active Vibes')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeVibes.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3\"\n stagger={0.04}\n >\n {activeVibes.slice(0, 6).map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard vibe={vibe} onOpen={(v) => handleNavigate(`/vibes/${v.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"text-center py-8 text-text-secondary\">\n <Zap className=\"size-12 mx-auto mb-2 opacity-50\" />\n <p>{tr('overviewPage.empty.noActiveVibes', 'No active vibes')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addVibe', 'Add a vibe')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../src/pages/OverviewPage.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport {\n Bot,\n Zap,\n Terminal,\n FileText,\n Activity,\n Server,\n Play,\n GitBranch,\n RefreshCw,\n} from 'lucide-react';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AgentCard } from '@/components/agents';\nimport { SessionCard } from '@/components/sessions';\nimport { VibeCard } from '@/components/vibes';\nimport {\n PageHeader,\n StatCard,\n Card,\n AgentSetupBanner,\n ErrorState,\n LoadingState,\n} from '@/components/shared';\nimport { useVibeNavigate, useVibeControls } from '@/providers/VibeControlsProvider';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\nimport { useListTargetsQuery } from '@/generated/wspace-operations';\nimport type { Vibe } from '@/components/vibes/types';\nimport type { Session } from '@/components/sessions/types';\n\n/**\n * Overview page showing summary dashboard with stats, active resources, and recent activity\n *\n * This page now uses cached initial data from Apollo instead of independent queries,\n * reducing GraphQL calls from 5 to 0 for the initial render.\n * The refresh button triggers a refetch of the initial data.\n */\nexport function OverviewPage() {\n const handleNavigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n };\n\n // Use cached initial data - reads from Apollo cache, no network request\n // Loading state reflects whether initial data has been loaded\n const {\n agents: cachedAgents,\n vibes: cachedVibes,\n sessions: cachedSessions,\n loading,\n error: dataError,\n totals,\n refetch,\n } = useCachedInitialData();\n\n // Targets is intentionally NOT part of VibeControlsInitialData (it caused a 25s\n // timeout there), so fetch the count with a lightweight standalone query — the\n // same pattern HealthPage uses.\n const { workspaceId } = useVibeControls();\n const targetsCountQuery = useListTargetsQuery({\n variables: { workspaceId: workspaceId || '', limit: 50 },\n skip: !workspaceId,\n fetchPolicy: 'cache-and-network',\n });\n\n const refetchTargets = targetsCountQuery.refetch;\n // The header Refresh button must refetch BOTH the cached initial data and the\n // standalone targets query — otherwise the Targets stat card stays stale after\n // a manual refresh.\n const handleRefresh = useCallback(() => {\n void refetch();\n void refetchTargets();\n }, [refetch, refetchTargets]);\n\n const targetItems: Array<{ status: string }> = targetsCountQuery.data?.targets.items ?? [];\n const targetsTotal = targetsCountQuery.data?.targets.total ?? 0;\n // `targetsConnected` is derived from `targetItems`, which is capped by the\n // query `limit`. When the workspace has more targets than the page size the\n // connected count would only reflect the first page and contradict the\n // server-side `targetsTotal`. Track truncation so we only surface the\n // connected detail when it is accurate for the full set.\n const targetsTruncated = targetsTotal > targetItems.length;\n const targetsConnected = targetItems.filter(\n (t) => t.status === 'connected' || t.status === 'active'\n ).length;\n\n if (dataError) {\n console.error('[overview] failed to load initial workspace data', dataError);\n }\n\n // Map cached data to component types\n // Using type assertion since we know the structure from the GraphQL query\n const agents = useMemo(() => {\n return cachedAgents.map((agent) => ({\n id: agent.id,\n workspaceId: '',\n name: agent.name,\n hostname: agent.hostname,\n platform: agent.platform,\n architecture: agent.architecture || '',\n version: agent.version || '',\n isActive: agent.isActive,\n status: agent.isActive ? ('ACTIVE' as const) : ('OFFLINE' as const),\n lastHeartbeat: agent.lastHeartbeat || null,\n tunnelUrl: agent.tunnelUrl ?? null,\n tunnelStatus: agent.tunnelStatus ?? null,\n agentApiKey: null,\n aiContext: null,\n configuration: {},\n capabilities: [],\n metadata: {},\n createdAt: agent.createdAt || '',\n updatedAt: '',\n }));\n }, [cachedAgents]);\n\n const vibes = useMemo(() => {\n return cachedVibes.map((vibe) => ({\n id: vibe.id,\n workspaceId: '',\n name: vibe.name,\n description: '',\n type: (vibe.type || 'PROJECT') as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: '',\n parentId: null,\n gitBranch: null,\n gitRemote: null,\n gitStatus: null,\n environmentVariables: {},\n tags: [],\n metadata: {},\n createdAt: '',\n updatedAt: '',\n archivedAt: null,\n // Multi-agent: surface attached agents so Overview cards can render a\n // count badge (\"+N agents\") or single name. Empty when none.\n agents: vibe.agents ?? [],\n }));\n }, [cachedVibes]);\n\n const sessions: Session[] = useMemo(() => {\n return cachedSessions.map((session) => ({\n id: session.id,\n workspaceId: '',\n agentId: session.agentId ?? null,\n vibeId: session.vibeId ?? null,\n name: session.name,\n type: (session.type || 'TERMINAL') as Session['type'],\n status: session.status as Session['status'],\n command: null,\n workingDirectory: null,\n environmentVariables: {},\n autoStart: false,\n pid: null,\n exitCode: null,\n lastOutput: null,\n metadata: {},\n createdAt: session.createdAt || '',\n updatedAt: '',\n agent: undefined,\n vibe: undefined,\n }));\n }, [cachedSessions]);\n\n // Calculate stats from cached totals\n const activeAgents = agents.filter((a) => a.isActive);\n const runningSessions = sessions.filter((s) => s.status === 'RUNNING');\n const activeVibes = vibes.filter((v) => v.status === 'ACTIVE');\n\n const stats = {\n agents: {\n total: totals.agents,\n active: activeAgents.length,\n offline: agents.filter((a) => !a.isActive).length,\n },\n vibes: {\n total: totals.vibes,\n active: activeVibes.length,\n archived: vibes.filter((v) => v.status === 'ARCHIVED').length,\n },\n sessions: {\n total: totals.sessions,\n running: runningSessions.length,\n stopped: sessions.filter((s) => s.status === 'STOPPED').length,\n },\n };\n\n // Recent activity - derive from session/agent data (simplified for now)\n const recentActivity = useMemo(() => {\n const activities: Array<{\n id: string;\n type: string;\n title: string;\n description: string;\n timestamp: string;\n color: string;\n }> = [];\n\n // Add recent sessions as activity\n sessions.slice(0, 3).forEach((session) => {\n activities.push({\n id: `session-${session.id}`,\n type: 'session_started',\n title:\n session.status === 'RUNNING'\n ? tr('overviewPage.activity.sessionRunning', 'Session Running')\n : tr('overviewPage.activity.sessionStatus', 'Session {{status}}', {\n status: session.status.toLowerCase(),\n }),\n description: `${session.name}`,\n timestamp: session.createdAt,\n color: session.status === 'RUNNING' ? 'green' : 'blue',\n });\n });\n\n // Add active agents as activity\n activeAgents.slice(0, 2).forEach((agent) => {\n activities.push({\n id: `agent-${agent.id}`,\n type: 'agent_connected',\n title: tr('overviewPage.activity.agentOnline', 'Agent Online'),\n description: tr('overviewPage.activity.agentOnlineDescription', '{{name}} is active', {\n name: agent.name,\n }),\n timestamp: agent.lastHeartbeat || agent.updatedAt,\n color: 'blue',\n });\n });\n\n // Sort by timestamp descending\n return activities\n .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())\n .slice(0, 6);\n }, [sessions, activeAgents]);\n\n if (\n permissionsLoading ||\n (loading &&\n cachedAgents.length === 0 &&\n cachedVibes.length === 0 &&\n cachedSessions.length === 0)\n )\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <LoadingState message={tr('overviewPage.loadingPermissions', 'Loading permissions...')} />\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px] px-4\">\n <div className=\"text-center space-y-2 max-w-sm\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary\">\n {tr('common.accessDeniedTitle', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'overviewPage.accessDeniedDescription',\n \"You don't have permission to access this page.\"\n )}\n </p>\n </div>\n </div>\n );\n\n const formatTimestamp = (timestamp: string) => {\n if (!timestamp) return tr('overviewPage.time.unknown', 'Unknown');\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return tr('overviewPage.time.justNow', 'Just now');\n if (diff < 3600000) {\n return tr('overviewPage.time.minutesAgo', '{{count}}m ago', {\n count: Math.floor(diff / 60000),\n });\n }\n if (diff < 86400000) {\n return tr('overviewPage.time.hoursAgo', '{{count}}h ago', {\n count: Math.floor(diff / 3600000),\n });\n }\n return tr('overviewPage.time.daysAgo', '{{count}}d ago', {\n count: Math.floor(diff / 86400000),\n });\n };\n\n const getActivityIcon = (type: string) => {\n const icons: Record<string, typeof Activity> = {\n session_started: Play,\n agent_connected: Server,\n vibe_updated: GitBranch,\n note_created: FileText,\n module_installed: Activity,\n };\n const Icon = icons[type] || Activity;\n return <Icon className=\"size-4\" />;\n };\n\n const getActivityColor = (color: string) => {\n const colors: Record<string, string> = {\n green: 'bg-status-success-bg-subtle text-status-success-text',\n blue: 'bg-[var(--color-accent-soft)] text-text-primary',\n purple: 'bg-accent-purple-subtle text-text-primary',\n orange: 'bg-status-warning-bg-subtle text-status-warning-text',\n cyan: 'bg-accent-teal-subtle text-accent-teal',\n yellow: 'bg-status-warning-bg-subtle text-status-warning-text',\n pink: 'bg-accent-purple-subtle text-text-primary',\n };\n return colors[color] || 'bg-bg-sunken text-text-secondary';\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('overviewPage.title', 'VibeControls Overview')}\n description={tr(\n 'overviewPage.description',\n 'Monitor and manage your development environments'\n )}\n actions={\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={loading}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm bg-bg-surface border border-border-subtle rounded-button text-text-primary hover:bg-bg-sunken hover:border-border-strong transition-all duration-200 disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} aria-hidden=\"true\" />\n {tr('overviewPage.refresh', 'Refresh')}\n </button>\n }\n />\n\n {/* Friendly fallback when the initial workspace load fails — keeps the\n page header / refresh button rendered above so the user can recover\n without a hard reload. */}\n {dataError && !loading && (\n <ErrorState\n title={tr('overviewPage.loadFailed.title', 'Could not load workspace data')}\n message={tr(\n 'overviewPage.loadFailed.message',\n \"We couldn't load your VibeControls overview. Please try again in a moment.\"\n )}\n onRetry={handleRefresh}\n />\n )}\n\n {/* Agent CLI Setup Banner — shown when no agents exist */}\n {!dataError && agents.length === 0 && !loading && (\n <AgentSetupBanner variant=\"full\" dismissKey=\"vibecontrols-overview-setup-banner\" />\n )}\n\n {/* Stats Grid */}\n <MotionList\n data-tour=\"overview-stats\"\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\"\n stagger={0.06}\n >\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.targets', 'Targets')}\n value={loading ? '-' : targetsTotal}\n subtitle={\n loading || targetsTruncated\n ? ''\n : tr('overviewPage.stats.connected', '{{count}} connected', {\n count: targetsConnected,\n })\n }\n icon={<Server className=\"size-6\" />}\n variant={!loading && targetsTotal > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/targets')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.agents', 'Agents')}\n value={loading ? '-' : stats.agents.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.agents.active,\n })\n }\n icon={<Bot className=\"size-6\" />}\n variant={!loading && stats.agents.active > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/agents')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.vibes', 'Vibes')}\n value={loading ? '-' : stats.vibes.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.active', '{{count}} active', {\n count: stats.vibes.active,\n })\n }\n icon={<Zap className=\"size-6\" />}\n onClick={() => handleNavigate('/vibes')}\n />\n </MotionItem>\n <MotionItem variant=\"fadeScale\">\n <StatCard\n title={tr('overviewPage.stats.sessions', 'Sessions')}\n value={loading ? '-' : stats.sessions.total}\n subtitle={\n loading\n ? ''\n : tr('overviewPage.stats.running', '{{count}} running', {\n count: stats.sessions.running,\n })\n }\n icon={<Terminal className=\"size-6\" />}\n variant={!loading && stats.sessions.running > 0 ? 'success' : 'default'}\n onClick={() => handleNavigate('/sessions')}\n />\n </MotionItem>\n </MotionList>\n\n {/* Main Content Grid */}\n <MotionList className=\"grid grid-cols-1 gap-6 xl:grid-cols-3\" stagger={0.08}>\n {/* Active Agents */}\n <MotionItem className=\"xl:col-span-2 h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.activeAgents', 'Active Agents')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllAgents', 'View all agents')}\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeAgents.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 gap-4 flex-1 content-start\"\n stagger={0.04}\n >\n {activeAgents.slice(0, 4).map((agent) => (\n <MotionItem key={agent.id}>\n <AgentCard agent={agent} onSelect={(a) => handleNavigate(`/agents/${a.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center text-center py-8 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Bot className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noActiveAgents', 'No active agents')}\n </p>\n <p className=\"text-xs text-text-muted mt-1 mb-4\">\n {tr('overviewPage.empty.installCliHint', 'Install the agent CLI to get started')}\n </p>\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate('/getting-started')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.setupGuide', 'Setup guide')}\n </button>\n <span className=\"text-text-muted\">·</span>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/agents')}\n className=\"text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addAgent', 'Add agent')}\n </button>\n </div>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Recent Activity */}\n <MotionItem className=\"h-full\">\n <Card className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Activity className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.recentActivity', 'Recent Activity')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n aria-label={tr('overviewPage.sections.viewAllActivity', 'View all activity')}\n >\n View all\n </button>\n </div>\n <MotionList className=\"space-y-3 flex-1\" stagger={0.04}>\n {recentActivity.length > 0 ? (\n recentActivity.map((activity) => (\n <MotionItem key={activity.id} variant=\"fadeRight\">\n <div className=\"flex items-start gap-3\">\n <div\n className={`flex-shrink-0 size-8 rounded-full flex items-center justify-center ${getActivityColor(activity.color)}`}\n >\n {getActivityIcon(activity.type)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {activity.title}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {activity.description}\n </p>\n </div>\n <span className=\"text-xs text-text-secondary flex-shrink-0\">\n {formatTimestamp(activity.timestamp)}\n </span>\n </div>\n </MotionItem>\n ))\n ) : (\n <div className=\"flex h-full flex-col items-center justify-center text-center py-4 text-text-secondary text-sm\">\n <p>{tr('overviewPage.empty.noRecentActivity', 'No recent activity')}</p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/audit')}\n className=\"mt-2 text-sm text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.viewAuditLog', 'View audit log')}\n </button>\n </div>\n )}\n </MotionList>\n </Card>\n </MotionItem>\n </MotionList>\n\n {/* Running Sessions */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Terminal className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.runningSessions', 'Running Sessions')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {runningSessions.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4\"\n stagger={0.04}\n >\n {runningSessions.slice(0, 6).map((session) => (\n <MotionItem key={session.id}>\n <SessionCard\n session={session}\n onOpen={(s) => handleNavigate(`/sessions/${s.id}`)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-col items-center text-center py-10 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Terminal className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noRunningSessions', 'No running sessions')}\n </p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/sessions')}\n className=\"mt-3 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.startSession', 'Start a session')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n\n {/* Active Vibes */}\n <MotionItem>\n <Card>\n <div className=\"flex items-center justify-between mb-5\">\n <h2 className=\"text-lg font-semibold tracking-tight text-text-primary flex items-center gap-2.5\">\n <span className=\"inline-flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Zap className=\"size-5\" />\n </span>\n {tr('overviewPage.sections.activeVibes', 'Active Vibes')}\n </h2>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.sections.viewAll', 'View all')}\n </button>\n </div>\n {activeVibes.length > 0 ? (\n <MotionList\n className=\"grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4\"\n stagger={0.04}\n >\n {activeVibes.slice(0, 6).map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard vibe={vibe} onOpen={(v) => handleNavigate(`/vibes/${v.id}`)} />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <div className=\"flex flex-col items-center text-center py-10 text-text-secondary\">\n <span className=\"inline-flex size-12 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-primary mb-3\">\n <Zap className=\"size-6\" />\n </span>\n <p className=\"text-base font-semibold text-text-primary\">\n {tr('overviewPage.empty.noActiveVibes', 'No active vibes')}\n </p>\n <button\n type=\"button\"\n onClick={() => handleNavigate('/vibes')}\n className=\"mt-3 text-sm font-medium text-text-link hover:underline\"\n >\n {tr('overviewPage.empty.addVibe', 'Add a vibe')}\n </button>\n </div>\n )}\n </Card>\n </MotionItem>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -88,7 +88,7 @@ function v(e) {
|
|
|
88
88
|
}
|
|
89
89
|
function y(e) {
|
|
90
90
|
switch (e) {
|
|
91
|
-
case "Meta": return "bg-accent-purple-subtle text-
|
|
91
|
+
case "Meta": return "bg-accent-purple-subtle text-text-primary border-accent-purple/30";
|
|
92
92
|
case "Provider": return "bg-status-info-bg text-status-info-text border-status-info-border";
|
|
93
93
|
default: return "bg-bg-sunken text-text-secondary border-border-default";
|
|
94
94
|
}
|
|
@@ -205,14 +205,17 @@ function S() {
|
|
|
205
205
|
children: /* @__PURE__ */ p(g, {
|
|
206
206
|
className: "flex flex-col h-full",
|
|
207
207
|
children: [/* @__PURE__ */ p("div", {
|
|
208
|
-
className: "sticky top-0 z-10 border-b border-border-
|
|
208
|
+
className: "sticky top-0 z-10 border-b border-border-subtle bg-bg-surface px-6 lg:px-8 pt-5 pb-4 space-y-3",
|
|
209
209
|
children: [
|
|
210
210
|
/* @__PURE__ */ p("div", {
|
|
211
211
|
className: "flex items-start gap-3",
|
|
212
|
-
children: [/* @__PURE__ */ f(
|
|
212
|
+
children: [/* @__PURE__ */ f("div", {
|
|
213
|
+
className: "flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)]",
|
|
214
|
+
children: /* @__PURE__ */ f(l, { className: "size-5 text-action-primary-bg" })
|
|
215
|
+
}), /* @__PURE__ */ p("div", {
|
|
213
216
|
className: "min-w-0 flex-1",
|
|
214
217
|
children: [/* @__PURE__ */ f("h1", {
|
|
215
|
-
className: "text-
|
|
218
|
+
className: "text-2xl font-semibold tracking-tight text-text-primary",
|
|
216
219
|
children: n("vibecontrols.plugins.title", "Plugins")
|
|
217
220
|
}), /* @__PURE__ */ f("p", {
|
|
218
221
|
className: "text-sm text-text-secondary mt-0.5",
|