feat: added chat based layout

This commit is contained in:
Sergey Yarkov 2022-02-07 22:52:21 +03:00
parent 25bed8b538
commit 682cbc6ce9
11 changed files with 157 additions and 24 deletions

View File

@ -0,0 +1,12 @@
import React from 'react';
import { GridItem, GridItemProps } from '@chakra-ui/react';
const Sidebar: React.FC<GridItemProps> = props => {
return (
<GridItem pt='7rem' borderRightWidth={'1px'} width='400px' pr='6' {...props}>
{props.children}
</GridItem>
);
};
export { Sidebar };

View File

@ -0,0 +1,8 @@
import React from 'react';
import { GridItem, GridItemProps } from '@chakra-ui/react';
const Window: React.FC<GridItemProps> = props => {
return <GridItem {...props}>{props.children}</GridItem>;
};
export { Window };

View File

@ -0,0 +1,22 @@
import React from 'react';
import { Grid, GridItemProps, GridProps } from '@chakra-ui/react';
import { Sidebar } from './Sidebar';
import { Window } from './Window';
interface IChatComposition {
Sidebar: React.FC<GridItemProps>;
Window: React.FC<GridItemProps>;
}
const Chat: React.FC<GridProps> & IChatComposition = props => {
return (
<Grid gridTemplateColumns={'1fr 3fr'} h='100vh' {...props}>
{props.children}
</Grid>
);
};
Chat.Sidebar = Sidebar;
Chat.Window = Window;
export { Chat };

View File

@ -81,7 +81,7 @@ const Layout: React.FC<ILayoutProps> = ({ children, roles }) => {
</Header> </Header>
<NavDesktop /> <NavDesktop />
<Flex minH='100vh' justifyContent='center'> <Flex minH='100vh' justifyContent='center'>
<Box as='main' flex='1 0' maxW='85rem' ml={{ base: '0', lg: '20rem' }} mt='7rem' mb='1rem' pr='2rem' pl='2rem'> <Box as='main' flex='1 0' ml={{ base: '0', lg: '20rem' }}>
<Suspense fallback={<LoadingPage />}> <Suspense fallback={<LoadingPage />}>
{(() => { {(() => {
/** /**
@ -98,12 +98,12 @@ const Layout: React.FC<ILayoutProps> = ({ children, roles }) => {
if (!userContainRoles(me.roles, roles)) return <Redirect to='/404' />; if (!userContainRoles(me.roles, roles)) return <Redirect to='/404' />;
return children; return children;
} else {
/**
* Return children if "roles" prop is not defined
*/
return children;
} }
/**
* Return children if "roles" prop is not defined
*/
return children;
})()} })()}
</Suspense> </Suspense>
</Box> </Box>

View File

