Fix: Set isLoggedIn
state and handle redirects
This commit addresses several issues related to the login flow: - Corrects the `isLoggedIn` variable to use `setIsLoggedIn` in `App.jsx`. - Passes `onLogin` and `isLoggedIn` props to `CampfireAppBar`. - Enables logout functionality in `AppBar.jsx`. - Modifies `AppRoutes.jsx` to conditionally render `CampfireAppBar`. - Converts pages to use `React.forwardRef` for page transitions. - Fixes the `useState` import in `App.jsx`. - Moves `NotificationGroup` to the top of the form in `LoginComponent.jsx` to prevent overlap. - Corrects the logo path in `LoginPage.jsx`. - Improves input accessibility with a border in `LoginComponent.jsx`.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
18
src/App.jsx
18
src/App.jsx
@@ -1,12 +1,24 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import AppRoutes from './routes/AppRoutes';
|
import AppRoutes from './routes/AppRoutes';
|
||||||
import CampfireAppBar from './components/AppBar';
|
import CampfireAppBar from './components/AppBar';
|
||||||
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const handleLogin = (isLoggedInState) => {
|
||||||
|
setIsLoggedIn(isLoggedInState);
|
||||||
|
if (isLoggedInState) {
|
||||||
|
navigate('/dashboard');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CampfireAppBar />
|
{location.pathname !== '/login' && <CampfireAppBar isLoggedIn={isLoggedIn} onLogin={handleLogin} />}
|
||||||
<AppRoutes />
|
<AppRoutes isLoggedIn={isLoggedIn} onLogin={handleLogin} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -3,6 +3,12 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
caret-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
@@ -25,6 +25,8 @@ a:hover {
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
min-width: 320px;
|
min-width: 320px;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@@ -66,3 +68,26 @@ button:focus-visible {
|
|||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-enter {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.page-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
}
|
||||||
|
.page-exit {
|
||||||
|
opacity: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.page-exit-active {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 300ms;
|
||||||
|
}
|
BIN
src/assets/images/campfire_logs_square_logo.png
Normal file
BIN
src/assets/images/campfire_logs_square_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/images/campfire_logs_square_logo_no_bg.png
Normal file
BIN
src/assets/images/campfire_logs_square_logo_no_bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
@@ -1,26 +1,46 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AppBar, AppBarSection, AppBarSpacer } from '@progress/kendo-react-layout';
|
import { AppBar, AppBarSection, AppBarSpacer } from '@progress/kendo-react-layout';
|
||||||
import { Button } from '@progress/kendo-react-buttons';
|
import { Button } from '@progress/kendo-react-buttons';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const CampfireAppBar = ({ isLoggedIn, onLogin }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
onLogin(false);
|
||||||
|
navigate('/login');
|
||||||
|
};
|
||||||
|
|
||||||
const CampfireAppBar = () => {
|
|
||||||
return (
|
return (
|
||||||
<AppBar position="sticky">
|
<div>
|
||||||
|
<h1 style={{ color: "#ff5733", textAlign: "center" }}>Campfire Logs</h1>
|
||||||
|
<AppBar position="sticky" style={{ backgroundColor: "#edbd7d"}}>
|
||||||
<AppBarSection>
|
<AppBarSection>
|
||||||
|
<AppBarSpacer style={{ width: 800 }} />
|
||||||
<Link to="/dashboard">
|
<Link to="/dashboard">
|
||||||
<Button look="flat">Dashboard</Button>
|
<Button look="flat">Dashboard</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
<AppBarSpacer style={{ width: 10 }} />
|
||||||
<Link to="/editor">
|
<Link to="/editor">
|
||||||
<Button look="flat">New Post</Button>
|
<Button look="flat">+ New Post</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</AppBarSection>
|
</AppBarSection>
|
||||||
|
|
||||||
<AppBarSpacer style={{width: 32 }} />
|
<AppBarSpacer style={{width: 50 }} />
|
||||||
|
|
||||||
<AppBarSection>
|
<AppBarSection>
|
||||||
<Button look="flat">Logout</Button>
|
{isLoggedIn ? (
|
||||||
|
<Button look="flat" onClick={handleLogout}>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Link to="/login">
|
||||||
|
<Button look="flat">Login</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</AppBarSection>
|
</AppBarSection>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
55
src/components/LoginComponent.jsx
Normal file
55
src/components/LoginComponent.jsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Input } from '@progress/kendo-react-inputs';
|
||||||
|
import { Label } from '@progress/kendo-react-labels';
|
||||||
|
import { Button } from '@progress/kendo-react-buttons';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Notification, NotificationGroup } from '@progress/kendo-react-notification';
|
||||||
|
|
||||||
|
const LoginComponent = ({ onLogin }) => {
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
const handleSubmit = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const mockUsername = import.meta.env.VITE_MOCK_USERNAME;
|
||||||
|
const mockPassword = import.meta.env.VITE_MOCK_PASSWORD;
|
||||||
|
|
||||||
|
if (username === mockUsername && password === mockPassword) {
|
||||||
|
onLogin(true);
|
||||||
|
} else {
|
||||||
|
console.error("Invalid username or password");
|
||||||
|
setError("Invalid username or password. Please try again.");
|
||||||
|
setTimeout(() => {
|
||||||
|
setError('');
|
||||||
|
}, 50000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="username">Username: </Label>
|
||||||
|
<Input style={{ border: '1px solid #edbd7d' }} type="text" id="username" value={username} onChange={(e) => setUsername(e.target.value)} autoComplete="username" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="password">Password: </Label>
|
||||||
|
<Input style={{ border: '1px solid #edbd7d' }} type="password" id="password" value={password} onChange={(e) => setPassword(e.target.value)} autoComplete="current-password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button look="flat" type="submit">Login</Button>
|
||||||
|
<div>
|
||||||
|
<NotificationGroup style={{ textAlign: 'center' }}>
|
||||||
|
{error && (<Notification type={{ style: 'error', icon: true }} closeable={true} onClose={() => setError('')}>
|
||||||
|
<span>{error}</span>
|
||||||
|
</Notification> )}
|
||||||
|
</NotificationGroup>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoginComponent;
|
@@ -2,7 +2,7 @@ import { StrictMode } from 'react';
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import 'campfire-logs-dashboard/dist/css/campfire-logs-dashboard.css';
|
import 'campfire-logs-dashboard/dist/css/campfire-logs-dashboard.css';
|
||||||
import './index.css';
|
import './assets/css/index.css';
|
||||||
import App from './App.jsx';
|
import App from './App.jsx';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = React.forwardRef((props, ref) => {
|
||||||
return (
|
return (
|
||||||
<h1>This is the Dashboard Page</h1>
|
<div ref={ref}>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default Dashboard;
|
export default Dashboard;
|
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const EditorPage = () => {
|
const EditorPage = React.forwardRef((props, ref) => {
|
||||||
return (
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
<h1>This is the Editor Page</h1>
|
<h1>This is the Editor Page</h1>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default EditorPage;
|
export default EditorPage;
|
15
src/pages/Login.jsx
Normal file
15
src/pages/Login.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import LoginComponent from '../components/LoginComponent';
|
||||||
|
import Logo from '../assets/images/campfire_logs_square_logo.png';
|
||||||
|
|
||||||
|
const LoginPage = React.forwardRef(({ onLogin }, ref) => {
|
||||||
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
|
<img src={Logo} alt="Campfire Logs Logo" width="350" height="280" />
|
||||||
|
<hr style={{ backgroundColor: '#edbd7d', height: '2px', border: 'none' }}/>
|
||||||
|
<LoginComponent onLogin={onLogin} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default LoginPage;
|
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const NotFound = () => {
|
const NotFound = React.forwardRef((props, ref) => {
|
||||||
return (
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
<h1>404: Page Not Found</h1>
|
<h1>404: Page Not Found</h1>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default NotFound;
|
export default NotFound;
|
@@ -1,18 +1,30 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Routes, Route } from 'react-router-dom';
|
import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
||||||
|
import { CSSTransition, SwitchTransition } from 'react-transition-group';
|
||||||
|
|
||||||
import Dashboard from '../pages/Dashboard';
|
import Dashboard from '../pages/Dashboard';
|
||||||
import EditorPage from '../pages/EditorPage';
|
import EditorPage from '../pages/EditorPage';
|
||||||
|
import LoginPage from '../pages/Login';
|
||||||
import NotFound from '../pages/NotFound';
|
import NotFound from '../pages/NotFound';
|
||||||
|
|
||||||
const AppRoutes = () => {
|
const AppRoutes = ({ isLoggedIn, onLogin }) => {
|
||||||
|
const location = useLocation();
|
||||||
|
const nodeRef = React.useRef(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<SwitchTransition>
|
||||||
<Route path="/" element={<Dashboard />} />
|
<CSSTransition key={location.pathname} nodeRef={nodeRef} classNames="page" timeout={300}>
|
||||||
<Route path="/dashboard" element={<Dashboard />} />
|
<div ref={nodeRef}>
|
||||||
<Route path="/editor" element={<EditorPage />} />
|
<Routes location={location}>
|
||||||
|
<Route path="/" element={isLoggedIn ? <Dashboard /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/dashboard" element={isLoggedIn ? <Dashboard /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/editor" element={isLoggedIn ? <EditorPage /> : <Navigate to="/login" />} />
|
||||||
|
<Route path="/login" element={<LoginPage onLogin={onLogin} />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</CSSTransition>
|
||||||
|
</SwitchTransition>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user