mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: added chat based layout
This commit is contained in:
parent
25bed8b538
commit
682cbc6ce9
12
src/components/Chat/Sidebar.tsx
Normal file
12
src/components/Chat/Sidebar.tsx
Normal 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 };
|
||||||
8
src/components/Chat/Window.tsx
Normal file
8
src/components/Chat/Window.tsx
Normal 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 };
|
||||||
22
src/components/Chat/index.tsx
Normal file
22
src/components/Chat/index.tsx
Normal 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 };
|
||||||
@ -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 if "roles" prop is not defined
|
||||||
*/
|
*/
|
||||||
return children;
|
return children;
|
||||||
}
|
|
||||||
})()}
|
})()}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -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'>
|
||||||
|
|||||||
@ -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}>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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'>
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user