refactor: profile layout

This commit is contained in:
Sergey Yarkov 2022-02-02 21:32:44 +03:00
parent a14ed5eff9
commit ab662a50f6
10 changed files with 224 additions and 88 deletions

View File

@ -0,0 +1,12 @@
import { Flex, FlexProps } from '@chakra-ui/react';
import React from 'react';
const Base: React.FC<FlexProps> = props => {
return (
<Flex flexDir={{ base: 'column', md: 'row' }} {...props}>
{props.children}
</Flex>
);
};
export { Base };

View File

@ -0,0 +1,3 @@
import { Base } from './Base';
export { Base };

View File

@ -0,0 +1,8 @@
import React from 'react';
import { Flex } from '@chakra-ui/react';
const Entry: React.FC = props => {
return <Flex flexDir={{ base: 'column', md: 'row' }}>{props.children}</Flex>;
};
export { Entry };

View File

@ -0,0 +1,12 @@
import React from 'react';
import { Flex } from '@chakra-ui/react';
const EntryContent: React.FC = props => {
return (
<Flex flexDir={'column'} w='full' mt={{ base: '6', md: '0' }}>
{props.children}
</Flex>
);
};
export { EntryContent };

View File

@ -0,0 +1,31 @@
import React from 'react';
import { Box, BoxProps, Heading as ChakraHeading, Divider, Stack, StackDivider } from '@chakra-ui/react';
import { Title, ITitleProps } from './Title';
import { Entry } from './Entry';
import { EntryContent } from './EntryContent';
export interface ISettingsComposition {
Title: React.FC<ITitleProps & BoxProps>;
Entry: React.FC;
EntryContent: React.FC;
}
const Settings: React.FC<BoxProps> & ISettingsComposition = props => {
return (
<Box maxW={{ base: 'full', lg: '750px' }} {...props}>
<ChakraHeading as='h2' mt='12' fontSize={'2xl'} fontWeight={'bold'}>
Account Settings
</ChakraHeading>
<Divider my='5' />
<Stack divider={<StackDivider />} spacing={'5'}>
{props.children}
</Stack>
</Box>
);
};
Settings.Title = Title;
Settings.Entry = Entry;
Settings.EntryContent = EntryContent;
export { Settings };

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Box, BoxProps, Heading } from '@chakra-ui/react';
export interface ITitleProps {
title?: string | undefined;
}
const Title: React.FC<ITitleProps & BoxProps> = props => {
return (
<Box minW='3xs' {...props}>
<Heading as='h2' fontWeight={'semibold'} fontSize={'lg'}>
{props.title}
</Heading>
</Box>
);
};
export { Title };

View File

@ -0,0 +1,3 @@
import { Settings } from './Settings';
export { Settings };

View File

@ -1,30 +1,37 @@
import React from 'react'; import React from 'react';
import { BoxProps, Flex, FlexProps } from '@chakra-ui/react'; import { BoxProps, Flex, FlexProps } from '@chakra-ui/react';
import { Base } from './Base';
import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar'; import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar';
import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info'; import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info';
import { Settings } from './Settings';
import { ISettingsComposition } from './Settings/Settings';
interface IUserProfileComposition { interface IUserProfileComposition {
Base: React.FC<FlexProps>;
Avatar: React.FC<IAvatarProps>; Avatar: React.FC<IAvatarProps>;
ChatButton: React.FC<IChatButtonProps>; ChatButton: React.FC<IChatButtonProps>;
Info: React.FC<BoxProps>; Info: React.FC<BoxProps>;
Heading: React.FC<IHeadingProps>; Heading: React.FC<IHeadingProps>;
Details: React.FC<IDetailsProps>; Details: React.FC<IDetailsProps>;
About: React.FC<IAboutProps>; About: React.FC<IAboutProps>;
Settings: React.FC<BoxProps> & ISettingsComposition;
} }
const UserProfile: React.FC<FlexProps> & IUserProfileComposition = props => { const UserProfile: React.FC<FlexProps> & IUserProfileComposition = props => {
return ( return (
<Flex flexDir={{ base: 'column', md: 'row' }} {...props}> <Flex flexDir={{ base: 'column' }} {...props}>
{props.children} {props.children}
</Flex> </Flex>
); );
}; };
UserProfile.Base = Base;
UserProfile.Avatar = Avatar; UserProfile.Avatar = Avatar;
UserProfile.ChatButton = ChatButton; UserProfile.ChatButton = ChatButton;
UserProfile.Info = Info; UserProfile.Info = Info;
UserProfile.Heading = Heading; UserProfile.Heading = Heading;
UserProfile.Details = Details; UserProfile.Details = Details;
UserProfile.About = About; UserProfile.About = About;
UserProfile.Settings = Settings;
export { UserProfile }; export { UserProfile };

