'use client' import { useState, useEffect, useCallback, useRef } from 'react' import type { ServiceTestInfo, TestRegistryStats, TestRun, CoverageData, TabType, Toast, FailedTest, BacklogItem, } from '../types' import { API_BASE, DEMO_SERVICES, DEMO_STATS } from '../_lib/constants' import type { ServiceProgress } from '../_components/ServiceTestCard' export function useTestDashboard() { const [activeTab, setActiveTab] = useState('overview') const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) // Toast state const [toasts, setToasts] = useState([]) const toastIdRef = useRef(0) const addToast = useCallback((type: Toast['type'], message: string) => { const id = ++toastIdRef.current setToasts((prev) => [...prev, { id, type, message }]) if (type !== 'loading') { setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)) }, 5000) } return id }, []) const removeToast = useCallback((id: number) => { setToasts((prev) => prev.filter((t) => t.id !== id)) }, []) const updateToast = useCallback((id: number, type: Toast['type'], message: string) => { setToasts((prev) => prev.map((t) => (t.id === id ? { ...t, type, message } : t))) if (type !== 'loading') { setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)) }, 5000) } }, []) // Data states const [services, setServices] = useState([]) const [stats, setStats] = useState(null) const [coverage, setCoverage] = useState([]) const [testRuns, setTestRuns] = useState([]) const [failedTests, setFailedTests] = useState([]) const [backlogItems, setBacklogItems] = useState([]) const [usePostgres, setUsePostgres] = useState(false) // Running states const [runningServices, setRunningServices] = useState>(new Set()) // Progress states fuer laufende Tests const [serviceProgress, setServiceProgress] = useState>({}) // Fetch data const fetchData = useCallback(async () => { setIsLoading(true) setError(null) try { const registryResponse = await fetch(`${API_BASE}/registry`) if (registryResponse.ok) { const data = await registryResponse.json() setServices(data.services || DEMO_SERVICES) setStats(data.stats || DEMO_STATS) } else { setServices(DEMO_SERVICES) setStats(DEMO_STATS) } const coverageResponse = await fetch(`${API_BASE}/coverage`) if (coverageResponse.ok) { const data = await coverageResponse.json() setCoverage(data.services || []) } else { setCoverage(DEMO_SERVICES.filter(s => s.coverage_percent).map(s => ({ service: s.service, display_name: s.display_name, coverage_percent: s.coverage_percent!, language: s.language, }))) } const runsResponse = await fetch(`${API_BASE}/runs`) if (runsResponse.ok) { const data = await runsResponse.json() setTestRuns(data.runs || []) } // Lade fehlgeschlagene Tests fuer Backlog const failedResponse = await fetch(`${API_BASE}/failed`) if (failedResponse.ok) { const data = await failedResponse.json() setFailedTests(data.tests || []) } // Versuche PostgreSQL-Backlog zu laden (neue API) try { const backlogResponse = await fetch(`${API_BASE}/backlog`) if (backlogResponse.ok) { const data = await backlogResponse.json() if (data.items && data.items.length > 0) { setBacklogItems(data.items) setUsePostgres(true) } } } catch { // PostgreSQL nicht verfuegbar, nutze Legacy setUsePostgres(false) } } catch (err) { console.error('Failed to fetch test registry data:', err) setServices(DEMO_SERVICES) setStats(DEMO_STATS) setCoverage(DEMO_SERVICES.filter(s => s.coverage_percent).map(s => ({ service: s.service, display_name: s.display_name, coverage_percent: s.coverage_percent!, language: s.language, }))) } finally { setIsLoading(false) } }, []) useEffect(() => { fetchData() }, [fetchData]) // Update failed test status const updateTestStatus = async (testId: string, status: string) => { try { // Nutze PostgreSQL-Endpoint wenn verfuegbar const endpoint = usePostgres ? `${API_BASE}/backlog/${testId}/status` : `${API_BASE}/failed/${encodeURIComponent(testId)}/status?status=${status}` const response = await fetch(endpoint, { method: 'POST', headers: usePostgres ? { 'Content-Type': 'application/json' } : undefined, body: usePostgres ? JSON.stringify({ status }) : undefined, }) if (response.ok) { // Aktualisiere lokalen State if (usePostgres) { setBacklogItems(prev => prev.map(t => String(t.id) === testId ? { ...t, status: status as any } : t) ) } setFailedTests(prev => prev.map(t => t.id === testId ? { ...t, status: status as any } : t) ) addToast('success', `Test-Status auf "${status}" gesetzt`) } } catch (err) { console.error('Failed to update test status:', err) // Trotzdem lokal aktualisieren fuer bessere UX setFailedTests(prev => prev.map(t => t.id === testId ? { ...t, status: status as any } : t) ) if (usePostgres) { setBacklogItems(prev => prev.map(t => String(t.id) === testId ? { ...t, status: status as any } : t) ) } } } // Update failed test priority (nur PostgreSQL) const updateTestPriority = async (testId: string, priority: string) => { if (!usePostgres) return try { const response = await fetch(`${API_BASE}/backlog/${testId}/priority`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ priority }), }) if (response.ok) { setBacklogItems(prev => prev.map(t => String(t.id) === testId ? { ...t, priority: priority as any } : t) ) addToast('success', `Prioritaet auf "${priority}" gesetzt`) } } catch (err) { console.error('Failed to update test priority:', err) // Trotzdem lokal aktualisieren setBacklogItems(prev => prev.map(t => String(t.id) === testId ? { ...t, priority: priority as any } : t) ) } } // Run tests mit Progress-Polling const runTests = async (service: string) => { setRunningServices((prev) => new Set(prev).add(service)) const loadingToast = addToast('loading', `Tests fuer ${service} werden gestartet...`) // Progress-Polling starten let pollInterval: NodeJS.Timeout | null = null const pollProgress = async () => { try { const progressResponse = await fetch(`${API_BASE}/progress/${service}`) if (progressResponse.ok) { const progress = await progressResponse.json() setServiceProgress((prev) => ({ ...prev, [service]: progress, })) // Toast-Message mit aktuellem Fortschritt aktualisieren if (progress.status === 'running' && progress.files_total > 0) { const toastMsg = `${service}: ${progress.current_file} (${progress.passed} bestanden, ${progress.failed} fehler)` updateToast(loadingToast, 'loading', toastMsg) } } } catch { // Ignore polling errors } } // Start polling (alle 1 Sekunde) pollInterval = setInterval(pollProgress, 1000) try { const response = await fetch(`${API_BASE}/run/${service}`, { method: 'POST', }) if (response.ok) { // Warte kurz und pruefe finalen Progress await new Promise(resolve => setTimeout(resolve, 500)) await pollProgress() const finalProgress = serviceProgress[service] const passedMsg = finalProgress ? `${finalProgress.passed} bestanden, ${finalProgress.failed} fehler` : 'abgeschlossen' updateToast(loadingToast, 'success', `${service}: Tests ${passedMsg}`) await fetchData() } else { updateToast(loadingToast, 'info', `${service}: Demo-Modus (API nicht verfuegbar)`) } } catch (err) { console.error('Failed to run tests:', err) updateToast(loadingToast, 'info', `${service}: Demo-Modus (API nicht verfuegbar)`) } finally { // Polling stoppen if (pollInterval) { clearInterval(pollInterval) } setRunningServices((prev) => { const next = new Set(prev) next.delete(service) return next }) // Progress-Daten entfernen nach Abschluss setServiceProgress((prev) => { const next = { ...prev } delete next[service] return next }) } } // Filter services by category const unitServices = services.filter(s => !s.service.startsWith('bqas-')) const bqasServices = services.filter(s => s.service.startsWith('bqas-')) return { // Tab activeTab, setActiveTab, // Loading / Error isLoading, error, fetchData, // Toast toasts, removeToast, // Data services, stats, coverage, testRuns, failedTests, backlogItems, usePostgres, // Running runningServices, serviceProgress, // Actions updateTestStatus, updateTestPriority, runTests, // Derived unitServices, bqasServices, } }