"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Play, Loader2, Info } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Form } from "@/components/ui/form"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { buildVariableSchemaFromList } from "@/lib/validation/variable-schema"; import { defaultValuesForScript } from "@/lib/config/defaults"; import type { ClientScript } from "@/lib/config/serialize"; import { FieldRenderer } from "./field-renderer"; /** Controls whose content doesn't shrink well into a narrow grid column - long-form text, * or a group of checkboxes that reads better as a single wide list - so they span the full * grid width instead of sharing a row with other fields. */ const WIDE_CONTROLS = new Set(["textarea", "checkboxGroup"]); /** `initialValues` comes from a previous run's (already-redacted) variables when re-running - * secret fields are deliberately excluded there (their stored value is just "***", not the real * one), so those always fall through to the normal default/empty state and have to be re-entered. */ function defaultValuesFor( script: ClientScript, initialValues?: Record, ): Record { const values = defaultValuesForScript(script.variables); if (!initialValues) return values; for (const variable of script.variables) { if (variable.secret) continue; const fromPreviousRun = initialValues[variable.name]; if (fromPreviousRun !== undefined) values[variable.name] = fromPreviousRun; } return values; } export function DynamicForm({ script, initialValues, }: { script: ClientScript; initialValues?: Record; }) { const router = useRouter(); const [submitError, setSubmitError] = useState(null); const schema = buildVariableSchemaFromList(script.variables); const hasSecretVariable = script.variables.some((v) => v.secret); const form = useForm({ resolver: zodResolver(schema), defaultValues: defaultValuesFor(script, initialValues), }); async function onSubmit(values: Record) { setSubmitError(null); const response = await fetch(`/api/scripts/${script.id}/runs`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ variables: values }), }); if (!response.ok) { const data = await response.json().catch(() => ({})); setSubmitError(data.error ?? "Failed to start run"); return; } const data = await response.json(); toast.success(`${script.name} started`); router.push(`/runs/${data.runId}`); } return (
{submitError && ( {submitError} )} {initialValues && ( Pre-filled from a previous run. {hasSecretVariable && " Secret fields aren't carried over and need to be entered again."} )} {script.variables.length === 0 && (

This script takes no parameters.

)} {script.variables.length > 0 && (
{script.variables.map((variable) => (
))}
)}
); }