import React from "react"; import { useParams, useSearchParams } from "react-router-dom"; import { DeliveryChoiceFlow } from "../components/client/DeliveryChoiceFlow"; import { DeliverySlotsPicker } from "../components/client/DeliverySlotsPicker"; import { DeliveryStateNotice } from "../components/client/DeliveryStateNotice"; import { Panel } from "../components/UI/Panel"; import { confirmDeliveryChoice, fetchDeliveryInvitation, } from "../services/deliveryInvitationApi"; export const groupSlotsFromInvitation = (invitation) => { if (!invitation) { return []; } const rawSlots = invitation.availableSlots || []; const deliveryDate = invitation.deliveryDate; const deliveryTime = invitation.deliveryTime; if (!rawSlots.length && !deliveryDate) { return []; } if (!rawSlots.length && deliveryDate) { return [ { id: `slot-${deliveryDate}-${deliveryTime || "default"}`, date: deliveryDate, time: deliveryTime || "Половина дня", }, ]; } return rawSlots.map((raw, index) => { const parts = raw.split(","); const datePart = parts[0]?.trim() || ""; const timePart = parts.slice(1).join(",").trim() || ""; const parsedDate = datePart.replace(/[а-яё]+/gi, "").trim() || deliveryDate || ""; return { id: `slot-${index}-${raw}`, date: deliveryDate || parsedDate, time: timePart || deliveryTime || raw, }; }); }; export const buildDeliveryConfirmationPayload = ({ slot, invitation, searchDate, }) => ({ deliveryDate: slot?.date || searchDate || invitation?.deliveryDate || undefined, deliveryTime: slot?.time || invitation?.deliveryTime || undefined, }); export const ClientDeliveryPage = () => { const { token } = useParams(); const [searchParams] = useSearchParams(); const [invitation, setInvitation] = React.useState(null); const [loading, setLoading] = React.useState(Boolean(token)); const [error, setError] = React.useState(""); const [actionMessage, setActionMessage] = React.useState(""); const [selectedSlotId, setSelectedSlotId] = React.useState(null); React.useEffect(() => { let cancelled = false; const loadInvitation = async () => { if (!token) { setLoading(false); setError("Не передан токен приглашения."); return; } setLoading(true); setError(""); try { const loadedInvitation = await fetchDeliveryInvitation(token); if (!cancelled) { setInvitation(loadedInvitation); } } catch (fetchError) { if (!cancelled) { setInvitation(null); setError(fetchError instanceof Error ? fetchError.message : "Не удалось загрузить приглашение"); } } finally { if (!cancelled) { setLoading(false); } } }; loadInvitation(); return () => { cancelled = true; }; }, [token]); const slots = React.useMemo( () => groupSlotsFromInvitation(invitation), [invitation], ); const invitationState = invitation?.state || "awaiting_choice"; const handleConfirmChoice = React.useCallback( async ({ deliveryDate, deliveryTime }) => { if (!token) { return; } setActionMessage("Сохраняем выбор..."); try { await confirmDeliveryChoice({ token, deliveryTime, deliveryDate, }); const loadedInvitation = await fetchDeliveryInvitation(token); setInvitation(loadedInvitation); setActionMessage("Выбор сохранен, спасибо."); } catch (confirmError) { setActionMessage(""); setError(confirmError instanceof Error ? confirmError.message : "Не удалось сохранить выбор"); } }, [token, invitation], ); const handleSlotSelect = React.useCallback( (slot) => { setSelectedSlotId(slot.id); handleConfirmChoice( buildDeliveryConfirmationPayload({ slot, invitation, searchDate: searchParams.get("date"), }), ); }, [handleConfirmChoice, invitation, searchParams], ); const handleRequestNewLink = React.useCallback(() => { setActionMessage("Если ссылка больше не работает, логист передаст новую ссылку вручную."); }, []); if (loading) { return (

Доставка заказа

Загрузка страницы

Подтягиваем актуальные данные по заказу.

); } if (error && !invitation) { return (

Доставка заказа

Не удалось открыть страницу

{error}

); } const isActiveState = ["awaiting_choice", "opened", "reminder_sent"].includes(invitationState); return (

Доставка заказа

Согласование доставки

{isActiveState ? "Вам предложены варианты доставки. Выберите удобную дату и время." : "По этому заказу согласование доставки завершено или передано логисту."}

{isActiveState && slots.length ? ( ) : null} {isActiveState ? ( ) : ( )} {actionMessage ? ( {actionMessage} ) : null} {!loading && error && invitation ? : null}
); };