'use client' import { Camera, User, X, Save, UserPlus, AlertCircle, RefreshCw, FileImage, Briefcase, Phone, Mail, Calendar, DollarSign, MessageCircle, } from 'lucide-react' import Image from 'next/image' import { useState, useRef } from 'react' import { toast } from 'sonner' import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { formatPhoneInput, formatSalary, formatNameInput, isValidEmail, isValidPhone, isValidBirthDate, isValidSalary, } from '@/lib/input-masks' interface EmployeeInlineFormProps { onSave: (employeeData: { firstName: string lastName: string middleName?: string birthDate?: string phone: string email?: string position: string salary?: number avatar?: string telegram?: string whatsapp?: string passportPhoto?: string hireDate: string }) => void onCancel: () => void isLoading?: boolean } interface ValidationErrors { [key: string]: string } export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: EmployeeInlineFormProps) { const [formData, setFormData] = useState({ firstName: '', lastName: '', middleName: '', birthDate: '', phone: '', telegram: '', whatsapp: '', email: '', position: '', salary: 0, avatar: '', passportPhoto: '', }) const [isUploadingAvatar, setIsUploadingAvatar] = useState(false) const [isUploadingPassport, setIsUploadingPassport] = useState(false) const [showPassportPreview, setShowPassportPreview] = useState(false) const [errors, setErrors] = useState({}) const avatarInputRef = useRef(null) const passportInputRef = 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': case 'whatsapp': if (field === 'phone' && (!value || String(value).trim() === '')) { return 'Телефон обязателен для заполнения' } if (value && String(value).trim() !== '' && !isValidPhone(String(value))) { return 'Введите корректный номер телефона в формате +7 (999) 123-45-67' } break case 'email': if (value && String(value).trim() !== '' && !isValidEmail(String(value))) { return 'Введите корректный email адрес' } break case 'birthDate': if (value && String(value).trim() !== '') { const validation = isValidBirthDate(String(value)) if (!validation.valid) { return validation.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 'whatsapp': processedValue = formatPhoneInput(value) break case 'firstName': case 'lastName': case 'middleName': 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 handleFileUpload = async (file: File, type: 'avatar' | 'passport') => { const setLoading = type === 'avatar' ? setIsUploadingAvatar : setIsUploadingPassport setLoading(true) try { const formDataUpload = new FormData() formDataUpload.append('file', file) let endpoint: string if (type === 'avatar') { // Для аватара используем upload-avatar API и добавляем временный userId formDataUpload.append('userId', `temp_${Date.now()}`) endpoint = '/api/upload-avatar' } else { // Для паспорта используем специальный API для документов сотрудников formDataUpload.append('documentType', 'passport') endpoint = '/api/upload-employee-document' } const response = await fetch(endpoint, { method: 'POST', body: formDataUpload, }) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.error || `Ошибка загрузки ${type === 'avatar' ? 'аватара' : 'паспорта'}`) } const result = await response.json() if (!result.success) { throw new Error(result.error || 'Неизвестная ошибка при загрузке') } setFormData((prev) => ({ ...prev, [type === 'avatar' ? 'avatar' : 'passportPhoto']: result.url, })) toast.success(`${type === 'avatar' ? 'Фото' : 'Паспорт'} успешно загружен`) } catch (error) { console.error(`Error uploading ${type}:`, error) const errorMessage = error instanceof Error ? error.message : `Ошибка при загрузке ${type === 'avatar' ? 'фото' : 'паспорта'}` toast.error(errorMessage) } finally { setLoading(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) // Дебаг: показываем все ошибки в консоли if (Object.keys(newErrors).filter((key) => newErrors[key]).length > 0) { console.warn('Ошибки валидации:', newErrors) } return Object.keys(newErrors).filter((key) => newErrors[key]).length === 0 } const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!validateForm()) { toast.error('Пожалуйста, исправьте ошибки в форме') return } // Подготавливаем данные для отправки const employeeData = { firstName: formData.firstName, lastName: formData.lastName, middleName: formData.middleName || undefined, birthDate: formData.birthDate || undefined, phone: formData.phone, email: formData.email || undefined, position: formData.position, salary: formData.salary || undefined, avatar: formData.avatar || undefined, telegram: formData.telegram || undefined, whatsapp: formData.whatsapp || undefined, passportPhoto: formData.passportPhoto || undefined, hireDate: new Date().toISOString().split('T')[0], } onSave(employeeData) } // Компонент для отображения ошибок const ErrorMessage = ({ error }: { error: string }) => { if (!error) return null return (
{error}
) } const getInitials = () => { const first = formData.firstName.charAt(0).toUpperCase() const last = formData.lastName.charAt(0).toUpperCase() return `${first}${last}` } return ( <>
{/* Информация о сотруднике - точно как в карточке */}
{/* Блок с аватаром и фото паспорта вертикально */}
{/* Аватар с иконкой камеры */}
{formData.avatar && formData.avatar.trim() !== '' ? ( ) : null} {getInitials() || }
Аватар
{/* Фото паспорта */}
{formData.passportPhoto && formData.passportPhoto.trim() !== '' ? ( Фото паспорта setShowPassportPreview(true)} /> ) : ( )}
Паспорт

