mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-09 10:20:39 +00:00
3aff09a2e5
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/ba267903-58ef-4b1e-ab4f-95c2845e5bb8.jpg
252 lines
8.1 KiB
TypeScript
252 lines
8.1 KiB
TypeScript
import React, { useState, useContext } from "react";
|
|
import { Link, useLocation } from "wouter";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import {
|
|
ChevronDown,
|
|
LogOut,
|
|
Menu,
|
|
Bell,
|
|
HelpCircle,
|
|
User,
|
|
Settings,
|
|
Home,
|
|
Users,
|
|
UserPlus,
|
|
FolderClosed,
|
|
Monitor,
|
|
Globe,
|
|
Key,
|
|
Server,
|
|
ShieldAlert,
|
|
Filter,
|
|
ClipboardList,
|
|
Building2,
|
|
BarChart3,
|
|
LineChart,
|
|
} from "lucide-react";
|
|
import { useMobile } from "@/hooks/use-mobile";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import { AuthContext } from "@/hooks/use-auth";
|
|
import { ThemeToggle } from "@/components/theme-toggle";
|
|
|
|
type MenuItem = {
|
|
title: string;
|
|
path: string;
|
|
icon: React.ReactNode;
|
|
};
|
|
|
|
type MenuSection = {
|
|
title: string;
|
|
items: MenuItem[];
|
|
};
|
|
|
|
const menuSections: MenuSection[] = [
|
|
{
|
|
title: "Active Directory",
|
|
items: [
|
|
{ title: "Users", path: "/users", icon: <Users className="h-4 w-4" /> },
|
|
{ title: "Groups", path: "/groups", icon: <UserPlus className="h-4 w-4" /> },
|
|
{ title: "Dynamic Groups", path: "/dynamic-groups", icon: <LineChart className="h-4 w-4" /> },
|
|
{ title: "Organizational Units", path: "/organizational-units", icon: <FolderClosed className="h-4 w-4" /> },
|
|
{ title: "Computers", path: "/computers", icon: <Monitor className="h-4 w-4" /> },
|
|
{ title: "Domains", path: "/domains", icon: <Globe className="h-4 w-4" /> },
|
|
{ title: "Sites & Subnets", path: "/sites", icon: <Building2 className="h-4 w-4" /> },
|
|
],
|
|
},
|
|
{
|
|
title: "Reporting",
|
|
items: [
|
|
{ title: "Custom Dashboards", path: "/reporting/dashboards", icon: <BarChart3 className="h-4 w-4" /> },
|
|
],
|
|
},
|
|
{
|
|
title: "Administration",
|
|
items: [
|
|
{ title: "API Tokens", path: "/api-tokens", icon: <Key className="h-4 w-4" /> },
|
|
{ title: "LDAP Connections", path: "/ldap-connections", icon: <Server className="h-4 w-4" /> },
|
|
{ title: "LDAP Query Builder", path: "/ldap-query-builder", icon: <Filter className="h-4 w-4" /> },
|
|
{ title: "Audit Logs", path: "/audit-logs", icon: <ClipboardList className="h-4 w-4" /> },
|
|
{ title: "Settings", path: "/settings", icon: <Settings className="h-4 w-4" /> },
|
|
{ title: "User Management", path: "/user-management", icon: <ShieldAlert className="h-4 w-4" /> },
|
|
],
|
|
},
|
|
];
|
|
|
|
interface DashboardLayoutProps {
|
|
children: React.ReactNode;
|
|
title: string;
|
|
description?: string;
|
|
}
|
|
|
|
export function DashboardLayout({ children, title, description }: DashboardLayoutProps) {
|
|
const [location, navigate] = useLocation();
|
|
const isMobile = useMobile();
|
|
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
|
|
const auth = useContext(AuthContext);
|
|
if (!auth) {
|
|
console.error("Auth context not available");
|
|
return <div>Authentication error. Please refresh the page.</div>;
|
|
}
|
|
const { user, logoutMutation } = auth;
|
|
const { toast } = useToast();
|
|
|
|
const handleLogout = async () => {
|
|
logoutMutation.mutate(undefined, {
|
|
onSuccess: () => {
|
|
navigate('/auth');
|
|
}
|
|
});
|
|
};
|
|
|
|
// Get user initials for avatar
|
|
const getInitials = () => {
|
|
if (!user) return "U";
|
|
|
|
if (user.fullName) {
|
|
const nameParts = user.fullName.split(" ");
|
|
if (nameParts.length > 1) {
|
|
return `${nameParts[0][0]}${nameParts[nameParts.length - 1][0]}`.toUpperCase();
|
|
}
|
|
return nameParts[0][0].toUpperCase();
|
|
}
|
|
|
|
return user.username[0].toUpperCase();
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-screen overflow-hidden bg-background">
|
|
{/* Sidebar */}
|
|
<div
|
|
className={`w-64 h-full dark:bg-sidebar bg-white shadow-md z-10 flex-shrink-0 transition-all duration-300 ease-in-out ${
|
|
sidebarOpen ? "" : "-ml-64"
|
|
} ${isMobile ? "absolute" : "relative"}`}
|
|
>
|
|
<div className="h-16 flex items-center px-4 border-b">
|
|
<Link href="/" className="font-medium text-lg text-primary">
|
|
AD Management API
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="overflow-y-auto h-[calc(100%-64px)]">
|
|
<div className="px-2 pt-4">
|
|
<Link
|
|
href="/"
|
|
className={`drawer-item px-4 py-2 flex items-center space-x-3 rounded cursor-pointer ${
|
|
location === "/" ? "active" : ""
|
|
}`}
|
|
>
|
|
<Home className="h-4 w-4" />
|
|
<span>Dashboard</span>
|
|
</Link>
|
|
|
|
{menuSections.map((section, idx) => (
|
|
<div key={idx}>
|
|
<div className="mt-6 mb-2 px-4 text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
|
{section.title}
|
|
</div>
|
|
|
|
{section.items.map((item, itemIdx) => (
|
|
<Link
|
|
href={item.path}
|
|
key={itemIdx}
|
|
className={`drawer-item px-4 py-2 flex items-center space-x-3 rounded cursor-pointer ${
|
|
location === item.path ? "active" : ""
|
|
}`}
|
|
>
|
|
{item.icon}
|
|
<span>{item.title}</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Content */}
|
|
<div className="flex-1 flex flex-col overflow-hidden">
|
|
{/* Top App Bar */}
|
|
<header className="h-16 bg-white dark:bg-sidebar shadow-sm flex items-center justify-between px-4 z-10">
|
|
<div className="flex items-center">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
className="lg:hidden"
|
|
>
|
|
<Menu className="h-5 w-5" />
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-4">
|
|
<ThemeToggle />
|
|
|
|
<Button variant="ghost" size="icon">
|
|
<HelpCircle className="h-5 w-5" />
|
|
</Button>
|
|
|
|
<Button variant="ghost" size="icon">
|
|
<Bell className="h-5 w-5" />
|
|
</Button>
|
|
|
|
<Separator orientation="vertical" className="h-8" />
|
|
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="flex items-center space-x-2">
|
|
<Avatar className="h-8 w-8">
|
|
<AvatarFallback className="bg-primary text-white">
|
|
{getInitials()}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<span className="text-sm hidden md:inline-block">{user?.username}</span>
|
|
<ChevronDown className="h-4 w-4 text-muted-foreground" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem>
|
|
<User className="mr-2 h-4 w-4" />
|
|
<span>Profile</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem>
|
|
<Settings className="mr-2 h-4 w-4" />
|
|
<span>Settings</span>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={handleLogout}>
|
|
<LogOut className="mr-2 h-4 w-4" />
|
|
<span>Log out</span>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Main Content Area */}
|
|
<main className="flex-1 overflow-y-auto p-6 bg-background">
|
|
<div className="mb-6">
|
|
<h1 className="text-2xl font-medium text-gray-800 dark:text-gray-100">{title}</h1>
|
|
{description && <p className="text-gray-600 dark:text-gray-400">{description}</p>}
|
|
</div>
|
|
|
|
{children}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default DashboardLayout;
|