import type { AMSUnit, AMSTray } from '../../api/client';
function trayColorToCSS(color: string | null): string {
if (!color) return '#808080';
return `#${color.slice(0, 6)}`;
}
function isTrayEmpty(tray: AMSTray): boolean {
return !tray.tray_type || tray.tray_type === '';
}
function getAmsName(id: number): string {
if (id <= 3) return `AMS ${String.fromCharCode(65 + id)}`;
if (id >= 128 && id <= 135) return `AMS HT ${String.fromCharCode(65 + id - 128)}`;
return `AMS ${id}`;
}
// --- SVG Icons (matching PrintersPage Bambu Lab style) ---
function WaterDropEmpty({ className }: { className?: string }) {
return (
);
}
function WaterDropHalf({ className }: { className?: string }) {
return (
);
}
function WaterDropFull({ className }: { className?: string }) {
return (
);
}
function ThermometerEmpty({ className }: { className?: string }) {
return (
);
}
function ThermometerHalf({ className }: { className?: string }) {
return (
);
}
function ThermometerFull({ className }: { className?: string }) {
return (
);
}
// --- Threshold-colored indicators ---
function HumidityIndicator({ humidity, goodThreshold = 40, fairThreshold = 60 }: { humidity: number; goodThreshold?: number; fairThreshold?: number }) {
let textColor: string;
let DropComponent: React.FC<{ className?: string }>;
if (humidity <= goodThreshold) {
textColor = '#22a352';
DropComponent = WaterDropEmpty;
} else if (humidity <= fairThreshold) {
textColor = '#d4a017';
DropComponent = WaterDropHalf;
} else {
textColor = '#c62828';
DropComponent = WaterDropFull;
}
return (
{/* Spool visualization */}
{isEmpty ? (
) : (
)}
{isActive && (
)}
{/* Material type */}
{isEmpty ? 'Empty' : tray.tray_type || 'Unknown'}
{/* Fill level bar */}
{!isEmpty && tray.remain !== null && tray.remain !== undefined && tray.remain >= 0 && (
50 ? '#22c55e' : tray.remain > 20 ? '#f59e0b' : '#ef4444',
}}
/>
)}
{/* Slot number */}
{slotIndex + 1}
);
}
export interface AmsThresholds {
humidityGood: number;
humidityFair: number;
tempGood: number;
tempFair: number;
}
interface AmsUnitCardProps {
unit: AMSUnit;
activeSlot: number | null;
onConfigureSlot?: (amsId: number, trayId: number, tray: AMSTray | null) => void;
isDualNozzle?: boolean;
nozzleSide?: 'L' | 'R' | null;
thresholds?: AmsThresholds;
}
export function AmsUnitCard({ unit, activeSlot, onConfigureSlot, isDualNozzle, nozzleSide, thresholds }: AmsUnitCardProps) {
const trays = unit.tray || [];
const isHt = unit.is_ams_ht;
const slotCount = isHt ? 1 : 4;
return (
{/* Header */}
{getAmsName(unit.id)}
{isDualNozzle && nozzleSide && (
)}
{unit.temp != null && (
)}
{unit.humidity != null && (
)}
{/* Slots grid */}
{Array.from({ length: slotCount }).map((_, i) => {
const tray = trays[i] || {
id: i,
tray_color: null,
tray_type: '',
tray_sub_brands: null,
tray_id_name: null,
tray_info_idx: null,
remain: -1,
k: null,
cali_idx: null,
tag_uid: null,
tray_uuid: null,
nozzle_temp_min: null,
nozzle_temp_max: null,
};
return (
onConfigureSlot(unit.id, i, isTrayEmpty(tray) ? null : tray) : undefined}
/>
);
})}
);
}
// Exported for use in SpoolBuddyAmsPage compact cards
export { HumidityIndicator, TemperatureIndicator, NozzleBadge };