Skip to content

Comments

Add create run form#308

Merged
jamescmartinez merged 1 commit intomainfrom
create-run-form
Feb 14, 2026
Merged

Add create run form#308
jamescmartinez merged 1 commit intomainfrom
create-run-form

Conversation

@jamescmartinez
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings February 14, 2026 23:17
@codecov
Copy link

codecov bot commented Feb 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jamescmartinez jamescmartinez merged commit aa141a0 into main Feb 14, 2026
14 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a user interface for creating workflow runs directly from the dashboard. It introduces a modal dialog with a form that allows users to trigger new workflow runs by providing workflow name, optional input JSON, scheduling parameters, and version information. The implementation includes both client-side form handling and a new server-side API endpoint.

Changes:

  • Added a new Dialog UI component library based on Base UI primitives
  • Created a CreateRunForm component with validation and error handling
  • Added a createWorkflowRunServerFn API endpoint for workflow run creation
  • Enhanced RunList component with optional header and count display controls

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/dashboard/src/components/ui/dialog.tsx New reusable Dialog component library with customizable size and styling
packages/dashboard/src/components/create-run-form.tsx Form component for creating workflow runs with input validation and error handling
packages/dashboard/src/lib/api.ts Added createWorkflowRunServerFn endpoint and supporting validation functions
packages/dashboard/src/components/run-list.tsx Added showHeader and showCount props for flexible display control
packages/dashboard/src/routes/index.tsx Integrated create run dialog into homepage with trigger button

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +102 to +106
onSuccess?.();
await navigate({
to: "/runs/$runId",
params: { runId: run.id },
});
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After successfully creating a workflow run, the form navigates to the run detail page, but the homepage data won't be immediately refreshed. While the usePolling hook will eventually update the list (every 2 seconds), consider calling router.invalidate() before navigation to ensure the homepage shows the new run when users navigate back. This would provide a better user experience.

Copilot uses AI. Check for mistakes.
Comment on lines +187 to +200
<Field>
<FieldLabel htmlFor="create-run-deadline-at">Deadline</FieldLabel>
<Input
id="create-run-deadline-at"
name="deadlineAt"
type="datetime-local"
value={deadlineAt}
onChange={(event) => {
setDeadlineAt(event.currentTarget.value);
}}
disabled={isSubmitting}
/>
<FieldDescription>Leave empty for no deadline.</FieldDescription>
</Field>
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The datetime-local input type is used for availableAt and deadlineAt fields, but there's no validation to ensure that deadlineAt is after availableAt if both are provided. Consider adding this logical validation to prevent users from setting a deadline before the scheduled start time, which would be an invalid configuration.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +112
export function CreateRunForm({ onCancel, onSuccess }: CreateRunFormProps) {
const navigate = useNavigate();

const [workflowName, setWorkflowName] = useState("");
const [version, setVersion] = useState("");
const [input, setInput] = useState("");
const [availableAt, setAvailableAt] = useState("");
const [deadlineAt, setDeadlineAt] = useState("");
const [inputError, setInputError] = useState<string | null>(null);
const [submitError, setSubmitError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);

async function submitForm() {
if (!workflowName.trim()) {
setSubmitError("Workflow name is required");
return;
}

const normalizedAvailableAt = normalizeOptionalField(availableAt);
const normalizedDeadlineAt = normalizeOptionalField(deadlineAt);
let availableAtIso: string | null = null;
let deadlineAtIso: string | null = null;

try {
if (normalizedAvailableAt) {
availableAtIso = toIsoDateTime(normalizedAvailableAt, "Schedule for");
}
if (normalizedDeadlineAt) {
deadlineAtIso = toIsoDateTime(normalizedDeadlineAt, "Deadline");
}
} catch (error) {
setSubmitError(getErrorMessage(error));
return;
}

const normalizedInput = normalizeOptionalField(input);
if (normalizedInput) {
try {
JSON.parse(normalizedInput);
} catch {
setInputError("Input must be valid JSON");
return;
}
}

setInputError(null);
setSubmitError(null);
setIsSubmitting(true);

try {
const run = await createWorkflowRunServerFn({
data: {
workflowName: workflowName.trim(),
version: normalizeOptionalField(version),
input: normalizedInput,
availableAt: availableAtIso,
deadlineAt: deadlineAtIso,
},
});

onSuccess?.();
await navigate({
to: "/runs/$runId",
params: { runId: run.id },
});
} catch (error) {
setSubmitError(getErrorMessage(error));
} finally {
setIsSubmitting(false);
}
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the dialog closes via onSuccess or onCancel, the form state (workflowName, version, input, etc.) is not reset. This means if a user opens the dialog again after creating a run, the form will still contain the previous values. Consider resetting the form state when the dialog closes or when it opens to provide a clean form experience.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +82
} catch {
setInputError("Input must be valid JSON");
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON.parse call at line 80 is wrapped in a try-catch, but the error is caught and a generic message "Input must be valid JSON" is set. Consider providing more specific error feedback that includes the parsing error details (e.g., the specific JSON syntax error) to help users fix their input more easily. For example: "Input must be valid JSON: unexpected token at position X".

Suggested change
} catch {
setInputError("Input must be valid JSON");
} catch (error) {
const baseMessage = "Input must be valid JSON";
if (error instanceof SyntaxError && error.message) {
setInputError(`${baseMessage}: ${error.message}`);
} else if (error instanceof Error && error.message) {
setInputError(`${baseMessage}: ${error.message}`);
} else {
setInputError(baseMessage);
}

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +85
if (normalizedInput) {
try {
JSON.parse(normalizedInput);
} catch {
setInputError("Input must be valid JSON");
return;
}
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate JSON validation logic exists in both the form and the API handler. The JSON parsing is performed here in the form and again in the API handler at line 122-126 of packages/dashboard/src/lib/api.ts. Consider removing this client-side validation and relying on the server-side validation, or ensure they remain synchronized if both are necessary for UX reasons.

Suggested change
if (normalizedInput) {
try {
JSON.parse(normalizedInput);
} catch {
setInputError("Input must be valid JSON");
return;
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +46
function parseOptionalDate(
value: string | null | undefined,
fieldName: string,
): Date | null {
if (!value) {
return null;
}

const parsedDate = new Date(value);
if (Number.isNaN(parsedDate.getTime())) {
throw new TypeError(`${fieldName} must be a valid date and time`);
}

return parsedDate;
}
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate date validation logic exists in both the form (toIsoDateTime at line 25-31 of packages/dashboard/src/components/create-run-form.tsx) and the API handler (parseOptionalDate). Both functions perform the same validation with similar error messages. Consider consolidating this validation to avoid duplication and potential inconsistencies.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant