// EmployeeList.tsx import React, { useState } from 'react'; import { ROLE_CONFIG, EMPLOYEE_TYPE_CONFIG } from '../../../models/defaults/employeeDefaults'; import { Employee } from '../../../models/Employee'; import { useAuth } from '../../../contexts/AuthContext'; interface EmployeeListProps { employees: Employee[]; onEdit: (employee: Employee) => void; onDelete: (employee: Employee) => void; onManageAvailability: (employee: Employee) => void; } type SortField = 'name' | 'employeeType' | 'canWorkAlone' | 'role' | 'lastLogin'; type SortDirection = 'asc' | 'desc'; // FIXED: Use the actual employee types from the Employee interface type EmployeeType = 'manager' | 'personell' | 'apprentice' | 'guest'; const EmployeeList: React.FC = ({ employees, onEdit, onDelete, onManageAvailability }) => { const [filter, setFilter] = useState<'all' | 'active' | 'inactive'>('active'); const [searchTerm, setSearchTerm] = useState(''); const [sortField, setSortField] = useState('name'); const [sortDirection, setSortDirection] = useState('asc'); const { user: currentUser, hasRole } = useAuth(); // Filter employees based on active/inactive and search term const filteredEmployees = employees.filter(employee => { if (filter === 'active' && !employee.isActive) return false; if (filter === 'inactive' && employee.isActive) return false; if (searchTerm) { const term = searchTerm.toLowerCase(); const fullName = `${employee.firstname} ${employee.lastname}`.toLowerCase(); return ( fullName.includes(term) || employee.email.toLowerCase().includes(term) || employee.employeeType.toLowerCase().includes(term) || (employee.roles && employee.roles.some(role => role.toLowerCase().includes(term))) ); } return true; }); // Helper to get highest role for sorting const getHighestRole = (roles: string[]): string => { if (roles.includes('admin')) return 'admin'; if (roles.includes('maintenance')) return 'maintenance'; return 'user'; }; // Sort employees based on selected field and direction const sortedEmployees = [...filteredEmployees].sort((a, b) => { let aValue: any; let bValue: any; switch (sortField) { case 'name': aValue = `${a.firstname} ${a.lastname}`.toLowerCase(); bValue = `${b.firstname} ${b.lastname}`.toLowerCase(); break; case 'employeeType': aValue = a.employeeType; bValue = b.employeeType; break; case 'canWorkAlone': aValue = a.canWorkAlone; bValue = b.canWorkAlone; break; case 'role': aValue = getHighestRole(a.roles || []); bValue = getHighestRole(b.roles || []); break; case 'lastLogin': // Handle null values for lastLogin (put them at the end) aValue = a.lastLogin ? new Date(a.lastLogin).getTime() : 0; bValue = b.lastLogin ? new Date(b.lastLogin).getTime() : 0; break; default: return 0; } if (sortDirection === 'asc') { return aValue < bValue ? -1 : aValue > bValue ? 1 : 0; } else { return aValue > bValue ? -1 : aValue < bValue ? 1 : 0; } }); const handleSort = (field: SortField) => { if (sortField === field) { // Toggle direction if same field setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { // New field, default to ascending setSortField(field); setSortDirection('asc'); } }; const getSortIndicator = (field: SortField) => { if (sortField !== field) return '↕'; return sortDirection === 'asc' ? '↑' : '↓'; }; // Simplified permission checks const canDeleteEmployee = (employee: Employee): boolean => { if (!hasRole(['admin'])) return false; if (employee.id === currentUser?.id) return false; if (employee.roles?.includes('admin') && !hasRole(['admin'])) return false; return true; }; const canEditEmployee = (employee: Employee): boolean => { if (hasRole(['admin'])) return true; if (hasRole(['maintenance'])) { return !employee.roles?.includes('admin') || employee.id === currentUser?.id; } return false; }; const getEmployeeTypeBadge = (type: EmployeeType, isTrainee: boolean = false) => { const config = EMPLOYEE_TYPE_CONFIG[type]; // FIXED: Updated color mapping for actual employee types const bgColor = type === 'manager' ? '#fadbd8' // light red : type === 'personell' ? isTrainee ? '#d5f4e6' : '#d6eaf8' // light green for trainee, light blue for experienced : type === 'apprentice' ? '#e8d7f7' // light purple for apprentice : '#f8f9fa'; // light gray for guest return { text: config.label, color: config.color, bgColor }; }; const getStatusBadge = (isActive: boolean) => { return isActive ? { text: 'Aktiv', color: '#27ae60', bgColor: '#d5f4e6' } : { text: 'Inaktiv', color: '#e74c3c', bgColor: '#fadbd8' }; }; const getIndependenceBadge = (canWorkAlone: boolean) => { return canWorkAlone ? { text: 'Eigenständig', color: '#27ae60', bgColor: '#d5f4e6' } : { text: 'Betreuung', color: '#e74c3c', bgColor: '#fadbd8' }; }; type Role = 'admin' | 'maintenance' | 'user'; const getRoleBadge = (roles: string[] = []) => { const highestRole = getHighestRole(roles); const { label, color } = ROLE_CONFIG.find(r => r.value === highestRole)!; const bgColor = highestRole === 'user' ? '#d5f4e6' : highestRole === 'maintenance' ? '#d6eaf8' : '#fadbd8'; // admin return { text: label, color, bgColor, roles }; }; const formatRoleDisplay = (roles: string[] = []) => { if (roles.length === 0) return 'MITARBEITER'; if (roles.includes('admin')) return 'ADMIN'; if (roles.includes('maintenance')) return 'INSTANDHALTER'; return 'MITARBEITER'; }; if (employees.length === 0) { return (
👥

