Skip to content

Commit 0790b17

Browse files
committed
feat(authentication): Added login, routes and navigation
1 parent 1175711 commit 0790b17

File tree

23 files changed

+6171
-6699
lines changed

23 files changed

+6171
-6699
lines changed

app/components/Navbar/index.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, {
2+
use,
3+
useCallback,
4+
} from 'react';
5+
import { useNavigate } from 'react-router';
6+
import {
7+
Button,
8+
DropdownMenu,
9+
Heading,
10+
} from '@ifrc-go/ui';
11+
import { gql } from 'urql';
12+
13+
import UserContext from '#contexts/UserContext';
14+
import { useLogoutMutation } from '#generated/types/graphql';
15+
16+
import styles from './styles.module.css';
17+
18+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
19+
const LOGOUT = gql`
20+
mutation Logout {
21+
logout
22+
}
23+
`;
24+
25+
function Navbar() {
26+
const { user, setUser } = use(UserContext);
27+
const navigate = useNavigate();
28+
29+
const [{ fetching }, triggerLogout] = useLogoutMutation();
30+
31+
const handleLogout = useCallback(async () => {
32+
const res = await triggerLogout({});
33+
const logoutResponse = res.data?.logout;
34+
if (logoutResponse) {
35+
setUser(undefined);
36+
navigate('/login');
37+
}
38+
}, [navigate, triggerLogout, setUser]);
39+
40+
return (
41+
<nav className={styles.navbar}>
42+
<Heading className={styles.title}>NRCS</Heading>
43+
<DropdownMenu
44+
variant="tertiary"
45+
label={(
46+
<div className={styles.userInfo}>
47+
<Heading level={5}>
48+
{user?.firstName}
49+
{' '}
50+
{user?.lastName}
51+
</Heading>
52+
<Heading level={6}>
53+
Admin
54+
</Heading>
55+
</div>
56+
)}
57+
>
58+
<React.Fragment key=".0">
59+
<Button
60+
name="logout"
61+
variant="tertiary"
62+
className={styles.dropdownOption}
63+
onClick={handleLogout}
64+
>
65+
{fetching ? 'Logging out' : 'Logout'}
66+
</Button>
67+
</React.Fragment>
68+
</DropdownMenu>
69+
</nav>
70+
);
71+
}
72+
73+
export default Navbar;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.navbar {
2+
display: flex;
3+
justify-content: space-between;
4+
padding: var(--go-ui-spacing-md) var(--go-ui-spacing-lg);
5+
border-bottom: var(--go-ui-width-separator-thin) solid
6+
var(--go-ui-color-primary-red);
7+
background-color: var(--go-ui-color-white);
8+
flex-shrink: 0;
9+
animation: slide-down var(--go-ui-duration-animation-medium) ease-in
10+
forwards;
11+
.title {
12+
font-size: var(--go-ui-font-size-3xl);
13+
color: var(--go-ui-color-red);
14+
}
15+
.userInfo {
16+
display: flex;
17+
align-items: start;
18+
flex-direction: column;
19+
}
20+
}
21+
.dropdown-option {
22+
padding: var(--go-ui-spacing-xs);
23+
margin-left: var(--go-ui-spacing-md);
24+
cursor: pointer;
25+
&:hover {
26+
color: var(--go-ui-color-primary-red);
27+
}
28+
}
29+
.test {
30+
width: 150px;
31+
}
32+
33+
@keyframes slide-down {
34+
0% {
35+
transform: translateY(-0.25rem);
36+
opacity: 0;
37+
}
38+
100% {
39+
transform: translateY(0);
40+
opacity: 1;
41+
}
42+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import {
2+
ReactNode,
3+
useState,
4+
} from 'react';
5+
import {
6+
IoChevronDownOutline,
7+
IoChevronUpOutline,
8+
} from 'react-icons/io5';
9+
import { NavLink } from 'react-router';
10+
import { Button } from '@ifrc-go/ui';
11+
import { _cs } from '@togglecorp/fujs';
12+
13+
import styles from './styles.module.css';
14+
15+
export interface NavigationItem {
16+
title: string;
17+
to?: string;
18+
icon?: ReactNode;
19+
variant: 'root' | 'group' | 'leaf';
20+
children?: NavigationItem[];
21+
22+
}
23+
function Navigation({ navigationItem }: { navigationItem: NavigationItem[] }) {
24+
const [openIndexes, setOpenIndexes] = useState(
25+
navigationItem.map((_, i) => i),
26+
);
27+
28+
const toggleAccordion = (index: number) => {
29+
setOpenIndexes((prev) => (prev.includes(index)
30+
? prev.filter((i) => i !== index)
31+
: [...prev, index]));
32+
};
33+
34+
return (
35+
<nav className={styles.nav}>
36+
{navigationItem.map((item, index) => {
37+
const isOpen = openIndexes.includes(index);
38+
return (
39+
<div
40+
key={item.title}
41+
className={_cs(
42+
styles[item.variant ?? 'leaf'],
43+
)}
44+
>
45+
<Button
46+
name={item.title}
47+
type="button"
48+
className={styles.navHeaderContainer}
49+
childrenContainerClassName={styles.navHeader}
50+
onClick={() => toggleAccordion(index)}
51+
variant="tertiary"
52+
icons={item.icon}
53+
>
54+
{item.title}
55+
{isOpen ? <IoChevronUpOutline /> : <IoChevronDownOutline />}
56+
</Button>
57+
{isOpen && (
58+
<div
59+
className={_cs(
60+
styles.navContent,
61+
styles[item.variant ?? 'leaf'],
62+
)}
63+
>
64+
{item.children && item.children.map((child) => {
65+
if (!child.to && child.children) {
66+
return (
67+
<Navigation
68+
key={child.title}
69+
navigationItem={[child]}
70+
/>
71+
);
72+
}
73+
return (
74+
<NavLink
75+
key={child.to}
76+
to={child.to ?? ''}
77+
className={({ isActive }) => _cs(
78+
styles.routeLink,
79+
isActive && styles.activeRoute,
80+
styles[child.variant ?? 'leaf'],
81+
82+
)}
83+
>
84+
{child.title}
85+
</NavLink>
86+
);
87+
})}
88+
89+
</div>
90+
)}
91+
</div>
92+
);
93+
})}
94+
</nav>
95+
);
96+
}
97+
98+
export default Navigation;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
.nav {
2+
height: 100%;
3+
.nav {
4+
border: none;
5+
}
6+
border-right: 2px solid var(--go-ui-color-gray-20);
7+
.navHeaderContainer {
8+
background-color: var(--go-ui-color-gray-10);
9+
padding: var(--go-ui-spacing-lg) var(--go-ui-spacing-2xl);
10+
width: 100%;
11+
border-bottom: 2px solid var(--go-ui-color-gray-20);
12+
.navHeader {
13+
display: flex;
14+
justify-content: space-between;
15+
}
16+
}
17+
.navContent {
18+
display: flex;
19+
flex-direction: column;
20+
.routeLink {
21+
border-bottom: 2px solid var(--go-ui-color-gray-20);
22+
&:hover {
23+
color: var(--go-ui-color-primary-red);
24+
}
25+
padding: var(--go-ui-spacing-lg) var(--go-ui-spacing-2xl);
26+
}
27+
28+
.activeRoute {
29+
color: var(--go-ui-color-primary-red);
30+
text-decoration: underline;
31+
}
32+
}
33+
.group {
34+
.navHeaderContainer {
35+
background: none;
36+
font-weight: var(--go-ui-font-weight-normal);
37+
}
38+
.leaf {
39+
padding-left: var(--go-ui-spacing-3xl);
40+
}
41+
}
42+
}

app/components/Page/index.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { RefObject } from 'react';
2+
import { _cs } from '@togglecorp/fujs';
3+
4+
import styles from './styles.module.css';
5+
6+
interface Props {
7+
className?: string;
8+
children?: React.ReactNode;
9+
elementRef?: RefObject<HTMLDivElement>
10+
leftPaneContent?: React.ReactNode;
11+
leftPaneContainerClassName?: string;
12+
}
13+
function Page(props: Props) {
14+
const {
15+
className,
16+
children,
17+
elementRef,
18+
leftPaneContent,
19+
leftPaneContainerClassName,
20+
} = props;
21+
22+
return (
23+
<div className={_cs(className, styles.page)} ref={elementRef}>
24+
{leftPaneContent && (
25+
<div className={_cs(leftPaneContainerClassName, styles.leftPane)}>
26+
{leftPaneContent}
27+
</div>
28+
)}
29+
{children}
30+
</div>
31+
);
32+
}
33+
34+
export default Page;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.page {
2+
display: flex;
3+
flex-direction: row;
4+
flex-grow: 1;
5+
overflow: hidden;
6+
7+
.leftPane {
8+
display: flex;
9+
flex-direction: column;
10+
box-shadow: 0px 4px 4px 0px rgba(227, 227, 227, 0.25);
11+
background-color: var(--go-ui-color-white);
12+
gap: var(--go-ui-spacing-md);
13+
}
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import styles from './styles.module.css';
2+
3+
interface Props {
4+
children?: React.ReactNode;
5+
}
6+
7+
function PreloadMessage(props: Props) {
8+
const { children } = props;
9+
10+
return (
11+
<div className={styles.preloadMessage}>
12+
{children}
13+
</div>
14+
);
15+
}
16+
17+
export default PreloadMessage;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.preload-message {
2+
display: flex;
3+
align-items: center;
4+
flex-direction: column;
5+
flex-grow: 1;
6+
justify-content: center;
7+
background-color: var(--color-background);
8+
height: 100vh;
9+
text-align: center;
10+
font-size: var(--font-size-lg);
11+
}

0 commit comments

Comments
 (0)