feat(forms): add declarative form builder and migrate routes to it - #546
Draft
thokra-nav wants to merge 4 commits into
Draft
thokra-nav wants to merge 4 commits into
thokra-nav wants to merge 4 commits into
Conversation
Forms were hand-rolled per route: each page wired up its own controls, its own validation and its own error handling, so the same concerns were re-implemented (and drifted) across every create, edit and delete page. Introduce a single declarative definition that both sides share: - `$lib/ui/Form` renders a form from a field definition, validates on the client with the very same zod schema the action uses, and reports issues in a linked error summary. Supports groups, conditional fields via `dynamic`, and repeatable field groups. - `$lib/server/form` provides `validateForm` and `formAction`, which build the validate/mutate/report cycle an action performs. GraphQL errors the API attributes to an input land on the offending control; submitted values are echoed back so a failed submit stays editable. - `$lib/forms/*` holds the field definitions, shared by page and action so the two can never disagree about which fields exist. Migrate the service account, team, workload (env/image/resize/delete), opensearch, valkey and postgres routes onto it, and add tests covering the schema building, rendering and action behaviour. Adds zod as a dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared, declarative form system (field definitions + Zod validation + reusable server action helpers) and migrates many routes from hand-rolled forms/actions to the new implementation, with tests and dependency updates to support the change.
Changes:
- Added
$lib/ui/Formrendering/validation components (including repeatable groups) and$lib/server/formhelpers (validateForm,formAction) to unify client/server validation + error mapping. - Migrated multiple create/edit/delete flows (team, service accounts, Valkey, OpenSearch, Postgres, app/job actions, vulnerabilities search) to the declarative forms.
- Added Vitest support + unit tests for schema building and action behavior; added
zoddependency.
Reviewed changes
Copilot reviewed 84 out of 85 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.config.js | Adds alias to stub $app/forms during tests. |
| src/styles/app.css | Adds a dark-mode <option> background fix. |
| src/routes/vulnerabilities/(single)/+page.server.ts | Uses shared validateForm for CVE search action. |
| src/routes/team/create/+page.svelte | Migrates team create UI to <Form> + shared field definition. |
| src/routes/team/create/+page.server.ts | Migrates team create action to formAction. |
| src/routes/team/[team]/valkey/create/+page.svelte | Migrates Valkey create UI to <Form> with groups/defaults/options overrides. |
| src/routes/team/[team]/valkey/create/+page.server.ts | Migrates Valkey create action to formAction. |
| src/routes/team/[team]/valkey/+page.svelte | Removes PageModal-based create flow wiring. |
| src/routes/team/[team]/settings/service_accounts/create/+page.ts | Improves page title + adds doc link metadata. |
| src/routes/team/[team]/settings/service_accounts/create/+page.svelte | Updates props usage for new form handling. |
| src/routes/team/[team]/settings/service_accounts/create/+page.server.ts | Migrates service account create action to formAction. |
| src/routes/team/[team]/settings/service_accounts/+page.svelte | Removes PageModal flow; uses List actions snippet for create button. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/token/create/+page.ts | Adds doc link + breadcrumb label adjustment. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/token/create/+page.svelte | Passes form into CreateTokenForm. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/token/create/+page.server.ts | Migrates token creation to formAction + server-side expiry resolution. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/binding/add/+page.ts | Adds doc link metadata. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/binding/add/+page.svelte | Uses shared failureMessage helper for enhance failures. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/binding/add/+page.server.ts | Migrates add-binding action to formAction. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/+page.svelte | Removes modal invalidation + embedded modal routes. |
| src/routes/team/[team]/settings/service_accounts/[serviceAccountID]/+page.server.ts | Migrates delete-token/remove-binding actions to formAction. |
| src/routes/team/[team]/opensearch/create/+page.svelte | Migrates OpenSearch create UI to <Form> with defaults/options overrides. |
| src/routes/team/[team]/opensearch/create/+page.server.ts | Migrates OpenSearch create action to formAction. |
| src/routes/team/[team]/[env]/valkey/[valkey]/edit/+page.svelte | Migrates Valkey edit UI to <Form> with defaults. |
| src/routes/team/[team]/[env]/valkey/[valkey]/edit/+page.server.ts | Migrates Valkey edit action to formAction. |
| src/routes/team/[team]/[env]/valkey/[valkey]/delete/+page.svelte | Migrates Valkey delete UI to <Form> + cancel/back link. |
| src/routes/team/[team]/[env]/valkey/[valkey]/delete/+page.server.ts | Migrates Valkey delete action to formAction + confirmation schema factory. |
| src/routes/team/[team]/[env]/postgres/[postgres]/delete/+page.svelte | Migrates Postgres delete UI to <Form> + cancel/back link. |
| src/routes/team/[team]/[env]/postgres/[postgres]/delete/+page.server.ts | Migrates Postgres delete action to formAction + confirmation schema factory. |
| src/routes/team/[team]/[env]/opensearch/[opensearch]/edit/+page.svelte | Migrates OpenSearch edit UI to <Form> and keeps “reducing storage” hint. |
| src/routes/team/[team]/[env]/opensearch/[opensearch]/edit/+page.server.ts | Migrates OpenSearch edit action to formAction. |
| src/routes/team/[team]/[env]/opensearch/[opensearch]/delete/+page.svelte | Migrates OpenSearch delete UI to <Form> + cancel/back link. |
| src/routes/team/[team]/[env]/opensearch/[opensearch]/delete/+page.server.ts | Migrates OpenSearch delete action to formAction + confirmation schema factory. |
| src/routes/team/[team]/[env]/job/[job]/JobActions.svelte | Removes PageModal-based “env” modal wiring. |
| src/routes/team/[team]/[env]/job/[job]/env/+page.svelte | Migrates job env var editor to repeatable <Form>. |
| src/routes/team/[team]/[env]/job/[job]/env/+page.server.ts | Migrates job env update action to formAction + rename mapping. |
| src/routes/team/[team]/[env]/job/[job]/delete/+page.svelte | Migrates job delete UI to shared delete confirmation form. |
| src/routes/team/[team]/[env]/job/[job]/delete/+page.server.ts | Adds server action for job deletion via formAction. |
| src/routes/team/[team]/[env]/app/[app]/resize/+page.svelte | Migrates app resize UI to <Form> + refine constraint. |
| src/routes/team/[team]/[env]/app/[app]/resize/+page.server.ts | Migrates app resize action to formAction + rename mapping. |
| src/routes/team/[team]/[env]/app/[app]/image/+page.svelte | Migrates image rollback UI to <Form> with radio options and better labels. |
| src/routes/team/[team]/[env]/app/[app]/image/+page.server.ts | Adds optionsOverrides fetching to restrict image choices server-side; uses formAction. |
| src/routes/team/[team]/[env]/app/[app]/env/+page.svelte | Migrates app env var editor to repeatable <Form>. |
| src/routes/team/[team]/[env]/app/[app]/env/+page.server.ts | Migrates app env update action to formAction + rename mapping. |
| src/routes/team/[team]/[env]/app/[app]/delete/+page.svelte | Migrates app delete UI to shared delete confirmation form. |
| src/routes/team/[team]/[env]/app/[app]/delete/+page.server.ts | Adds server action for app deletion via formAction. |
| src/routes/team/[team]/[env]/app/[app]/AppActions.svelte | Removes PageModal wiring for resize/env/image actions. |
| src/routes/admin/service_accounts/create/+page.svelte | Updates to accept form via props rather than $app/state. |
| src/routes/admin/service_accounts/create/+page.server.ts | Migrates admin service account create action to formAction. |
| src/routes/admin/service_accounts/[serviceAccountID]/token/create/+page.svelte | Passes form into CreateTokenForm. |
| src/routes/admin/service_accounts/[serviceAccountID]/token/create/+page.server.ts | Migrates admin token creation to formAction + server-side expiry resolution. |
| src/routes/admin/service_accounts/[serviceAccountID]/binding/add/+page.svelte | Uses shared failureMessage helper for enhance failures. |
| src/routes/admin/service_accounts/[serviceAccountID]/binding/add/+page.server.ts | Migrates admin add-binding action to formAction. |
| src/routes/admin/service_accounts/[serviceAccountID]/+page.server.ts | Migrates delete-token/remove-binding actions to formAction. |
| src/routes/+page.svelte | Removes PageModal-based “Create team” flow. |
| src/lib/ui/Textarea.svelte | Extends Textarea with error rendering/ARIA, optional snippet description, sizing. |
| src/lib/ui/Form/RepeatField.svelte | Adds repeatable field-group rendering (add/remove rows). |
| src/lib/ui/Form/FormField.svelte | Adds field renderer mapping form field definitions to DS components. |
| src/lib/ui/Form/Form.svelte | Adds core form renderer with client-side Zod validation + error summary linking + enhance handling. |
| src/lib/test/app-forms.ts | Provides Vitest stub for $app/forms. |
| src/lib/server/form.ts | Adds server helpers for validation + action wiring + GraphQL error→field mapping. |
| src/lib/server/form.test.ts | Adds unit tests for server form helpers and GraphQL issue mapping. |
| src/lib/forms/workload.ts | Adds shared workload forms (resize, set image version) + refinement. |
| src/lib/forms/workload-env.ts | Adds repeatable env-var form + uniqueness refinement. |
| src/lib/forms/vulnerability.ts | Adds CVE search form definition. |
| src/lib/forms/valkey.ts | Adds Valkey form definition + group config. |
| src/lib/forms/valkey.snippets.svelte | Adds snippet descriptions for Valkey fields. |
| src/lib/forms/team.ts | Adds team create form definition + validation. |
| src/lib/forms/serviceaccounts.ts | Adds service account + binding/token removal form definitions. |
| src/lib/forms/service-account-token.ts | Adds service account token create/delete form definitions. |
| src/lib/forms/opensearch.ts | Adds OpenSearch form definition with dynamic option/constraint logic. |
| src/lib/forms/forms.test.ts | Adds unit tests for several form schemas/refinements. |
| src/lib/forms/delete-confirmation.ts | Adds reusable “type name to confirm” form factory. |
| src/lib/domain/vulnerability/CveSearch.svelte | Improves enhance failure handling and displays failure message. |
| src/lib/domain/service-accounts/tokenExpiry.ts | Updates expiry option shape to { value, label }. |
| src/lib/domain/service-accounts/ServiceAccountDetail.svelte | Removes modal-click wiring for token/binding actions. |
| src/lib/domain/service-accounts/ServiceAccountAuthentications.svelte | Uses shared failureMessage helper for enhance failures. |
| src/lib/domain/service-accounts/CreateTokenForm.svelte | Migrates token form UI to shared <Form> + shared field definition. |
| src/lib/domain/service-accounts/createToken.server.ts | Removes legacy create-token action implementation. |
| src/lib/domain/service-accounts/CreateServiceAccountForm.svelte | Migrates service account create UI to shared <Form>. |
| pnpm-workspace.yaml | Allows building houdini-svelte in pnpm constraints. |
| pnpm-lock.yaml | Locks added zod@4.4.3. |
| package.json | Adds zod@4.4.3 dependency. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comment on lines
+484
to
+489
| /* fix option background color in dark mode */ | ||
| @media (prefers-color-scheme: dark) { | ||
| option { | ||
| background-color: var(--ax-bg-raised); | ||
| } | ||
| } |
Comment on lines
75
to
79
| {/if} | ||
| <textarea | ||
| class="aksel-textarea__input aksel-body-short aksel-body-short--small textarea" | ||
| {name} | ||
| class="aksel-textarea__input aksel-body-short aksel-body-short--{size} textarea" | ||
| {id} |
Comment on lines
+90
to
+97
| <Button | ||
| {size} | ||
| type="button" | ||
| variant="tertiary-neutral" | ||
| icon={TrashIcon} | ||
| title="Remove {field.label} entry {index + 1}" | ||
| onclick={() => removeRow(index)} | ||
| /> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Forms were hand-rolled per route: each page wired up its own controls, its own validation and its own error handling, so the same concerns were re-implemented (and drifted) across every create, edit and delete page.
Introduce a single declarative definition that both sides share:
$lib/ui/Formrenders a form from a field definition, validates on the client with the very same zod schema the action uses, and reports issues in a linked error summary. Supports groups, conditional fields viadynamic, and repeatable field groups.$lib/server/formprovidesvalidateFormandformAction, which build the validate/mutate/report cycle an action performs. GraphQL errors the API attributes to an input land on the offending control; submitted values are echoed back so a failed submit stays editable.$lib/forms/*holds the field definitions, shared by page and action so the two can never disagree about which fields exist.Migrate the service account, team, workload (env/image/resize/delete), opensearch, valkey and postgres routes onto it, and add tests covering the schema building, rendering and action behaviour.
Adds zod as a dependency.