Files
shop-server/client/src/pages/admin-orders/ui/AdminOrdersPage.tsx
T
2026-05-19 11:25:23 +05:00

175 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Fragment, useMemo, useState } from 'react'
import Alert from '@mui/material/Alert'
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import FormControl from '@mui/material/FormControl'
import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem'
import Select from '@mui/material/Select'
import Stack from '@mui/material/Stack'
import Table from '@mui/material/Table'
import TableBody from '@mui/material/TableBody'
import TableCell from '@mui/material/TableCell'
import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'
import TextField from '@mui/material/TextField'
import Typography from '@mui/material/Typography'
import { useQuery } from '@tanstack/react-query'
import { fetchAdminOrder, fetchAdminOrders } from '@/entities/order/api/admin-order-api'
import { OrderDetailContent } from '@/features/order-detail/ui/OrderDetailContent'
import { ORDER_STATUSES } from '@/shared/constants/order'
import { formatPriceRub } from '@/shared/lib/format-price'
import { groupOrdersByStatus } from '@/shared/lib/group-orders-by-status'
import { orderStatusLabelRu } from '@/shared/lib/order-status-labels'
import { AdminDialog } from '@/shared/ui/AdminDialog/AdminDialog'
export function AdminOrdersPage() {
const [q, setQ] = useState('')
const [status, setStatus] = useState('')
const [deliveryType, setDeliveryType] = useState<'delivery' | 'pickup' | ''>('')
const [dialogOpen, setDialogOpen] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const ordersQuery = useQuery({
queryKey: ['admin', 'orders', { q, status, deliveryType }],
queryFn: () =>
fetchAdminOrders({
q: q.trim() || undefined,
status: status || undefined,
deliveryType: deliveryType || undefined,
}),
})
const orderDetailQuery = useQuery({
queryKey: ['admin', 'orders', 'detail', selectedId],
queryFn: () => fetchAdminOrder(selectedId!),
enabled: Boolean(selectedId),
})
const open = (id: string) => {
setSelectedId(id)
setDialogOpen(true)
}
const items = useMemo(() => ordersQuery.data?.items ?? [], [ordersQuery.data?.items])
const groupedItems = useMemo(
() =>
groupOrdersByStatus(items, ORDER_STATUSES).map((group) => ({
statusCode: group.status,
items: group.items,
})),
[items],
)
const detail = orderDetailQuery.data?.item
return (
<Box>
<Typography variant="h4" sx={{ mb: 2 }}>
Заказы
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
Управление заказами доступно пользователю с правами администратора.
</Typography>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} sx={{ mb: 2 }}>
<TextField size="small" label="Поиск (id/email)" value={q} onChange={(e) => setQ(e.target.value)} fullWidth />
<FormControl size="small" sx={{ minWidth: 220 }}>
<InputLabel id="status-label">Статус</InputLabel>
<Select
labelId="status-label"
label="Статус"
value={status}
onChange={(e) => setStatus(String(e.target.value))}
>
<MenuItem value="">
<em>Все</em>
</MenuItem>
{ORDER_STATUSES.map((s) => (
<MenuItem key={s} value={s}>
{orderStatusLabelRu(s)}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl size="small" sx={{ minWidth: 220 }}>
<InputLabel id="delivery-type-label">Способ получения</InputLabel>
<Select
labelId="delivery-type-label"
label="Способ получения"
value={deliveryType}
onChange={(e) => {
const v = String(e.target.value)
if (v === '' || v === 'delivery' || v === 'pickup') setDeliveryType(v)
}}
>
<MenuItem value="">
<em>Все</em>
</MenuItem>
<MenuItem value="delivery">Доставка</MenuItem>
<MenuItem value="pickup">Самовывоз</MenuItem>
</Select>
</FormControl>
</Stack>
{ordersQuery.isError && <Alert severity="error">Не удалось загрузить заказы.</Alert>}
<Table size="small">
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Покупатель</TableCell>
<TableCell>Создан</TableCell>
<TableCell>Сумма</TableCell>
<TableCell>Позиций</TableCell>
<TableCell align="right">Действия</TableCell>
</TableRow>
</TableHead>
<TableBody>
{groupedItems.map((group) => (
<Fragment key={`group:${group.statusCode}`}>
<TableRow>
<TableCell colSpan={6} sx={{ fontWeight: 700, bgcolor: 'action.hover' }}>
{orderStatusLabelRu(group.statusCode)} ({group.items.length})
</TableCell>
</TableRow>
{group.items.map((o) => (
<TableRow key={o.id} hover>
<TableCell>{o.id.slice(-8)}</TableCell>
<TableCell>{o.user.email}</TableCell>
<TableCell>{new Date(o.createdAt).toLocaleString('ru-RU')}</TableCell>
<TableCell>{formatPriceRub(o.totalCents)}</TableCell>
<TableCell>{o.itemsCount}</TableCell>
<TableCell align="right">
<Button size="small" onClick={() => open(o.id)}>
Открыть
</Button>
</TableCell>
</TableRow>
))}
</Fragment>
))}
{ordersQuery.isSuccess && items.length === 0 && (
<TableRow>
<TableCell colSpan={6} sx={{ color: 'text.secondary' }}>
Заказов пока нет.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<AdminDialog
open={dialogOpen}
onClose={() => setDialogOpen(false)}
title="Заказ"
maxWidth="md"
loading={!detail && orderDetailQuery.isLoading}
error={orderDetailQuery.isError ? 'Не удалось загрузить заказ.' : null}
>
{detail && <OrderDetailContent detail={detail} orderId={detail.id} />}
</AdminDialog>
</Box>
)
}