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)} />
)
}