feat(ui): move admin panel and join-game into profile dropdown
- Replace header admin link + profile modal with a dropdown menu - Dropdown contains: profile, settings, admin (admins only), join-game form, logout - Remove join-game card from homepage for logged-in users (now in dropdown) - AuthModal now only handles guest/login/register flows Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { IconUser, IconShield } from "@tabler/icons-react";
|
||||
import { IconUser, IconShield, IconUserCircle, IconSettings2, IconLogout } from "@tabler/icons-react";
|
||||
import Link from "next/link";
|
||||
import NotificationPanel from "./NotificationPanel";
|
||||
import ThemeToggle from "./ThemeToggle";
|
||||
import { useSession } from "@/context/session";
|
||||
import { logout } from "@/lib/auth";
|
||||
import JoinGame from "./home/JoinGame";
|
||||
|
||||
export default function Header() {
|
||||
const { user } = useSession();
|
||||
const { user, setUser } = useSession();
|
||||
|
||||
async function handleLogout() {
|
||||
await logout();
|
||||
setUser(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="navbar border-base-300 dark:border-neutral mx-1 w-auto justify-center border-b-2 md:mx-16 lg:mx-40">
|
||||
@@ -30,12 +37,6 @@ export default function Header() {
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex-none gap-1">
|
||||
{user?.role === "admin" && (
|
||||
<Link href="/admin" className="btn btn-ghost btn-sm gap-1 normal-case" title="Admin-Panel">
|
||||
<IconShield size={16} />
|
||||
Admin
|
||||
</Link>
|
||||
)}
|
||||
{user?.id && typeof user.id === "number" && <NotificationPanel />}
|
||||
{user?.elo != null && typeof user.id === "number" && (
|
||||
<Link href={`/user/${user.name}`} className="btn btn-ghost btn-sm font-mono tabular-nums" title="Deine ELO-Wertung">
|
||||
@@ -43,11 +44,57 @@ export default function Header() {
|
||||
</Link>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
<label tabIndex={0} htmlFor="auth-modal" className="btn btn-ghost btn-circle avatar">
|
||||
<div className="w-10 rounded-full">
|
||||
<IconUser className="m-auto block h-full" />
|
||||
|
||||
{user?.id && typeof user.id === "number" ? (
|
||||
<div className="dropdown dropdown-end">
|
||||
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
|
||||
<div className="w-10 rounded-full">
|
||||
<IconUser className="m-auto block h-full" />
|
||||
</div>
|
||||
</label>
|
||||
<div tabIndex={0} className="dropdown-content z-50 mt-3 w-80 rounded-box bg-base-100 p-3 shadow-lg">
|
||||
<div className="px-2 pb-2 font-semibold">{user.name}</div>
|
||||
<ul className="menu menu-sm p-0">
|
||||
<li>
|
||||
<Link href={`/user/${user.name}`}>
|
||||
<IconUserCircle size={16} /> Profil
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/settings">
|
||||
<IconSettings2 size={16} /> Einstellungen
|
||||
</Link>
|
||||
</li>
|
||||
{user.role === "admin" && (
|
||||
<li>
|
||||
<Link href="/admin">
|
||||
<IconShield size={16} /> Admin-Panel
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<div className="divider my-2" />
|
||||
<div className="px-2 pb-1 text-xs font-semibold uppercase opacity-50">Spiel beitreten</div>
|
||||
<div className="px-2 pb-2">
|
||||
<JoinGame />
|
||||
</div>
|
||||
<div className="divider my-2" />
|
||||
<ul className="menu menu-sm p-0">
|
||||
<li>
|
||||
<button onClick={handleLogout} className="text-error">
|
||||
<IconLogout size={16} /> Abmelden
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
) : (
|
||||
<label tabIndex={0} htmlFor="auth-modal" className="btn btn-ghost btn-circle avatar">
|
||||
<div className="w-10 rounded-full">
|
||||
<IconUser className="m-auto block h-full" />
|
||||
</div>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { SessionContext } from "@/context/session";
|
||||
import { login, logout, register, setGuestSession } from "@/lib/auth";
|
||||
import Link from "next/link";
|
||||
import { login, register, setGuestSession } from "@/lib/auth";
|
||||
import type { FormEvent } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { IconSettings2, IconUserCircle } from "@tabler/icons-react";
|
||||
import Guest from "./Guest";
|
||||
import Login from "./Login";
|
||||
import Register from "./Register";
|
||||
@@ -18,15 +16,6 @@ export default function AuthModal() {
|
||||
const [buttonLoading, setButtonLoading] = useState(false);
|
||||
const modalToggleRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
async function clickLogout() {
|
||||
if (serverMessage) {
|
||||
setServerMessage(null);
|
||||
}
|
||||
setActiveTab("login");
|
||||
await logout();
|
||||
session?.setUser(null);
|
||||
}
|
||||
|
||||
async function submitAuth(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -105,102 +94,65 @@ export default function AuthModal() {
|
||||
className={"modal" + (session?.user === null ? " modal-open" : "")}
|
||||
>
|
||||
<label className="modal-box flex max-w-sm flex-col gap-4 pt-2">
|
||||
{session?.user?.id && typeof session.user.id === "number" ? (
|
||||
<div className="flex flex-col gap-2 pt-2">
|
||||
<div className="flex w-full justify-between">
|
||||
<div>
|
||||
Logged in as <b>{session.user.name}</b>
|
||||
</div>
|
||||
<a className="link" onClick={clickLogout}>
|
||||
Logout
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex w-full flex-col">
|
||||
<Link
|
||||
className="btn btn-ghost gap-1 normal-case"
|
||||
href={`/user/${session.user.name}`}
|
||||
onClick={() => ((modalToggleRef.current as HTMLInputElement).checked = false)}
|
||||
>
|
||||
<IconUserCircle /> View profile
|
||||
</Link>
|
||||
<Link
|
||||
className="btn btn-ghost gap-1 normal-case"
|
||||
href="/settings"
|
||||
onClick={() => ((modalToggleRef.current as HTMLInputElement).checked = false)}
|
||||
>
|
||||
<IconSettings2 /> Account settings
|
||||
</Link>
|
||||
</div>
|
||||
<div className="tabs flex-nowap w-full">
|
||||
<span
|
||||
onClick={() => {
|
||||
setActiveTab("guest");
|
||||
}}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "guest"
|
||||
? " tab-active text-base-content"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Guest
|
||||
</span>
|
||||
<span
|
||||
onClick={() => setActiveTab("login")}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "login"
|
||||
? " tab-active"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Login
|
||||
</span>
|
||||
<span
|
||||
onClick={() => setActiveTab("register")}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "register"
|
||||
? " tab-active"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Register
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="modal-action">
|
||||
<label htmlFor="auth-modal" className="btn">
|
||||
<form className="flex flex-col px-2" onSubmit={submitAuth}>
|
||||
{activeTab === "guest" && (
|
||||
<Guest currentName={session?.user?.name || "unknown user"} />
|
||||
)}
|
||||
{activeTab === "login" && <Login />}
|
||||
{activeTab === "register" && <Register />}
|
||||
|
||||
{serverMessage && <div className="text-error mt-2">{serverMessage}</div>}
|
||||
<div className="modal-action items-center">
|
||||
{session?.user !== null && (
|
||||
<label htmlFor="auth-modal" className="btn btn-ghost">
|
||||
Close
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<button className={"btn" + (buttonLoading ? " loading" : "")} type="submit">
|
||||
{activeTab === "guest" && "Confirm"}
|
||||
{activeTab === "login" && "Login"}
|
||||
{activeTab === "register" && "Register"}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="tabs flex-nowap w-full">
|
||||
<span
|
||||
onClick={() => {
|
||||
setActiveTab("guest");
|
||||
}}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "guest"
|
||||
? " tab-active text-base-content"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Guest
|
||||
</span>
|
||||
<span
|
||||
onClick={() => setActiveTab("login")}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "login"
|
||||
? " tab-active"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Login
|
||||
</span>
|
||||
<span
|
||||
onClick={() => setActiveTab("register")}
|
||||
className={
|
||||
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||
(activeTab === "register"
|
||||
? " tab-active"
|
||||
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||
}
|
||||
>
|
||||
Register
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form className="flex flex-col px-2" onSubmit={submitAuth}>
|
||||
{activeTab === "guest" && (
|
||||
<Guest currentName={session?.user?.name || "unknown user"} />
|
||||
)}
|
||||
{activeTab === "login" && <Login />}
|
||||
{activeTab === "register" && <Register />}
|
||||
|
||||
{serverMessage && <div className="text-error mt-2">{serverMessage}</div>}
|
||||
<div className="modal-action items-center">
|
||||
{session?.user !== null && (
|
||||
<label htmlFor="auth-modal" className="btn btn-ghost">
|
||||
Close
|
||||
</label>
|
||||
)}
|
||||
<button className={"btn" + (buttonLoading ? " loading" : "")} type="submit">
|
||||
{activeTab === "guest" && "Confirm"}
|
||||
{activeTab === "login" && "Login"}
|
||||
{activeTab === "register" && "Register"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</label>
|
||||
</label>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user