This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/app/(admin)/dashboard/page.tsx
BreakPilot Dev f09e24d52c refactor(admin-v2): Consolidate compliance/DSGVO pages into SDK pipeline
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>
2026-02-10 23:26:05 +01:00

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