Files
breakpilot-compliance/admin-compliance/app/sdk/audit-llm/page.tsx
Sharang Parnerkar 519ffdc8dc refactor(admin): split dsfa, audit-llm, quality pages
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>
2026-04-16 13:20:17 +02:00

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>
)
}