import { useEffect, useMemo } from 'react' import Alert from '@mui/material/Alert' import Box from '@mui/material/Box' import Button from '@mui/material/Button' import Divider from '@mui/material/Divider' import Link from '@mui/material/Link' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { Link as RouterLink, useNavigate, useParams, useSearchParams } from 'react-router-dom' import { confirmOrderReceived, createOrderPayment, fetchMyOrder, getOrderPaymentStatus, postOrderMessage, fetchOrderReviewEligibility, } from '@/entities/order/api/order-api' import { postProductReview, uploadReviewImage } from '@/entities/review/api/reviews-api' import { markOrderMessagesRead } from '@/entities/user/api/messages-api' import { OrderChat } from '@/features/order-chat' import { OrderPaymentSection } from '@/features/order-payment' import { ReviewSection } from '@/features/product-review' import { deliveryCarrierLabelRu } from '@/shared/constants/delivery-carrier' import { PICKUP_ADDRESS_FULL } from '@/shared/constants/pickup-point' import { formatPriceRub } from '@/shared/lib/format-price' import { parseOrderAddressSnapshot } from '@/shared/lib/order-address-snapshot' import { orderStatusLabelRu } from '@/shared/lib/order-status-labels' export function OrderDetailPage() { const { id } = useParams() const qc = useQueryClient() const navigate = useNavigate() const [searchParams] = useSearchParams() const paidParam = searchParams.get('paid') const orderQuery = useQuery({ queryKey: ['me', 'orders', id], queryFn: () => fetchMyOrder(id!), enabled: Boolean(id), }) const payMut = useMutation({ mutationFn: () => createOrderPayment(id!), onSuccess: async (data) => { window.location.href = data.confirmationUrl }, }) const paymentStatusQuery = useQuery({ queryKey: ['me', 'orders', id, 'payment-status'], queryFn: () => getOrderPaymentStatus(id!), enabled: Boolean(id && paidParam === '1'), refetchInterval: (query) => { const data = query.state.data if (data && (data.paid || data.status === 'canceled')) return false return 3000 }, }) useEffect(() => { const data = paymentStatusQuery.data if (data && (data.paid || data.status === 'canceled') && paidParam === '1') { qc.invalidateQueries({ queryKey: ['me', 'orders', id] }) navigate(`/me/orders/${id}`, { replace: true }) } }, [paymentStatusQuery.data, paidParam, qc, id, navigate]) const confirmMut = useMutation({ mutationFn: () => confirmOrderReceived(id!), onSuccess: () => Promise.all([ qc.invalidateQueries({ queryKey: ['me', 'orders', id] }), qc.invalidateQueries({ queryKey: ['me', 'orders'] }), ]), }) const msgMut = useMutation({ mutationFn: (text: string) => postOrderMessage(id!, text), onSuccess: async () => { await qc.invalidateQueries({ queryKey: ['me', 'orders', id] }) await qc.invalidateQueries({ queryKey: ['me', 'conversations'] }) }, }) const order = orderQuery.data?.item const eligibilityQuery = useQuery({ queryKey: ['me', 'orders', id, 'review-eligibility'], queryFn: () => fetchOrderReviewEligibility(id!), enabled: Boolean(id && order?.status === 'DONE'), }) const reviewMut = useMutation({ mutationFn: async (params: { productId: string; rating: number; text: string; imageUrl: string | null }) => { await postProductReview(params.productId, { rating: params.rating, text: params.text.length ? params.text : null, imageUrl: params.imageUrl, }) }, onSuccess: async () => { await qc.invalidateQueries({ queryKey: ['me', 'orders', id, 'review-eligibility'] }) }, }) const uploadReviewImageMut = useMutation({ mutationFn: (file: File) => uploadReviewImage(file), }) useEffect(() => { if (!id || orderQuery.status !== 'success' || !order) return void (async () => { await markOrderMessagesRead(id).catch(() => undefined) await qc.invalidateQueries({ queryKey: ['me', 'messages', 'unread-count'] }) })() }, [id, order, orderQuery.status, qc]) const address = useMemo(() => parseOrderAddressSnapshot(order?.addressSnapshotJson), [order?.addressSnapshotJson]) if (!id) return Некорректный заказ. if (orderQuery.isLoading) return Загрузка… if (orderQuery.isError || !order) return Не удалось загрузить заказ. const payOnPickup = (order.paymentMethod ?? 'online') === 'on_pickup' return ( Заказ #{order.id.slice(-6)} Статус: {orderStatusLabelRu(order.status)} {paidParam === '1' && paymentStatusQuery.data && ( {paymentStatusQuery.data.paid ? 'Оплата прошла успешно!' : paymentStatusQuery.data.status === 'canceled' ? 'Оплата отмена. Вы можете попробовать снова.' : 'Ожидаем подтверждения оплаты…'} )} Позиции {order.items.map((i) => ( {i.titleSnapshot} {i.qty} × {formatPriceRub(i.priceCentsSnapshot)} {formatPriceRub(i.priceCentsSnapshot * i.qty)} ))} Товары: {formatPriceRub(order.itemsSubtotalCents)} {order.deliveryType === 'delivery' && ( Доставка: {formatPriceRub(order.deliveryFeeCents)} )} Итого: {formatPriceRub(order.totalCents)} Получение Способ: {order.deliveryType === 'pickup' ? 'Самовывоз' : 'Доставка'} {order.deliveryType === 'pickup' && <> · оплата: {payOnPickup ? 'при получении' : 'онлайн'}} {order.deliveryType === 'delivery' && deliveryCarrierLabelRu(order.deliveryCarrier) && ( Служба: {deliveryCarrierLabelRu(order.deliveryCarrier)} )} {order.deliveryType === 'delivery' && ( <> {address ? ( <> {address.addressLine} Получатель: {address.recipientName} · {address.recipientPhone} {address.comment && ( Комментарий: {address.comment} )} ) : ( Адрес не распознан. )} )} {order.deliveryType === 'pickup' && ( Адрес самовывоза и карта — на странице{' '} О нас . {PICKUP_ADDRESS_FULL} Заберите заказ ко времени, которое согласуем в чате заказа. )} {order.comment && ( Комментарий к заказу: {order.comment} )} payMut.mutate()} /> {(order.deliveryType === 'delivery' && order.status === 'SHIPPED') || (order.deliveryType === 'pickup' && order.status === 'READY_FOR_PICKUP') ? ( Получение заказа {order.deliveryType === 'delivery' ? 'Когда забрали посылку у курьера или на пункте выдачи — подтвердите получение.' : 'Когда забрали заказ самовывозом — подтвердите получение.'} ) : null} {order.status === 'DONE' && eligibilityQuery.isSuccess && eligibilityQuery.data.canReview && ( { await reviewMut.mutateAsync(params) }} onUploadImage={async (file) => { const result = await uploadReviewImageMut.mutateAsync(file) return result }} /> )} msgMut.mutate(text)} /> ) }