From 27c5a6940a0cf0cd5fdfb4de7fed95a5caefb709 Mon Sep 17 00:00:00 2001 From: "t.indorf" Date: Fri, 22 May 2026 15:50:03 +0200 Subject: [PATCH] feat: implement premium responsive mobile shell and drawer --- src/app/dashboard/layout.tsx | 91 ++--------- src/components/dashboard/dashboard-shell.tsx | 162 +++++++++++++++++++ 2 files changed, 179 insertions(+), 74 deletions(-) create mode 100644 src/components/dashboard/dashboard-shell.tsx diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index e44f15c..b69623a 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -1,11 +1,9 @@ import type { Metadata } from "next"; -import { LogOut } from "lucide-react"; import { signOut } from "@/auth"; import { requireKitaSession } from "@/lib/auth-utils"; import { prisma } from "@/lib/prisma"; -import { Button } from "@/components/ui/button"; -import { SidebarNav } from "@/components/dashboard/sidebar-nav"; +import { DashboardShell } from "@/components/dashboard/dashboard-shell"; export const metadata: Metadata = { robots: { @@ -15,11 +13,11 @@ export const metadata: Metadata = { }; // ===================================================================== -// Dashboard-Layout · Linke Sidebar +// Dashboard-Layout · Responsive App Shell // --------------------------------------------------------------------- // Server Component: liest kitaId aus der Session, holt den Kita-Namen -// aus der DB und rendert das App-Shell (Sidebar + main content area). -// Der eigentliche Seiteninhalt kommt via {children} rein. +// aus der DB und rendert die responsive DashboardShell (mit Desktop-Sidebar +// und Mobile-Drawer). // ===================================================================== export default async function DashboardLayout({ @@ -35,75 +33,20 @@ export default async function DashboardLayout({ }); const displayName = session.user.name ?? session.user.email ?? "Kita-Planer"; + async function handleSignOut() { + "use server"; + await signOut({ redirectTo: "/" }); + } + return ( -
- {/* ── Sidebar ───────────────────────────────────────────────────── */} - - - {/* ── Main Content ──────────────────────────────────────────────── */} -
- {children} -
-
+ + {children} + ); } -function getInitials(value: string) { - const parts = value.trim().split(/\s+/).filter(Boolean); - if (parts.length >= 2) { - return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); - } - - return value.slice(0, 2).toUpperCase(); -} diff --git a/src/components/dashboard/dashboard-shell.tsx b/src/components/dashboard/dashboard-shell.tsx new file mode 100644 index 0000000..d40c807 --- /dev/null +++ b/src/components/dashboard/dashboard-shell.tsx @@ -0,0 +1,162 @@ +"use client"; + +import React, { useState } from "react"; +import { LogOut, Menu, X } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { SidebarNav } from "./sidebar-nav"; +import { UserRole } from "@prisma/client"; + +type DashboardShellProps = { + kitaName: string; + userName: string; + userRole: UserRole; + children: React.ReactNode; + signOutAction: () => void | Promise; +}; + +export function DashboardShell({ + kitaName, + userName, + userRole, + children, + signOutAction, +}: DashboardShellProps) { + const [isOpen, setIsOpen] = useState(false); + + const displayName = userName; + const initials = getInitials(displayName); + + return ( +
+ {/* ── Desktop Sidebar ── */} + + + {/* ── Mobile Top Header Bar ── */} +
+
+
+ {kitaName.slice(0, 1).toUpperCase()} +
+
+

+ {kitaName} +

+
+
+ + +
+ + {/* ── Mobile Sidebar Drawer/Overlay ── */} + {isOpen && ( +
+ {/* Backdrop */} +
setIsOpen(false)} + /> + {/* Drawer content */} + +
+ )} + + {/* ── Main Content Area ── */} +
+ {children} +
+
+ ); +} + +function getInitials(value: string) { + const parts = value.trim().split(/\s+/).filter(Boolean); + if (parts.length >= 2) { + return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); + } + return value.slice(0, 2).toUpperCase(); +}