From d1f0e6e5c2ac2fe18a4291de265d874840f922eb Mon Sep 17 00:00:00 2001 From: twwu Date: Sun, 27 Apr 2025 09:56:48 +0800 Subject: [PATCH] feat: Implement Zod schema generation for form validation and update form component usage --- .../base/form/form-scenarios/base/index.tsx | 18 +++++- .../base/form/form-scenarios/base/utils.ts | 56 +++++++++++++++++++ web/app/dev-preview/page.tsx | 19 ++++--- 3 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 web/app/components/base/form/form-scenarios/base/utils.ts diff --git a/web/app/components/base/form/form-scenarios/base/index.tsx b/web/app/components/base/form/form-scenarios/base/index.tsx index 8ba943a23a..ec1bff7cfc 100644 --- a/web/app/components/base/form/form-scenarios/base/index.tsx +++ b/web/app/components/base/form/form-scenarios/base/index.tsx @@ -1,7 +1,8 @@ -import React from 'react' +import React, { useMemo } from 'react' import { useAppForm } from '../..' import BaseField from './field' import type { BaseFormProps } from './types' +import { generateZodSchema } from './utils' const BaseForm = ({ initialData, @@ -9,11 +10,22 @@ const BaseForm = ({ onSubmit, CustomActions, }: BaseFormProps) => { + const schema = useMemo(() => { + const schema = generateZodSchema(configurations) + return schema + }, [configurations]) + const baseForm = useAppForm({ defaultValues: initialData, validators: { - onSubmit: ({ value }) => { - console.log('onSubmit', value) + onChange: ({ value }) => { + const result = schema.safeParse(value) + if (!result.success) { + const issues = result.error.issues + const firstIssue = issues[0].message + return firstIssue + } + return undefined }, }, onSubmit: ({ value }) => { diff --git a/web/app/components/base/form/form-scenarios/base/utils.ts b/web/app/components/base/form/form-scenarios/base/utils.ts new file mode 100644 index 0000000000..e332dd5392 --- /dev/null +++ b/web/app/components/base/form/form-scenarios/base/utils.ts @@ -0,0 +1,56 @@ +import type { ZodSchema, ZodString } from 'zod' +import { z } from 'zod' +import { type BaseConfiguration, BaseVarType } from './types' + +export const generateZodSchema = (fields: BaseConfiguration[]) => { + const shape: Record = {} + + fields.forEach((field) => { + let zodType + + switch (field.type) { + case BaseVarType.textInput: + zodType = z.string() + break + case BaseVarType.numberInput: + zodType = z.number() + break + case BaseVarType.checkbox: + zodType = z.boolean() + break + case BaseVarType.select: + zodType = z.string() + break + default: + zodType = z.any() + break + } + + if (field.required) { + if ([BaseVarType.textInput].includes(field.type)) + zodType = (zodType as ZodString).nonempty(`${field.label} is required`) + } + else { + zodType = zodType.optional() + } + + if (field.maxLength) { + if ([BaseVarType.textInput].includes(field.type)) + zodType = (zodType as ZodString).max(field.maxLength, `${field.label} exceeds max length of ${field.maxLength}`) + } + + if (field.min) { + if ([BaseVarType.numberInput].includes(field.type)) + zodType = (zodType as ZodString).min(field.min, `${field.label} must be at least ${field.min}`) + } + + if (field.max) { + if ([BaseVarType.numberInput].includes(field.type)) + zodType = (zodType as ZodString).max(field.max, `${field.label} exceeds max value of ${field.max}`) + } + + shape[field.variable] = zodType + }) + + return z.object(shape) +} diff --git a/web/app/dev-preview/page.tsx b/web/app/dev-preview/page.tsx index 4849e29478..e0c4a8c300 100644 --- a/web/app/dev-preview/page.tsx +++ b/web/app/dev-preview/page.tsx @@ -1,16 +1,17 @@ 'use client' -import InputFieldForm from '../components/base/form/form-scenarios/base' +import BaseForm from '../components/base/form/form-scenarios/base' import { BaseVarType } from '../components/base/form/form-scenarios/base/types' export default function Page() { return (
-