fix(dashboard): layout

This commit is contained in:
Lakhan Samani 2022-01-19 22:20:25 +05:30
parent 3b4d0d9769
commit ddda237178
11 changed files with 350 additions and 141 deletions

View File

@ -42,11 +42,7 @@ export default function App() {
<BrowserRouter>
<AuthorizerProvider
config={{
<<<<<<< HEAD
authorizerURL: window.location.origin,
=======
authorizerURL: globalState.authorizerURL,
>>>>>>> main
redirectURL: globalState.redirectURL,
}}
>

View File

@ -18,6 +18,7 @@ const theme = extendTheme({
styles: {
global: {
'html, body, #root': {
fontFamily: 'Avenir, Helvetica, Arial, sans-serif',
height: '100%',
},
},

View File

@ -0,0 +1,215 @@
import React, { ReactNode } from 'react';
import {
IconButton,
Avatar,
Box,
CloseButton,
Flex,
Image,
HStack,
VStack,
Icon,
useColorModeValue,
Link,
Text,
BoxProps,
FlexProps,
Menu,
MenuButton,
MenuItem,
MenuList,
} from '@chakra-ui/react';
import {
FiHome,
FiTrendingUp,
FiCompass,
FiStar,
FiSettings,
FiMenu,
FiUser,
FiUsers,
FiChevronDown,
} from 'react-icons/fi';
import { IconType } from 'react-icons';
import { ReactText } from 'react';
import { useMutation } from 'urql';
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { AdminLogout } from '../graphql/mutation';
interface LinkItemProps {
name: string;
icon: IconType;
route: string;
}
const LinkItems: Array<LinkItemProps> = [
{ name: 'Home', icon: FiHome, route: '/' },
{ name: 'Users', icon: FiUsers, route: '/users' },
{ name: 'Environment Variables', icon: FiSettings, route: '/environment' },
];
interface SidebarProps extends BoxProps {
onClose: () => void;
}
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
const { pathname } = useLocation();
return (
<Box
transition="3s ease"
bg={useColorModeValue('white', 'gray.900')}
borderRight="1px"
borderRightColor={useColorModeValue('gray.200', 'gray.700')}
w={{ base: 'full', md: 60 }}
pos="fixed"
h="full"
{...rest}
>
<Flex h="20" alignItems="center" mx="8" justifyContent="space-between">
<NavLink to="/">
<Flex alignItems="center">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="36px"
/>
<Text fontSize="large" ml="2" letterSpacing="3">
AUTHORIZER
</Text>
</Flex>
</NavLink>
<CloseButton display={{ base: 'flex', md: 'none' }} onClick={onClose} />
</Flex>
{LinkItems.map((link) => (
<NavLink key={link.name} to={link.route}>
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
>
{link.name}
</NavItem>
</NavLink>
))}
</Box>
);
};
interface NavItemProps extends FlexProps {
icon: IconType;
children: ReactText;
}
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
return (
<Link
href="#"
style={{ textDecoration: 'none' }}
_focus={{ boxShadow: 'none' }}
>
<Flex
align="center"
p="3"
mx="3"
borderRadius="md"
role="group"
cursor="pointer"
_hover={{
bg: 'blue.500',
color: 'white',
}}
{...rest}
>
{icon && (
<Icon
mr="4"
fontSize="16"
_groupHover={{
color: 'white',
}}
as={icon}
/>
)}
{children}
</Flex>
</Link>
);
};
interface MobileProps extends FlexProps {
onOpen: () => void;
}
export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
const [_, logout] = useMutation(AdminLogout);
const { setIsLoggedIn } = useAuthContext();
const navigate = useNavigate();
const handleLogout = async () => {
await logout();
setIsLoggedIn(false);
navigate('/', { replace: true });
};
return (
<Flex
ml={{ base: 0, md: 60 }}
px={{ base: 4, md: 4 }}
height="20"
position="fixed"
right="0"
left="0"
alignItems="center"
bg={useColorModeValue('white', 'gray.900')}
borderBottomWidth="1px"
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
justifyContent={{ base: 'space-between', md: 'flex-end' }}
{...rest}
>
<IconButton
display={{ base: 'flex', md: 'none' }}
onClick={onOpen}
variant="outline"
aria-label="open menu"
icon={<FiMenu />}
/>
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="36px"
display={{ base: 'flex', md: 'none' }}
/>
<HStack spacing={{ base: '0', md: '6' }}>
<Flex alignItems={'center'}>
<Menu>
<MenuButton
py={2}
transition="all 0.3s"
_focus={{ boxShadow: 'none' }}
>
<HStack>
<FiUser />
<VStack
display={{ base: 'none', md: 'flex' }}
alignItems="flex-start"
spacing="1px"
ml="2"
>
<Text fontSize="sm">Admin</Text>
</VStack>
<Box display={{ base: 'none', md: 'flex' }}>
<FiChevronDown />
</Box>
</HStack>
</MenuButton>
<MenuList
bg={useColorModeValue('white', 'gray.900')}
borderColor={useColorModeValue('gray.200', 'gray.700')}
>
<MenuItem onClick={handleLogout}>Sign out</MenuItem>
</MenuList>
</Menu>
</Flex>
</HStack>
</Flex>
);
};

View File

@ -1,78 +0,0 @@
import { Box, Image, Link, Text, Button } from '@chakra-ui/react';
import { NavLink, useLocation, useNavigate } from 'react-router-dom';
import React from 'react';
import { LOGO_URL } from '../constants';
import { useMutation } from 'urql';
import { AdminLogout } from '../graphql/mutation';
import { useAuthContext } from '../contexts/AuthContext';
const routes = [
{
route: '/users',
name: 'Users',
},
{
route: '/',
name: 'Environment Variable',
},
];
export const Sidebar = () => {
const { pathname } = useLocation();
const [_, logout] = useMutation(AdminLogout);
const { setIsLoggedIn } = useAuthContext();
const navigate = useNavigate();
const handleLogout = async () => {
await logout();
setIsLoggedIn(false);
navigate('/', { replace: true });
};
return (
<Box as="nav" h="100%">
<NavLink to="/">
<Box d="flex" alignItems="center" p="4" mt="4" mb="10">
<Image w="8" src={LOGO_URL} alt="" />
<Text
color="white"
casing="uppercase"
fontSize="1xl"
ml="3"
letterSpacing="1.5px"
>
Authorizer
</Text>
</Box>
</NavLink>
{routes.map(({ route, name }: any) => (
<Link
color={pathname === route ? 'blue.500' : 'white'}
transition="all ease-in 0.2s"
_hover={{ color: pathname === route ? 'blue.200' : 'whiteAlpha.700' }}
px="4"
py="2"
bg={pathname === route ? 'white' : ''}
fontWeight="bold"
display="block"
as={NavLink}
key={name}
to={route}
>
{name}
</Link>
))}
<Box
as="div"
w="100%"
position="absolute"
bottom="5"
display="flex"
justifyContent="center"
>
<Button onClick={handleLogout}>Logout</Button>
</Box>
</Box>
);
};

View File

@ -4,23 +4,28 @@ import { LOGO_URL } from '../constants';
export function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<Flex flexWrap="wrap" h="100%">
<Center h="100%" flex="2" bg="blue.500" flexDirection="column">
<Image src={LOGO_URL} alt="" />
<Text
color="white"
casing="uppercase"
fontSize="3xl"
mt="2"
letterSpacing="2.25px"
>
Authorizer
<Flex
flexWrap="wrap"
h="100%"
bg="gray.100"
alignItems="center"
justifyContent="center"
flexDirection="column"
>
<Flex alignItems="center">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="50"
/>
<Text fontSize="x-large" ml="3" letterSpacing="3">
AUTHORIZER
</Text>
</Center>
<Center h="100%" flex="2">
</Flex>
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
{children}
</Center>
</Box>
</Flex>
);
}

