"use client"; import { type ColumnDef } from "@tanstack/react-table"; import { formatDistanceToNow } from "date-fns"; import { Copy, Edit, Eye, FlaskConical, LayoutTemplate, MoreHorizontal, Play, TestTube, Trash2, } from "lucide-react"; import Link from "next/link"; import { toast } from "sonner"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Checkbox } from "~/components/ui/checkbox"; import { DataTableColumnHeader } from "~/components/ui/data-table-column-header"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "~/components/ui/dropdown-menu"; import { api } from "~/trpc/react"; export type Experiment = { id: string; name: string; description: string | null; status: "draft" | "testing" | "ready" | "deprecated"; createdAt: Date; updatedAt: Date; studyId: string; study: { id: string; name: string; }; createdBy: string; owner: { name: string | null; email: string; }; _count?: { steps: number; trials: number; }; userRole?: "owner" | "researcher" | "wizard" | "observer"; canEdit?: boolean; canDelete?: boolean; }; const statusConfig = { draft: { label: "Draft", className: "bg-gray-100 text-gray-800 hover:bg-gray-200", description: "Experiment in preparation", }, testing: { label: "Testing", className: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200", description: "Experiment being tested", }, ready: { label: "Ready", className: "bg-green-100 text-green-800 hover:bg-green-200", description: "Experiment ready for trials", }, deprecated: { label: "Deprecated", className: "bg-slate-100 text-slate-800 hover:bg-slate-200", description: "Experiment deprecated", }, }; function ExperimentActionsCell({ experiment }: { experiment: Experiment }) { const utils = api.useUtils(); const deleteMutation = api.experiments.delete.useMutation({ onSuccess: () => { toast.success("Experiment deleted successfully"); utils.experiments.list.invalidate(); }, onError: (error) => { toast.error(`Failed to delete experiment: ${error.message}`); }, }); const handleDelete = () => { if ( window.confirm(`Are you sure you want to delete "${experiment.name}"?`) ) { deleteMutation.mutate({ id: experiment.id }); } }; return (
{experiment.canDelete && ( )}
); } export const experimentsColumns: 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 experiment = row.original; return (
{experiment.name} {experiment.description && (

{experiment.description}

)}
); }, }, { accessorKey: "study", header: ({ column }) => ( ), cell: ({ row }) => { const study = row.original.study; if (!study?.id || !study?.name) return No study; return ( {study.name} ); }, enableSorting: false, }, { accessorKey: "status", header: ({ column }) => ( ), cell: ({ row }) => { const status = row.getValue("status"); const config = statusConfig[status as keyof typeof statusConfig]; return ( {config.label} ); }, filterFn: (row, id, value: string[]) => { return value.includes(row.getValue(id)); }, }, { id: "stats", header: "Statistics", cell: ({ row }) => { const experiment = row.original; const counts = experiment._count; return (
{counts?.steps ?? 0}
{counts?.trials ?? 0}
); }, enableSorting: false, enableHiding: false, }, { accessorKey: "owner", header: ({ column }) => ( ), cell: ({ row }) => { const owner = row.original.owner; if (!owner) { return No owner; } return (
{owner.name ?? "Unknown"}
{owner.email ?? ""}
); }, enableSorting: false, }, { accessorKey: "updatedAt", header: ({ column }) => ( ), cell: ({ row }) => { const date = row.getValue("updatedAt"); return (
{formatDistanceToNow(date as Date, { addSuffix: true })}
); }, }, { id: "actions", header: "Actions", cell: ({ row }) => , enableSorting: false, enableHiding: false, }, ];