CUB-26: Quick-jump drawer and modal components
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m5s
All checks were successful
Dev Build / build-test (pull_request) Successful in 2m5s
This commit is contained in:
@@ -13,7 +13,16 @@
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.hub-page__placeholder {
|
||||
.hub-page__title {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--cc-on-surface);
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.hub-page__placeholder,
|
||||
.hub-page__empty {
|
||||
color: var(--cc-on-surface-variant);
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
|
||||
@@ -1,15 +1,161 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
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: [],
|
||||
imports: [CommonModule, AgentCardComponent, AgentSessionDrawerComponent],
|
||||
template: `
|
||||
<div class="hub-page">
|
||||
<p class="hub-page__placeholder">Command Hub — Fleet status grid will render here</p>
|
||||
<h1 class="hub-page__title">Command Hub</h1>
|
||||
<div class="hub-page__grid">
|
||||
@for (agent of agents(); track agent.id) {
|
||||
<app-agent-card
|
||||
[status]="agent.status"
|
||||
[task]="agent.currentTask ?? ''"
|
||||
[progress]="agent.taskProgress ?? 0"
|
||||
[sessionKey]="agent.sessionKey"
|
||||
[channel]="agent.channel"
|
||||
[lastActivity]="agent.lastActivity"
|
||||
[agentId]="agent.id"
|
||||
[displayName]="agent.displayName"
|
||||
[role]="agent.role"
|
||||
[errorMessage]="agent.errorMessage ?? ''"
|
||||
(cardClick)="onCardClick($event)"
|
||||
(cardLongPress)="onCardLongPress($event)"
|
||||
/>
|
||||
} @empty {
|
||||
<p class="hub-page__empty">No agents online</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent Session Drawer -->
|
||||
<app-agent-session-drawer
|
||||
[isMobile]="isMobile()"
|
||||
(openSession)="onOpenSession($event)"
|
||||
(pinToDashboard)="onPinToDashboard($event)"
|
||||
(drawerClose)="onDrawerClose()"
|
||||
/>
|
||||
`,
|
||||
styleUrl: './hub-page.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class HubPageComponent {}
|
||||
export class HubPageComponent {
|
||||
@ViewChild(AgentSessionDrawerComponent) sessionDrawer!: AgentSessionDrawerComponent;
|
||||
|
||||
readonly isMobile = signal(false);
|
||||
|
||||
/** Stub agent data (TODO: wire to AgentStatusService / SignalR). */
|
||||
readonly agents = signal<AgentCardData[]>([
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user