Новый сотрудник

{/* Имя */}
handleInputChange('firstName', e.target.value)} placeholder="Имя *" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.firstName ? 'border-red-400' : ''}`} required />
{/* Фамилия */}
handleInputChange('lastName', e.target.value)} placeholder="Фамилия *" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.lastName ? 'border-red-400' : ''}`} required />
{/* Отчество */}
handleInputChange('middleName', e.target.value)} placeholder="Отчество" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.middleName ? 'border-red-400' : ''}`} />
{/* Должность */}
handleInputChange('position', e.target.value)} placeholder="Должность *" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.position ? 'border-red-400' : ''}`} required />
{/* Телефон */}
handleInputChange('phone', e.target.value)} placeholder="Телефон *" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.phone ? 'border-red-400' : ''}`} required />
{/* Email */}
handleInputChange('email', e.target.value)} placeholder="Email" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.email ? 'border-red-400' : ''}`} />
{/* Дата рождения */}
handleInputChange('birthDate', e.target.value)} placeholder="Дата рождения" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.birthDate ? 'border-red-400' : ''}`} />
{/* Зарплата */}
handleSalaryChange(e.target.value)} placeholder="Зарплата" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.salary ? 'border-red-400' : ''}`} />
{/* Telegram */}
handleInputChange('telegram', e.target.value)} placeholder="@telegram" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.telegram ? 'border-red-400' : ''}`} />
{/* WhatsApp */}
handleInputChange('whatsapp', e.target.value)} placeholder="WhatsApp" className={`glass-input text-white placeholder:text-white/40 h-9 ${errors.whatsapp ? 'border-red-400' : ''}`} />
{/* Скрытые input элементы для загрузки файлов */} e.target.files?.[0] && handleFileUpload(e.target.files[0], 'avatar')} className="hidden" disabled={isUploadingAvatar} /> e.target.files?.[0] && handleFileUpload(e.target.files[0], 'passport')} className="hidden" disabled={isUploadingPassport} />
{/* Табель работы - точно как в карточке но пустой */}

Табель работы (будет доступен после создания)

{/* Пустая сетка календаря */}
{/* Заголовки дней недели */} {['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'].map((day) => (
{day}
))} {/* Пустые дни месяца */} {Array.from({ length: 35 }, (_, i) => { const day = i + 1 if (day > 31) return
return (
{day <= 31 ? day : ''}
) })}
{/* Статистика - пустая */}

0

Рабочих дней

0

Отпуск

0

Больничный

Всего часов

{/* Кнопка сохранения */}
{/* Превью паспорта */} Фото паспорта
{formData.passportPhoto && formData.passportPhoto.trim() !== '' && ( Паспорт )}
) }