added login page and started integrated of security

This commit is contained in:
GotthardG
2024-12-03 23:01:38 +01:00
parent 1798c480f6
commit 0d1374ded7
8 changed files with 302 additions and 101 deletions

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ResponsiveAppBar from './components/ResponsiveAppBar';
import ShipmentView from './pages/ShipmentView';
import HomePage from './pages/HomeView';
@ -8,6 +9,8 @@ import PlanningView from './pages/PlanningView';
import Modal from './components/Modal';
import AddressManager from './pages/AddressManagerView';
import ContactsManager from './pages/ContactsManagerView';
import LoginView from './pages/LoginView';
import ProtectedRoute from './components/ProtectedRoute';
const App: React.FC = () => {
const [openAddressManager, setOpenAddressManager] = useState(false);
@ -36,10 +39,11 @@ const App: React.FC = () => {
onOpenContactsManager={handleOpenContactsManager}
/>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/shipments" element={<ShipmentView />} />
<Route path="/planning" element={<PlanningView />} />
<Route path="/results" element={<ResultsView />} />
<Route path="/login" element={<LoginView />} />
<Route path="/" element={<ProtectedRoute element={<HomePage />} />} />
<Route path="/shipments" element={<ProtectedRoute element={<ShipmentView />} />} />
<Route path="/planning" element={<ProtectedRoute element={<PlanningView />} />} />
<Route path="/results" element={<ProtectedRoute element={<ResultsView />} />} />
{/* Other routes as necessary */}
</Routes>
<Modal open={openAddressManager} onClose={handleCloseAddressManager} title="Address Management">

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
interface ProtectedRouteProps {
element: JSX.Element;
}
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ element }) => {
const isAuthenticated = () => {
const token = localStorage.getItem('token');
console.log("Is Authenticated: ", token !== null);
return token !== null;
};
return isAuthenticated() ? element : <Navigate to="/login" />;
};
export default ProtectedRoute;

View File

