78 lines
2.6 KiB
TypeScript
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>
|
|
);
|
|
}
|