frontend with ony errors

This commit is contained in:
2025-10-12 00:59:57 +02:00
parent 75d4d86ef3
commit 90d8ae5140
31 changed files with 869 additions and 1481 deletions

View File

@@ -1,9 +1,9 @@
// frontend/src/pages/Employees/components/AvailabilityManager.tsx
import React, { useState, useEffect } from 'react';
import { Employee, EmployeeAvailability } from '../../../../../backend/src/models/employee';
import { employeeService } from '../../../services/employeeService';
import { shiftPlanService } from '../../../services/shiftPlanService';
import { ShiftPlan, TimeSlot, Shift } from '../../../../../backend/src/models/shiftPlan';
import { Employee, EmployeeAvailability } from '../../../models/Employee';
import { ShiftPlan, TimeSlot, Shift } from '../../../models/ShiftPlan';
interface AvailabilityManagerProps {
employee: Employee;
@@ -180,11 +180,13 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
// 3. Extract time slots from plans
let extractedTimeSlots = extractTimeSlotsFromPlans(plans);
// 4. Fallback to default slots if none found
/* 4. Fallback to default slots if none found
if (extractedTimeSlots.length === 0) {
console.log('⚠️ KEINE ZEIT-SLOTS GEFUNDEN, VERWENDE STANDARD-SLOTS');
extractedTimeSlots = getDefaultTimeSlots();
}
}*/
console.log('✅ GEFUNDENE ZEIT-SLOTS:', extractedTimeSlots.length, extractedTimeSlots);
setTimeSlots(extractedTimeSlots);
setShiftPlans(plans);

View File

@@ -1,6 +1,7 @@
// frontend/src/pages/Employees/components/EmployeeForm.tsx - KORRIGIERT
import React, { useState, useEffect } from 'react';
import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest } from '../../../types/employee';
import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest } from '../../../models/Employee';
import { ROLE_CONFIG, EMPLOYEE_TYPE_CONFIG } from '../../../models/defaults/employeeDefaults';
import { employeeService } from '../../../services/employeeService';
import { useAuth } from '../../../contexts/AuthContext';
@@ -11,34 +12,6 @@ interface EmployeeFormProps {
onCancel: () => void;
}
// Rollen Definition
const ROLE_OPTIONS = [
{ value: 'user', label: 'Mitarbeiter', description: 'Kann eigene Schichten einsehen' },
{ value: 'instandhalter', label: 'Instandhalter', description: 'Kann Schichtpläne erstellen und Mitarbeiter verwalten' },
{ value: 'admin', label: 'Administrator', description: 'Voller Zugriff auf alle Funktionen' }
] as const;
// Mitarbeiter Typen Definition
const EMPLOYEE_TYPE_OPTIONS = [
{
value: 'chef',
label: '👨‍💼 Chef/Administrator',
description: 'Vollzugriff auf alle Funktionen und Mitarbeiterverwaltung',
color: '#e74c3c'
},
{
value: 'erfahren',
label: '👴 Erfahren',
description: 'Langjährige Erfahrung, kann komplexe Aufgaben übernehmen',
color: '#3498db'
},
{
value: 'neuling',
label: '👶 Neuling',
description: 'Benötigt Einarbeitung und Unterstützung',
color: '#27ae60'
}
] as const;
const EmployeeForm: React.FC<EmployeeFormProps> = ({
mode,
@@ -50,9 +23,9 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
name: '',
email: '',
password: '',
role: 'user' as 'admin' | 'instandhalter' | 'user',
employeeType: 'neuling' as 'chef' | 'neuling' | 'erfahren',
isSufficientlyIndependent: false,
role: 'user' as 'admin' | 'maintenance' | 'user',
employeeType: 'trainee' as 'manager' | 'trainee' | 'experienced',
canWorkAlone: false,
isActive: true
});
const [loading, setLoading] = useState(false);
@@ -61,22 +34,20 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
useEffect(() => {
if (mode === 'edit' && employee) {
console.log('📝 Lade Mitarbeiter-Daten:', employee);
setFormData({
name: employee.name,
email: employee.email,
password: '', // Passwort wird beim Bearbeiten nicht angezeigt
role: employee.role,
employeeType: employee.employeeType,
isSufficientlyIndependent: employee.isSufficientlyIndependent,
canWorkAlone: employee.canWorkAlone,
isActive: employee.isActive
});
}
}, [mode, employee]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value, type } = e.target;
console.log(`🔄 Feld geändert: ${name} = ${value}`);
setFormData(prev => ({
...prev,
@@ -84,25 +55,14 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
}));
};
const handleRoleChange = (roleValue: 'admin' | 'instandhalter' | 'user') => {
console.log(`🔄 Rolle geändert: ${roleValue}`);
setFormData(prev => ({
...prev,
role: roleValue
}));
};
const handleEmployeeTypeChange = (employeeType: 'chef' | 'neuling' | 'erfahren') => {
console.log(`🔄 Mitarbeiter-Typ geändert: ${employeeType}`);
const handleEmployeeTypeChange = (employeeType: 'manager' | 'trainee' | 'experienced') => {
// Manager and experienced can work alone, trainee cannot
const canWorkAlone = employeeType === 'manager' || employeeType === 'experienced';
// Automatische Werte basierend auf Typ
const isSufficientlyIndependent = employeeType === 'chef' ? true :
employeeType === 'erfahren' ? true : false;
setFormData(prev => ({
...prev,
employeeType,
isSufficientlyIndependent
canWorkAlone
}));
};
@@ -111,8 +71,6 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
setLoading(true);
setError('');
console.log('📤 Sende Formulardaten:', formData);
try {
if (mode === 'create') {
const createData: CreateEmployeeRequest = {
@@ -121,52 +79,37 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
password: formData.password,
role: formData.role,
employeeType: formData.employeeType,
isSufficientlyIndependent: formData.isSufficientlyIndependent,
contractType: 'small', // Default value
canWorkAlone: formData.canWorkAlone
};
console.log(' Erstelle Mitarbeiter:', createData);
await employeeService.createEmployee(createData);
} else if (employee) {
const updateData: UpdateEmployeeRequest = {
name: formData.name.trim(),
role: formData.role,
employeeType: formData.employeeType,
isSufficientlyIndependent: formData.isSufficientlyIndependent,
contractType: employee.contractType, // Keep the existing contract type
canWorkAlone: formData.canWorkAlone,
isActive: formData.isActive,
};
console.log('✏️ Aktualisiere Mitarbeiter:', updateData);
await employeeService.updateEmployee(employee.id, updateData);
}
console.log('✅ Erfolg - rufe onSuccess auf');
onSuccess();
} catch (err: any) {
console.error('❌ Fehler beim Speichern:', err);
setError(err.message || `Fehler beim ${mode === 'create' ? 'Erstellen' : 'Aktualisieren'} des Mitarbeiters`);
} finally {
setLoading(false);
}
};
const isFormValid = () => {
if (mode === 'create') {
return formData.name.trim() &&
formData.email.trim() &&
formData.password.length >= 6;
}
return formData.name.trim() && formData.email.trim();
};
const isFormValid = mode === 'create'
? formData.name.trim() && formData.email.trim() && formData.password.length >= 6
: formData.name.trim() && formData.email.trim();
const getAvailableRoles = () => {
if (hasRole(['admin'])) {
return ROLE_OPTIONS;
}
if (hasRole(['instandhalter'])) {
return ROLE_OPTIONS.filter(role => role.value !== 'admin');
}
return ROLE_OPTIONS.filter(role => role.value === 'user');
};
const availableRoles = getAvailableRoles();
const availableRoles = hasRole(['admin'])
? ROLE_CONFIG
: ROLE_CONFIG.filter(role => role.value !== 'admin');
return (
<div style={{
@@ -294,7 +237,7 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
<h3 style={{ margin: '0 0 15px 0', color: '#495057' }}>👥 Mitarbeiter Kategorie</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{EMPLOYEE_TYPE_OPTIONS.map(type => (
{EMPLOYEE_TYPE_CONFIG.map(type => (
<div
key={type.value}
style={{
@@ -352,18 +295,6 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
</div>
))}
</div>
{/* Debug-Anzeige */}
<div style={{
marginTop: '15px',
padding: '10px',
backgroundColor: '#e8f4fd',
border: '1px solid #b6d7e8',
borderRadius: '4px',
fontSize: '12px'
}}>
<strong>Debug:</strong> Ausgewählter Typ: <code>{formData.employeeType}</code>
</div>
</div>
{/* Eigenständigkeit */}
@@ -386,29 +317,29 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
}}>
<input
type="checkbox"
name="isSufficientlyIndependent"
id="isSufficientlyIndependent"
checked={formData.isSufficientlyIndependent}
name="canWorkAlone"
id="canWorkAlone"
checked={formData.canWorkAlone}
onChange={handleChange}
disabled={formData.employeeType === 'chef'}
disabled={formData.employeeType === 'manager'}
style={{
width: '20px',
height: '20px',
opacity: formData.employeeType === 'chef' ? 0.5 : 1
opacity: formData.employeeType === 'manager' ? 0.5 : 1
}}
/>
<div style={{ flex: 1 }}>
<label htmlFor="isSufficientlyIndependent" style={{
<label htmlFor="canWorkAlone" style={{
fontWeight: 'bold',
color: '#2c3e50',
display: 'block',
opacity: formData.employeeType === 'chef' ? 0.5 : 1
opacity: formData.employeeType === 'manager' ? 0.5 : 1
}}>
Als ausreichend eigenständig markieren
{formData.employeeType === 'chef' && ' (Automatisch für Chefs)'}
{formData.employeeType === 'manager' && ' (Automatisch für Chefs)'}
</label>
<div style={{ fontSize: '14px', color: '#7f8c8d' }}>
{formData.employeeType === 'chef'
{formData.employeeType === 'manager'
? 'Chefs sind automatisch als eigenständig markiert.'
: 'Dieser Mitarbeiter kann komplexe Aufgaben eigenständig lösen und benötigt keine ständige Betreuung.'
}
@@ -416,14 +347,14 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
</div>
<div style={{
padding: '6px 12px',
backgroundColor: formData.isSufficientlyIndependent ? '#27ae60' : '#e74c3c',
backgroundColor: formData.canWorkAlone ? '#27ae60' : '#e74c3c',
color: 'white',
borderRadius: '15px',
fontSize: '12px',
fontWeight: 'bold',
opacity: formData.employeeType === 'chef' ? 0.7 : 1
opacity: formData.employeeType === 'manager' ? 0.7 : 1
}}>
{formData.isSufficientlyIndependent ? 'EIGENSTÄNDIG' : 'BETREUUNG'}
{formData.canWorkAlone ? 'EIGENSTÄNDIG' : 'BETREUUNG'}
</div>
</div>
</div>
@@ -451,14 +382,14 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
backgroundColor: formData.role === role.value ? '#fef9e7' : 'white',
cursor: 'pointer'
}}
onClick={() => handleRoleChange(role.value)}
onClick={() => setFormData(prev => ({ ...prev, role: role.value }))}
>
<input
type="radio"
name="role"
value={role.value}
checked={formData.role === role.value}
onChange={() => handleRoleChange(role.value)}
onChange={() => setFormData(prev => ({ ...prev, role: role.value }))}
style={{
marginRight: '10px',
marginTop: '2px'
@@ -537,14 +468,14 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
<button
type="submit"
disabled={loading || !isFormValid()}
disabled={loading || !isFormValid}
style={{
padding: '12px 24px',
backgroundColor: loading ? '#bdc3c7' : (isFormValid() ? '#27ae60' : '#95a5a6'),
backgroundColor: loading ? '#bdc3c7' : (isFormValid ? '#27ae60' : '#95a5a6'),
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: (loading || !isFormValid()) ? 'not-allowed' : 'pointer',
cursor: (loading || !isFormValid) ? 'not-allowed' : 'pointer',
fontWeight: 'bold'
}}
>

View File

@@ -1,6 +1,7 @@
// frontend/src/pages/Employees/components/EmployeeList.tsx - KORRIGIERT
import React, { useState } from 'react';
import { Employee } from '../../../types/employee';
import { ROLE_CONFIG, EMPLOYEE_TYPE_CONFIG } from '../../../../../backend/src/models/defaults/employeeDefaults';
import { Employee } from '../../../../../backend/src/models/employee';
import { useAuth } from '../../../contexts/AuthContext';
interface EmployeeListProps {
@@ -8,26 +9,22 @@ interface EmployeeListProps {
onEdit: (employee: Employee) => void;
onDelete: (employee: Employee) => void;
onManageAvailability: (employee: Employee) => void;
currentUserRole: 'admin' | 'instandhalter';
}
const EmployeeList: React.FC<EmployeeListProps> = ({
employees,
onEdit,
onDelete,
onManageAvailability,
currentUserRole
onManageAvailability
}) => {
const [filter, setFilter] = useState<'all' | 'active' | 'inactive'>('active');
const [searchTerm, setSearchTerm] = useState('');
const { user: currentUser } = useAuth();
const { user: currentUser, hasRole } = useAuth();
const filteredEmployees = employees.filter(employee => {
// Status-Filter
if (filter === 'active' && !employee.isActive) return false;
if (filter === 'inactive' && employee.isActive) return false;
// Suchfilter
if (searchTerm) {
const term = searchTerm.toLowerCase();
return (
@@ -41,28 +38,25 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
return true;
});
const getRoleBadgeColor = (role: string) => {
switch (role) {
case 'admin': return '#e74c3c';
case 'instandhalter': return '#3498db';
case 'user': return '#27ae60';
default: return '#95a5a6';
}
// Simplified permission checks
const canDeleteEmployee = (employee: Employee): boolean => {
if (!hasRole(['admin'])) return false;
if (employee.id === currentUser?.id) return false;
if (employee.role === 'admin' && !hasRole(['admin'])) return false;
return true;
};
const getEmployeeTypeBadge = (type: string) => {
switch (type) {
case 'chef': return { text: '👨‍💼 CHEF', color: '#e74c3c', bgColor: '#fadbd8' };
case 'erfahren': return { text: '👴 ERFAHREN', color: '#3498db', bgColor: '#d6eaf8' };
case 'neuling': return { text: '👶 NEULING', color: '#27ae60', bgColor: '#d5f4e6' };
default: return { text: 'UNBEKANNT', color: '#95a5a6', bgColor: '#ecf0f1' };
const canEditEmployee = (employee: Employee): boolean => {
if (hasRole(['admin'])) return true;
if (hasRole(['maintenance'])) {
return employee.role === 'user' || employee.id === currentUser?.id;
}
return false;
};
const getIndependenceBadge = (isIndependent: boolean) => {
return isIndependent
? { text: '✅ Eigenständig', color: '#27ae60', bgColor: '#d5f4e6' }
: { text: '❌ Betreuung', color: '#e74c3c', bgColor: '#fadbd8' };
// Using shared configuration for consistent styling
const getEmployeeTypeBadge = (type: keyof typeof EMPLOYEE_TYPE_CONFIG) => {
return EMPLOYEE_TYPE_CONFIG[type] || EMPLOYEE_TYPE_CONFIG.trainee;
};
const getStatusBadge = (isActive: boolean) => {
@@ -71,31 +65,10 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
: { text: 'Inaktiv', color: '#e74c3c', bgColor: '#fadbd8' };
};
// Kann Benutzer löschen?
const canDeleteEmployee = (employee: Employee): boolean => {
// Nur Admins können löschen
if (currentUserRole !== 'admin') return false;
// Kann sich nicht selbst löschen
if (employee.id === currentUser?.id) return false;
// Admins können nur von Admins gelöscht werden
if (employee.role === 'admin' && currentUserRole !== 'admin') return false;
return true;
};
// Kann Benutzer bearbeiten?
const canEditEmployee = (employee: Employee): boolean => {
// Admins können alle bearbeiten
if (currentUserRole === 'admin') return true;
// Instandhalter können nur User und sich selbst bearbeiten
if (currentUserRole === 'instandhalter') {
return employee.role === 'user' || employee.id === currentUser?.id;
}
return false;
const getIndependenceBadge = (canWorkAlone: boolean) => {
return canWorkAlone
? { text: '✅ Eigenständig', color: '#27ae60', bgColor: '#d5f4e6' }
: { text: '❌ Betreuung', color: '#e74c3c', bgColor: '#fadbd8' };
};
if (employees.length === 0) {
@@ -197,8 +170,8 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
{filteredEmployees.map(employee => {
const employeeType = getEmployeeTypeBadge(employee.employeeType);
const independence = getIndependenceBadge(employee.isSufficientlyIndependent);
const roleColor = getRoleBadgeColor(employee.role);
const independence = getIndependenceBadge(employee.canWorkAlone);
const roleColor = '#d5f4e6'; // Default color
const status = getStatusBadge(employee.isActive);
const canEdit = canEditEmployee(employee);
const canDelete = canDeleteEmployee(employee);
@@ -239,7 +212,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
<div style={{ textAlign: 'center' }}>
<span
style={{
backgroundColor: employeeType.bgColor,
backgroundColor: employeeType.color,
color: employeeType.color,
padding: '6px 12px',
borderRadius: '15px',
@@ -248,7 +221,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
display: 'inline-block'
}}
>
{employeeType.text}
{employeeType.label}
</span>
</div>
@@ -284,7 +257,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
}}
>
{employee.role === 'admin' ? 'ADMIN' :
employee.role === 'instandhalter' ? 'INSTANDHALTER' : 'MITARBEITER'}
employee.role === 'maintenance' ? 'INSTANDHALTER' : 'MITARBEITER'}
</span>
</div>
@@ -322,7 +295,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
flexWrap: 'wrap'
}}>
{/* Verfügbarkeit Button */}
{(currentUserRole === 'admin' || currentUserRole === 'instandhalter') && (
{(employee.role === 'admin' || employee.role === 'maintenance') && (
<button
onClick={() => onManageAvailability(employee)}
style={{
@@ -385,7 +358,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
)}
{/* Platzhalter für Symmetrie */}
{!canEdit && !canDelete && (currentUserRole !== 'admin' && currentUserRole !== 'instandhalter') && (
{!canEdit && !canDelete && (employee.role !== 'admin' && employee.role !== 'maintenance') && (
<div style={{ width: '32px', height: '32px' }}></div>
)}
</div>