import { ChangeDetectionStrategy, Component, signal, computed, ViewChild } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatChipsModule } from '@angular/material/chips'; 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'; import { AgentStatus } from '../../models/agent.model'; /** * Filter options for the hub page agent card grid. * Per CUB-27: "Filter chip group (All, Active, Error, etc.) with horizontal scroll on mobile" */ export type AgentFilter = 'all' | AgentStatus; @Component({ selector: 'app-hub-page', standalone: true, imports: [CommonModule, MatChipsModule, AgentCardComponent, AgentSessionDrawerComponent], templateUrl: './hub-page.component.html', styleUrl: './hub-page.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class HubPageComponent { @ViewChild(AgentSessionDrawerComponent) sessionDrawer!: AgentSessionDrawerComponent; readonly isMobile = signal(false); protected readonly filters: { label: string; value: AgentFilter }[] = [ { label: 'All', value: 'all' }, { label: 'Active', value: 'active' }, { label: 'Idle', value: 'idle' }, { label: 'Thinking', value: 'thinking' }, { label: 'Error', value: 'error' }, { label: 'Offline', value: 'offline' }, ]; protected readonly activeFilter = signal('all'); /** 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 responsive layout', taskProgress: 40, taskElapsed: '02m 30s', sessionKey: 'agent:rex:telegram:CUB-27: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), }, ]); protected readonly filteredAgents = computed(() => { const filter = this.activeFilter(); if (filter === 'all') return this.agents(); return this.agents().filter(a => a.status === filter); }); 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)); } } protected selectFilter(filter: AgentFilter): void { this.activeFilter.set(filter); } /** 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 } }