Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development 291 add to cart #321

Merged
merged 17 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d89a084
Добавил api для получения корзины и добавления в корзину. Начал писат…
kirill-k88 Apr 4, 2024
9c64d94
Перенес запрос корзины в App
kirill-k88 Apr 4, 2024
6a5157d
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 5, 2024
14e6316
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 10, 2024
397b762
Добавление в корзину на странице товара. Добавил в Axios обработку ку…
kirill-k88 Apr 10, 2024
a5111f2
Зачинил перезапрос корзины после доабавления в корзину нового товара.…
kirill-k88 Apr 11, 2024
4b2a62f
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 12, 2024
c4d606a
добавил импорт типов
kirill-k88 Apr 12, 2024
9b12891
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 15, 2024
48c385d
Сделал добавление в корзину для виджета ProductItem и его попапа
kirill-k88 Apr 17, 2024
f000c39
Дабавил api для кнопок и чуть подправил стили
kirill-k88 Apr 17, 2024
ffeceb3
Смерджил с master
kirill-k88 Apr 17, 2024
44cc92a
Исправил замечания по код-ревью. Немного подправил стили
kirill-k88 Apr 19, 2024
3f8f28c
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 19, 2024
a0c8adc
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 22, 2024
ffc0d4b
Вынес вспомогательные функции добавления в корзину с хук. поправил до…
kirill-k88 Apr 22, 2024
24848fe
Merge branch 'master' of github.com:Studio-Yandex-Practicum/maxboom_f…
kirill-k88 Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { RouterProvider } from 'react-router-dom'

import { getCart } from '@/entities/CartEntity/model/slice/cartEntitySlice'
import { loginActions } from '@/features/login/model/slice/loginSlice'
import { $api } from '@/shared/api/api'
import { tokenFromStorageGet } from '@/shared/libs/helpers/localStorageHandler'
Expand All @@ -17,6 +18,8 @@ function App() {
dispatch(loginActions.initAuth(token))
$api.addToken(token)
}

dispatch(getCart())
}, [dispatch])
return <RouterProvider router={router} />
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/providers/StoreProvider/config/StateSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IFeedbackSchema } from '@/pages/FeedbackPage/model/types/types'
import { ICategorySchema, IMainCategorySchema } from '@/widgets/CategoryList/types/types'
import { ICategoryFiltersSchema } from '@/components/Dropdown/types/types'
import type { IFeedbackFormSchema } from '@/widgets/FeedbackForm/model/scheme/feedbackFormSliceSchema'
import { ICartEntitySchema } from '@/entities/CartEntity/model/types/types'
import { IAboutUsSchema } from '@/pages/AboutUsPage/model/types/types'
import { ICartSchema } from '@/pages/CartPage/model/types'
import { IProductAmountStateSchema } from '@/features/CartEdit/model/types'
Expand All @@ -42,6 +43,7 @@ export interface StateSchema {
categoryBranches: ICategorySchema
getCategories: IMainCategorySchema
cart: ICartSchema
cartEntity: ICartEntitySchema
categoryFilters: ICategoryFiltersSchema
productAmount: IProductAmountStateSchema
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/providers/StoreProvider/config/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { categoryFiltersSliceReducer } from '@/components/Dropdown/slice/filters
import { feedbackFormReducer } from '@/widgets/FeedbackForm/model/slice/feedbackFormSlice'
import { aboutUsReducer } from '@/pages/AboutUsPage/model/slice/aboutUsSlice'
import { cartReducer } from '@/pages/CartPage/model/slice'
import { cartEntityReducer } from '@/entities/CartEntity/model/slice/cartEntitySlice'
import { productAmountReducer } from '@/features/CartEdit/model/slice/productAmountSlice'

export type RootState = StateSchema
Expand All @@ -48,6 +49,7 @@ const rootReducer: ReducersMapObject<RootState> = {
categorySlug: categorySlugSliceReducer,
categoryBranches: categoryBranchesReducer,
getCategories: getCategoriesReducer,
cartEntity: cartEntityReducer,
cart: cartReducer,
categoryFilters: categoryFiltersSliceReducer,
productAmount: productAmountReducer
Expand Down
27 changes: 27 additions & 0 deletions src/entities/CartEntity/model/functions/cartHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ICartProduct } from '../types/types'

/**
* Функция проверки наличия товара в корзине
*
* @param {string} slug Slug товара
* @param {ICartProduct[]} cartProducts Массив товаров в корзине
* @returns {boolean} true, если товар есть в корзине
*/
export const isInCartBySlug = (slug: string, cartProducts: ICartProduct[]): boolean => {
if (cartProducts.length === 0) return false

return cartProducts.some(p => p.product.slug === slug)
}

/**
* Функция проверки наличия товара в корзине
*
* @param {number} id Id товара
* @param {ICartProduct[]} cartProducts Массив товаров в корзине
* @returns {boolean} true, если товар есть в корзине
*/
export const isInCartById = (id: number, cartProducts: ICartProduct[]): boolean => {
if (cartProducts.length === 0) return false

return cartProducts.some(p => p.product.id === id)
}
21 changes: 21 additions & 0 deletions src/entities/CartEntity/model/hooks/cartHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from 'react'

import { isInCartBySlug } from '../functions/cartHelper'
import { ICartProduct } from '../types/types'

/**
* Hook для отслеживания есть ли товар в корзине
*
* @param {string} slug - slug продукта
* @param {ICartProduct[]} productsInCart - массив продуктов в корзине
* @returns boolean - возвращает стейт isInCart со значением true/false
*/
export const useIsProductInCart = (slug: string, productsInCart: ICartProduct[]) => {
const [isInCart, setIsInCart] = useState(false)

useEffect(() => {
setIsInCart(isInCartBySlug(slug, productsInCart))
}, [slug, productsInCart])

return isInCart
}
5 changes: 5 additions & 0 deletions src/entities/CartEntity/model/hooks/sliceHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useSelector } from 'react-redux'

