Skip to content

Commit

Permalink
Merge pull request #189 from Studio-Yandex-Practicum/api-146-insert-t…
Browse files Browse the repository at this point in the history
…o-ReviewsBlock

#146-api-inserted-to-ReviewsBlock
  • Loading branch information
JuliaAvramenko authored Jan 25, 2024
2 parents 1415e68 + 8be7f0d commit 27ef5ba
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 31 deletions.
2 changes: 2 additions & 0 deletions src/app/providers/SroreProvider/config/StateSchema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { CategorySchema } from '@/entities/Category/types/types'
import { LoginSchema } from '@/features/login/model/types/types'
import { ApiInstance } from '@/shared/api/api'
import { StoreReviewsSchema } from '@/widgets/ReviewsBlock/model/types/types'

export interface StateSchema {
login: LoginSchema
storeReviews: StoreReviewsSchema
category: CategorySchema
}

Expand Down
2 changes: 2 additions & 0 deletions src/app/providers/SroreProvider/config/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { loginReducer } from '@/features/login/model/slice/loginSlice'
import { StateSchema, ThunkExtraArg } from './StateSchema'
import { $api } from '@/shared/api/api'
import categorySlice from '@/entities/Category/slice/categorySlice'
import { storeReviewsReducer } from '@/widgets/ReviewsBlock/model/slice/reviewsSlice'

export type RootState = StateSchema

const rootReducer: ReducersMapObject<StateSchema> = {
login: loginReducer,
storeReviews: storeReviewsReducer,
category: categorySlice
}

Expand Down
12 changes: 5 additions & 7 deletions src/entities/CardReview/ui/CardReview/CardReview.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ type Story = StoryObj<typeof meta>

export const Default: Story = {
args: {
review: {
id: 1,
name: 'Рейтинг нашего магазина',
score: '4.3',
text: 'Мы очень ним гордимся, это результат упорного труда в течении длительного времени и сейчас наша команда ежедневно работает над улучшением сервиса.',
date: ''
}
pk: 1,
name: 'Рейтинг нашего магазина',
score: 4.3,
text: 'Мы очень ним гордимся, это результат упорного труда в течении длительного времени и сейчас наша команда ежедневно работает над улучшением сервиса.',
date: '2024-01-22T09:42:35.242681+03:00'
}
}
46 changes: 33 additions & 13 deletions src/entities/CardReview/ui/CardReview/CardReview.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
import { FC, useMemo } from 'react'
import { TReview } from '@/models/ReviewModel'
import IconStar from '@/assets/icons/IconStar'
import Paragraph from '@/shared/ui/Paragraph/Paragraph'
import Heading, { HeadingType } from '@/shared/ui/Heading/Heading'
import Link from '@/shared/ui/Link/Link'
import styles from './cardReview.module.scss'
import Subheading from '@/shared/ui/Subheading/Subheading'

export type Props = {
review: TReview
pk: number
text: string
date: string
score: number
name: string
}

