From 5809e6c44c9aa25228345e3aff81813b891c19eb Mon Sep 17 00:00:00 2001 From: donpat1to Date: Tue, 21 Oct 2025 20:23:12 +0200 Subject: [PATCH] fixed role naming instandhalter -> maintenance --- backend/src/routes/employees.ts | 4 +- backend/src/routes/scheduledShifts.ts | 4 +- backend/src/routes/shiftPlans.ts | 10 +- frontend/src/App.tsx | 8 +- frontend/src/components/Layout/Navigation.tsx | 10 +- frontend/src/pages/Dashboard/Dashboard.tsx | 6 +- .../components/AvailabilityManager.tsx | 43 +++- .../Employees/components/EmployeeForm.tsx | 202 ++++++++++-------- .../Employees/components/EmployeeList.tsx | 15 +- frontend/src/pages/Help/Help.tsx | 147 +------------ .../src/pages/Settings/type/SettingsType.tsx | 2 +- .../src/pages/ShiftPlans/ShiftPlanList.tsx | 4 +- .../src/pages/ShiftPlans/ShiftPlanView.tsx | 4 +- 13 files changed, 193 insertions(+), 266 deletions(-) diff --git a/backend/src/routes/employees.ts b/backend/src/routes/employees.ts index 2c0ea58..f2ceeff 100644 --- a/backend/src/routes/employees.ts +++ b/backend/src/routes/employees.ts @@ -20,9 +20,9 @@ router.use(authMiddleware); // Employee CRUD Routes router.get('/', authMiddleware, getEmployees); -router.get('/:id', requireRole(['admin', 'instandhalter']), getEmployee); +router.get('/:id', requireRole(['admin', 'maintenance']), getEmployee); router.post('/', requireRole(['admin']), createEmployee); -router.put('/:id', requireRole(['admin']), updateEmployee); +router.put('/:id', requireRole(['admin', 'maintenance']), updateEmployee); router.delete('/:id', requireRole(['admin']), deleteEmployee); router.put('/:id/password', authMiddleware, changePassword); router.put('/:id/last-login', authMiddleware, updateLastLogin); diff --git a/backend/src/routes/scheduledShifts.ts b/backend/src/routes/scheduledShifts.ts index 91d1181..3ca8e20 100644 --- a/backend/src/routes/scheduledShifts.ts +++ b/backend/src/routes/scheduledShifts.ts @@ -14,9 +14,9 @@ const router = express.Router(); router.use(authMiddleware); -router.post('/:id/generate-shifts', requireRole(['admin', 'instandhalter']), generateScheduledShiftsForPlan); +router.post('/:id/generate-shifts', requireRole(['admin', 'maintenance']), generateScheduledShiftsForPlan); -router.post('/:id/regenerate-shifts', requireRole(['admin', 'instandhalter']), regenerateScheduledShifts); +router.post('/:id/regenerate-shifts', requireRole(['admin', 'maintenance']), regenerateScheduledShifts); // GET all scheduled shifts for a plan router.get('/plan/:planId', authMiddleware, getScheduledShiftsFromPlan); diff --git a/backend/src/routes/shiftPlans.ts b/backend/src/routes/shiftPlans.ts index 8f4a71d..164b3ad 100644 --- a/backend/src/routes/shiftPlans.ts +++ b/backend/src/routes/shiftPlans.ts @@ -24,18 +24,18 @@ router.get('/' , authMiddleware, getShiftPlans); router.get('/:id', authMiddleware, getShiftPlan); // POST create new shift plan -router.post('/', requireRole(['admin', 'instandhalter']), createShiftPlan); +router.post('/', requireRole(['admin', 'maintenance']), createShiftPlan); // POST create new plan from preset -router.post('/from-preset', requireRole(['admin', 'instandhalter']), createFromPreset); +router.post('/from-preset', requireRole(['admin', 'maintenance']), createFromPreset); // PUT update shift plan or template -router.put('/:id', requireRole(['admin', 'instandhalter']), updateShiftPlan); +router.put('/:id', requireRole(['admin', 'maintenance']), updateShiftPlan); // DELETE shift plan or template -router.delete('/:id', requireRole(['admin', 'instandhalter']), deleteShiftPlan); +router.delete('/:id', requireRole(['admin', 'maintenance']), deleteShiftPlan); // POST clear assignments and reset to draft -router.post('/:id/clear-assignments', requireRole(['admin', 'instandhalter']), clearAssignments); +router.post('/:id/clear-assignments', requireRole(['admin', 'maintenance']), clearAssignments); export default router; \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index db3cc92..ce6720d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -19,7 +19,7 @@ import Setup from './pages/Setup/Setup'; // Protected Route Component const ProtectedRoute: React.FC<{ children: React.ReactNode; roles?: string[] }> = ({ children, - roles = ['admin', 'instandhalter', 'user'] + roles = ['admin', 'maintenance', 'user'] }) => { const { user, loading, hasRole } = useAuth(); @@ -91,12 +91,12 @@ const AppContent: React.FC = () => { } /> + } /> + } /> @@ -106,7 +106,7 @@ const AppContent: React.FC = () => { } /> + } /> diff --git a/frontend/src/components/Layout/Navigation.tsx b/frontend/src/components/Layout/Navigation.tsx index 731822e..18be3a9 100644 --- a/frontend/src/components/Layout/Navigation.tsx +++ b/frontend/src/components/Layout/Navigation.tsx @@ -30,11 +30,11 @@ const Navigation: React.FC = () => { }; const navigationItems = [ - { path: '/', label: 'Dashboard', roles: ['admin', 'instandhalter', 'user'] }, - { path: '/shift-plans', label: 'Schichtpläne', roles: ['admin', 'instandhalter', 'user'] }, - { path: '/employees', label: 'Mitarbeiter', roles: ['admin', 'instandhalter'] }, - { path: '/help', label: 'Hilfe', roles: ['admin', 'instandhalter', 'user'] }, - { path: '/settings', label: 'Einstellungen', roles: ['admin', 'instandhalter', 'user'] }, + { path: '/', label: 'Dashboard', roles: ['admin', 'maintenance', 'user'] }, + { path: '/shift-plans', label: 'Schichtpläne', roles: ['admin', 'maintenance', 'user'] }, + { path: '/employees', label: 'Mitarbeiter', roles: ['admin', 'maintenance'] }, + { path: '/help', label: 'Hilfe', roles: ['admin', 'maintenance', 'user'] }, + { path: '/settings', label: 'Einstellungen', roles: ['admin', 'maintenance', 'user'] }, ]; const filteredNavigation = navigationItems.filter(item => diff --git a/frontend/src/pages/Dashboard/Dashboard.tsx b/frontend/src/pages/Dashboard/Dashboard.tsx index a1bc583..dc581a9 100644 --- a/frontend/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/src/pages/Dashboard/Dashboard.tsx @@ -393,7 +393,7 @@ const Dashboard: React.FC = () => { {/* Quick Actions - Nur für Admins/Instandhalter */} - {hasRole(['admin', 'instandhalter']) && ( + {hasRole(['admin', 'maintenance']) && (

