/** * Filament model matching the Extrudex backend FilamentResponse DTO. * Used for displaying spool inventory in the filament table UI. */ export interface Filament { /** Unique identifier for the filament spool. */ id: string; /** Foreign key to the base material. */ materialBaseId: string; /** Name of the base material (e.g., "PLA", "PETG"). */ materialBaseName: string; /** Foreign key to the material finish. */ materialFinishId: string; /** Name of the material finish (e.g., "Basic", "Matte"). */ materialFinishName: string; /** Foreign key to the optional material modifier. */ materialModifierId: string | null; /** Name of the material modifier (e.g., "Carbon Fiber"). Null if none. */ materialModifierName: string | null; /** Brand name (e.g., "Bambu Lab", "Polymaker"). */ brand: string; /** Human-readable color name (e.g., "Fire Engine Red"). */ colorName: string; /** Hex color code (e.g., "#FF0000"). */ colorHex: string; /** Total spool weight in grams when full. */ weightTotalGrams: number; /** Current remaining weight in grams. */ weightRemainingGrams: number; /** Filament diameter in millimeters. Typically 1.75mm. */ filamentDiameterMm: number; /** Manufacturer-assigned serial number. */ spoolSerial: string; /** Purchase price per spool. Null if not tracked. */ purchasePrice: number | null; /** Date the spool was purchased or received. */ purchaseDate: string | null; /** Whether the spool is currently active and available. */ isActive: boolean; /** Timestamp when this record was created (UTC). */ createdAt: string; /** Timestamp when this record was last updated (UTC). */ updatedAt: string; /** URL to the QR code image for this spool. */ qrCodeUrl: string; } /** * Stock level classification for low stock indicators. * - critical: ≤ 10% remaining * - low: ≤ 25% remaining * - moderate: ≤ 50% remaining * - healthy: > 50% remaining */ export type StockLevel = 'critical' | 'low' | 'moderate' | 'healthy'; /** * Compute the remaining weight percentage for a filament spool. * Returns a value from 0 to 100. */ export function getRemainingPercent(filament: Filament): number { if (filament.weightTotalGrams <= 0) return 0; const pct = (filament.weightRemainingGrams / filament.weightTotalGrams) * 100; return Math.min(Math.max(pct, 0), 100); } /** * Classify the stock level based on remaining percentage. * Thresholds: * critical — ≤ 10% (nearly empty, red alert) * low — ≤ 25% (getting low, amber warning) * moderate — ≤ 50% (half or less, yellow info) * healthy — > 50% (plenty left, green OK) */ export function classifyStockLevel(filament: Filament): StockLevel { const pct = getRemainingPercent(filament); if (pct <= 10) return 'critical'; if (pct <= 25) return 'low'; if (pct <= 50) return 'moderate'; return 'healthy'; }