import { useState, useEffect, useCallback } from 'react'; import { X, Play, RotateCw } from 'lucide-react'; import { spoolbuddyApi } from '../../api/client'; import { useTranslation } from 'react-i18next'; interface DiagnosticModalProps { type: 'scale' | 'nfc' | 'read_tag'; deviceId: string; onClose: () => void; } export function DiagnosticModal({ type, deviceId, onClose }: DiagnosticModalProps) { const { t } = useTranslation(); const [isRunning, setIsRunning] = useState(false); const [output, setOutput] = useState(''); const [error, setError] = useState(''); const [hasRun, setHasRun] = useState(false); // Close on Escape useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && !isRunning) { onClose(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [isRunning, onClose]); const runDiagnostic = useCallback(async () => { setIsRunning(true); setOutput(''); setError(''); setHasRun(true); try { // Step 1: Queue the diagnostic on the device setOutput(t('spoolbuddy.diagnostic.queuing', 'Queuing diagnostic on device...\n')); await spoolbuddyApi.queueDiagnostics(deviceId, type); // Step 2: Poll for results with timeout let result = null; const maxRetries = 60; // 30s timeout with 500ms polling let retryCount = 0; while (retryCount < maxRetries && !result) { // Wait a bit before polling await new Promise(resolve => setTimeout(resolve, 500)); try { result = await spoolbuddyApi.getDiagnosticResult(deviceId, type); break; } catch { // Not ready yet, continue polling retryCount++; if (retryCount % 4 === 0) { // Update every 2 seconds (after 4 retries of 500ms) setOutput(prev => prev + '.'); } } } if (!result) { throw new Error('Diagnostic timed out - device did not report results'); } setOutput(result.output); if (!result.success) { setError(`Exit code: ${result.exit_code}`); } } catch (e) { setError(e instanceof Error ? e.message : 'Unknown error'); setOutput(''); } finally { setIsRunning(false); } }, [type, deviceId, t]); const title = type === 'scale' ? t('spoolbuddy.diagnostic.scaleTitle', 'Scale Diagnostic') : type === 'read_tag' ? t('spoolbuddy.diagnostic.readTagTitle', 'Read Tag Diagnostic') : t('spoolbuddy.diagnostic.nfcTitle', 'NFC Reader Diagnostic'); return (
e.stopPropagation()} > {/* Header */}

{title}

{isRunning ? (
{t('spoolbuddy.diagnostic.running', 'Running diagnostic on device...')}
) : output ? ( <>
{output}
{error && (
❌ {error}
)} ) : hasRun ? (
{error ? (
ERROR: {error}
) : ( {t('spoolbuddy.diagnostic.completed', 'Diagnostic completed successfully.')} )}
) : (
{t('spoolbuddy.diagnostic.clickStart', 'Click "Run Diagnostic" to start the hardware diagnostic on')} {deviceId}.
)}
{/* Footer */}
); }