File size: 3,913 Bytes
88c4c60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | "use client";
import { useState } from "react";
import { usePathname } from "next/navigation";
import { useNotificationStore } from "@/store/notificationStore";
import Sidebar from "../Sidebar";
import Header from "../Header";
function getToastStyle(type) {
if (type === "success") {
return {
wrapper: "border-green-500/30 bg-green-500/10 text-green-600 dark:text-green-400",
icon: "check_circle",
};
}
if (type === "error") {
return {
wrapper: "border-red-500/30 bg-red-500/10 text-red-600 dark:text-red-400",
icon: "error",
};
}
if (type === "warning") {
return {
wrapper: "border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400",
icon: "warning",
};
}
return {
wrapper: "border-blue-500/30 bg-blue-500/10 text-blue-600 dark:text-blue-400",
icon: "info",
};
}
export default function DashboardLayout({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(false);
const pathname = usePathname();
const notifications = useNotificationStore((state) => state.notifications);
const removeNotification = useNotificationStore((state) => state.removeNotification);
return (
<div className="flex h-screen w-full overflow-hidden bg-bg">
<div className="fixed top-4 right-4 z-[80] flex w-[min(92vw,380px)] flex-col gap-2">
{notifications.map((n) => {
const style = getToastStyle(n.type);
return (
<div
key={n.id}
className={`rounded-lg border px-3 py-2 shadow-lg backdrop-blur-sm ${style.wrapper}`}
>
<div className="flex items-start gap-2">
<span className="material-symbols-outlined text-[18px] leading-5">{style.icon}</span>
<div className="min-w-0 flex-1">
{n.title ? <p className="text-xs font-semibold mb-0.5">{n.title}</p> : null}
<p className="text-xs whitespace-pre-wrap break-words">{n.message}</p>
</div>
{n.dismissible ? (
<button
type="button"
onClick={() => removeNotification(n.id)}
className="text-current/70 hover:text-current"
aria-label="Dismiss notification"
>
<span className="material-symbols-outlined text-[16px]">close</span>
</button>
) : null}
</div>
</div>
);
})}
</div>
{/* Mobile sidebar overlay */}
{sidebarOpen && (
<div
className="fixed inset-0 z-40 bg-black/20 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Sidebar - Desktop */}
<div className="hidden lg:flex">
<Sidebar />
</div>
{/* Sidebar - Mobile */}
<div
className={`fixed inset-y-0 left-0 z-50 transform lg:hidden transition-transform duration-300 ease-in-out ${
sidebarOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<Sidebar onClose={() => setSidebarOpen(false)} />
</div>
{/* Main content */}
<main className="flex flex-col flex-1 h-full min-w-0 relative transition-colors duration-300 isolate">
{/* Faint grid background */}
<div className="landing-grid absolute inset-0 pointer-events-none -z-10" aria-hidden="true" />
<Header key={pathname} onMenuClick={() => setSidebarOpen(true)} />
<div className={`flex-1 overflow-y-auto custom-scrollbar ${pathname === "/dashboard/basic-chat" ? "" : "p-6 lg:p-10"} ${pathname === "/dashboard/basic-chat" ? "flex flex-col overflow-hidden" : ""}`}>
<div className={`${pathname === "/dashboard/basic-chat" ? "flex-1 w-full h-full flex flex-col" : "max-w-7xl mx-auto"}`}>{children}</div>
</div>
</main>
</div>
);
}
|