import { StateSchema } from '@/app/providers/StoreProvider'

export const useCartSelector = () => useSelector((store: StateSchema) => store.cartEntity)
79 changes: 79 additions & 0 deletions src/entities/CartEntity/model/slice/cartEntitySlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

import type { ThunkConfig } from '@/app/providers/StoreProvider/config/StateSchema'
import { apiErrorIdentify } from '@/shared/api/apiErrorIdentify'
import { rejectedPayloadHandle } from '@/shared/api/rejectedPayloadHandle'
import { ApiError, ApiErrorTypes, ApiRoutes } from '@/shared/api/types'

import type { IAddedProduct, ICartEntity, ICartEntitySchema } from '../types/types'

export const getCart = createAsyncThunk<ICartEntity, void, ThunkConfig<ApiError>>(
'cart/getCart',
async (_, thunkAPI) => {
const { rejectWithValue, extra } = thunkAPI
try {
const { data } = await extra.api.get(`api/${ApiRoutes.CART_LIST}/`)
return data
} catch (error) {
return rejectWithValue(apiErrorIdentify(error, ApiErrorTypes.DATA_EMPTY_ERROR))
}
}
)

export const addToCart = createAsyncThunk<void, IAddedProduct, ThunkConfig<ApiError>>(
'cart/addToCart',
async (addedProduct, thunkAPI) => {
const { rejectWithValue, extra } = thunkAPI
try {
await extra.api.post(`api/${ApiRoutes.CART_LIST}/`, addedProduct)

thunkAPI.dispatch(getCart())
} catch (error) {
return rejectWithValue(apiErrorIdentify(error, ApiErrorTypes.DATA_EMPTY_ERROR))
}
}
)

const initialState: ICartEntitySchema = {
isLoading: false,
error: null,
cart: {
id: 0,
products: [],
user: 0,
cart_full_price: 0
}
}

export const cartEntitySlice = createSlice({
name: 'cart',
initialState,
reducers: {},
extraReducers: builder => {
builder
.addCase(getCart.pending, state => {
state.isLoading = true
})
.addCase(getCart.fulfilled, (state, { payload }) => {
state.isLoading = false
state.cart = payload
})
.addCase(getCart.rejected, (state, { payload }) => {
state.isLoading = false
state.error = rejectedPayloadHandle(payload)
}),
builder
.addCase(addToCart.pending, state => {
state.isLoading = true
})
.addCase(addToCart.fulfilled, state => {
state.isLoading = false
})
.addCase(addToCart.rejected, (state, { payload }) => {
state.isLoading = false
state.error = rejectedPayloadHandle(payload)
})
}
})

