From 42a66ddda123b9644ec0b38744b17e279fc40004 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 16 May 2025 01:46:51 +0200 Subject: [PATCH] Functionnal login system, tabs for settings, fixed nested anchors warnings and a lot of other things --- frontend/index.html | 2 +- frontend/package.json | 4 +- frontend/pnpm-lock.yaml | 48 ++++-- frontend/src/app.jsx | 15 +- frontend/src/assets/login.svg | 1 + frontend/src/assets/login_small.svg | 1 + frontend/src/components/NavBar/navbar.jsx | 44 +++++- .../src/components/NavBar/navbar.module.css | 37 ++++- frontend/src/pages/about.jsx | 2 +- frontend/src/pages/home.jsx | 2 +- frontend/src/pages/login.jsx | 144 ++++++++++++++++++ frontend/src/pages/mod_page.jsx | 2 +- frontend/src/pages/modpacks.jsx | 2 +- frontend/src/pages/register.jsx | 93 +++++++++++ frontend/src/pages/settings.jsx | 102 +++++++++---- frontend/src/services/api.js | 7 +- frontend/src/styles/content_page.module.css | 5 +- frontend/src/styles/login.module.css | 41 +++++ frontend/src/styles/settings.module.css | 46 ++++++ 19 files changed, 532 insertions(+), 66 deletions(-) create mode 100644 frontend/src/assets/login.svg create mode 100644 frontend/src/assets/login_small.svg create mode 100644 frontend/src/pages/login.jsx create mode 100644 frontend/src/pages/register.jsx create mode 100644 frontend/src/styles/login.module.css create mode 100644 frontend/src/styles/settings.module.css diff --git a/frontend/index.html b/frontend/index.html index 0211e3c..04d2aa4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - WF radio + WOLFforce radio
diff --git a/frontend/package.json b/frontend/package.json index c97f046..0648b66 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,9 @@ "preview": "vite preview" }, "dependencies": { - "preact": "^10.26.5", + "js-cookie": "^3.0.5", + "jwt-decode": "^4.0.0", + "preact": "^10.26.6", "preact-router": "^4.1.2" }, "devDependencies": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index dd14fcf..398e91a 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -8,8 +8,14 @@ importers: .: dependencies: + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 + jwt-decode: + specifier: ^4.0.0 + version: 4.0.0 preact: - specifier: ^10.26.5 + specifier: ^10.26.6 version: 10.26.6 preact-router: specifier: ^4.1.2 @@ -428,8 +434,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - caniuse-lite@1.0.30001717: - resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==} + caniuse-lite@1.0.30001718: + resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -441,8 +447,8 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -463,8 +469,8 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - electron-to-chromium@1.5.151: - resolution: {integrity: sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==} + electron-to-chromium@1.5.155: + resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -507,6 +513,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -520,6 +530,10 @@ packages: engines: {node: '>=6'} hasBin: true + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -679,7 +693,7 @@ snapshots: '@babel/traverse': 7.27.1 '@babel/types': 7.27.1 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -775,7 +789,7 @@ snapshots: '@babel/parser': 7.27.2 '@babel/template': 7.27.2 '@babel/types': 7.27.1 - debug: 4.4.0 + debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -885,7 +899,7 @@ snapshots: '@prefresh/vite': 2.4.7(preact@10.26.6)(vite@6.3.5) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.27.1) - debug: 4.4.0 + debug: 4.4.1 kolorist: 1.8.0 vite: 6.3.5 vite-prerender-plugin: 0.5.10(vite@6.3.5) @@ -988,12 +1002,12 @@ snapshots: browserslist@4.24.5: dependencies: - caniuse-lite: 1.0.30001717 - electron-to-chromium: 1.5.151 + caniuse-lite: 1.0.30001718 + electron-to-chromium: 1.5.155 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.5) - caniuse-lite@1.0.30001717: {} + caniuse-lite@1.0.30001718: {} convert-source-map@2.0.0: {} @@ -1007,7 +1021,7 @@ snapshots: css-what@6.1.0: {} - debug@4.4.0: + debug@4.4.1: dependencies: ms: 2.1.3 @@ -1029,7 +1043,7 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - electron-to-chromium@1.5.151: {} + electron-to-chromium@1.5.155: {} entities@4.5.0: {} @@ -1078,12 +1092,16 @@ snapshots: he@1.2.0: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} jsesc@3.1.0: {} json5@2.2.3: {} + jwt-decode@4.0.0: {} + kolorist@1.8.0: {} lru-cache@5.1.1: diff --git a/frontend/src/app.jsx b/frontend/src/app.jsx index b61c897..5d08b7d 100644 --- a/frontend/src/app.jsx +++ b/frontend/src/app.jsx @@ -6,13 +6,19 @@ import { Router } from 'preact-router'; // Pages import HomePage from './pages/home'; -import ModsPage from './pages/mods'; -import ModpacksPage from './pages/modpacks'; import AboutPage from './pages/about'; -import SettingsPage from './pages/settings'; + +import ModsPage from './pages/mods'; import ModPage from './pages/mod_page' import ModCreationPage from './pages/mod_creation' +import ModpacksPage from './pages/modpacks'; + +import LoginPage from './pages/login'; +import RegisterPage from './pages/register'; +import SettingsPage from './pages/settings'; + + // Components import NavBar from './components/NavBar/navbar' import Button from './components/Buttons/button' @@ -39,6 +45,9 @@ export function App() { + + + diff --git a/frontend/src/assets/login.svg b/frontend/src/assets/login.svg new file mode 100644 index 0000000..1bc327d --- /dev/null +++ b/frontend/src/assets/login.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/login_small.svg b/frontend/src/assets/login_small.svg new file mode 100644 index 0000000..822c05c --- /dev/null +++ b/frontend/src/assets/login_small.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/NavBar/navbar.jsx b/frontend/src/components/NavBar/navbar.jsx index cf93e59..884e6f4 100644 --- a/frontend/src/components/NavBar/navbar.jsx +++ b/frontend/src/components/NavBar/navbar.jsx @@ -1,16 +1,36 @@ // Functions import { h } from 'preact' +import { useEffect, useState } from 'preact/hooks'; import { Link } from 'preact-router/match'; +import Cookies from 'js-cookie' +import { jwtDecode } from 'jwt-decode' // Styles import styles from './navbar.module.css' // Images -import userImg from '../../assets/settings.svg' - +import Settings from '../../assets/settings.svg' +import Login from '../../assets/login_small.svg' +import Account from '../../assets/account.svg' function NavBar({ children, className, ...rest}) { + const [user, setUser] = useState(null); + + useEffect(() => { + const token = Cookies.get('authToken'); + if (token) { + const decoded_token = jwtDecode(token); + if (decoded_token) { + setUser({ username: decoded_token.username}); + } + else { + console.warn('Cannot decode authToken'); + } + } + }, []); + + return ( ) diff --git a/frontend/src/components/NavBar/navbar.module.css b/frontend/src/components/NavBar/navbar.module.css index c52752e..954bc7d 100644 --- a/frontend/src/components/NavBar/navbar.module.css +++ b/frontend/src/components/NavBar/navbar.module.css @@ -42,21 +42,48 @@ /* WIP */ .rightItem { - margin-left: auto; - margin-right: 2em; - margin-top: .3em; + margin: 0 1em; user-select: none; transition: 200ms; } -.rightItem > img { +.rightItems { + margin-left: auto; + margin-right: 1em; + margin-top: .3em; + + display: flex; + +} + +.settings { border-radius: 100rem; transition: 600ms; } -.rightItem > img:hover { +.profile { + border-radius: 100rem; + transition: 300ms; +} + +.login { + border-radius: .5rem; + transition: 600ms; + margin-top: .06em; + height: 80%; +} + +.login:hover { + background-color: #eaeaea10; +} + +.settings:hover { background-color: #eaeaea10; transform: rotate(180deg); +} + +.profile:hover { + background-color: #eaeaea10; } \ No newline at end of file diff --git a/frontend/src/pages/about.jsx b/frontend/src/pages/about.jsx index 5b8ca56..aa72e9d 100644 --- a/frontend/src/pages/about.jsx +++ b/frontend/src/pages/about.jsx @@ -18,7 +18,7 @@ function AboutPage() { <> - +
About us
diff --git a/frontend/src/pages/home.jsx b/frontend/src/pages/home.jsx index e776dd0..072aeb5 100644 --- a/frontend/src/pages/home.jsx +++ b/frontend/src/pages/home.jsx @@ -18,7 +18,7 @@ function HomePage() { <> - +
An open place for mods
diff --git a/frontend/src/pages/login.jsx b/frontend/src/pages/login.jsx new file mode 100644 index 0000000..423fc91 --- /dev/null +++ b/frontend/src/pages/login.jsx @@ -0,0 +1,144 @@ +// Preact +import { h } from 'preact'; +import { useState } from 'preact/hooks'; +import Cookies from 'js-cookie'; + +// Images +import logo from '../assets/logo.png' + +// Styles +import styles from '../styles/login.module.css' + +// Components +import Button from '../components/Buttons/button'; +import InputField from '../components/Fields/input_field'; + +// Functions +import { login } from '../services/api'; + + +function LoginPage() { + + // useState + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [loginStatus, setLoginStatus] = useState(null); // To track success/error + const [fieldErrors, setFieldErrors] = useState({ + username: null, + password: null + }); + + const handleUsernameChange = (event) => { + setUsername(event.target.value); + // Reset error + setFieldErrors(prevErrors => ({ ...prevErrors, username: null })); + }; + + const handlePasswordChange = (event) => { + setPassword(event.target.value); + // Reset error + setFieldErrors(prevErrors => ({ ...prevErrors, password: null })); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); // Prevent the default form submission + setFieldErrors({ name: null, email: null, message: null }); + + setLoginStatus('logging in'); + + try { + const response = await login(username, password); + + console.debug('Logged in successfully:', response); + setLoginStatus('success'); + + if (response && response.token) { + Cookies.set('authToken', response.token, { + expires: 30, + path: '/', + secure: true, // only send over https + sameSite: 'strict' + }) + + window.location.replace("/mods"); + + } else { + console.warn("Couldn't retrieve token from api response"); + } + + + } catch (error) { + + console.error('Login failed:', error); + setLoginStatus('error'); + + //TODO handle different codes differently + setFieldErrors({ + username: ' ', + password: 'Wrong username or password.', + }); + } + finally { + setLoginStatus(null); + } + }; + + return ( + <> + + + + +
+ +
+
+ {/* username */} + + + + {/* password */} + + +
+
+ + +
+
+
+ + + ); +} + +export default LoginPage; \ No newline at end of file diff --git a/frontend/src/pages/mod_page.jsx b/frontend/src/pages/mod_page.jsx index 66dfe79..b49aa88 100644 --- a/frontend/src/pages/mod_page.jsx +++ b/frontend/src/pages/mod_page.jsx @@ -89,7 +89,7 @@ function ModPage({name}) {
- v3.0 + v3.0 (latest)
diff --git a/frontend/src/pages/modpacks.jsx b/frontend/src/pages/modpacks.jsx index 7e4e408..dda30ed 100644 --- a/frontend/src/pages/modpacks.jsx +++ b/frontend/src/pages/modpacks.jsx @@ -18,7 +18,7 @@ function ModpacksPage() { <> WF - modpacks +

modpacks

Coming soon™
diff --git a/frontend/src/pages/register.jsx b/frontend/src/pages/register.jsx new file mode 100644 index 0000000..e2b7bdd --- /dev/null +++ b/frontend/src/pages/register.jsx @@ -0,0 +1,93 @@ +// Functions +import { h } from 'preact'; + +// Images +import logo from '../assets/logo.png' + +// Styles +import styles from '../styles/login.module.css' + +// Components +import Button from '../components/Buttons/button'; +import InputField from '../components/Fields/input_field'; + + +function RegisterPage() { + + const username_input= ""; + const display_name_input= ""; + const email_input= ""; + const password_input= ""; + + return ( + <> + + + + +
+ +
+
+ {/* username */} + + + + {/* display_name */} + + + + {/* email */} + + + + {/* password */} + + +
+
+ +
+
+
+ + + ); +} + +export default RegisterPage; \ No newline at end of file diff --git a/frontend/src/pages/settings.jsx b/frontend/src/pages/settings.jsx index 6d254fa..9d8f28b 100644 --- a/frontend/src/pages/settings.jsx +++ b/frontend/src/pages/settings.jsx @@ -1,45 +1,89 @@ +// Preact import { h } from 'preact'; -import { useState } from 'preact/hooks'; // If you need state for settings +import { useState, useEffect } from 'preact/hooks'; // If you need state for settings +import Cookies from 'js-cookie' +import { jwtDecode } from 'jwt-decode' + +// Styles +import styles from '../styles/settings.module.css' + +// Images +import Logo from '../assets/logo.png' + function SettingsPage() { - const [theme, setTheme] = useState('light'); + const [theme, setTheme] = useState('dark'); const [notificationsEnabled, setNotificationsEnabled] = useState(true); + const [user, setUser] = useState(null) + + + useEffect( () => { + const token = Cookies.get('authToken'); + if (token) { + setUser(token); + } + }, + []) const handleThemeChange = (event) => { setTheme(event.target.value); // Save theme preference (e.g., to local storage) }; - const handleNotificationsChange = (event) => { - setNotificationsEnabled(event.target.checked); - // Save notification preference - }; + const handleNotificationsChange = (event) => { + setNotificationsEnabled(event.target.checked); + // Save notification preference + }; - return ( - <> -

