import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQueryClient } from '@tanstack/react-query'; import { useAuth } from '../contexts/AuthContext'; import { X, Square, Pause, Play, ChevronDown, BellOff, Eraser, } from 'lucide-react'; import { Button } from './Button'; import { filterKnownHMSErrors } from './HMSErrorModal'; import type { Printer, HMSError } from '../api/client'; export type BulkAction = 'stop' | 'pause' | 'resume' | 'clearPlate' | 'clearHMS'; export type PrinterState = 'printing' | 'paused' | 'finished' | 'idle' | 'error' | 'offline'; interface PrinterStatus { connected: boolean; state: string | null; hms_errors?: HMSError[]; awaiting_plate_clear?: boolean; } interface BulkPrinterToolbarProps { selectedIds: Set; printers: Printer[]; onClose: () => void; onSelectAll: () => void; onSelectByLocation: (location: string) => void; onSelectByState: (state: PrinterState) => void; onAction: (action: BulkAction) => void; actionPending: boolean; } const STATE_OPTIONS: { key: PrinterState; dot: string }[] = [ { key: 'printing', dot: 'bg-bambu-green' }, { key: 'paused', dot: 'bg-status-warning' }, { key: 'finished', dot: 'bg-blue-400' }, { key: 'idle', dot: 'bg-bambu-green' }, { key: 'error', dot: 'bg-status-error' }, { key: 'offline', dot: 'bg-gray-400' }, ]; export function BulkPrinterToolbar({ selectedIds, printers, onClose, onSelectAll, onSelectByLocation, onSelectByState, onAction, actionPending, }: BulkPrinterToolbarProps) { const { t } = useTranslation(); const { hasPermission } = useAuth(); const queryClient = useQueryClient(); const [showLocationDropdown, setShowLocationDropdown] = useState(false); const [showStateDropdown, setShowStateDropdown] = useState(false); // Read cached statuses for selected printers const selectedStatuses = Array.from(selectedIds).map(id => ({ id, status: queryClient.getQueryData(['printerStatus', id]), })); // Smart enablement: check if any selected printer is in the right state const anyRunning = selectedStatuses.some( ({ status }) => status?.connected && status.state === 'RUNNING', ); const anyPaused = selectedStatuses.some( ({ status }) => status?.connected && status.state === 'PAUSE', ); const anyStoppable = anyRunning || anyPaused; const anyNeedsClearPlate = selectedStatuses.some( ({ status }) => !!(status?.connected && status.awaiting_plate_clear), ); const anyWithHMS = selectedStatuses.some(({ status }) => { if (!status?.connected || !status.hms_errors) return false; return filterKnownHMSErrors(status.hms_errors).length > 0; }); const canControl = hasPermission('printers:control'); const canClearPlate = hasPermission('printers:clear_plate'); // Unique locations from all printers (not just selected) const locations = [...new Set(printers.map(p => p.location).filter((l): l is string => !!l))].sort(); // Count printers per state for the state dropdown const stateCounts: Record = { printing: 0, paused: 0, finished: 0, idle: 0, error: 0, offline: 0 }; printers.forEach(p => { const status = queryClient.getQueryData(['printerStatus', p.id]); if (!status || !status.connected) { stateCounts.offline++; return; } const hasKnownHms = status.hms_errors ? filterKnownHMSErrors(status.hms_errors).length > 0 : false; if (hasKnownHms) stateCounts.error++; switch (status.state) { case 'RUNNING': stateCounts.printing++; break; case 'PAUSE': stateCounts.paused++; break; case 'FINISH': stateCounts.finished++; break; // FAILED without an active HMS error is the post-cancel terminal state — // group with FINISH. When HMS is active the error bucket is already // incremented above; don't double-count. case 'FAILED': if (!hasKnownHms) stateCounts.finished++; break; default: stateCounts.idle++; break; } }); const stateLabels: Record = { printing: t('printers.status.printing'), paused: t('printers.status.paused', 'Paused'), finished: t('printers.status.finished', 'Finished'), idle: t('printers.status.idle'), error: t('printers.status.problem'), offline: t('printers.status.offline'), }; return (
{/* Close */}
{/* Selection count */} {t('printers.bulk.selected', { count: selectedIds.size })}
{/* Select All */} {/* Select by State */}
{showStateDropdown && ( <>
setShowStateDropdown(false)} />
{STATE_OPTIONS.filter(({ key }) => stateCounts[key] > 0).map(({ key, dot }) => ( ))}
)}
{/* Select by Location */} {locations.length > 0 && (
{showLocationDropdown && ( <>
setShowLocationDropdown(false)} />
{locations.map(location => ( ))}
)}
)}
{/* Action buttons */}
); }