const CardReview: FC<Props> = props => {
const { review } = props
/**
* Отзыв
* @param {number} pk - id отзыва
* @param {string} text - текст отзыва
* @param {string} date - дата отзыва
* @param {number} score - очко рейтинга отзыва
* @param {string} name - имя оставившего отзыв
*/

const CardReview: FC<Props> = ({ pk, text, date, score, name }) => {
const initials = useMemo(() => {
return review.name.slice(0, 1)
return name.slice(0, 1)
}, [0, 1])
const linkTextStyle = styles.link__text

const newDate = useMemo(() => {
const _parsedDate = new Date(date)
const year = _parsedDate.getFullYear()
const formatter = new Intl.DateTimeFormat('ru', { month: 'long', day: 'numeric' }).format(_parsedDate)

return `${formatter}, ${year}`
}, [date])

return (
<article className={styles.review}>
{review.id === 0 ? (
{pk === 0 ? (
<>
<Heading type={HeadingType.SMALL} className={styles.title}>
{review.name} - {review.score}
{name} - {score}
<IconStar></IconStar>
</Heading>

<p>{review.text}</p>
<p>{text}</p>
<p className={styles.subtitle}>
Вы можете{' '}
<Link to="#" className={styles.link__text}>
Expand All @@ -44,16 +64,16 @@ const CardReview: FC<Props> = props => {
<div className={styles.review__header}>
<div className={styles.review__initials}>{initials}</div>
<div>
<Heading type={HeadingType.SMALL}>{review.name}</Heading>
<Heading type={HeadingType.SMALL}>{name}</Heading>
<span>
Оценил(а) магазин на {review.score}
<IconStar></IconStar>
Оценил(а) магазин на {score}
<IconStar />
</span>
</div>
</div>
<div className={styles.review__data}>
<Paragraph>{review.text}</Paragraph>
<span>{review.date}</span>
<Paragraph>{text}</Paragraph>
<Subheading>{newDate}</Subheading>
</div>
<Link to="#" className={linkTextStyle}>
Читать полный отзыв
Expand Down
4 changes: 1 addition & 3 deletions src/pages/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import BrandsBlock from '@/widgets/BrandBlock/BrandBlock'
import { storiesData } from '@/mockData/storiesData'
import { blogData } from '@/mockData/blogData'
import { newsData } from '@/mockData/newsData'
import { reviewsData } from '@/mockData/reviews.Data'
import {
TEXT_STORIES,
TEXT_BLOG,
Expand All @@ -20,7 +19,6 @@ import CategoryGrid from '@/widgets/CategoryGrid/CategoryGrid'
import ReviewsBlock from '@/widgets/ReviewsBlock/ui/ReviewsBlock/ReviewsBlock'
import Advantages from '@/widgets/Advantages/ui/Advantages/Advantages'


const MainPage = () => {
return (
<>
Expand All @@ -30,7 +28,7 @@ const MainPage = () => {
<ContainerCards title={TEXT_BLOG} linkText={LINK_SHOW_ALL} cards={blogData} />
<ContainerCards title={TEXT_NEWS} linkText={LINK_NEWS_ALL} cards={newsData} />
<CategoryGrid />
<ReviewsBlock title={TEXT_CUSTOMERS_ABOUT_US} linkText={LINK_REVIEWS_ALL} reviews={reviewsData} />
<ReviewsBlock title={TEXT_CUSTOMERS_ABOUT_US} linkText={LINK_REVIEWS_ALL} />
<BrandsBlock />
<Advantages />
<ArticleBlock />
Expand Down
1 change: 1 addition & 0 deletions src/shared/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export enum ApiRoutes {
LOGIN = 'token/login',
LOGOUT = 'token/logout',
STORE_REVIEWS = 'store-reviews',
CATEGORIES = 'catalogue/category'
}

Expand Down
5 changes: 5 additions & 0 deletions src/widgets/ReviewsBlock/model/selectors/selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { StateSchema } from '@/app/providers/SroreProvider'

export const getStoreReviewsSelector = (state: StateSchema) => {
return state.storeReviews.reviews
}
21 changes: 21 additions & 0 deletions src/widgets/ReviewsBlock/model/services/getStoreReviews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createAsyncThunk } from '@reduxjs/toolkit'
import { ThunkConfig } from '@/app/providers/SroreProvider/config/StateSchema'
import { ApiError, ApiErrorTypes, ApiRoutes } from '@/shared/api/types'
import { apiErrorIdentify } from '@/shared/api/apiErrorIdentify'
import { StoreReviewData } from '../types/types'

export const getStoreReviews = createAsyncThunk<StoreReviewData[], void, ThunkConfig<ApiError>>(
//void1- выходные данные, void2- входные данные , thunkConfig- тип store
'store-reviews', // action type, первый аргумент
async (_, thunkAPI) => {
// второй аргумент- асинхронная функция , кот вызовет dispatch в компоненте
const { rejectWithValue, extra } = thunkAPI
try {
const { data } = await extra.api.get(ApiRoutes.STORE_REVIEWS)

return data.results
} catch (error) {
return rejectWithValue(apiErrorIdentify(error, ApiErrorTypes.DATA_EMPTY_ERROR))
}
}
)
33 changes: 33 additions & 0 deletions src/widgets/ReviewsBlock/model/slice/reviewsSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createSlice } from '@reduxjs/toolkit'
import { StoreReviewsSchema } from '../types/types'
import { getStoreReviews } from '../services/getStoreReviews'

const initialState: StoreReviewsSchema = {
isLoading: false,
reviews: []
}

export const reviewsSlice = createSlice({
name: 'storeReviews',
initialState,
reducers: {
// для обычных actions, не thunk
},
extraReducers: builder => {
// для thunk actions
builder
.addCase(getStoreReviews.pending, state => {
state.isLoading = true
})
.addCase(getStoreReviews.fulfilled, (state, { payload }) => {
state.isLoading = false
state.reviews = payload
//state.reviews = payload.test2 // StoreeviewsData с сервера переклыдвается в StoreReviewsSchema (наше Redux хранилище)
})
.addCase(getStoreReviews.rejected, state => {
state.isLoading = false
})
}
})

export const { actions: reviewsActions, reducer: storeReviewsReducer } = reviewsSlice
29 changes: 29 additions & 0 deletions src/widgets/ReviewsBlock/model/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export interface GetStoreReviewsResponse {
count: number
previous: string
next: string
results: StoreReviewData[]
}

export interface StoreReviewData {
pk: number
text: string
pub_date: string
author_name: string
author_email: string
average_score: number
delivery_speed_score: number
quality_score: number
price_score: number
replay: StoreReviewReplay
}
export interface StoreReviewReplay {
text: string
pub_date: string
name: string
}

export interface StoreReviewsSchema {
isLoading: boolean
reviews: StoreReviewData[]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react'
import ReviewsBlock from './ReviewsBlock'
import { reviewsData } from '@/mockData/reviews.Data'
import { LINK_REVIEWS_ALL, TEXT_CUSTOMERS_ABOUT_US } from '@/shared/constants/constants'

const meta = {
Expand All @@ -18,7 +17,6 @@ type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
title: TEXT_CUSTOMERS_ABOUT_US,
reviews: reviewsData,
linkText: LINK_REVIEWS_ALL
}
}
27 changes: 21 additions & 6 deletions src/widgets/ReviewsBlock/ui/ReviewsBlock/ReviewsBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
import { type FC } from 'react'
import { TReview } from '@/models/ReviewModel'
import { getStoreReviewsSelector } from '../../model/selectors/selectors'
import { useSelector } from 'react-redux'
import { useAppDispatch } from '@/shared/libs/hooks/store'
import { useEffect, type FC } from 'react'
import IconHand from '@/assets/images/img-hand.png.png'
import IconLink from '@/assets/icons/IconLink'
import Heading, { HeadingType } from '@/shared/ui/Heading/Heading'
import Link from '@/shared/ui/Link/Link'
import styles from './reviewsBlock.module.scss'
import CardReview from '@/entities/CardReview/ui/CardReview/CardReview'
import { getStoreReviews } from '../../model/services/getStoreReviews'

export type Props = {
title: string
linkText?: string
linkPath?: string
reviews: TReview[]
}

/**
* Контейнер для отзывов, scrollbar
* @param {string} title - загаловок контейнера
* @param {string} linkText - загаловок ссылки
* @param {string} linkPath - адрес ссылки
* @param {array} reviews - массив отзывов
*/
const ReviewsBlock: FC<Props> = props => {
const { title, linkText = '', linkPath = '', reviews } = props
const { title, linkText = '', linkPath = '' } = props
const linkTextStyle = styles.link

const dispatch = useAppDispatch()
const reviews = useSelector(getStoreReviewsSelector)

useEffect(() => {
dispatch(getStoreReviews())
}, [])

return (
<section className={styles.wrapper}>
<article className={styles.header}>
Expand All @@ -40,7 +48,14 @@ const ReviewsBlock: FC<Props> = props => {
</article>
<ul>
{reviews.map(item => (
<CardReview key={item.id} review={item} />
<CardReview
key={item.pk}
pk={item.pk}
text={item.text}
date={item.pub_date}
score={item.average_score}
name={item.author_name}
/>
))}
</ul>
</section>
Expand Down

0 comments on commit 27ef5ba

Please sign in to comment.