// frontend/src/pages/Dashboard/Dashboard.tsx - Updated calculations import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { shiftPlanService } from '../../services/shiftPlanService'; import { employeeService } from '../../services/employeeService'; import { ShiftPlan, ScheduledShift } from '../../models/ShiftPlan'; import { Employee } from '../../models/Employee'; import { shiftAssignmentService } from '../../services/shiftAssignmentService'; interface DashboardData { currentShiftPlan: ShiftPlan | null; upcomingShifts: Array<{ id: string; date: string; time: string; type: string; assigned: boolean; planName: string; }>; teamStats: { totalEmployees: number; personell: number; manager: number; trainee: number; experienced: number; }; recentPlans: ShiftPlan[]; } const Dashboard: React.FC = () => { const { user, hasRole } = useAuth(); const [loading, setLoading] = useState(true); const [currentPlanShifts, setCurrentPlanShifts] = useState([]); const [data, setData] = useState({ currentShiftPlan: null, upcomingShifts: [], teamStats: { totalEmployees: 0, personell: 0, manager: 0, trainee: 0, experienced: 0 }, recentPlans: [] }); useEffect(() => { loadDashboardData(); }, []); const loadDashboardData = async () => { try { setLoading(true); console.log('🔄 Loading dashboard data...'); const [shiftPlans, employees] = await Promise.all([ shiftPlanService.getShiftPlans(), employeeService.getEmployees(), ]); // Find current shift plan const today = new Date().toISOString().split('T')[0]; const currentPlan = findCurrentShiftPlan(shiftPlans, today); // Load shifts for current plan if (currentPlan) { const shifts = await shiftAssignmentService.getScheduledShiftsForPlan(currentPlan.id); setCurrentPlanShifts(shifts); } else { setCurrentPlanShifts([]); } console.log('📊 Loaded data:', { plans: shiftPlans.length, employees: employees.length, currentPlanShifts }); // Debug: Log plan details shiftPlans.forEach(plan => { console.log(`Plan: ${plan.name}`, { status: plan.status, startDate: plan.startDate, endDate: plan.endDate, //scheduledShifts: plan.scheduledShifts?.length || 0, isTemplate: plan.isTemplate }); }); // Find current shift plan (published and current date within range) //const today = new Date().toISOString().split('T')[0]; //const currentPlan = findCurrentShiftPlan(shiftPlans, today); // Get user's upcoming shifts const userShifts = await loadUserUpcomingShifts(shiftPlans, today); // Calculate team stats const activeEmployees = employees.filter(emp => emp.isActive); const teamStats = calculateTeamStats(activeEmployees); // Get recent plans (non-templates, sorted by creation date) const recentPlans = getRecentPlans(shiftPlans); setData({ currentShiftPlan: currentPlan, upcomingShifts: userShifts, teamStats, recentPlans }); console.log('✅ Dashboard data loaded:', { currentPlan: currentPlan?.name, //userShifts: userShifts.length, teamStats, recentPlans: recentPlans.length }); } catch (error) { console.error('❌ Error loading dashboard data:', error); } finally { setLoading(false); } }; const findCurrentShiftPlan = (plans: ShiftPlan[], today: string): ShiftPlan | null => { // First, try to find a published plan where today is within the date range const activePlan = plans.find(plan => plan.status === 'published' && !plan.isTemplate && plan.startDate && plan.endDate && plan.startDate <= today && plan.endDate >= today ); if (activePlan) { console.log('✅ Found active plan:', activePlan.name); return activePlan; } // If no active plan found, try to find the most recent published plan const publishedPlans = plans .filter(plan => plan.status === 'published' && !plan.isTemplate) .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); console.log('📅 Published plans available:', publishedPlans.map(p => p.name)); return publishedPlans[0] || null; }; const loadUserUpcomingShifts = async (shiftPlans: ShiftPlan[], today: string): Promise => { if (!user) return []; try { const userShifts: DashboardData['upcomingShifts'] = []; // Check each plan for user assignments for (const plan of shiftPlans) { const scheduledShifts = (await shiftAssignmentService.getScheduledShiftsForPlan(plan.id)); if (plan.status !== 'published' || scheduledShifts.length === 0) { continue; } console.log(`🔍 Checking plan ${plan.name} for user shifts:`, scheduledShifts.length); for (const scheduledShift of scheduledShifts) { // Ensure assignedEmployees is an array const assignedEmployees = Array.isArray(scheduledShift.assignedEmployees) ? scheduledShift.assignedEmployees : []; if (scheduledShift.date >= today && assignedEmployees.includes(user.id)) { const timeSlot = plan.timeSlots.find(ts => ts.id === scheduledShift.timeSlotId); userShifts.push({ id: scheduledShift.id, date: formatShiftDate(scheduledShift.date), time: timeSlot ? `${timeSlot.startTime} - ${timeSlot.endTime}` : 'Unbekannt', type: timeSlot?.name || 'Unbekannt', assigned: true, planName: plan.name }); } } } // Sort by date and limit to 5 upcoming shifts return userShifts .sort((a, b) => { // Convert formatted dates back to Date objects for sorting const dateA = a.date === 'Heute' ? today : a.date === 'Morgen' ? new Date(new Date().setDate(new Date().getDate() + 1)).toISOString().split('T')[0] : a.date; const dateB = b.date === 'Heute' ? today : b.date === 'Morgen' ? new Date(new Date().setDate(new Date().getDate() + 1)).toISOString().split('T')[0] : b.date; return new Date(dateA).getTime() - new Date(dateB).getTime(); }) .slice(0, 5); } catch (error) { console.error('Error loading user shifts:', error); return []; } }; const calculateTeamStats = (employees: Employee[]) => { const totalEmployees = employees.length; // Count by type const managerCount = employees.filter(e => e.employeeType === 'manager').length; const personellCount = employees.filter(e => e.employeeType === 'personell').length; const traineeCount = employees.filter(e => e.isTrainee === true).length; const experiencedCount = employees.filter(e => e.isTrainee === false).length; return { totalEmployees, personell: personellCount, manager: managerCount, trainee: traineeCount, experienced: experiencedCount, }; }; const getRecentPlans = (plans: ShiftPlan[]): ShiftPlan[] => { return plans .filter(plan => !plan.isTemplate) .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) .slice(0, 3); }; const formatShiftDate = (dateString: string): string => { const today = new Date().toISOString().split('T')[0]; const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); const tomorrowString = tomorrow.toISOString().split('T')[0]; if (dateString === today) { return 'Heute'; } else if (dateString === tomorrowString) { return 'Morgen'; } else { return new Date(dateString).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); } }; const formatPlanPeriod = (plan: ShiftPlan): string => { if (!plan.startDate || !plan.endDate) return 'Kein Zeitraum definiert'; const start = new Date(plan.startDate).toLocaleDateString('de-DE'); const end = new Date(plan.endDate).toLocaleDateString('de-DE'); return `${start} - ${end}`; }; const calculatePlanProgress = (plan: ShiftPlan, shifts: ScheduledShift[]): { covered: number; total: number; percentage: number } => { if (!plan.id || shifts.length === 0) { console.log(`📊 Plan ${plan.name} has no scheduled shifts`); return { covered: 0, total: 0, percentage: 0 }; } const currentDate = new Date(); const totalShifts = shifts.length; const coveredShifts = shifts.filter(shift => { const shiftDate = new Date(shift.date); return currentDate > shiftDate; }).length; const percentage = totalShifts > 0 ? Math.round((coveredShifts / totalShifts) * 100) : 0; console.log(`📊 Plan ${plan.name} progress:`, { totalShifts, coveredShifts, percentage }); return { covered: coveredShifts, total: totalShifts, percentage }; }; if (loading) { return (
⏳ Lade Dashboard...
); } const regenerateScheduledShifts = async (planId: string) => { await shiftPlanService.regenerateScheduledShifts(planId); loadDashboardData(); }; const PlanDebugInfo = () => { if (!data.currentShiftPlan) return null; return (

🔍 Plan Debug Information:

Plan ID: {data.currentShiftPlan.id}
Status: {data.currentShiftPlan.status}
Is Template: {data.currentShiftPlan.isTemplate ? 'Yes' : 'No'}
Start Date: {data.currentShiftPlan.startDate}
End Date: {data.currentShiftPlan.endDate}
Shifts Defined: {data.currentShiftPlan.shifts?.length || 0}
Time Slots: {data.currentShiftPlan.timeSlots?.length || 0}
Scheduled Shifts: {data.currentShiftPlan.shifts.length || 0}
{data.currentShiftPlan.shifts && data.currentShiftPlan.shifts.length > 0 && (
Defined Shifts: {data.currentShiftPlan.shifts.slice(0, 3).map(shift => (
Day {shift.dayOfWeek} - TimeSlot: {shift.timeSlotId} - Required: {shift.requiredEmployees}
))} {data.currentShiftPlan.shifts.length > 3 &&
... and {data.currentShiftPlan.shifts.length - 3} more
}
)}
); }; const progress = data.currentShiftPlan ? calculatePlanProgress(data.currentShiftPlan, currentPlanShifts) : { covered: 0, total: 0, percentage: 0 }; return (
{/* Minimalist Welcome Section */}

Willkommen

{user?.firstname} {user?.lastname}

{/* Quick Actions - Nur fĂĽr Admins/Instandhalter */} {hasRole(['admin', 'maintenance']) && (

Schnellaktionen

{ e.currentTarget.style.transform = 'translateY(-2px)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; }}>
đź“…
Neuen Schichtplan
Erstellen
{ e.currentTarget.style.transform = 'translateY(-2px)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; }}>
👥
Mitarbeiter
Verwalten
{ e.currentTarget.style.transform = 'translateY(-2px)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; }}>
đź“‹
Alle Pläne
Anzeigen
)} {/* Haupt-Grid mit Informationen */}
{/* Aktueller Schichtplan */}

📊 Aktueller Schichtplan

{data.currentShiftPlan ? ( <>
{data.currentShiftPlan.name}
{formatPlanPeriod(data.currentShiftPlan)}
Fortschritt: {progress.covered}/{progress.total} Schichten ({progress.percentage}%)
0 ? '#854eca' : '#95a5a6', height: '8px', borderRadius: '10px', transition: 'width 0.3s ease' }} />
{progress.total === 0 && (
Keine Schichten im Plan definiert
)}
{data.currentShiftPlan.status === 'published' ? 'Aktiv' : 'Entwurf'}
) : (
đź“…
Kein aktiver Schichtplan
{hasRole(['admin', 'maintenance']) && ( )}
)}
{/* Team-Statistiken */}

👥 Team-Übersicht

Gesamte Belegschaft: {data.teamStats.totalEmployees}
Chef: {data.teamStats.manager}
Personal: {data.teamStats.personell}
{/* Unteres Grid */}
{/* Meine nächsten Schichten (für normale User) */} {hasRole(['user']) && (

⏰ Meine nächsten Schichten

{data.upcomingShifts.length > 0 ? (
{data.upcomingShifts.map(shift => (
{shift.date}
{shift.time}
{shift.type}
{shift.planName}
Zugewiesen
))}
) : (
⏰
Keine anstehenden Schichten
)}
)} {/* Letzte Schichtpläne (für Admins/Instandhalter) */} {hasRole(['admin', 'maintenance']) && (

📝 Schichtpläne

{data.recentPlans.length > 0 ? (
{data.recentPlans.map(plan => (
{plan.name}
{formatPlanPeriod(plan)}
Status: {plan.status === 'published' ? 'Veröffentlicht' : plan.status === 'draft' ? 'Entwurf' : 'Archiviert'} Anzeigen →
))}
) : (
đź“‹
Noch keine Schichtpläne erstellt
)}
)} {/* Schnelllinks */}

đź”— Schnellzugriff

{ e.currentTarget.style.backgroundColor = '#e9ecef'; }} onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#f8f9fa'; }}> 📅 Alle Schichtpläne anzeigen
{ e.currentTarget.style.backgroundColor = '#e9ecef'; }} onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#f8f9fa'; }}> âť“ Hilfe & Anleitung
{hasRole(['user']) && (
{ e.currentTarget.style.backgroundColor = '#e9ecef'; }} onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = '#f8f9fa'; }}> 📝 Meine Verfügbarkeit bearbeiten
)}
); }; export default Dashboard;