Skip to content

Commit

Permalink
feat: add articles details starting components and main types
Browse files Browse the repository at this point in the history
  • Loading branch information
TomatoVan committed Dec 20, 2023
1 parent f885103 commit a682dd1
Show file tree
Hide file tree
Showing 20 changed files with 383 additions and 13 deletions.
114 changes: 103 additions & 11 deletions json-server/db.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/app/providers/StoreProvider/config/StateSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { ProfileSchema } from 'entities/Profile';
import { AxiosInstance } from 'axios';
import { To } from '@remix-run/router';
import { NavigateOptions } from 'react-router/dist/lib/context';
import { ArticleDetailsSchema } from 'entities/Article';

export interface StateSchema {
counter: CounterSchema;
user: UserSchema;
// async reducers
loginForm?: LoginSchema;
profile?: ProfileSchema
profile?: ProfileSchema;
articleDetails?: ArticleDetailsSchema;
}

export type StateSchemaKey = keyof StateSchema;
Expand Down
4 changes: 4 additions & 0 deletions src/entities/Article/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { ArticleDetails } from './ui/ArticleDetails/ArticleDetails';

export type { Article } from './model/types/article';
export type { ArticleDetailsSchema } from './model/types/articleDetailsSchema';
38 changes: 38 additions & 0 deletions src/entities/Article/model/slice/articleDetailsSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { ArticleDetailsSchema } from '../types/articleDetailsSchema';
import { fetchArticleById } from '../../services/fetchArticleById/fetchArticleById';
import { Article } from '../types/article';

const initialState: ArticleDetailsSchema = {
isLoading: false,
error: undefined,
data: undefined,
};

export const articleDetailsSlice = createSlice({
name: 'articleDetails',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchArticleById.pending, (state) => {
state.error = undefined;
state.isLoading = true;
})
.addCase(
fetchArticleById.fulfilled,
(state, action: PayloadAction<Article>) => {
state.isLoading = false;
state.data = action.payload;
},
)
.addCase(fetchArticleById.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload;
});
},
});

// Action creators are generated for each case reducer function
export const { actions: articleDetailsActions } = articleDetailsSlice;
export const { reducer: articleDetailsReducer } = articleDetailsSlice;
46 changes: 46 additions & 0 deletions src/entities/Article/model/types/article.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export enum ArticleBlockType {
CODE = 'CODE',
IMAGE = 'IMAGE',
TEXT = 'TEXT',
}

export interface ArticleBlockBase {
id: string;
type: ArticleBlockType;
}

export interface ArticleCodeBlock extends ArticleBlockBase {
type: ArticleBlockType.CODE;
code: string;
}

export interface ArticleImageBlock extends ArticleBlockBase {
type: ArticleBlockType.IMAGE;
src: string;
title: string;
}

export interface ArticleTextBlock extends ArticleBlockBase {
type: ArticleBlockType.TEXT;
paragraphs: string[];
title?: string;
}

export type ArticleBlock = ArticleCodeBlock | ArticleImageBlock | ArticleTextBlock;

export enum ArticleType {
IT = 'IT',
SCIENCE = 'SCIENCE',
ECONOMICS = 'ECONOMICS'
}

export interface Article {
id: string;
title: string;
subtitle: string;
img: string;
views: number;
createdAt: string;
type: ArticleType[];
blocks: ArticleBlock[];
}
7 changes: 7 additions & 0 deletions src/entities/Article/model/types/articleDetailsSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Article } from './article';

export interface ArticleDetailsSchema {
isLoading: boolean;
error?: string;
data?: Article
}
22 changes: 22 additions & 0 deletions src/entities/Article/services/fetchArticleById/fetchArticleById.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import { ThunkConfig } from 'app/providers/StoreProvider';
import { Article } from '../../model/types/article';

