123 lines
4.8 KiB
HTML
123 lines
4.8 KiB
HTML
<!-- Filament Inventory Table — with low stock indicators -->
|
|
<div class="filament-table-container" role="region" aria-label="Filament inventory">
|
|
|
|
<!-- Low Stock Alert Banner — shown when critical or low stock spools exist -->
|
|
@if (criticalCount() > 0) {
|
|
<div class="alert-banner critical" role="alert">
|
|
<mat-icon aria-hidden="true">error</mat-icon>
|
|
<span>{{ criticalCount() }} spool{{ criticalCount() > 1 ? 's' : '' }} critically low (≤10% remaining)</span>
|
|
</div>
|
|
} @else if (lowStockCount() > 0) {
|
|
<div class="alert-banner low" role="alert">
|
|
<mat-icon aria-hidden="true">warning</mat-icon>
|
|
<span>{{ lowStockCount() }} spool{{ lowStockCount() > 1 ? 's' : '' }} running low (≤25% remaining)</span>
|
|
</div>
|
|
}
|
|
|
|
<!-- Filament Table -->
|
|
<table mat-table
|
|
[dataSource]="sortedFilaments()"
|
|
matSort
|
|
(matSortChange)="sortData($event)"
|
|
class="filament-table"
|
|
aria-label="Filament inventory table">
|
|
|
|
<!-- Color Column -->
|
|
<ng-container matColumnDef="color">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="color">Color</th>
|
|
<td mat-cell *matCellDef="let filament">
|
|
<span class="color-swatch"
|
|
[style.background-color]="filament.colorHex"
|
|
[matTooltip]="filament.colorName"
|
|
matTooltipPosition="after"
|
|
[attr.aria-label]="filament.colorName">
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Material Column -->
|
|
<ng-container matColumnDef="material">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="material">Material</th>
|
|
<td mat-cell *matCellDef="let filament">
|
|
<span class="material-name">{{ filament.materialBaseName }}</span>
|
|
@if (filament.materialModifierName) {
|
|
<span class="material-modifier"> {{ filament.materialModifierName }}</span>
|
|
}
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Brand Column -->
|
|
<ng-container matColumnDef="brand">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="brand">Brand</th>
|
|
<td mat-cell *matCellDef="let filament">{{ filament.brand }}</td>
|
|
</ng-container>
|
|
|
|
<!-- Serial Column -->
|
|
<ng-container matColumnDef="serial">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="serial">Serial</th>
|
|
<td mat-cell *matCellDef="let filament" class="serial-cell">{{ filament.spoolSerial }}</td>
|
|
</ng-container>
|
|
|
|
<!-- Remaining Weight Column -->
|
|
<ng-container matColumnDef="remaining">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="remaining">Remaining</th>
|
|
<td mat-cell *matCellDef="let filament">
|
|
<div class="remaining-cell">
|
|
<span class="remaining-text">
|
|
{{ formatWeight(filament.weightRemainingGrams) }} / {{ formatWeight(filament.weightTotalGrams) }}
|
|
</span>
|
|
<mat-progress-bar
|
|
mode="determinate"
|
|
[value]="getRemainingPercent(filament)"
|
|
[ngClass]="classifyStockLevel(filament)"
|
|
[matTooltip]="getRemainingPercent(filament).toFixed(0) + '% remaining'"
|
|
matTooltipPosition="below">
|
|
</mat-progress-bar>
|
|
</div>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Stock Level Indicator Column -->
|
|
<ng-container matColumnDef="stockLevel">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="stockLevel">Stock</th>
|
|
<td mat-cell *matCellDef="let filament">
|
|
@let level = classifyStockLevel(filament);
|
|
<mat-chip-set aria-label="Stock level">
|
|
<mat-chip
|
|
[ngClass]="level"
|
|
[matTooltip]="stockLevelLabel(level) + ' — ' + getRemainingPercent(filament).toFixed(0) + '% remaining'"
|
|
matTooltipPosition="below">
|
|
<mat-icon matChipStart [ngClass]="level">{{ stockLevelIcon(level) }}</mat-icon>
|
|
<span>{{ stockLevelLabel(level) }}</span>
|
|
</mat-chip>
|
|
</mat-chip-set>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Status Column -->
|
|
<ng-container matColumnDef="status">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header="status">Status</th>
|
|
<td mat-cell *matCellDef="let filament">
|
|
<span class="status-badge"
|
|
[class.active]="filament.isActive"
|
|
[class.inactive]="!filament.isActive">
|
|
{{ filament.isActive ? 'Active' : 'Inactive' }}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<tr mat-header-row *matHeaderRowDef="columns()"></tr>
|
|
<tr mat-row *matRowDef="let row; columns: columns();"
|
|
[class.row-critical]="classifyStockLevel(row) === 'critical'"
|
|
[class.row-low]="classifyStockLevel(row) === 'low'">
|
|
</tr>
|
|
</table>
|
|
|
|
<!-- Empty state -->
|
|
@if (filaments().length === 0) {
|
|
<div class="empty-state" role="status">
|
|
<mat-icon aria-hidden="true">inventory_2</mat-icon>
|
|
<p>No filament spools found</p>
|
|
</div>
|
|
}
|
|
</div> |