Noch keine Mitarbeiter

Erstellen Sie den ersten Mitarbeiter, um zu beginnen.

); } return (
{/* Filter und Suche */}
setSearchTerm(e.target.value)} style={{ padding: '8px 12px', border: '1px solid #ddd', borderRadius: '4px', flex: 1, maxWidth: '400px' }} />
{sortedEmployees.length} von {employees.length} Mitarbeitern
{/* Mitarbeiter Tabelle */}
{/* Tabellen-Header */}
handleSort('name')} style={{ cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '5px' }} > Name & E-Mail {getSortIndicator('name')}
handleSort('employeeType')} style={{ textAlign: 'center', cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '5px', justifyContent: 'center' }} > Typ {getSortIndicator('employeeType')}
handleSort('canWorkAlone')} style={{ textAlign: 'center', cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '5px', justifyContent: 'center' }} > Eigenständigkeit {getSortIndicator('canWorkAlone')}
handleSort('role')} style={{ textAlign: 'center', cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '5px', justifyContent: 'center' }} > Rolle {getSortIndicator('role')}
Status
handleSort('lastLogin')} style={{ textAlign: 'center', cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '5px', justifyContent: 'center' }} > Letzter Login {getSortIndicator('lastLogin')}
Aktionen
{sortedEmployees.map(employee => { // FIXED: Type assertion to ensure type safety const employeeType = getEmployeeTypeBadge(employee.employeeType as EmployeeType, employee.isTrainee); const independence = getIndependenceBadge(employee.canWorkAlone); const roleInfo = getRoleBadge(employee.roles); const status = getStatusBadge(employee.isActive); const canEdit = canEditEmployee(employee); const canDelete = canDeleteEmployee(employee); return (
{/* Name & E-Mail */}
{employee.firstname} {employee.lastname} {employee.id === currentUser?.id && ( (Sie) )}
{employee.email}
{/* Mitarbeiter Typ */}
{employeeType.text}
{/* Eigenständigkeit */}
{independence.text}
{/* Rolle */}
{formatRoleDisplay(employee.roles)}
{/* Status */}
{status.text}
{/* Letzter Login */}
{employee.lastLogin ? new Date(employee.lastLogin).toLocaleDateString('de-DE') : 'Noch nie' }
{/* Aktionen */}
{/* Verfügbarkeit Button */} {/* Bearbeiten Button */} {canEdit && ( )} {/* Löschen Button */} {canDelete && ( )} {/* Platzhalter für Symmetrie */} {!canEdit && !canDelete && (
)}
); })}
{/* Info-Box über Berechtigungen */}
💡 Informationen zu Berechtigungen:
  • Admins können alle Benutzer bearbeiten und löschen
  • Instandhalter können nur Mitarbeiter bearbeiten
  • Mindestens ein Admin muss immer im System vorhanden sein
  • Benutzer können sich nicht selbst löschen
{/* Legende für Mitarbeiter Typen */}
🎯 Legende Mitarbeiter Typen:
👨‍💼 CHEF Vollzugriff
👨‍🏭 PERSONAL Reguläre Mitarbeiter
👨‍🎓 AUSZUBILDENDER Auszubildende
👤 GAST Externe Mitarbeiter
); }; export default EmployeeList;