"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { FlaskConical } from "lucide-react"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useBreadcrumbsEffect } from "~/components/ui/breadcrumb-provider"; import { EntityForm, FormField, FormSection, NextSteps, Tips, } from "~/components/ui/entity-form"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "~/components/ui/select"; import { Textarea } from "~/components/ui/textarea"; import { useStudyContext } from "~/lib/study-context"; import { api } from "~/trpc/react"; const experimentSchema = z.object({ name: z .string() .min(1, "Experiment name is required") .max(100, "Name too long"), description: z .string() .min(10, "Description must be at least 10 characters") .max(1000, "Description too long"), studyId: z.string().uuid("Please select a study"), estimatedDuration: z .number() .min(1, "Duration must be at least 1 minute") .max(480, "Duration cannot exceed 8 hours") .optional(), status: z.enum(["draft", "testing", "ready", "deprecated"]), }); type ExperimentFormData = z.infer; interface ExperimentFormProps { mode: "create" | "edit"; experimentId?: string; } export function ExperimentForm({ mode, experimentId }: ExperimentFormProps) { const router = useRouter(); const { selectedStudyId } = useStudyContext(); const [isSubmitting, setIsSubmitting] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [error, setError] = useState(null); const form = useForm({ resolver: zodResolver(experimentSchema), defaultValues: { status: "draft" as const, studyId: selectedStudyId || "", }, }); // Fetch experiment data for edit mode const { data: experiment, isLoading, error: fetchError, } = api.experiments.get.useQuery( { id: experimentId! }, { enabled: mode === "edit" && !!experimentId }, ); // Fetch user's studies for the dropdown const { data: studiesData, isLoading: studiesLoading } = api.studies.list.useQuery({ memberOnly: true }); // Set breadcrumbs const breadcrumbs = [ { label: "Dashboard", href: "/dashboard" }, { label: "Experiments", href: "/experiments" }, ...(mode === "edit" && experiment ? [ { label: experiment.name, href: `/experiments/${experiment.id}` }, { label: "Edit" }, ] : [{ label: "New Experiment" }]), ]; useBreadcrumbsEffect(breadcrumbs); // Populate form with existing data in edit mode useEffect(() => { if (mode === "edit" && experiment) { form.reset({ name: experiment.name, description: experiment.description ?? "", studyId: experiment.studyId, estimatedDuration: experiment.estimatedDuration ?? undefined, status: experiment.status, }); } }, [experiment, mode, form]); // Update studyId when selectedStudyId changes (for create mode) useEffect(() => { if (mode === "create" && selectedStudyId) { form.setValue("studyId", selectedStudyId); } }, [selectedStudyId, mode, form]); const createExperimentMutation = api.experiments.create.useMutation(); const updateExperimentMutation = api.experiments.update.useMutation(); const deleteExperimentMutation = api.experiments.delete.useMutation(); // Form submission const onSubmit = async (data: ExperimentFormData) => { setIsSubmitting(true); setError(null); try { if (mode === "create") { const newExperiment = await createExperimentMutation.mutateAsync({ ...data, estimatedDuration: data.estimatedDuration || undefined, }); router.push(`/experiments/${newExperiment.id}/designer`); } else { const updatedExperiment = await updateExperimentMutation.mutateAsync({ id: experimentId!, ...data, estimatedDuration: data.estimatedDuration || undefined, }); router.push(`/experiments/${updatedExperiment.id}`); } } catch (error) { setError( `Failed to ${mode} experiment: ${error instanceof Error ? error.message : "Unknown error"}`, ); } finally { setIsSubmitting(false); } }; // Delete handler const onDelete = async () => { if (!experimentId) return; setIsDeleting(true); setError(null); try { await deleteExperimentMutation.mutateAsync({ id: experimentId }); router.push("/experiments"); } catch (error) { setError( `Failed to delete experiment: ${error instanceof Error ? error.message : "Unknown error"}`, ); } finally { setIsDeleting(false); } }; // Loading state for edit mode if (mode === "edit" && isLoading) { return
Loading experiment...
; } // Error state for edit mode if (mode === "edit" && fetchError) { return
Error loading experiment: {fetchError.message}
; } // Form fields const formFields = ( {form.formState.errors.name && (

{form.formState.errors.name.message}

)}