Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
237 lines
9.2 KiB
TypeScript
237 lines
9.2 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Admin Dashboard
|
|
*
|
|
* Central overview with quick stats and navigation to all admin sections
|
|
*/
|
|
|
|
import AdminLayout from '@/components/admin/AdminLayout'
|
|
import AiPrompt from '@/components/admin/AiPrompt'
|
|
import Link from 'next/link'
|
|
import { useEffect, useState } from 'react'
|
|
|
|
interface Stats {
|
|
activeDocuments: number
|
|
openDSR: number
|
|
registeredUsers: number
|
|
totalConsents: number
|
|
gpuInstances: number
|
|
pendingTranslations: number
|
|
}
|
|
|
|
export default function AdminDashboard() {
|
|
const [stats, setStats] = useState<Stats>({
|
|
activeDocuments: 0,
|
|
openDSR: 0,
|
|
registeredUsers: 0,
|
|
totalConsents: 0,
|
|
gpuInstances: 0,
|
|
pendingTranslations: 0,
|
|
})
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
// Load stats from consent service
|
|
const loadStats = async () => {
|
|
try {
|
|
// Try to fetch from consent service
|
|
const response = await fetch('http://localhost:8081/api/v1/admin/stats', {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('adminToken') || ''}`,
|
|
},
|
|
})
|
|
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, // Will be loaded from vast.ai
|
|
pendingTranslations: 0,
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.log('Stats not available:', error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
loadStats()
|
|
}, [])
|
|
|
|
const quickActions = [
|
|
{
|
|
title: 'GPU Infrastruktur',
|
|
description: 'vast.ai Instanzen verwalten',
|
|
href: '/admin/gpu',
|
|
icon: (
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
|
</svg>
|
|
),
|
|
color: 'bg-purple-50 border-purple-200 hover:border-purple-400 text-purple-700',
|
|
},
|
|
{
|
|
title: 'Consent Verwaltung',
|
|
description: 'Dokumente & Versionen',
|
|
href: '/admin/consent',
|
|
icon: (
|
|
<svg className="w-8 h-8" 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>
|
|
),
|
|
color: 'bg-green-50 border-green-200 hover:border-green-400 text-green-700',
|
|
},
|
|
{
|
|
title: 'Datenschutzanfragen',
|
|
description: 'DSGVO Art. 15-21',
|
|
href: '/admin/dsr',
|
|
icon: (
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
),
|
|
color: 'bg-orange-50 border-orange-200 hover:border-orange-400 text-orange-700',
|
|
},
|
|
{
|
|
title: 'DSMS',
|
|
description: 'Datenschutz-Management',
|
|
href: '/admin/dsms',
|
|
icon: (
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
|
</svg>
|
|
),
|
|
color: 'bg-blue-50 border-blue-200 hover:border-blue-400 text-blue-700',
|
|
},
|
|
{
|
|
title: 'Übersetzungen',
|
|
description: 'Website Content',
|
|
href: '/admin/content',
|
|
icon: (
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" />
|
|
</svg>
|
|
),
|
|
color: 'bg-indigo-50 border-indigo-200 hover:border-indigo-400 text-indigo-700',
|
|
},
|
|
{
|
|
title: 'Production Backlog',
|
|
description: 'Go-Live Checkliste',
|
|
href: '/admin/backlog',
|
|
icon: (
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
|
|
</svg>
|
|
),
|
|
color: 'bg-amber-50 border-amber-200 hover:border-amber-400 text-amber-700',
|
|
},
|
|
]
|
|
|
|
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' },
|
|
]
|
|
|
|
return (
|
|
<AdminLayout title="Dashboard" description="Übersicht aller Systeme">
|
|
{/* AI Prompt */}
|
|
<AiPrompt />
|
|
|
|
{/* 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>
|
|
|
|
{/* Quick Actions */}
|
|
<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-3 gap-4 mb-8">
|
|
{quickActions.map((action) => (
|
|
<Link
|
|
key={action.href}
|
|
href={action.href}
|
|
className={`flex items-center gap-4 p-4 rounded-xl border-2 transition-all ${action.color}`}
|
|
>
|
|
<div className="flex-shrink-0">{action.icon}</div>
|
|
<div>
|
|
<h3 className="font-semibold">{action.title}</h3>
|
|
<p className="text-sm opacity-75">{action.description}</p>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{/* Recent Activity */}
|
|
<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="/admin/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>
|
|
|
|
{/* System Status */}
|
|
<div className="bg-white rounded-xl border border-slate-200 shadow-sm">
|
|
<div className="px-4 py-3 border-b border-slate-200">
|
|
<h3 className="font-semibold text-slate-900">System Status</h3>
|
|
</div>
|
|
<div className="p-4 space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-slate-600">Consent Service</span>
|
|
<span className="flex items-center gap-2 text-sm">
|
|
<span className="w-2 h-2 rounded-full bg-green-500"></span>
|
|
Online
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-slate-600">Backend API</span>
|
|
<span className="flex items-center gap-2 text-sm">
|
|
<span className="w-2 h-2 rounded-full bg-green-500"></span>
|
|
Online
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-slate-600">GPU Cluster</span>
|
|
<span className="flex items-center gap-2 text-sm">
|
|
<span className="w-2 h-2 rounded-full bg-slate-300"></span>
|
|
Nicht verbunden
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Info Box */}
|
|
<div className="mt-8 bg-slate-100 rounded-xl p-6">
|
|
<h3 className="font-semibold text-slate-900">Entwickler Admin Portal</h3>
|
|
<p className="text-sm text-slate-600 mt-2">
|
|
Dieses Portal ist nur für autorisierte Entwickler zugänglich. Hier verwaltest du die gesamte
|
|
BreakPilot-Infrastruktur: GPU-Ressourcen, Consent-Dokumente, Datenschutzanfragen und
|
|
Website-Übersetzungen.
|
|
</p>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|