refactor: user rendering

This commit is contained in:
Sergey Yarkov 2021-11-03 00:27:15 +03:00
parent 5f7c27b060
commit 3e69a47907
4 changed files with 86 additions and 83 deletions

View File

@ -21,7 +21,7 @@ import UserBadge from '@educt/components/UserBadge';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { useColorModeValue } from '@chakra-ui/react'; import { useColorModeValue } from '@chakra-ui/react';
type UserItemPropsType = { export type UserItemPropsType = {
user: IUser; user: IUser;
onEdit: (user: IUser) => void; onEdit: (user: IUser) => void;
onDelete: (user: IUser) => void; onDelete: (user: IUser) => void;

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react';
import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons';
import { Flex, Box, Stack, Text } from '@chakra-ui/layout'; import { Flex, Box, Stack, Text } from '@chakra-ui/layout';
import { Button } from '@chakra-ui/button'; import { Button } from '@chakra-ui/button';
@ -6,15 +7,15 @@ import { Button } from '@chakra-ui/button';
/** /**
* Components * Components
*/ */
import UserItem from './UserItem';
import EditUserForm from '../EditUserForm'; import EditUserForm from '../EditUserForm';
import DeleteUserDialog from '../DeleteUserDialog'; import DeleteUserDialog from '../DeleteUserDialog';
/** /**
* Types * Types
*/ */
import { IPaginationMeta, IUser } from '@educt/interfaces'; import type { IUser } from '@educt/interfaces';
import { UsersPageContextType } from '@educt/types'; import type { UsersPageContextType } from '@educt/types';
import type { UserItemPropsType } from './UserItem';
/** /**
* Contexts * Contexts
@ -24,21 +25,31 @@ import { UsersPageContext } from '@educt/contexts';
/** /**
* Hooks * Hooks
*/ */
import { useContext } from 'react'; import { useEffect, useContext } from 'react';
import { useLocation } from 'react-router';
import { useErrorHandler } from 'react-error-boundary';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { useDisclosure } from '@chakra-ui/hooks'; import { useDisclosure } from '@chakra-ui/hooks';
import { useErrorHandler } from 'react-error-boundary';
type UserListPropsType = { users: IUser[]; pagination: IPaginationMeta }; type UserListPropsType = { render: React.FC<UserItemPropsType> };
const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => { const UserList: React.FC<UserListPropsType> = ({ render: Item }) => {
const { userStore } = useRootStore(); const { userStore } = useRootStore();
const { searchingRole, loading, setLoading, search, editingUser, setEditingUser, deletingUser, setDeletingUser } = const { searchingRole, loading, setLoading, search, editingUser, setEditingUser, deletingUser, setDeletingUser } =
useContext<UsersPageContextType>(UsersPageContext); useContext<UsersPageContextType>(UsersPageContext);
const location = useLocation();
const handleError = useErrorHandler(); const handleError = useErrorHandler();
const pagesCount = Math.ceil(pagination.total / pagination.per_page);
const { onOpen: onOpenEditModal, onClose: onCloseEditModal, isOpen: isOpenEditModal } = useDisclosure(); const { onOpen: onOpenEditModal, onClose: onCloseEditModal, isOpen: isOpenEditModal } = useDisclosure();
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure(); const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
const { users, pagination } = userStore;
useEffect(() => {
userStore.loadUsersData().catch(error => handleError(error));
}, [handleError, userStore, location.search]);
if (pagination === undefined || users === null) return <div>Loading users...</div>;
const pagesCount = Math.ceil(pagination.total / pagination.per_page);
/** /**
* Handle next page * Handle next page
@ -96,60 +107,59 @@ const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
onOpenDeleteDialog(); onOpenDeleteDialog();
}; };
if (pagination.total === 0)
return (
<Box textAlign='center' mt='10' userSelect='none'>
<Text color='gray.500'>Cannot find any users.</Text>
</Box>
);
return ( return (
<> <>
{pagination.total !== 0 ? ( {editingUser && <EditUserForm onClose={onCloseEditModal} isOpen={isOpenEditModal} />}
<> {deletingUser && <DeleteUserDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
{editingUser && <EditUserForm onClose={onCloseEditModal} isOpen={isOpenEditModal} />} <Box>
{deletingUser && <DeleteUserDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />} <Flex mt='7' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'>
<Box> <Text>Total: ({pagination.total})</Text>
<Flex mt='7' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'> <Text>Actions</Text>
<Text>Total: ({pagination.total})</Text> </Flex>
<Text>Actions</Text> {loading ? (
</Flex> <Box textAlign='center' mt='10' userSelect='none'>
{loading ? ( <Text color='gray.500'>Wait a second...</Text>
<Box textAlign='center' mt='10' userSelect='none'>
<Text color='gray.500'>Wait a second...</Text>
</Box>
) : (
<Stack mt='4' spacing='2'>
{users.map(user => (
<UserItem key={user.id} user={user} onEdit={onEditUser} onDelete={onDeleteUser} />
))}
</Stack>
)}
</Box> </Box>
<Flex margin='2rem 0' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}> ) : (
<Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}> <Stack mt='4' spacing='2'>
Page <b>{pagination.current_page}</b> of {pagesCount} {users.map(user => (
</Box> <Item key={user.id} user={user} onEdit={onEditUser} onDelete={onDeleteUser} />
<Flex ml='auto' mr='auto' alignItems='center' sx={{ gap: '30px' }}> ))}
<Button </Stack>
onClick={prevPage} )}
disabled={pagination.current_page <= 1} </Box>
variant='link' <Flex margin='2rem 0' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}>
leftIcon={<ChevronLeftIcon />} <Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}>
> Page <b>{pagination.current_page}</b> of {pagesCount}
Previous page
</Button>
<Button
onClick={nextPage}
disabled={pagination.current_page === pagesCount}
variant='link'
rightIcon={<ChevronRightIcon />}
>
Next page
</Button>
</Flex>
</Flex>
</>
) : (
<Box textAlign='center' mt='10' userSelect='none'>
<Text color='gray.500'>Cannot find any users.</Text>
</Box> </Box>
)} <Flex ml='auto' mr='auto' alignItems='center' sx={{ gap: '30px' }}>
<Button
onClick={prevPage}
disabled={pagination.current_page <= 1}
variant='link'
leftIcon={<ChevronLeftIcon />}
>
Previous page
</Button>
<Button
onClick={nextPage}
disabled={pagination.current_page === pagesCount}
variant='link'
rightIcon={<ChevronRightIcon />}
>
Next page
</Button>
</Flex>
</Flex>
</> </>
); );
}; };
export default UserList; export default observer(UserList);

View File

@ -6,6 +6,7 @@ import { UserRoleEnum } from '@educt/enums';
import { useErrorHandler } from 'react-error-boundary'; import { useErrorHandler } from 'react-error-boundary';
import { UsersPageContext } from '@educt/contexts'; import { UsersPageContext } from '@educt/contexts';
import { UsersPageContextType } from '@educt/types'; import { UsersPageContextType } from '@educt/types';
import { observer } from 'mobx-react';
type SelectRoleStateType = UserRoleEnum | 'any'; type SelectRoleStateType = UserRoleEnum | 'any';
@ -70,4 +71,4 @@ const UserSearch: React.FC = () => {
); );
}; };
export default UserSearch; export default observer(UserSearch);

View File

@ -1,5 +1,4 @@
import React, { useEffect } from 'react'; import React from 'react';
import { observer } from 'mobx-react';
import { Box, Flex, Heading, Text } from '@chakra-ui/react'; import { Box, Flex, Heading, Text } from '@chakra-ui/react';
/** /**
@ -10,56 +9,49 @@ import { IPageProps } from '@educt/interfaces';
/** /**
* Components * Components
*/ */
import LoadingPage from '@educt/components/LoadingPage';
import UserSearch from './components/UserSearch'; import UserSearch from './components/UserSearch';
import UserList from './components/UserList'; import UserList from './components/UserList';
import LoadingPage from './components/LoadingPage'; import UserItem from './components/UserList/UserItem';
import CreateUserForm from './components/CreateUserForm';
/** /**
* Hooks * Hooks
*/ */
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { useErrorHandler } from 'react-error-boundary';
import { useLocation } from 'react-router';
/** /**
* Context * Context
*/ */
import CreateUserForm from './components/CreateUserForm';
import { UsersPageContextProvider } from '@educt/providers'; import { UsersPageContextProvider } from '@educt/providers';
/** /**
* Users Page * Users Page
*/ */
const UsersPage: React.FC<IPageProps> = () => { const UsersPage: React.FC<IPageProps> = () => {
const { userStore } = useRootStore(); const {
const handleError = useErrorHandler(); userStore: { me },
const location = useLocation(); } = useRootStore();
useEffect(() => { if (me === null) return <LoadingPage />;
userStore.loadUsersData().catch(error => handleError(error));
}, [handleError, userStore, location.search]);
return ( return (
<UsersPageContextProvider> <UsersPageContextProvider>
<Box h='100%'> <Box h='100%'>
<Heading as='h1'>User management</Heading> <Heading as='h1'>User management</Heading>
<Text mt='2'>You can add or delete users on this page.</Text> <Text mt='2'>You can add or delete users on this page.</Text>
{userStore.users !== null && userStore.me !== null && userStore.pagination !== undefined ? ( <Flex mt='5' flexDir='column' h='full'>
<Flex mt='5' flexDir='column' h='full'> <Flex justifyContent='space-between' sx={{ gap: '10px' }} flexWrap='wrap'>
<Flex justifyContent='space-between' sx={{ gap: '10px' }} flexWrap='wrap'> <UserSearch />
<UserSearch /> <Flex sx={{ gap: '10px' }}>
<Flex sx={{ gap: '10px' }}> <CreateUserForm me={me} />
<CreateUserForm me={userStore.me} />
</Flex>
</Flex> </Flex>
<UserList users={userStore.users} pagination={userStore.pagination} />
</Flex> </Flex>
) : ( <UserList render={UserItem} />
<LoadingPage /> </Flex>
)}
</Box> </Box>
</UsersPageContextProvider> </UsersPageContextProvider>
); );
}; };
export default observer(UsersPage); export default UsersPage;