Skip to content
Snippets Groups Projects
Commit dac711d5 authored by Cross, Liam (UG - Comp Sci & Elec Eng)'s avatar Cross, Liam (UG - Comp Sci & Elec Eng)
Browse files

Merge branch 'LC/auth' into 'main'

Added Dummy Auth

See merge request ma03081/COM3014!11
parents 61cd66a1 6a943b69
No related branches found
No related tags found
No related merge requests found
Showing
with 242 additions and 27 deletions
import { Outlet } from 'react-router-dom';
import AuthProvider from './providers/AuthProvider';
import Header from './components/Header/Header'; import Header from './components/Header/Header';
import Footer from './components/Footer/Footer'; import Footer from './components/Footer/Footer';
import { Outlet } from 'react-router-dom';
import './App.scss'; import './App.scss';
function App() { function App() {
return ( return (
<> <>
<div className='wrapper'> <AuthProvider>
<Header></Header> <div className='wrapper'>
<div className='main'> <Header></Header>
<Outlet></Outlet> <div className='main'>
</div> <Outlet></Outlet>
<div className='footer-wrapper'> </div>
<Footer></Footer> <div className='footer-wrapper'>
<Footer></Footer>
</div>
</div> </div>
</div> </AuthProvider>
</> </>
); );
} }
export default App; export default App;
\ No newline at end of file
...@@ -97,7 +97,7 @@ function CustomerDashboard() { ...@@ -97,7 +97,7 @@ function CustomerDashboard() {
</div> </div>
<div className='flights'> <div className='flights'>
<div> <div className='flex-row'>
<span className='flights-title'>Upcoming Flights</span> <span className='flights-title'>Upcoming Flights</span>
<button type='submit' className='view_more_button'>View more</button> <button type='submit' className='view_more_button'>View more</button>
</div> </div>
...@@ -111,7 +111,7 @@ function CustomerDashboard() { ...@@ -111,7 +111,7 @@ function CustomerDashboard() {
</div> </div>
<div className='flights'> <div className='flights'>
<div> <div className='flex-row'>
<span className='flights-title'>Flights History</span> <span className='flights-title'>Flights History</span>
<button type='submit' className='view_more_button'>View more</button> <button type='submit' className='view_more_button'>View more</button>
</div> </div>
......
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
import './Header.scss'; import './Header.scss';
function Header() { function Header() {
const { isAuth } = useAuth();
return ( return (
<> <>
<div className='header'> <div className='header'>
...@@ -10,7 +13,16 @@ function Header() { ...@@ -10,7 +13,16 @@ function Header() {
<nav className='nav'> <nav className='nav'>
<Link to={'/'} className='nav-item'>Home</Link> <Link to={'/'} className='nav-item'>Home</Link>
<Link to={'booking/query'} className='nav-item'>Book a Flight</Link>
{isAuth ?
<div>
<Link to={'booking/query'} className='nav-item'>Book a Flight</Link>
<Link to={'logout'} className='nav-item'>Logout</Link>
</div> :
<div>
<Link to={'login'} className='nav-item'>Login</Link>
<Link to={'register'} className='nav-item'>Register</Link>
</div>}
</nav> </nav>
</div> </div>
</div> </div>
......
import { useForm } from 'react-hook-form';
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useAuth } from '../../hooks/useAuth';
import './Login.scss'; import './Login.scss';
interface ILogin { interface ILogin {
...@@ -9,11 +11,15 @@ interface ILogin { ...@@ -9,11 +11,15 @@ interface ILogin {
export function Login() { export function Login() {
const [error, setError] = useState(''); const [error, setError] = useState('');
const { giveAuth } = useAuth();
const navigate = useNavigate();
const { register, handleSubmit } = useForm<ILogin>({mode: 'onChange'}); const { register, handleSubmit } = useForm<ILogin>({mode: 'onChange'});
const onSubmit = (formValue: ILogin) => { const onSubmit = (formValue: ILogin) => {
setError('TODO: remove me once actual errors are implemented'); setError('TODO: remove me once actual errors are implemented');
console.log('ready to make login api call', formValue); console.log('ready to make login api call', formValue);
giveAuth('testToken');
navigate('/');
}; };
return ( return (
......
.logout {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.logout-card {
width: 20vw;
min-width: 350px;
}
.full-main {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.logout-content {
display: flex;
flex-direction: column;
gap: 2rem;
align-items: center;
justify-content: center;
font-size: 2rem;
text-align: center;
}
import { useEffect, useState } from 'react';
import { useAuth } from '../../hooks/useAuth';
import Spinner from '../Spinner/Spinner';
import './Logout.scss';
function Logout() {
const [loading, setLoading] = useState(true);
const { isAuth, removeAuth } = useAuth();
useEffect(() => {
// TODO: do logout api call
if (isAuth) {
removeAuth();
setLoading(false);
} else {
setLoading(false);
}
}, []);
return (
<>
{
loading ?
<div className='full-main'>
<Spinner></Spinner>
</div> :
<div className='logout'>
<div className='card logout-card'>
<div className='logout-content'>
<span>Sucessfully logged out!</span>
<span>Use the Header to navigate!</span>
</div>
</div>
</div>
}
</>
);
}
export default Logout;
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from '../../hooks/useAuth';
function ProtectedRoute() {
const { isAuth } = useAuth();
if (isAuth) {
return <Outlet></Outlet>
}
return <Navigate to={'login'}></Navigate>
}
export default ProtectedRoute;
.spinner {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 30px;
height: 30px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
import './Spinner.scss';
function Spinner() {
return (
<>
<div className='spinner'></div>
</>
);
}
export default Spinner;
import { createContext } from 'react';
interface IAuthContext {
isAuth: boolean;
giveAuth: (token: string) => void;
removeAuth: () => void;
}
export const AuthContext = createContext<IAuthContext>({
isAuth: false,
giveAuth: () => console.error('no give auth function'),
removeAuth: () => console.error('no remove auth function')
});
export function getSearchParam(requestURL: string, param: string): string { export function getSearchParam(requestURL: string, param: string): string {
const url = new URL(requestURL); const url = new URL(requestURL);
return url.searchParams.get(param) ?? ''; return url.searchParams.get(param) ?? '';
} }
\ No newline at end of file
import { useContext } from 'react';
import { AuthContext } from '../contexts/AuthContext';
export const useAuth = () => {
return useContext(AuthContext);
};
...@@ -82,4 +82,18 @@ body { ...@@ -82,4 +82,18 @@ body {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 1rem; gap: 1rem;
} }
\ No newline at end of file
.flex-row {
display: flex;
flex-direction: row;
align-items: center;
}
.full {
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
}
...@@ -4,12 +4,14 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; ...@@ -4,12 +4,14 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import App from './App.tsx'; import App from './App.tsx';
import Login from './components/Login/Login.tsx'; import Login from './components/Login/Login.tsx';
import Register from './components/Register/Register.tsx'; import Register from './components/Register/Register.tsx';
import Logout from './components/Logout/Logout.tsx';
import ProtectedRoute from './components/ProtectedRoute/ProtectedRoute.tsx';
import CustomerDashboard from './components/CustomerDashboard/CustomerDashboard.tsx'; import CustomerDashboard from './components/CustomerDashboard/CustomerDashboard.tsx';
import BookingQuery from './components/BookingQuery/BookingQuery.tsx'; import BookingQuery from './components/BookingQuery/BookingQuery.tsx';
import BookingList from './components/BookingList/BookingList.tsx'; import BookingList from './components/BookingList/BookingList.tsx';
import { GetCustomerDashboardData } from './services/CustomerDashboard/CustomerDashboard.ts'; import { GetCustomerDashboardData } from './services/CustomerDashboard/CustomerDashboard.ts';
import './index.scss';
import { GetBookingList } from './services/BookingList/BookingList.ts'; import { GetBookingList } from './services/BookingList/BookingList.ts';
import './index.scss';
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
...@@ -25,18 +27,27 @@ const router = createBrowserRouter([ ...@@ -25,18 +27,27 @@ const router = createBrowserRouter([
element: <Register></Register> element: <Register></Register>
}, },
{ {
path: 'customer-dashboard', path: 'logout',
loader: GetCustomerDashboardData, element: <Logout></Logout>
element: <CustomerDashboard></CustomerDashboard>
},
{
path: 'booking/query',
element: <BookingQuery></BookingQuery>
}, },
{ {
path: 'booking/list', element: <ProtectedRoute></ProtectedRoute>,
loader: GetBookingList, children: [
element: <BookingList></BookingList> {
path: 'customer-dashboard',
loader: GetCustomerDashboardData,
element: <CustomerDashboard></CustomerDashboard>
},
{
path: 'booking/query',
element: <BookingQuery></BookingQuery>
},
{
path: 'booking/list',
loader: GetBookingList,
element: <BookingList></BookingList>
}
]
} }
] ]
} }
......
import { ReactNode, useEffect, useState } from 'react';
import { AuthContext } from '../contexts/AuthContext';
import Spinner from '../components/Spinner/Spinner';
function AuthProvider({ children }: { children: ReactNode }) {
const [loading, setLoading] = useState(true);
const [token, setToken] = useState(localStorage.getItem('token') ?? '');
useEffect(() => {
if (token) {
// validate token
}
setTimeout(() => setLoading(false), 500); // Fake api timer
}, []);
const giveAuth = (token: string) => {
setToken(token);
localStorage.setItem('token', token);
};
const removeAuth = () => {
setToken('');
localStorage.removeItem('token');
};
if (loading) {
return (
<div className='full'>
<Spinner></Spinner>
</div>
);
}
return (
<AuthContext.Provider value={{ isAuth: !!token, giveAuth, removeAuth }}>
{!loading && children}
</AuthContext.Provider>
);
}
export default AuthProvider;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment