Files
breakpilot-lehrer/website/app/admin/compliance/audit-workspace/page.tsx
Benjamin Admin b6983ab1dc [split-required] Split 500-1000 LOC files across all services
backend-lehrer (5 files):
- alerts_agent/db/repository.py (992 → 5), abitur_docs_api.py (956 → 3)
- teacher_dashboard_api.py (951 → 3), services/pdf_service.py (916 → 3)
- mail/mail_db.py (987 → 6)

klausur-service (5 files):
- legal_templates_ingestion.py (942 → 3), ocr_pipeline_postprocess.py (929 → 4)
- ocr_pipeline_words.py (876 → 3), ocr_pipeline_ocr_merge.py (616 → 2)
- KorrekturPage.tsx (956 → 6)

website (5 pages):
- mail (985 → 9), edu-search (958 → 8), mac-mini (950 → 7)
- ocr-labeling (946 → 7), audit-workspace (871 → 4)

studio-v2 (5 files + 1 deleted):
- page.tsx (946 → 5), MessagesContext.tsx (925 → 4)
- korrektur (914 → 6), worksheet-cleanup (899 → 6)
- useVocabWorksheet.ts (888 → 3)
- Deleted dead page-original.tsx (934 LOC)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 23:35:37 +02:00

171 lines
6.4 KiB
TypeScript

'use client'
/**
* Audit Workspace - Collaborative Audit Management
*
* Features:
* - View all requirements with original text from regulations
* - Document implementation details for each requirement
* - Link to source documents (PDFs, EUR-Lex)
* - Track audit status (pending, in_review, approved, rejected)
* - Add code references and evidence
* - Auditor notes and sign-off
*/
import { useState, useEffect } from 'react'
import Link from 'next/link'
import AdminLayout from '@/components/admin/AdminLayout'
import type { Regulation, Requirement, RequirementUpdate } from './types'
import RequirementList from './_components/RequirementList'
import RequirementDetailPanel from './_components/RequirementDetailPanel'
export default function AuditWorkspacePage() {
const [regulations, setRegulations] = useState<Regulation[]>([])
const [requirements, setRequirements] = useState<Requirement[]>([])
const [selectedRegulation, setSelectedRegulation] = useState<string | null>(null)
const [selectedRequirement, setSelectedRequirement] = useState<Requirement | null>(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [filterAuditStatus, setFilterAuditStatus] = useState<string>('all')
const [filterImplStatus, setFilterImplStatus] = useState<string>('all')
const [searchQuery, setSearchQuery] = useState('')
const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000'
useEffect(() => { loadRegulations() }, [])
useEffect(() => {
if (selectedRegulation) loadRequirements(selectedRegulation)
}, [selectedRegulation])
const loadRegulations = async () => {
try {
const res = await fetch(`${BACKEND_URL}/api/v1/compliance/regulations`)
if (res.ok) {
const data = await res.json()
setRegulations(data.regulations || [])
if (data.regulations?.length > 0) setSelectedRegulation(data.regulations[0].code)
}
} catch (err) {
console.error('Failed to load regulations:', err)
} finally {
setLoading(false)
}
}
const loadRequirements = async (regCode: string) => {
try {
const res = await fetch(`${BACKEND_URL}/api/v1/compliance/regulations/${regCode}/requirements`)
if (res.ok) {
const data = await res.json()
setRequirements(data.requirements || [])
}
} catch (err) {
console.error('Failed to load requirements:', err)
}
}
const updateRequirement = async (reqId: string, updates: RequirementUpdate) => {
setSaving(true)
try {
const res = await fetch(`${BACKEND_URL}/api/v1/compliance/requirements/${reqId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates),
})
if (res.ok) {
setRequirements(prev => prev.map(r => r.id === reqId ? { ...r, ...updates } : r))
if (selectedRequirement?.id === reqId) {
setSelectedRequirement({ ...selectedRequirement, ...updates })
}
}
} catch (err) {
console.error('Failed to update requirement:', err)
} finally {
setSaving(false)
}
}
const filteredRequirements = requirements.filter(req => {
if (filterAuditStatus !== 'all' && req.audit_status !== filterAuditStatus) return false
if (filterImplStatus !== 'all' && req.implementation_status !== filterImplStatus) return false
if (searchQuery) {
const query = searchQuery.toLowerCase()
return (
req.title.toLowerCase().includes(query) ||
req.article.toLowerCase().includes(query) ||
req.requirement_text?.toLowerCase().includes(query)
)
}
return true
})
const currentRegulation = regulations.find(r => r.code === selectedRegulation)
const stats = {
total: requirements.length,
verified: requirements.filter(r => r.implementation_status === 'verified').length,
approved: requirements.filter(r => r.audit_status === 'approved').length,
pending: requirements.filter(r => r.audit_status === 'pending').length,
}
return (
<AdminLayout title="Audit Workspace" description="Gemeinsam mit Pruefern arbeiten">
{/* Header with back link */}
<div className="mb-6 flex items-center justify-between">
<Link
href="/admin/compliance"
className="text-sm text-slate-600 hover:text-slate-900 flex items-center gap-1"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Zurueck zu Compliance
</Link>
<div className="flex items-center gap-4">
<div className="text-sm text-slate-500">
{stats.approved}/{stats.total} genehmigt | {stats.verified}/{stats.total} verifiziert
</div>
</div>
</div>
<div className="grid grid-cols-12 gap-6">
<RequirementList
regulations={regulations}
selectedRegulation={selectedRegulation}
setSelectedRegulation={setSelectedRegulation}
filteredRequirements={filteredRequirements}
selectedRequirement={selectedRequirement}
setSelectedRequirement={setSelectedRequirement}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
filterAuditStatus={filterAuditStatus}
setFilterAuditStatus={setFilterAuditStatus}
filterImplStatus={filterImplStatus}
setFilterImplStatus={setFilterImplStatus}
/>
{/* Right Panel */}
<div className="col-span-8">
{selectedRequirement ? (
<RequirementDetailPanel
requirement={selectedRequirement}
regulation={currentRegulation}
onUpdate={(updates) => updateRequirement(selectedRequirement.id, updates)}
saving={saving}
/>
) : (
<div className="bg-white rounded-lg border border-slate-200 p-8 text-center">
<svg className="w-12 h-12 mx-auto text-slate-300 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<p className="text-slate-500">Waehlen Sie eine Anforderung aus der Liste</p>
</div>
)}
</div>
</div>
</AdminLayout>
)
}