Skip to content

Commit

Permalink
Merge branch 'master' into api-294-get-cart-list
Browse files Browse the repository at this point in the history
  • Loading branch information
JuliaAvramenko authored Apr 17, 2024
2 parents 19580fe + b8c1caa commit f436eee
Show file tree
Hide file tree
Showing 26 changed files with 354 additions and 112 deletions.
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 @@ -14,6 +14,7 @@ import { CategoryListSchema } from '@/widgets/CategoryGrid/model/types/types'
import { ICategoryProductsSchema } from '@/pages/ProductsPage/types/types'
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 { ICartSchema } from '@/pages/CartPage/model/types'

Expand All @@ -38,6 +39,7 @@ export interface StateSchema {
categoryBranches: ICategorySchema
getCategories: IMainCategorySchema
cart: ICartSchema
categoryFilters: ICategoryFiltersSchema
}

export interface ThunkExtraArg {
Expand Down
4 changes: 3 additions & 1 deletion src/app/providers/StoreProvider/config/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { categorySlugSliceReducer } from '@/entities/Category/slice/categorySlug
import { categoryBranchesReducer } from '@/widgets/CategoryList/slice/pageCategoryBranchesSlice'
import { getCategoriesReducer } from '@/widgets/CategoryList/slice/pageCategoriesSlice'
import { feedbackReducer } from '@/pages/FeedbackPage/model/slice/feedbackSlice'
import { categoryFiltersSliceReducer } from '@/components/Dropdown/slice/filtersSlice'
import { feedbackFormReducer } from '@/widgets/FeedbackForm/model/slice/feedbackFormSlice'
import { cartReducer } from '@/pages/CartPage/model/slice'

Expand All @@ -44,7 +45,8 @@ const rootReducer: ReducersMapObject<RootState> = {
categorySlug: categorySlugSliceReducer,
categoryBranches: categoryBranchesReducer,
getCategories: getCategoriesReducer,
cart: cartReducer
cart: cartReducer,
categoryFilters: categoryFiltersSliceReducer
}

export function createReduxStore(initialState: RootState) {
Expand Down
3 changes: 2 additions & 1 deletion src/app/router/AppRouter/ui/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ProductPage } from '@/pages/ProductPage/ProductPage'
import { ProductsPage } from '@/pages/ProductsPage/ProductsPage'
import RootPage from '@/pages/RootPage/RootPage'
import SearchResultsPage from '@/pages/SearchResultsPage/SearchResultsPage'
import VouchersPage from '@/pages/VouchersPage/VouchersPage'
import { Routes } from '@/shared/config/routerConfig/routes'

export const AppRouter = createBrowserRouter([
Expand Down Expand Up @@ -105,7 +106,7 @@ export const AppRouter = createBrowserRouter([
},
{
path: Routes.VOUCHERS,
element: <ProductsPage /> // временная заглушка нужна страница с подарочными сертификатами
element: <VouchersPage />
},
{
path: Routes.PRODUCT + '/:slug',
Expand Down
8 changes: 5 additions & 3 deletions src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { ChangeEvent, useState } from 'react'

import type { TSortOptions } from '@/components/PageControls/PageControls'

import styles from './Dropdown.module.scss'

interface DropdownProps extends React.HTMLProps<HTMLSelectElement> {
items: (string | number)[]
items: TSortOptions[]
defaultItem?: string
onSelect: React.ChangeEventHandler<HTMLSelectElement>
}
Expand All @@ -26,8 +28,8 @@ export const Dropdown: React.FC<DropdownProps> = ({ items, defaultItem, onSelect
return (
<select className={styles.select} value={selectedItem} onChange={handleSelect} {...props}>
{items.map(item => (
<option key={item} value={item}>
{item}
<option value={item.name} key={item.name}>
{item.name}
</option>
))}
</select>
Expand Down
5 changes: 5 additions & 0 deletions src/components/Dropdown/selectors/selectors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { RootState } from '@/app/providers/StoreProvider/config/store'

export const selectFilterProducts = (state: RootState) => state.categoryFilters.filterProducts

export const selectFilterQuantity = (state: RootState) => state.categoryFilters.productQuantityFilter
26 changes: 26 additions & 0 deletions src/components/Dropdown/slice/filtersSlice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createSlice } from '@reduxjs/toolkit'

import type { ICategoryFiltersSchema } from '@/components/Dropdown/types/types'
import { SORT_NAMES, SORT_VALUES } from '@/shared/constants/constants'

const initialState: ICategoryFiltersSchema = {
filterProducts: { name: SORT_NAMES.NAMES_A_YA, value: SORT_VALUES.NAMES_A_YA },
productQuantityFilter: { name: '15', value: '15' }
}

const categoryFiltersSlice = createSlice({
name: 'categoryFilters',
initialState,
reducers: {
setFilterProducts(state, action) {
state.filterProducts = action.payload
},
setProductQuantityFilter(state, action) {
state.productQuantityFilter = action.payload
}
}
})

export const { reducer: categoryFiltersSliceReducer } = categoryFiltersSlice

export const { setFilterProducts, setProductQuantityFilter } = categoryFiltersSlice.actions
9 changes: 9 additions & 0 deletions src/components/Dropdown/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface ICategoryFiltersSchema {
filterProducts: IFilterSchema
productQuantityFilter: IFilterSchema
}

export type IFilterSchema = {
name: string
value: string
}
9 changes: 7 additions & 2 deletions src/components/PageControls/PageControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ import { ECardView } from '@/shared/model/types/common'

import styles from './PageControls.module.scss'

export type TSortOptions = {
name: string
value?: string
}

type TPageControls = {
cardView: string
handleCardViewChange: (view: ECardView) => void
handleItemsPerPageChange: ChangeEventHandler<HTMLSelectElement>
handleSortChange: ChangeEventHandler<HTMLSelectElement>
itemPerPageOptions: number[]
sortOptions: string[]
itemPerPageOptions: TSortOptions[]
sortOptions: TSortOptions[]
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/features/CategoryItem/CategoryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const CategoryItem: FC<Props> = ({ name, slug, count, id }) => {
return (
<li className={styles['category-list__item']}>
<Link
to={`${Routes.CATEGORIES}/${slug}`}
to={`${Routes.CATEGORIES}/${slug}?id=${id}`}
className={styles['category-list__link']}
onClick={() => {
dispatch(setCategoryId(id))
Expand Down
11 changes: 0 additions & 11 deletions src/mockData/productsPageOptions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
export const SORT_OPTION = [
'Название А-Я',
'Название Я-А',
'Сначала дешевые',
'Сначала дорогие',
'Модель А-Я',
'Модель Я-А'
]

export const ITEMS_PER_PAGE_OPTION = [15, 25, 50, 75, 100]

export const TOTAL_PAGES = 10

import image1 from '@/assets/images/product/1-260x260.webp'
Expand Down
64 changes: 28 additions & 36 deletions src/pages/ProductsPage/ProductsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useLocation } from 'react-router'

import { selectFilterProducts, selectFilterQuantity } from '@/components/Dropdown/selectors/selectors'
import { setFilterProducts, setProductQuantityFilter } from '@/components/Dropdown/slice/filtersSlice'
import { PageControls } from '@/components/PageControls/PageControls'
import { PageDescription } from '@/components/PageDescription/PageDescription'
import { Pagination } from '@/components/Pagination/Pagination'
import WrapperForMainContent from '@/components/WrapperForMainContent/WrapperForMainContent'
import { selectCategoryId } from '@/entities/Category/selectors/categorySelectors'
import { selectCategorySlug } from '@/entities/Category/selectors/categorySelectors'
import { ITEMS_PER_PAGE_OPTION, SORT_OPTION, TOTAL_PAGES } from '@/mockData/productsPageOptions'
import { IObjectWithImage } from '@/pages/ProductPage/model/types/productTypes'
import { TOTAL_PAGES } from '@/mockData/productsPageOptions'
import { getProductsOfCategorySelector } from '@/pages/ProductsPage/selectors/selectors'
import { getProducts } from '@/pages/ProductsPage/services/getProducts'
import { ITEMS_PER_PAGE_OPTION, SORT_OPTION } from '@/shared/constants/constants'
import { useAppDispatch } from '@/shared/libs/hooks/store'
import { ECardView } from '@/shared/model/types/common'
import { CategoryList } from '@/widgets/CategoryList/CategoryList'
import { ProductItem } from '@/widgets/ProductItem/ProductItem'
import { ProductsList } from '@/widgets/ProductsList/ProductsList'

import styles from './ProductsPage.module.scss'

Expand All @@ -29,16 +31,31 @@ export const ProductsPage = () => {
const [cardView, setCardView] = useState<ECardView>(ECardView.GRID)
const [currentPage, setCurrentPage] = useState(1)

const dispatch = useAppDispatch()

const categoriesProducts = useSelector(getProductsOfCategorySelector)
const categorySlug = useSelector(selectCategorySlug)

const location = useLocation()

const categoryId = Number(location.search.replace('?id=', ''))

const selectProductsFilter = useSelector(selectFilterProducts)
const filterProducts = selectProductsFilter ? `&ordering=${selectProductsFilter.value}` : ''

const selectQuantityFilter = useSelector(selectFilterQuantity)
const filterQuantity = selectQuantityFilter ? `&limit=${selectQuantityFilter.value}` : ''

const handleSortChange: React.ChangeEventHandler<HTMLSelectElement> = event => {
// Handle sort change logic here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const selectedOption = event.target.value
const setCategoryFilters = SORT_OPTION.find(item => item.name === selectedOption)
dispatch(setFilterProducts(setCategoryFilters))
}

const handleItemsPerPageChange: React.ChangeEventHandler<HTMLSelectElement> = event => {
// Handle items per page change logic here
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const selectedOption = event.target.value
const setQuantityFilters = ITEMS_PER_PAGE_OPTION.find(item => item.name === selectedOption)
dispatch(setProductQuantityFilter(setQuantityFilters))
}

const handleCardViewChange = (view: ECardView) => {
Expand All @@ -59,14 +76,9 @@ export const ProductsPage = () => {
if (currentPage < TOTAL_PAGES) setCurrentPage(currentPage + 1)
}

const dispatch = useAppDispatch()
const categoriesProducts = useSelector(getProductsOfCategorySelector)
const categoryId = useSelector(selectCategoryId)
const categorySlug = useSelector(selectCategorySlug)

useEffect(() => {
dispatch(getProducts(categoryId))
}, [categoryId, categorySlug])
dispatch(getProducts({ categoryId, filterProducts, filterQuantity }))
}, [categoryId, categorySlug, filterProducts, filterQuantity])

return (
<>
Expand All @@ -86,27 +98,7 @@ export const ProductsPage = () => {
itemPerPageOptions={ITEMS_PER_PAGE_OPTION}
sortOptions={SORT_OPTION}
/>
{categoriesProducts.results.map(item => (
<ProductItem
key={item.id}
layout={cardView}
name={item.name}
price={item.price}
brand={item.brand}
slug={item.slug}
description={item.description}
code={item.code}
images={item.images.map((img: IObjectWithImage, index: number) => {
return {
image: img.image,
index
}
})}
label_hit={item.label_hit}
label_popular={item.label_popular}
quantity={item.quantity}
/>
))}
<ProductsList items={categoriesProducts} cardView={cardView} />
</>
) : (
'В данной категории нет товаров'
Expand Down
16 changes: 13 additions & 3 deletions src/pages/ProductsPage/services/getProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ import { ACTION_GET_PRODUCTS_OF_CATEGORY } from '@/shared/constants/constants'

import { ProductsInfo } from '../types/types'

export const getProducts = createAsyncThunk<ProductsInfo, number, ThunkConfig<ApiError>>(
type Params = {
categoryId: number
filterProducts: string
filterQuantity: string
}
export const getProducts = createAsyncThunk<ProductsInfo, Params, ThunkConfig<ApiError>>(
ACTION_GET_PRODUCTS_OF_CATEGORY,
async (id: number, thunkAPI) => {
async (params, thunkAPI) => {
const { categoryId, filterProducts, filterQuantity } = params
const { rejectWithValue, extra } = thunkAPI
try {
const { data } = await extra.api.get(`api/${ApiRoutes.PRODUCT}${id > 0 ? `?category=${id}` : ''}`)
const { data } = await extra.api.get(
`api/${ApiRoutes.PRODUCT}${
categoryId > 0 ? `?category=${categoryId}` : ''
}${filterProducts}${filterQuantity}`
)
return data
} catch (error) {
return rejectWithValue(apiErrorIdentify(error, ApiErrorTypes.DATA_EMPTY_ERROR))
Expand Down
17 changes: 17 additions & 0 deletions src/pages/VouchersPage/VouchersPage.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.vouchers {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
width: 100%;
box-sizing: border-box;
margin: 0;
padding: 56px 0;
}

.title {
width: 100%;
max-width: 700px;
margin: 0;
padding: 0;
}
20 changes: 20 additions & 0 deletions src/pages/VouchersPage/VouchersPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import WrapperForMainContent from '@/components/WrapperForMainContent/WrapperForMainContent'
import Heading, { HeadingType } from '@/shared/ui/Heading/Heading'
import FormBuyGiftCertificate from '@/widgets/FormBuyGiftCertificate'

import styles from './VouchersPage.module.scss'

const VouchersPage = () => {
return (
<WrapperForMainContent>
<section className={styles.vouchers}>
<Heading type={HeadingType.MAIN} className={styles.title}>
Купить подарочный сертификат
</Heading>
<FormBuyGiftCertificate />
</section>
</WrapperForMainContent>
)
}

export default VouchersPage
40 changes: 38 additions & 2 deletions src/shared/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export const ACTION_GET_CATEGORIES = 'get-categories'
export const REDUCER_SHOP_NEWS = 'shopNews'
export const REDUCER_BLOG_POSTS = 'shopBlogPosts'

export const REDUCER_CATEGORY_BRANCHES = 'getCategoryBranches'
export const REDUCER_CATEGORIES = 'getCategories'
export const REDUCER_CATEGORIES_PRODUCTS = 'shopCategoriesProducts'

//Product page
Expand All @@ -55,5 +57,39 @@ export const NAME_LENGTH_MAX_LIMIT = 30
export const FEEDBACK_LENGTH_MIN_LIMIT = 25
export const FEEDBACK_LENGTH_MAX_LIMIT = 1000

export const REDUCER_CATEGORY_BRANCHES = 'getCategoryBranches'
export const REDUCER_CATEGORIES = 'getCategories'
//Filters for ProductsPage
export enum SORT_NAMES {
NAMES_A_YA = 'Название А-Я',
NAMES_YA_A = 'Название Я-А',
PRICE_TO_MORE = 'Сначала дешевые',
PRICE_TO_LESS = 'Сначала дорогие',
MODEL_A_YA = 'Модель А-Я',
MODEL_YA_A = 'Модель Я-А'
}

export enum SORT_VALUES {
NAMES_A_YA = 'name',
NAMES_YA_A = '-name',
PRICE_TO_MORE = 'price',
PRICE_TO_LESS = '-price',
/* eslint-disable */
MODEL_A_YA = 'name',
MODEL_YA_A = '-name'
}

export const SORT_OPTION = [
{ name: SORT_NAMES.NAMES_A_YA, value: SORT_VALUES.NAMES_A_YA },
{ name: SORT_NAMES.NAMES_YA_A, value: SORT_VALUES.NAMES_YA_A },
{ name: SORT_NAMES.PRICE_TO_MORE, value: SORT_VALUES.PRICE_TO_MORE },
{ name: SORT_NAMES.PRICE_TO_LESS, value: SORT_VALUES.PRICE_TO_LESS },
{ name: SORT_NAMES.MODEL_A_YA, value: SORT_VALUES.MODEL_A_YA },
{ name: SORT_NAMES.MODEL_YA_A, value: SORT_VALUES.MODEL_YA_A }
]

export const ITEMS_PER_PAGE_OPTION = [
{ name: '15', value: '15' },
{ name: '25', value: '25' },
{ name: '50', value: '50' },
{ name: '75', value: '75' },
{ name: '100', value: '100' }
]
Loading

0 comments on commit f436eee

Please sign in to comment.