pelagia-portal/App/app/(portal)/layout.tsx
2026-05-18 23:18:58 +05:30

78 lines
2.6 KiB
TypeScript

import { auth } from "@/auth";
import { db } from "@/lib/db";
import { redirect } from "next/navigation";
import { Sidebar } from "@/components/layout/sidebar";
import { Header } from "@/components/layout/header";
import { MobileHeader } from "@/components/layout/mobile-header";
import { MobileBottomNav } from "@/components/layout/mobile-bottom-nav";
import { DesktopRequired } from "@/components/layout/desktop-required";
// Roles that have a useful mobile experience
const MOBILE_ROLES = ["MANAGER", "SUPERUSER", "ACCOUNTS"] as const;
export default async function PortalLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await auth();
if (!session?.user) redirect("/login");
const hasMobile = (MOBILE_ROLES as readonly string[]).includes(session.user.role);
const [notifications, unreadCount] = await Promise.all([
db.notification.findMany({
where: { userId: session.user.id },
orderBy: { sentAt: "desc" },
take: 20,
select: { id: true, body: true, link: true, isRead: true, sentAt: true, poId: true },
}),
db.notification.count({
where: { userId: session.user.id, isRead: false },
}),
]);
// Dates must be serialised before being passed to Client Components
const serialisedNotifications = notifications.map((n) => ({
...n,
sentAt: n.sentAt.toISOString(),
}));
return (
<div className="flex h-screen overflow-hidden bg-neutral-50">
{/* Desktop sidebar — hidden on small screens */}
<div className="hidden md:flex">
<Sidebar userRole={session.user.role} />
</div>
<div className="flex flex-1 flex-col overflow-hidden">
{/* Mobile top bar */}
<MobileHeader
user={session.user}
initialUnreadCount={unreadCount}
initialNotifications={serialisedNotifications}
/>
{/* Desktop top bar — hidden on small screens */}
<div className="hidden md:block">
<Header
user={session.user}
initialUnreadCount={unreadCount}
initialNotifications={serialisedNotifications}
/>
</div>
{/* Page content — add bottom padding on mobile to clear the bottom nav */}
<main className="flex-1 overflow-y-auto p-4 md:p-6 pb-20 md:pb-6">
{children}
</main>
{/* Mobile bottom nav — roles with a mobile experience */}
{hasMobile && <MobileBottomNav role={session.user.role} />}
{/* Full-screen overlay for roles without a mobile experience */}
{!hasMobile && <DesktopRequired />}
</div>
</div>
);
}