mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 02:09:08 +03:00
refactor: profile layout
This commit is contained in:
parent
a14ed5eff9
commit
ab662a50f6
12
src/components/UserProfile/Base/Base.tsx
Normal file
12
src/components/UserProfile/Base/Base.tsx
Normal 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 };
|
||||
3
src/components/UserProfile/Base/index.ts
Normal file
3
src/components/UserProfile/Base/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Base } from './Base';
|
||||
|
||||
export { Base };
|
||||
8
src/components/UserProfile/Settings/Entry.tsx
Normal file
8
src/components/UserProfile/Settings/Entry.tsx
Normal 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 };
|
||||
12
src/components/UserProfile/Settings/EntryContent.tsx
Normal file
12
src/components/UserProfile/Settings/EntryContent.tsx
Normal 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 };
|
||||
31
src/components/UserProfile/Settings/Settings.tsx
Normal file
31
src/components/UserProfile/Settings/Settings.tsx
Normal 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 };
|
||||
18
src/components/UserProfile/Settings/Title.tsx
Normal file
18
src/components/UserProfile/Settings/Title.tsx
Normal 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 };
|
||||
3
src/components/UserProfile/Settings/index.ts
Normal file
3
src/components/UserProfile/Settings/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Settings } from './Settings';
|
||||
|
||||
export { Settings };
|
||||
@ -1,30 +1,37 @@
|
||||
import React from 'react';
|
||||
import { BoxProps, Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { Base } from './Base';
|
||||
import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar';
|
||||
import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info';
|
||||
import { Settings } from './Settings';
|
||||
import { ISettingsComposition } from './Settings/Settings';
|
||||
|
||||
interface IUserProfileComposition {
|
||||
Base: React.FC<FlexProps>;
|
||||
Avatar: React.FC<IAvatarProps>;
|
||||
ChatButton: React.FC<IChatButtonProps>;
|
||||
Info: React.FC<BoxProps>;
|
||||
Heading: React.FC<IHeadingProps>;
|
||||
Details: React.FC<IDetailsProps>;
|
||||
About: React.FC<IAboutProps>;
|
||||
Settings: React.FC<BoxProps> & ISettingsComposition;
|
||||
}
|
||||
|
||||
const UserProfile: React.FC<FlexProps> & IUserProfileComposition = props => {
|
||||
return (
|
||||
<Flex flexDir={{ base: 'column', md: 'row' }} {...props}>
|
||||
<Flex flexDir={{ base: 'column' }} {...props}>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
UserProfile.Base = Base;
|
||||
UserProfile.Avatar = Avatar;
|
||||
UserProfile.ChatButton = ChatButton;
|
||||
UserProfile.Info = Info;
|
||||
UserProfile.Heading = Heading;
|
||||
UserProfile.Details = Details;
|
||||
UserProfile.About = About;
|
||||
UserProfile.Settings = Settings;
|
||||
|
||||
export { UserProfile };
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanels,
|
||||
TabPanel,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
Flex,
|
||||
Switch,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Textarea,
|
||||
FormHelperText,
|
||||
Select,
|
||||
Button,
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Stack } from '@chakra-ui/layout';
|
||||
|
||||
@ -25,15 +25,13 @@ import { IPageProps } from '@educt/interfaces';
|
||||
import { Page } from '@educt/components/PageElements';
|
||||
import { ProfileLoading } from './components';
|
||||
import { UserProfile } from '@educt/components/UserProfile';
|
||||
import { CourseList } from './components/CourseList';
|
||||
import UpdateUserContactsForm from '@educt/pages/profile/components/UpdateUserContactsForm';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { observer } from 'mobx-react';
|
||||
import { MdOutlineSave } from 'react-icons/md';
|
||||
|
||||
/**
|
||||
* Profile page
|
||||
@ -42,7 +40,6 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
||||
const {
|
||||
userStore: { me },
|
||||
} = useRootStore();
|
||||
const history = useHistory();
|
||||
|
||||
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.Content>
|
||||
<UserProfile mt='14'>
|
||||
<UserProfile.Avatar name={me.fullname} />
|
||||
<UserProfile.Info>
|
||||
<UserProfile.Heading fullname={me.fullname} roles={me.roles} />
|
||||
<UserProfile.Details registered={me.created_at} lastLogin={me.last_login} />
|
||||
<UserProfile.About about={me.about} />
|
||||
</UserProfile.Info>
|
||||
</UserProfile>
|
||||
<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>
|
||||
<UserProfile.Base>
|
||||
<UserProfile.Avatar name={me.fullname} />
|
||||
<UserProfile.Info>
|
||||
<UserProfile.Heading fullname={me.fullname} roles={me.roles} />
|
||||
<UserProfile.Details registered={me.created_at} lastLogin={me.last_login} />
|
||||
<UserProfile.About about={me.about} />
|
||||
</UserProfile.Info>
|
||||
</UserProfile.Base>
|
||||
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>Last name</FormHelperText>
|
||||
<Input type='text' value={me.last_name} size='md' variant='flushed' />
|
||||
</FormControl>
|
||||
<UserProfile.Settings>
|
||||
{/* Personal Info */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title={'Personal Info'} />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Stack spacing={'6'}>
|
||||
<FormControl>
|
||||
<FormLabel>Fullname</FormLabel>
|
||||
<Input />
|
||||
</FormControl>
|
||||
<FormControl isReadOnly>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<InputGroup size='md'>
|
||||
<Input defaultValue={'serzh.yarkov@gmail.com'} />
|
||||
<InputRightElement width='4.5rem' pr='0.5rem'>
|
||||
<Button h='1.75rem' size='sm'>
|
||||
Change
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>About</FormLabel>
|
||||
<Textarea minH='130px' maxH='500px' />
|
||||
<FormHelperText>Write description for your profile.</FormHelperText>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>Email</FormHelperText>
|
||||
<InputGroup>
|
||||
<Input type='text' value={me.email} size='md' variant='flushed' />
|
||||
<InputRightElement width='4rem'>
|
||||
<Button size='sm' h='1.90rem' w='full' onClick={() => history.push('/profile/change-email')}>
|
||||
Edit
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
{/* 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>
|
||||
|
||||
<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
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
{/* 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>
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<UpdateUserContactsForm contacts={me.contacts} />
|
||||
</TabPanel>
|
||||
{me.isStudent && (
|
||||
<TabPanel>
|
||||
<CourseList courses={me.courses} />
|
||||
</TabPanel>
|
||||
)}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
</UserProfile.Settings>
|
||||
</UserProfile>
|
||||
</Page.Content>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@ -33,14 +33,16 @@ const UserPage: React.FC<IPageProps> = () => {
|
||||
<Page>
|
||||
<Page.Content maxW={'900px'}>
|
||||
<UserProfile>
|
||||
<UserProfile.Avatar name={user.fullname} isOnline>
|
||||
<UserProfile.ChatButton userid={user.id} />
|
||||
</UserProfile.Avatar>
|
||||
<UserProfile.Info>
|
||||
<UserProfile.Heading fullname={user.fullname} roles={user.roles} />
|
||||
<UserProfile.Details registered={user.created_at} lastLogin={user.last_login} />
|
||||
<UserProfile.About about={user.about} />
|
||||
</UserProfile.Info>
|
||||
<UserProfile.Base>
|
||||
<UserProfile.Avatar name={user.fullname} isOnline>
|
||||
<UserProfile.ChatButton userid={user.id} />
|
||||
</UserProfile.Avatar>
|
||||
<UserProfile.Info>
|
||||
<UserProfile.Heading fullname={user.fullname} roles={user.roles} />
|
||||
<UserProfile.Details registered={user.created_at} lastLogin={user.last_login} />
|
||||
<UserProfile.About about={user.about} />
|
||||
</UserProfile.Info>
|
||||
</UserProfile.Base>
|
||||
</UserProfile>
|
||||
</Page.Content>
|
||||
</Page>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user