diff --git a/frontend/src/app/components/agent-card/agent-card.component.html b/frontend/src/app/components/agent-card/agent-card.component.html index ebc18b0..12cbd71 100644 --- a/frontend/src/app/components/agent-card/agent-card.component.html +++ b/frontend/src/app/components/agent-card/agent-card.component.html @@ -1,6 +1,7 @@
- +
- - - {{ statusLabel() }} - + +
@if (channel()) { @@ -39,33 +36,23 @@

{{ task() }}

} - + @if (showProgress()) { -
- -
- {{ progress() }}% -
-
+ } - +
\ No newline at end of file diff --git a/frontend/src/app/components/agent-card/agent-card.component.scss b/frontend/src/app/components/agent-card/agent-card.component.scss index ba53328..a6f8716 100644 --- a/frontend/src/app/components/agent-card/agent-card.component.scss +++ b/frontend/src/app/components/agent-card/agent-card.component.scss @@ -1,7 +1,9 @@ // ============================================================================ -// Agent Card Styles — M3 Tactical Dark -// Per spec Section 7.3: Agent Card Component -// Section 7.5: Animation Specs +// Agent Card Styles — M3 Tactical Dark (Final Integration, CUB-45) +// Uses sub-components: +// - AgentStatusBadge (CUB-48) for status display +// - TaskProgressBar (CUB-44) for progress +// - QuickJumpButton (CUB-46) for navigation // ============================================================================ // --------------------------------------------------------------------------- @@ -78,17 +80,6 @@ gap: 8px; } -.agent-card__dot { - flex-shrink: 0; -} - -.agent-card__status-label { - font-size: 14px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; -} - .agent-card__meta { display: flex; align-items: center; @@ -129,23 +120,11 @@ } // --------------------------------------------------------------------------- -// Progress Bar +// Task Progress Bar (CUB-44 sub-component) +// Override the sub-component's progress bar colors to match status color // --------------------------------------------------------------------------- -.agent-card__progress { - display: flex; - flex-direction: column; - gap: 4px; -} - -.agent-card__progress-info { - display: flex; - justify-content: flex-end; - font-size: 12px; - color: var(--cc-on-surface-variant); -} - -// Override M3 progress bar track to use status color -.agent-card__progress .mat-mdc-progress-bar { +:host ::ng-deep .task-progress-bar .mat-mdc-progress-bar, +.agent-card .mat-mdc-progress-bar { --mdc-linear-progress-active-indicator-color: var(--agent-status-color); --mdc-linear-progress-track-color: var(--agent-status-bg); } @@ -172,53 +151,6 @@ min-width: 0; } -// --------------------------------------------------------------------------- -// Quick-Jump Button — M3 FilledTonalIconButton style -// Per spec: → Jump button using M3 FilledTonalIconButton -// --------------------------------------------------------------------------- -.agent-card__jump { - // M3 FilledTonal style: secondary container color - --mdc-icon-button-icon-color: var(--cc-on-surface); - background-color: var(--cc-surface-container-high); - border-radius: 50%; - width: 40px; - height: 40px; - flex-shrink: 0; - - // State layer overlay on hover (8% primary) - &::before { - content: ''; - position: absolute; - inset: 0; - border-radius: 50%; - background-color: rgba(255, 255, 255, 0.08); - opacity: 0; - transition: opacity 150ms ease; - pointer-events: none; - } - - &:hover::before { - opacity: 1; - } - - // Focus-visible ring - &:focus-visible { - outline: 3px solid var(--status-active); - outline-offset: 2px; - } - - // Active state - &:active { - transform: scale(0.95); - } - - .mat-icon { - font-size: 20px; - width: 20px; - height: 20px; - } -} - // --------------------------------------------------------------------------- // Status-specific card backgrounds (subtle tint) // --------------------------------------------------------------------------- @@ -236,15 +168,6 @@ // Idle and offline use default surface-container -// --------------------------------------------------------------------------- -// Touch-First Targets -// --------------------------------------------------------------------------- -// Minimum touch target: 48px (M3 standard) -.agent-card__jump { - min-width: 48px; - min-height: 48px; -} - // --------------------------------------------------------------------------- // Accessibility: Reduced Motion // --------------------------------------------------------------------------- @@ -252,14 +175,6 @@ .agent-card { transition: none; } - - .agent-card__jump { - transition: none; - - &:active { - transform: none; - } - } } // --------------------------------------------------------------------------- diff --git a/frontend/src/app/components/agent-card/agent-card.component.ts b/frontend/src/app/components/agent-card/agent-card.component.ts index 6ccc7be..28dfe71 100644 --- a/frontend/src/app/components/agent-card/agent-card.component.ts +++ b/frontend/src/app/components/agent-card/agent-card.component.ts @@ -1,19 +1,31 @@ // ============================================================================ -// Agent Card Component -// Per spec Section 7.3: Agent Card Component Interface -// Section 7.5: Animation Specs +// Agent Card Component — Final Integration +// Per CUB-45: Compose AgentCard from all sub-components. +// - AgentStatusBadge (CUB-48) for status display +// - TaskProgressBar (CUB-44) for progress indication +// - QuickJumpButton (CUB-46) for session navigation +// Layout: left-border accent, aria-labels, role="article" // ============================================================================ -import { ChangeDetectionStrategy, Component, input, computed } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Output, input, computed } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatIconModule } from '@angular/material/icon'; -import { MatButtonModule } from '@angular/material/button'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { AgentStatus } from '../../models/agent.model'; + +// Sub-components (CUB-48, CUB-44, CUB-46) +import { AgentStatusBadgeComponent } from '../agent-status-badge'; +import { TaskProgressBarComponent } from '../task-progress-bar'; +import { QuickJumpButtonComponent } from '../quick-jump-button'; + +import { AgentStatus } from '../../models'; /** * AgentCard displays a single agent's status in the Command Hub grid. * + * Composes three sub-components: + * - AgentStatusBadge: colored pill with pulse animation (CUB-48) + * - TaskProgressBar: determinate progress with optional elapsed time (CUB-44) + * - QuickJumpButton: M3 FilledTonal icon button for session navigation (CUB-46) + * * Inputs: * - status: AgentStatus — current agent status * - task: string — current task description @@ -22,9 +34,14 @@ import { AgentStatus } from '../../models/agent.model'; * - channel: string — communication channel (e.g., "telegram") * - lastActivity: Date — timestamp of last activity * + * Outputs: + * - jumpClick: string — emitted when Quick-Jump button is clicked, carries sessionKey + * * Accessibility: * - role="article" on the card element - * - aria-labels for status badge and interactive elements + * - aria-label on the card summarizing status + * - aria-label on the progress bar (via TaskProgressBar) + * - aria-label on the Quick-Jump button (via QuickJumpButton) * - focus-visible outlines for keyboard navigation */ @Component({ @@ -33,15 +50,21 @@ import { AgentStatus } from '../../models/agent.model'; imports: [ CommonModule, MatIconModule, - MatButtonModule, - MatProgressBarModule, MatTooltipModule, + // Sub-components + AgentStatusBadgeComponent, + TaskProgressBarComponent, + QuickJumpButtonComponent, ], templateUrl: './agent-card.component.html', styleUrl: './agent-card.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class AgentCardComponent { + // --------------------------------------------------------------------------- + // Inputs (all 6 required by CUB-45 definition of done) + // --------------------------------------------------------------------------- + /** Current agent status */ readonly status = input.required(); @@ -60,6 +83,17 @@ export class AgentCardComponent { /** Timestamp of last activity */ readonly lastActivity = input(new Date()); + // --------------------------------------------------------------------------- + // Outputs + // --------------------------------------------------------------------------- + + /** Emitted when the Quick-Jump button is clicked with the session key. */ + @Output() readonly jumpClick = new EventEmitter(); + + // --------------------------------------------------------------------------- + // Computed values + // --------------------------------------------------------------------------- + /** Map status to CSS custom property name for dynamic color binding */ readonly statusColorVar = computed(() => { const map: Record = { @@ -84,7 +118,7 @@ export class AgentCardComponent { return map[this.status()]; }); - /** Human-readable status label */ + /** Human-readable status label (delegates to AgentStatusBadgeComponent) */ readonly statusLabel = computed(() => { const map: Record = { active: 'Active', @@ -96,9 +130,6 @@ export class AgentCardComponent { return map[this.status()]; }); - /** CSS class for status dot animation */ - readonly statusDotClass = computed(() => `status-dot--${this.status()}`); - /** Format last activity as relative time string */ readonly lastActivityText = computed(() => { const now = Date.now(); diff --git a/frontend/src/app/components/agent-card/index.ts b/frontend/src/app/components/agent-card/index.ts index f9c7df5..30b3caf 100644 --- a/frontend/src/app/components/agent-card/index.ts +++ b/frontend/src/app/components/agent-card/index.ts @@ -1 +1,6 @@ +// ============================================================================ +// Agent Card — Barrel Export +// CUB-45: Final integration with sub-components +// ============================================================================ + export { AgentCardComponent } from './agent-card.component'; \ No newline at end of file diff --git a/frontend/src/app/components/index.ts b/frontend/src/app/components/index.ts index 7e48ebb..6cf0ef0 100644 --- a/frontend/src/app/components/index.ts +++ b/frontend/src/app/components/index.ts @@ -1 +1,8 @@ -export * from './quick-jump-button/quick-jump-button.component'; \ No newline at end of file +// ============================================================================ +// Components Barrel Export +// ============================================================================ + +export * from './agent-card'; +export * from './agent-status-badge'; +export * from './task-progress-bar'; +export * from './quick-jump-button'; \ No newline at end of file diff --git a/frontend/src/app/components/quick-jump-button/index.ts b/frontend/src/app/components/quick-jump-button/index.ts new file mode 100644 index 0000000..72518de --- /dev/null +++ b/frontend/src/app/components/quick-jump-button/index.ts @@ -0,0 +1,6 @@ +// ============================================================================ +// Quick-Jump Button — Barrel Export +// CUB-46 +// ============================================================================ + +export { QuickJumpButtonComponent } from './quick-jump-button.component'; \ No newline at end of file diff --git a/frontend/src/app/components/quick-jump-button/quick-jump-button.component.ts b/frontend/src/app/components/quick-jump-button/quick-jump-button.component.ts index 899fc1d..927dc9e 100644 --- a/frontend/src/app/components/quick-jump-button/quick-jump-button.component.ts +++ b/frontend/src/app/components/quick-jump-button/quick-jump-button.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { MatIconButton } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; @@ -20,12 +20,13 @@ import { MatIcon } from '@angular/material/icon'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class QuickJumpButtonComponent { + /** The session key to navigate to. Set by the parent agent card. */ + @Input() + sessionKey = ''; + /** Emitted when the button is clicked, carrying the session key for navigation. */ @Output() jumpClick = new EventEmitter(); - /** The session key to navigate to. Set by the parent agent card. */ - sessionKey = ''; - onJumpClick(): void { this.jumpClick.emit(this.sessionKey); }