import { format, formatDistanceToNow } from "date-fns"; import { Activity, AlertTriangle, ArrowLeft, BarChart3, Bot, CheckCircle, Clock, Download, Eye, Play, Settings, Share, Target, Timer, User, Users, XCircle, } from "lucide-react"; import Link from "next/link"; import { notFound, redirect } from "next/navigation"; import { Alert, AlertDescription } from "~/components/ui/alert"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Progress } from "~/components/ui/progress"; import { Separator } from "~/components/ui/separator"; import { auth } from "~/server/auth"; import { api } from "~/trpc/server"; interface TrialDetailPageProps { params: Promise<{ trialId: string; }>; searchParams: Promise<{ error?: string; }>; } export default async function TrialDetailPage({ params, searchParams, }: TrialDetailPageProps) { const session = await auth(); if (!session) { redirect("/auth/signin"); } const { trialId } = await params; const { error } = await searchParams; let trial; try { trial = await api.trials.get({ id: trialId }); } catch (_error) { notFound(); } const userRole = session.user.roles?.[0]?.role; const canControl = userRole && ["wizard", "researcher", "administrator"].includes(userRole); const statusConfig = { scheduled: { label: "Scheduled", className: "bg-blue-100 text-blue-800", icon: Clock, }, in_progress: { label: "In Progress", className: "bg-green-100 text-green-800", icon: Activity, }, completed: { label: "Completed", className: "bg-gray-100 text-gray-800", icon: CheckCircle, }, aborted: { label: "Aborted", className: "bg-red-100 text-red-800", icon: XCircle, }, failed: { label: "Failed", className: "bg-red-100 text-red-800", icon: AlertTriangle, }, }; const currentStatus = statusConfig[trial.status]; const StatusIcon = currentStatus.icon; // Calculate trial duration const duration = trial.startedAt && trial.completedAt ? Math.floor( (new Date(trial.completedAt).getTime() - new Date(trial.startedAt).getTime()) / 1000 / 60, ) : trial.startedAt ? Math.floor( (Date.now() - new Date(trial.startedAt).getTime()) / 1000 / 60, ) : null; // Mock experiment steps - in real implementation, fetch from experiment API const experimentSteps: any[] = []; const stepTypes = experimentSteps.reduce( (acc: Record, step: any) => { acc[step.type] = (acc[step.type] || 0) + 1; return acc; }, {}, ); return (
{/* Header */}

Trial Details

{trial.experiment.name} • Participant:{" "} {trial.participant.participantCode}

{currentStatus.label}
{/* Error Alert */} {error && (
{error === "trial_not_active" && "This trial is not currently active for wizard control."} {error === "insufficient_permissions" && "You don't have permission to access the wizard interface."}
)}
{/* Quick Actions */}
{trial.status === "scheduled" && canControl && ( )} {trial.status === "in_progress" && ( )} {trial.status === "completed" && ( )}
{/* Main Content */}
{/* Trial Overview */} Trial Overview

{trial.id}

{currentStatus.label}

{trial.startedAt ? format(trial.startedAt, "PPP 'at' p") : "Not scheduled"}

{trial.startedAt && (

{format(trial.startedAt, "PPP 'at' p")}

{formatDistanceToNow(trial.startedAt, { addSuffix: true, })}

)} {trial.completedAt && (

{format(trial.completedAt, "PPP 'at' p")}

{formatDistanceToNow(trial.completedAt, { addSuffix: true, })}

)} {duration !== null && (
{duration} minutes
)}
{trial.notes && (

{trial.notes}

)}
{/* Experiment Details */} Experiment Protocol

{trial.experiment.name}

{trial.experiment.description && (

{trial.experiment.description}

)}
Study Details
{/* Experiment Steps Summary */}

Protocol Summary

{experimentSteps.length}

{Math.round( experimentSteps.reduce( (sum: number, step: any) => sum + (step.duration || 0), 0, ) / 60, )}{" "} min

{Object.keys(stepTypes).length > 0 && (
{Object.entries(stepTypes).map(([type, count]) => ( {type.replace(/_/g, " ")}: {String(count)} ))}
)}
{/* Trial Progress */} {trial.status === "in_progress" && ( Current Progress
Trial Progress Step 1 of {experimentSteps.length}
Currently executing the first step of the experiment protocol.
)}
{/* Sidebar */}
{/* Participant Info */} Participant

{trial.participant.participantCode}

Consent verified
{/* Wizard Assignment */} Team
No wizard assigned
{userRole || "Observer"}
{/* Quick Stats */} Statistics
0
Events
0
Media
0
Annotations
0
Interventions
{trial.status === "completed" && ( <> )}
{/* Recent Activity */} Recent Activity
No recent activity
); } // Generate metadata for the page export async function generateMetadata({ params }: TrialDetailPageProps) { try { const { trialId } = await params; const trial = await api.trials.get({ id: trialId }); return { title: `${trial.experiment.name} - Trial ${trial.participant.participantCode} | HRIStudio`, description: `Trial details for ${trial.experiment.name} with participant ${trial.participant.participantCode}`, }; } catch { return { title: "Trial Details | HRIStudio", description: "View trial information and control wizard interface", }; } }