import { useState } from 'react' import { useNavigate } from 'react-router-dom' import type { Employee } from '@skillmate/shared' import { employeeApi } from '../services/api' import { SKILL_HIERARCHY, LANGUAGE_LEVELS } from '../data/skillCategories' import PhotoPreview from '../components/PhotoPreview' import OrganizationSelector from '../components/OrganizationSelector' export default function EmployeeForm() { const navigate = useNavigate() const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [validationErrors, setValidationErrors] = useState>({}) const [formData, setFormData] = useState({ firstName: '', lastName: '', employeeNumber: '', position: '', department: '', email: '', phone: '', mobile: '', office: '', clearance: '', availability: 'available' as 'available' | 'parttime' | 'unavailable', partTimeHours: '', skills: [] as any[], languages: [] as string[], specializations: [] as string[] }) const [primaryUnitId, setPrimaryUnitId] = useState(null) const [primaryUnitName, setPrimaryUnitName] = useState('') const [employeePhoto, setEmployeePhoto] = useState(null) const [photoFile, setPhotoFile] = useState(null) const [expandedCategories, setExpandedCategories] = useState>(new Set()) const [expandedSubCategories, setExpandedSubCategories] = useState>(new Set()) const [skillSearchTerm, setSkillSearchTerm] = useState('') const [searchResults, setSearchResults] = useState([]) const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target setFormData(prev => ({ ...prev, [name]: value })) } const toggleCategory = (categoryId: string) => { setExpandedCategories(prev => { const newSet = new Set(prev) if (newSet.has(categoryId)) { newSet.delete(categoryId) } else { newSet.add(categoryId) } return newSet }) } const toggleSubCategory = (subCategoryId: string) => { setExpandedSubCategories(prev => { const newSet = new Set(prev) if (newSet.has(subCategoryId)) { newSet.delete(subCategoryId) } else { newSet.add(subCategoryId) } return newSet }) } const handleSkillToggle = (categoryId: string, subCategoryId: string, skillId: string, skillName: string) => { setFormData(prev => { const skills = [...prev.skills] const existingIndex = skills.findIndex(s => s.categoryId === categoryId && s.subCategoryId === subCategoryId && s.skillId === skillId ) if (existingIndex > -1) { skills.splice(existingIndex, 1) } else { skills.push({ categoryId, subCategoryId, skillId, name: skillName, level: '' }) } return { ...prev, skills } }) } const handleSkillLevelChange = (categoryId: string, subCategoryId: string, skillId: string, level: string) => { setFormData(prev => { const skills = [...prev.skills] const skill = skills.find(s => s.categoryId === categoryId && s.subCategoryId === subCategoryId && s.skillId === skillId ) if (skill) { skill.level = level } return { ...prev, skills } }) } const isSkillSelected = (categoryId: string, subCategoryId: string, skillId: string) => { return formData.skills.some(s => s.categoryId === categoryId && s.subCategoryId === subCategoryId && s.skillId === skillId ) } const getSkillLevel = (categoryId: string, subCategoryId: string, skillId: string) => { const skill = formData.skills.find(s => s.categoryId === categoryId && s.subCategoryId === subCategoryId && s.skillId === skillId ) return skill?.level || '' } // Skill-Suche const handleSkillSearch = (searchTerm: string) => { setSkillSearchTerm(searchTerm) if (searchTerm.length < 2) { setSearchResults([]) return } const results: any[] = [] const lowerSearch = searchTerm.toLowerCase() SKILL_HIERARCHY.forEach(category => { category.subcategories.forEach(subCategory => { subCategory.skills.forEach(skill => { if (skill.name.toLowerCase().includes(lowerSearch)) { results.push({ categoryId: category.id, categoryName: category.name, subCategoryId: subCategory.id, subCategoryName: subCategory.name, skillId: skill.id, skillName: skill.name }) } }) }) }) setSearchResults(results) } const handleSearchResultClick = (result: any) => { // Öffne die entsprechenden Kategorien setExpandedCategories(prev => new Set([...prev, result.categoryId])) setExpandedSubCategories(prev => new Set([...prev, `${result.categoryId}-${result.subCategoryId}`])) // Wähle den Skill aus handleSkillToggle(result.categoryId, result.subCategoryId, result.skillId, result.skillName) // Lösche die Suche setSkillSearchTerm('') setSearchResults([]) } const validateForm = () => { const errors: Record = {} if (!formData.firstName.trim()) errors.firstName = 'Vorname ist erforderlich' if (!formData.lastName.trim()) errors.lastName = 'Nachname ist erforderlich' if (!formData.employeeNumber.trim()) errors.employeeNumber = 'Personalnummer ist erforderlich' if (!formData.position.trim()) errors.position = 'Position ist erforderlich' if (!formData.department.trim()) errors.department = 'Abteilung ist erforderlich' if (!formData.email.trim()) errors.email = 'E-Mail ist erforderlich' else if (!/\S+@\S+\.\S+/.test(formData.email)) errors.email = 'Ungültige E-Mail-Adresse' if (!formData.phone.trim()) errors.phone = 'Telefonnummer ist erforderlich' if (!primaryUnitId) errors.primaryUnitId = 'Organisatorische Einheit ist erforderlich' setValidationErrors(errors) return Object.keys(errors).length === 0 } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') setValidationErrors({}) if (!validateForm()) { setError('Bitte füllen Sie alle Pflichtfelder aus') return } setLoading(true) try { const newEmployee: Partial = { ...formData, skills: formData.skills.map((skill, index) => ({ id: `skill-${index}`, name: skill.name, category: skill.categoryId, level: skill.level || 3 })), languages: formData.skills .filter(s => s.subCategoryId === 'languages') .map(s => ({ language: s.name, proficiency: s.level || 'B1' })), clearance: formData.clearance ? { level: formData.clearance as 'Ü2' | 'Ü3', validUntil: new Date(new Date().setFullYear(new Date().getFullYear() + 5)), issuedDate: new Date() } : undefined, createdAt: new Date(), updatedAt: new Date(), createdBy: 'admin' } const result = await employeeApi.create({ ...newEmployee, primaryUnitId }) const newEmployeeId = result.data.id // Upload photo if we have one if (photoFile && newEmployeeId) { const formData = new FormData() formData.append('photo', photoFile) try { await fetch(`http://localhost:3001/api/upload/employee-photo/${newEmployeeId}`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: formData }) } catch (uploadError) { console.error('Failed to upload photo:', uploadError) } } navigate('/employees') } catch (err: any) { if (err.response?.status === 401) { setError('Ihre Session ist abgelaufen. Bitte melden Sie sich erneut an.') } else { setError(err.response?.data?.message || 'Fehler beim Erstellen des Profils') } } finally { setLoading(false) } } return (

