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:
2025-09-15 15:44:56 -05:00
parent 51812d6493
commit 9f09f78288
14 changed files with 190 additions and 37 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
.env
.env.*
# Logs # Logs
logs logs
*.log *.log

View File

@@ -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>
); );

View File

@@ -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 {

View File

@@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -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>
<AppBarSection> <h1 style={{ color: "#ff5733", textAlign: "center" }}>Campfire Logs</h1>
<Link to="/dashboard"> <AppBar position="sticky" style={{ backgroundColor: "#edbd7d"}}>
<Button look="flat">Dashboard</Button> <AppBarSection>
</Link> <AppBarSpacer style={{ width: 800 }} />
<Link to="/editor"> <Link to="/dashboard">
<Button look="flat">New Post</Button> <Button look="flat">Dashboard</Button>
</Link> </Link>
</AppBarSection> <AppBarSpacer style={{ width: 10 }} />
<Link to="/editor">
<Button look="flat">+ New Post</Button>
</Link>
</AppBarSection>
<AppBarSpacer style={{width: 32 }} /> <AppBarSpacer style={{width: 50 }} />
<AppBarSection> <AppBarSection>
<Button look="flat">Logout</Button> {isLoggedIn ? (
</AppBarSection> <Button look="flat" onClick={handleLogout}>
</AppBar> Logout
</Button>
) : (
<Link to="/login">
<Button look="flat">Login</Button>
</Link>
)}
</AppBarSection>
</AppBar>
</div>
); );
}; };

View 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;

View File

@@ -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(

View File

@@ -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;

View File

@@ -1,9 +1,11 @@
import React from 'react'; import React from 'react';
const EditorPage = () => { const EditorPage = React.forwardRef((props, ref) => {
return ( return (
<h1>This is the Editor Page</h1> <div ref={ref}>
<h1>This is the Editor Page</h1>
</div>
); );
}; });
export default EditorPage; export default EditorPage;

15
src/pages/Login.jsx Normal file
View 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;

View File

@@ -1,9 +1,11 @@
import React from 'react'; import React from 'react';
const NotFound = () => { const NotFound = React.forwardRef((props, ref) => {
return ( return (
<h1>404: Page Not Found</h1> <div ref={ref}>
<h1>404: Page Not Found</h1>
</div>
); );
}; });
export default NotFound; export default NotFound;

View File

@@ -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={<NotFound />} /> <Route path="/" element={isLoggedIn ? <Dashboard /> : <Navigate to="/login" />} />
</Routes> <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 />} />
</Routes>
</div>
</CSSTransition>
</SwitchTransition>
); );
}; };