// ============================================================================= // 🧑‍💼 EMPLOYEE FORM V2 // ============================================================================= // Универсальная модульная форма для создания/редактирования сотрудников 'use client' import { User, FileText, Phone, Briefcase, Save, X } from 'lucide-react' import React, { useState, useCallback, useMemo } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Textarea } from '@/components/ui/textarea' import type { EmployeeV2, CreateEmployeeInput, UpdateEmployeeInput, EmployeeStatus, } from '../types' interface EmployeeFormProps { employee?: EmployeeV2 | null // для редактирования onSubmit: (data: CreateEmployeeInput | UpdateEmployeeInput) => Promise onCancel: () => void loading?: boolean } export const EmployeeForm = React.memo(function EmployeeForm({ employee, onSubmit, onCancel, loading = false, }: EmployeeFormProps) { const isEditing = !!employee // ============================================================================= // СОСТОЯНИЕ ФОРМЫ // ============================================================================= const [formData, setFormData] = useState(() => ({ // Личная информация firstName: employee?.personalInfo.firstName || '', lastName: employee?.personalInfo.lastName || '', middleName: employee?.personalInfo.middleName || '', birthDate: employee?.personalInfo.birthDate ? new Date(employee.personalInfo.birthDate).toISOString().split('T')[0] : '', avatar: employee?.personalInfo.avatar || '', // Паспортные данные passportPhoto: employee?.documentsInfo.passportPhoto || '', passportSeries: employee?.documentsInfo.passportSeries || '', passportNumber: employee?.documentsInfo.passportNumber || '', passportIssued: employee?.documentsInfo.passportIssued || '', passportDate: employee?.documentsInfo.passportDate ? new Date(employee.documentsInfo.passportDate).toISOString().split('T')[0] : '', // Контактная информация phone: employee?.contactInfo.phone || '', email: employee?.contactInfo.email || '', telegram: employee?.contactInfo.telegram || '', whatsapp: employee?.contactInfo.whatsapp || '', address: employee?.contactInfo.address || '', emergencyContact: employee?.contactInfo.emergencyContact || '', emergencyPhone: employee?.contactInfo.emergencyPhone || '', // Рабочая информация position: employee?.workInfo.position || '', department: employee?.workInfo.department || '', hireDate: employee?.workInfo.hireDate ? new Date(employee.workInfo.hireDate).toISOString().split('T')[0] : '', salary: employee?.workInfo.salary?.toString() || '', status: employee?.workInfo.status || 'ACTIVE' as EmployeeStatus, })) const [activeTab, setActiveTab] = useState<'personal' | 'documents' | 'contact' | 'work'>('personal') // ============================================================================= // ВАЛИДАЦИЯ // ============================================================================= const validation = useMemo(() => { const errors: string[] = [] if (!formData.firstName.trim()) errors.push('Имя обязательно') if (!formData.lastName.trim()) errors.push('Фамилия обязательна') if (!formData.phone.trim()) errors.push('Телефон обязателен') if (!formData.position.trim()) errors.push('Должность обязательна') if (!formData.hireDate) errors.push('Дата найма обязательна') // Валидация телефона if (formData.phone && !/^\+7\d{10}$/.test(formData.phone.replace(/\s/g, ''))) { errors.push('Неверный формат телефона') } // Валидация email if (formData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { errors.push('Неверный формат email') } return { isValid: errors.length === 0, errors, } }, [formData]) // ============================================================================= // ОБРАБОТЧИКИ // ============================================================================= const handleInputChange = useCallback((field: string, value: string) => { setFormData(prev => ({ ...prev, [field]: value })) }, []) const handleSubmit = useCallback(async (e: React.FormEvent) => { e.preventDefault() if (!validation.isValid) { return } const submitData = isEditing ? { // Для обновления отправляем только измененные поля personalInfo: { firstName: formData.firstName, lastName: formData.lastName, middleName: formData.middleName || undefined, birthDate: formData.birthDate ? new Date(formData.birthDate) : undefined, avatar: formData.avatar || undefined, }, documentsInfo: { passportPhoto: formData.passportPhoto || undefined, passportSeries: formData.passportSeries || undefined, passportNumber: formData.passportNumber || undefined, passportIssued: formData.passportIssued || undefined, passportDate: formData.passportDate ? new Date(formData.passportDate) : undefined, }, contactInfo: { phone: formData.phone, email: formData.email || undefined, telegram: formData.telegram || undefined, whatsapp: formData.whatsapp || undefined, address: formData.address || undefined, emergencyContact: formData.emergencyContact || undefined, emergencyPhone: formData.emergencyPhone || undefined, }, workInfo: { position: formData.position, department: formData.department || undefined, hireDate: new Date(formData.hireDate), salary: formData.salary ? parseFloat(formData.salary) : undefined, status: formData.status, }, } as UpdateEmployeeInput : { // Для создания все обязательные поля personalInfo: { firstName: formData.firstName, lastName: formData.lastName, middleName: formData.middleName || undefined, birthDate: formData.birthDate ? new Date(formData.birthDate) : undefined, avatar: formData.avatar || undefined, }, documentsInfo: formData.passportSeries || formData.passportNumber ? { passportPhoto: formData.passportPhoto || undefined, passportSeries: formData.passportSeries || undefined, passportNumber: formData.passportNumber || undefined, passportIssued: formData.passportIssued || undefined, passportDate: formData.passportDate ? new Date(formData.passportDate) : undefined, } : undefined, contactInfo: { phone: formData.phone, email: formData.email || undefined, telegram: formData.telegram || undefined, whatsapp: formData.whatsapp || undefined, address: formData.address || undefined, emergencyContact: formData.emergencyContact || undefined, emergencyPhone: formData.emergencyPhone || undefined, }, workInfo: { position: formData.position, department: formData.department || undefined, hireDate: new Date(formData.hireDate), salary: formData.salary ? parseFloat(formData.salary) : undefined, }, } as CreateEmployeeInput await onSubmit(submitData) }, [formData, validation.isValid, isEditing, onSubmit]) // ============================================================================= // RENDER // ============================================================================= return (
{/* Заголовок */}

{isEditing ? 'Редактирование сотрудника' : 'Новый сотрудник'}

{/* Ошибки валидации */} {!validation.isValid && (
    {validation.errors.map((error, index) => (
  • • {error}
  • ))}
)} {/* Табы для секций формы */} Личные данные Документы Контакты Работа {/* Личные данные */}
handleInputChange('firstName', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('lastName', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('middleName', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('birthDate', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
{/* Документы */}
handleInputChange('passportSeries', e.target.value)} placeholder="1234" className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('passportNumber', e.target.value)} placeholder="567890" className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('passportIssued', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('passportDate', e.target.value)} className="bg-white/5 border-white/20 text-white" disabled={loading} />
{/* Контакты */}
handleInputChange('phone', e.target.value)} placeholder="+7 (999) 123-45-67" className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('email', e.target.value)} placeholder="email@company.com" className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('telegram', e.target.value)} placeholder="@username" className="bg-white/5 border-white/20 text-white" disabled={loading} />
handleInputChange('whatsapp', e.target.value)} placeholder="+7 (999) 123-45-67" className="bg-white/5 border-white/20 text-white" disabled={loading} />