base commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { CartPage } from './ui/CartPage'
|
||||
@@ -0,0 +1,127 @@
|
||||
import AddIcon from '@mui/icons-material/Add'
|
||||
import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined'
|
||||
import RemoveIcon from '@mui/icons-material/Remove'
|
||||
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 IconButton from '@mui/material/IconButton'
|
||||
import Stack from '@mui/material/Stack'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useUnit } from 'effector-react'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import { fetchMyCart, removeCartItem, setCartQty } from '@/entities/cart/api/cart-api'
|
||||
import { $user } from '@/shared/model/auth'
|
||||
import { formatPriceRub } from '@/shared/lib/format-price'
|
||||
|
||||
export function CartPage() {
|
||||
const user = useUnit($user)
|
||||
const qc = useQueryClient()
|
||||
|
||||
const cartQuery = useQuery({
|
||||
queryKey: ['me', 'cart'],
|
||||
queryFn: fetchMyCart,
|
||||
enabled: Boolean(user),
|
||||
})
|
||||
|
||||
const qtyMut = useMutation({
|
||||
mutationFn: (params: { id: string; qty: number }) => setCartQty(params.id, params.qty),
|
||||
onSuccess: () => void qc.invalidateQueries({ queryKey: ['me', 'cart'] }),
|
||||
})
|
||||
|
||||
const removeMut = useMutation({
|
||||
mutationFn: (id: string) => removeCartItem(id),
|
||||
onSuccess: () => void qc.invalidateQueries({ queryKey: ['me', 'cart'] }),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<Alert severity="info">
|
||||
Чтобы пользоваться корзиной, нужно войти. Перейдите на страницу{' '}
|
||||
<Typography component={RouterLink} to="/auth" sx={{ textDecoration: 'underline' }}>
|
||||
Вход
|
||||
</Typography>
|
||||
.
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
|
||||
const items = cartQuery.data?.items ?? []
|
||||
const total = items.reduce((s, x) => s + x.product.priceCents * x.qty, 0)
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Корзина
|
||||
</Typography>
|
||||
|
||||
{cartQuery.isError && <Alert severity="error">Не удалось загрузить корзину.</Alert>}
|
||||
|
||||
{cartQuery.isSuccess && items.length === 0 && <Alert severity="info">Корзина пуста.</Alert>}
|
||||
|
||||
{items.length > 0 && (
|
||||
<Stack spacing={2}>
|
||||
{items.map((x) => (
|
||||
<Box
|
||||
key={x.id}
|
||||
sx={{
|
||||
border: 1,
|
||||
borderColor: 'divider',
|
||||
borderRadius: 2,
|
||||
p: 2,
|
||||
bgcolor: 'background.paper',
|
||||
}}
|
||||
>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} alignItems={{ sm: 'center' }}>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Typography sx={{ fontWeight: 700 }}>{x.product.title}</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
{formatPriceRub(x.product.priceCents)} · {x.qty} шт.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<IconButton
|
||||
onClick={() => qtyMut.mutate({ id: x.id, qty: Math.max(0, x.qty - 1) })}
|
||||
disabled={qtyMut.isPending}
|
||||
aria-label="Уменьшить количество"
|
||||
>
|
||||
<RemoveIcon />
|
||||
</IconButton>
|
||||
<Typography sx={{ minWidth: 24, textAlign: 'center' }}>{x.qty}</Typography>
|
||||
<IconButton
|
||||
onClick={() => qtyMut.mutate({ id: x.id, qty: x.qty + 1 })}
|
||||
disabled={qtyMut.isPending}
|
||||
aria-label="Увеличить количество"
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
|
||||
<IconButton
|
||||
onClick={() => removeMut.mutate(x.id)}
|
||||
disabled={removeMut.isPending}
|
||||
aria-label="Удалить"
|
||||
>
|
||||
<DeleteOutlineOutlinedIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
|
||||
<Divider />
|
||||
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2} alignItems={{ sm: 'center' }}>
|
||||
<Typography variant="h6" sx={{ flexGrow: 1 }}>
|
||||
Итого: {formatPriceRub(total)}
|
||||
</Typography>
|
||||
<Button component={RouterLink} to="/checkout" variant="contained">
|
||||
Оформить заказ
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user