@ -3,7 +3,7 @@ import React from 'react';
const LoadingPage: React.FC = () => { const LoadingPage: React.FC = () => {
return ( return (
<Flex justifyContent='center' mt='40'> <Flex justifyContent='center' mt='20rem'>
<Box mt='auto' mb='auto' textAlign='center'> <Box mt='auto' mb='auto' textAlign='center'>
<Spinner thickness='4px' speed='0.55s' emptyColor='gray.200' color='blue.500' size='xl' /> <Spinner thickness='4px' speed='0.55s' emptyColor='gray.200' color='blue.500' size='xl' />
<Text mt='2' color='gray.500' userSelect='none'> <Text mt='2' color='gray.500' userSelect='none'>

View File

@ -8,7 +8,11 @@ interface IPageComposition {
} }
const Page: React.FC<BoxProps> & IPageComposition = props => { const Page: React.FC<BoxProps> & IPageComposition = props => {
return <Box {...props}>{props.children}</Box>; return (
<Box maxW='85rem' ml='auto' mr='auto' mt='7rem' mb='2rem' px='2rem' {...props}>
{props.children}
</Box>
);
}; };
interface IPageHeadingProps { interface IPageHeadingProps {
@ -27,16 +31,12 @@ Page.Heading = props => {
</Flex> </Flex>
{description && <Text mt='2'>{description}</Text>} {description && <Text mt='2'>{description}</Text>}
{props.children} {props.children}
<Divider mt='5' /> <Divider my='5' />
</Box> </Box>
); );
}; };
Page.Content = props => ( Page.Content = props => <Box {...props}>{props.children}</Box>;
<Box mt='4' {...props}>
{props.children}
</Box>
);
Page.Footer = props => ( Page.Footer = props => (
<Box mt='4' {...props}> <Box mt='4' {...props}>

View File

@ -50,7 +50,7 @@ const CoursePage: React.FC<IPageProps> = () => {
const handleEditCourse = () => history.push(`/courses/edit/${course.id}`); const handleEditCourse = () => history.push(`/courses/edit/${course.id}`);
return ( return (
<Page maxW='1200px'> <Page>
<Helmet> <Helmet>
<title>{course.title}</title> <title>{course.title}</title>
</Helmet> </Helmet>

View File

@ -24,7 +24,7 @@ export const CourseCardList: React.FC<SimpleGridProps> = props => (
mt='4' mt='4'
templateColumns={{ templateColumns={{
base: '1fr', base: '1fr',
md: 'repeat(auto-fill, minmax(320px, 1fr))', md: 'repeat(auto-fill, minmax(400px, 1fr))',
}} }}
spacing={'6'} spacing={'6'}
{...props} {...props}

View File

@ -1,16 +1,110 @@
import React from 'react'; import React from 'react';
import { Box, Heading } from '@chakra-ui/react'; import {
Avatar,
AvatarBadge,
Box,
Flex,
Heading,
WrapItem,
Text,
Stack,
Input,
Icon,
Button,
HStack,
InputGroup,
InputLeftElement,
useColorModeValue,
} from '@chakra-ui/react';
import { Link as ReactRouterLink } from 'react-router-dom';
import { Page } from '@educt/components/PageElements'; import { Page } from '@educt/components/PageElements';
import { IPageProps } from '@educt/interfaces'; import { IPageProps } from '@educt/interfaces';
import { MdOutlineChat } from 'react-icons/md';
import { LinkBox, LinkOverlay } from '@chakra-ui/react';
import { SearchIcon } from '@chakra-ui/icons';
import { Chat } from '@educt/components/Chat';
/** /**
* Messages page * Messages page
*/ */
const MessagesPage: React.FC<IPageProps> = () => { const MessagesPage: React.FC<IPageProps> = () => {
return ( return (
<Page> <Page maxW='100%' mt='0' mb='0'>
<Page.Heading heading='Messages' /> <Page.Content>
<Chat>
<Chat.Sidebar>
<Flex my='4' justifyContent={'space-between'} alignItems={'center'}>
<Heading as='h2' fontSize={'md'} fontWeight='medium' mr='9'>
Chats
</Heading>
<InputGroup maxW='230px' size='sm'>
<InputLeftElement pointerEvents='none'>
<SearchIcon color='gray.300' />
</InputLeftElement>
<Input type='text' placeholder='Search' borderRadius={'md'} />
</InputGroup>
</Flex>
<HStack>
<Button isActive variant={'ghost'} size='sm'>
All
</Button>
<Button colorScheme={'blue'} variant='ghost' size='sm'>
Unread
</Button>
</HStack>
<Stack mt='12' maxH='690px' overflowY={'scroll'} pr='3'>
{Array(20)
.fill(0)
.map((e, i) => i)
.map(n => (
<LinkBox
key={n}
py='5'
px='4'
bg={n === 2 ? useColorModeValue('gray.50', 'gray.700') : ''}
borderRadius={'lg'}
_hover={{ bg: `${useColorModeValue('gray.50', 'gray.700')}` }}
transition='all .1s'
>
<Flex flexDir={'row'} alignItems={'center'}>
<WrapItem>
<Avatar name='Sergey Yarkov' size='md'>
<AvatarBadge boxSize='0.90em' bg='green.500' />
</Avatar>
</WrapItem>
<Box ml='4'>
<LinkOverlay as={ReactRouterLink} to='/'>
<Text fontWeight={'semibold'}>
Sergey Yarkov <br />
<Text as={'span'} fontWeight='normal' fontSize={'sm'} color='gray.500'>
Sergey: hello, how you doing?
</Text>
</Text>
</LinkOverlay>
</Box>
<Box alignSelf={'flex-end'} ml='auto'>
<Text as='span' color='gray.500'>
2h
</Text>
</Box>
</Flex>
</LinkBox>
))}
</Stack>
</Chat.Sidebar>
<Chat.Window>
<Flex justifyContent={'center'} mt='20rem'>
<Box textAlign={'center'}>
<Icon as={MdOutlineChat} color='gray.600' w='16' h='16' />
<Text color='gray.500' userSelect={'none'}>
Select a chat to start messaging
</Text>
</Box>
</Flex>
</Chat.Window>
</Chat>
</Page.Content>
</Page> </Page>
); );
}; };

View File

@ -30,7 +30,7 @@ const ProfilePage: React.FC<IPageProps> = () => {
if (me === null) return <LoadingPage />; if (me === null) return <LoadingPage />;
return ( return (
<Page maxW={'900px'}> <Page>
<Page.Heading heading='My account' description='Update your profile information or password here.' /> <Page.Heading heading='My account' description='Update your profile information or password here.' />
<Page.Content> <Page.Content>
<UserProfile mt='14'> <UserProfile mt='14'>

View File

@ -11,10 +11,7 @@ export const theme = extendTheme({
width: '5px', width: '5px',
height: '5px', height: '5px',
}, },
'::-webkit-scrollbar-track': {
backgroundColor: 'gray.100',
borderRadius: '100px',
},
'::-webkit-scrollbar-thumb': { '::-webkit-scrollbar-thumb': {
background: 'blue.600', background: 'blue.600',
borderRadius: '100px', borderRadius: '100px',