DropdownMenuLabel (rendert Menu.GroupLabel) stand direkt im DropdownMenuContent statt in einem DropdownMenuGroup - Base UI wirft dafür beim Öffnen einen ungefangenen Fehler, der den kompletten React-Baum crasht. Das war die eigentliche Ursache des seit Sitzungsbeginn gemeldeten "This page couldn't load" beim Klick auf das Profilbild oben rechts, nicht der Service Worker (der war schon korrekt).
94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { ReactNode, useEffect } from "react";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { Hexagon } from "lucide-react";
|
|
import { useAuth } from "@/lib/auth/auth-context";
|
|
import { NotificationBell } from "@/components/notification-bell";
|
|
import { OfflineSyncBadge } from "@/components/offline-sync-badge";
|
|
import { UserAvatar } from "@/components/user-avatar";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuGroup,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { Sidebar } from "./sidebar";
|
|
import { BottomNav } from "./bottom-nav";
|
|
import { QuickAddMenu } from "./quick-add-menu";
|
|
import { PageTransition } from "./page-transition";
|
|
|
|
export default function AppLayout({ children }: { children: ReactNode }) {
|
|
const router = useRouter();
|
|
const { user, isLoading, logout } = useAuth();
|
|
|
|
useEffect(() => {
|
|
if (!isLoading && !user) {
|
|
router.replace("/login");
|
|
}
|
|
}, [isLoading, user, router]);
|
|
|
|
if (isLoading || !user) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-screen">
|
|
<Sidebar user={user} />
|
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
<header className="sticky top-0 z-10 flex items-center justify-between gap-3 bg-card/85 px-4 py-3 shadow-[var(--shadow-soft)] backdrop-blur-md sm:justify-end sm:px-6">
|
|
<Link
|
|
href="/dashboard"
|
|
className="flex items-center gap-1.5 font-heading font-semibold sm:hidden"
|
|
>
|
|
<span
|
|
className="flex size-7 items-center justify-center rounded-lg"
|
|
style={{ background: "var(--gradient-honey)" }}
|
|
>
|
|
<Hexagon className="size-4 fill-white/25 text-white" strokeWidth={2.25} />
|
|
</span>
|
|
ApiCore
|
|
</Link>
|
|
<div className="flex shrink-0 items-center gap-2 text-sm">
|
|
<OfflineSyncBadge />
|
|
<NotificationBell />
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger className="rounded-full">
|
|
<UserAvatar name={user.name} hasAvatar={user.hasAvatar} size="sm" />
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuGroup>
|
|
<DropdownMenuLabel className="max-w-48 truncate font-normal text-muted-foreground">
|
|
{user.email}
|
|
</DropdownMenuLabel>
|
|
</DropdownMenuGroup>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem render={<Link href="/profile" />}>
|
|
Profil
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
variant="destructive"
|
|
onClick={() => {
|
|
void logout().then(() => router.push("/login"));
|
|
}}
|
|
>
|
|
Abmelden
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</header>
|
|
<div className="flex-1 overflow-y-auto pb-16 sm:pb-0">
|
|
<PageTransition>{children}</PageTransition>
|
|
</div>
|
|
</div>
|
|
<BottomNav />
|
|
<QuickAddMenu />
|
|
</div>
|
|
);
|
|
}
|