feat: render courses by selected categoru

This commit is contained in:
Sergey Yarkov 2021-10-12 21:18:11 +03:00
parent c0e324890f
commit 962e43c81c
7 changed files with 73 additions and 33 deletions

View File

@ -3,12 +3,16 @@ import { Box, Flex, Tag, TagLabel } from '@chakra-ui/react';
import { CoursesPageContext } from 'contexts'; import { CoursesPageContext } from 'contexts';
import CategoryItem from './CategoryItem'; import CategoryItem from './CategoryItem';
import { ICategory } from 'interfaces'; import { ICategory } from 'interfaces';
import CategoryListLoading from './CategoryListLoading';
import { useRootStore } from 'hooks/useRootStore';
type CategoryListPropsType = { type CategoryListPropsType = {
categories: ICategory[]; categories: ICategory[] | null;
isLoading: boolean;
}; };
const CategoryList: React.FC<CategoryListPropsType> = ({ categories }) => { const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }) => {
const { courseStore } = useRootStore();
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext); const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
/** /**
@ -16,7 +20,12 @@ const CategoryList: React.FC<CategoryListPropsType> = ({ categories }) => {
*/ */
useEffect(() => { useEffect(() => {
console.log(`[LOG:] Selected category: ${selectedCategory?.title}`); console.log(`[LOG:] Selected category: ${selectedCategory?.title}`);
}, [selectedCategory]); courseStore.loadCourses({ category_id: selectedCategory?.id });
}, [courseStore, selectedCategory]);
if (isLoading || categories === null) {
return <CategoryListLoading />;
}
return ( return (
<Box mb='5'> <Box mb='5'>

View File

@ -10,12 +10,18 @@ import { ICourse } from 'interfaces';
* Components * Components
*/ */
import CourseItem from './CourseItem'; import CourseItem from './CourseItem';
import CourseListLoading from './CourseListLoading';
type CourseListPropsType = { type CourseListPropsType = {
courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[]; courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null;
isLoading: boolean;
}; };
const CourseList: React.FC<CourseListPropsType> = ({ courses }) => { const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
if (isLoading || courses === null) {
return <CourseListLoading />;
}
return ( return (
<Grid <Grid
templateColumns='repeat(3, 1fr)' templateColumns='repeat(3, 1fr)'

View File

@ -1,13 +1,11 @@
import React from 'react'; import React from 'react';
import { Grid, Skeleton } from '@chakra-ui/react'; import { Box, Text } from '@chakra-ui/react';
const CourseListLoading: React.FC = () => { const CourseListLoading: React.FC = () => {
return ( return (
<Grid templateColumns='repeat(auto-fit, minmax(300px, 1fr))' gap='6'> <Box textAlign='center' mt='10' userSelect='none'>
<Skeleton height='200px' borderRadius='lg' /> <Text color='gray.500'>Wait a second...</Text>
<Skeleton height='200px' borderRadius='lg' /> </Box>
<Skeleton height='200px' borderRadius='lg' />
</Grid>
); );
}; };

View File

@ -17,8 +17,7 @@ import CategoryList from './components/CategoryList';
import { useRootStore } from 'hooks/useRootStore'; import { useRootStore } from 'hooks/useRootStore';
import { CoursesPageContextProvider } from 'providers'; import { CoursesPageContextProvider } from 'providers';
import { useErrorHandler } from 'react-error-boundary'; import { useErrorHandler } from 'react-error-boundary';
import CategoryListLoading from './components/CategoryList/CategoryListLoading'; import LoadingPage from 'components/LoadingPage';
import CourseListLoading from './components/CourseList/CourseListLoading';
/** /**
* Courses page * Courses page
@ -34,14 +33,11 @@ const CoursesPage: React.FC<IPageProps> = ({ title }) => {
useEffect(() => { useEffect(() => {
if (me !== null) { if (me !== null) {
categoryStore.loadCategories().catch(error => handleError(error)); categoryStore.loadCategories().catch(error => handleError(error));
courseStore.loadCourses().catch(error => handleError(error)); // courseStore.loadCourses().catch(error => handleError(error));
} }
}, [me, categoryStore, courseStore, handleError]); }, [me, categoryStore, handleError]);
/** if (me === null) return <LoadingPage />;
* TODO: Loading page for courses
*/
if (me === null) return null;
return ( return (
<CoursesPageContextProvider> <CoursesPageContextProvider>
@ -68,15 +64,8 @@ const CoursesPage: React.FC<IPageProps> = ({ title }) => {
</Tabs> </Tabs>
)} )}
<Box mt='5'> <Box mt='5'>
{/* Category List Rendering */} <CategoryList categories={categoryStore.categories} isLoading={categoryStore.isLoading} />
{categoryStore.categories !== null ? ( <CourseList courses={courseStore.courses} isLoading={courseStore.isLoading} />
<CategoryList categories={categoryStore.categories} />
) : (
<CategoryListLoading />
)}
{/* Course List Rendering */}
{courseStore.courses !== null ? <CourseList courses={courseStore.courses} /> : <CourseListLoading />}
</Box> </Box>
</Box> </Box>
</Box> </Box>

View File

@ -1,6 +1,19 @@
import { AxiosInstance } from 'axios'; import { AxiosInstance } from 'axios';
import { CourseStatusEnum } from 'enums';
import { IApiRespose, ICourse } from 'interfaces'; import { IApiRespose, ICourse } from 'interfaces';
export type FetchCoursesParams = {
/**
* Get courses by status
*/
status?: CourseStatusEnum;
/**
* Get courses with category by id
*/
category_id?: string;
};
export class CourseService { export class CourseService {
public api: AxiosInstance; public api: AxiosInstance;
@ -13,8 +26,15 @@ export class CourseService {
* *
* @returns Array of courses * @returns Array of courses
*/ */
public async fetchAll(): Promise<IApiRespose<Omit<ICourse, 'teacher' | 'students' | 'lessons'>[]>> { public async fetchAll(
const result = await this.api.get('/v1/courses'); params?: FetchCoursesParams
): Promise<IApiRespose<Omit<ICourse, 'teacher' | 'students' | 'lessons'>[]>> {
const result = await this.api.get('/v1/courses', {
params: {
status: params?.status,
category_id: params?.category_id,
},
});
return result.data; return result.data;
} }
} }

View File

@ -11,14 +11,21 @@ export default class CategoryStore {
public categories: ICategory[] | null = null; public categories: ICategory[] | null = null;
public isLoading: boolean = false;
constructor(root: RootStore, api: AxiosInstance) { constructor(root: RootStore, api: AxiosInstance) {
this.root = root; this.root = root;
this.categoryService = new CategoryService(api); this.categoryService = new CategoryService(api);
makeAutoObservable(this); makeAutoObservable(this);
} }
public setLoading(loading: boolean) {
this.isLoading = loading;
}
public async loadCategories() { public async loadCategories() {
try { try {
this.setLoading(true);
const result = await this.categoryService.fetchAll(); const result = await this.categoryService.fetchAll();
console.log(`[${this.constructor.name}]: ${result.message}`, result); console.log(`[${this.constructor.name}]: ${result.message}`, result);
@ -29,6 +36,8 @@ export default class CategoryStore {
return result; return result;
} catch (error: any) { } catch (error: any) {
throw error; throw error;
} finally {
this.setLoading(false);
} }
} }
} }

View File

@ -1,7 +1,7 @@
import { AxiosInstance } from 'axios'; import { AxiosInstance } from 'axios';
import { ICourse } from 'interfaces'; import { ICourse } from 'interfaces';
import { makeAutoObservable, runInAction } from 'mobx'; import { makeAutoObservable, runInAction } from 'mobx';
import { CourseService } from 'services/CourseService'; import { CourseService, FetchCoursesParams } from 'services/CourseService';
import RootStore from './RootStore'; import RootStore from './RootStore';
export default class CourseStore { export default class CourseStore {
@ -11,15 +11,22 @@ export default class CourseStore {
public courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null = null; public courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null = null;
public isLoading: boolean = false;
constructor(root: RootStore, api: AxiosInstance) { constructor(root: RootStore, api: AxiosInstance) {
this.root = root; this.root = root;
this.courseService = new CourseService(api); this.courseService = new CourseService(api);
makeAutoObservable(this); makeAutoObservable(this);
} }
public async loadCourses() { public setLoading(loading: boolean) {
this.isLoading = loading;
}
public async loadCourses(params?: FetchCoursesParams) {
try { try {
const result = await this.courseService.fetchAll(); this.setLoading(true);
const result = await this.courseService.fetchAll(params);
console.log(`[${this.constructor.name}]: ${result.message}`, result); console.log(`[${this.constructor.name}]: ${result.message}`, result);
runInAction(() => { runInAction(() => {
@ -29,6 +36,8 @@ export default class CourseStore {
return result; return result;
} catch (error: any) { } catch (error: any) {
throw error; throw error;
} finally {
this.setLoading(false);
} }
} }
} }