223 lines
5.7 KiB
TypeScript
223 lines
5.7 KiB
TypeScript
"use client";
|
|
|
|
import type React from "react";
|
|
import { useState } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
import {
|
|
Clock,
|
|
Calendar,
|
|
FileText,
|
|
Bot,
|
|
Video,
|
|
Radio,
|
|
Eye,
|
|
Bell,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
Palette,
|
|
GitFork,
|
|
Calculator,
|
|
Building2,
|
|
} from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { useRouter, usePathname } from "next/navigation";
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import { useSession } from "next-auth/react";
|
|
|
|
interface SidebarProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
interface MenuItem {
|
|
title: string;
|
|
icon: any;
|
|
href: string;
|
|
iframe?: string;
|
|
external?: boolean;
|
|
requiredRole?: string;
|
|
}
|
|
|
|
export function Sidebar({ isOpen, onClose }: SidebarProps) {
|
|
const { data: session } = useSession();
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
|
|
// Function to check if user has a specific role
|
|
const hasRole = (requiredRole: string) => {
|
|
if (!session?.user?.role) return false;
|
|
const userRoles = Array.isArray(session.user.role) ? session.user.role : [session.user.role];
|
|
|
|
// Add console.log to debug roles
|
|
console.log('User roles:', userRoles);
|
|
console.log('Required role:', requiredRole);
|
|
|
|
return userRoles.some(role => {
|
|
// Remove ROLE_ prefix if it exists
|
|
const cleanRole = role.replace('ROLE_', '');
|
|
return cleanRole === requiredRole || cleanRole === 'Admin';
|
|
});
|
|
};
|
|
|
|
const handleNavigation = (href: string, external?: boolean) => {
|
|
if (external && href) {
|
|
window.open(href, "_blank");
|
|
} else {
|
|
router.push(href);
|
|
}
|
|
onClose();
|
|
};
|
|
|
|
// Base menu items (available for everyone)
|
|
const baseMenuItems: MenuItem[] = [
|
|
{
|
|
title: "TimeTracker",
|
|
icon: Clock,
|
|
href: "/timetracker",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_TIMETRACKER_URL || '',
|
|
},
|
|
{
|
|
title: "Calendar",
|
|
icon: Calendar,
|
|
href: "/calendar",
|
|
external: false,
|
|
},
|
|
{
|
|
title: "Notes",
|
|
icon: FileText,
|
|
href: "/notes",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_NOTES_URL || '',
|
|
},
|
|
{
|
|
title: "AI Assistant",
|
|
icon: Bot,
|
|
href: "/ai-assistant",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_AI_ASSISTANT_URL || '',
|
|
},
|
|
{
|
|
title: "Conference",
|
|
icon: Video,
|
|
href: "/conference",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_CONFERENCE_URL || '',
|
|
},
|
|
{
|
|
title: "Radio",
|
|
icon: Radio,
|
|
href: "/radio",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_RADIO_URL || '',
|
|
},
|
|
{
|
|
title: "Observatory",
|
|
icon: Eye,
|
|
href: "/observatory",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_OBSERVATORY_URL || '',
|
|
},
|
|
{
|
|
title: "Announcement",
|
|
icon: Bell,
|
|
href: "/announcement",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_ANNOUNCEMENT_URL || '',
|
|
},
|
|
];
|
|
|
|
// Role-specific menu items
|
|
const roleSpecificItems: MenuItem[] = [
|
|
{
|
|
title: "Artlab",
|
|
icon: Palette,
|
|
href: "/design",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_ARTLAB_URL || '',
|
|
requiredRole: "Expression",
|
|
},
|
|
{
|
|
title: "Gite",
|
|
icon: GitFork,
|
|
href: "/gite",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_GITE_URL || '',
|
|
requiredRole: "Coding",
|
|
},
|
|
{
|
|
title: "Calculation",
|
|
icon: Calculator,
|
|
href: "/calculation",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_CALCULATION_URL || '',
|
|
requiredRole: "DataIntelligence",
|
|
},
|
|
{
|
|
title: "Mediations",
|
|
icon: Building2,
|
|
href: "/crm",
|
|
iframe: process.env.NEXT_PUBLIC_IFRAME_MEDIATIONS_URL || '',
|
|
requiredRole: "Mediation",
|
|
},
|
|
];
|
|
|
|
// Combine base items with role-specific items based on user roles
|
|
const visibleMenuItems = [
|
|
...baseMenuItems,
|
|
...roleSpecificItems.filter(item => hasRole(item.requiredRole))
|
|
];
|
|
|
|
return (
|
|
<>
|
|
{/* Backdrop */}
|
|
{isOpen && (
|
|
<div
|
|
className="fixed inset-0 z-40 bg-background/80 backdrop-blur-sm"
|
|
onClick={onClose}
|
|
/>
|
|
)}
|
|
|
|
{/* Sidebar */}
|
|
<div
|
|
className={cn(
|
|
"fixed top-0 left-0 z-50 h-full w-64 transform bg-panel transition-all duration-200 ease-in-out",
|
|
isOpen ? "translate-x-0" : "-translate-x-full"
|
|
)}
|
|
>
|
|
<ScrollArea className="h-full w-full relative">
|
|
{/* Hide Button */}
|
|
<button
|
|
onClick={onClose}
|
|
className="absolute -right-3 top-1/2 transform -translate-y-1/2 w-6 h-12 bg-black text-white rounded-r-md flex items-center justify-center hover:bg-gray-800 transition-colors z-[60]"
|
|
>
|
|
<ChevronLeft className="h-4 w-4" />
|
|
</button>
|
|
|
|
{/* Logo */}
|
|
<div className="flex justify-center p-6 border-b">
|
|
<Image
|
|
src="/Neahv3 logo.png"
|
|
alt="Neah Logo"
|
|
width={100}
|
|
height={33}
|
|
className="text-black"
|
|
/>
|
|
</div>
|
|
|
|
{/* Menu Items */}
|
|
<div className="space-y-1 p-4">
|
|
{visibleMenuItems.map((item) => (
|
|
<Button
|
|
key={item.title}
|
|
variant="ghost"
|
|
className={cn(
|
|
"w-full justify-start gap-2 text-black hover:bg-gray-100",
|
|
pathname === item.href && !item.external && "bg-gray-100"
|
|
)}
|
|
onClick={() => handleNavigation(item.href, item.external)}
|
|
>
|
|
<item.icon className="h-5 w-5" />
|
|
<span>{item.title}</span>
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</ScrollArea>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|