"use client" import { useState, useRef } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Card } from '@/components/ui/card' import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' import { User, Camera, AlertCircle } from 'lucide-react' import { toast } from 'sonner' import { formatPhoneInput, formatPassportSeries, formatPassportNumber, formatSalary, formatNameInput, isValidEmail, isValidPhone, isValidPassportSeries, isValidPassportNumber, isValidBirthDate, isValidHireDate, isValidSalary } from '@/lib/input-masks' interface Employee { id: string firstName: string lastName: string middleName?: string position: string phone: string email?: string avatar?: string hireDate: string status: 'ACTIVE' | 'VACATION' | 'SICK' | 'FIRED' salary?: number address?: string birthDate?: string passportSeries?: string passportNumber?: string passportIssued?: string passportDate?: string emergencyContact?: string emergencyPhone?: string telegram?: string whatsapp?: string passportPhoto?: string createdAt: string updatedAt: string } interface EmployeeFormProps { employee?: Employee | null onSave: (employeeData: Partial) => void onCancel: () => void } interface ValidationErrors { [key: string]: string } export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps) { const [formData, setFormData] = useState({ firstName: employee?.firstName || '', lastName: employee?.lastName || '', middleName: employee?.middleName || '', position: employee?.position || '', phone: employee?.phone || '', email: employee?.email || '', avatar: employee?.avatar || '', hireDate: employee?.hireDate || new Date().toISOString().split('T')[0], status: employee?.status || 'ACTIVE' as const, salary: employee?.salary || 0, address: employee?.address || '', birthDate: employee?.birthDate || '', passportSeries: employee?.passportSeries || '', passportNumber: employee?.passportNumber || '', passportIssued: employee?.passportIssued || '', passportDate: employee?.passportDate || '', emergencyContact: employee?.emergencyContact || '', emergencyPhone: employee?.emergencyPhone || '' }) const [isUploadingAvatar, setIsUploadingAvatar] = useState(false) const [loading, setLoading] = useState(false) const [errors, setErrors] = useState({}) const fileInputRef = useRef(null) const validateField = (field: string, value: string | number): string | null => { switch (field) { case 'firstName': case 'lastName': if (!value || String(value).trim() === '') { return field === 'firstName' ? 'Имя обязательно для заполнения' : 'Фамилия обязательна для заполнения' } if (String(value).length < 2) { return field === 'firstName' ? 'Имя должно содержать минимум 2 символа' : 'Фамилия должна содержать минимум 2 символа' } if (!/^[а-яёА-ЯЁa-zA-Z\s-]+$/.test(String(value))) { return field === 'firstName' ? 'Имя может содержать только буквы, пробелы и дефисы' : 'Фамилия может содержать только буквы, пробелы и дефисы' } break case 'middleName': if (value && String(value).length > 0) { if (String(value).length < 2) { return 'Отчество должно содержать минимум 2 символа' } if (!/^[а-яёА-ЯЁa-zA-Z\s-]+$/.test(String(value))) { return 'Отчество может содержать только буквы, пробелы и дефисы' } } break case 'position': if (!value || String(value).trim() === '') { return 'Должность обязательна для заполнения' } if (String(value).length < 2) { return 'Должность должна содержать минимум 2 символа' } break case 'phone': if (!value || String(value).trim() === '') { return 'Телефон обязателен для заполнения' } if (!isValidPhone(String(value))) { return 'Введите корректный номер телефона в формате +7 (999) 123-45-67' } break case 'email': if (value && String(value).trim() !== '' && !isValidEmail(String(value))) { return 'Введите корректный email адрес' } break case 'emergencyPhone': if (value && String(value).trim() !== '' && !isValidPhone(String(value))) { return 'Введите корректный номер телефона в формате +7 (999) 123-45-67' } break case 'passportSeries': if (value && String(value).trim() !== '' && !isValidPassportSeries(String(value))) { return 'Серия паспорта должна содержать 4 цифры' } break case 'passportNumber': if (value && String(value).trim() !== '' && !isValidPassportNumber(String(value))) { return 'Номер паспорта должен содержать 6 цифр' } break case 'birthDate': if (value && String(value).trim() !== '') { const validation = isValidBirthDate(String(value)) if (!validation.valid) { return validation.message || 'Некорректная дата рождения' } } break case 'hireDate': const hireValidation = isValidHireDate(String(value)) if (!hireValidation.valid) { return hireValidation.message || 'Некорректная дата приема' } break case 'salary': const salaryValidation = isValidSalary(Number(value)) if (!salaryValidation.valid) { return salaryValidation.message || 'Некорректная сумма зарплаты' } break } return null } const handleInputChange = (field: string, value: string | number) => { let processedValue = value // Применяем маски ввода if (typeof value === 'string') { switch (field) { case 'phone': case 'emergencyPhone': processedValue = formatPhoneInput(value) break case 'passportSeries': processedValue = formatPassportSeries(value) break case 'passportNumber': processedValue = formatPassportNumber(value) break case 'firstName': case 'lastName': case 'middleName': case 'emergencyContact': processedValue = formatNameInput(value) break } } setFormData(prev => ({ ...prev, [field]: processedValue })) // Валидация в реальном времени const error = validateField(field, processedValue) setErrors(prev => ({ ...prev, [field]: error || '' })) } const handleSalaryChange = (value: string) => { const numericValue = parseInt(value.replace(/\D/g, '')) || 0 setFormData(prev => ({ ...prev, salary: numericValue })) const error = validateField('salary', numericValue) setErrors(prev => ({ ...prev, salary: error || '' })) } const handleAvatarUpload = async (file: File) => { setIsUploadingAvatar(true) try { const formDataUpload = new FormData() formDataUpload.append('file', file) formDataUpload.append('key', `avatars/employees/${Date.now()}-${file.name}`) const response = await fetch('/api/upload-avatar', { method: 'POST', body: formDataUpload }) if (!response.ok) { throw new Error('Ошибка загрузки аватара') } const result = await response.json() setFormData(prev => ({ ...prev, avatar: result.url })) toast.success('Аватар успешно загружен') } catch (error) { console.error('Error uploading avatar:', error) toast.error('Ошибка при загрузке аватара') } finally { setIsUploadingAvatar(false) } } const validateForm = (): boolean => { const newErrors: ValidationErrors = {} // Валидируем все поля Object.keys(formData).forEach(field => { const error = validateField(field, formData[field as keyof typeof formData]) if (error) { newErrors[field] = error } }) setErrors(newErrors) return Object.keys(newErrors).filter(key => newErrors[key]).length === 0 } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setLoading(true) if (!validateForm()) { toast.error('Пожалуйста, исправьте ошибки в форме') setLoading(false) return } try { const employeeData = { firstName: formData.firstName, lastName: formData.lastName, middleName: formData.middleName || undefined, position: formData.position, phone: formData.phone, email: formData.email || undefined, avatar: formData.avatar || undefined, hireDate: formData.hireDate, salary: formData.salary || undefined, address: formData.address || undefined, birthDate: formData.birthDate || undefined, passportSeries: formData.passportSeries || undefined, passportNumber: formData.passportNumber || undefined, passportIssued: formData.passportIssued || undefined, passportDate: formData.passportDate || undefined, emergencyContact: formData.emergencyContact || undefined, emergencyPhone: formData.emergencyPhone || undefined } onSave(employeeData) toast.success(employee ? 'Сотрудник успешно обновлен' : 'Сотрудник успешно добавлен') } catch (error) { console.error('Error saving employee:', error) toast.error('Ошибка при сохранении данных сотрудника') } finally { setLoading(false) } } const getInitials = () => { const first = formData.firstName.charAt(0).toUpperCase() const last = formData.lastName.charAt(0).toUpperCase() return `${first}${last}` } // Компонент для отображения ошибок const ErrorMessage = ({ error }: { error: string }) => { if (!error) return null return (
{error}
) } return (
{/* Фото и основная информация */}

Личные данные

{/* Аватар */}
{formData.avatar ? ( ) : null} {getInitials() || } e.target.files?.[0] && handleAvatarUpload(e.target.files[0])} className="hidden" />
handleInputChange('firstName', e.target.value)} placeholder="Александр" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.firstName ? 'border-red-400' : ''}`} required />
handleInputChange('lastName', e.target.value)} placeholder="Петров" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.lastName ? 'border-red-400' : ''}`} required />
handleInputChange('middleName', e.target.value)} placeholder="Иванович" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.middleName ? 'border-red-400' : ''}`} />
handleInputChange('birthDate', e.target.value)} className={`glass-input text-white h-10 ${errors.birthDate ? 'border-red-400' : ''}`} />
handleInputChange('passportSeries', e.target.value)} placeholder="1234" maxLength={4} className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.passportSeries ? 'border-red-400' : ''}`} />
handleInputChange('passportNumber', e.target.value)} placeholder="567890" maxLength={6} className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.passportNumber ? 'border-red-400' : ''}`} />
handleInputChange('passportIssued', e.target.value)} placeholder="ОУФМС России по г. Москве" className="glass-input text-white placeholder:text-white/40 h-10" />
handleInputChange('passportDate', e.target.value)} className="glass-input text-white h-10" />
handleInputChange('address', e.target.value)} placeholder="Москва, ул. Ленина, 10, кв. 5" className="glass-input text-white placeholder:text-white/40 h-10" />
{/* Рабочая информация */}

Трудовая деятельность

handleInputChange('position', e.target.value)} placeholder="Менеджер склада" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.position ? 'border-red-400' : ''}`} required />
handleInputChange('hireDate', e.target.value)} className={`glass-input text-white h-10 ${errors.hireDate ? 'border-red-400' : ''}`} />
handleSalaryChange(e.target.value)} placeholder="80 000" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.salary ? 'border-red-400' : ''}`} />
{/* Контактная информация */}

Контактные данные

handleInputChange('phone', e.target.value)} placeholder="+7 (999) 123-45-67" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.phone ? 'border-red-400' : ''}`} />
handleInputChange('email', e.target.value)} placeholder="a.petrov@company.com" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.email ? 'border-red-400' : ''}`} />
handleInputChange('emergencyContact', e.target.value)} placeholder="ФИО близкого родственника" className="glass-input text-white placeholder:text-white/40 h-10" />
handleInputChange('emergencyPhone', e.target.value)} placeholder="+7 (999) 123-45-67" className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.emergencyPhone ? 'border-red-400' : ''}`} />
{/* Кнопки управления */}
) }