mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +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 React from 'react';
|
||||||
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { IPageProps } from '@educt/interfaces';
|
|
||||||
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
|
||||||
import { Redirect, useParams } from 'react-router-dom';
|
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
import {
|
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
||||||
Box,
|
import { UserProfile } from '@educt/components/UserProfile';
|
||||||
Button,
|
import type { IPageProps } from '@educt/interfaces';
|
||||||
Flex,
|
|
||||||
Avatar,
|
/**
|
||||||
Text,
|
* Hooks
|
||||||
Heading,
|
*/
|
||||||
useColorModeValue,
|
import { useParams } from 'react-router-dom';
|
||||||
Icon,
|
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
||||||
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';
|
|
||||||
|
|
||||||
const UserPage: React.FC<IPageProps> = () => {
|
const UserPage: React.FC<IPageProps> = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const { data: user, error, isLoading } = useFetchUser(id);
|
const { data: user, error, isLoading } = useFetchUser(id);
|
||||||
const color = useColorModeValue('#E2E8F0', '#2D3748');
|
|
||||||
|
|
||||||
if (error?.response?.status === 404) return <Redirect to='/404' />;
|
if (error?.response?.status === 404) return <Redirect to='/404' />;
|
||||||
|
|
||||||
@ -32,49 +22,20 @@ const UserPage: React.FC<IPageProps> = () => {
|
|||||||
return (
|
return (
|
||||||
<PageWrapper maxW='800px'>
|
<PageWrapper maxW='800px'>
|
||||||
<PageContent>
|
<PageContent>
|
||||||
<Flex>
|
<UserProfile>
|
||||||
<Flex flexDir={'column'} alignItems={'center'}>
|
<UserProfile.Avatar name={user.fullname} isOnline>
|
||||||
<Avatar border={`4px solid ${color}`} borderColor={color} name={user.fullname} size='2xl'>
|
<UserProfile.ChatButton userid={user.id} />
|
||||||
<AvatarBadge boxSize='0.75em' bg='green.500' />
|
</UserProfile.Avatar>
|
||||||
</Avatar>
|
<UserProfile.Info>
|
||||||
<Button mt='5' variant={'outline'} size={'sm'} leftIcon={<MdOutlineMessage />}>
|
<UserProfile.Heading fullname={user.fullname} roles={user.roles} />
|
||||||
Message
|
<UserProfile.Details registered={user.created_at} lastLogin='3 days ago' />
|
||||||
</Button>
|
<UserProfile.About
|
||||||
</Flex>
|
about={
|
||||||
<Box ml='10'>
|
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla asperiores consectetur quasi minima veritatis id quisquam natus in nostrum quam?'
|
||||||
<Flex>
|
}
|
||||||
<Heading as='h2' fontSize={'xl'} fontWeight={'bold'}>
|
/>
|
||||||
{user.fullname} •
|
</UserProfile.Info>
|
||||||
</Heading>
|
</UserProfile>
|
||||||
<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>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user