|
|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
import { Fragment, useCallback } from 'react'
|
|
|
|
|
import { Fragment, useCallback, useMemo, useState } from 'react'
|
|
|
|
|
import {
|
|
|
|
|
Box,
|
|
|
|
|
Button,
|
|
|
|
|
Divider,
|
|
|
|
|
IconButton,
|
|
|
|
|
List,
|
|
|
|
|
@ -10,11 +11,14 @@ import {
|
|
|
|
|
Typography,
|
|
|
|
|
} from '@mui/material'
|
|
|
|
|
import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material'
|
|
|
|
|
import { basket, useAppSelector } from '../storage'
|
|
|
|
|
import { BackButton } from '../components'
|
|
|
|
|
import { withProtection, withResponse } from '../hocs'
|
|
|
|
|
import { basket, useAppDispatch, useAppSelector } from '../storage'
|
|
|
|
|
import { BackButton, Spinner } from '../components'
|
|
|
|
|
import { withProtection } from '../hocs'
|
|
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
|
|
|
import type { BasketType } from '../types'
|
|
|
|
|
import { toast } from 'react-toastify'
|
|
|
|
|
import { isFulfilledAction, type ResultAction } from '../utils'
|
|
|
|
|
import type { ResponseData } from '../network'
|
|
|
|
|
|
|
|
|
|
const MAX_LEN_STR = 20
|
|
|
|
|
|
|
|
|
|
@ -22,7 +26,11 @@ const getText = (str?: string): string => {
|
|
|
|
|
return (str?.length || 0) <= MAX_LEN_STR ? str || '' : str?.substring(0, MAX_LEN_STR) + '...'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const BasketWithProtection = withProtection(() => {
|
|
|
|
|
type LasFetchType = 'addProduct' | 'deleteProduct' | 'buyProducts' | 'none'
|
|
|
|
|
|
|
|
|
|
export const BasketPage = withProtection(() => {
|
|
|
|
|
const [lasFetch, setLasFetch] = useState<LasFetchType>('none')
|
|
|
|
|
|
|
|
|
|
const products = useAppSelector(basket.selector.basket)
|
|
|
|
|
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
@ -34,13 +42,27 @@ const BasketWithProtection = withProtection(() => {
|
|
|
|
|
[products]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const sortedProducts = useMemo(() => {
|
|
|
|
|
return [...products].sort((product1, product2) => {
|
|
|
|
|
if (product1.name < product2.name) return -1
|
|
|
|
|
if (product1.name > product2.name) return 1
|
|
|
|
|
return 0
|
|
|
|
|
})
|
|
|
|
|
}, [products])
|
|
|
|
|
|
|
|
|
|
const dispatch = useAppDispatch()
|
|
|
|
|
|
|
|
|
|
const handleUpdateQuantity = useCallback(
|
|
|
|
|
(productId: number, quantity: number) => {
|
|
|
|
|
const stock = getProduct(productId)?.user_count || 0
|
|
|
|
|
// TODO: сделать fetch на изменение количества товаров
|
|
|
|
|
console.debug({ stock, quantity })
|
|
|
|
|
if (quantity > 0) {
|
|
|
|
|
setLasFetch('addProduct')
|
|
|
|
|
dispatch(basket.fetch.addProduct({ product_id: productId }))
|
|
|
|
|
} else {
|
|
|
|
|
setLasFetch('deleteProduct')
|
|
|
|
|
dispatch(basket.fetch.deleteProduct({ product_id: productId }))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[getProduct]
|
|
|
|
|
[dispatch]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleProduct = useCallback(
|
|
|
|
|
@ -50,6 +72,30 @@ const BasketWithProtection = withProtection(() => {
|
|
|
|
|
[navigate]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleBuy = useCallback(async () => {
|
|
|
|
|
setLasFetch('buyProducts')
|
|
|
|
|
const result: ResultAction<ResponseData> = await dispatch(basket.fetch.buyProducts())
|
|
|
|
|
if (isFulfilledAction(result)) {
|
|
|
|
|
toast.success('Успешная покупка', { toastId: ' toast-success' })
|
|
|
|
|
}
|
|
|
|
|
}, [dispatch])
|
|
|
|
|
|
|
|
|
|
const loading = useAppSelector(basket.selector.loading)
|
|
|
|
|
const error = useAppSelector(basket.selector.error)
|
|
|
|
|
if (error) {
|
|
|
|
|
toast.error(error || 'Не известная ошибка при аутентификации пользователя', {
|
|
|
|
|
toastId: 'error-toast',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loading && lasFetch === 'buyProducts') {
|
|
|
|
|
return (
|
|
|
|
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
|
|
|
|
|
<Spinner />
|
|
|
|
|
</Box>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div>
|
|
|
|
|
<BackButton text="< Главная" path="/" />
|
|
|
|
|
@ -60,12 +106,16 @@ const BasketWithProtection = withProtection(() => {
|
|
|
|
|
</Stack>
|
|
|
|
|
<List component="nav" aria-label="cart items">
|
|
|
|
|
<Fragment>
|
|
|
|
|
<Stack direction="row" alignItems="center" spacing={2} sx={{ pb: 2 }}>
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
|
|
|
{`Заказ №: ${1}`}
|
|
|
|
|
</Typography>
|
|
|
|
|
</Stack>
|
|
|
|
|
{products?.map(product => (
|
|
|
|
|
{sortedProducts.length ? (
|
|
|
|
|
<Stack direction="row" alignItems="center" spacing={2} sx={{ pb: 2 }}>
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
|
|
|
{`Заказ №: ${1}`}
|
|
|
|
|
</Typography>
|
|
|
|
|
</Stack>
|
|
|
|
|
) : (
|
|
|
|
|
<></>
|
|
|
|
|
)}
|
|
|
|
|
{sortedProducts?.map(product => (
|
|
|
|
|
<ListItem key={product.id}>
|
|
|
|
|
<ListItemText
|
|
|
|
|
onClick={() => handleProduct(product.id)}
|
|
|
|
|
@ -86,18 +136,14 @@ const BasketWithProtection = withProtection(() => {
|
|
|
|
|
<IconButton
|
|
|
|
|
edge="start"
|
|
|
|
|
aria-label="add"
|
|
|
|
|
onClick={() =>
|
|
|
|
|
handleUpdateQuantity(product.id, (product.user_count || 0) + 1)
|
|
|
|
|
}
|
|
|
|
|
onClick={() => handleUpdateQuantity(product.id, 1)}
|
|
|
|
|
>
|
|
|
|
|
<AddCircleOutline />
|
|
|
|
|
</IconButton>
|
|
|
|
|
<IconButton
|
|
|
|
|
edge="start"
|
|
|
|
|
aria-label="remove"
|
|
|
|
|
onClick={() =>
|
|
|
|
|
handleUpdateQuantity(product.id, (product.user_count || 0) - 1)
|
|
|
|
|
}
|
|
|
|
|
onClick={() => handleUpdateQuantity(product.id, -1)}
|
|
|
|
|
>
|
|
|
|
|
<RemoveCircleOutline />
|
|
|
|
|
</IconButton>
|
|
|
|
|
@ -106,10 +152,17 @@ const BasketWithProtection = withProtection(() => {
|
|
|
|
|
<Box sx={{ pb: 3 }}>
|
|
|
|
|
<Divider />
|
|
|
|
|
</Box>
|
|
|
|
|
{sortedProducts.length ? (
|
|
|
|
|
<Button color="success" variant="contained" onClick={handleBuy}>
|
|
|
|
|
{'КУПИТЬ'}
|
|
|
|
|
</Button>
|
|
|
|
|
) : (
|
|
|
|
|
<Typography variant="button" sx={{ fontSize: 16, pt: 2, pb: 2 }}>
|
|
|
|
|
Корзина пуста
|
|
|
|
|
</Typography>
|
|
|
|
|
)}
|
|
|
|
|
</Fragment>
|
|
|
|
|
</List>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const BasketPage = withResponse(BasketWithProtection, basket, basket.fetch.basket)
|
|
|
|
|
|