✨ Add Sidebar to new frontend (#587)
This commit is contained in:
48
src/new-frontend/src/components/Sidebar.tsx
Normal file
48
src/new-frontend/src/components/Sidebar.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Box, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerOverlay, Flex, IconButton, Image, useDisclosure } from '@chakra-ui/react';
|
||||||
|
import { FiMenu } from 'react-icons/fi';
|
||||||
|
|
||||||
|
import Logo from "../assets/images/fastapi-logo.png";
|
||||||
|
import SidebarItems from './SidebarItems';
|
||||||
|
import UserInfo from './UserInfo';
|
||||||
|
|
||||||
|
|
||||||
|
const Sidebar: React.FC = () => {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Mobile */}
|
||||||
|
<IconButton onClick={onOpen} display={{ base: 'flex', md: 'none' }} aria-label="Open Menu" position="absolute" fontSize='20px' m={4} icon={<FiMenu />} />
|
||||||
|
<Drawer isOpen={isOpen} placement="left" onClose={onClose}>
|
||||||
|
<DrawerOverlay />
|
||||||
|
<DrawerContent bg="gray.100" maxW="250px">
|
||||||
|
<DrawerCloseButton />
|
||||||
|
<DrawerBody py={8}>
|
||||||
|
<Flex flexDir="column" justify="space-between" h="100%">
|
||||||
|
<Box>
|
||||||
|
<Image src={Logo} alt="Logo" />
|
||||||
|
<SidebarItems />
|
||||||
|
</Box>
|
||||||
|
<UserInfo />
|
||||||
|
</Flex>
|
||||||
|
</DrawerBody>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
|
||||||
|
{/* Desktop */}
|
||||||
|
<Box bg="white" p={3} h="100vh" position="sticky" top="0" display={{ base: 'none', md: 'flex' }}>
|
||||||
|
<Flex flexDir="column" justify="space-between" bg="gray.100" p={6} borderRadius={12}>
|
||||||
|
<Box>
|
||||||
|
<Image src={Logo} alt="Logo" w="180px" maxW="2xs" />
|
||||||
|
<SidebarItems />
|
||||||
|
</Box>
|
||||||
|
<UserInfo />
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sidebar;
|
37
src/new-frontend/src/components/SidebarItems.tsx
Normal file
37
src/new-frontend/src/components/SidebarItems.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Flex, Icon, Text } from '@chakra-ui/react';
|
||||||
|
import { FiBriefcase, FiHome, FiLogOut, FiUser, FiUsers } from 'react-icons/fi';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ icon: FiHome, title: 'Dashboard', path: "/" },
|
||||||
|
{ icon: FiUser, title: 'Profile', path: "/profile" },
|
||||||
|
{ icon: FiBriefcase, title: 'Items', path: "/items" },
|
||||||
|
{ icon: FiUsers, title: 'Admin', path: "/admin" },
|
||||||
|
{ icon: FiLogOut, title: 'Log out' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const SidebarItems: React.FC = () => {
|
||||||
|
const listItems = items.map((item) => (
|
||||||
|
<Flex w="100%" p={2} key={item.title} _hover={{
|
||||||
|
background: "gray.200",
|
||||||
|
borderRadius: "12px",
|
||||||
|
}}>
|
||||||
|
<Link to={item.path || "/"}>
|
||||||
|
<Flex color="teal.500" gap={4}>
|
||||||
|
<Icon as={item.icon} alignSelf="center" />
|
||||||
|
<Text>{item.title}</Text>
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{listItems}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarItems;
|
40
src/new-frontend/src/components/UserInfo.tsx
Normal file
40
src/new-frontend/src/components/UserInfo.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { Avatar, Flex, Skeleton, Text } from '@chakra-ui/react';
|
||||||
|
import { FaUserAstronaut } from 'react-icons/fa';
|
||||||
|
|
||||||
|
import { UserOut, UsersService } from '../client';
|
||||||
|
|
||||||
|
const UserInfo: React.FC = () => {
|
||||||
|
const [userData, setUserData] = useState<UserOut>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchUserData = async () => {
|
||||||
|
try {
|
||||||
|
const userResponse = await UsersService.readUserMe();
|
||||||
|
setUserData(userResponse);
|
||||||
|
} catch (error) {
|
||||||
|
// TODO: Handle error to give feedback to the user
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchUserData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{userData ? (
|
||||||
|
<Flex gap={2} maxW="180px">
|
||||||
|
<Avatar icon={<FaUserAstronaut fontSize="18px" />} size='sm' alignSelf="center" />
|
||||||
|
{/* TODO: Conditional tooltip based on email length */}
|
||||||
|
<Text color='gray' alignSelf={"center"} noOfLines={1} fontSize="14px">{userData.email}</Text>
|
||||||
|
</Flex>
|
||||||
|
) :
|
||||||
|
<Skeleton height='20px' />
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserInfo;
|
14
src/new-frontend/src/pages/Layout.tsx
Normal file
14
src/new-frontend/src/pages/Layout.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Sidebar from '../components/Sidebar';
|
||||||
|
|
||||||
|
import { Flex } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<Flex maxW="large" h="auto" position="relative">
|
||||||
|
<Sidebar />
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
Reference in New Issue
Block a user