」 feat(src/front): taskbar to access profile and settings for user management is done :D and also its very pretty :3

This commit is contained in:
y-syo
2025-10-12 13:56:10 +02:00
parent 26b16749bb
commit 1141fe3159
7 changed files with 857 additions and 768 deletions

View File

@ -12,7 +12,7 @@
<!--body class="bg-gray-100 dark:bg-neutral-950 h-screen flex flex-col"-->
<body class="bg-neutral-950 dark:bg-[url(https://api.kanel.ovh/random)] bg-center bg-cover h-screen flex flex-col">
<body class="bg-[url(https://y-syo.me/res/bg.jpg)] dark:bg-[url(https://api.kanel.ovh/random)] bg-center bg-cover h-screen flex flex-col">
<!--script src="https://kanel.ovh/oneko.js"></script-->
<!--script src="./static/ts/oneko.js"></script-->
@ -35,15 +35,22 @@
<div id="app" class="flex-1 flex items-center justify-center">
</div>
<div class="border-t-2 border-neutral-300 dark:border-neutral-800 sticky bottom-0">
<nav class="bg-neutral-200 dark:bg-neutral-900 shadow-md border-t-2 border-neutral-400 dark:border-neutral-700 px-4 sm:px-6 lg:px-8 flex justify-start h-12 items-center space-x-6 font-[Kubasta]">
<a id="profile-button" class="text-neutral-900 hover:text-neutral-700 dark:text-white dark:hover:text-neutral-400" href="/login" data-link>login</a>
<a class="text-neutral-900 hover:text-neutral-700 dark:text-white dark:hover:text-neutral-400" href="/" data-link>home</a>
</nav>
</div>
<div id="taskbar-menu" class="absolute bottom-13 left-0"></div>
<div class="border-t-2 border-neutral-300 dark:border-neutral-800 sticky bottom-0">
<nav class="bg-neutral-200 dark:bg-neutral-900 shadow-md border-t-2 border-neutral-400 dark:border-neutral-700 flex justify-between h-12 items-center content-center space-x-6 font-[Kubasta]">
<div class="flex px-4 items-center content-center space-x-2">
<button id="profile-button" class="taskbar-button flex flex-row justify-center items-center"><img class="object-scale-down mr-2 h-5 w-5" src="https://api.kanel.ovh/id?id=65" /> start</button>
<div class="text-neutral-700 dark:text-neutral-400">|</div>
<a class="taskbar-button" href="https://rusty.42angouleme.fr/">rusty</a>
<a class="taskbar-button" href="/tetris" data-link>tetris</a>
</div>
<div class="reverse-border m-1.5 h-8/10 content-center">
<span id="taskbar-clock" class="text- neutral-900 dark:text-white px-4">12:37</span>
</div>
</nav>
</div>
<script type="module" src="/static/ts/main.ts"></script>
</body>
</html>

View File

@ -42,8 +42,37 @@
text-neutral-900 dark:text-white
px-4 py-2
delay-0 duration-150 transition-colors
border-2 border-t-neutral-100 dark:border-t-neutral-500 border-l-neutral-100 dark:border-l-neutral-500 border-r-neutral-400 dark:border-r-neutral-700 border-b-neutral-400 dark:border-b-neutral-700
active:border-t-neutral-400 dark:active:border-t-neutral-700 active:border-l-neutral-400 dark:active:border-l-neutral-700 active:border-r-neutral-100 dark:active:border-r-neutral-500 active:border-b-neutral-100 dark:active:border-b-neutral-500
;
}
.taskbar-button {
@apply shadow-2x1
bg-neutral-200 hover:bg-neutral-300 active:bg-neutral-300 dark:bg-neutral-800 dark:hover:bg-neutral-700 dark:active:bg-neutral-700
text-neutral-900 dark:text-white
px-4 py-0.5
content-center text-center
delay-0 duration-150 transition-colors
border-2 border-t-neutral-100 dark:border-t-neutral-500 border-l-neutral-100 dark:border-l-neutral-500 border-r-neutral-400 dark:border-r-neutral-700 border-b-neutral-400 dark:border-b-neutral-700
active:border-t-neutral-400 dark:active:border-t-neutral-700 active:border-l-neutral-400 dark:active:border-l-neutral-700 active:border-r-neutral-100 dark:active:border-r-neutral-500 active:border-b-neutral-100 dark:active:border-b-neutral-500
;
}
.menu-default-button {
@apply w-46 h-12
text-neutral-900 dark:text-white
bg-neutral-200 hover:bg-neutral-300
dark:bg-neutral-800 dark:hover:bg-neutral-700
;
}
.menu-default-label {
@apply w-46 h-12
text-neutral-900 dark:text-white
bg-neutral-200
dark:bg-neutral-800
;
}

