fixed role handling for employees

This commit is contained in:
2025-10-20 11:27:06 +02:00
parent ec28c061a0
commit 3c4fbc0798
18 changed files with 640 additions and 318 deletions

View File

@@ -352,7 +352,7 @@ const Dashboard: React.FC = () => {
}}>
<div>
<h1 style={{ margin: '0 0 10px 0', color: '#2c3e50' }}>
Willkommen zurück, {user?.name}! 👋
Willkommen zurück, {user?.firstname} {user?.lastname} ! 👋
</h1>
<p style={{ margin: 0, color: '#546e7a', fontSize: '16px' }}>
{new Date().toLocaleDateString('de-DE', {

View File

@@ -84,16 +84,26 @@ const EmployeeManagement: React.FC = () => {
});
};
// Helper function to get full name
const getFullName = (employee: Employee): string => {
return `${employee.firstname} ${employee.lastname}`;
};
// Verbesserte Lösch-Funktion mit Bestätigungs-Dialog
const handleDeleteEmployee = async (employee: Employee) => {
try {
const fullName = getFullName(employee);
// Bestätigungs-Dialog basierend auf Rolle
let confirmMessage = `Möchten Sie den Mitarbeiter "${employee.name}" wirklich PERMANENT LÖSCHEN?\n\nDie Daten des Mitarbeiters werden unwiderruflich gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.`;
let confirmMessage = `Möchten Sie den Mitarbeiter "${fullName}" (${employee.email}) wirklich PERMANENT LÖSCHEN?\n\nDie Daten des Mitarbeiters werden unwiderruflich gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.`;
let confirmTitle = 'Mitarbeiter löschen';
if (employee.role === 'admin') {
// Check if employee has admin role (now in roles array)
const isAdmin = employee.roles?.includes('admin') || false;
if (isAdmin) {
const adminCount = employees.filter(emp =>
emp.role === 'admin' && emp.isActive
(emp.roles?.includes('admin') || false) && emp.isActive
).length;
if (adminCount <= 1) {
@@ -106,7 +116,7 @@ const EmployeeManagement: React.FC = () => {
}
confirmTitle = 'Administrator löschen';
confirmMessage = `Möchten Sie den Administrator "${employee.name}" wirklich PERMANENT LÖSCHEN?\n\nAchtung: Diese Aktion ist permanent und kann nicht rückgängig gemacht werden.`;
confirmMessage = `Möchten Sie den Administrator "${fullName}" (${employee.email}) wirklich PERMANENT LÖSCHEN?\n\nAchtung: Diese Aktion ist permanent und kann nicht rückgängig gemacht werden.`;
}
const confirmed = await confirmDialog({
@@ -119,7 +129,7 @@ const EmployeeManagement: React.FC = () => {
if (!confirmed) return;
console.log('Starting deletion process for employee:', employee.name);
console.log('Starting deletion process for employee:', fullName);
await employeeService.deleteEmployee(employee.id);
console.log('Employee deleted, reloading list');
@@ -130,7 +140,7 @@ const EmployeeManagement: React.FC = () => {
showNotification({
type: 'success',
title: 'Erfolg',
message: `Mitarbeiter "${employee.name}" wurde erfolgreich gelöscht`
message: `Mitarbeiter "${fullName}" wurde erfolgreich gelöscht`
});
} catch (err: any) {
@@ -218,7 +228,7 @@ const EmployeeManagement: React.FC = () => {
<EmployeeList
employees={employees}
onEdit={handleEditEmployee}
onDelete={handleDeleteEmployee} // Jetzt mit Employee-Objekt
onDelete={handleDeleteEmployee}
onManageAvailability={handleManageAvailability}
/>
)}

View File

@@ -1,4 +1,3 @@
// frontend/src/pages/Employees/components/AvailabilityManager.tsx
import React, { useState, useEffect } from 'react';
import { employeeService } from '../../../services/employeeService';
import { shiftPlanService } from '../../../services/shiftPlanService';
@@ -200,19 +199,11 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
return updated;
} else {
// Create new availability using shiftId directly
const shift = selectedPlan?.shifts?.find(s => s.id === shiftId);
if (!shift) {
console.error('❌ Shift nicht gefunden:', shiftId);
return prev;
}
const newAvailability: Availability = {
id: `temp-${shiftId}-${Date.now()}`,
employeeId: employee.id,
planId: selectedPlanId,
shiftId: shiftId, // Use shiftId directly
dayOfWeek: shift.dayOfWeek, // Keep for backward compatibility if needed
timeSlotId: shift.timeSlotId, // Keep for backward compatibility if needed
shiftId: shiftId,
preferenceLevel: level,
isAvailable: level !== 3
};
@@ -433,9 +424,7 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
planId: selectedPlanId,
availabilities: validAvailabilities.map(avail => ({
planId: selectedPlanId,
shiftId: avail.shiftId, // Use shiftId directly
dayOfWeek: avail.dayOfWeek, // Keep for backward compatibility
timeSlotId: avail.timeSlotId, // Keep for backward compatibility
shiftId: avail.shiftId,
preferenceLevel: avail.preferenceLevel,
notes: avail.notes
}))
@@ -472,6 +461,9 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
});
const shiftsCount = allShiftIds.size;
// Get full name for display
const employeeFullName = `${employee.firstname} ${employee.lastname}`;
return (
<div style={{
maxWidth: '1900px',
@@ -517,10 +509,13 @@ const AvailabilityManager: React.FC<AvailabilityManagerProps> = ({
{/* Employee Info */}
<div style={{ marginBottom: '20px' }}>
<h3 style={{ margin: '0 0 10px 0', color: '#34495e' }}>
{employee.name}
{employeeFullName}
</h3>
<p style={{ margin: 0, color: '#7f8c8d' }}>
Legen Sie die Verfügbarkeit für {employee.name} fest (basierend auf Shift-IDs).
<strong>Email:</strong> {employee.email}
</p>
<p style={{ margin: '5px 0 0 0', color: '#7f8c8d' }}>
Legen Sie die Verfügbarkeit für {employeeFullName} fest (basierend auf Shift-IDs).
</p>
</div>

View File

@@ -1,4 +1,3 @@
// frontend/src/pages/Employees/components/EmployeeForm.tsx
import React, { useState, useEffect } from 'react';
import { Employee, CreateEmployeeRequest, UpdateEmployeeRequest } from '../../../models/Employee';
import { ROLE_CONFIG, EMPLOYEE_TYPE_CONFIG } from '../../../models/defaults/employeeDefaults';
@@ -19,10 +18,11 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
onCancel
}) => {
const [formData, setFormData] = useState({
name: '',
email: '',
firstname: '',
lastname: '',
email: '', // Will be auto-generated and display only
password: '',
role: 'user' as 'admin' | 'maintenance' | 'user',
roles: ['user'] as string[], // Changed from single role to array
employeeType: 'trainee' as 'manager' | 'trainee' | 'experienced',
contractType: 'small' as 'small' | 'large',
canWorkAlone: false,
@@ -37,13 +37,33 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
const [error, setError] = useState('');
const { hasRole } = useAuth();
// Generate email preview
const generateEmailPreview = (firstname: string, lastname: string): string => {
const convertUmlauts = (str: string): string => {
return str
.toLowerCase()
.replace(/ü/g, 'ue')
.replace(/ö/g, 'oe')
.replace(/ä/g, 'ae')
.replace(/ß/g, 'ss');
};
const cleanFirstname = convertUmlauts(firstname).replace(/[^a-z0-9]/g, '');
const cleanLastname = convertUmlauts(lastname).replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
};
const emailPreview = generateEmailPreview(formData.firstname, formData.lastname);
useEffect(() => {
if (mode === 'edit' && employee) {
setFormData({
name: employee.name,
firstname: employee.firstname,
lastname: employee.lastname,
email: employee.email,
password: '', // Passwort wird beim Bearbeiten nicht angezeigt
role: employee.role,
password: '', // Password wird beim Bearbeiten nicht angezeigt
roles: employee.roles || ['user'], // Use roles array
employeeType: employee.employeeType,
contractType: employee.contractType,
canWorkAlone: employee.canWorkAlone,
@@ -69,6 +89,24 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
}));
};
const handleRoleChange = (role: string, checked: boolean) => {
setFormData(prev => {
if (checked) {
// Add role if checked
return {
...prev,
roles: [...prev.roles, role]
};
} else {
// Remove role if unchecked
return {
...prev,
roles: prev.roles.filter(r => r !== role)
};
}
});
};
const handleEmployeeTypeChange = (employeeType: 'manager' | 'trainee' | 'experienced') => {
// Manager and experienced can work alone, trainee cannot
const canWorkAlone = employeeType === 'manager' || employeeType === 'experienced';
@@ -95,10 +133,10 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
try {
if (mode === 'create') {
const createData: CreateEmployeeRequest = {
name: formData.name.trim(),
email: formData.email.trim(),
firstname: formData.firstname.trim(),
lastname: formData.lastname.trim(),
password: formData.password,
role: formData.role,
roles: formData.roles, // Use roles array
employeeType: formData.employeeType,
contractType: formData.contractType,
canWorkAlone: formData.canWorkAlone
@@ -106,8 +144,9 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
await employeeService.createEmployee(createData);
} else if (employee) {
const updateData: UpdateEmployeeRequest = {
name: formData.name.trim(),
role: formData.role,
firstname: formData.firstname.trim(),
lastname: formData.lastname.trim(),
roles: formData.roles, // Use roles array
employeeType: formData.employeeType,
contractType: formData.contractType,
canWorkAlone: formData.canWorkAlone,
@@ -141,8 +180,8 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
};
const isFormValid = mode === 'create'
? formData.name.trim() && formData.email.trim() && formData.password.length >= 6
: formData.name.trim() && formData.email.trim();
? formData.firstname.trim() && formData.lastname.trim() && formData.password.length >= 6
: formData.firstname.trim() && formData.lastname.trim();
const availableRoles = hasRole(['admin'])
? ROLE_CONFIG
@@ -200,12 +239,12 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px' }}>
<div>
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
Vollständiger Name *
Vorname *
</label>
<input
type="text"
name="name"
value={formData.name}
name="firstname"
value={formData.firstname}
onChange={handleChange}
required
style={{
@@ -215,34 +254,54 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
borderRadius: '4px',
fontSize: '16px'
}}
placeholder="Max Mustermann"
placeholder="Max"
/>
</div>
<div>
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
E-Mail Adresse *
Nachname *
</label>
<input
type="email"
name="email"
value={formData.email}
type="text"
name="lastname"
value={formData.lastname}
onChange={handleChange}
required
disabled={mode === 'edit'} // Email cannot be changed in edit mode
style={{
width: '100%',
padding: '10px',
border: '1px solid #ddd',
borderRadius: '4px',
fontSize: '16px',
backgroundColor: mode === 'edit' ? '#f8f9fa' : 'white'
fontSize: '16px'
}}
placeholder="max.mustermann@example.com"
placeholder="Mustermann"
/>
</div>
</div>
{/* Email Preview */}
<div style={{ marginTop: '15px' }}>
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
E-Mail Adresse (automatisch generiert)
</label>
<div style={{
width: '100%',
padding: '10px',
border: '1px solid #ddd',
borderRadius: '4px',
fontSize: '16px',
backgroundColor: '#f8f9fa',
color: '#6c757d'
}}>
{emailPreview || 'max.mustermann@sp.de'}
</div>
<div style={{ fontSize: '12px', color: '#7f8c8d', marginTop: '5px' }}>
Die E-Mail Adresse wird automatisch aus Vorname und Nachname generiert.
{formData.firstname && formData.lastname && ` Beispiel: ${emailPreview}`}
</div>
</div>
{mode === 'create' && (
<div style={{ marginTop: '15px' }}>
<label style={{ display: 'block', marginBottom: '8px', fontWeight: 'bold', color: '#2c3e50' }}>
@@ -353,7 +412,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_CONFIG.map(type => (
{Object.values(EMPLOYEE_TYPE_CONFIG).map(type => (
<div
key={type.value}
style={{
@@ -573,7 +632,7 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
</div>
)}
{/* Systemrolle (nur für Admins) */}
{/* Systemrollen (nur für Admins) */}
{hasRole(['admin']) && (
<div style={{
padding: '20px',
@@ -581,7 +640,7 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
borderRadius: '8px',
border: '1px solid #ffeaa7'
}}>
<h3 style={{ margin: '0 0 15px 0', color: '#856404' }}> Systemrolle</h3>
<h3 style={{ margin: '0 0 15px 0', color: '#856404' }}> Systemrollen</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{availableRoles.map(role => (
@@ -591,29 +650,19 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
display: 'flex',
alignItems: 'flex-start',
padding: '12px',
border: `2px solid ${formData.role === role.value ? '#f39c12' : '#e0e0e0'}`,
border: `2px solid ${formData.roles.includes(role.value) ? '#f39c12' : '#e0e0e0'}`,
borderRadius: '6px',
backgroundColor: formData.role === role.value ? '#fef9e7' : 'white',
backgroundColor: formData.roles.includes(role.value) ? '#fef9e7' : 'white',
cursor: 'pointer'
}}
onClick={() => {
setFormData(prev => ({
...prev,
role: role.value as 'admin' | 'maintenance' | 'user'
}));
}}
onClick={() => handleRoleChange(role.value, !formData.roles.includes(role.value))}
>
<input
type="radio"
name="role"
type="checkbox"
name="roles"
value={role.value}
checked={formData.role === role.value}
onChange={(e) => {
setFormData(prev => ({
...prev,
role: e.target.value as 'admin' | 'maintenance' | 'user'
}));
}}
checked={formData.roles.includes(role.value)}
onChange={(e) => handleRoleChange(role.value, e.target.checked)}
style={{
marginRight: '10px',
marginTop: '2px'
@@ -630,6 +679,9 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
</div>
))}
</div>
<div style={{ fontSize: '12px', color: '#7f8c8d', marginTop: '10px' }}>
<strong>Hinweis:</strong> Ein Mitarbeiter kann mehrere Rollen haben.
</div>
</div>
)}

View File

@@ -1,4 +1,3 @@
// frontend/src/pages/Employees/components/EmployeeList.tsx
import React, { useState } from 'react';
import { ROLE_CONFIG, EMPLOYEE_TYPE_CONFIG } from '../../../models/defaults/employeeDefaults';
import { Employee } from '../../../models/Employee';
@@ -33,11 +32,12 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
if (searchTerm) {
const term = searchTerm.toLowerCase();
const fullName = `${employee.firstname} ${employee.lastname}`.toLowerCase();
return (
employee.name.toLowerCase().includes(term) ||
fullName.includes(term) ||
employee.email.toLowerCase().includes(term) ||
employee.employeeType.toLowerCase().includes(term) ||
employee.role.toLowerCase().includes(term)
(employee.roles && employee.roles.some(role => role.toLowerCase().includes(term)))
);
}
@@ -51,8 +51,8 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
switch (sortField) {
case 'name':
aValue = a.name.toLowerCase();
bValue = b.name.toLowerCase();
aValue = `${a.firstname} ${a.lastname}`.toLowerCase();
bValue = `${b.firstname} ${b.lastname}`.toLowerCase();
break;
case 'employeeType':
aValue = a.employeeType;
@@ -63,8 +63,9 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
bValue = b.canWorkAlone;
break;
case 'role':
aValue = a.role;
bValue = b.role;
// Use the highest role for sorting
aValue = getHighestRole(a.roles || []);
bValue = getHighestRole(b.roles || []);
break;
case 'lastLogin':
// Handle null values for lastLogin (put them at the end)
@@ -82,6 +83,13 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
}
});
// 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
@@ -102,23 +110,23 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
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;
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.role === 'user' || employee.id === currentUser?.id;
return !employee.roles?.includes('admin') || employee.id === currentUser?.id;
}
return false;
};
// Using shared configuration for consistent styling
type EmployeeType = typeof EMPLOYEE_TYPE_CONFIG[number]['value'];
type EmployeeType = 'manager' | 'trainee' | 'experienced';
const getEmployeeTypeBadge = (type: EmployeeType) => {
const config = EMPLOYEE_TYPE_CONFIG.find(t => t.value === type)!;
const config = EMPLOYEE_TYPE_CONFIG[type];
const bgColor =
type === 'manager'
@@ -142,19 +150,27 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
: { text: 'Betreuung', color: '#e74c3c', bgColor: '#fadbd8' };
};
type Role = typeof ROLE_CONFIG[number]['value'];
type Role = 'admin' | 'maintenance' | 'user';
const getRoleBadge = (role: Role) => {
const { label, color } = ROLE_CONFIG.find(r => r.value === role)!;
const getRoleBadge = (roles: string[] = []) => {
const highestRole = getHighestRole(roles);
const { label, color } = ROLE_CONFIG.find(r => r.value === highestRole)!;
const bgColor =
role === 'user'
highestRole === 'user'
? '#d5f4e6'
: role === 'maintenance'
: highestRole === 'maintenance'
? '#d6eaf8'
: '#fadbd8'; // admin
return { text: label, color, bgColor };
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) {
@@ -282,7 +298,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
{sortedEmployees.map(employee => {
const employeeType = getEmployeeTypeBadge(employee.employeeType);
const independence = getIndependenceBadge(employee.canWorkAlone);
const roleColor = getRoleBadge(employee.role);
const roleInfo = getRoleBadge(employee.roles);
const status = getStatusBadge(employee.isActive);
const canEdit = canEditEmployee(employee);
const canDelete = canDeleteEmployee(employee);
@@ -302,7 +318,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
{/* Name & E-Mail */}
<div>
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>
{employee.name}
{employee.firstname} {employee.lastname}
{employee.id === currentUser?.id && (
<span style={{
marginLeft: '8px',
@@ -357,8 +373,8 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
<div style={{ textAlign: 'center' }}>
<span
style={{
backgroundColor: roleColor.bgColor,
color: roleColor.color,
backgroundColor: roleInfo.bgColor,
color: roleInfo.color,
padding: '6px 12px',
borderRadius: '15px',
fontSize: '12px',
@@ -366,9 +382,9 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
display: 'inline-block',
minWidth: '80px'
}}
title={employee.roles?.join(', ') || 'user'}
>
{employee.role === 'admin' ? 'ADMIN' :
employee.role === 'maintenance' ? 'INSTANDHALTER' : 'MITARBEITER'}
{formatRoleDisplay(employee.roles)}
</span>
</div>
@@ -406,25 +422,23 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
flexWrap: 'wrap'
}}>
{/* Verfügbarkeit Button */}
{(employee.role === 'admin' || employee.role === 'maintenance' || employee.role === 'user') && (
<button
onClick={() => onManageAvailability(employee)}
style={{
padding: '6px 8px',
backgroundColor: '#3498db',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
minWidth: '32px',
height: '32px'
}}
title="Verfügbarkeit verwalten"
>
📅
</button>
)}
<button
onClick={() => onManageAvailability(employee)}
style={{
padding: '6px 8px',
backgroundColor: '#3498db',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
minWidth: '32px',
height: '32px'
}}
title="Verfügbarkeit verwalten"
>
📅
</button>
{/* Bearbeiten Button */}
{canEdit && (
@@ -469,7 +483,7 @@ const EmployeeList: React.FC<EmployeeListProps> = ({
)}
{/* Platzhalter für Symmetrie */}
{!canEdit && !canDelete && (employee.role !== 'admin' && employee.role !== 'maintenance') && (
{!canEdit && !canDelete && (
<div style={{ width: '32px', height: '32px' }}></div>
)}
</div>

View File

@@ -319,7 +319,7 @@ const Settings: React.FC = () => {
</label>
<input
type="text"
value={currentUser.role}
value={currentUser.roles}
disabled
style={styles.fieldInputDisabled}
/>

View File

@@ -1,4 +1,4 @@
// frontend/src/pages/Setup/Setup.tsx - KORRIGIERT
// frontend/src/pages/Setup/Setup.tsx - UPDATED
import React, { useState } from 'react';
import { useAuth } from '../../contexts/AuthContext';
@@ -7,7 +7,8 @@ const Setup: React.FC = () => {
const [formData, setFormData] = useState({
password: '',
confirmPassword: '',
name: ''
firstname: '',
lastname: ''
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
@@ -34,8 +35,12 @@ const Setup: React.FC = () => {
};
const validateStep2 = () => {
if (!formData.name.trim()) {
setError('Bitte geben Sie einen Namen ein.');
if (!formData.firstname.trim()) {
setError('Bitte geben Sie einen Vornamen ein.');
return false;
}
if (!formData.lastname.trim()) {
setError('Bitte geben Sie einen Nachnamen ein.');
return false;
}
return true;
@@ -62,10 +67,11 @@ const Setup: React.FC = () => {
const payload = {
password: formData.password,
name: formData.name
firstname: formData.firstname,
lastname: formData.lastname
};
console.log('🚀 Sending setup request...');
console.log('🚀 Sending setup request...', payload);
const response = await fetch('http://localhost:3002/api/setup/admin', {
method: 'POST',
@@ -94,6 +100,17 @@ const Setup: React.FC = () => {
}
};
// Helper to display generated email preview
const getEmailPreview = () => {
if (!formData.firstname.trim() || !formData.lastname.trim()) {
return 'vorname.nachname@sp.de';
}
const cleanFirstname = formData.firstname.toLowerCase().replace(/[^a-z0-9]/g, '');
const cleanLastname = formData.lastname.toLowerCase().replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
};
return (
<div style={{
minHeight: '100vh',
@@ -144,34 +161,6 @@ const Setup: React.FC = () => {
{step === 1 && (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div>
<label style={{
display: 'block',
marginBottom: '0.5rem',
fontWeight: '600',
color: '#495057'
}}>
Administrator E-Mail
</label>
<div style={{
padding: '0.75rem',
backgroundColor: '#e9ecef',
border: '1px solid #ced4da',
borderRadius: '6px',
color: '#495057',
fontWeight: '500'
}}>
admin@instandhaltung.de
</div>
<div style={{
fontSize: '0.875rem',
color: '#6c757d',
marginTop: '0.25rem'
}}>
Diese E-Mail wird für den Administrator-Account verwendet
</div>
</div>
<div>
<label style={{
display: 'block',
@@ -237,12 +226,12 @@ const Setup: React.FC = () => {
fontWeight: '600',
color: '#495057'
}}>
Vollständiger Name
Vorname
</label>
<input
type="text"
name="name"
value={formData.name}
name="firstname"
value={formData.firstname}
onChange={handleInputChange}
style={{
width: '100%',
@@ -251,10 +240,65 @@ const Setup: React.FC = () => {
borderRadius: '6px',
fontSize: '1rem'
}}
placeholder="Max Mustermann"
placeholder="Max"
required
/>
</div>
<div>
<label style={{
display: 'block',
marginBottom: '0.5rem',
fontWeight: '600',
color: '#495057'
}}>
Nachname
</label>
<input
type="text"
name="lastname"
value={formData.lastname}
onChange={handleInputChange}
style={{
width: '100%',
padding: '0.75rem',
border: '1px solid #ced4da',
borderRadius: '6px',
fontSize: '1rem'
}}
placeholder="Mustermann"
required
/>
</div>
<div>
<label style={{
display: 'block',
marginBottom: '0.5rem',
fontWeight: '600',
color: '#495057'
}}>
Automatisch generierte E-Mail
</label>
<div style={{
padding: '0.75rem',
backgroundColor: '#e9ecef',
border: '1px solid #ced4da',
borderRadius: '6px',
color: '#495057',
fontWeight: '500',
fontFamily: 'monospace'
}}>
{getEmailPreview()}
</div>
<div style={{
fontSize: '0.875rem',
color: '#6c757d',
marginTop: '0.25rem'
}}>
Die E-Mail wird automatisch aus Vor- und Nachname generiert
</div>
</div>
</div>
)}
@@ -315,7 +359,7 @@ const Setup: React.FC = () => {
border: '1px solid #b6d7e8'
}}>
💡 Nach dem erfolgreichen Setup werden Sie zur Anmeldeseite weitergeleitet,
wo Sie sich mit Ihren Zugangsdaten anmelden können.
wo Sie sich mit Ihrer automatisch generierten E-Mail anmelden können.
</div>
)}
</div>