import { ChangeDetectionStrategy, Component, signal, ViewChild } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AgentCardComponent } from '../../command-hub/components/agent-card/agent-card.component'; import { AgentSessionDrawerComponent } from '../../components/agent-session-drawer/index'; import { AgentCardData } from '../../models/agent.model'; // ============================================================================ // Hub Page — Fleet status grid // CUB-26: Integrates AgentCard click/long-press with session drawer. // ============================================================================ @Component({ selector: 'app-hub-page', standalone: true, imports: [CommonModule, AgentCardComponent, AgentSessionDrawerComponent], template: `

Command Hub

@for (agent of agents(); track agent.id) { } @empty {

No agents online

}
`, styleUrl: './hub-page.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class HubPageComponent { @ViewChild(AgentSessionDrawerComponent) sessionDrawer!: AgentSessionDrawerComponent; readonly isMobile = signal(false); /** Stub agent data (TODO: wire to AgentStatusService / SignalR). */ readonly agents = signal([ { id: 'otto', displayName: 'Otto', role: 'Orchestrator Agent', status: 'active', currentTask: 'Reviewing PR #42', taskProgress: 67, taskElapsed: '04m 12s', sessionKey: 'agent:otto:slack:CUB-42:abc123', channel: 'slack', lastActivity: new Date(), }, { id: 'rex', displayName: 'Rex', role: 'Frontend Agent', status: 'thinking', currentTask: 'Building agent session drawer', taskProgress: 40, taskElapsed: '02m 30s', sessionKey: 'agent:rex:telegram:CUB-26:def456', channel: 'telegram', lastActivity: new Date(Date.now() - 30000), }, { id: 'dex', displayName: 'Dex', role: 'Backend Agent', status: 'idle', currentTask: undefined, taskProgress: undefined, taskElapsed: undefined, sessionKey: 'agent:dex:slack:CUB-53:ghi789', channel: 'slack', lastActivity: new Date(Date.now() - 300000), }, { id: 'hex', displayName: 'Hex', role: 'Database Agent', status: 'error', currentTask: 'Migration failed — rollback initiated', taskProgress: 0, taskElapsed: '00m 45s', sessionKey: 'agent:hex:slack:CUB-56:jkl012', channel: 'slack', lastActivity: new Date(Date.now() - 60000), errorMessage: 'Connection timeout to database server', }, { id: 'nano', displayName: 'Nano', role: 'ESP32 Agent', status: 'offline', currentTask: undefined, taskProgress: undefined, taskElapsed: undefined, sessionKey: 'agent:nano:mqtt:CUB-48:mno345', channel: 'mqtt', lastActivity: new Date(Date.now() - 86400000), }, ]); constructor() { // Detect mobile viewport if (typeof window !== 'undefined') { const mql = window.matchMedia('(max-width: 599px)'); this.isMobile.set(mql.matches); mql.addEventListener('change', (e) => this.isMobile.set(e.matches)); } } /** Card click → open session drawer with agent details. */ onCardClick(sessionKey: string): void { const agent = this.agents().find((a) => a.sessionKey === sessionKey); if (agent) { this.sessionDrawer?.open(agent); } } /** Long-press on card → bypass drawer, go directly to session log. */ onCardLongPress(sessionKey: string): void { console.log('[Hub] Long press — navigate to session log:', sessionKey); // TODO: Navigate directly to session log page when sessions route is implemented } /** Open full session from drawer action button. */ onOpenSession(sessionKey: string): void { console.log('[Hub] Open full session:', sessionKey); // TODO: Navigate to full session view } /** Pin agent to dashboard from drawer action button. */ onPinToDashboard(sessionKey: string): void { console.log('[Hub] Pin to dashboard:', sessionKey); // TODO: Implement pin-to-dashboard } /** Drawer closed. */ onDrawerClose(): void { // No-op for now — drawer is self-managing } }