Remove duplicate compliance and DSGVO admin pages that have been superseded by the unified SDK pipeline. Update navigation, sidebar, roles, and module registry to reflect the new structure. Add DSFA corpus API proxy and source-policy components. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
156 lines
5.5 KiB
TypeScript
156 lines
5.5 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { navigation, metaModules } from '@/lib/navigation'
|
|
import { getStoredRole, isCategoryVisibleForRole, RoleId } from '@/lib/roles'
|
|
import { CategoryCard } from '@/components/common/ModuleCard'
|
|
import { InfoNote } from '@/components/common/InfoBox'
|
|
import { ServiceStatus } from '@/components/common/ServiceStatus'
|
|
import { NightModeWidget } from '@/components/dashboard/NightModeWidget'
|
|
import Link from 'next/link'
|
|
|
|
interface Stats {
|
|
activeDocuments: number
|
|
openDSR: number
|
|
registeredUsers: number
|
|
totalConsents: number
|
|
gpuInstances: number
|
|
}
|
|
|
|
export default function DashboardPage() {
|
|
const [stats, setStats] = useState<Stats>({
|
|
activeDocuments: 0,
|
|
openDSR: 0,
|
|
registeredUsers: 0,
|
|
totalConsents: 0,
|
|
gpuInstances: 0,
|
|
})
|
|
const [loading, setLoading] = useState(true)
|
|
const [currentRole, setCurrentRole] = useState<RoleId | null>(null)
|
|
|
|
useEffect(() => {
|
|
const role = getStoredRole()
|
|
setCurrentRole(role)
|
|
|
|
// Load stats
|
|
const loadStats = async () => {
|
|
try {
|
|
const response = await fetch('http://localhost:8081/api/v1/admin/stats')
|
|
if (response.ok) {
|
|
const data = await response.json()
|
|
setStats({
|
|
activeDocuments: data.documents_count || 0,
|
|
openDSR: data.open_dsr_count || 0,
|
|
registeredUsers: data.users_count || 0,
|
|
totalConsents: data.consents_count || 0,
|
|
gpuInstances: 0,
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.log('Stats not available')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
loadStats()
|
|
}, [])
|
|
|
|
const statCards = [
|
|
{ label: 'Aktive Dokumente', value: stats.activeDocuments, color: 'text-green-600' },
|
|
{ label: 'Offene DSR', value: stats.openDSR, color: stats.openDSR > 0 ? 'text-orange-600' : 'text-slate-600' },
|
|
{ label: 'Registrierte Nutzer', value: stats.registeredUsers, color: 'text-blue-600' },
|
|
{ label: 'Zustimmungen', value: stats.totalConsents, color: 'text-purple-600' },
|
|
{ label: 'GPU Instanzen', value: stats.gpuInstances, color: 'text-pink-600' },
|
|
]
|
|
|
|
const visibleCategories = currentRole
|
|
? navigation.filter(cat => isCategoryVisibleForRole(cat.id, currentRole))
|
|
: navigation
|
|
|
|
return (
|
|
<div>
|
|
{/* Stats Grid */}
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-8">
|
|
{statCards.map((stat) => (
|
|
<div key={stat.label} className="bg-white rounded-xl border border-slate-200 p-4 shadow-sm">
|
|
<div className={`text-3xl font-bold ${stat.color}`}>
|
|
{loading ? '-' : stat.value}
|
|
</div>
|
|
<div className="text-sm text-slate-500 mt-1">{stat.label}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Categories */}
|
|
<h2 className="text-lg font-semibold text-slate-900 mb-4">Bereiche</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
|
|
{visibleCategories.map((category) => (
|
|
<CategoryCard key={category.id} category={category} />
|
|
))}
|
|
</div>
|
|
|
|
{/* Quick Links */}
|
|
<h2 className="text-lg font-semibold text-slate-900 mb-4">Schnellzugriff</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
|
|
{metaModules.filter(m => m.id !== 'dashboard').map((module) => (
|
|
<Link
|
|
key={module.id}
|
|
href={module.href}
|
|
className="flex items-center gap-3 p-4 bg-white rounded-xl border border-slate-200 hover:border-primary-300 hover:shadow-md transition-all"
|
|
>
|
|
<div className="w-10 h-10 bg-slate-100 rounded-lg flex items-center justify-center">
|
|
{module.id === 'onboarding' && '📖'}
|
|
{module.id === 'backlog' && '📋'}
|
|
{module.id === 'rbac' && '👥'}
|
|
</div>
|
|
<div>
|
|
<h3 className="font-medium text-slate-900">{module.name}</h3>
|
|
<p className="text-sm text-slate-500">{module.description}</p>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{/* Infrastructure & System Status */}
|
|
<h2 className="text-lg font-semibold text-slate-900 mb-4">Infrastruktur</h2>
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
|
{/* Night Mode Widget */}
|
|
<NightModeWidget />
|
|
|
|
{/* System Status */}
|
|
<ServiceStatus />
|
|
</div>
|
|
|
|
{/* Recent Activity */}
|
|
<h2 className="text-lg font-semibold text-slate-900 mb-4">Aktivitaet</h2>
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{/* Recent DSR */}
|
|
<div className="bg-white rounded-xl border border-slate-200 shadow-sm">
|
|
<div className="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
|
|
<h3 className="font-semibold text-slate-900">Neueste Datenschutzanfragen</h3>
|
|
<Link href="/sdk/dsr" className="text-sm text-primary-600 hover:text-primary-700">
|
|
Alle anzeigen
|
|
</Link>
|
|
</div>
|
|
<div className="p-4">
|
|
<p className="text-sm text-slate-500 text-center py-4">
|
|
Keine offenen Anfragen
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Info Box */}
|
|
<div className="mt-8">
|
|
<InfoNote title="Admin v2 - Neues Frontend">
|
|
<p>
|
|
Dieses neue Admin-Frontend bietet eine verbesserte Navigation mit Kategorien und Rollen-basiertem Zugriff.
|
|
Das alte Admin-Frontend ist weiterhin unter Port 3000 verfuegbar.
|
|
</p>
|
|
</InfoNote>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|