fixed timeout issue
This commit is contained in:
@@ -2,9 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>dashboard</title>
|
||||
<title>Test House Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useRef, useCallback } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useConfig, useSaveConfig } from '../hooks/useConfig'
|
||||
import { apiFetch } from '../lib/api'
|
||||
|
||||
function useScanPoller(onDone) {
|
||||
const timerRef = useRef(null)
|
||||
const onDoneRef = useRef(onDone)
|
||||
onDoneRef.current = onDone
|
||||
|
||||
const stop = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearInterval(timerRef.current)
|
||||
timerRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
const start = useCallback(() => {
|
||||
stop()
|
||||
timerRef.current = setInterval(async () => {
|
||||
try {
|
||||
const data = await apiFetch('/scan-status')
|
||||
if (!data?.scanning) {
|
||||
stop()
|
||||
onDoneRef.current()
|
||||
}
|
||||
} catch {
|
||||
stop()
|
||||
onDoneRef.current()
|
||||
}
|
||||
}, 2000)
|
||||
}, [stop])
|
||||
|
||||
useEffect(() => () => stop(), [stop])
|
||||
|
||||
return { start, stop }
|
||||
}
|
||||
|
||||
function fmtSeconds(s) {
|
||||
if (s == null) return ''
|
||||
const m = Math.round(parseFloat(s) / 60)
|
||||
@@ -14,12 +47,20 @@ export default function ConfigModal({ onClose }) {
|
||||
const { data: config, isLoading } = useConfig()
|
||||
const { mutate: save, isPending } = useSaveConfig()
|
||||
|
||||
const [form, setForm] = useState({})
|
||||
const [scanResult, setScanResult] = useState(null) // { testCount, completedCount } | null
|
||||
const [saveError, setSaveError] = useState(null)
|
||||
const [form, setForm] = useState({})
|
||||
const [scanResult, setScanResult] = useState(null)
|
||||
const [saveError, setSaveError] = useState(null)
|
||||
const [isRescanning, setIsRescanning] = useState(false)
|
||||
const [isScanning, setIsScanning] = useState(false)
|
||||
const [isSavingTimes, setIsSavingTimes] = useState(false)
|
||||
|
||||
const scanPoller = useScanPoller(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['stats'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['tests'] })
|
||||
setIsRescanning(false)
|
||||
setIsScanning(false)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (config) {
|
||||
setForm({
|
||||
@@ -55,10 +96,9 @@ export default function ConfigModal({ onClose }) {
|
||||
}
|
||||
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)
|
||||
if (data?.scanning) {
|
||||
setIsScanning(true)
|
||||
scanPoller.start()
|
||||
} else {
|
||||
onClose()
|
||||
}
|
||||
@@ -98,14 +138,15 @@ export default function ConfigModal({ onClose }) {
|
||||
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 })
|
||||
if (data?.scanning) {
|
||||
scanPoller.start()
|
||||
} else {
|
||||
setIsRescanning(false)
|
||||
queryClient.invalidateQueries({ queryKey: ['stats'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['tests'] })
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ['stats'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['tests'] })
|
||||
} catch (err) {
|
||||
setSaveError(err?.message ?? 'Rescan failed')
|
||||
} finally {
|
||||
setIsRescanning(false)
|
||||
}
|
||||
}
|
||||
@@ -134,16 +175,19 @@ export default function ConfigModal({ onClose }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Scan result banner */}
|
||||
{scanResult !== null && (
|
||||
<div className={`rounded-lg px-4 py-3 text-sm border ${
|
||||
scanResult.testCount === 0
|
||||
? 'bg-amber-950/50 border-amber-700 text-amber-300'
|
||||
: 'bg-emerald-950/50 border-emerald-700 text-emerald-300'
|
||||
}`}>
|
||||
{scanResult.testCount === 0
|
||||
? 'No tests found — check that the target directory contains TC_WIFI_*.ini files in subdirectories.'
|
||||
: `Found ${scanResult.testCount} tests (${scanResult.completedCount} completed).`}
|
||||
{/* Scanning indicator (save triggered a scan) */}
|
||||
{isScanning && (
|
||||
<div className="bg-blue-950/50 border border-blue-700 rounded-lg px-4 py-3 text-blue-300 text-sm flex items-center gap-2">
|
||||
<span className="animate-spin inline-block">⟳</span>
|
||||
Scan in progress — the table will refresh when complete.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rescan indicator (rescan button used) */}
|
||||
{!isScanning && isRescanning && (
|
||||
<div className="bg-blue-950/50 border border-blue-700 rounded-lg px-4 py-3 text-blue-300 text-sm flex items-center gap-2">
|
||||
<span className="animate-spin inline-block">⟳</span>
|
||||
Results scan in progress…
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -4,10 +4,5 @@ import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
server: {
|
||||
host: true,
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3001',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user