import { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { X } from 'lucide-react'; import type { LoadedSpoolPrinter, LoadedSpoolTray, UnifiedPresetsResponse } from '../api/client'; import { FilamentSwatch } from './FilamentSwatch'; import { formatSlotLabel, getAmsLabel, getEmptySlotKind } from '../utils/amsHelpers'; import { matchSlotPreset, presetDisplayName, savedPresetFor, trayColourHex, type FilamentNameIndex, type SlotMatch, } from '../utils/sliceLoadedSpools'; import type { PrinterCompatibilityIndex } from '../utils/slicerPrinterMatch'; interface SliceSpoolPickerProps { /** Connected printers to offer, already narrowed to the selected model. */ printers: LoadedSpoolPrinter[]; data: UnifiedPresetsResponse; index: FilamentNameIndex; selectedPrinterName: string | null; compatIndex: PrinterCompatibilityIndex; /** The filament row being filled, e.g. "Filament 2 (PLA)". */ slotLabel: string; onPick: (match: SlotMatch, colour: string | null) => void; onClose: () => void; } /** * The spools loaded in the connected printers, laid out like the units * themselves, for filling one filament row of the Slice dialog (#3172). * * Regular AMS units keep their four-slot grid, empty slots included, so the * screen matches the machine. AMS-HT units follow, packed four to a row, then * the external holders that have a spool in them. A loaded spool with no * profile for the selected printer stays visible but can't be picked. */ export function SliceSpoolPicker({ printers, data, index, selectedPrinterName, compatIndex, slotLabel, onPick, onClose, }: SliceSpoolPickerProps) { const { t } = useTranslation(); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); return (
e.stopPropagation()} >

{t('slice.loadedSpools.title', 'Loaded spools')}

{slotLabel}

{printers.length === 0 ? (

{t('slice.loadedSpools.none', 'No printer of this model is online.')}

) : ( printers.map((printer) => ( matchSlotPreset(tray, data, index, selectedPrinterName, compatIndex)} onPick={onPick} /> )) )}
); } function PrinterSection({ printer, matchFor, onPick, }: { printer: LoadedSpoolPrinter; matchFor: (tray: LoadedSpoolTray) => SlotMatch | null; onPick: (match: SlotMatch, colour: string | null) => void; }) { const { t } = useTranslation(); const regular = printer.ams.filter((u) => !u.is_ams_ht); const ht = printer.ams.filter((u) => u.is_ams_ht); const hasAny = printer.ams.length > 0 || printer.external.length > 0; const tile = (tray: LoadedSpoolTray, label: string) => ( ); return (

{printer.name} {printer.model && {printer.model}}

{!hasAny && (

{t('slice.loadedSpools.nothingLoaded', 'No AMS and no external spool.')}

)}
{regular.map((unit) => (
{getAmsLabel(unit.id)}
{unit.trays.map((tray) => tile(tray, formatSlotLabel(unit.id, tray.tray_id, false, false)))}
))} {ht.length > 0 && (
AMS-HT
{ht.flatMap((unit) => unit.trays.map((tray) => tile(tray, formatSlotLabel(unit.id, tray.tray_id, true, false))))}
)} {printer.external.length > 0 && (
{t('printers.external')}
{printer.external.map((tray) => tile( tray, printer.external_holders > 1 ? (tray.tray_id === 0 ? t('printers.extL') : t('printers.extR')) : t('printers.ext'), ), )}
)}
); } function TrayTile({ tray, label, match, onPick, }: { tray: LoadedSpoolTray; label: string; match: SlotMatch | null; onPick: (match: SlotMatch, colour: string | null) => void; }) { const { t } = useTranslation(); const emptyKind = getEmptySlotKind(tray); const colour = trayColourHex(tray); const profileName = useMemo(() => { if (match) return presetDisplayName(match.preset.name); const saved = savedPresetFor(tray); if (saved?.preset_name) return presetDisplayName(saved.preset_name); return tray.tray_sub_brands || null; }, [match, tray]); if (emptyKind !== null) { return (
{label}
{emptyKind === 'reset' ? t('slice.loadedSpools.unknownSpool', 'Unidentified spool') : t('slice.loadedSpools.empty', 'Empty')}
); } const disabled = match === null; return ( ); }