diff --git a/public/i18n/de.json b/public/i18n/de.json index 7e36f15..6fc0559 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -1,5 +1,17 @@ { + "IMPRINT": { + "TITLE": "Impressum", + "LEGAL": "Angaben gemäß § 5 DDG", + "CONTACT": "Kontakt" + }, + "LOGIN": { + "TITLE": "Login", + "EMAIL": "Email", + "PASSWORD": "Passwort", + "SUBMIT": "Spiel betreten", + "ERROR_EMAIL": "Gib eine gültige Email-Adresse ein mit '@' und mit '.'.", + "ERROR_PASSWORD": "Das Passwort darf nicht leer sein." + }, "welcome": "Dein Game-Backlog unter Kontrolle", - "login": "Anmelden", - "imprint": "Impressum" + "login": "Anmelden" } \ No newline at end of file diff --git a/public/i18n/en.json b/public/i18n/en.json index 208f748..b31a296 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -1,5 +1,17 @@ { + "IMPRINT": { + "TITLE": "Imprint", + "LEGAL": "Information according to § 5 TMG.", + "CONTACT": "Contact" + }, + "LOGIN": { + "TITLE": "Login", + "EMAIL": "Email", + "PASSWORD": "Password", + "SUBMIT": "Enter the game", + "ERROR_EMAIL": "Enter a valid email address with '@' and with '.'.", + "ERROR_PASSWORD": "The password must have at least one character." + }, "welcome": "Control your game backlog", - "login": "Login", - "imprint": "Imprint" + "login": "Login" } \ No newline at end of file diff --git a/public/icons.svg b/public/icons.svg deleted file mode 100644 index e952219..0000000 --- a/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/App.jsx b/src/App.jsx index 16ef0ff..eb0ce86 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,42 +1,16 @@ -import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import { useState, useEffect } from 'react'; +import {BrowserRouter, Link, Route, Routes} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import {useEffect, useState} from 'react'; -// Placeholder components to keep the routing structure simple -function LandingPage() { - const { t } = useTranslation(); - return ( -
-

- {t('welcome')} -

-
- ); -} - -function LoginPage() { - const { t } = useTranslation(); - return ( -
-

{t('login')}

-
- ); -} - -function ImprintPage() { - const { t } = useTranslation(); - return ( -
-

{t('imprint')}

-
- ); -} +import LandingPage from './pages/LandingPage'; +import LoginPage from './pages/LoginPage'; +import ImprintPage from './pages/ImprintPage'; function App() { - const { t, i18n } = useTranslation(); + const {t, i18n} = useTranslation(); const [isDarkMode, setIsDarkMode] = useState(true); - // Apply dark mode class to the root HTML element for Tailwind + // Sync React state with the HTML class required by Tailwind CSS useEffect(() => { if (isDarkMode) { document.documentElement.classList.add('dark'); @@ -47,44 +21,46 @@ function App() { const toggleLanguage = () => { if (i18n.language === 'de') { - i18n.changeLanguage('en').then(r => console.log("Changed language to en")); + i18n.changeLanguage('en').then(r => console.log("Changed language to 'en'")); } else { - i18n.changeLanguage('de').then(r => console.log("Changed language to en")); + i18n.changeLanguage('de').then(r => console.log("Changed language to 'de'")); } }; return ( -
-
- - To Be Played +
+
+ + Game Log -
- - - + {t('login')}
-
+
- } /> - } /> - } /> + }/> + }/> + }/>
-
- - {t('imprint')} +
+ + {t('IMPRINT.TITLE')}
diff --git a/src/assets/hero.png b/src/assets/hero.png deleted file mode 100644 index cc51a3d..0000000 Binary files a/src/assets/hero.png and /dev/null differ diff --git a/src/assets/vite.svg b/src/assets/vite.svg deleted file mode 100644 index 5101b67..0000000 --- a/src/assets/vite.svg +++ /dev/null @@ -1 +0,0 @@ -Vite diff --git a/src/assets/vue.svg b/src/assets/vue.svg deleted file mode 100644 index 770e9d3..0000000 --- a/src/assets/vue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/ImprintPage.jsx b/src/pages/ImprintPage.jsx new file mode 100644 index 0000000..477298e --- /dev/null +++ b/src/pages/ImprintPage.jsx @@ -0,0 +1,28 @@ +import {useTranslation} from 'react-i18next'; + +export default function ImprintPage() { + const {t} = useTranslation(); + + return ( +
+
+

+ {t('IMPRINT.TITLE')} +

+
+

{t('IMPRINT.LEGAL')}

+

+ Andreas Dahm
+ Bgm.-Germeier-Str. 2
+ 85586 Poing
+ Deutschland +

+

+ {t('IMPRINT.CONTACT')} +

+

Email: andreas.dahm@gmail.com

+
+
+
+ ); +} \ No newline at end of file diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx new file mode 100644 index 0000000..f3d3eb0 --- /dev/null +++ b/src/pages/LandingPage.jsx @@ -0,0 +1,45 @@ +import {useTranslation} from 'react-i18next'; +import {Link} from 'react-router-dom'; + +export default function LandingPage() { + const {t} = useTranslation(); + const mockGames = [1, 2, 3]; + + return ( +
+
+

+ {t('welcome')} +

+

+ Organize your pile of shame. Track, manage, and conquer your gaming backlog like never before. +

+ + Get Started + +
+ +
+ {mockGames.map((n) => { + return ( +
+
+ {`Game +
+ BACKLOG +
+
+
+

Epic Title {n}

+

RPG • ~50h playtime

+ +
+
+ ); + })} +
+
+ ); +} \ No newline at end of file diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx new file mode 100644 index 0000000..3524584 --- /dev/null +++ b/src/pages/LoginPage.jsx @@ -0,0 +1,92 @@ +import {useTranslation} from 'react-i18next'; +import {useState} from 'react'; + +export default function LoginPage() { + const {t} = useTranslation(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [emailError, setEmailError] = useState(''); + const [passwordError, setPasswordError] = useState(''); + + const handleLogin = (e) => { + e.preventDefault(); + + setEmailError(''); + setPasswordError(''); + + let hasError = false; + + if (!email.includes('@') && !email.includes('.')) { + setEmailError(t('LOGIN.ERROR_EMAIL')); + hasError = true; + } + + if (password.trim() === '') { + setPasswordError(t('LOGIN.ERROR_PASSWORD')); + hasError = true; + } + + // Stop execution if form inputs are invalid + if (hasError) { + return; + } + + // Proceed with API authentication + console.log('Login attempt with:', email); + }; + + return ( +
+
+

+ {t('LOGIN.TITLE')} +

+ +
+
+ + { + setEmail(e.target.value); + }} + className={`w-full bg-slate-50 dark:bg-slate-950 border ${emailError ? 'border-red-500' : 'border-slate-200 dark:border-slate-800'} rounded-lg px-4 py-3 text-slate-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500`} + placeholder="player@tobeplayed.com" + /> + {emailError && ( +

+ {emailError} +

+ )} +
+
+ + { + setPassword(e.target.value); + }} + className={`w-full bg-slate-50 dark:bg-slate-950 border ${passwordError ? 'border-red-500' : 'border-slate-200 dark:border-slate-800'} rounded-lg px-4 py-3 text-slate-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500`} + placeholder="••••••••" + /> + {passwordError && ( +

+ {passwordError} +

+ )} +
+ +
+
+
+ ); +} \ No newline at end of file