mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: page contexts
This commit is contained in:
parent
99e22a4c69
commit
3a4296f3bd
@ -43,7 +43,7 @@ const App = () => {
|
||||
<PrivateRoute
|
||||
path='/users'
|
||||
component={UsersPage}
|
||||
title='Control'
|
||||
title='Users Management'
|
||||
roles={[UserRoleEnum.ADMIN, UserRoleEnum.TEACHER]}
|
||||
/>
|
||||
<PublicRoute path='/auth' component={AuthPage} />
|
||||
|
||||
@ -4,9 +4,11 @@ import RootStore from '../stores/RootStore';
|
||||
|
||||
export const StoreContext = createContext<RootStore | undefined>(undefined);
|
||||
|
||||
export const ProfilePageViewContext = createContext<ProfilePageViewStatusContextType>({
|
||||
export const ProfilePageContext = createContext<ProfilePageViewStatusContextType>({
|
||||
statusPageView: 'default',
|
||||
setStatusPageView: () => {},
|
||||
pageData: {},
|
||||
setPageData: () => {},
|
||||
});
|
||||
|
||||
export const UsersPageContext = createContext<UsersPageContextType>({
|
||||
@ -16,4 +18,6 @@ export const UsersPageContext = createContext<UsersPageContextType>({
|
||||
setLoading: () => {},
|
||||
search: undefined,
|
||||
setSearch: () => {},
|
||||
editingUser: undefined,
|
||||
setEditingUser: () => {},
|
||||
});
|
||||
|
||||
@ -2,12 +2,12 @@ import { useState } from 'react';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useRootStore } from './useRootStore';
|
||||
import { IDataResult } from 'interfaces';
|
||||
import { IApiRespose } from 'interfaces';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
|
||||
type LogoutStateType = {
|
||||
result: IDataResult | null;
|
||||
result: IApiRespose<{}> | null;
|
||||
error: any;
|
||||
loading: boolean;
|
||||
fetched: boolean;
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { IDataResult, IUserContacts } from 'interfaces';
|
||||
import { IApiRespose, IUserContacts } from 'interfaces';
|
||||
import { useState } from 'react';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
import { useRootStore } from './useRootStore';
|
||||
|
||||
type UpdateContactsStateType = {
|
||||
result: IDataResult | null;
|
||||
result: IApiRespose<{}> | null;
|
||||
error: any;
|
||||
loading: boolean;
|
||||
fetched: boolean;
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { useToast } from '@chakra-ui/toast';
|
||||
import { ISentCodeResult } from 'interfaces';
|
||||
import { IApiRespose } from 'interfaces';
|
||||
import { useState } from 'react';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import useIsMountedRef from './useIsMountedRef';
|
||||
import { useRootStore } from './useRootStore';
|
||||
|
||||
type UpdateUserEmailStateType = {
|
||||
result: ISentCodeResult | null;
|
||||
result: IApiRespose<{ expired_seconds: number }> | null;
|
||||
error: any;
|
||||
loading: boolean;
|
||||
fetched: boolean;
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, Heading, Stack, Flex, StackDivider, Text, Button } from '@chakra-ui/react';
|
||||
import { IUser } from 'interfaces';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
|
||||
type UserAccountInfoPropsType = {
|
||||
user: IUser;
|
||||
};
|
||||
|
||||
const UserAccountInfo: React.FC<UserAccountInfoPropsType> = ({ user }) => {
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||
|
||||
return (
|
||||
<Box borderRadius='md' borderWidth='1px' padding='20px' boxShadow='sm'>
|
||||
@ -45,7 +45,7 @@ const UserAccountInfo: React.FC<UserAccountInfoPropsType> = ({ user }) => {
|
||||
{user.email}
|
||||
</Text>
|
||||
</Box>
|
||||
<Button onClick={() => setStatusPageView({ status: 'update-email' })}>Edit</Button>
|
||||
<Button onClick={() => setStatusPageView('update-email')}>Edit</Button>
|
||||
</Flex>
|
||||
<Flex justifyContent='space-between' alignItems='center'>
|
||||
<Box>
|
||||
@ -56,7 +56,7 @@ const UserAccountInfo: React.FC<UserAccountInfoPropsType> = ({ user }) => {
|
||||
**********
|
||||
</Text>
|
||||
</Box>
|
||||
<Button onClick={() => setStatusPageView({ status: 'update-password' })}>Change</Button>
|
||||
<Button onClick={() => setStatusPageView('update-password')}>Change</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
@ -7,7 +7,7 @@ import useUpdateUserEmailQuery from 'hooks/useUpdateUserEmailQuery.ts';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useToast } from '@chakra-ui/toast';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
import useIsMountedRef from 'hooks/useIsMountedRef';
|
||||
|
||||
type ConfirmEmailContainerPropsType = {
|
||||
@ -16,7 +16,7 @@ type ConfirmEmailContainerPropsType = {
|
||||
|
||||
const ConfirmEmailContainer: React.FC<ConfirmEmailContainerPropsType> = ({ data }) => {
|
||||
const { userStore } = useRootStore();
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||
const { sendConfirmationCode, loading } = useUpdateUserEmailQuery();
|
||||
const [isVerifying, setIsVerifying] = useState<boolean>(false);
|
||||
const [code, setCode] = useState<string>('');
|
||||
@ -47,7 +47,7 @@ const ConfirmEmailContainer: React.FC<ConfirmEmailContainerPropsType> = ({ data
|
||||
status: 'info',
|
||||
});
|
||||
if (isMountedRef.current) {
|
||||
setStatusPageView({ status: 'default' });
|
||||
setStatusPageView('default');
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (error.response) {
|
||||
@ -76,8 +76,10 @@ const ConfirmEmailContainer: React.FC<ConfirmEmailContainerPropsType> = ({ data
|
||||
handleError(error);
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setIsVerifying(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resendCodeHandler = async () => {
|
||||
|
||||
105
src/pages/profile/containers/MainContainer/MainContainer.tsx
Normal file
105
src/pages/profile/containers/MainContainer/MainContainer.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import React from 'react';
|
||||
import { Box, Heading, Flex, Avatar, Text, Tabs, TabList, Tab, TabPanels, TabPanel } from '@chakra-ui/react';
|
||||
|
||||
/**
|
||||
* Components
|
||||
*/
|
||||
import UserBadge from 'components/UserBadge';
|
||||
import UserAccountInfo from 'pages/profile/components/UserAccountInfo';
|
||||
import UpdateUserContactsForm from 'pages/profile/components/UpdateUserContactsForm';
|
||||
import CourseList from 'pages/profile/components/CourseList';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useColorMode } from '@chakra-ui/color-mode';
|
||||
|
||||
const MainContainer: React.FC = () => {
|
||||
const {
|
||||
userStore: { me },
|
||||
} = useRootStore();
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
if (me === null) return null;
|
||||
|
||||
return (
|
||||
<Box maxW='900px'>
|
||||
<Heading as='h1'>My account</Heading>
|
||||
<Text mt='2'>Update you contacts information or password here.</Text>
|
||||
<Flex
|
||||
alignItems='center'
|
||||
mt='20'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
mt: '10',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Avatar border={`4px solid ${colorMode === 'dark' ? '#2D3748' : '#E2E8F0'}`} size='2xl' name={me.fullname} />
|
||||
</Box>
|
||||
<Flex
|
||||
ml='50'
|
||||
flexDirection='column'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
ml: '0',
|
||||
mt: '5',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Heading as='h2'>{me.fullname}</Heading>
|
||||
<Flex
|
||||
alignItems='center'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
mr='10px'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
mr: '0',
|
||||
mb: '5px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{me.email}
|
||||
</Text>
|
||||
<UserBadge roles={me.roles} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box mt='10'>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab>Account information</Tab>
|
||||
<Tab>My contacts</Tab>
|
||||
{me.isStudent && <Tab>Available courses</Tab>}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<UserAccountInfo user={me} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<UpdateUserContactsForm contacts={me.contacts} />
|
||||
</TabPanel>
|
||||
{me.isStudent && (
|
||||
<TabPanel>
|
||||
<CourseList courses={me.courses} />
|
||||
</TabPanel>
|
||||
)}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainContainer;
|
||||
3
src/pages/profile/containers/MainContainer/index.ts
Normal file
3
src/pages/profile/containers/MainContainer/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import MainContainer from './MainContainer';
|
||||
|
||||
export default MainContainer;
|
||||
@ -1,12 +1,12 @@
|
||||
import { IconButton } from '@chakra-ui/button';
|
||||
import { ChevronLeftIcon } from '@chakra-ui/icons';
|
||||
import { Box, Flex, Heading, Text } from '@chakra-ui/layout';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
import React, { useContext } from 'react';
|
||||
import UpdateEmailForm from './UpdateEmailForm';
|
||||
|
||||
const UpdateEmailContainer: React.FC = () => {
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||
|
||||
return (
|
||||
<Box maxW='700px'>
|
||||
@ -15,7 +15,7 @@ const UpdateEmailContainer: React.FC = () => {
|
||||
aria-label='Back'
|
||||
borderRadius='full'
|
||||
icon={<ChevronLeftIcon />}
|
||||
onClick={() => setStatusPageView({ status: 'default' })}
|
||||
onClick={() => setStatusPageView('default')}
|
||||
mr='5'
|
||||
/>
|
||||
<Heading as='h1'>Edit email address</Heading>
|
||||
|
||||
@ -3,7 +3,7 @@ import { FormControl, FormHelperText, FormLabel } from '@chakra-ui/form-control'
|
||||
import { Input } from '@chakra-ui/input';
|
||||
import { Box, Stack } from '@chakra-ui/layout';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import useUpdateUserEmailQuery from 'hooks/useUpdateUserEmailQuery.ts';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
@ -19,7 +19,7 @@ type UpdateEmailInputType = {
|
||||
*/
|
||||
const UpdateEmailForm: React.FC = () => {
|
||||
const [email, setEmail] = useState<string | null>(null);
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView, setPageData } = useContext(ProfilePageContext);
|
||||
const { userStore } = useRootStore();
|
||||
const { sendConfirmationCode, loading, result } = useUpdateUserEmailQuery();
|
||||
const {
|
||||
@ -32,10 +32,8 @@ const UpdateEmailForm: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (email !== null && result !== null) {
|
||||
setStatusPageView({
|
||||
status: 'confirm-email',
|
||||
data: { confirmEmailData: { newEmail: email, expired_seconds: result.data.expired_seconds } },
|
||||
});
|
||||
setStatusPageView('confirm-email');
|
||||
setPageData({ confirmEmailData: { newEmail: email, expired_seconds: result.data.expired_seconds } });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, Heading, Flex, Text, IconButton } from '@chakra-ui/react';
|
||||
import { ChevronLeftIcon } from '@chakra-ui/icons';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
import UpdatePasswordForm from './UpdatePasswordForm';
|
||||
|
||||
const UpdatePasswordContainer: React.FC = () => {
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||
|
||||
return (
|
||||
<Box maxW='700px'>
|
||||
@ -14,7 +14,7 @@ const UpdatePasswordContainer: React.FC = () => {
|
||||
aria-label='Back'
|
||||
borderRadius='full'
|
||||
icon={<ChevronLeftIcon />}
|
||||
onClick={() => setStatusPageView({ status: 'default' })}
|
||||
onClick={() => setStatusPageView('default')}
|
||||
mr='5'
|
||||
/>
|
||||
<Heading as='h1'>Update password</Heading>
|
||||
|
||||
@ -9,7 +9,7 @@ import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import UpdatePasswordSchema from './UpdatePasswordForm.validator';
|
||||
import { useToast } from '@chakra-ui/toast';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
import useIsMountedRef from 'hooks/useIsMountedRef';
|
||||
|
||||
type UpdatePasswordInputType = {
|
||||
@ -22,7 +22,7 @@ type UpdatePasswordInputType = {
|
||||
* Update password using form
|
||||
*/
|
||||
const UpdatePasswordForm: React.FC = () => {
|
||||
const { setStatusPageView } = useContext(ProfilePageViewContext);
|
||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||
const { userStore } = useRootStore();
|
||||
const isMountedRef = useIsMountedRef();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
@ -45,7 +45,7 @@ const UpdatePasswordForm: React.FC = () => {
|
||||
await userStore.updateCurrentUserPassword(old_password, new_password);
|
||||
toast({ title: 'Password changed.', status: 'success' });
|
||||
reset();
|
||||
setStatusPageView({ status: 'default' });
|
||||
setStatusPageView('default');
|
||||
} catch (error: any) {
|
||||
if (error.response) {
|
||||
if (error.response.status === 401) {
|
||||
|
||||
@ -1,35 +1,15 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import {
|
||||
Box,
|
||||
Heading,
|
||||
Flex,
|
||||
Avatar,
|
||||
useColorMode,
|
||||
Text,
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
} from '@chakra-ui/react';
|
||||
import { userHasRoles } from 'helpers';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
import { ProfilePageStatusType } from 'types';
|
||||
import { IPageProps } from 'interfaces';
|
||||
import { UserRoleEnum } from 'enums';
|
||||
|
||||
/**
|
||||
* Components
|
||||
*/
|
||||
import CourseList from './components/CourseList';
|
||||
import UserBadge from 'components/UserBadge';
|
||||
import LoadingPage from './components/LoadingPage';
|
||||
import UserAccountInfo from './components/UserAccountInfo';
|
||||
import UpdateUserContactsForm from './components/UpdateUserContactsForm';
|
||||
|
||||
/**
|
||||
* Containers
|
||||
@ -47,15 +27,22 @@ import { useHistory } from 'react-router-dom';
|
||||
/**
|
||||
* Contexts
|
||||
*/
|
||||
import { ProfilePageViewContext } from 'contexts';
|
||||
import { ProfilePageContext } from 'contexts';
|
||||
|
||||
/**
|
||||
* Providers
|
||||
*/
|
||||
import { ProfilePageContextProvider } from 'providers';
|
||||
import MainContainer from './containers/MainContainer';
|
||||
|
||||
/**
|
||||
* Profile page
|
||||
*/
|
||||
const ProfilePage: React.FC<IPageProps> = () => {
|
||||
const [statusPageView, setStatusPageView] = useState<ProfilePageStatusType>({ status: 'default' });
|
||||
const { colorMode } = useColorMode();
|
||||
const { userStore, authStore } = useRootStore();
|
||||
const {
|
||||
userStore: { me },
|
||||
authStore,
|
||||
} = useRootStore();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
@ -64,101 +51,29 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
||||
}
|
||||
});
|
||||
|
||||
if (userStore.me === null) {
|
||||
if (me === null) {
|
||||
return <LoadingPage />;
|
||||
}
|
||||
|
||||
const isStudent = userHasRoles(userStore.me.roles, [UserRoleEnum.STUDENT]);
|
||||
|
||||
return (
|
||||
<ProfilePageViewContext.Provider value={{ statusPageView: statusPageView.status, setStatusPageView }}>
|
||||
{statusPageView.status === 'default' && (
|
||||
<Box maxW='900px'>
|
||||
<Heading as='h1'>My account</Heading>
|
||||
<Text mt='2'>Update you contacts information or password here.</Text>
|
||||
<Flex
|
||||
alignItems='center'
|
||||
mt='20'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center',
|
||||
mt: '10',
|
||||
},
|
||||
<ProfilePageContextProvider>
|
||||
<ProfilePageContext.Consumer>
|
||||
{({ statusPageView, pageData }) => {
|
||||
switch (statusPageView) {
|
||||
case 'default':
|
||||
return <MainContainer />;
|
||||
case 'update-password':
|
||||
return <UpdatePasswordContainer />;
|
||||
case 'update-email':
|
||||
return <UpdateEmailContainer />;
|
||||
case 'confirm-email':
|
||||
return pageData.confirmEmailData && <ConfirmEmailContainer data={pageData.confirmEmailData} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Avatar
|
||||
border={`4px solid ${colorMode === 'dark' ? '#2D3748' : '#E2E8F0'}`}
|
||||
size='2xl'
|
||||
name={userStore.me.fullname}
|
||||
/>
|
||||
</Box>
|
||||
<Flex
|
||||
ml='50'
|
||||
flexDirection='column'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
ml: '0',
|
||||
mt: '5',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Heading as='h2'>{userStore.me.fullname}</Heading>
|
||||
<Flex
|
||||
alignItems='center'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
flexDirection: 'column',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
mr='10px'
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
mr: '0',
|
||||
mb: '5px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{userStore.me.email}
|
||||
</Text>
|
||||
<UserBadge roles={userStore.me.roles} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box mt='10'>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
<Tab>Account information</Tab>
|
||||
<Tab>My contacts</Tab>
|
||||
{isStudent && <Tab>Available courses</Tab>}
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<UserAccountInfo user={userStore.me} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<UpdateUserContactsForm contacts={userStore.me.contacts} />
|
||||
</TabPanel>
|
||||
{isStudent && (
|
||||
<TabPanel>
|
||||
<CourseList courses={userStore.me.courses} />
|
||||
</TabPanel>
|
||||
)}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
{statusPageView.status === 'update-password' && <UpdatePasswordContainer />}
|
||||
{statusPageView.status === 'update-email' && <UpdateEmailContainer />}
|
||||
{statusPageView.status === 'confirm-email' && statusPageView.data?.confirmEmailData && (
|
||||
<ConfirmEmailContainer data={statusPageView.data.confirmEmailData} />
|
||||
)}
|
||||
</ProfilePageViewContext.Provider>
|
||||
</ProfilePageContext.Consumer>
|
||||
</ProfilePageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
3
src/pages/users/components/EditUserForm/index.ts
Normal file
3
src/pages/users/components/EditUserForm/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import EditUserForm from './EditUserForm';
|
||||
|
||||
export default EditUserForm;
|
||||
@ -1,3 +0,0 @@
|
||||
import UpdateUserForm from './UpdateUserForm';
|
||||
|
||||
export default UpdateUserForm;
|
||||
@ -4,7 +4,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons';
|
||||
import { Flex, Box, Stack, Text } from '@chakra-ui/layout';
|
||||
import { Button } from '@chakra-ui/button';
|
||||
import UserItem from './UserItem';
|
||||
import UpdateUserForm from '../UpdateUserForm';
|
||||
import EditUserForm from '../EditUserForm';
|
||||
|
||||
/**
|
||||
* Types
|
||||
@ -20,7 +20,7 @@ import { UsersPageContext } from 'contexts';
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useContext, useState } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useDisclosure } from '@chakra-ui/hooks';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
@ -29,8 +29,8 @@ type UserListPropsType = { users: IUser[]; pagination: IPaginationMeta };
|
||||
|
||||
const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
|
||||
const { userStore } = useRootStore();
|
||||
const [editingUser, setEditingUser] = useState<IUser | undefined>(undefined);
|
||||
const { searchingRole, loading, setLoading, search } = useContext<UsersPageContextType>(UsersPageContext);
|
||||
const { searchingRole, loading, setLoading, search, editingUser, setEditingUser } =
|
||||
useContext<UsersPageContextType>(UsersPageContext);
|
||||
const handleError = useErrorHandler();
|
||||
const pagesCount = Math.ceil(pagination.total / pagination.per_page);
|
||||
const { onOpen: onOpenEditModal, onClose: onCloseEditModal, isOpen: isOpenEditModal } = useDisclosure();
|
||||
@ -86,7 +86,7 @@ const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
|
||||
<>
|
||||
{pagination.total !== 0 ? (
|
||||
<>
|
||||
{editingUser && <UpdateUserForm user={editingUser} onClose={onCloseEditModal} isOpen={isOpenEditModal} />}
|
||||
{editingUser && <EditUserForm user={editingUser} onClose={onCloseEditModal} isOpen={isOpenEditModal} />}
|
||||
<Box>
|
||||
<Flex mt='7' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'>
|
||||
<Text>Total: ({pagination.total})</Text>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Box, Flex, Button, Heading, Text } from '@chakra-ui/react';
|
||||
import { FaFileExcel } from 'react-icons/fa';
|
||||
@ -7,7 +7,6 @@ import { FaFileExcel } from 'react-icons/fa';
|
||||
* Types
|
||||
*/
|
||||
import { IPageProps } from 'interfaces';
|
||||
import { SearchingRoleStateType } from 'types';
|
||||
|
||||
/**
|
||||
* Components
|
||||
@ -26,17 +25,14 @@ import { useLocation } from 'react-router';
|
||||
/**
|
||||
* Context
|
||||
*/
|
||||
import { UsersPageContext } from 'contexts';
|
||||
import CreateUserForm from './components/CreateUserForm';
|
||||
import { UsersPageContextProvider } from 'providers';
|
||||
|
||||
/**
|
||||
* Users Page
|
||||
*/
|
||||
const UsersPage: React.FC<IPageProps> = ({ title }) => {
|
||||
const UsersPage: React.FC<IPageProps> = () => {
|
||||
const { userStore } = useRootStore();
|
||||
const [searchingRole, setSearchingRole] = useState<SearchingRoleStateType>(undefined);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [search, setSearch] = useState<string | undefined>(undefined);
|
||||
const handleError = useErrorHandler();
|
||||
const location = useLocation();
|
||||
|
||||
@ -45,7 +41,7 @@ const UsersPage: React.FC<IPageProps> = ({ title }) => {
|
||||
}, [handleError, userStore, location.search]);
|
||||
|
||||
return (
|
||||
<UsersPageContext.Provider value={{ searchingRole, setSearchingRole, loading, setLoading, search, setSearch }}>
|
||||
<UsersPageContextProvider>
|
||||
<Box h='100%'>
|
||||
<Heading as='h1'>User management</Heading>
|
||||
<Text mt='2'>You can add or delete your users on this page.</Text>
|
||||
@ -66,7 +62,7 @@ const UsersPage: React.FC<IPageProps> = ({ title }) => {
|
||||
<LoadingPage />
|
||||
)}
|
||||
</Box>
|
||||
</UsersPageContext.Provider>
|
||||
</UsersPageContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,12 +1,46 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StoreContext } from 'contexts';
|
||||
import React, { useState, ReactNode } from 'react';
|
||||
import { ProfilePageContext, StoreContext, UsersPageContext } from 'contexts';
|
||||
import { ProfilePageDataType, ProfilePageStatusType, SearchingRoleStateType } from 'types';
|
||||
import RootStore from 'stores/RootStore';
|
||||
import { IUser } from 'interfaces';
|
||||
|
||||
/**
|
||||
* Root store provider
|
||||
* Root store context provider
|
||||
*/
|
||||
export const RootStoreProvider = ({ children }: { children: ReactNode }) => {
|
||||
const store = new RootStore();
|
||||
|
||||
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Profile page context provider
|
||||
*/
|
||||
export const ProfilePageContextProvider: React.FC = ({ children }) => {
|
||||
const [statusPageView, setStatusPageView] = useState<ProfilePageStatusType>('default');
|
||||
const [pageData, setPageData] = useState<ProfilePageDataType>({});
|
||||
|
||||
return (
|
||||
<ProfilePageContext.Provider value={{ statusPageView, setStatusPageView, pageData, setPageData }}>
|
||||
{children}
|
||||
</ProfilePageContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Users page context provider
|
||||
*/
|
||||
export const UsersPageContextProvider: React.FC = ({ children }) => {
|
||||
const [searchingRole, setSearchingRole] = useState<SearchingRoleStateType>(undefined);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [search, setSearch] = useState<string | undefined>(undefined);
|
||||
const [editingUser, setEditingUser] = useState<IUser | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<UsersPageContext.Provider
|
||||
value={{ searchingRole, setSearchingRole, loading, setLoading, search, setSearch, editingUser, setEditingUser }}
|
||||
>
|
||||
{children}
|
||||
</UsersPageContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
25
src/types/index.d.ts
vendored
25
src/types/index.d.ts
vendored
@ -1,4 +1,5 @@
|
||||
import { UserRoleEnum } from 'enums';
|
||||
import { IUser } from 'interfaces';
|
||||
import React from 'react';
|
||||
import { IconType } from 'react-icons/lib';
|
||||
|
||||
@ -19,19 +20,25 @@ export type ConfirmEmailContainerDataType = {
|
||||
expired_seconds: number;
|
||||
};
|
||||
|
||||
export type ProfilePageStatusType = {
|
||||
status: 'update-password' | 'update-email' | 'confirm-email' | 'default';
|
||||
data?: {
|
||||
export type ProfilePageStatusType = 'update-password' | 'update-email' | 'confirm-email' | 'default';
|
||||
export type ProfilePageDataType = {
|
||||
/**
|
||||
* The field that is set when trying to change the mail address through form
|
||||
*/
|
||||
confirmEmailData?: ConfirmEmailContainerDataType;
|
||||
};
|
||||
};
|
||||
|
||||
export type ProfilePageViewStatusContextType = {
|
||||
statusPageView: ProfilePageStatusType['status'];
|
||||
/**
|
||||
* Render current container by status
|
||||
*/
|
||||
statusPageView: ProfilePageStatusType;
|
||||
setStatusPageView: React.Dispatch<React.SetStateAction<ProfilePageStatusType>>;
|
||||
|
||||
/**
|
||||
* Some data for other components
|
||||
*/
|
||||
pageData: ProfilePageDataType;
|
||||
setPageData: React.Dispatch<React.SetStateAction<ProfilePageDataType>>;
|
||||
};
|
||||
|
||||
export type SearchingRoleStateType = UserRoleEnum | 'any' | undefined;
|
||||
@ -53,4 +60,10 @@ export type UsersPageContextType = {
|
||||
*/
|
||||
search: string | undefined;
|
||||
setSearch: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
|
||||
/**
|
||||
* Set editing user state when press on action button
|
||||
*/
|
||||
editingUser: IUser | undefined;
|
||||
setEditingUser: React.Dispatch<React.SetStateAction<IUser | undefined>>;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user