Neues Mitarbeitendenprofil

{error && (
{error}
)}
{/* Persönliche Informationen */}

Persönliche Informationen

{ setPhotoFile(file) // Create preview URL const reader = new FileReader() reader.onloadend = () => { setEmployeePhoto(reader.result as string) } reader.readAsDataURL(file) }} onPhotoRemove={() => { setPhotoFile(null) setEmployeePhoto(null) }} />
{validationErrors.firstName && (

{validationErrors.firstName}

)}
{validationErrors.lastName && (

{validationErrors.lastName}

)}
{validationErrors.employeeNumber && (

{validationErrors.employeeNumber}

)}
{validationErrors.email && (

{validationErrors.email}

)}
{validationErrors.position && (

{validationErrors.position}

)}
{validationErrors.department && (

{validationErrors.department}

)}
{ setPrimaryUnitId(unitId) setPrimaryUnitName(unitName) }} />

{primaryUnitName || 'Bitte auswählen'}

{validationErrors.primaryUnitId && (

{validationErrors.primaryUnitId}

)}
{validationErrors.phone && (

{validationErrors.phone}

)}
{formData.availability === 'parttime' && (
)}
{/* Skills */}

Fähigkeiten und Qualifikationen

{/* Skill-Suche */}
handleSkillSearch(e.target.value)} placeholder="Skills suchen..." className="input-field w-full pl-10 pr-3" /> {/* Suchergebnisse */} {searchResults.length > 0 && (
{searchResults.map((result, index) => ( ))}
)} {skillSearchTerm.length >= 2 && searchResults.length === 0 && (

Keine Skills gefunden

)}
{SKILL_HIERARCHY.map(category => (
{/* Oberste Kategorie */} {/* Unterkategorien */} {expandedCategories.has(category.id) && (
{category.subcategories.map(subCategory => (
{/* Mittlere Kategorie */} {/* Skills */} {expandedSubCategories.has(`${category.id}-${subCategory.id}`) && (
{subCategory.skills.map(skill => (
{/* Niveauauswahl */} {isSkillSelected(category.id, subCategory.id, skill.id) && ( subCategory.id === 'languages' ? ( ) : ( ) )}
))}
)}
))}
)}
))}
{/* Submit */}
) }