Settings

-
-
- - + const logout = () => { + Cookies.remove('authToken'); + location.replace('/'); + } + + return ( + <> + + WF +

settings

+
+
+ +

Global

+
+ +

User

+
+ {user ? ( + {logout()}}> +

Logout

+
+ ) : + '' + } +
-
- -
-
- - ); +
+
+ + +
+
+ +
+
+ + ); } export default SettingsPage; \ No newline at end of file diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index a16886e..55291bc 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -32,6 +32,9 @@ export async function login(username, password) { try { const response = await fetch(`${API_BASE_URL}/login`, { method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, body: JSON.stringify({ username: username, password: password }) }); if (!response.ok) { @@ -40,8 +43,8 @@ export async function login(username, password) { const data = await response.json(); return data; } catch (error) { - console.error('Failed to fetch items:', error); - return null; + // console.error('Failed to fetch items:', error); + throw error; } } diff --git a/frontend/src/styles/content_page.module.css b/frontend/src/styles/content_page.module.css index 118af4b..3ef7214 100644 --- a/frontend/src/styles/content_page.module.css +++ b/frontend/src/styles/content_page.module.css @@ -82,7 +82,7 @@ background: url("https://resources.oblic-parallels.fr/example.jpg"); background-size: cover; border-top-right-radius: .4rem; - -webkit-mask-image: radial-gradient(ellipse at top right, black 0%, #00000010 50%, transparent 70%); + mask-image: radial-gradient(ellipse at top right, black 0%, #00000010 50%, transparent 70%); z-index: 0; } @@ -164,7 +164,8 @@ margin-bottom: 2em; margin-left: 1em; - padding: .2em; + padding: .2em .4em; + width: fit-content; color: #8a8a8a; font-size: 1.3em; diff --git a/frontend/src/styles/login.module.css b/frontend/src/styles/login.module.css new file mode 100644 index 0000000..0a19e35 --- /dev/null +++ b/frontend/src/styles/login.module.css @@ -0,0 +1,41 @@ +.container { + position: absolute; + left: 35vw; + right: 35vw; + top: 11rem; + bottom: 4rem; + + min-width: 20em; + min-height: 25em; + padding: 2em; + + background-color: #1a1a1a; + border: #3a3a3a solid .1rem; + border-radius: .5rem; + + overflow: scroll; +} + +.loginImage { + display: block; + margin: 1em auto; + width: 10rem; + /* padding: 5em; */ +} + +.buttonsContainer { + padding: 0 15% 5vh 15%; +} + +.fieldsContainer { + /* margin-top: 5vh; */ + padding: 5vh 10%; +} + + +.loginButton { + width: 100%; + padding: .5em 0; + font-size: 1.2em; + margin-bottom: 1em; +} \ No newline at end of file diff --git a/frontend/src/styles/settings.module.css b/frontend/src/styles/settings.module.css new file mode 100644 index 0000000..1d51359 --- /dev/null +++ b/frontend/src/styles/settings.module.css @@ -0,0 +1,46 @@ +.tabsContainer { + position: absolute; + left: 4rem; + top: 11rem; + bottom: 4rem; + /* right: 97rem; */ + + width: 15rem; + + border-radius: .5rem; + + /* background-color: #1a1a1a; */ + +} + +.tab { + position: relative; + left: 4rem; + right: 0; + /* transform: translate(-50% , -50%); */ + + margin: 1em 0; + + padding: .2em .4em; + width: fit-content; + + color: #8a8a8a; + font-size: 1.5em; + border-radius: .5em; + + transition: 200ms; +} + +.tab:hover { + background-color: #3a3a3a; +} + +.logout { + color: #c24040; + cursor: pointer; +} + +.logout:hover { + color: #eaeaea; + background-color: #bc3939; +} \ No newline at end of file