import React, { useState, useEffect, useCallback } from 'react'; import { useAuth } from '../context/AuthContext'; import { useToast } from '../components/Toast'; import FillModal from '../components/FillModal'; import TemplateCard from '../components/TemplateCard'; import { apiFetch } from '../utils/api'; export default function Dashboard({ onNavigate }) { const { user } = useAuth(); const toast = useToast(); const isAdmin = user?.role === 'admin'; const [templates, setTemplates] = useState([]); const [stats, setStats] = useState(null); const [search, setSearch] = useState(''); const [loading, setLoading] = useState(true); const [fillTemplate, setFillTemplate] = useState(null); // Pagination für die Vorlagen-Table-Liste (10 Einträge pro Seite) const TEMPLATES_PER_PAGE = 10; const [currentPage, setCurrentPage] = useState(1); const fetchTemplates = useCallback(async () => { try { const res = await apiFetch('/templates'); const data = await res.json(); setTemplates(data); } catch (e) { console.error('Fehler beim Laden der Vorlagen:', e); } finally { setLoading(false); } }, []); const fetchStats = useCallback(async () => { if (!isAdmin) return; try { const res = await apiFetch('/stats'); if (res.ok) { const data = await res.json(); setStats(data); } } catch (e) { console.error('Fehler beim Laden der Statistiken:', e); } }, [isAdmin]); useEffect(() => { fetchTemplates(); fetchStats(); const interval = setInterval(fetchTemplates, 30000); return () => clearInterval(interval); }, [fetchTemplates, fetchStats]); // Alle Vorlagen sind für jeden User sichtbar (nicht mehr nach is_assignable gefiltert) const topTemplates = stats?.topTemplates || []; const filtered = templates.filter((t) => t.name.toLowerCase().includes(search.toLowerCase()) || (t.description || '').toLowerCase().includes(search.toLowerCase()) ); // Pagination: Suchwechsel setzt auf Seite 1 zurück const totalPages = Math.max(Math.ceil(filtered.length / TEMPLATES_PER_PAGE), 1); const pagedTemplates = filtered.slice((currentPage - 1) * TEMPLATES_PER_PAGE, currentPage * TEMPLATES_PER_PAGE); useEffect(() => { setCurrentPage(1); }, [search]); const handleSubmitTask = async (payload) => { const res = await apiFetch('/tasks', { method: 'POST', body: JSON.stringify({ ...payload, user_id: user.id }), }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || 'Fehler beim Absenden'); } setFillTemplate(null); toast.success('Aufgabe erfolgreich abgesendet!'); fetchStats(); }; if (loading) { return (
); } return (
{/* KPI-Sektion: 4 Statistik-Karten mit Delta + Fortschrittsbalken */} {isAdmin && stats && (
{/* StatCard: Offene Aufgaben */}
onNavigate?.('tasks')}>
Offene Aufgaben
{stats.openTasks} {stats.totalTasks > 0 && ( {Math.round((stats.openTasks / stats.totalTasks) * 100)}% )}
0 ? (stats.openTasks / stats.totalTasks) * 100 : 0}%` }} />
{/* StatCard: Abgeschlossen */}
onNavigate?.('tasks')}>
Abgeschlossen
{stats.completedTasks} {stats.totalTasks > 0 && ( {Math.round((stats.completedTasks / stats.totalTasks) * 100)}% )}
0 ? (stats.completedTasks / stats.totalTasks) * 100 : 0}%` }} />
{/* StatCard: Aktive Nutzer */}
onNavigate?.('users')}>
Aktive Nutzer
{stats.activeUsers} {stats.totalUsers > 0 && ( von {stats.totalUsers} )}
0 ? (stats.activeUsers / stats.totalUsers) * 100 : 0}%` }} />
{/* StatCard: AD-Nutzer */}
onNavigate?.('users')}>
AD-Nutzer
{stats.adUsers}
0 ? (stats.adUsers / stats.totalUsers) * 100 : 0}%` }} />
)} {/* Haupt-Grid: Vorlagen-Liste (links) | Graph-Card (rechts) — gleich große Spalten */}
{/* Linke Spalte: Vorlagen als Table-Liste */}

{isAdmin ? 'Vorlagen' : 'Verfügbare Vorlagen'}

setSearch(e.target.value)} />
{templates.length === 0 ? (

Keine Vorlagen vorhanden.

) : filtered.length === 0 ? (

Keine Vorlagen für „{search}" gefunden.

) : ( <> {/* Scrollbare Liste — flex-1 + min-h-0 (Pflicht für overflow in Flexbox), damit die Liste innerhalb der Card-Höhe scrollt statt sie aufzuspannen */}
{pagedTemplates.map((tpl) => ( setFillTemplate(tpl)} > Starten } /> ))}
{/* Pagination: 10 Vorlagen pro Seite */} {totalPages > 1 && (
Seite {currentPage} von {totalPages}
)} )}
{/* Rechte Spalte: Meistgenutzte Vorlagen als Column-Chart (Anteil in %) */} {isAdmin && topTemplates.length > 0 && (
{/* Header mit gleicher Mindesthöhe wie der Listen-Header (Suchbox = 48px), damit beide Cards auf derselben Höhe starten */}

Meistgenutzt

{(() => { // Prozentanteil jeder Vorlage an allen Task-Zuordnungen der Top-Liste const totalCount = topTemplates.reduce((sum, t) => sum + (t.task_count || 0), 0); if (totalCount === 0) return null; return (
{topTemplates.map((t, idx) => { const pct = Math.round(((t.task_count || 0) / totalCount) * 100); return (
{/* Prozentwert über der Säule */} {pct}% {/* Säule: Höhe proportional zum Prozentanteil, animiert per CSS-Transition */}
{/* Beschriftung unter der Säule */} {t.name}
); })}
); })()}
)}
{fillTemplate && ( setFillTemplate(null)} /> )}
); }