import { useState, useEffect } from 'react' import DirectoryBrowser from './DirectoryBrowser' import { useConfig, useSaveConfig } from '../hooks/useConfig' import { apiFetch } from '../lib/api' function fmtSeconds(s) { if (s == null) return '' const m = Math.round(parseFloat(s) / 60) return String(m) } export default function ConfigModal({ onClose }) { const { data: config, isLoading } = useConfig() const { mutate: save, isPending } = useSaveConfig() const [form, setForm] = useState({}) const [browser, setBrowser] = useState(null) // 'target_dir' | 'results_dir' | null const [scanResult, setScanResult] = useState(null) // { testCount, completedCount } | null const [saveError, setSaveError] = useState(null) const [isRescanning, setIsRescanning] = useState(false) const [isSavingTimes, setIsSavingTimes] = useState(false) useEffect(() => { if (config) { setForm({ target_dir: config.target_dir ?? '', results_dir: config.results_dir ?? '', results_dir_ref: config.results_dir_ref ?? '', avg_time_coe: config.avg_time_coe ? fmtSeconds(config.avg_time_coe) : '', avg_time_p2p: config.avg_time_p2p ? fmtSeconds(config.avg_time_p2p) : '', avg_time_p3p: config.avg_time_p3p ? fmtSeconds(config.avg_time_p3p) : '', smb_username: config.smb_username ?? '', smb_password: config.smb_password ?? '', smb_domain: config.smb_domain ?? '', }) } }, [config]) function handleSave() { setSaveError(null) setScanResult(null) const payload = { target_dir: form.target_dir || null, results_dir: form.results_dir || null, results_dir_ref: form.results_dir_ref || null, // Convert minutes → seconds; empty/null clears the override avg_time_coe: form.avg_time_coe ? String(parseFloat(form.avg_time_coe) * 60) : null, avg_time_p2p: form.avg_time_p2p ? String(parseFloat(form.avg_time_p2p) * 60) : null, avg_time_p3p: form.avg_time_p3p ? String(parseFloat(form.avg_time_p3p) * 60) : null, smb_username: form.smb_username || null, smb_password: form.smb_password || null, smb_domain: form.smb_domain || null, } save(payload, { onSuccess: (data) => { if (data?.testCount !== null && data?.testCount !== undefined) { setScanResult({ testCount: data.testCount, completedCount: data.completedCount }) // Auto-close after showing result only if tests were found if (data.testCount > 0) setTimeout(onClose, 1500) } else { onClose() } }, onError: (err) => { setSaveError(err?.message ?? 'Failed to save settings') }, }) } async function handleSaveTimes() { setSaveError(null) setScanResult(null) setIsSavingTimes(true) try { await apiFetch('/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ avg_time_coe: form.avg_time_coe ? String(parseFloat(form.avg_time_coe) * 60) : null, avg_time_p2p: form.avg_time_p2p ? String(parseFloat(form.avg_time_p2p) * 60) : null, avg_time_p3p: form.avg_time_p3p ? String(parseFloat(form.avg_time_p3p) * 60) : null, }), }) } catch (err) { setSaveError(err?.message ?? 'Failed to save') } finally { setIsSavingTimes(false) } } async function handleRescanResults() { setSaveError(null) setScanResult(null) setIsRescanning(true) try { const data = await apiFetch('/config/rescan-results', { method: 'POST' }) if (data?.testCount !== null && data?.testCount !== undefined) { setScanResult({ testCount: data.testCount, completedCount: data.completedCount }) } } catch (err) { setSaveError(err?.message ?? 'Rescan failed') } finally { setIsRescanning(false) } } const AVG_FIELDS = [ { key: 'avg_time_coe', label: 'COE avg time (min)' }, { key: 'avg_time_p2p', label: 'P2P avg time (min)' }, { key: 'avg_time_p3p', label: 'P3P avg time (min)' }, ] return ( <>
Loading…
) : ( <> {/* Directories */}Manually set avg minutes per test type. Leave blank to use calculated average from completed tests.
Optional credentials for network shares used by target/results directories.