View File

@ -1,16 +1,16 @@
import React from 'react'; import React from 'react';
import { import {
Tabs,
TabList,
Tab,
TabPanels,
TabPanel,
Button,
FormControl,
FormHelperText,
Input,
InputGroup, InputGroup,
InputRightElement, InputRightElement,
Flex,
Switch,
FormControl,
FormLabel,
Input,
Textarea,
FormHelperText,
Select,
Button,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { Box, Stack } from '@chakra-ui/layout'; import { Box, Stack } from '@chakra-ui/layout';
@ -25,15 +25,13 @@ import { IPageProps } from '@educt/interfaces';
import { Page } from '@educt/components/PageElements'; import { Page } from '@educt/components/PageElements';
import { ProfileLoading } from './components'; import { ProfileLoading } from './components';
import { UserProfile } from '@educt/components/UserProfile'; import { UserProfile } from '@educt/components/UserProfile';
import { CourseList } from './components/CourseList';
import UpdateUserContactsForm from '@educt/pages/profile/components/UpdateUserContactsForm';
/** /**
* Hooks * Hooks
*/ */
import { useHistory } from 'react-router-dom';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { MdOutlineSave } from 'react-icons/md';
/** /**
* Profile page * Profile page
@ -42,7 +40,6 @@ const ProfilePage: React.FC<IPageProps> = () => {
const { const {
userStore: { me }, userStore: { me },
} = useRootStore(); } = useRootStore();
const history = useHistory();
if (me === null) return <ProfileLoading />; if (me === null) return <ProfileLoading />;
@ -51,76 +48,119 @@ const ProfilePage: React.FC<IPageProps> = () => {
<Page.Heading heading='My account' description='Update you contacts information or password here.' /> <Page.Heading heading='My account' description='Update you contacts information or password here.' />
<Page.Content> <Page.Content>
<UserProfile mt='14'> <UserProfile mt='14'>
<UserProfile.Base>
<UserProfile.Avatar name={me.fullname} /> <UserProfile.Avatar name={me.fullname} />
<UserProfile.Info> <UserProfile.Info>
<UserProfile.Heading fullname={me.fullname} roles={me.roles} /> <UserProfile.Heading fullname={me.fullname} roles={me.roles} />
<UserProfile.Details registered={me.created_at} lastLogin={me.last_login} /> <UserProfile.Details registered={me.created_at} lastLogin={me.last_login} />
<UserProfile.About about={me.about} /> <UserProfile.About about={me.about} />
</UserProfile.Info> </UserProfile.Info>
</UserProfile> </UserProfile.Base>
<Tabs mt='7'>
<TabList>
<Tab>Account information</Tab>
<Tab>My contacts</Tab>
{me.isStudent && <Tab>Available courses</Tab>}
</TabList>
<TabPanels mt='2'>
<TabPanel>
<form>
<Box borderRadius='md' borderWidth='1px' padding='5'>
<Stack spacing='5px'>
<FormControl isDisabled>
<FormHelperText color='gray.500'>First name</FormHelperText>
<Input type='text' value={me.first_name} size='md' variant='flushed' />
</FormControl>
<FormControl isDisabled> <UserProfile.Settings>
<FormHelperText color='gray.500'>Last name</FormHelperText> {/* Personal Info */}
<Input type='text' value={me.last_name} size='md' variant='flushed' /> <UserProfile.Settings.Entry>
<UserProfile.Settings.Title title={'Personal Info'} />
<UserProfile.Settings.EntryContent>
<Stack spacing={'6'}>
<FormControl>
<FormLabel>Fullname</FormLabel>
<Input />
</FormControl> </FormControl>
<FormControl isReadOnly>
<FormControl isDisabled> <FormLabel>Email</FormLabel>
<FormHelperText color='gray.500'>Email</FormHelperText> <InputGroup size='md'>
<InputGroup> <Input defaultValue={'serzh.yarkov@gmail.com'} />
<Input type='text' value={me.email} size='md' variant='flushed' /> <InputRightElement width='4.5rem' pr='0.5rem'>
<InputRightElement width='4rem'> <Button h='1.75rem' size='sm'>
<Button size='sm' h='1.90rem' w='full' onClick={() => history.push('/profile/change-email')}>
Edit
</Button>
</InputRightElement>
</InputGroup>
</FormControl>
<FormControl isDisabled>
<FormHelperText color='gray.500'>Password</FormHelperText>
<InputGroup>
<Input type='text' value='**********' size='md' variant='flushed' />
<InputRightElement width='5rem'>
<Button
size='sm'
h='1.90rem'
w='full'
onClick={() => history.push('/profile/change-password')}
>
Change Change
</Button> </Button>
</InputRightElement> </InputRightElement>
</InputGroup> </InputGroup>
</FormControl> </FormControl>
<FormControl>
<FormLabel>About</FormLabel>
<Textarea minH='130px' maxH='500px' />
<FormHelperText>Write description for your profile.</FormHelperText>
</FormControl>
</Stack> </Stack>
</UserProfile.Settings.EntryContent>
</UserProfile.Settings.Entry>
{/* Contacts */}
<UserProfile.Settings.Entry>
<UserProfile.Settings.Title title={'Contacts'} />
<UserProfile.Settings.EntryContent>
<Stack spacing={'6'}>
<FormControl>
<FormLabel>Phone</FormLabel>
<Input placeholder='+1 (234) 555-1234' />
</FormControl>
<FormControl>
<FormLabel>Twitter</FormLabel>
<Input placeholder='@sergeyyarkov' />
</FormControl>
<FormControl>
<FormLabel>Telegram</FormLabel>
<Input placeholder='@sergeyyarkov' />
</FormControl>
</Stack>
</UserProfile.Settings.EntryContent>
</UserProfile.Settings.Entry>
{/* Security */}
<UserProfile.Settings.Entry>
<UserProfile.Settings.Title title='Security' />
<UserProfile.Settings.EntryContent>
<FormControl isReadOnly>
<FormLabel>Password</FormLabel>
<InputGroup size='md'>
<Input defaultValue={'**********'} />
<InputRightElement width='4.5rem' pr='0.5rem'>
<Button h='1.75rem' size='sm'>
Change
</Button>
</InputRightElement>
</InputGroup>
</FormControl>
</UserProfile.Settings.EntryContent>
</UserProfile.Settings.Entry>
{/* Language */}
<UserProfile.Settings.Entry>
<UserProfile.Settings.Title title='Language' />
<UserProfile.Settings.EntryContent>
<FormControl>
<FormLabel>Display Language</FormLabel>
<Select>
<option value='en'>English</option>
</Select>
</FormControl>
</UserProfile.Settings.EntryContent>
</UserProfile.Settings.Entry>
{/* Night Theme */}
<UserProfile.Settings.Entry>
<UserProfile.Settings.Title title='Night Theme' />
<UserProfile.Settings.EntryContent>
<Flex mt={{ base: '6', md: '0' }}>
<Switch size='lg' />
</Flex>
</UserProfile.Settings.EntryContent>
</UserProfile.Settings.Entry>
<UserProfile.Settings.Entry>
<UserProfile.Settings.Title />
<UserProfile.Settings.EntryContent>
<Box>
<Button variant={'outline'} leftIcon={<MdOutlineSave />}>
Save
</Button>
</Box> </Box>
</form> </UserProfile.Settings.EntryContent>
</TabPanel> </UserProfile.Settings.Entry>
<TabPanel> </UserProfile.Settings>
<UpdateUserContactsForm contacts={me.contacts} /> </UserProfile>
</TabPanel>
{me.isStudent && (
<TabPanel>
<CourseList courses={me.courses} />
</TabPanel>
)}
</TabPanels>
</Tabs>
</Page.Content> </Page.Content>
</Page> </Page>
); );

View File

@ -33,6 +33,7 @@ const UserPage: React.FC<IPageProps> = () => {
<Page> <Page>
<Page.Content maxW={'900px'}> <Page.Content maxW={'900px'}>
<UserProfile> <UserProfile>
<UserProfile.Base>
<UserProfile.Avatar name={user.fullname} isOnline> <UserProfile.Avatar name={user.fullname} isOnline>
<UserProfile.ChatButton userid={user.id} /> <UserProfile.ChatButton userid={user.id} />
</UserProfile.Avatar> </UserProfile.Avatar>
@ -41,6 +42,7 @@ const UserPage: React.FC<IPageProps> = () => {
<UserProfile.Details registered={user.created_at} lastLogin={user.last_login} /> <UserProfile.Details registered={user.created_at} lastLogin={user.last_login} />
<UserProfile.About about={user.about} /> <UserProfile.About about={user.about} />
</UserProfile.Info> </UserProfile.Info>
</UserProfile.Base>
</UserProfile> </UserProfile>
</Page.Content> </Page.Content>
</Page> </Page>