mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-28 11:37:13 +00:00
Add user authentication and Active Directory management features. Includes a new admin UI and Swagger API documentation.
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/e9f0a10f-e323-455c-82c9-1da4a687f20e.jpg
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import React, { useState } from "react";
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
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,
|
||||
} from "lucide-react";
|
||||
import { useMobile } from "@/hooks/use-mobile";
|
||||
|
||||
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: "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: "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: "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] = useLocation();
|
||||
const { user, logoutMutation } = useAuth();
|
||||
const isMobile = useMobile();
|
||||
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
|
||||
|
||||
const handleLogout = () => {
|
||||
logoutMutation.mutate();
|
||||
};
|
||||
|
||||
// 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 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="/">
|
||||
<a className="font-medium text-lg text-primary">AD Management API</a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto h-[calc(100%-64px)]">
|
||||
<div className="px-2 pt-4">
|
||||
<Link href="/">
|
||||
<a
|
||||
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>
|
||||
</a>
|
||||
</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}>
|
||||
<a
|
||||
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>
|
||||
</a>
|
||||
</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 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">
|
||||
<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">{title}</h1>
|
||||
{description && <p className="text-gray-600">{description}</p>}
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user