View File

@ -1,16 +1,39 @@
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import { Sidebar } from '../components/Sidebar';
import {
Box,
Drawer,
DrawerContent,
useDisclosure,
useColorModeValue,
} from '@chakra-ui/react';
import React, { ReactNode } from 'react';
import { Sidebar, MobileNav } from '../components/Menu';
export function DashboardLayout({ children }: { children: React.ReactNode }) {
export function DashboardLayout({ children }: { children: ReactNode }) {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Flex flexWrap="wrap" h="100%">
<Box w="72" bg="blue.500" flex="1" position="fixed" h="100vh">
<Sidebar />
</Box>
<Box as="main" flex="2" p="10" marginLeft="72">
<Box minH="100vh" bg={useColorModeValue('gray.100', 'gray.900')}>
<Sidebar
onClose={() => onClose}
display={{ base: 'none', md: 'block' }}
/>
<Drawer
autoFocus={false}
isOpen={isOpen}
placement="left"
onClose={onClose}
returnFocusOnClose={false}
onOverlayClick={onClose}
size="full"
>
<DrawerContent>
<Sidebar onClose={onClose} />
</DrawerContent>
</Drawer>
{/* mobilenav */}
<MobileNav onOpen={onOpen} />
<Box ml={{ base: 0, md: 60 }} p="4" pt="24">
{children}
</Box>
</Flex>
</Box>
);
}

View File

