refactor: render list

This commit is contained in:
Sergey Yarkov 2021-11-02 23:51:29 +03:00
parent c92163accb
commit 5f7c27b060
14 changed files with 108 additions and 82 deletions

View File

@ -0,0 +1,24 @@
import React from 'react';
import { IconButton } from '@chakra-ui/react';
import { ChevronLeftIcon } from '@chakra-ui/icons';
import { useHistory } from 'react-router';
type PrevPageButtonProps = {
prevPage: string;
};
const PrevPageButton: React.FC<PrevPageButtonProps> = ({ prevPage }) => {
const history = useHistory();
return (
<IconButton
aria-label='Back'
borderRadius='full'
icon={<ChevronLeftIcon />}
onClick={() => history.push(prevPage)}
mr='5'
/>
);
};
export default PrevPageButton;

View File

@ -0,0 +1,3 @@
import PrevPageButton from './PrevPageButton';
export default PrevPageButton;

View File

@ -16,11 +16,11 @@ import { CoursesPageContext } from '@educt/contexts';
*/
import { useContext } from 'react';
type CategoryItemProps = {
export type CategoryItemPropsType = {
category: ICategory;
};
const CategoryItem: React.FC<CategoryItemProps> = ({ category }) => {
const CategoryItem: React.FC<CategoryItemPropsType> = ({ category }) => {
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
/**

View File

@ -1,15 +1,15 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Box, Flex, Tag, TagLabel } from '@chakra-ui/react';
/**
* Types
*/
import { ICategory } from '@educt/interfaces';
import { CategoryItemPropsType } from './CategoryItem';
/**
* Components
*/
import CategoryItem from './CategoryItem';
import CategoryListLoading from './CategoryListLoading';
/**
@ -25,14 +25,14 @@ import { useRootStore } from '@educt/hooks/useRootStore';
import { useErrorHandler } from 'react-error-boundary';
type CategoryListPropsType = {
categories: ICategory[] | null;
isLoading: boolean;
render: React.FC<CategoryItemPropsType>;
};
const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }) => {
const CategoryList: React.FC<CategoryListPropsType> = ({ render: Item }) => {
const { categoryStore } = useRootStore();
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
const handleError = useErrorHandler();
const { categories, isLoading } = categoryStore;
/**
* Fetch handler
@ -41,9 +41,10 @@ const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }
categoryStore.loadCategories().catch(error => handleError(error));
}, [categoryStore, handleError]);
if (isLoading || categories === null) {
return <CategoryListLoading />;
}
/**
* Loading
*/
if (categories === null || isLoading) return <CategoryListLoading />;
return (
<Box mb='5'>
@ -58,13 +59,13 @@ const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }
>
<TagLabel>All categories</TagLabel>
</Tag>
{/* Render items */}
{categories.map((category, i) => (
<CategoryItem key={category.id} category={category} />
{categories.map(category => (
<Item key={category.id} category={category} />
))}
</Flex>
</Box>
);
};
export default CategoryList;
export default observer(CategoryList);

View File

@ -24,7 +24,7 @@ import CourseStatusBadge from '@educt/components/CourseStatusBadge';
import { useColorMode } from '@chakra-ui/color-mode';
import { useRootStore } from '@educt/hooks/useRootStore';
type CourseItemPropsType = {
export type CourseItemPropsType = {
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
onDelete: (course: Pick<ICourse, 'id' | 'title'>) => void;
onSetStatus: (id: string, status: CourseStatusEnum) => Promise<void>;

View File

@ -1,4 +1,5 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Box, Grid, Text } from '@chakra-ui/react';
/**
@ -10,7 +11,7 @@ import { CourseStatusEnum } from '@educt/enums';
/**
* Components
*/
import CourseItem from './CourseItem';
import CourseItem, { CourseItemPropsType } from './CourseItem';
import CourseListLoading from './CourseListLoading';
import DeleteCourseDialog from '../DeleteCourseDialog';
@ -29,11 +30,10 @@ import { useErrorHandler } from 'react-error-boundary';
import { CoursesPageContext } from '@educt/contexts';
type CourseListPropsType = {
courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null;
isLoading: boolean;
render: React.FC<CourseItemPropsType>;
};
const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
const CourseList: React.FC<CourseListPropsType> = ({ render: Item }) => {
const {
userStore: { me },
courseStore,
@ -42,6 +42,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
const handleError = useErrorHandler();
const toast = useToast();
const { courses, isLoading } = courseStore;
/**
* Fetch courses handler
@ -80,27 +81,31 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
}
};
if (isLoading || courses === null) {
return <CourseListLoading />;
}
/**
* Loading
*/
if (courses === null || isLoading) return <CourseListLoading />;
/**
* No courses
*/
if (courses.length === 0)
return (
<>
{deletingCourse && <DeleteCourseDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
{courses.length !== 0 ? (
<Grid templateColumns={{ sm: 'repeat(auto-fill, minmax(400px, 1fr))' }} gap='4'>
{/* Render items */}
{courses.map(course => (
<CourseItem key={course.id} course={course} onDelete={onDeleteCourse} onSetStatus={onSetStatus} />
))}
</Grid>
) : (
<Box textAlign='center' mt='10' userSelect='none'>
<Text color='gray.500'>There are no courses in this category {courseStatus && 'or with this status'}.</Text>
</Box>
)}
</>
);
return (
<Box>
{deletingCourse && <DeleteCourseDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
<Grid templateColumns={{ sm: 'repeat(auto-fill, minmax(400px, 1fr))' }} gap='4'>
{courses.map(course => (
<Item key={course.id} course={course} onDelete={onDeleteCourse} onSetStatus={onSetStatus} />
))}
</Grid>
</Box>
);
};
export default CourseList;
export default observer(CourseList);

View File

@ -1,5 +1,4 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Flex, Box, Heading, Text, Button } from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons';
@ -26,6 +25,8 @@ import { CoursesPageContextProvider } from '@educt/providers';
*/
import { useRootStore } from '@educt/hooks/useRootStore';
import { useHistory } from 'react-router';
import CategoryItem from './components/CategoryList/CategoryItem';
import CourseItem from './components/CourseList/CourseItem';
/**
* Courses page
@ -33,8 +34,6 @@ import { useHistory } from 'react-router';
const CoursesPage: React.FC<IPageProps> = () => {
const {
userStore: { me },
categoryStore,
courseStore,
} = useRootStore();
const history = useHistory();
@ -62,8 +61,8 @@ const CoursesPage: React.FC<IPageProps> = () => {
<Box mt='7'>
{(me.isAdmin || me.isTeacher) && <CourseStatusTabs />}
<Box mt='5'>
<CategoryList categories={categoryStore.categories} isLoading={categoryStore.isLoading} />
<CourseList courses={courseStore.courses} isLoading={courseStore.isLoading} />
<CategoryList render={CategoryItem} />
<CourseList render={CourseItem} />
</Box>
</Box>
</Box>
@ -71,4 +70,4 @@ const CoursesPage: React.FC<IPageProps> = () => {
);
};
export default observer(CoursesPage);
export default CoursesPage;

View File

@ -1,21 +1,29 @@
import React from 'react';
import { Box, Heading, Text } from '@chakra-ui/react';
import { Flex, Box, Heading, Text } from '@chakra-ui/react';
/**
* Types
*/
import { IPageProps } from '@educt/interfaces';
/**
* Components
*/
import PrevPageButton from '@educt/components/PrevPageButton';
import CreateCourseForm from './components';
/**
* Create course page
*/
const CreateCoursePage: React.FC<IPageProps> = ({ title }) => {
const CreateCoursePage: React.FC<IPageProps> = () => {
return (
<Box maxW='1200px'>
<Box>
<Flex alignItems='center'>
<PrevPageButton prevPage='/courses' />
<Heading as='h1'>Create course</Heading>
<Text mt='2'>Fill in the required fields to create a course</Text>
</Flex>
<Text mt='3'>Fill in the required fields to create a course</Text>
</Box>
<Box mt='6'>
<CreateCourseForm />

View File

@ -1,7 +1,6 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Box, Flex, Heading, Text } from '@chakra-ui/layout';
import { IconButton } from '@chakra-ui/button';
import { ChevronLeftIcon } from '@chakra-ui/icons';
/**
* Types
@ -22,9 +21,7 @@ import ConfirmEmailContainer from './containers/ConfirmEmailContainer';
/**
* Hooks
*/
import { useHistory } from 'react-router';
import { useRootStore } from '@educt/hooks/useRootStore';
import { observer } from 'mobx-react';
/**
* Contexts
@ -35,6 +32,7 @@ import { ChangeEmailPageContext } from '@educt/contexts';
* Providers
*/
import { ChangeEmailPageContextProvider } from '@educt/providers';
import PrevPageButton from '@educt/components/PrevPageButton';
/**
* Change email page
@ -43,7 +41,6 @@ const ChangeEmailPage: React.FC<IPageProps> = () => {
const {
userStore: { me },
} = useRootStore();
const history = useHistory();
if (me === null) return <LoadingPage />;
@ -55,13 +52,7 @@ const ChangeEmailPage: React.FC<IPageProps> = () => {
{!isCodeSent ? (
<Box maxW='700px'>
<Flex alignItems='center'>
<IconButton
aria-label='Back'
borderRadius='full'
icon={<ChevronLeftIcon />}
onClick={() => history.push('/profile')}
mr='5'
/>
<PrevPageButton prevPage='/profile' />
<Heading as='h1'>Edit email address</Heading>
</Flex>
<Box mt='3'>

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Box, Heading, Flex, Text, IconButton } from '@chakra-ui/react';
import { ChevronLeftIcon } from '@chakra-ui/icons';
import { observer } from 'mobx-react';
import { Box, Heading, Flex, Text } from '@chakra-ui/react';
/**
* Types
@ -11,14 +11,13 @@ import { IPageProps } from '@educt/interfaces';
* Components
*/
import UpdatePasswordForm from './components/UpdatePasswordForm';
import PrevPageButton from '@educt/components/PrevPageButton';
import LoadingPage from '@educt/components/LoadingPage';
/**
* Hooks
*/
import { useRootStore } from '@educt/hooks/useRootStore';
import { useHistory } from 'react-router';
import LoadingPage from '@educt/components/LoadingPage';
import { observer } from 'mobx-react';
/**
* Change password page
@ -27,20 +26,13 @@ const ChangePasswordPage: React.FC<IPageProps> = () => {
const {
userStore: { me },
} = useRootStore();
const history = useHistory();
if (me === null) return <LoadingPage />;
return (
<Box maxW='700px'>
<Flex alignItems='center'>
<IconButton
aria-label='Profile page'
borderRadius='full'
icon={<ChevronLeftIcon />}
onClick={() => history.push('/profile')}
mr='5'
/>
<PrevPageButton prevPage='/profile' />
<Heading as='h1'>Update password</Heading>
</Flex>
<Box mt='3'>

View File

@ -3,7 +3,7 @@ import { observer } from 'mobx-react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { MdSave } from 'react-icons/md';
import { Text, Input, Button } from '@chakra-ui/react';
import { Box, Heading, Stack } from '@chakra-ui/layout';
import { Box, Stack } from '@chakra-ui/layout';
import { FormControl, FormHelperText } from '@chakra-ui/form-control';
import { yupResolver } from '@hookform/resolvers/yup';
@ -90,7 +90,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
return (
<Box>
<form onSubmit={handleSubmit(onSubmit)}>
<Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='5'>
<Box borderRadius='lg' borderWidth='1px' padding='5'>
<Stack spacing='5px'>
<FormControl id='phone_number'>
<FormHelperText color='gray.500'>Phone number with country code</FormHelperText>

View File

@ -83,7 +83,7 @@ const ProfilePage: React.FC<IPageProps> = () => {
<TabPanels mt='2'>
<TabPanel>
<form>
<Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='5'>
<Box borderRadius='lg' borderWidth='1px' padding='5'>
<Stack spacing='5px'>
<FormControl isDisabled>
<FormHelperText color='gray.500'>First name</FormHelperText>

View File

@ -1,8 +1,8 @@
import React from 'react';
import { Link as ReactRouterLink } from 'react-router-dom';
import * as helpres from '@educt/helpers';
import { Box, Flex, Avatar, Text, IconButton, Link } from '@chakra-ui/react';
import { Link as ReactRouterLink } from 'react-router-dom';
import UserBadge from '@educt/components/UserBadge';
import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
/**
* Types
@ -10,11 +10,16 @@ import UserBadge from '@educt/components/UserBadge';
import { IUser } from '@educt/interfaces';
import { UserRoleEnum } from '@educt/enums';
/**
* Components
*/
import UserBadge from '@educt/components/UserBadge';
/**
* Hooks
*/
import { useRootStore } from '@educt/hooks/useRootStore';
import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
import { useColorModeValue } from '@chakra-ui/react';
type UserItemPropsType = {
user: IUser;
@ -46,7 +51,7 @@ const UserItem: React.FC<UserItemPropsType> = ({ user, onEdit, onDelete }) => {
})();
return (
<Box borderWidth='1px' borderRadius='lg' w='full' p='3'>
<Box borderBottomWidth='1px' borderRadius='lg' w='full' p='3' bg={useColorModeValue('gray.50', 'gray.700')}>
<Flex alignItems='center' justifyContent='space-between' sx={{ gap: '20px' }}>
<Flex alignItems='center' flexBasis='230px'>
<Avatar name={user.fullname} size='sm' mr='3' />

View File

@ -8,6 +8,7 @@ import { Button } from '@chakra-ui/button';
*/
import UserItem from './UserItem';
import EditUserForm from '../EditUserForm';
import DeleteUserDialog from '../DeleteUserDialog';
/**
* Types
@ -27,7 +28,6 @@ import { useContext } from 'react';
import { useRootStore } from '@educt/hooks/useRootStore';
import { useDisclosure } from '@chakra-ui/hooks';
import { useErrorHandler } from 'react-error-boundary';
import DeleteUserDialog from '../DeleteUserDialog';
type UserListPropsType = { users: IUser[]; pagination: IPaginationMeta };
@ -85,7 +85,6 @@ const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
const onEditUser = (user: IUser) => {
setEditingUser(user);
onOpenEditModal();
console.log(`[LOG]: Editing user "${user.id}"`);
};
/**
@ -95,7 +94,6 @@ const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
const onDeleteUser = (user: IUser) => {
setDeletingUser(user);
onOpenDeleteDialog();
console.log(`[LOG]: Alert delete "${user.id}"`);
};
return (