View File

@ -1,6 +1,8 @@
import { oneko } from "./oneko.ts";
import Profile from "./views/Profile.ts";
let profile_view = new Profile;
export async function isLogged(): boolean {
export async function isLogged(): Promise<boolean> {
let uuid_req = await fetch("http://localhost:3001/me", {
method: "GET",
credentials: "include",
@ -9,63 +11,11 @@ export async function isLogged(): boolean {
{
let uuid = await uuid_req.json();
document.cookie = `uuid=${uuid.user};max-age=${60*60*24*7}`;
const old_button = document.getElementById("profile-button");
const dropdown = document.createElement("div");
dropdown.classList.add("relative", "inline-block", "group");
dropdown.id = "profile-button";
const button_dropdown = dropdown.appendChild(document.createElement("button"));
button_dropdown.innerHTML = uuid.user;
button_dropdown.classList.add("text-neutral-900", "group-hover:text-neutral-700", "dark:text-white", "dark:group-hover:text-neutral-400");
const menu_div = dropdown.appendChild(document.createElement("div"));
menu_div.classList.add("float:right", "hidden", "absolute", "left-0", "bottom-full", "dark:bg-neutral-800", "dark:text-white", "min-w-[160px]", "shadow-lg", "z-10", "group-hover:block");
const profile_a = menu_div.appendChild(document.createElement("a"));
const settings_a = menu_div.appendChild(document.createElement("a"));
const logout_button = menu_div.appendChild(document.createElement("button"));
profile_a.text = "profile";
profile_a.classList.add("block", "no-underline", "px-4", "py-3");
profile_a.href = "/profile";
profile_a.setAttribute("data-link", "");
settings_a.text = "settings";
settings_a.classList.add("block", "no-underline", "px-4", "py-3");
settings_a.href = "/settings";
settings_a.setAttribute("data-link", "");
logout_button.innerHTML = "logout";
logout_button.classList.add("block", "no-underline", "px-4", "py-3");
logout_button.id = "logout-button";
//document.getElementById("logout-button")?.addEventListener("click", async () => {
logout_button.addEventListener("click", async () => {
let req = await fetch("http://localhost:3001/logout", {
method: "GET",
credentials: "include",
});
if (req.status === 200)
isLogged();
else
console.error("logout failed");
});
old_button.replaceWith(dropdown);
return true;
}
else // 401
{
document.cookie = `uuid=;max-age=0`;
const old_button = document.getElementById("profile-button");
const login_button = document.createElement("a");
login_button.id = "profile-button";
login_button.text = "login";
login_button.classList.add("text-neutral-900", "hover:text-neutral-700", "dark:text-white", "dark:hover:text-neutral-400");
login_button.href = "/login";
login_button.setAttribute("data-link", "");
old_button.replaceWith(login_button);
return false;
}
}
@ -90,8 +40,6 @@ const routes = [
{ path: "/login", view: () => import("./views/LoginPage.ts") },
{ path: "/register", view: () => import("./views/RegisterPage.ts") },
{ path: "/profile", view: () => import("./views/Profile.ts") },
];
const router = async () => {
@ -107,7 +55,7 @@ const router = async () => {
if (view)
view.running = false;
//console.log(match);
const module = await match.route.view();
@ -117,12 +65,18 @@ const router = async () => {
view.run();
};
document.getElementById("profile-button")?.addEventListener("click", () => {profile_view.run();});
window.addEventListener("popstate", router);
document.addEventListener("DOMContentLoaded", () => {
isLogged();
document.body.addEventListener("click", e=> {
if (!e.target.closest("#taskbar-menu") && !e.target.matches("#profile-button"))
{
profile_view.open = false;
document.getElementById("taskbar-menu").innerHTML = "";
}
if (e.target.matches("[data-link]"))
{
e.preventDefault();
@ -144,3 +98,21 @@ document.addEventListener("DOMContentLoaded", () => {
});
oneko();
function updateClock()
{
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const months = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
const clock = document.getElementById("taskbar-clock");
const now = new Date();
let hours = now.getHours();
let minutes = now.getMinutes();
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
clock.innerHTML = `${days[now.getDay()]} ${now.getDate()} ${months[now.getMonth()]} ` + hours + ":" + minutes;
}
setInterval(updateClock, 5000);
updateClock();

View File

@ -1,4 +1,5 @@
import Aview from "./Aview.ts"
import { dragElement } from "./drag.js"
import { setOnekoState } from "../oneko.ts"
import { isLogged, navigationManager } from "../main.ts"
@ -13,22 +14,30 @@ export default class extends Aview {
async getHTML() {
return `
<form method="dialog" class="text-center p-10 bg-white dark:bg-neutral-800 rounded-xl shadow space-y-4 flex flex-col">
<h1 class="text-4xl font-bold text-blue-600">login</h1>
<div id="window" class="absolute default-border">
<div id="window-header" class="bg-linear-to-r from-orange-200 to-orange-300 flex flex-row min-w-75 justify-between px-2">
<span class="font-[Kubasta]">login.ts</span>
<div>
<button> - </button>
<button> □ </button>
<a href="/" data-link> × </a>
</div>
</div>
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 border rounded-md w-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required></input>
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 border w-full px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required></input>
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
<button id="login-button" type="submit" class="bg-blue-600 text-white hover:bg-blue-500 w-full py-2 rounded-md transition-colors">login</button>
<a class="text-gray-400 dark:text-gray-600 underline" href="/register" data-link>
register
</a>
</form>
<form method="dialog" class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
<h1 class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome back ! please login.</h1>
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
</br>
<button id="login-button" type="submit" class="default-button w-full">login</button>
</form>
</div>
`;
}
async run() {
dragElement(document.getElementById("window"));
const login = async () => {
const username = (document.getElementById("username") as HTMLInputElement).value;
const password = (document.getElementById("password") as HTMLInputElement).value;

View File

@ -6,36 +6,81 @@ export default class extends Aview {
constructor()
{
super();
if (!isLogged())
navigationManager("/login");
this.setTitle("profile");
}
async getHTML() {
return `
<div id="main-window" class="text-center p-10 bg-white dark:bg-neutral-800 rounded-xl shadow space-y-4">
<div id="main-window" class="default-border shadow-2x1 bg-neutral-200 dark:bg-neutral-800">
<div class="flex flex-row items-stretch">
<div class="inline-block bg-linear-to-b from-orange-200 to-orange-300 min-h-84 w-6 relative">
<!--div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">knl_meowscendence</div-->
<div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">girls kissing :3</div>
</div>
<div class="flex flex-col items-center">
<div id="profile-items" class="flex flex-col items-center">
</div>
<div id="menu-bottom-div" class="hidden mt-auto flex flex-col items-center">
<hr class="my-2 w-32 reverse-border">
<button id="menu-logout" class="menu-default-button">logout</button>
</div>
</div>
</div>
</div>
`;
}
open: boolean = false;
async run() {
const uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, {
let uuid: String;
if (this.open)
{
this.open = false;
document.getElementById("taskbar-menu").innerHTML = "";
return ;
}
this.open = true;
document.getElementById("taskbar-menu").innerHTML = await this.getHTML();
async function getMainHTML() {
if (!(await isLogged()))
{
document.getElementById("menu-bottom-div").classList.add("hidden");
return `
<a class="menu-default-button inline-flex items-center justify-center" href="/login" data-link>login</a>
<a class="menu-default-button inline-flex items-center justify-center" href="/register" data-link>register</a>
`;
}
document.getElementById("menu-bottom-div").classList.remove("hidden");
uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
return `
<span class="menu-default-label inline-flex items-center justify-center">hi, ${uuid} !</span>
<hr class="my-2 w-32 reverse-border">
<button class="menu-default-button">profile</button>
<button class="menu-default-button">settings</button>
`;
}
document.getElementById("profile-items").innerHTML = await getMainHTML();
/*const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, {
method: "GET",
credentials: "include",
});
if (userdata_req.status == 404)
{
console.error("invalid user");
return ;
}
let userdata = await userdata_req.json();
console.log(userdata_req);
console.log(userdata_req);*/
const main = document.getElementById("main-window");
/*const main = document.getElementById("profile-profile");
const nametag = main.appendChild(document.createElement("span"));
nametag.innerHTML = `Hiiiiii ${userdata.displayName} ! :D`;
@ -44,6 +89,17 @@ export default class extends Aview {
const winrate = main.appendChild(document.createElement("div"));
winrate.innerHTML = `wins: ${userdata.wins} | losses: ${userdata.losses} | winrate: ${userdata.wins / (userdata.wins + userdata.losses)}`;
winrate.classList.add("text-neutral-900", "dark:text-white");
winrate.classList.add("text-neutral-900", "dark:text-white");*/
//console.log(document.getElementById("menu-logout"));
document.getElementById("menu-logout").addEventListener("click", async () => {
let req = await fetch("http://localhost:3001/logout", {
method: "GET",
credentials: "include",
});
if (req.status === 200)
this.run();
else
console.error("logout failed");
});
}
}

View File

@ -1,6 +1,7 @@
import Aview from "./Aview.ts"
import { setOnekoState } from "../oneko.ts"
import { isLogged, navigationManager } from "../main.ts"
import { dragElement } from "./drag.ts";
export default class extends Aview {
@ -13,22 +14,30 @@ export default class extends Aview {
async getHTML() {
return `
<form method="dialog" class="text-center p-10 bg-white dark:bg-neutral-800 rounded-xl shadow space-y-4 flex flex-col">
<h1 class="text-4xl font-bold text-blue-600">register</h1>
<div id="window" class="absolute default-border">
<div id="window-header" class="bg-linear-to-r from-orange-200 to-orange-300 flex flex-row min-w-75 justify-between px-2">
<span class="font-[Kubasta]">register.ts</span>
<div>
<button> - </button>
<button> □ </button>
<a href="/" data-link> × </a>
</div>
</div>
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 border rounded-md w-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" required></input>
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 border w-full px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" required></input>
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
<button id="register-button" type="submit" class="bg-blue-600 text-white hover:bg-blue-500 w-full py-2 rounded-md transition-colors">register</button>
<a class="text-gray-400 dark:text-gray-600 underline" href="/login" data-link>
i already have an account
</a>
</form>
<form method="dialog" class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
<p class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome ! please register.</p>
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
</br>
<button id="register-button" type="submit" class="default-button w-full">register</button>
</form>
</div>
`;
}
async run() {
dragElement(document.getElementById("window"));
const login = async () => {
const username = (document.getElementById("username") as HTMLInputElement).value;
const password = (document.getElementById("password") as HTMLInputElement).value;

File diff suppressed because it is too large Load Diff