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>
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { useSDK } from '@/lib/sdk'
|
|
import { useAuditData } from './_hooks/useAuditData'
|
|
import { LLMLogTab } from './_components/LLMLogTab'
|
|
import { UsageTab } from './_components/UsageTab'
|
|
import { ComplianceTab } from './_components/ComplianceTab'
|
|
import type { TabId } from './_components/types'
|
|
|
|
const tabs: { id: TabId; label: string }[] = [
|
|
{ id: 'llm-log', label: 'LLM-Log' },
|
|
{ id: 'usage', label: 'Nutzung' },
|
|
{ id: 'compliance', label: 'Compliance' },
|
|
]
|
|
|
|
export default function AuditLLMPage() {
|
|
const { state } = useSDK()
|
|
const [activeTab, setActiveTab] = useState<TabId>('llm-log')
|
|
const [period, setPeriod] = useState('7d')
|
|
const [logFilter, setLogFilter] = useState({ model: '', pii: '' })
|
|
|
|
const {
|
|
logEntries,
|
|
usageStats,
|
|
complianceReport,
|
|
loading,
|
|
error,
|
|
loadLLMLog,
|
|
loadUsage,
|
|
loadCompliance,
|
|
handleExport,
|
|
} = useAuditData(period, logFilter)
|
|
|
|
useEffect(() => {
|
|
if (activeTab === 'llm-log') loadLLMLog()
|
|
else if (activeTab === 'usage') loadUsage()
|
|
else if (activeTab === 'compliance') loadCompliance()
|
|
}, [activeTab, loadLLMLog, loadUsage, loadCompliance])
|
|
|
|
return (
|
|
<div className="p-6 max-w-7xl mx-auto">
|
|
<div className="mb-6">
|
|
<h1 className="text-2xl font-bold text-gray-900">LLM Audit Dashboard</h1>
|
|
<p className="text-gray-500 mt-1">Monitoring und Compliance-Analyse der LLM-Operationen</p>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div className="flex gap-1 bg-gray-100 rounded-lg p-1">
|
|
{tabs.map(tab => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
activeTab === tab.id
|
|
? 'bg-white text-purple-700 shadow-sm'
|
|
: 'text-gray-600 hover:text-gray-900'
|
|
}`}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<select
|
|
value={period}
|
|
onChange={e => setPeriod(e.target.value)}
|
|
className="border border-gray-300 rounded-lg px-3 py-2 text-sm"
|
|
>
|
|
<option value="7d">Letzte 7 Tage</option>
|
|
<option value="30d">Letzte 30 Tage</option>
|
|
<option value="90d">Letzte 90 Tage</option>
|
|
</select>
|
|
<button
|
|
onClick={() => {
|
|
if (activeTab === 'llm-log') handleExport('llm', 'csv')
|
|
else if (activeTab === 'compliance') handleExport('compliance', 'json')
|
|
else handleExport('general', 'csv')
|
|
}}
|
|
className="px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
|
>
|
|
Export
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="mb-4 p-3 bg-red-50 text-red-700 rounded-lg text-sm">{error}</div>
|
|
)}
|
|
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="w-8 h-8 border-4 border-purple-200 border-t-purple-600 rounded-full animate-spin" />
|
|
</div>
|
|
)}
|
|
|
|
{!loading && activeTab === 'llm-log' && (
|
|
<LLMLogTab
|
|
logEntries={logEntries}
|
|
logFilter={logFilter}
|
|
onFilterChange={setLogFilter}
|
|
/>
|
|
)}
|
|
|
|
{!loading && activeTab === 'usage' && usageStats && (
|
|
<UsageTab usageStats={usageStats} />
|
|
)}
|
|
|
|
{!loading && activeTab === 'compliance' && complianceReport && (
|
|
<ComplianceTab complianceReport={complianceReport} />
|
|
)}
|
|
|
|
{!loading && activeTab === 'usage' && !usageStats && !error && (
|
|
<div className="text-center py-12 text-gray-400">Keine Nutzungsdaten verfuegbar</div>
|
|
)}
|
|
{!loading && activeTab === 'compliance' && !complianceReport && !error && (
|
|
<div className="text-center py-12 text-gray-400">Kein Compliance-Report verfuegbar</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|