mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: render courses by selected categoru
This commit is contained in:
parent
c0e324890f
commit
962e43c81c
@ -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'>
|
||||||
|
|||||||
@ -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)'
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user