From 012cfa0a068feeee14615e0b4caa12a6d744f861 Mon Sep 17 00:00:00 2001 From: Stepan Pilipenko Date: Sun, 9 Nov 2025 15:53:42 +0300 Subject: [PATCH] style fix --- backend/server/views.py | 12 ++ frontend/src/components/product-card.tsx | 25 ++-- frontend/src/components/product-detail.tsx | 18 ++- frontend/src/hocs/with-response.tsx | 5 + frontend/src/pages/basket.tsx | 159 +++++++++++++-------- frontend/src/pages/login.tsx | 5 +- frontend/src/pages/register.tsx | 8 +- frontend/src/pages/user.tsx | 15 +- frontend/src/storage/basket-slice.ts | 6 +- frontend/src/storage/shop-slice.ts | 3 + frontend/src/storage/user-slice.ts | 35 +++-- 11 files changed, 188 insertions(+), 103 deletions(-) diff --git a/backend/server/views.py b/backend/server/views.py index cdb9845..32b8500 100644 --- a/backend/server/views.py +++ b/backend/server/views.py @@ -18,6 +18,16 @@ async def shop(request): except Exception as error: return JsonResponse({"error": format(error)}, status=500) +initialUser = { + "id": -1, + "nickname": '', + "email": '', + "token": '', + "token_expiry_date": '', + "money": '', + "histories_id": [] +} + @csrf_exempt async def user(request): try: @@ -37,9 +47,11 @@ async def user(request): elif "unregister" in body.keys(): token = format_token(request.headers.get("Authorization")) api.unregister(token) + return JsonResponse({"success": [initialUser]}, status=200) elif "logout" in body.keys(): token = format_token(request.headers.get("Authorization")) api.logout(token) + return JsonResponse({"success": [initialUser]}, status=200) elif "add_money" in body.keys(): token = format_token(request.headers.get("Authorization")) api.add_money(token, body["add_money"]["money"]) diff --git a/frontend/src/components/product-card.tsx b/frontend/src/components/product-card.tsx index fcc10f4..19066f8 100644 --- a/frontend/src/components/product-card.tsx +++ b/frontend/src/components/product-card.tsx @@ -23,19 +23,24 @@ export const ProductCard = ({ productId }: ProductCardProps) => { - {name[0]} - + title={ + + {name} + } - title={name} - subheader={'2025 год'} /> ) => { + onError={(e: React.SyntheticEvent) => { e.currentTarget.src = 'https://react-learning.ru/image-compressed/default-image.jpg' }} @@ -44,13 +49,13 @@ export const ProductCard = ({ productId }: ProductCardProps) => { /> - + {description} - + {`Цена: ${cost} ₽`} - + {`Доступно: ${count} шт.`} diff --git a/frontend/src/components/product-detail.tsx b/frontend/src/components/product-detail.tsx index 1ec6d9c..c541f7b 100644 --- a/frontend/src/components/product-detail.tsx +++ b/frontend/src/components/product-detail.tsx @@ -42,6 +42,7 @@ export const ProductDetail = withProtection(({ productId }: ProductDetailProps) toast.error(error || 'Не известная ошибка при аутентификации пользователя', { toastId: 'error-toast', }) + dispatch(basket.action.clearError()) } return ( @@ -58,13 +59,18 @@ export const ProductDetail = withProtection(({ productId }: ProductDetailProps) }} > - {product?.name[0]} - + title={ + + {product?.name} + } - title={product?.name} - subheader={'2025 год'} /> UnknownAction + } } export const withResponse =