export const fetchArticleById = createAsyncThunk<
Article,
string,
ThunkConfig<string>
>('articleDetails/fetchArticleById', async (articleId, thunkAPI) => {
const { extra, rejectWithValue } = thunkAPI;

try {
const response = await extra.api.get<Article>(`/articles/${articleId}`);

if (!response.data) {
throw new Error();
}
return response.data;
} catch (error) {
return rejectWithValue('error');
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ArticleImageBlockComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { ArticleImageBlockComponent } from './ArticleImageBlockComponent';

export default {
title: 'shared/ArticleImageBlockComponent',
component: ArticleImageBlockComponent,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof ArticleImageBlockComponent>;

const Template: ComponentStory<typeof ArticleImageBlockComponent> = (args) => <ArticleImageBlockComponent {...args} />;

export const Normal = Template.bind({});
Normal.args = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import cls from './ArticleImageBlockComponent.module.scss';

interface ArticleImageBlockComponentProps {
className?: string;
}

export const ArticleImageBlockComponent = ({ className }: ArticleImageBlockComponentProps) => {
const { t } = useTranslation();
return (
<div className={classNames(cls.ArticleImageBlockComponent, {}, [className])} />
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ArticleDetails {

}
17 changes: 17 additions & 0 deletions src/entities/Article/ui/ArticleDetails/ArticleDetails.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { ArticleDetails } from './ArticleDetails';

export default {
title: 'shared/ArticleDetails',
component: ArticleDetails,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof ArticleDetails>;

const Template: ComponentStory<typeof ArticleDetails> = (args) => <ArticleDetails {...args} />;

export const Normal = Template.bind({});
Normal.args = {};
32 changes: 32 additions & 0 deletions src/entities/Article/ui/ArticleDetails/ArticleDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import { DynamicModuleLoader, ReducersList } from 'shared/lib/components/DynamicModuleLoared/DynamicModuleLoared';
import { memo, useEffect } from 'react';
import { useAppDispatch } from 'shared/lib/hook/useAppDispatch/useAppDispatch';
import { fetchArticleById } from '../../services/fetchArticleById/fetchArticleById';
import { articleDetailsReducer } from '../../model/slice/articleDetailsSlice';
import cls from './ArticleDetails.module.scss';

interface ArticleDetailsProps {
className?: string;
}

const reducers: ReducersList = {
articleDetails: articleDetailsReducer,
};

export const ArticleDetails = memo(({ className }: ArticleDetailsProps) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(fetchArticleById('1'));
}, [dispatch]);

return (
<DynamicModuleLoader reducers={reducers} removeAfterUnmount>
<div className={classNames(cls.ArticleDetails, {}, [className])}>
ARTICLE DETAILS
</div>
</DynamicModuleLoader>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ArticleImageBlockComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { ArticleImageBlockComponent } from './ArticleImageBlockComponent';

export default {
title: 'shared/ArticleImageBlockComponent',
component: ArticleImageBlockComponent,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof ArticleImageBlockComponent>;

const Template: ComponentStory<typeof ArticleImageBlockComponent> = (args) => <ArticleImageBlockComponent {...args} />;

export const Normal = Template.bind({});
Normal.args = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import cls from './ArticleImageBlockComponent.module.scss';

interface ArticleImageBlockComponentProps {
className?: string;
}

export const ArticleImageBlockComponent = ({ className }: ArticleImageBlockComponentProps) => {
const { t } = useTranslation();
return (
<div className={classNames(cls.ArticleImageBlockComponent, {}, [className])} />
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.ArticleTextBlockComponent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { ArticleTextBlockComponent } from './ArticleTextBlockComponent';

export default {
title: 'shared/ArticleTextBlockComponent',
component: ArticleTextBlockComponent,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof ArticleTextBlockComponent>;

const Template: ComponentStory<typeof ArticleTextBlockComponent> = (args) => <ArticleTextBlockComponent {...args} />;

export const Normal = Template.bind({});
Normal.args = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { classNames } from 'shared/lib/classNames/classNames';
import { useTranslation } from 'react-i18next';
import cls from './ArticleTextBlockComponent.module.scss';

interface ArticleTextBlockComponentProps {
className?: string;
}

export const ArticleTextBlockComponent = ({ className }: ArticleTextBlockComponentProps) => {
const { t } = useTranslation();
return (
<div className={classNames(cls.ArticleTextBlockComponent, {}, [className])} />
);
};
7 changes: 6 additions & 1 deletion src/pages/ArticlesDetailsPage/ui/ArticlesDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { useTranslation } from 'react-i18next';
import { memo } from 'react';
import { ArticleDetails } from '../../../entities/Article';

const ArticlesDetailsPage = (props: any) => {
const { t } = useTranslation();
return <div>{t('articles_details page')}</div>;
return (
<div>
<ArticleDetails />
</div>
);
};
export default memo(ArticlesDetailsPage);

0 comments on commit a682dd1

Please sign in to comment.