feat: Introduce dedicated participant, experiment, and trial detail/edit pages, enable MinIO, and refactor dashboard navigation.

This commit is contained in:
2025-12-11 20:04:52 -05:00
parent 5be4ff0372
commit d83c02759a
45 changed files with 4123 additions and 1455 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import { type ColumnDef } from "@tanstack/react-table";
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
import { ArrowUpDown, MoreHorizontal, Copy, Eye, Edit, LayoutTemplate, PlayCircle, Archive } from "lucide-react";
import * as React from "react";
import { formatDistanceToNow } from "date-fns";
@@ -259,20 +259,26 @@ export const columns: ColumnDef<Experiment>[] = [
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(experiment.id)}
>
Copy experiment ID
<Copy className="mr-2 h-4 w-4" />
Copy ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`/studies/${experiment.studyId}/experiments/${experiment.id}`}>View details</Link>
<Link href={`/studies/${experiment.studyId}/experiments/${experiment.id}`}>
<Eye className="mr-2 h-4 w-4" />
Details
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/studies/${experiment.studyId}/experiments/${experiment.id}/edit`}>
Edit experiment
<Edit className="mr-2 h-4 w-4" />
Edit
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/studies/${experiment.studyId}/experiments/${experiment.id}/designer`}>
Open designer
<LayoutTemplate className="mr-2 h-4 w-4" />
Designer
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -280,12 +286,14 @@ export const columns: ColumnDef<Experiment>[] = [
<Link
href={`/studies/${experiment.studyId}/trials/new?experimentId=${experiment.id}`}
>
Create trial
<PlayCircle className="mr-2 h-4 w-4" />
Start Trial
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-red-600">
Archive experiment
<Archive className="mr-2 h-4 w-4" />
Archive
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -736,6 +736,16 @@ export function DesignerRoot({
const targetStep = steps.find((s) => s.id === stepId);
if (!targetStep) return;
const fullDef = actionRegistry.getAction(actionDef.type);
const defaultParams: Record<string, unknown> = {};
if (fullDef?.parameters) {
for (const param of fullDef.parameters) {
if (param.default !== undefined) {
defaultParams[param.id] = param.default;
}
}
}
const execution: ExperimentAction["execution"] =
actionDef.execution &&
(actionDef.execution.transport === "internal" ||
@@ -754,7 +764,7 @@ export function DesignerRoot({
type: actionDef.type,
name: actionDef.name,
category: actionDef.category as ExperimentAction["category"],
parameters: {},
parameters: defaultParams,
source: actionDef.source as ExperimentAction["source"],
execution,
};
@@ -818,6 +828,7 @@ export function DesignerRoot({
description={designMeta.description || "No description"}
icon={Play}
actions={actions}
className="pb-6"
/>
<div className="relative flex flex-1 flex-col overflow-hidden">

View File

@@ -70,7 +70,7 @@ export interface PropertiesPanelProps {
className?: string;
}
export function PropertiesPanel({
export function PropertiesPanelBase({
design,
selectedStep,
selectedAction,
@@ -198,8 +198,8 @@ export function PropertiesPanel({
const ResolvedIcon: React.ComponentType<{ className?: string }> =
def?.icon && iconComponents[def.icon]
? (iconComponents[def.icon] as React.ComponentType<{
className?: string;
}>)
className?: string;
}>)
: Zap;
return (
@@ -633,3 +633,5 @@ export function PropertiesPanel({
</div>
);
}
export const PropertiesPanel = React.memo(PropertiesPanelBase);

View File

@@ -284,14 +284,17 @@ export function InspectorPanel({
<div className="flex-1 overflow-x-hidden overflow-y-auto">
<div className="w-full px-0 py-2 break-words whitespace-normal">
<PropertiesPanel
design={{
id: "design",
name: "Design",
description: "",
version: 1,
steps,
lastSaved: new Date(),
}}
design={useMemo(
() => ({
id: "design",
name: "Design",
description: "",
version: 1,
steps,
lastSaved: new Date(),
}),
[steps],
)}
selectedStep={selectedStep}
selectedAction={selectedAction}
onActionUpdate={handleActionUpdate}

View File

@@ -645,7 +645,7 @@ export function validateExecution(
issues.push({
severity: "warning",
message:
"Multiple steps with trial_start trigger may cause execution conflicts",
"Multiple steps will start simultaneously. Ensure parallel execution is intended.",
category: "execution",
field: "trigger.type",
stepId: step.id,