94 lines
2.3 KiB
TypeScript

import { useAppForm } from '@/app/components/base/form'
import BaseField from '@/app/components/base/form/form-scenarios/base/field'
import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types'
import Toast from '@/app/components/base/toast'
import { useCallback, useImperativeHandle } from 'react'
import type { ZodSchema } from 'zod'
import Header from './header'
type OptionsProps = {
initialData: Record<string, any>
configurations: BaseConfiguration[]
schema: ZodSchema
onSubmit: (data: Record<string, any>) => void
onPreview: () => void
ref: React.RefObject<any>
}
const Form = ({
initialData,
configurations,
schema,
onSubmit,
onPreview,
ref,
}: OptionsProps) => {
const form = useAppForm({
defaultValues: initialData,
validators: {
onSubmit: ({ value }) => {
const result = schema.safeParse(value)
if (!result.success) {
const issues = result.error.issues
const firstIssue = issues[0]
const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}`
Toast.notify({
type: 'error',
message: errorMessage,
})
return errorMessage
}
return undefined
},
},
onSubmit: ({ value }) => {
onSubmit(value)
},
})
useImperativeHandle(ref, () => {
return {
submit: () => {
form.handleSubmit()
},
}
}, [form])
const handleReset = useCallback(() => {
form.reset()
}, [form])
return (
<form
className='flex w-full flex-col rounded-lg border border-components-panel-border bg-components-panel-bg'
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<form.Subscribe
selector={state => state.isDirty}
children={isDirty => (
<Header
onReset={handleReset}
resetDisabled={!isDirty}
onPreview={onPreview}
/>
)}
/>
<div className='flex flex-col gap-3 border-t border-divider-subtle px-4 py-3'>
{configurations.map((config, index) => {
const FieldComponent = BaseField({
initialData,
config,
})
return <FieldComponent key={index} form={form} />
})}
</div>
</form>
)
}
export default Form