import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Button, Center, Icon, Sheet, Spinner } from '@barev/ui'; import { confirmDialog, dateKeyToDate, errorText, haptic, navigate, nextDays, paramsExtras, useI18n, useLocation, useToast, useTRPC } from '@barev/web'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Stars, Timeline, toneClass, useOrderTimeline, useParamsSummary } from '../lib.tsx'; export function OrderDetail({ id }: { id: string }) { const { t, amd, dt, locale } = useI18n(); const trpc = useTRPC(); const qc = useQueryClient(); const toast = useToast(); const summary = useParamsSummary(); const timeline = useOrderTimeline(); const order = useQuery({ ...trpc.client.orders.get.queryOptions({ id }), refetchInterval: 15_000 }); const preview = useQuery({ ...trpc.client.orders.cancelPreview.queryOptions({ id }), enabled: !!order.data?.active }); const [reschedule, setReschedule] = useState(false); const cancel = useMutation( trpc.client.orders.cancel.mutationOptions({ onSuccess: (r) => { haptic.ok(); toast(r.fee > 0 ? t('client.detail.cancelledWithFee', { fee: amd(r.fee) }) : t('client.detail.cancelled')); void qc.invalidateQueries(); }, onError: (e) => toast(errorText(t, e), 'error'), }), ); if (!order.data) return (
); const o = order.data; const arrived = o.history.some((h) => h.event === 'arrived'); const extras = paramsExtras(t, o.params); async function onCancel() { const p = preview.data?.policy; if (!p) return; const text = p.kind === 'free' ? t('client.detail.confirmCancelFree') : p.kind === 'late' ? t('client.detail.confirmCancelLate', { fee: amd(p.fee), pct: p.feePct }) : ''; if (await confirmDialog(text)) cancel.mutate({ id }); } return (
#{o.number}
{t(`status.${o.flow}.${o.status}`)} {arrived && o.status === 'EN_ROUTE' ? ` · ${t('client.detail.arrived')}` : ''}

{t(`services.${o.serviceCode}.name`)}

{summary(o.serviceCode, o.params)} {extras.length > 0 && ` + ${extras.map((e) => e.label).join(', ')}`}
{t('client.detail.when')}
{dt(o.scheduledAt, { weekday: 'long', day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' })}
{o.addressText && (
{t('client.detail.where')}
{o.districtCode ? `${t(`districts.${o.districtCode}`)}, ` : ''} {o.addressText}
)}
{t(`client.detail.providerLabel.${o.flow}`)}
{o.providerName ?? {t('client.detail.whoPending')}}
{o.done && }

{t('client.detail.progress')}

{o.photos.length > 0 && }
{t('client.order.price')} {amd(o.basePrice)}
{o.discountAmount > 0 && (
{t('client.order.discount', { pct: o.discountPct })} −{amd(o.discountAmount)}
)} {o.pointsUsed > 0 && (
{t('client.points.paidLine')} −{amd(o.pointsUsed)}
)}
{t('client.order.total')} {amd(o.totalPrice - o.pointsUsed)}
{o.cancelFee > 0 && (
{t('client.detail.cancelFee')} {amd(o.cancelFee)}
)}
{t(`payment.${o.paymentStatus}`)} {o.paymentMethod ? ` · ${o.paymentMethod.name[locale] || o.paymentMethod.name.ru}` : ''}
{o.note && (
{t('client.order.note')}
{o.note}
)}
{!o.active && o.status !== 'CANCELLED' && ( )} {o.status === 'CANCELLED' && ( )} {o.active && preview.data?.canReschedule && ( )} {o.active && preview.data?.cancellable && preview.data.policy.kind !== 'operator_only' && ( )} {o.active && preview.data && (!preview.data.cancellable || preview.data.policy.kind === 'operator_only' || !preview.data.canReschedule) && (

{t('client.detail.viaOperator', { hours: preview.data.freeHours })}

)}
setReschedule(false)} order={o} />
); } /** Оценка после выполнения: звёзды и комментарий; уже оценённый заказ показывает оценку с кнопкой «Изменить» */ function RateBlock({ id, rating }: { id: string; rating: { score: number; comment: string | null } | null }) { const { t } = useI18n(); const trpc = useTRPC(); const qc = useQueryClient(); const toast = useToast(); const { query } = useLocation(); const ref = useRef(null); const [editing, setEditing] = useState(!rating); const [score, setScore] = useState(rating?.score ?? 0); const [comment, setComment] = useState(rating?.comment ?? ''); useEffect(() => { if (query.get('rate') && ref.current) ref.current.scrollIntoView({ block: 'center' }); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const rate = useMutation( trpc.client.orders.rate.mutationOptions({ onSuccess: () => { haptic.ok(); toast(t('client.rate.thanks')); setEditing(false); void qc.invalidateQueries({ queryKey: trpc.client.orders.pathKey() }); }, onError: (e) => toast(errorText(t, e), 'error'), }), ); if (!editing && rating) return (
{t('client.rate.yours')}
{rating.comment &&

{rating.comment}

}
); return (

{t('client.rate.title')}

{t('client.rate.text')}

{score > 0 && ( <>