CUB-43: add inventory dashboard summary component with FilamentService
This commit is contained in:
176
frontend/src/app/services/filament.service.ts
Normal file
176
frontend/src/app/services/filament.service.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { Injectable, signal, computed } from '@angular/core';
|
||||
import { Filament, classifyStockLevel } from '../models/filament.model';
|
||||
|
||||
/**
|
||||
* FilamentService — shared reactive state for filament inventory.
|
||||
*
|
||||
* Provides a single source of truth for filament data across components.
|
||||
* Both FilamentTableComponent and InventorySummaryComponent read from this service.
|
||||
* Data is updated via setFilaments() — called by SignalR handlers or HTTP load.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FilamentService {
|
||||
/** Primary data store — reactive signal */
|
||||
private readonly _filaments = signal<Filament[]>([
|
||||
{
|
||||
id: '1',
|
||||
materialBaseId: 'm1',
|
||||
materialBaseName: 'PLA',
|
||||
materialFinishId: 'f1',
|
||||
materialFinishName: 'Basic',
|
||||
materialModifierId: null,
|
||||
materialModifierName: null,
|
||||
brand: 'Bambu Lab',
|
||||
colorName: 'White',
|
||||
colorHex: '#F5F5F5',
|
||||
weightTotalGrams: 1000,
|
||||
weightRemainingGrams: 850,
|
||||
filamentDiameterMm: 1.75,
|
||||
spoolSerial: 'SN-001',
|
||||
purchasePrice: 25.00,
|
||||
purchaseDate: '2026-01-15T00:00:00Z',
|
||||
isActive: true,
|
||||
createdAt: '2026-01-15T00:00:00Z',
|
||||
updatedAt: '2026-04-20T00:00:00Z',
|
||||
qrCodeUrl: '',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
materialBaseId: 'm2',
|
||||
materialBaseName: 'PETG',
|
||||
materialFinishId: 'f2',
|
||||
materialFinishName: 'Matte',
|
||||
materialModifierId: 'mod1',
|
||||
materialModifierName: 'Carbon Fiber',
|
||||
brand: 'Polymaker',
|
||||
colorName: 'Fire Engine Red',
|
||||
colorHex: '#FF0000',
|
||||
weightTotalGrams: 1000,
|
||||
weightRemainingGrams: 80,
|
||||
filamentDiameterMm: 1.75,
|
||||
spoolSerial: 'SN-002',
|
||||
purchasePrice: 35.00,
|
||||
purchaseDate: '2026-02-01T00:00:00Z',
|
||||
isActive: true,
|
||||
createdAt: '2026-02-01T00:00:00Z',
|
||||
updatedAt: '2026-04-25T00:00:00Z',
|
||||
qrCodeUrl: '',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
materialBaseId: 'm1',
|
||||
materialBaseName: 'PLA',
|
||||
materialFinishId: 'f1',
|
||||
materialFinishName: 'Basic',
|
||||
materialModifierId: null,
|
||||
materialModifierName: null,
|
||||
brand: 'eSun',
|
||||
colorName: 'Sky Blue',
|
||||
colorHex: '#87CEEB',
|
||||
weightTotalGrams: 1000,
|
||||
weightRemainingGrams: 200,
|
||||
filamentDiameterMm: 1.75,
|
||||
spoolSerial: 'SN-003',
|
||||
purchasePrice: 20.00,
|
||||
purchaseDate: '2026-03-10T00:00:00Z',
|
||||
isActive: true,
|
||||
createdAt: '2026-03-10T00:00:00Z',
|
||||
updatedAt: '2026-04-26T00:00:00Z',
|
||||
qrCodeUrl: '',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
materialBaseId: 'm3',
|
||||
materialBaseName: 'ABS',
|
||||
materialFinishId: 'f1',
|
||||
materialFinishName: 'Basic',
|
||||
materialModifierId: null,
|
||||
materialModifierName: null,
|
||||
brand: 'Hatchbox',
|
||||
colorName: 'Black',
|
||||
colorHex: '#1A1A1A',
|
||||
weightTotalGrams: 1000,
|
||||
weightRemainingGrams: 450,
|
||||
filamentDiameterMm: 1.75,
|
||||
spoolSerial: 'SN-004',
|
||||
purchasePrice: 22.00,
|
||||
purchaseDate: null,
|
||||
isActive: true,
|
||||
createdAt: '2026-01-20T00:00:00Z',
|
||||
updatedAt: '2026-04-18T00:00:00Z',
|
||||
qrCodeUrl: '',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
materialBaseId: 'm1',
|
||||
materialBaseName: 'PLA',
|
||||
materialFinishId: 'f3',
|
||||
materialFinishName: 'Silk',
|
||||
materialModifierId: null,
|
||||
materialModifierName: null,
|
||||
brand: 'Overturn',
|
||||
colorName: 'Gold',
|
||||
colorHex: '#FFD700',
|
||||
weightTotalGrams: 500,
|
||||
weightRemainingGrams: 15,
|
||||
filamentDiameterMm: 1.75,
|
||||
spoolSerial: 'SN-005',
|
||||
purchasePrice: 28.00,
|
||||
purchaseDate: null,
|
||||
isActive: false,
|
||||
createdAt: '2025-12-01T00:00:00Z',
|
||||
updatedAt: '2026-04-01T00:00:00Z',
|
||||
qrCodeUrl: '',
|
||||
},
|
||||
]);
|
||||
|
||||
/** Public read-only view of all filaments */
|
||||
readonly filaments = this._filaments.asReadonly();
|
||||
|
||||
/**
|
||||
* Replace the full filament list.
|
||||
* Called by SignalR handlers, HTTP responses, or test setup.
|
||||
*/
|
||||
setFilaments(data: Filament[]): void {
|
||||
this._filaments.set(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or insert a single filament by id.
|
||||
* Called when a real-time SignalR update arrives for one spool.
|
||||
*/
|
||||
upsertFilament(updated: Filament): void {
|
||||
this._filaments.update((current) => {
|
||||
const idx = current.findIndex((f) => f.id === updated.id);
|
||||
if (idx === -1) {
|
||||
return [...current, updated];
|
||||
}
|
||||
const copy = [...current];
|
||||
copy[idx] = updated;
|
||||
return copy;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a filament by id.
|
||||
* Called when a spool is deleted via real-time event.
|
||||
*/
|
||||
removeFilament(id: string): void {
|
||||
this._filaments.update((current) => current.filter((f) => f.id !== id));
|
||||
}
|
||||
|
||||
/** Computed: count of active spools */
|
||||
readonly activeCount = computed(() =>
|
||||
this._filaments().filter((f) => f.isActive).length
|
||||
);
|
||||
|
||||
/** Computed: count of low or critical stock spools */
|
||||
readonly lowStockCount = computed(() =>
|
||||
this._filaments().filter((f) => {
|
||||
const level = classifyStockLevel(f);
|
||||
return level === 'low' || level === 'critical';
|
||||
}).length
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user