mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 02:09:08 +03:00
refactor: user profile component decomposition
This commit is contained in:
parent
c80ef877c5
commit
20066ec1ba
20
src/components/UserProfile/Avatar/Avatar.tsx
Normal file
20
src/components/UserProfile/Avatar/Avatar.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Flex, Avatar as ChakraAvatar, AvatarBadge as ChakraAvatarBadge, useColorModeValue } from '@chakra-ui/react';
|
||||
import type { IAvatarProps } from '.';
|
||||
|
||||
const Avatar: React.FC<IAvatarProps> = props => {
|
||||
const { name, isOnline } = props;
|
||||
const onlineColor = isOnline ? 'green.500' : 'red.500';
|
||||
const borderColor = useColorModeValue('#E2E8F0', '#2D3748');
|
||||
|
||||
return (
|
||||
<Flex flexDir={'column'} alignItems={'center'}>
|
||||
<ChakraAvatar border={`4px solid ${borderColor}`} borderColor={borderColor} name={name} size='2xl'>
|
||||
<ChakraAvatarBadge boxSize='0.75em' bg={onlineColor} />
|
||||
</ChakraAvatar>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export { Avatar };
|
||||
19
src/components/UserProfile/Avatar/ChatButton.tsx
Normal file
19
src/components/UserProfile/Avatar/ChatButton.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Button, ButtonProps } from '@chakra-ui/react';
|
||||
import { MdOutlineMessage } from 'react-icons/md';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import type { IChatButtonProps } from '.';
|
||||
|
||||
const ChatButton: React.FC<IChatButtonProps & ButtonProps> = props => {
|
||||
const { userid } = props;
|
||||
const history = useHistory();
|
||||
const handleChat = () => history.push(`/chat/${userid}`);
|
||||
|
||||
return (
|
||||
<Button onClick={handleChat} mt='5' variant={'outline'} size={'sm'} leftIcon={<MdOutlineMessage />} {...props}>
|
||||
Chat
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export { ChatButton };
|
||||
13
src/components/UserProfile/Avatar/index.ts
Normal file
13
src/components/UserProfile/Avatar/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Avatar } from './Avatar';
|
||||
import { ChatButton } from './ChatButton';
|
||||
|
||||
export interface IAvatarProps {
|
||||
name: string;
|
||||
isOnline: boolean;
|
||||
}
|
||||
|
||||
export interface IChatButtonProps {
|
||||
userid: string;
|
||||
}
|
||||
|
||||
export { Avatar, ChatButton };
|
||||
16
src/components/UserProfile/Info/About.tsx
Normal file
16
src/components/UserProfile/Info/About.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from '@chakra-ui/react';
|
||||
import type { IAboutProps } from '.';
|
||||
|
||||
const About: React.FC<IAboutProps> = () => {
|
||||
return (
|
||||
<Box>
|
||||
<Text fontSize={'sm'}>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. A quibusdam sapiente quisquam vero harum ipsam officiis
|
||||
rerum. Cum, excepturi nulla.
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export { About };
|
||||
30
src/components/UserProfile/Info/Details.tsx
Normal file
30
src/components/UserProfile/Info/Details.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Flex, Text, Icon, Divider } from '@chakra-ui/react';
|
||||
import { CalendarIcon, RepeatClockIcon } from '@chakra-ui/icons';
|
||||
import type { IDetailsProps } from '.';
|
||||
|
||||
const Details: React.FC<IDetailsProps> = ({ registered, lastLogin }) => {
|
||||
return (
|
||||
<>
|
||||
<Flex my={'3'} justifyContent={{ base: 'center', md: 'flex-start' }}>
|
||||
<Flex>
|
||||
<Flex alignItems={'center'} mr='5'>
|
||||
<Icon as={CalendarIcon} color='gray.500' />
|
||||
<Text fontWeight={'medium'} fontSize={'sm'} color='gray.500' ml='3'>
|
||||
Joined: {new Date(registered).toLocaleDateString()}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr='5'>
|
||||
<Icon as={RepeatClockIcon} color='gray.500' />
|
||||
<Text fontWeight={'medium'} fontSize={'sm'} color='gray.500' ml='3'>
|
||||
Last login: {lastLogin}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider my='3' />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { Details };
|
||||
19
src/components/UserProfile/Info/Heading.tsx
Normal file
19
src/components/UserProfile/Info/Heading.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import UserBadge from '@educt/components/UserBadge';
|
||||
import { Flex, Box, Heading as ChakraHeading } from '@chakra-ui/react';
|
||||
import type { IHeadingProps } from '.';
|
||||
|
||||
const Heading: React.FC<IHeadingProps> = ({ fullname, roles }) => {
|
||||
return (
|
||||
<Flex justifyContent={{ base: 'center', md: 'flex-start' }} mt={{ base: '5', md: '0' }}>
|
||||
<ChakraHeading as='h2' fontSize={'xl'} fontWeight={'bold'}>
|
||||
{fullname} •
|
||||
</ChakraHeading>
|
||||
<Box>
|
||||
<UserBadge roles={roles} />
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export { Heading };
|
||||
12
src/components/UserProfile/Info/Info.tsx
Normal file
12
src/components/UserProfile/Info/Info.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
|
||||
const Info: React.FC = props => {
|
||||
return (
|
||||
<Box ml={{ base: '0', md: '10' }} {...props}>
|
||||
{props.children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export { Info };
|
||||
21
src/components/UserProfile/Info/index.ts
Normal file
21
src/components/UserProfile/Info/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { IUser } from '@educt/interfaces';
|
||||
import { Info } from './Info';
|
||||
import { Heading } from './Heading';
|
||||
import { Details } from './Details';
|
||||
import { About } from './About';
|
||||
|
||||
export interface IHeadingProps {
|
||||
fullname: string;
|
||||
roles: IUser['roles'];
|
||||
}
|
||||
|
||||
export interface IDetailsProps {
|
||||
registered: string;
|
||||
lastLogin: string;
|
||||
}
|
||||
|
||||
export interface IAboutProps {
|
||||
about: string;
|
||||
}
|
||||
|
||||
export { Info, Heading, Details, About };
|
||||
29
src/components/UserProfile/index.tsx
Normal file
29
src/components/UserProfile/index.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { BoxProps, Flex } from '@chakra-ui/react';
|
||||
import { IUser } from '@educt/interfaces';
|
||||
import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar';
|
||||
import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info';
|
||||
|
||||
interface IUserProfileComposition {
|
||||
Avatar: React.FC<IAvatarProps>;
|
||||
ChatButton: React.FC<IChatButtonProps>;
|
||||
Info: React.FC<BoxProps>;
|
||||
Heading: React.FC<IHeadingProps>;
|
||||
Details: React.FC<IDetailsProps>;
|
||||
About: React.FC<IAboutProps>;
|
||||
}
|
||||
|
||||
export const UserProfileContext = createContext<IUser | undefined>(undefined);
|
||||
|
||||
const UserProfile: React.FC & IUserProfileComposition = props => {
|
||||
return <Flex flexDir={{ base: 'column', md: 'row' }}>{props.children}</Flex>;
|
||||
};
|
||||
|
||||
UserProfile.Avatar = Avatar;
|
||||
UserProfile.ChatButton = ChatButton;
|
||||
UserProfile.Info = Info;
|
||||
UserProfile.Heading = Heading;
|
||||
UserProfile.Details = Details;
|
||||
UserProfile.About = About;
|
||||
|
||||
export { UserProfile };
|
||||
@ -1,29 +1,19 @@
|
||||
import React from 'react';
|
||||
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
||||
import { IPageProps } from '@educt/interfaces';
|
||||
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
||||
import { Redirect, useParams } from 'react-router-dom';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import LoadingPage from '@educt/components/LoadingPage';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Avatar,
|
||||
Text,
|
||||
Heading,
|
||||
useColorModeValue,
|
||||
Icon,
|
||||
AvatarBadge,
|
||||
Divider,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdOutlineMessage } from 'react-icons/md';
|
||||
import UserBadge from '@educt/components/UserBadge';
|
||||
import { CalendarIcon, RepeatClockIcon } from '@chakra-ui/icons';
|
||||
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
||||
import { UserProfile } from '@educt/components/UserProfile';
|
||||
import type { IPageProps } from '@educt/interfaces';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
||||
|
||||
const UserPage: React.FC<IPageProps> = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const { data: user, error, isLoading } = useFetchUser(id);
|
||||
const color = useColorModeValue('#E2E8F0', '#2D3748');
|
||||
|
||||
if (error?.response?.status === 404) return <Redirect to='/404' />;
|
||||
|
||||
@ -32,49 +22,20 @@ const UserPage: React.FC<IPageProps> = () => {
|
||||
return (
|
||||
<PageWrapper maxW='800px'>
|
||||
<PageContent>
|
||||
<Flex>
|
||||
<Flex flexDir={'column'} alignItems={'center'}>
|
||||
<Avatar border={`4px solid ${color}`} borderColor={color} name={user.fullname} size='2xl'>
|
||||
<AvatarBadge boxSize='0.75em' bg='green.500' />
|
||||
</Avatar>
|
||||
<Button mt='5' variant={'outline'} size={'sm'} leftIcon={<MdOutlineMessage />}>
|
||||
Message
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box ml='10'>
|
||||
<Flex>
|
||||
<Heading as='h2' fontSize={'xl'} fontWeight={'bold'}>
|
||||
{user.fullname} •
|
||||
</Heading>
|
||||
<Box>
|
||||
<UserBadge roles={user.roles} />
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex my={'3'}>
|
||||
<Flex>
|
||||
<Flex alignItems={'center'} mr='5'>
|
||||
<Icon as={CalendarIcon} color='gray.500' />
|
||||
<Text fontWeight={'medium'} fontSize={'sm'} color='gray.500' ml='1'>
|
||||
Registered: {new Date(user.created_at).toLocaleDateString()}
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mr='5'>
|
||||
<Icon as={RepeatClockIcon} color='gray.500' />
|
||||
<Text fontWeight={'medium'} fontSize={'sm'} color='gray.500' ml='1'>
|
||||
Last login: 3 min ago
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Divider my='3' />
|
||||
<Box>
|
||||
<Text fontSize={'sm'}>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. A quibusdam sapiente quisquam vero harum ipsam
|
||||
officiis rerum. Cum, excepturi nulla.
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
<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='3 days ago' />
|
||||
<UserProfile.About
|
||||
about={
|
||||
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla asperiores consectetur quasi minima veritatis id quisquam natus in nostrum quam?'
|
||||
}
|
||||
/>
|
||||
</UserProfile.Info>
|
||||
</UserProfile>
|
||||
</PageContent>
|
||||
</PageWrapper>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user