From 6e2a23c3efa6bf5f3f794003477f6afc0e98bec9 Mon Sep 17 00:00:00 2001 From: Lobo Date: Sat, 4 Apr 2026 10:49:14 +0200 Subject: [PATCH] Added routing, jwt handling and Dashboard.jsx --- public/i18n/de.json | 10 +++++ public/i18n/en.json | 10 +++++ src/App.jsx | 70 +++++++++++++++--------------- src/main.jsx | 9 ++-- src/pages/Dashboard.jsx | 96 +++++++++++++++++++++++++++++++++++++++++ src/pages/LoginPage.jsx | 13 +++--- 6 files changed, 164 insertions(+), 44 deletions(-) create mode 100644 src/pages/Dashboard.jsx diff --git a/public/i18n/de.json b/public/i18n/de.json index 6fc0559..8fba93a 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -12,6 +12,16 @@ "ERROR_EMAIL": "Gib eine gültige Email-Adresse ein mit '@' und mit '.'.", "ERROR_PASSWORD": "Das Passwort darf nicht leer sein." }, + "INFO": { + "ERROR": { + "LOAD_PROFILE": "Fehler beim Laden der Profildaten.", + "NETWORK_FAILURE": "Netzwerkfehler. Konnte keine Verbindung zum Server herstellen." + } + }, + "DASHBOARD": { + "LOADING": "Lade Dashboard ...", + "HELLO": "Hallo" + }, "welcome": "Dein Game-Backlog unter Kontrolle", "login": "Anmelden" } \ No newline at end of file diff --git a/public/i18n/en.json b/public/i18n/en.json index b31a296..a2698ef 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -12,6 +12,16 @@ "ERROR_EMAIL": "Enter a valid email address with '@' and with '.'.", "ERROR_PASSWORD": "The password must have at least one character." }, + "INFO": { + "ERROR": { + "LOAD_PROFILE": "Error during loading of the user profile data.", + "NETWORK_FAILURE": "Network failure. Could not connect to server." + } + }, + "DASHBOARD": { + "LOADING": "Loading dashboard ...", + "HELLO": "Hello" + }, "welcome": "Control your game backlog", "login": "Login" } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index eb0ce86..af968f6 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,11 @@ -import {BrowserRouter, Link, Route, Routes} from 'react-router-dom'; +import {Link, Route, Routes} from 'react-router-dom'; import {useTranslation} from 'react-i18next'; import {useEffect, useState} from 'react'; import LandingPage from './pages/LandingPage'; import LoginPage from './pages/LoginPage'; import ImprintPage from './pages/ImprintPage'; +import Dashboard from "./pages/Dashboard.jsx"; function App() { const {t, i18n} = useTranslation(); @@ -28,43 +29,42 @@ function App() { }; return ( - -
-
- - Game Log +
+
+ + Game Log + + +
+ + + + {t('login')} +
+
-
- - - - {t('login')} - -
-
+
+ + }/> + }/> + }/> + }/> + +
-
- - }/> - }/> - }/> - -
- -
- - {t('IMPRINT.TITLE')} - -
-
-
+ + ); } diff --git a/src/main.jsx b/src/main.jsx index 85bc3cc..5480994 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,5 +1,6 @@ -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; +import {StrictMode} from 'react'; +import {createRoot} from 'react-dom/client'; +import {BrowserRouter} from 'react-router-dom'; import App from './App.jsx'; import './i18n'; import './style.css'; @@ -7,6 +8,8 @@ import '@mdi/font/css/materialdesignicons.css'; createRoot(document.getElementById('root')).render( - + + + ); \ No newline at end of file diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx new file mode 100644 index 0000000..566a9de --- /dev/null +++ b/src/pages/Dashboard.jsx @@ -0,0 +1,96 @@ +import React, {useEffect, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; + +const Dashboard = () => { + const {t} = useTranslation(); + const navigate = useNavigate(); + const [userData, setUserData] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchUserData = async () => { + const token = localStorage.getItem('tbp_jwt'); + + if (!token) { + navigate('/login'); + return; + } + + try { + const response = await fetch(`${import.meta.env.VITE_API_URL}/api/users/me?v=1`, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + const data = await response.json(); + setUserData(data); + } else if (response.status === 401) { + localStorage.removeItem('tbp_jwt'); + navigate('/login'); + } else { + setError(t('INFO.ERROR.LOAD_PROFILE')); + } + } catch (err) { + console.error('Dashboard fetch error:', err); + setError(t('INFO.ERROR.NETWORK_FAILURE')); + } finally { + setIsLoading(false); + } + }; + + fetchUserData(); + }, [navigate]); + + if (isLoading) { + return ( +
+ {t('DASHBOARD.LOADING')} +
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + return ( +
+
+ +
+

+ {t('DASHBOARD.HELLO')} {userData?.email.split('@')[0]} 👋 +

+

+ {userData?.dashboardMessage} +

+
+ +
+
+ Tile 1 (Coming soon) +
+
+ Tile 2 (Coming soon) +
+
+ Settings (Coming soon) +
+
+ +
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index ae245be..66a6829 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -1,8 +1,10 @@ import {useTranslation} from 'react-i18next'; import {useState} from 'react'; +import {useNavigate} from 'react-router-dom'; export default function LoginPage() { const {t} = useTranslation(); + const navigate = useNavigate(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -35,12 +37,11 @@ export default function LoginPage() { if (response.ok) { // HTTP 200 OK - setFeedback({ - message: `Success: ${data.message} - Welcome ${data.email}!`, - isError: false - }); - - // TODO: Save JWT token or trigger routing here in the future + localStorage.setItem('tbp_jwt', data.token); + setFeedback({message: 'Login successful!', isError: false}); + setTimeout(() => { + navigate('/dashboard'); + }, 500); } else { // HTTP 401 Unauthorized or other errors setFeedback({