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:
@@ -82,21 +82,8 @@ export default function Home() {
|
|||||||
{/* Laufende Partien – Live-Kacheln */}
|
{/* Laufende Partien – Live-Kacheln */}
|
||||||
<LiveGames />
|
<LiveGames />
|
||||||
|
|
||||||
{/* Öffentliche Spiele + Beitreten */}
|
{/* Öffentliche Spiele */}
|
||||||
<div className={`grid gap-6 grid-cols-1 ${isLoggedIn ? "lg:grid-cols-3" : ""}`}>
|
<PublicGames />
|
||||||
<div className={isLoggedIn ? "lg:col-span-2" : ""}>
|
|
||||||
<PublicGames />
|
|
||||||
</div>
|
|
||||||
{isLoggedIn && (
|
|
||||||
<div className="card bg-base-200 shadow h-fit">
|
|
||||||
<div className="card-body">
|
|
||||||
<h2 className="card-title text-lg">🔗 Einladung annehmen</h2>
|
|
||||||
<p className="text-sm opacity-70 mb-2">Link oder Code eingeben</p>
|
|
||||||
<JoinGame />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
"use client";
|
"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 Link from "next/link";
|
||||||
import NotificationPanel from "./NotificationPanel";
|
import NotificationPanel from "./NotificationPanel";
|
||||||
import ThemeToggle from "./ThemeToggle";
|
import ThemeToggle from "./ThemeToggle";
|
||||||
import { useSession } from "@/context/session";
|
import { useSession } from "@/context/session";
|
||||||
|
import { logout } from "@/lib/auth";
|
||||||
|
import JoinGame from "./home/JoinGame";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const { user } = useSession();
|
const { user, setUser } = useSession();
|
||||||
|
|
||||||
|
async function handleLogout() {
|
||||||
|
await logout();
|
||||||
|
setUser(null);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
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">
|
<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>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-none gap-1">
|
<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?.id && typeof user.id === "number" && <NotificationPanel />}
|
||||||
{user?.elo != null && typeof user.id === "number" && (
|
{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">
|
<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>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<label tabIndex={0} htmlFor="auth-modal" className="btn btn-ghost btn-circle avatar">
|
|
||||||
<div className="w-10 rounded-full">
|
{user?.id && typeof user.id === "number" ? (
|
||||||
<IconUser className="m-auto block h-full" />
|
<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>
|
</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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { SessionContext } from "@/context/session";
|
import { SessionContext } from "@/context/session";
|
||||||
import { login, logout, register, setGuestSession } from "@/lib/auth";
|
import { login, register, setGuestSession } from "@/lib/auth";
|
||||||
import Link from "next/link";
|
|
||||||
import type { FormEvent } from "react";
|
import type { FormEvent } from "react";
|
||||||
import { useContext, useEffect, useRef, useState } from "react";
|
import { useContext, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { IconSettings2, IconUserCircle } from "@tabler/icons-react";
|
|
||||||
import Guest from "./Guest";
|
import Guest from "./Guest";
|
||||||
import Login from "./Login";
|
import Login from "./Login";
|
||||||
import Register from "./Register";
|
import Register from "./Register";
|
||||||
@@ -18,15 +16,6 @@ export default function AuthModal() {
|
|||||||
const [buttonLoading, setButtonLoading] = useState(false);
|
const [buttonLoading, setButtonLoading] = useState(false);
|
||||||
const modalToggleRef = useRef<HTMLInputElement>(null);
|
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>) {
|
async function submitAuth(e: FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -105,102 +94,65 @@ export default function AuthModal() {
|
|||||||
className={"modal" + (session?.user === null ? " modal-open" : "")}
|
className={"modal" + (session?.user === null ? " modal-open" : "")}
|
||||||
>
|
>
|
||||||
<label className="modal-box flex max-w-sm flex-col gap-4 pt-2">
|
<label className="modal-box flex max-w-sm flex-col gap-4 pt-2">
|
||||||
{session?.user?.id && typeof session.user.id === "number" ? (
|
<div className="tabs flex-nowap w-full">
|
||||||
<div className="flex flex-col gap-2 pt-2">
|
<span
|
||||||
<div className="flex w-full justify-between">
|
onClick={() => {
|
||||||
<div>
|
setActiveTab("guest");
|
||||||
Logged in as <b>{session.user.name}</b>
|
}}
|
||||||
</div>
|
className={
|
||||||
<a className="link" onClick={clickLogout}>
|
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||||
Logout
|
(activeTab === "guest"
|
||||||
</a>
|
? " tab-active text-base-content"
|
||||||
</div>
|
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||||
<div className="flex w-full flex-col">
|
}
|
||||||
<Link
|
>
|
||||||
className="btn btn-ghost gap-1 normal-case"
|
Guest
|
||||||
href={`/user/${session.user.name}`}
|
</span>
|
||||||
onClick={() => ((modalToggleRef.current as HTMLInputElement).checked = false)}
|
<span
|
||||||
>
|
onClick={() => setActiveTab("login")}
|
||||||
<IconUserCircle /> View profile
|
className={
|
||||||
</Link>
|
"tab tab-bordered tab-border-2 flex-grow rounded-tl-lg" +
|
||||||
<Link
|
(activeTab === "login"
|
||||||
className="btn btn-ghost gap-1 normal-case"
|
? " tab-active"
|
||||||
href="/settings"
|
: " text-base-content border-opacity-10 hover:border-opacity-30")
|
||||||
onClick={() => ((modalToggleRef.current as HTMLInputElement).checked = false)}
|
}
|
||||||
>
|
>
|
||||||
<IconSettings2 /> Account settings
|
Login
|
||||||
</Link>
|
</span>
|
||||||
</div>
|
<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">
|
<form className="flex flex-col px-2" onSubmit={submitAuth}>
|
||||||
<label htmlFor="auth-modal" className="btn">
|
{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
|
Close
|
||||||
</label>
|
</label>
|
||||||
</div>
|
)}
|
||||||
|
<button className={"btn" + (buttonLoading ? " loading" : "")} type="submit">
|
||||||
|
{activeTab === "guest" && "Confirm"}
|
||||||
|
{activeTab === "login" && "Login"}
|
||||||
|
{activeTab === "register" && "Register"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</form>
|
||||||
<>
|
|
||||||
<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>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</label>
|
</label>
|
||||||
</label>
|
</label>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user