export const { actions: cartEntityActions, reducer: cartEntityReducer } = cartEntitySlice
52 changes: 52 additions & 0 deletions src/entities/CartEntity/model/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export interface ICartEntitySchema {
isLoading?: boolean
error?: string | string[] | null
cart: ICartEntity
}

export interface ICartEntity {
id: number
products: ICartProduct[]
user: number
cart_full_price: number
}

export interface ICartProduct {
name: string
image: 'string'
price: number
amount: number
full_price: number
product: TProduct
}

export interface IAddedProduct {
product: number
cart: number
amount: number
}

interface IObjectWithImage {
image: string
index?: number
}

type TImgList = Array<IObjectWithImage>

type TProduct = {
label_popular: boolean
label_hit: boolean
id: number
category: string
brand: string
price: number
name: string
slug: string
description: string
code: number
wb_urls: string
quantity: number
is_deleted: boolean
wholesale: number
images: TImgList
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
display: flex;
background-color: var.$white;

@include media.respond-to('middle') {
@include media.respond-to('large') {
flex-direction: column-reverse;
align-items: center;
}
Expand All @@ -29,7 +29,7 @@
border-top: 2px solid var.$bg-subscribe;
border-bottom: 2px solid var.$bg-subscribe;

@include media.respond-to('middle') {
@include media.respond-to('large') {
border-top: none;
border-bottom: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
flex-direction: column;
overflow: hidden;

@include media.respond-to('middle') {
@include media.respond-to('large') {
flex-direction: row;
justify-content: center;
width: 100%;
height: 160px;
}
Expand All @@ -24,15 +25,17 @@
flex-direction: column;
overflow: hidden;

@include media.respond-to('middle') {
@include media.respond-to('large') {
flex-direction: row;
justify-content: center;
width: 80%;
height: 100%;
}
}

&__imageframe {
width: 100%;
max-width: 160px;
min-height: 110px;
box-sizing: border-box;
border-top: 2px solid var.$bg-subscribe;
Expand All @@ -43,15 +46,22 @@
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.3s;

&_active {
background-color: var.$body-bg;
}

@include media.respond-to('middle') {
@include media.respond-to('large') {
border-right: none;
border-bottom: 2px solid var.$bg-subscribe;
}

&:last-child {
@include media.respond-to('large') {
border-right: 2px solid var.$bg-subscribe;
}
}
}

&__image {
Expand All @@ -76,18 +86,42 @@
display: none;
}

@include media.respond-to('middle') {
@include media.respond-to('large') {
width: 20%;
height: 100%;
flex-direction: row-reverse;
}
}

&__button {
width: 50%;
height: 100%;
transition: background-color 0.3s;

&:hover {
background-color: var.$body-bg;
}

@include media.respond-to('middle') {
padding: 1px;
}

&_arrowDown {
@include media.respond-to('large') {
transform: rotate(270deg);
}
@include media.respond-to('middle') {
transform: rotate(270deg) scale(0.5);
}
}

&_arrowUp {
@include media.respond-to('large') {
transform: rotate(270deg);
}
@include media.respond-to('middle') {
transform: rotate(270deg) scale(0.5);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const PreviewCarousel: FC<TPreviewCarouselProps> = ({ imgList, curImg, se
size={ButtonSize.M}
className={styles.previewcarousel__button}
onClick={onNextHandle}>
<IconArrowDown />
<IconArrowDown className={styles.previewcarousel__button_arrowDown} />
</Button>
<Button
type="button"
Expand All @@ -107,7 +107,7 @@ export const PreviewCarousel: FC<TPreviewCarouselProps> = ({ imgList, curImg, se
size={ButtonSize.M}
className={styles.previewcarousel__button}
onClick={onPrevHandle}>
<IconArrowUp />
<IconArrowUp className={styles.previewcarousel__button_arrowUp} />
</Button>
</div>
</div>
Expand Down
Loading
Loading