diff --git a/src/app/providers/StoreProvider/config/StateSchema.ts b/src/app/providers/StoreProvider/config/StateSchema.ts index ddee4ea2..2c1a35a5 100644 --- a/src/app/providers/StoreProvider/config/StateSchema.ts +++ b/src/app/providers/StoreProvider/config/StateSchema.ts @@ -2,6 +2,7 @@ import { CategorySchema } from '@/entities/Category/types/types' import { SearchResultSchema } from '@/features/SearchProduct/types/types' import { LoginSchema } from '@/features/login/model/types/types' import { ApiInstance } from '@/shared/api/api' +import { ShopNewsSchema } from '@/widgets/NewsBlock/model/types/types' import { StoreReviewsSchema } from '@/widgets/ReviewsBlock/model/types/types' export interface StateSchema { @@ -9,6 +10,7 @@ export interface StateSchema { storeReviews: StoreReviewsSchema category: CategorySchema searchResult: SearchResultSchema + shopNews: ShopNewsSchema } export interface ThunkExtraArg { diff --git a/src/app/providers/StoreProvider/config/store.ts b/src/app/providers/StoreProvider/config/store.ts index 927e75ac..52e6e993 100644 --- a/src/app/providers/StoreProvider/config/store.ts +++ b/src/app/providers/StoreProvider/config/store.ts @@ -5,6 +5,7 @@ import { $api } from '@/shared/api/api' import categorySlice from '@/entities/Category/slice/categorySlice' import searchProductSlice from '@/features/SearchProduct/slice/searchProductSlice' import { storeReviewsReducer } from '@/widgets/ReviewsBlock/model/slice/reviewsSlice' +import { shopNewsReducer } from '@/widgets/NewsBlock/model/slice/shopNewsSlice' export type RootState = StateSchema @@ -12,7 +13,8 @@ const rootReducer: ReducersMapObject = { login: loginReducer, category: categorySlice, searchResult: searchProductSlice, - storeReviews: storeReviewsReducer + storeReviews: storeReviewsReducer, + shopNews: shopNewsReducer } export function createReduxStore(initialState: RootState) { diff --git a/src/assets/icons/image-not-found-icon.svg b/src/assets/icons/image-not-found-icon.svg new file mode 100644 index 00000000..91c64e00 --- /dev/null +++ b/src/assets/icons/image-not-found-icon.svg @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/src/entities/NewsCard/NewsCard.module.scss b/src/entities/NewsCard/NewsCard.module.scss index 84722723..8f3ed61f 100644 --- a/src/entities/NewsCard/NewsCard.module.scss +++ b/src/entities/NewsCard/NewsCard.module.scss @@ -1,6 +1,7 @@ @use '@/app/styles/index' as var; .card { + max-width: 340px; min-width: 340px; position: relative; transition: transform 0.3s ease-in-out; @@ -14,12 +15,6 @@ transition: transform 0.3s ease-in-out; } - img { - border-radius: 6px; - transition: transform 0.3s ease-in-out; - scroll-snap-align: start; - } - .heading { font-size: #{'min(max(14px, 1.2vw), 16px)'}; @@ -46,4 +41,12 @@ line-height: 120%; font-weight: 500; } + + .img { + height: 180px; + width: 100%; + border-radius: 6px; + transition: transform 0.3s ease-in-out; + scroll-snap-align: start; + } } \ No newline at end of file diff --git a/src/entities/NewsCard/NewsCard.stories.tsx b/src/entities/NewsCard/NewsCard.stories.tsx index 5b63741a..c4344e62 100644 --- a/src/entities/NewsCard/NewsCard.stories.tsx +++ b/src/entities/NewsCard/NewsCard.stories.tsx @@ -1,6 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react' import NewsCard from './NewsCard' -import Img1 from '@/assets/images/news/img-news-01.png' const meta = { title: 'entities/NewsCard', @@ -16,13 +15,9 @@ type Story = StoryObj export const Default: Story = { args: { - card: { - id: 1, - src: Img1, - alt: 'Покупай и не жди. До -50% на весь электротранспорт!', - title: 'Покупай и не жди. До -50% на весь электротранспорт!', - date: '15 Мая, 2022', - promo: true - } + id: 1, + image: 'http://gealit.ru/media/news/18.png', + title: 'Покупай и не жди. До -50% на весь электротранспорт!', + date: '2022-05-15' } } diff --git a/src/entities/NewsCard/NewsCard.tsx b/src/entities/NewsCard/NewsCard.tsx index 006c6674..a3fa6b9d 100644 --- a/src/entities/NewsCard/NewsCard.tsx +++ b/src/entities/NewsCard/NewsCard.tsx @@ -1,12 +1,14 @@ -import { FC } from 'react' -import { TCard } from '@/models/CardModel' -import { TEXT_PROMO } from '@/shared/constants/constants' +import { FC, useMemo } from 'react' import styles from './NewsCard.module.scss' import Link from '@/shared/ui/Link/Link' import Heading, { HeadingType } from '@/shared/ui/Heading/Heading' +import NoImage from '@/assets/icons/image-not-found-icon.svg' export type Props = { - card: TCard + id: number + image: string + date: string + title: string } /** @@ -14,15 +16,27 @@ export type Props = { * @param {TCard} card - параметры карточки из группы новостей */ -const NewsCard: FC = ({ card }) => { +const NewsCard: FC = ({ image, date, title }) => { + 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 ( - {card.alt} + {image ? ( + {'новость'} + ) : ( + + )} - {card.title} + {title} - {card.date} - {card.promo ? {TEXT_PROMO} : null} + {newDate} + {/* {promo ? {TEXT_PROMO} : null} */} ) } diff --git a/src/shared/api/types.ts b/src/shared/api/types.ts index 72d6cf72..9467dee8 100644 --- a/src/shared/api/types.ts +++ b/src/shared/api/types.ts @@ -3,7 +3,8 @@ export enum ApiRoutes { LOGOUT = 'token/logout', SEARCH = 'search', STORE_REVIEWS = 'store-reviews', - CATEGORIES = 'catalogue/category' + CATEGORIES = 'catalogue/category', + SHOP_NEWS = 'shopnews' } export enum ApiErrorTypes { diff --git a/src/widgets/NewsBlock/model/selectors/selectors.ts b/src/widgets/NewsBlock/model/selectors/selectors.ts new file mode 100644 index 00000000..bcd58e38 --- /dev/null +++ b/src/widgets/NewsBlock/model/selectors/selectors.ts @@ -0,0 +1,5 @@ +import { StateSchema } from '@/app/providers/StoreProvider' + +export const getShopNewsSelector = (state: StateSchema) => { + return state.shopNews.news +} diff --git a/src/widgets/NewsBlock/model/services/getShopNews.ts b/src/widgets/NewsBlock/model/services/getShopNews.ts new file mode 100644 index 00000000..101275d7 --- /dev/null +++ b/src/widgets/NewsBlock/model/services/getShopNews.ts @@ -0,0 +1,22 @@ +import { createAsyncThunk } from '@reduxjs/toolkit' +import { ThunkConfig } from '@/app/providers/StoreProvider/config/StateSchema' +import { ApiError, ApiErrorTypes, ApiRoutes } from '@/shared/api/types' +import { apiErrorIdentify } from '@/shared/api/apiErrorIdentify' +import { ShopNewsData } from '../types/types' + +// export const getStoreReviews = createAsyncThunk>( +export const getShopNews = createAsyncThunk>( + //void1- выходные данные, void2- входные данные , thunkConfig- тип store + 'shop-news', // action type, первый аргумент + async (_, thunkAPI) => { + // второй аргумент- асинхронная функция , кот вызовет dispatch в компоненте + const { rejectWithValue, extra } = thunkAPI + try { + const { data } = await extra.api.get(ApiRoutes.SHOP_NEWS) + + return data.results + } catch (error) { + return rejectWithValue(apiErrorIdentify(error, ApiErrorTypes.DATA_EMPTY_ERROR)) + } + } +) diff --git a/src/widgets/NewsBlock/model/slice/shopNewsSlice.ts b/src/widgets/NewsBlock/model/slice/shopNewsSlice.ts new file mode 100644 index 00000000..02751137 --- /dev/null +++ b/src/widgets/NewsBlock/model/slice/shopNewsSlice.ts @@ -0,0 +1,29 @@ +import { createSlice } from '@reduxjs/toolkit' +import { getShopNews } from '../services/getShopNews' +import { ShopNewsSchema } from '../types/types' + +const initialState: ShopNewsSchema = { + isLoading: false, + news: [] +} + +export const shopNewsSlice = createSlice({ + name: 'shopNews', + initialState, + reducers: {}, + extraReducers: builder => { + builder + .addCase(getShopNews.pending, state => { + state.isLoading = true + }) + .addCase(getShopNews.fulfilled, (state, { payload }) => { + state.isLoading = false + state.news = payload + }) + .addCase(getShopNews.rejected, state => { + state.isLoading = false + }) + } +}) + +export const { actions: shopNewsActions, reducer: shopNewsReducer } = shopNewsSlice diff --git a/src/widgets/NewsBlock/model/types/types.ts b/src/widgets/NewsBlock/model/types/types.ts new file mode 100644 index 00000000..3fa6bf28 --- /dev/null +++ b/src/widgets/NewsBlock/model/types/types.ts @@ -0,0 +1,22 @@ +export interface GetShopNewsResponse { + count: number + previous: string + next: string + results: ShopNewsData[] +} + +export interface ShopNewsData { + id: number + title: string + text: string + image: string + pub_date: string + slug: string + meta_title: string + meta_description: string +} + +export interface ShopNewsSchema { + isLoading: boolean + news: ShopNewsData[] +} diff --git a/src/widgets/NewsBlock/ui/NewsBlock.tsx b/src/widgets/NewsBlock/ui/NewsBlock.tsx index 521490da..b588231c 100644 --- a/src/widgets/NewsBlock/ui/NewsBlock.tsx +++ b/src/widgets/NewsBlock/ui/NewsBlock.tsx @@ -1,16 +1,26 @@ -import { FC } from 'react' +import { FC, useEffect } from 'react' import IconLink from '@/assets/icons/IconLink' import Heading, { HeadingType } from '@/shared/ui/Heading/Heading' import Link from '@/shared/ui/Link/Link' import styles from './NewsBlock.module.scss' import NewsCard from '@/entities/NewsCard/NewsCard' -import { newsData } from '@/mockData/newsData' import Scroll from '@/shared/ui/Scroll/Scroll' +import { useAppDispatch } from '@/shared/libs/hooks/store' +import { getShopNewsSelector } from '../model/selectors/selectors' +import { useSelector } from 'react-redux' +import { getShopNews } from '../model/services/getShopNews' /** * Блок группы новостей */ const NewsBlock: FC = () => { + const dispatch = useAppDispatch() + const news = useSelector(getShopNewsSelector) + + useEffect(() => { + dispatch(getShopNews()) + }, []) + return (
@@ -21,8 +31,8 @@ const NewsBlock: FC = () => {
- {newsData.map(item => ( - + {news.map(item => ( + ))}