mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-03-24 03:37:51 -04:00
feat(analytics): refine timeline visualization and add print support
This commit is contained in:
@@ -1,125 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Activity, Calendar, CheckCircle, FlaskConical } from "lucide-react";
|
||||
import { DashboardOverviewLayout } from "~/components/ui/page-layout";
|
||||
|
||||
interface DashboardContentProps {
|
||||
userName: string;
|
||||
userRole: string;
|
||||
totalStudies: number;
|
||||
activeTrials: number;
|
||||
scheduledTrials: number;
|
||||
completedToday: number;
|
||||
canControl: boolean;
|
||||
canManage: boolean;
|
||||
_recentTrials: unknown[];
|
||||
}
|
||||
|
||||
export function DashboardContent({
|
||||
userName,
|
||||
userRole,
|
||||
totalStudies,
|
||||
activeTrials,
|
||||
scheduledTrials,
|
||||
completedToday,
|
||||
canControl,
|
||||
canManage,
|
||||
_recentTrials,
|
||||
}: DashboardContentProps) {
|
||||
const getWelcomeMessage = () => {
|
||||
switch (userRole) {
|
||||
case "wizard":
|
||||
return "Ready to control trials";
|
||||
case "researcher":
|
||||
return "Your research platform awaits";
|
||||
case "administrator":
|
||||
return "System management dashboard";
|
||||
default:
|
||||
return "Welcome to HRIStudio";
|
||||
}
|
||||
};
|
||||
|
||||
const quickActions = [
|
||||
...(canManage
|
||||
? [
|
||||
{
|
||||
title: "Create Study",
|
||||
description: "Start a new research study",
|
||||
icon: FlaskConical,
|
||||
href: "/studies/new",
|
||||
variant: "primary" as const,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(canControl
|
||||
? [
|
||||
{
|
||||
title: "Browse Studies",
|
||||
description: "View and manage studies",
|
||||
icon: Calendar,
|
||||
href: "/studies",
|
||||
variant: "default" as const,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{
|
||||
title: "Studies",
|
||||
value: totalStudies,
|
||||
description: "Research studies",
|
||||
icon: FlaskConical,
|
||||
variant: "primary" as const,
|
||||
action: {
|
||||
label: "View All",
|
||||
href: "/studies",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Active Trials",
|
||||
value: activeTrials,
|
||||
description: "Currently running",
|
||||
icon: Activity,
|
||||
variant: "success" as const,
|
||||
...(canControl && {
|
||||
action: {
|
||||
label: "View",
|
||||
href: "/studies",
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: "Scheduled",
|
||||
value: scheduledTrials,
|
||||
description: "Upcoming trials",
|
||||
icon: Calendar,
|
||||
variant: "default" as const,
|
||||
},
|
||||
{
|
||||
title: "Completed Today",
|
||||
value: completedToday,
|
||||
description: "Finished trials",
|
||||
icon: CheckCircle,
|
||||
variant: "success" as const,
|
||||
},
|
||||
];
|
||||
|
||||
const alerts: never[] = [];
|
||||
|
||||
const recentActivity = null;
|
||||
|
||||
return (
|
||||
<DashboardOverviewLayout
|
||||
title={`${getWelcomeMessage()}, ${userName}`}
|
||||
description="Monitor your HRI research activities and manage ongoing studies"
|
||||
userName={userName}
|
||||
userRole={userRole}
|
||||
breadcrumb={[{ label: "Dashboard" }]}
|
||||
quickActions={quickActions}
|
||||
stats={stats}
|
||||
alerts={alerts}
|
||||
recentActivity={recentActivity}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
BarChart3,
|
||||
BookOpen,
|
||||
Building,
|
||||
ChevronDown,
|
||||
FlaskConical,
|
||||
@@ -113,6 +114,14 @@ const adminItems = [
|
||||
},
|
||||
];
|
||||
|
||||
const helpItems = [
|
||||
{
|
||||
title: "Help Center",
|
||||
url: "/help",
|
||||
icon: BookOpen, // Make sure to import this from lucide-react
|
||||
},
|
||||
];
|
||||
|
||||
interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
|
||||
userRole?: string;
|
||||
}
|
||||
@@ -126,9 +135,38 @@ export function AppSidebar({
|
||||
const isAdmin = userRole === "administrator";
|
||||
const { state: sidebarState } = useSidebar();
|
||||
const isCollapsed = sidebarState === "collapsed";
|
||||
const { selectedStudyId, userStudies, selectStudy, refreshStudyData } =
|
||||
const { selectedStudyId, userStudies, selectStudy, refreshStudyData, isLoadingUserStudies } =
|
||||
useStudyManagement();
|
||||
|
||||
// Reference to track if we've already attempted auto-selection to avoid fighting with manual clearing
|
||||
const hasAutoSelected = useRef(false);
|
||||
|
||||
// Auto-select most recently touched study if none selected
|
||||
useEffect(() => {
|
||||
// Only run if not loading, no study selected, and we have studies available
|
||||
// And only run once per session (using ref) to allow user to clear selection if desired
|
||||
if (
|
||||
!isLoadingUserStudies &&
|
||||
!selectedStudyId &&
|
||||
userStudies.length > 0 &&
|
||||
!hasAutoSelected.current
|
||||
) {
|
||||
// userStudies is sorted by updatedAt desc from the API, so the first one is the most recent
|
||||
// userStudies is sorted by updatedAt desc from the API, so the first one is the most recent
|
||||
const mostRecent = userStudies[0];
|
||||
if (mostRecent) {
|
||||
console.log("Auto-selecting most recent study:", mostRecent.name);
|
||||
void selectStudy(mostRecent.id);
|
||||
hasAutoSelected.current = true;
|
||||
}
|
||||
}
|
||||
}, [
|
||||
isLoadingUserStudies,
|
||||
selectedStudyId,
|
||||
userStudies,
|
||||
selectStudy,
|
||||
]);
|
||||
|
||||
// Debug API call
|
||||
const { data: debugData } = api.dashboard.debug.useQuery(undefined, {
|
||||
enabled: process.env.NODE_ENV === "development",
|
||||
@@ -520,6 +558,44 @@ export function AppSidebar({
|
||||
)}
|
||||
</SidebarContent>
|
||||
|
||||
{/* Help Section */}
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Support</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{helpItems.map((item) => {
|
||||
const isActive = pathname.startsWith(item.url);
|
||||
|
||||
const menuButton = (
|
||||
<SidebarMenuButton asChild isActive={isActive}>
|
||||
<Link href={item.url}>
|
||||
<item.icon className="h-4 w-4" />
|
||||
<span>{item.title}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
{isCollapsed ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{menuButton}</TooltipTrigger>
|
||||
<TooltipContent side="right" className="text-sm">
|
||||
{item.title}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
menuButton
|
||||
)}
|
||||
</SidebarMenuItem>
|
||||
);
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
|
||||
{/* Debug info moved to footer tooltip button */}
|
||||
|
||||
<SidebarFooter>
|
||||
|
||||
Reference in New Issue
Block a user