import { useRef, useState, type FormEvent } from 'react'; import { AlertCircle, Loader2 } from 'lucide-react'; import { Button, Input, Select, Modal } from '../ui'; import { deliveryRequestSourceOptions, pickupOptions } from '../../constants/pickup'; import { formatDateForInput, parseDateFromInput, getTodayFrontend } from '../../utils/date'; import type { Delivery, DeliveryRequestSource, PickupLocation, PaymentStatus } from '../../types'; type DeliveryInput = Omit; interface DeliveryFormProps { isOpen: boolean; onClose: () => void; onSubmit: (delivery: DeliveryInput) => void | Promise; initialData?: Delivery | null; defaultDate?: string; } const PHONE_REGEX = /^\+7\s?\(?\d{3}\)?\s?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/; const requestSourceOptions = [{ value: '', label: 'Выберите источник заявки' }, ...deliveryRequestSourceOptions]; const paymentOptions = [{ value: 'prepaid', label: 'Предоплата' }, { value: 'paid', label: 'Полная оплата' }]; const emptyDelivery = (date: string): DeliveryInput => ({ date, pickupLocation: 'warehouse', pickupLocation2: null, warehouseRequestSource: null, warehouseRequestSource2: null, productName: '', productName2: '', customerName: '', address: '', street: '', house: '', apartment: '', entrance: '', floor: '', phone: '', additionalPhone: '', hasElevator: false, paymentStatus: 'prepaid', serviceInfo: '', comment: '', status: 'new', }); // A fresh mount on each opening prevents edited data leaking into a new delivery. export const DeliveryForm = (props: DeliveryFormProps) => props.isOpen ? : null; function DeliveryFormContent({ onClose, onSubmit, initialData, defaultDate }: DeliveryFormProps) { const [baseline] = useState(() => initialData ? { ...initialData } : emptyDelivery(defaultDate || getTodayFrontend())); const [data, setData] = useState(baseline); const [secondPickup, setSecondPickup] = useState(!!baseline.pickupLocation2); const [isSubmitting, setSubmitting] = useState(false); const [error, setError] = useState(''); const [attempted, setAttempted] = useState(false); const submitting = useRef(false); const dirty = JSON.stringify(data) !== JSON.stringify(baseline); const update = (key: K, value: DeliveryInput[K]) => setData(prev => ({ ...prev, [key]: value })); const phoneError = data.phone && !PHONE_REGEX.test(data.phone) ? 'Введите номер в формате +7 (776) 567-89-01' : undefined; const extraPhoneError = data.additionalPhone && !PHONE_REGEX.test(data.additionalPhone) ? 'Проверьте дополнительный номер или очистите поле' : undefined; const sourceMissing = data.pickupLocation === 'warehouse' && !data.warehouseRequestSource; const secondSourceMissing = secondPickup && data.pickupLocation2 === 'warehouse' && !data.warehouseRequestSource2; const requestClose = () => { if (submitting.current) return; if (!dirty || window.confirm('Закрыть форму? Несохранённые изменения будут потеряны.')) onClose(); }; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setAttempted(true); if (submitting.current) return; if (phoneError || extraPhoneError || sourceMissing || secondSourceMissing) { setError('Проверьте отмеченные поля. Данные остаются в форме.'); return; } submitting.current = true; setSubmitting(true); setError(''); const address = ['Кокшетау', `ул. ${data.street.trim()}`, `д. ${data.house.trim()}`, data.apartment && `кв. ${data.apartment}`, data.entrance && `подъезд ${data.entrance}`].filter(Boolean).join(', '); try { await onSubmit({ ...data, productName: data.productName.trim(), customerName: data.customerName.trim(), street: data.street.trim(), house: data.house.trim(), address }); onClose(); } catch { setError('Не удалось сохранить доставку. Проверьте соединение и попробуйте ещё раз. Все введённые данные сохранены в форме.'); } finally { submitting.current = false; setSubmitting(false); } }; return ( {error &&

{error}

}
}>

Поля со звёздочкой обязательны.

Дата и загрузка update('date', parseDateFromInput(e.target.value))} /> update('warehouseRequestSource', e.target.value as DeliveryRequestSource || null)} />} update('productName', e.target.value)} placeholder="Например, диван и два кресла" /> {secondPickup &&
update('warehouseRequestSource2', e.target.value as DeliveryRequestSource || null)} />} update('productName2', e.target.value)} />
}
Адрес доставки update('street', e.target.value)} />
update('house', e.target.value)} /> update('apartment', e.target.value)} /> update('entrance', e.target.value)} /> update('floor', e.target.value)} />
Клиент и оплата update('customerName', e.target.value)} /> update('phone', e.target.value)} /> update('additionalPhone', e.target.value)} /> update('serviceInfo', e.target.value)} /> update('comment', e.target.value)} />
); }