From 232938f1f9e035cc11b46b2e52f6cf766e260fbc Mon Sep 17 00:00:00 2001 From: IAmKirbki Date: Fri, 8 May 2026 14:07:59 +0200 Subject: [PATCH] fix: enhance components with error handling and improve rate limit logic --- console/src/components/project-switcher.tsx | 2 +- console/src/components/schema-fields.tsx | 71 +++++++++++++++++-- console/src/components/ui/textarea.tsx | 28 ++++---- .../src/views/settings/IntegrationSetup.tsx | 54 +++++++++----- console/src/views/settings/Integrations.tsx | 4 +- console/src/views/users/ListCreateForm.tsx | 4 +- .../controllers/v1/management/providers.go | 16 +++-- 7 files changed, 136 insertions(+), 43 deletions(-) diff --git a/console/src/components/project-switcher.tsx b/console/src/components/project-switcher.tsx index 1c0d839a3..95987af49 100644 --- a/console/src/components/project-switcher.tsx +++ b/console/src/components/project-switcher.tsx @@ -34,7 +34,7 @@ export function ProjectSwitcher({ - + ) => void /** Optional variable groups for TemplateInput / CodeEditor pickers. */ variables?: VariableGroup[] + /** Optional field-level error messages keyed by property name. */ + errors?: Record } export function SchemaFields({ @@ -93,6 +94,7 @@ export function SchemaFields({ value, onChange, variables, + errors, }: SchemaFieldsProps) { const entries = normalizeProperties(schema?.properties) @@ -113,6 +115,21 @@ export function SchemaFields({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [entryKeys]) + // Initialize missing boolean fields to false. + useEffect(() => { + if (!entries.length) return + let changed = false + const next = { ...value } + for (const [key, item] of entries) { + if (item.type === "boolean" && next[key] === undefined) { + next[key] = false + changed = true + } + } + if (changed) onChange(next) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [entryKeys]) + if (!entries.length) return null const sorted = [...entries].sort((a, b) => (a[1].order ?? 0) - (b[1].order ?? 0)) @@ -151,9 +168,12 @@ export function SchemaFields({ } }} /> - {value[key] && ( + {Boolean(value[key]) && (

File configured

)} + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -176,6 +196,9 @@ export function SchemaFields({ maxHeight={300} variables={variables} /> + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -196,6 +219,9 @@ export function SchemaFields({ onChange={(val) => set(key, val)} variables={variables} /> + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -226,6 +252,9 @@ export function SchemaFields({ ))} + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -277,6 +306,9 @@ export function SchemaFields({ placeholder={item.preview} /> )} + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -300,6 +332,9 @@ export function SchemaFields({ checked={(value[key] as boolean) ?? false} onCheckedChange={(checked) => set(key, checked)} /> + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -317,7 +352,11 @@ export function SchemaFields({ value={(value[key] as Record) ?? {}} onChange={(nested) => set(key, nested)} variables={variables} + errors={errors} /> + {errors?.[key] && ( +

{errors[key]}

+ )} ) } @@ -328,6 +367,13 @@ export function SchemaFields({ ) } +interface FormAdapter { + watch(name: string): Record | undefined + setValue(name: string, value: unknown, options?: { shouldDirty?: boolean }): void + clearErrors(name?: string | string[] | readonly string[]): void + formState: { errors: Record } +} + export interface FormSchemaFieldsProps { /** Optional heading rendered above the fields. */ title?: string @@ -338,8 +384,7 @@ export interface FormSchemaFieldsProps { /** The JSON schema describing the fields. */ schema: Schema /** The react-hook-form instance. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - form: UseFormReturn + form: FormAdapter } /** @@ -356,6 +401,7 @@ export function FormSchemaFields({ schema, }: FormSchemaFieldsProps) { const watched = form.watch(parent) ?? {} + const formErrors = form.formState.errors // Derive valid keys from the current schema so we can clean up stale ones const schemaKeys = useMemo(() => { @@ -363,12 +409,28 @@ export function FormSchemaFields({ return new Set(entries.map(([k]) => k)) }, [schema]) + // Extract field-level errors from react-hook-form for the parent path + const errors = useMemo(() => { + const parentErrors = formErrors[parent] + if (!parentErrors || typeof parentErrors !== "object") return undefined + const result: Record = {} + for (const [key, err] of Object.entries( + parentErrors as Record, + )) { + if (err?.message) { + result[key] = err.message + } + } + return Object.keys(result).length > 0 ? result : undefined + }, [formErrors, parent]) + return ( { // Diff and set only the keys that changed so we don't // unnecessarily mark untouched fields as dirty. @@ -376,6 +438,7 @@ export function FormSchemaFields({ const fieldName = `${parent}.${key}` if (next[key] !== watched[key]) { form.setValue(fieldName, next[key], { shouldDirty: true }) + form.clearErrors(fieldName) } } // Remove stale keys that no longer exist in the current schema diff --git a/console/src/components/ui/textarea.tsx b/console/src/components/ui/textarea.tsx index 088defdde..3f9845b06 100644 --- a/console/src/components/ui/textarea.tsx +++ b/console/src/components/ui/textarea.tsx @@ -2,17 +2,21 @@ import * as React from "react" import { cn } from "@/utils" -function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { - return ( -