Extract components and hooks from oversized page files (563/561/520 LOC) into colocated _components/ and _hooks/ subdirectories. All three page.tsx files are now thin orchestrators under 300 LOC each (dsfa: 216, audit-llm: 121, quality: 163). Zero behavior changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
126 lines
3.4 KiB
TypeScript
126 lines
3.4 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useCallback } from 'react'
|
|
import type { QualityMetric } from '../_components/MetricCard'
|
|
import type { QualityTest } from '../_components/TestRow'
|
|
|
|
interface Stats {
|
|
total_metrics: number
|
|
avg_score: number
|
|
metrics_above_threshold: number
|
|
passed: number
|
|
failed: number
|
|
warning: number
|
|
total_tests: number
|
|
}
|
|
|
|
const API_BASE = '/api/sdk/v1/compliance/quality'
|
|
|
|
export function useQualityData() {
|
|
const [metrics, setMetrics] = useState<QualityMetric[]>([])
|
|
const [tests, setTests] = useState<QualityTest[]>([])
|
|
const [apiStats, setApiStats] = useState<Stats | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
const loadAll = useCallback(async () => {
|
|
setLoading(true)
|
|
try {
|
|
const [metricsRes, testsRes, statsRes] = await Promise.all([
|
|
fetch(`${API_BASE}/metrics?limit=100`),
|
|
fetch(`${API_BASE}/tests?limit=100`),
|
|
fetch(`${API_BASE}/stats`),
|
|
])
|
|
if (metricsRes.ok) {
|
|
const d = await metricsRes.json()
|
|
setMetrics(Array.isArray(d.metrics) ? d.metrics : [])
|
|
}
|
|
if (testsRes.ok) {
|
|
const d = await testsRes.json()
|
|
setTests(Array.isArray(d.tests) ? d.tests : [])
|
|
}
|
|
if (statsRes.ok) {
|
|
setApiStats(await statsRes.json())
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to load quality data:', err)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
const handleCreateMetric = useCallback(async (form: any) => {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/metrics`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(form),
|
|
})
|
|
if (res.ok) {
|
|
const created = await res.json()
|
|
setMetrics(prev => [...prev, created])
|
|
loadAll()
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to create metric:', err)
|
|
}
|
|
}, [loadAll])
|
|
|
|
const handleUpdateMetric = useCallback(async (editMetric: QualityMetric, form: any) => {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/metrics/${editMetric.id}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(form),
|
|
})
|
|
if (res.ok) {
|
|
const updated = await res.json()
|
|
setMetrics(prev => prev.map(m => m.id === updated.id ? updated : m))
|
|
loadAll()
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to update metric:', err)
|
|
}
|
|
}, [loadAll])
|
|
|
|
const handleCreateTest = useCallback(async (form: any) => {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/tests`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(form),
|
|
})
|
|
if (res.ok) {
|
|
const created = await res.json()
|
|
setTests(prev => [created, ...prev])
|
|
loadAll()
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to create test:', err)
|
|
}
|
|
}, [loadAll])
|
|
|
|
const handleDeleteTest = useCallback(async (id: string) => {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/tests/${id}`, { method: 'DELETE' })
|
|
if (res.ok || res.status === 204) {
|
|
setTests(prev => prev.filter(t => t.id !== id))
|
|
loadAll()
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to delete test:', err)
|
|
}
|
|
}, [loadAll])
|
|
|
|
return {
|
|
metrics,
|
|
tests,
|
|
apiStats,
|
|
loading,
|
|
loadAll,
|
|
handleCreateMetric,
|
|
handleUpdateMetric,
|
|
handleCreateTest,
|
|
handleDeleteTest,
|
|
}
|
|
}
|