@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
@ -20,24 +21,26 @@ interface ResponsiveAppBarProps {
onOpenContactsManager: () => void;
}
const pages = [
{ name: 'Home', path: '/' },
{ name: 'Shipments', path: '/shipments' },
{ name: 'Planning', path: '/planning' },
{ name: 'Results', path: '/results' }
];
const userMenuItems = [
{ name: 'My Contacts', action: 'contacts' },
{ name: 'My Addresses', action: 'addresses' },
{ name: 'DUO', path: '/duo' },
{ name: 'Logout', path: '/logout' }
];
const ResponsiveAppBar: React.FC<ResponsiveAppBarProps> = ({ onOpenAddressManager, onOpenContactsManager }) => {
const navigate = useNavigate();
const location = useLocation();
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
const pages = [
{ name: 'Home', path: '/' },
{ name: 'Shipments', path: '/shipments' },
{ name: 'Planning', path: '/planning' },
{ name: 'Results', path: '/results' }
];
const userMenuItems = [
{ name: 'My Contacts', action: 'contacts' },
{ name: 'My Addresses', action: 'addresses' },
{ name: 'DUO', path: '/duo' },
{ name: 'Logout', action: 'logout' }
];
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
};
@ -54,81 +57,67 @@ const ResponsiveAppBar: React.FC<ResponsiveAppBarProps> = ({ onOpenAddressManage
setAnchorElUser(null);
};
const handleLogout = () => {
console.log("Performing logout...");
localStorage.removeItem('token');
navigate('/login');
};
const handleMenuItemClick = (action: string) => {
if (action === 'contacts') {
onOpenContactsManager();
} else if (action === 'addresses') {
onOpenAddressManager();
} else if (action === 'logout') {
handleLogout();
}
handleCloseUserMenu();
};
return (
<div>
<AppBar position="static" sx={{ backgroundColor: '#2F4858' }}>
<AppBar position="static" sx={{ backgroundColor: '#324A5F' }}>
<Container maxWidth="xl">
<Toolbar disableGutters>
<Link to="/" style={{ textDecoration: 'none' }}>
<img src={logo} height="50px" alt="PSI logo" />
<Link to="/" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center' }}>
<img src={logo} height="40px" alt="PSI logo" style={{ marginRight: 12 }}/>
<Typography
variant="h6"
noWrap
sx={{
display: 'flex',
fontFamily: 'Roboto, sans-serif',
fontWeight: 700,
color: 'inherit',
textDecoration: 'none',
transition: 'color 0.3s',
'&:hover': {
color: '#f0db4f',
},
}}
>
AARE v0.1
</Typography>
</Link>
<Typography
variant="h6"
noWrap
component="a"
href="#app-bar-with-responsive-menu"
sx={{
mr: 2,
display: { xs: 'none', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 300,
color: 'inherit',
textDecoration: 'none',
}}
>
Heidi v2
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
<IconButton
size="large"
aria-label="menu"
onClick={handleOpenNavMenu}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorElNav}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={Boolean(anchorElNav)}
onClose={handleCloseNavMenu}
>
{pages.map((page) => (
<MenuItem key={page.name} onClick={handleCloseNavMenu}>
<Link to={page.path} style={{ textDecoration: 'none', color: 'inherit' }}>
{page.name}
</Link>
</MenuItem>
))}
</Menu>
</Box>
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' }, justifyContent: 'center' }}>
{pages.map((page) => (
<Button
key={page.name}
component={Link}
to={page.path}
sx={{ my: 2, color: 'white', display: 'block', fontSize: '1rem', padding: '12px 24px' }}
sx={{
mx: 2,
color: page.path === location.pathname ? '#f0db4f' : 'white',
fontWeight: page.path === location.pathname ? 700 : 500,
textTransform: 'none',
fontSize: '1rem',
transition: 'color 0.3s, background-color 0.3s',
'&:hover': {
color: '#f0db4f',
backgroundColor: 'rgba(255,255,255,0.1)',
},
}}
>
{page.name}
</Button>
@ -138,40 +127,24 @@ const ResponsiveAppBar: React.FC<ResponsiveAppBarProps> = ({ onOpenAddressManage
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar />
<Avatar sx={{ bgcolor: 'yellowgreen' }} />
</IconButton>
</Tooltip>
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{userMenuItems.map((item) =>
item.action ? (
<MenuItem key={item.name} onClick={() => handleMenuItemClick(item.action)}>
{item.name}
</MenuItem>
) : (
item.path && (
<MenuItem key={item.name} onClick={handleCloseUserMenu}>
<Link to={item.path} style={{ textDecoration: 'none', color: 'inherit' }}>
{item.name}
</Link>
</MenuItem>
)
)
)}
{userMenuItems.map((item) => (
<MenuItem key={item.name} onClick={() => handleMenuItemClick(item.action ?? '')}>
{item.name}
</MenuItem>
))}
</Menu>
</Box>
</Toolbar>

View File

@ -0,0 +1,123 @@
// LoginView.tsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AuthService } from '../../openapi'; // Adjust import path
const containerStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
};
const cardStyle = {
padding: '50px',
width: '350px',
backgroundColor: '#fff',
boxShadow: '0 15px 30px rgba(0, 0, 0, 0.1)',
borderRadius: '8px',
textAlign: 'center' as const,
animation: 'fadeIn 1s ease-in-out',
};
const inputStyle = {
width: '100%',
padding: '15px',
margin: '10px 0',
borderRadius: '30px',
border: '1px solid #ddd',
fontSize: '16px',
transition: 'border-color 0.3s ease',
};
const buttonStyle = {
width: '100%',
padding: '15px',
margin: '20px 0',
borderRadius: '30px',
backgroundColor: '#764ba2',
color: '#fff',
border: 'none',
fontSize: '16px',
cursor: 'pointer',
transition: 'background 0.3s ease',
};
const errorStyle = {
color: 'red',
marginTop: '20px',
fontWeight: 'bold',
};
const LoginView: React.FC = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const navigate = useNavigate();
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await AuthService.loginAuthTokenLoginPost({
grant_type: 'password',
username: username,
password: password,
});
localStorage.setItem('token', response.access_token);
navigate('/'); // Redirect post-login
} catch (err) {
setError('Login failed. Please check your credentials.');
}
};
return (
<div style={containerStyle}>
<form style={cardStyle} onSubmit={handleLogin}>
<h2>Welcome Back!</h2>
<input
style={{
...inputStyle,
borderColor: username ? '#764ba2' : '#ddd',
}}
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
<input
style={{
...inputStyle,
borderColor: password ? '#764ba2' : '#ddd',
}}
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button
style={{
...buttonStyle,
backgroundColor: '#764ba2',
}}
type="submit"
onMouseOver={(e) =>
(e.currentTarget.style.backgroundColor = '#6a3a89')
}
onMouseOut={(e) =>
(e.currentTarget.style.backgroundColor = '#764ba2')
}
>
Login
</button>
{error && <p style={errorStyle}>{error}</p>}
</form>
</div>
);
};
export default LoginView;