From 69c3059ef0d0d5adab4420ac0853e4f1137c2348 Mon Sep 17 00:00:00 2001 From: MattLeo Date: Tue, 2 Dec 2025 10:20:49 -0600 Subject: [PATCH] updated app.jsx to include a login workflow --- client/kb-frontend/src/App.css | 27 +++ client/kb-frontend/src/App.jsx | 157 ++++++++++++++---- .../src/components/Registration.jsx | 6 +- server/db.js | 6 +- server/server.js | 6 +- 5 files changed, 160 insertions(+), 42 deletions(-) diff --git a/client/kb-frontend/src/App.css b/client/kb-frontend/src/App.css index 7d2a6ef..4a895ba 100644 --- a/client/kb-frontend/src/App.css +++ b/client/kb-frontend/src/App.css @@ -47,3 +47,30 @@ .create-new-button:hover { background-color: #229954; } + +.user-info { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + background-color: #34495e; + color: white; +} + +.user-info span { + font-size: 1rem; +} + +.logout-button { + background-color: #e74c3c; + color: white; + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; +} + +.logout-button:hover { + background-color: #c0392b; +} diff --git a/client/kb-frontend/src/App.jsx b/client/kb-frontend/src/App.jsx index 3320ebe..499be6a 100644 --- a/client/kb-frontend/src/App.jsx +++ b/client/kb-frontend/src/App.jsx @@ -32,24 +32,72 @@ function App() { if (isLoggedIn && token) { fetchArticles(); } - }, []); + }, [isLoggedIn, token]); + + const handleLoginSuccess = (user, authToken) => { + setCurrentUser(user); + setToken(authToken); + setIsLoggedIn(true); + + localStorage.setItem('token', authToken); + localStorage.setItem('user', JSON.stringify(user)); + }; + + const handleRegistrationSuccess = (user, authToken) => { + handleLoginSuccess(user, authToken); + }; + + const handleLogout = () => { + setIsLoggedIn(false); + setCurrentUser(null); + setToken(null); + setArticles([]); + setCurrentView('list'); + setSelectedArticle(null); + + localStorage.removeItem('token'); + localStorage.removeItem('user'); + }; const fetchArticles = () => { - fetch('http://localhost:9000/api/articles') - .then(response => response.json()) - .then(data => setArticles(data)) - .catch(error => console.error('Error fetching articles:', error)) + fetch('http://localhost:9000/api/articles', { + headers: { + 'Authorization': `Bearer ${token}` + }, + }) + .then(response => { + if (response.status === 401 || response.status === 403) { + handleLogout(); + return; + } + return response.json(); + }) + .then(data => { + if (data) setArticles(data); + }) + .catch(error => console.error('Error fetching articles:', error)); }; const handleSearch = (query) => { if (!query) { - fetch('http://localhost:9000/api/articles') - .then(response => response.json()) - .then(data => setArticles(data)); + fetchArticles(); } else { - fetch(`http://localhost:9000/api/search?q=${encodeURIComponent(query)}`) - .then(response => response.json()) - .then(data => setArticles(data)); + fetch('http://localhost:9000/api/articles', { + headers: { + 'Authorization': `Bearer ${token}` + } + }) + .then(response => { + if (response.status === 401 || response.status === 403) { + handleLogout(); + return; + } + return response.json(); + }) + .then(data => { + if (data) setArticles(data); + }) + .catch(error => console.error('Error searching articles:', error)); } }; @@ -73,25 +121,35 @@ function App() { if (isNew) { fetch('http://localhost:9000/api/articles', { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, body: JSON.stringify(updatedArticle) }) .then(response => response.json()) .then(data => { setSelectedArticle(data); setCurrentView('detail'); - }); + fetchArticles(); + }) + .catch(error => console.error('Error creating article:', error)); } else { fetch(`http://localhost:9000/api/articles/${updatedArticle.ka_number}`, { method: 'PUT', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, body: JSON.stringify(updatedArticle) }) .then(response => response.json()) .then(data => { setSelectedArticle(data); setCurrentView('detail'); - }); + fetchArticles(); + }) + .catch(error => console.error('Error updating article:', error)); } }; @@ -117,40 +175,73 @@ function App() { const handleDelete = () => { if (confirm('Are you sure you want to delete this article?')) { fetch(`http://localhost:9000/api/articles/${selectedArticle.ka_number}`, { - method: 'DELETE' + method: 'DELETE', + headers: {'Authorization': `Bearer ${token}`} }) - .then(() => { - return fetch('http://localhost:9000/api/articles'); + .then(response => { + if (response.status === 401 || response.status === 403) { + handleLogout(); + return response.json(); + } }) - .then(response => response.json()) - .then(data => { - setArticles(data); + .then(() => { + fetchArticles(); setCurrentView('list'); setSelectedArticle(null); }) .catch(error => console.error('Error deleting article:', error)); } -}; + }; + + const handleSwitchToLogin = () => { + setAuthView('login'); + }; + + const handleSwitchToRegister = () => { + setAuthView('register'); + }; return (
- {currentView === 'list' ? ( + {/* Show Login/Registration if not logged in */} + {!isLoggedIn ? ( + authView === 'login' ? ( + + ) : ( + + ) + ) : ( <> - - - +
+ Welcome, {currentUser?.display_name || currentUser?.username}! + +
+ + {currentView === 'list' ? ( + <> + + + + + ) : currentView === 'detail' ? ( + + ) : ( + + )} - ) : currentView === 'detail' ? ( - - ) : ( - )}
); } -export default App +export default App; \ No newline at end of file diff --git a/client/kb-frontend/src/components/Registration.jsx b/client/kb-frontend/src/components/Registration.jsx index bd3121a..0a7f284 100644 --- a/client/kb-frontend/src/components/Registration.jsx +++ b/client/kb-frontend/src/components/Registration.jsx @@ -61,7 +61,7 @@ function Registration({ onRegistrationSuccess, onSwitchToLogin }) {

Create Account

{error &&
{error}
} -
+
setEmail(e.target.value)} @@ -141,4 +141,4 @@ function Registration({ onRegistrationSuccess, onSwitchToLogin }) { ); } -export default Reguistration \ No newline at end of file +export default Registration \ No newline at end of file diff --git a/server/db.js b/server/db.js index 4ee783f..3e77e7a 100644 --- a/server/db.js +++ b/server/db.js @@ -208,14 +208,14 @@ function searchArticles(query) { * @param {string} username - The username for the newly created user * @param {string} email - The email address for the newly created user * @param {string} passHash - The hashed password for validation purposes - * @param {string} displayName - The name that will be desplayed when an article is created or updated + * @param {string} display_name - The name that will be desplayed when an article is created or updated * @param {string} authProvider - the source of the authentication: 'local' or 'entra' * @param {string} entraId - The ID number for the associated entra account, can be null if auth provider is local * @returns {Object} - The user object of the newly created user */ -function createUser(username, email, passHash, displayName, authProvider = 'local', entraId = null) { +function createUser(username, email, passHash, display_name, authProvider = 'local', entraId = null) { db.run("INSERT INTO users (username, email, pass_hash, display_name, auth_provider, entra_id) VALUES (?, ?, ?, ?, ?, ?)", - [username, email, passHash, displayName, authProvider, entraId] + [username, email, passHash, display_name, authProvider, entraId] ) // Saving DB with newly created record diff --git a/server/server.js b/server/server.js index 1bbbf2f..a4cec70 100644 --- a/server/server.js +++ b/server/server.js @@ -118,9 +118,9 @@ initDb().then(() => { app.post('/api/auth/register', async (req, res) => { try { - const {username, email, password, displayName} = req.body; + const {username, email, password, display_name} = req.body; - if (!username || !email || !password || !displayName) { + if (!username || !email || !password || !display_name) { return res.status(400).json({error: 'Username, email, password, and display name are required'}); } @@ -135,7 +135,7 @@ initDb().then(() => { } const pass_hash = await argon2.hash(password); - const newUser = createUser(username, email, pass_hash, displayName); + const newUser = createUser(username, email, pass_hash, display_name); const token = generateToken(newUser); return res.status(201).json({