Добавлены модели и функциональность для управления логистикой, включая создание, обновление и удаление логистических маршрутов через GraphQL. Обновлены компоненты для отображения и управления логистикой, улучшен интерфейс взаимодействия с пользователем. Реализованы новые типы данных и интерфейсы для логистики, а также улучшена обработка ошибок.

This commit is contained in:
Bivekich
2025-07-18 15:40:12 +03:00
parent 7e7e4a9b4a
commit 93bb5827d2
20 changed files with 5015 additions and 667 deletions

View File

@ -7,8 +7,22 @@ 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 } from 'lucide-react'
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
@ -43,6 +57,10 @@ interface EmployeeFormProps {
onCancel: () => void
}
interface ValidationErrors {
[key: string]: string
}
export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps) {
const [formData, setFormData] = useState({
firstName: employee?.firstName || '',
@ -66,13 +84,154 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
})
const [isUploadingAvatar, setIsUploadingAvatar] = useState(false)
const fileInputRef = useRef<HTMLInputElement>(null)
const [loading, setLoading] = useState(false)
const [errors, setErrors] = useState<ValidationErrors>({})
const fileInputRef = useRef<HTMLInputElement>(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]: value
[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 || ''
}))
}
@ -109,35 +268,36 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
}
}
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 (!formData.firstName || !formData.lastName || !formData.position) {
toast.error('Пожалуйста, заполните все обязательные поля')
setLoading(false)
return
}
if (formData.email && !/\S+@\S+\.\S+/.test(formData.email)) {
toast.error('Введите корректный email адрес')
setLoading(false)
return
}
if (formData.phone && !/^[\+]?[1-9][\d]{0,15}$/.test(formData.phone.replace(/\s/g, ''))) {
toast.error('Введите корректный номер телефона')
if (!validateForm()) {
toast.error('Пожалуйста, исправьте ошибки в форме')
setLoading(false)
return
}
try {
// Для создания/обновления отправляем только нужные поля
const employeeData = {
firstName: formData.firstName,
lastName: formData.lastName,
middleName: formData.middleName,
middleName: formData.middleName || undefined,
position: formData.position,
phone: formData.phone,
email: formData.email || undefined,
@ -170,13 +330,15 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
return `${first}${last}`
}
const formatPhoneInput = (value: string) => {
const cleaned = value.replace(/\D/g, '')
if (cleaned.length <= 1) return cleaned
if (cleaned.length <= 4) return `+7 (${cleaned.slice(1)}`
if (cleaned.length <= 7) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4)}`
if (cleaned.length <= 9) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7)}`
return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7, 9)}-${cleaned.slice(9, 11)}`
// Компонент для отображения ошибок
const ErrorMessage = ({ error }: { error: string }) => {
if (!error) return null
return (
<div className="flex items-center gap-1 mt-1 text-red-400 text-xs">
<AlertCircle className="h-3 w-3 flex-shrink-0" />
<span>{error}</span>
</div>
)
}
return (
@ -227,9 +389,10 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
placeholder="Александр"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.firstName ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.firstName} />
</div>
<div>
@ -240,9 +403,10 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
placeholder="Петров"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.lastName ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.lastName} />
</div>
<div>
@ -251,8 +415,9 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.middleName}
onChange={(e) => handleInputChange('middleName', e.target.value)}
placeholder="Иванович"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.middleName ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.middleName} />
</div>
<div>
@ -261,8 +426,9 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
type="date"
value={formData.birthDate}
onChange={(e) => handleInputChange('birthDate', e.target.value)}
className="glass-input text-white h-10"
className={`glass-input text-white h-10 ${errors.birthDate ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.birthDate} />
</div>
<div>
@ -271,8 +437,10 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.passportSeries}
onChange={(e) => handleInputChange('passportSeries', e.target.value)}
placeholder="1234"
className="glass-input text-white placeholder:text-white/40 h-10"
maxLength={4}
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.passportSeries ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.passportSeries} />
</div>
<div>
@ -281,8 +449,10 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.passportNumber}
onChange={(e) => handleInputChange('passportNumber', e.target.value)}
placeholder="567890"
className="glass-input text-white placeholder:text-white/40 h-10"
maxLength={6}
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.passportNumber ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.passportNumber} />
</div>
<div>
@ -330,35 +500,39 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.position}
onChange={(e) => handleInputChange('position', e.target.value)}
placeholder="Менеджер склада"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.position ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.position} />
</div>
<div>
<Label className="text-white/80 text-sm mb-2 block">Дата приема на работу</Label>
<Label className="text-white/80 text-sm mb-2 block">
Дата приема на работу <span className="text-red-400">*</span>
</Label>
<Input
type="date"
value={formData.hireDate}
onChange={(e) => handleInputChange('hireDate', e.target.value)}
className="glass-input text-white h-10"
className={`glass-input text-white h-10 ${errors.hireDate ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.hireDate} />
</div>
<div>
<Label className="text-white/80 text-sm mb-2 block">Статус</Label>
<Select
value={formData.status}
onValueChange={(value: 'active' | 'vacation' | 'sick' | 'inactive') => handleInputChange('status', value)}
onValueChange={(value: 'ACTIVE' | 'VACATION' | 'SICK' | 'FIRED') => handleInputChange('status', value)}
>
<SelectTrigger className="glass-input text-white h-10">
<SelectValue />
</SelectTrigger>
<SelectContent className="bg-gray-900 border-white/20">
<SelectItem value="active" className="text-white hover:bg-white/10">Активен</SelectItem>
<SelectItem value="vacation" className="text-white hover:bg-white/10">В отпуске</SelectItem>
<SelectItem value="sick" className="text-white hover:bg-white/10">На больничном</SelectItem>
<SelectItem value="inactive" className="text-white hover:bg-white/10">Неактивен</SelectItem>
<SelectItem value="ACTIVE" className="text-white hover:bg-white/10">Активен</SelectItem>
<SelectItem value="VACATION" className="text-white hover:bg-white/10">В отпуске</SelectItem>
<SelectItem value="SICK" className="text-white hover:bg-white/10">На больничном</SelectItem>
<SelectItem value="FIRED" className="text-white hover:bg-white/10">Уволен</SelectItem>
</SelectContent>
</Select>
</div>
@ -367,13 +541,12 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
<div className="mt-4">
<Label className="text-white/80 text-sm mb-2 block">Зарплата ()</Label>
<Input
type="number"
min="0"
value={formData.salary || ''}
onChange={(e) => handleInputChange('salary', parseInt(e.target.value) || 0)}
placeholder="80000"
className="glass-input text-white placeholder:text-white/40 h-10"
value={formData.salary ? formatSalary(formData.salary.toString()) : ''}
onChange={(e) => handleSalaryChange(e.target.value)}
placeholder="80 000"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.salary ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.salary} />
</div>
</Card>
@ -382,16 +555,16 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
<h3 className="text-white font-medium mb-4">Контактные данные</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-white/80 text-sm mb-2 block">Телефон</Label>
<Label className="text-white/80 text-sm mb-2 block">
Телефон <span className="text-red-400">*</span>
</Label>
<Input
value={formData.phone}
onChange={(e) => {
const formatted = formatPhoneInput(e.target.value)
handleInputChange('phone', formatted)
}}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+7 (999) 123-45-67"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.phone ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.phone} />
</div>
<div>
@ -401,8 +574,9 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="a.petrov@company.com"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.email ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.email} />
</div>
<div>
@ -419,13 +593,11 @@ export function EmployeeForm({ employee, onSave, onCancel }: EmployeeFormProps)
<Label className="text-white/80 text-sm mb-2 block">Телефон экстренного контакта</Label>
<Input
value={formData.emergencyPhone}
onChange={(e) => {
const formatted = formatPhoneInput(e.target.value)
handleInputChange('emergencyPhone', formatted)
}}
onChange={(e) => handleInputChange('emergencyPhone', e.target.value)}
placeholder="+7 (999) 123-45-67"
className="glass-input text-white placeholder:text-white/40 h-10"
className={`glass-input text-white placeholder:text-white/40 h-10 ${errors.emergencyPhone ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.emergencyPhone} />
</div>
</div>
</Card>

View File

@ -20,11 +20,25 @@ import {
Mail,
Briefcase,
DollarSign,
FileText,
MessageCircle
MessageCircle,
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 EmployeeInlineFormProps {
onSave: (employeeData: {
@ -46,6 +60,10 @@ interface EmployeeInlineFormProps {
isLoading?: boolean
}
interface ValidationErrors {
[key: string]: string
}
export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: EmployeeInlineFormProps) {
const [formData, setFormData] = useState({
firstName: '',
@ -65,13 +83,123 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
const [isUploadingAvatar, setIsUploadingAvatar] = useState(false)
const [isUploadingPassport, setIsUploadingPassport] = useState(false)
const [showPassportPreview, setShowPassportPreview] = useState(false)
const [errors, setErrors] = useState<ValidationErrors>({})
const avatarInputRef = useRef<HTMLInputElement>(null)
const passportInputRef = useRef<HTMLInputElement>(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]: value
[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 || ''
}))
}
@ -126,26 +254,28 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
}
}
const formatPhoneInput = (value: string) => {
const cleaned = value.replace(/\D/g, '')
if (cleaned.length <= 1) return cleaned
if (cleaned.length <= 4) return `+7 (${cleaned.slice(1)}`
if (cleaned.length <= 7) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4)}`
if (cleaned.length <= 9) return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7)}`
return `+7 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7, 9)}-${cleaned.slice(9, 11)}`
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 = (e: React.FormEvent) => {
e.preventDefault()
// Валидация обязательных полей
if (!formData.firstName || !formData.lastName || !formData.phone || !formData.position) {
toast.error('Пожалуйста, заполните все обязательные поля')
return
}
if (formData.email && !/\S+@\S+\.\S+/.test(formData.email)) {
toast.error('Введите корректный email адрес')
if (!validateForm()) {
toast.error('Пожалуйста, исправьте ошибки в форме')
return
}
@ -169,6 +299,17 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
onSave(employeeData)
}
// Компонент для отображения ошибок
const ErrorMessage = ({ error }: { error: string }) => {
if (!error) return null
return (
<div className="flex items-center gap-1 mt-1 text-red-400 text-xs">
<AlertCircle className="h-3 w-3 flex-shrink-0" />
<span>{error}</span>
</div>
)
}
const getInitials = () => {
const first = formData.firstName.charAt(0).toUpperCase()
const last = formData.lastName.charAt(0).toUpperCase()
@ -316,7 +457,7 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
</Label>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<div>
<Label className="text-white/80 text-sm mb-2 block">
Имя <span className="text-red-400">*</span>
</Label>
@ -324,11 +465,12 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
placeholder="Александр"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.firstName ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.firstName} />
</div>
<div>
<Label className="text-white/80 text-sm mb-2 block">
Фамилия <span className="text-red-400">*</span>
@ -337,9 +479,10 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
placeholder="Петров"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.lastName ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.lastName} />
</div>
<div>
@ -348,8 +491,9 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
value={formData.middleName}
onChange={(e) => handleInputChange('middleName', e.target.value)}
placeholder="Иванович"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.middleName ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.middleName} />
</div>
<div>
@ -382,14 +526,12 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
</Label>
<Input
value={formData.phone}
onChange={(e) => {
const formatted = formatPhoneInput(e.target.value)
handleInputChange('phone', formatted)
}}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+7 (999) 123-45-67"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.phone ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.phone} />
</div>
<div>
@ -412,13 +554,11 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
</Label>
<Input
value={formData.whatsapp}
onChange={(e) => {
const formatted = formatPhoneInput(e.target.value)
handleInputChange('whatsapp', formatted)
}}
onChange={(e) => handleInputChange('whatsapp', e.target.value)}
placeholder="+7 (999) 123-45-67"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.whatsapp ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.whatsapp} />
</div>
<div>
@ -431,8 +571,9 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="a.petrov@company.com"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.email ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.email} />
</div>
</div>
</div>
@ -455,9 +596,10 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
value={formData.position}
onChange={(e) => handleInputChange('position', e.target.value)}
placeholder="Менеджер склада"
className="glass-input text-white placeholder:text-white/40"
className={`glass-input text-white placeholder:text-white/40 ${errors.position ? 'border-red-400' : ''}`}
required
/>
<ErrorMessage error={errors.position} />
</div>
<div>
@ -466,13 +608,12 @@ export function EmployeeInlineForm({ onSave, onCancel, isLoading = false }: Empl
Зарплата
</Label>
<Input
type="number"
min="0"
value={formData.salary || ''}
onChange={(e) => handleInputChange('salary', parseInt(e.target.value) || 0)}
placeholder="80000"
className="glass-input text-white placeholder:text-white/40"
value={formData.salary ? formatSalary(formData.salary.toString()) : ''}
onChange={(e) => handleSalaryChange(e.target.value)}
placeholder="80 000"
className={`glass-input text-white placeholder:text-white/40 ${errors.salary ? 'border-red-400' : ''}`}
/>
<ErrorMessage error={errors.salary} />
</div>
</div>
</div>