"use client"; import { type ColumnDef } from "@tanstack/react-table"; import { formatDistanceToNow } from "date-fns"; import { MoreHorizontal, Eye, Edit, Trash2, Play, Pause, StopCircle, Copy, TestTube, User, FlaskConical, Calendar, BarChart3, } from "lucide-react"; import Link from "next/link"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Checkbox } from "~/components/ui/checkbox"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "~/components/ui/dropdown-menu"; import { DataTableColumnHeader } from "~/components/ui/data-table-column-header"; import { toast } from "sonner"; export type Trial = { id: string; name: string; description: string | null; status: "scheduled" | "in_progress" | "completed" | "aborted" | "failed"; scheduledAt: Date | null; startedAt: Date | null; completedAt: Date | null; createdAt: Date; updatedAt: Date; studyId: string; experimentId: string; participantId: string; wizardId: string | null; study: { id: string; name: string; }; experiment: { id: string; name: string; }; participant: { id: string; name: string; email: string; }; wizard: { id: string; name: string | null; email: string; } | null; duration?: number; // in minutes _count?: { actions: number; logs: number; }; userRole?: "owner" | "researcher" | "wizard" | "observer"; canEdit?: boolean; canDelete?: boolean; canExecute?: boolean; }; const statusConfig = { scheduled: { label: "Scheduled", className: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200", description: "Trial is scheduled for future execution", }, in_progress: { label: "In Progress", className: "bg-blue-100 text-blue-800 hover:bg-blue-200", description: "Trial is currently running", }, completed: { label: "Completed", className: "bg-green-100 text-green-800 hover:bg-green-200", description: "Trial has been completed successfully", }, aborted: { label: "Aborted", className: "bg-red-100 text-red-800 hover:bg-red-200", description: "Trial was aborted before completion", }, failed: { label: "Failed", className: "bg-red-100 text-red-800 hover:bg-red-200", description: "Trial failed due to an error", }, }; function TrialActionsCell({ trial }: { trial: Trial }) { const handleDelete = async () => { if ( window.confirm(`Are you sure you want to delete trial "${trial.name}"?`) ) { try { // Delete trial functionality not yet implemented toast.success("Trial deleted successfully"); } catch { toast.error("Failed to delete trial"); } } }; const handleCopyId = () => { navigator.clipboard.writeText(trial.id); toast.success("Trial ID copied to clipboard"); }; const handleStartTrial = () => { window.location.href = `/trials/${trial.id}/wizard`; }; const handlePauseTrial = async () => { try { // Pause trial functionality not yet implemented toast.success("Trial paused"); } catch { toast.error("Failed to pause trial"); } }; const handleStopTrial = async () => { if (window.confirm("Are you sure you want to stop this trial?")) { try { // Stop trial functionality not yet implemented toast.success("Trial stopped"); } catch { toast.error("Failed to stop trial"); } } }; const canStart = trial.status === "scheduled" && trial.canExecute; const canPause = trial.status === "in_progress" && trial.canExecute; const canStop = trial.status === "in_progress" && trial.canExecute; return ( Actions View Details {trial.canEdit && ( Edit Trial )} {canStart && ( Start Trial )} {canPause && ( Pause Trial )} {canStop && ( Stop Trial )} Wizard Interface View Analysis Copy Trial ID {trial.canDelete && ( <> Delete Trial )} ); } export const trialsColumns: ColumnDef[] = [ { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} aria-label="Select all" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} aria-label="Select row" /> ), enableSorting: false, enableHiding: false, }, { accessorKey: "name", header: ({ column }) => ( ), cell: ({ row }) => { const trial = row.original; return (
{trial.name}
); }, }, { accessorKey: "status", header: ({ column }) => ( ), cell: ({ row }) => { const status = row.getValue("status") as Trial["status"]; const config = statusConfig[status]; return ( {config.label} ); }, filterFn: (row, id, value: string[]) => { const status = row.getValue(id) as string; return value.includes(status); }, }, { accessorKey: "participant", header: ({ column }) => ( ), cell: ({ row }) => { const participant = row.getValue("participant") as Trial["participant"]; return (
{participant.name || "Unnamed Participant"}
); }, enableSorting: false, }, { accessorKey: "experiment", header: ({ column }) => ( ), cell: ({ row }) => { const experiment = row.getValue("experiment") as Trial["experiment"]; return (
{experiment.name || "Unnamed Experiment"}
); }, enableSorting: false, enableHiding: true, meta: { defaultHidden: true, }, }, { accessorKey: "wizard", header: ({ column }) => ( ), cell: ({ row }) => { const wizard = row.getValue("wizard") as Trial["wizard"]; if (!wizard) { return ( Not assigned ); } return (
{wizard.name ?? ""}
{wizard.email}
); }, enableSorting: false, enableHiding: true, meta: { defaultHidden: true, }, }, { accessorKey: "scheduledAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("scheduledAt") as Date | null; if (!date) { return ( Not scheduled ); } return (
{formatDistanceToNow(date, { addSuffix: true })}
); }, enableHiding: true, meta: { defaultHidden: true, }, }, { id: "duration", header: "Duration", cell: ({ row }) => { const trial = row.original; if ( trial.status === "completed" && trial.startedAt && trial.completedAt ) { const duration = Math.round( (trial.completedAt.getTime() - trial.startedAt.getTime()) / (1000 * 60), ); return
{duration}m
; } if (trial.status === "in_progress" && trial.startedAt) { const duration = Math.round( (Date.now() - trial.startedAt.getTime()) / (1000 * 60), ); return (
{duration}m
); } if (trial.duration) { return (
~{trial.duration}m
); } return -; }, enableSorting: false, }, { id: "stats", header: "Data", cell: ({ row }) => { const trial = row.original; const counts = trial._count; return (
{counts?.actions ?? 0}
{counts?.logs ?? 0}
); }, enableSorting: false, enableHiding: true, meta: { defaultHidden: true, }, }, { accessorKey: "createdAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("createdAt") as Date; return (
{formatDistanceToNow(date, { addSuffix: true })}
); }, enableHiding: true, meta: { defaultHidden: true, }, }, { id: "actions", header: "Actions", cell: ({ row }) => , enableSorting: false, enableHiding: false, }, ];