( @@ -34,6 +38,7 @@ export const withResponse =

( toast.error(error || 'Не известная ошибка при аутентификации пользователя', { toastId: 'error-toast', }) + dispatch(storage.action.clearError()) } if (loading) { diff --git a/frontend/src/pages/basket.tsx b/frontend/src/pages/basket.tsx index 3ec74b1..887487c 100644 --- a/frontend/src/pages/basket.tsx +++ b/frontend/src/pages/basket.tsx @@ -1,13 +1,22 @@ -import { Fragment, useCallback, useMemo, useState } from 'react' +import { Fragment, useCallback, useEffect, useMemo, useState } from 'react' import { + Avatar, Box, Button, Divider, IconButton, List, ListItem, + ListItemIcon, ListItemText, + Paper, Stack, + styled, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, Typography, } from '@mui/material' import { AddCircleOutline, RemoveCircleOutline } from '@mui/icons-material' @@ -26,6 +35,13 @@ const getText = (str?: string): string => { return (str?.length || 0) <= MAX_LEN_STR ? str || '' : str?.substring(0, MAX_LEN_STR) + '...' } +const StyledTableRow = styled(TableRow)(({ theme }) => ({ + cursor: 'pointer', + '&:hover': { + backgroundColor: theme.palette.action.hover, + }, +})) + type LasFetchType = 'addProduct' | 'deleteProduct' | 'buyProducts' | 'none' export const BasketPage = withProtection(() => { @@ -52,6 +68,10 @@ export const BasketPage = withProtection(() => { const dispatch = useAppDispatch() + useEffect(() => { + dispatch(basket.fetch.basket()) + }, [dispatch]) + const handleUpdateQuantity = useCallback( (productId: number, quantity: number) => { if (quantity > 0) { @@ -86,6 +106,7 @@ export const BasketPage = withProtection(() => { toast.error(error || 'Не известная ошибка при аутентификации пользователя', { toastId: 'error-toast', }) + dispatch(basket.action.clearError()) } if (loading && lasFetch === 'buyProducts') { @@ -104,65 +125,83 @@ export const BasketPage = withProtection(() => { Продуктовая корзина - - - {sortedProducts.length ? ( - - - {`Заказ №: ${1}`} - - - ) : ( - <> - )} - {sortedProducts?.map(product => ( - - handleProduct(product.id)} - primary={`Название: ${getText(getProduct(product.id)?.name)}`} - /> - handleProduct(product.id)} - primary={`Цена: ${Number(product.cost) * product.user_count} ₽`} - /> - handleProduct(product.id)} - primary={`На складе: ${getProduct(product.id)?.count} `} - /> - handleProduct(product.id)} - primary={`${product.user_count} шт.`} - /> - handleUpdateQuantity(product.id, 1)} - > - - - handleUpdateQuantity(product.id, -1)} - > - - - - ))} - - - - {sortedProducts.length ? ( - - ) : ( - - Корзина пуста - - )} - - + + + + {products.map(product => { + const prod = getProduct(product.id) + if (!prod) return null + + return ( + handleProduct(product.id)} + > + {/* Квадратное изображение */} + + + + + {/* Название */} + {getText(prod.name)} + + {/* Цена */} + {`Цена: ${Number(product.cost) * product.user_count} ₽`} + + {/* На складе */} + {`Доступно: ${prod.count}`} + + {/* Количество у пользователя */} + {`${product.user_count} шт.`} + + {/* Кнопки управления */} + + { + e.stopPropagation() // предотвращает срабатывание onClick строки + handleUpdateQuantity(product.id, 1) + }} + size="small" + > + + + { + e.stopPropagation() + handleUpdateQuantity(product.id, -1) + }} + size="small" + > + + + + + ) + })} + +
+
+ + + + {sortedProducts.length ? ( + + ) : ( + + Корзина пуста + + )} ) }) diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index ade2d10..a3c0dee 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -30,13 +30,14 @@ export const LoginPage = (): JSX.Element => { useEffect(() => { if (token && !error) { toast.success('Вы успешно вошли в систему!', { toastId: 'success-toast' }) - navigate('/') + navigate('/user') } if (error) { console.log({ error }) toast.error(error, { toastId: 'error-toast' }) + dispatch(user.action.clearError()) } - }, [token, error, navigate]) + }, [token, error, navigate, dispatch]) // инициализируем react-hook-form const { diff --git a/frontend/src/pages/register.tsx b/frontend/src/pages/register.tsx index 6685ddb..8fa8f17 100644 --- a/frontend/src/pages/register.tsx +++ b/frontend/src/pages/register.tsx @@ -32,15 +32,13 @@ export const RegisterPage = (): JSX.Element => { useEffect(() => { if (token && !error) { toast.success('Вы успешно зарегистрировались!', { toastId: 'success-toast' }) - // переходит туда откуда выпали на логин - if (state?.from) { - navigate(state?.from) - } + navigate('/user') } if (error) { toast.error(error, { toastId: 'error-toast' }) + dispatch(user.action.clearError()) } - }, [token, error, navigate, state]) + }, [token, error, navigate, state, dispatch]) // инициализируем react-hook-form const { diff --git a/frontend/src/pages/user.tsx b/frontend/src/pages/user.tsx index 97ea648..ff9ec23 100644 --- a/frontend/src/pages/user.tsx +++ b/frontend/src/pages/user.tsx @@ -18,11 +18,14 @@ const UserWithProtection = withProtection(() => { const handleLogout = useCallback(() => { navigate('/logout') - }, []) + }, [navigate]) - const handleAddMoney = useCallback((value: number) => { - dispatch(user.fetch.addMoney({ money: String(value) })) - }, []) + const handleAddMoney = useCallback( + (value: number) => { + dispatch(user.fetch.addMoney({ money: String(value) })) + }, + [dispatch] + ) const handleAddMoneyOpen = useCallback(() => { setOpenAddMoney(true) @@ -33,8 +36,10 @@ const UserWithProtection = withProtection(() => { }, []) const handleDeleteAccout = useCallback(() => { + navigate('/') dispatch(user.fetch.unregister()) - }, []) + dispatch(user.action.clearUser()) + }, [dispatch, navigate]) const handleDeleteAccoutOpen = useCallback(() => { setOpenDeleteAccout(true) diff --git a/frontend/src/storage/basket-slice.ts b/frontend/src/storage/basket-slice.ts index acfe8b1..1350c7e 100644 --- a/frontend/src/storage/basket-slice.ts +++ b/frontend/src/storage/basket-slice.ts @@ -24,7 +24,11 @@ const initialState: BasketState = { const basketSlice = createSlice({ name: 'basket', initialState, - reducers: {}, + reducers: { + clearError: state => { + state.error = '' + }, + }, extraReducers: builder => { builder .addCase(fetchBasket.fulfilled, (state, action) => { diff --git a/frontend/src/storage/shop-slice.ts b/frontend/src/storage/shop-slice.ts index 4aca8c5..55433ff 100644 --- a/frontend/src/storage/shop-slice.ts +++ b/frontend/src/storage/shop-slice.ts @@ -29,6 +29,9 @@ export const shopSlice = createSlice({ product.count += action.payload.count } }, + clearError: state => { + state.error = '' + }, }, extraReducers: builder => { builder diff --git a/frontend/src/storage/user-slice.ts b/frontend/src/storage/user-slice.ts index 663aa4e..9466665 100644 --- a/frontend/src/storage/user-slice.ts +++ b/frontend/src/storage/user-slice.ts @@ -16,32 +16,39 @@ type UserState = { error?: string } -const defaultUser: UserType = { - id: -1, - nickname: '', - email: '', - token: token.load(), - token_expiry_date: '', - money: '', - histories_id: [], +const defaultUser = (): UserType => { + return { + id: -1, + nickname: '', + email: '', + token: token.load(), + token_expiry_date: '', + money: '', + histories_id: [], + } } -const initialState: UserState = { - user: defaultUser, - loading: false, - error: undefined, +const initialState = (): UserState => { + return { + user: defaultUser(), + loading: false, + error: undefined, + } } const userSlice = createSlice({ name: 'user', - initialState, + initialState: initialState(), reducers: { setUser: (state, action: PayloadAction) => { state.user = action.payload }, clearUser() { token.save('') - return initialState + return initialState() + }, + clearError: state => { + state.error = '' }, }, extraReducers: builder => {