import { ChangeDetectionStrategy, Component, computed, inject, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { FilamentService } from '../../services/filament.service'; import { classifyStockLevel } from '../../models/filament.model'; /** * InventorySummaryComponent — dashboard summary for filament inventory metrics. * * Displays three key metrics driven by FilamentService: * - Total filament count (active spools) * - Low stock count (spools classified as 'low' or 'critical') * - Estimated total filament value (sum of purchase prices for active spools) * * All values update dynamically whenever FilamentService data changes. */ @Component({ selector: 'app-inventory-summary', standalone: true, imports: [ CommonModule, MatIconModule, MatTooltipModule, ], templateUrl: './inventory-summary.component.html', styleUrl: './inventory-summary.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class InventorySummaryComponent { private readonly filamentService = inject(FilamentService); /** Computed: total number of active filament spools */ readonly totalFilamentCount = computed(() => this.filamentService.filaments().filter((f) => f.isActive).length ); /** Computed: count of spools at low or critical stock levels */ readonly lowStockCount = computed(() => this.filamentService.filaments().filter((f) => { const level = classifyStockLevel(f); return level === 'low' || level === 'critical'; }).length ); /** Computed: count of spools at critical stock level only */ readonly criticalStockCount = computed(() => this.filamentService.filaments().filter( (f) => classifyStockLevel(f) === 'critical' ).length ); /** Computed: estimated total value of all active spools with a recorded price */ readonly estimatedTotalValue = computed(() => this.filamentService .filaments() .filter((f) => f.isActive && f.purchasePrice !== null) .reduce((sum, f) => sum + (f.purchasePrice ?? 0), 0) ); /** Computed: whether there are low-stock spools to highlight */ readonly hasLowStock = computed(() => this.lowStockCount() > 0); /** Computed: whether there are critical-stock spools */ readonly hasCriticalStock = computed(() => this.criticalStockCount() > 0); /** Format a currency value for display */ formatCurrency(value: number): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 2, }).format(value); } }