Schnellaktionen

{
📅
Kein aktiver Schichtplan
- {hasRole(['admin', 'instandhalter']) && ( + {hasRole(['admin', 'maintenance']) && (
diff --git a/frontend/src/pages/Employees/components/EmployeeForm.tsx b/frontend/src/pages/Employees/components/EmployeeForm.tsx index 754917d..c4feaa0 100644 --- a/frontend/src/pages/Employees/components/EmployeeForm.tsx +++ b/frontend/src/pages/Employees/components/EmployeeForm.tsx @@ -99,22 +99,27 @@ const EmployeeForm: React.FC = ({ if (checked) { return { ...prev, - roles: [...prev.roles, role] + roles: [role] }; } else { - return { + const newRoles = prev.roles.filter(r => r !== role); + return{ ...prev, - roles: prev.roles.filter(r => r !== role) + roles: newRoles.length > 0 ? newRoles : ['user'] }; } }); }; const handleEmployeeTypeChange = (employeeType: EmployeeType) => { - // Determine if contract type should be shown and set default - const requiresContract = employeeType !== 'guest'; - const defaultContractType = requiresContract ? 'small' as ContractType : undefined; - + // Determine contract type based on employee type + let contractType: ContractType | undefined; + if (employeeType === 'manager' || employeeType === 'apprentice') { + contractType = 'flexible'; + } else if (employeeType !== 'guest') { + contractType = 'small'; + } + // Determine if can work alone based on employee type const canWorkAlone = employeeType === 'manager' || (employeeType === 'personell' && !formData.isTrainee); @@ -125,7 +130,7 @@ const EmployeeForm: React.FC = ({ setFormData(prev => ({ ...prev, employeeType, - contractType: defaultContractType, + contractType, canWorkAlone, isTrainee })); @@ -273,7 +278,7 @@ const EmployeeForm: React.FC = ({ onChange={handleChange} required style={{ - width: '100%', + width: '94%', padding: '10px', border: '1px solid #ddd', borderRadius: '4px', @@ -294,7 +299,7 @@ const EmployeeForm: React.FC = ({ onChange={handleChange} required style={{ - width: '100%', + width: '94%', padding: '10px', border: '1px solid #ddd', borderRadius: '4px', @@ -311,7 +316,7 @@ const EmployeeForm: React.FC = ({ E-Mail Adresse (automatisch generiert)
= ({ required minLength={6} style={{ - width: '100%', + width: '97%', padding: '10px', border: '1px solid #ddd', borderRadius: '4px', @@ -355,78 +360,6 @@ const EmployeeForm: React.FC = ({ )}
- {/* Vertragstyp (nur für Admins und interne Mitarbeiter) */} - {hasRole(['admin']) && showContractType && ( -
-

📝 Vertragstyp

- -
- {contractTypeOptions.map(contract => ( -
handleContractTypeChange(contract.value)} - > - handleContractTypeChange(contract.value)} - style={{ - marginRight: '12px', - marginTop: '2px', - width: '18px', - height: '18px' - }} - /> -
-
- {contract.label} -
-
- {contract.description} -
-
-
- {contract.value.toUpperCase()} -
-
- ))} -
-
- )} - {/* Mitarbeiter Kategorie */}
= ({ )}
+ {/* Vertragstyp (nur für Admins und interne Mitarbeiter) */} + {hasRole(['admin']) && showContractType && ( +
+

📝 Vertragstyp

+ +
+ {contractTypeOptions.map(contract => { + const isFlexibleDisabled = contract.value === 'flexible' && formData.employeeType === 'personell'; + const isSmallLargeDisabled = (contract.value === 'small' || contract.value === 'large') && + (formData.employeeType === 'manager' || formData.employeeType === 'apprentice'); + const isDisabled = isFlexibleDisabled || isSmallLargeDisabled; + + return ( +
handleContractTypeChange(contract.value)} + > + handleContractTypeChange(contract.value)} + disabled={isDisabled} + style={{ + marginRight: '12px', + marginTop: '2px', + width: '18px', + height: '18px' + }} + /> +
+
+ {contract.label} + {isFlexibleDisabled && ( + + (Nicht verfügbar für Personell) + + )} + {isSmallLargeDisabled && ( + + (Nicht verfügbar für {formData.employeeType === 'manager' ? 'Manager' : 'Auszubildende'}) + + )} +
+
+ {contract.description} +
+
+
+ {contract.value.toUpperCase()} +
+
+ ); + })} +
+
+ )} + {/* Eigenständigkeit */}
= ({ 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; @@ -67,7 +74,6 @@ const EmployeeList: React.FC = ({ bValue = b.canWorkAlone; break; case 'role': - // Use the highest role for sorting aValue = getHighestRole(a.roles || []); bValue = getHighestRole(b.roles || []); break; @@ -87,13 +93,6 @@ const EmployeeList: React.FC = ({ } }); - // 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'; - }; - const handleSort = (field: SortField) => { if (sortField === field) { // Toggle direction if same field diff --git a/frontend/src/pages/Help/Help.tsx b/frontend/src/pages/Help/Help.tsx index 86edc61..23b7d70 100644 --- a/frontend/src/pages/Help/Help.tsx +++ b/frontend/src/pages/Help/Help.tsx @@ -80,134 +80,6 @@ const Help: React.FC = () => {

❓ Hilfe & Support - Scheduling Algorithmus

- {/* Algorithm Visualization */} -
-
-

🧠 Algorithmus Visualisierung

- -
- - {/* Stage Indicators */} -
- {algorithmStages.map((stage, index) => ( - -
-
- {index + 1} -
-
- {stage.title.split(':')[0]} -
-
- - {index < algorithmStages.length - 1 && ( -
index ? stage.color : '#ecf0f1', - alignSelf: 'center', - margin: '0 10px', - transition: 'all 0.5s ease' - }} /> - )} - - ))} -
- - {/* Current Stage Details */} -
-

- {algorithmStages[currentStage].title} -

-

- {algorithmStages[currentStage].description} -

-
- {algorithmStages[currentStage].steps.map((step, stepIndex) => ( -
- - {step} -
- ))} -
-
-
- {/* Business Rules */}
{

🏗️ Phasen-basierter Ansatz

Der Algorithmus arbeitet in klar definierten Phasen, um komplexe Probleme schrittweise zu lösen und Stabilität zu gewährleisten.

- -
-

⚖️ Wert-basierte Entscheidungen

-

Jede Zuweisung wird anhand eines Wertesystems bewertet, das Verfügbarkeit, Erfahrung und aktuelle Auslastung berücksichtigt.

-
- -
-

🔧 Automatische Reparatur

-

Probleme werden automatisch erkannt und durch intelligente Tausch- und Bewegungsoperationen behoben.

-
- -
-

📊 Transparente Berichterstattung

-

Detaillierte Berichte zeigen genau, welche Probleme behoben wurden und welche verbleiben.

-
-
- - +
{ marginBottom: '30px' }}>

📅 Schichtpläne

- {hasRole(['admin', 'instandhalter']) && ( + {hasRole(['admin', 'maintenance']) && ( - {hasRole(['admin', 'instandhalter']) && ( + {hasRole(['admin', 'maintenance']) && ( <>
- {shiftPlan.status === 'published' && hasRole(['admin', 'instandhalter']) && ( + {shiftPlan.status === 'published' && hasRole(['admin', 'maintenance']) && (
- {hasRole(['admin', 'instandhalter']) && ( + {hasRole(['admin', 'maintenance']) && (