"feat: Added debug server and related components for improved development experience"
This commit is contained in:
85
frontend/src/components/header/index.tsx
Normal file
85
frontend/src/components/header/index.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { HeaderNavigation } from "@/components/header/navigation";
|
||||
import { LoginButton } from "@/components/login-button";
|
||||
import { LogoutButton } from "@/components/logout-button";
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { validateRequest } from "@/server/auth";
|
||||
import { UserRole, hasRole } from "@/server/auth/permissions";
|
||||
import { StickerIcon, UserIcon, WrenchIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { If, Then } from "react-if";
|
||||
|
||||
function getInitials(name: string | undefined) {
|
||||
if (!name) return "";
|
||||
|
||||
const parts = name.split(" ");
|
||||
if (parts.length === 1) return parts[0].slice(0, 2);
|
||||
|
||||
return parts[0].charAt(0) + parts[parts.length - 1].charAt(0);
|
||||
}
|
||||
|
||||
export async function Header() {
|
||||
const { user } = await validateRequest();
|
||||
|
||||
return (
|
||||
<header className="h-16 bg-neutral-900 border-b-4 border-neutral-600 text-white select-none shadow-md">
|
||||
<div className="px-8 h-full max-w-screen-2xl w-full mx-auto flex items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-8">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<StickerIcon size={20} />
|
||||
<h1 className="text-lg font-mono">MYP</h1>
|
||||
</Link>
|
||||
<HeaderNavigation />
|
||||
</div>
|
||||
|
||||
{user != null && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Avatar>
|
||||
<AvatarFallback className="bg-neutral-700">
|
||||
<span className="font-semibold">{getInitials(user?.displayName)}</span>
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Mein Account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/my/profile/" className="flex items-center gap-2">
|
||||
<UserIcon className="w-4 h-4" />
|
||||
<span>Mein Profil</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<If condition={hasRole(user, UserRole.ADMIN)}>
|
||||
<Then>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/admin/" className="flex items-center gap-2">
|
||||
<WrenchIcon className="w-4 h-4" />
|
||||
<span>Adminbereich</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</Then>
|
||||
</If>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<LogoutButton />
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
{user == null && <LoginButton />}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
49
frontend/src/components/header/navigation.tsx
Normal file
49
frontend/src/components/header/navigation.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
"use client";
|
||||
import { cn } from "@/utils/styles";
|
||||
import { ContactRoundIcon, LayersIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
interface Site {
|
||||
name: string;
|
||||
icon: JSX.Element;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export function HeaderNavigation() {
|
||||
const pathname = usePathname();
|
||||
const sites: Site[] = [
|
||||
{
|
||||
name: "Dashboard",
|
||||
icon: <LayersIcon className="w-4 h-4" />,
|
||||
path: "/",
|
||||
},
|
||||
/* {
|
||||
name: "Meine Druckaufträge",
|
||||
path: "/my/jobs",
|
||||
}, */
|
||||
{
|
||||
name: "Mein Profil",
|
||||
icon: <ContactRoundIcon className="w-4 h-4" />,
|
||||
path: "/my/profile",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<nav className="font-medium text-sm flex items-center gap-4 flex-row">
|
||||
{sites.map((site) => (
|
||||
<Link
|
||||
key={site.path}
|
||||
href={site.path}
|
||||
className={cn("transition-colors hover:text-neutral-50 flex items-center gap-x-1", {
|
||||
"text-primary-foreground font-semibold": pathname === site.path,
|
||||
"text-neutral-500": pathname !== site.path,
|
||||
})}
|
||||
>
|
||||
{site.icon}
|
||||
<span>{site.name}</span>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user