// ============================================================================ // Task Progress Bar Component // Per CUB-44: Determinate progress bar with optional elapsed time display. // Uses Angular Material mat-progress-bar in determinate mode with tactical // dark theme styling via CSS custom properties. // ============================================================================ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatProgressBarModule } from '@angular/material/progress-bar'; /** * Displays a determinate progress bar with an optional elapsed time indicator. * * Usage: * ```html * * * ``` */ @Component({ selector: 'app-task-progress-bar', standalone: true, imports: [CommonModule, MatProgressBarModule], templateUrl: './task-progress-bar.component.html', styleUrl: './task-progress-bar.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class TaskProgressBarComponent implements OnInit, OnDestroy { // --------------------------------------------------------------------------- // Inputs // --------------------------------------------------------------------------- /** Current progress percentage (0–100). Required. */ @Input({ required: true }) progress!: number; /** Whether to show elapsed time next to the percentage. Defaults to false. */ @Input() showElapsed = false; // --------------------------------------------------------------------------- // Internal state // --------------------------------------------------------------------------- /** Timestamp when the component initialized — used for elapsed calculation. */ startTime = Date.now(); /** Formatted elapsed time string, e.g. "2m 15s ago". */ elapsedText = ''; /** Interval timer for updating the elapsed display. */ private timer: ReturnType | null = null; constructor(private cdr: ChangeDetectorRef) {} // --------------------------------------------------------------------------- // Lifecycle // --------------------------------------------------------------------------- ngOnInit(): void { this.updateElapsed(); if (this.showElapsed) { // Update elapsed time every second this.timer = setInterval(() => { this.updateElapsed(); this.cdr.markForCheck(); }, 1000); } } ngOnDestroy(): void { if (this.timer) { clearInterval(this.timer); this.timer = null; } } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /** Clamp progress to 0–100 for safety. */ get clampedProgress(): number { return Math.max(0, Math.min(100, this.progress ?? 0)); } /** Recalculate the elapsed time string. */ private updateElapsed(): void { const elapsedMs = Date.now() - this.startTime; const totalSeconds = Math.floor(elapsedMs / 1000); const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; if (minutes > 0) { this.elapsedText = `${minutes}m ${seconds}s ago`; } else { this.elapsedText = `${seconds}s ago`; } } }