@ -5,6 +5,8 @@ import {
Input,
useToast,
VStack,
Text,
Divider,
} from '@chakra-ui/react';
import React, { useEffect } from 'react';
import { useMutation } from 'urql';
@ -15,7 +17,7 @@ import { useNavigate } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { capitalizeFirstLetter, hasAdminSecret } from '../utils';
export const Auth = () => {
export default function Auth() {
const [loginResult, login] = useMutation(AdminLogin);
const [signUpResult, signup] = useMutation(AdminSignup);
const { setIsLoggedIn } = useAuthContext();
@ -40,8 +42,8 @@ export const Auth = () => {
(isLogin ? login : signup)({
secret: formValues['admin-secret'],
}).then((res) => {
setIsLoggedIn(true);
if (res.data) {
setIsLoggedIn(true);
navigate('/', { replace: true });
}
});
@ -64,12 +66,24 @@ export const Auth = () => {
return (
<AuthLayout>
<Text
fontSize="large"
textAlign="center"
color="gray.600"
fontWeight="bold"
mb="2"
>
Hi there 👋 <br />
</Text>
<Text fontSize="large" textAlign="center" color="gray.500" mb="8">
Welcome to Authorizer Administrative Dashboard
</Text>
<form onSubmit={handleSubmit}>
<VStack spacing="2.5" justify="space-between">
<VStack spacing="5" justify="space-between">
<FormControl isRequired>
<FormLabel htmlFor="admin-secret">
{isLogin ? 'Enter' : 'Setup'} Admin Secret
</FormLabel>
{/* <FormLabel htmlFor="admin-secret">
{isLogin ? 'Enter' : 'Configure'} Admin Secret
</FormLabel> */}
<Input
size="lg"
id="admin-secret"
@ -88,8 +102,23 @@ export const Auth = () => {
>
{isLogin ? 'Login' : 'Sign up'}
</Button>
{isLogin ? (
<Text color="gray.600" fontSize="sm">
<b>Note:</b> In case if you have forgot your admin secret, you can
reset it by updating <code>ADMIN_SECRET</code> environment
variable. For more information, please refer to the{' '}
<a href="https://docs.authorizer.dev/core/env/">documentation</a>.
</Text>
) : (
<Text color="gray.600" fontSize="sm">
<b>Note:</b> You can also configure admin secret by setting{' '}
<code>ADMIN_SECRET</code> environment variable. For more
information, please refer to the{' '}
<a href="https://docs.authorizer.dev/core/env/">documentation</a>.
</Text>
)}
</VStack>
</form>
</AuthLayout>
);
};
}

View File

@ -0,0 +1,6 @@
import { Box } from '@chakra-ui/react';
import React from 'react';
export default function Environment() {
return <Box>Welcome to Environment Page</Box>;
}

View File

@ -1,6 +1,11 @@
import { Box } from "@chakra-ui/react";
import React from "react";
import { Box } from '@chakra-ui/react';
import React from 'react';
export function Home() {
return <Box>Welcome to Authorizer dashboard!</Box>;
export default function Home() {
return (
<Box>
Hi there 👋 <br />
Welcome to Authorizer administrative dashboard!
</Box>
);
}

View File

@ -1,6 +1,6 @@
import { Box } from "@chakra-ui/react";
import React from "react";
import { Box } from '@chakra-ui/react';
import React from 'react';
export function Users() {
return <Box>users</Box>;
export default function Users() {
return <Box>Welcome to Users Page</Box>;
}

View File

@ -1,34 +1,41 @@
import React from 'react';
import React, { lazy, Suspense } from 'react';
import { Outlet, Route, Routes } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { DashboardLayout } from '../layouts/DashboardLayout';
import { Auth } from '../pages/Auth';
import { Home } from '../pages/Home';
import { Users } from '../pages/Users';
const Auth = lazy(() => import('../pages/Auth'));
const Environment = lazy(() => import('../pages/Environment'));
const Home = lazy(() => import('../pages/Home'));
const Users = lazy(() => import('../pages/Users'));
export const AppRoutes = () => {
const { isLoggedIn } = useAuthContext();
if (isLoggedIn) {
return (
<Routes>
<Route
element={
<DashboardLayout>
<Outlet />
</DashboardLayout>
}
>
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />} />
</Route>
</Routes>
<Suspense fallback={<></>}>
<Routes>
<Route
element={
<DashboardLayout>
<Outlet />
</DashboardLayout>
}
>
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />} />
<Route path="environment" element={<Environment />} />
</Route>
</Routes>
</Suspense>
);
}
return (
<Routes>
<Route path="/" element={<Auth />} />
</Routes>
<Suspense fallback={<></>}>
<Routes>
<Route path="/" element={<Auth />} />
</Routes>
</Suspense>
);
};