"use client"; import React, { useEffect } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { signOut, useSession } from "next-auth/react"; import { BarChart3, Building, ChevronDown, FlaskConical, Home, LogOut, MoreHorizontal, Settings, User, Users, UserCheck, TestTube, } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "~/components/ui/dropdown-menu"; import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarRail, } from "~/components/ui/sidebar"; import { Logo } from "~/components/ui/logo"; import { useStudyManagement } from "~/hooks/useStudyManagement"; // Navigation items const navigationItems = [ { title: "Overview", url: "/dashboard", icon: Home, }, { title: "Studies", url: "/studies", icon: Building, }, { title: "Experiments", url: "/experiments", icon: FlaskConical, }, { title: "Participants", url: "/participants", icon: Users, }, { title: "Trials", url: "/trials", icon: TestTube, }, { title: "Analytics", url: "/analytics", icon: BarChart3, }, ]; const adminItems = [ { title: "Administration", url: "/admin", icon: UserCheck, }, ]; interface AppSidebarProps extends React.ComponentProps { userRole?: string; } export function AppSidebar({ userRole = "researcher", ...props }: AppSidebarProps) { const { data: session } = useSession(); const pathname = usePathname(); const isAdmin = userRole === "administrator"; const { selectedStudyId, userStudies, selectStudy, refreshStudyData } = useStudyManagement(); type Study = { id: string; name: string; }; // Filter navigation items based on study selection const availableNavigationItems = navigationItems.filter((item) => { // These items are always available if (item.url === "/dashboard" || item.url === "/studies") { return true; } // These items require a selected study return selectedStudyId !== null; }); const handleSignOut = async () => { await signOut({ callbackUrl: "/" }); }; const handleStudySelect = async (studyId: string) => { try { await selectStudy(studyId); } catch (error) { console.error("Failed to select study:", error); // If study selection fails (e.g., study not found), clear the selection await selectStudy(null); } }; const selectedStudy = userStudies.find( (study: Study) => study.id === selectedStudyId, ); // If we have a selectedStudyId but can't find the study, clear the selection React.useEffect(() => { if (selectedStudyId && userStudies.length > 0 && !selectedStudy) { console.warn( "Selected study not found in user studies, clearing selection", ); void selectStudy(null); } }, [selectedStudyId, userStudies, selectedStudy, selectStudy]); // Auto-refresh studies list when component mounts to catch external changes useEffect(() => { const interval = setInterval(() => { void refreshStudyData(); }, 30000); // Refresh every 30 seconds return () => clearInterval(interval); }, [refreshStudyData]); return ( {/* Study Selector */} Active Study {selectedStudy?.name ?? "Select Study"} Studies {userStudies.map((study: Study) => ( handleStudySelect(study.id)} className="cursor-pointer" > {study.name} ))} {selectedStudyId && ( { await selectStudy(null); }} > Clear selection )} Create study {/* Main Navigation */} Research {availableNavigationItems.map((item) => { const isActive = pathname === item.url || (item.url !== "/dashboard" && pathname.startsWith(item.url)); return ( {item.title} ); })} {/* Study-specific items hint */} {!selectedStudyId && (
Select a study to access experiments, participants, trials, and analytics.
)} {/* Admin Section */} {isAdmin && ( Administration {adminItems.map((item) => { const isActive = pathname.startsWith(item.url); return ( {item.title} ); })} )}
{session?.user?.name ?? "User"} {session?.user?.name ?? "User"} Profile & Settings Sign out
); }