validator update

This commit is contained in:
Nate Kelley 2025-03-25 13:04:40 -06:00
parent 1d37f2fe2f
commit 550dda19ca
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 109 additions and 84 deletions

View File

@ -57,7 +57,7 @@ export enum DataSourceEnvironment {
}
export const PostgresCredentialsSchema = v.object({
name: v.string(),
name: v.pipe(v.string(), v.minLength(3, 'Name must be at least 3 characters')),
type: v.union([v.literal('postgres'), v.literal('supabase')]),
host: v.string(),
port: v.pipe(

View File

@ -14,28 +14,6 @@ import { useAppForm } from '@/components/ui/form/useFormBaseHooks';
import { MultipleInlineFields } from '@/components/ui/form/FormBase';
import { useDataSourceFormSuccess } from './helpers';
import * as v from 'valibot';
import { useForm } from '@tanstack/react-form';
const ValibotSchema = v.object({
// firstName: v.pipe(
// v.string(),
// v.minLength(3, '[Valibot] You must have a length of at least 3'),
// v.startsWith('A', "[Valibot] First name must start with 'A'")
// ),
// lastName: v.pipe(v.string(), v.minLength(3, '[Valibot] You must have a length of at least 3'))
name: v.string(),
type: v.union([v.literal('postgres'), v.literal('supabase')]),
host: v.string(),
port: v.pipe(
v.number(),
v.minValue(1, 'Port must be greater than 0'),
v.maxValue(65535, 'Port must be less than or equal to 65535')
),
username: v.string(),
password: v.string(),
default_database: v.string(), // postgres
default_schema: v.string() // public
});
export const PostgresForm: React.FC<{
dataSource?: DataSource;
@ -55,20 +33,22 @@ export const PostgresForm: React.FC<{
password: credentials?.password || '',
default_database: credentials?.default_database || '',
default_schema: credentials?.default_schema || '',
type: 'postgres',
type: credentials?.type || 'postgres',
name: dataSource?.name || credentials?.name || ''
} as Parameters<typeof createPostgresDataSource>[0],
onSubmit: async ({ value }) => {
await dataSourceFormSubmit({
flow,
dataSourceId: dataSource?.id,
onUpdate: () => updateDataSource({ id: dataSource!.id, ...value }),
onCreate: () => createDataSource(value)
});
} satisfies PostgresCredentials,
onSubmit: async ({ value, ...rest }) => {
console.log(rest);
// await dataSourceFormSubmit({
// flow,
// dataSourceId: dataSource?.id,
// onUpdate: () => updateDataSource({ id: dataSource!.id, ...value }),
// onCreate: () => createDataSource(value)
// });
},
validators: {
// onChangeAsyncDebounceMs: 1000,
onChange: PostgresCredentialsSchema
onChangeAsyncDebounceMs: 1000,
onChangeAsync: PostgresCredentialsSchema,
onSubmit: PostgresCredentialsSchema
}
});
@ -127,37 +107,3 @@ export const PostgresForm: React.FC<{
</FormWrapper>
);
};
const Test2 = () => {
const flow = 'create';
const dataSourceFormSubmit = useDataSourceFormSuccess();
const form2 = useForm({
defaultValues: {
host: 'test',
port: 5432,
username: 'test',
password: 'test',
default_database: 'test',
default_schema: 'test',
type: 'postgres',
name: 'test'
} satisfies Parameters<typeof createPostgresDataSource>[0],
onSubmit: async ({ value }) => {
await dataSourceFormSubmit({
flow,
dataSourceId: '123',
onUpdate: async () => {},
onCreate: async () => {}
});
},
validators: {
// DEMO: You can switch between schemas seamlessly
// onChange: ZodSchema,
onChange: ValibotSchema
// onChange: ArkTypeSchema,
}
});
return <div>Test2</div>;
};

View File

@ -5,6 +5,7 @@ import { InputPassword } from '../inputs/InputPassword';
import { cn } from '@/lib/classMerge';
import { Button } from '../buttons';
import { ReactNode } from 'react';
import { Text } from '../typography';
export const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts();
@ -65,16 +66,33 @@ export function TextField({
type?: Parameters<typeof Input>[0]['type'];
}) {
const field = useFieldContext<string>();
const {
name,
state: {
value,
meta: { errors, isTouched }
}
} = field;
const error = errors?.[0]?.message;
const isFormSubmitted = field.form.state.submissionAttempts > 1;
const showError = !!error && (isFormSubmitted || isTouched);
const InputComponent = (
<div className={cn('relative flex w-full flex-col', className)}>
<Input
id={field.name}
className={cn('flex-shrink', className)}
value={field.state.value}
id={name}
className={cn('w-full flex-shrink')}
value={value}
onChange={(e) => field.handleChange(e.target.value)}
type={type}
placeholder={placeholder}
/>
{showError && (
<Text className="mt-0.5 text-left" size={'sm'} variant={'danger'}>
{error}
</Text>
)}
</div>
);
if (label === null) return InputComponent;
@ -91,7 +109,51 @@ export function TextField({
}
export function NumberField(props: Parameters<typeof TextField>[0]) {
return <TextField {...props} type="number" />;
const field = useFieldContext<number>();
const isFormSubmitted = field.form.state.submissionAttempts > 1;
const {
name,
state: {
value,
meta: { errors, isTouched }
}
} = field;
const error = errors?.[0]?.message;
const showError = !!error && (isFormSubmitted || isTouched);
const InputComponent = (
<div className={cn('relative flex w-full flex-col', props.className)}>
<Input
id={name}
className={cn('w-full flex-shrink')}
value={value ?? ''}
onChange={(e) => {
const val = e.target.value === '' ? 0 : Number(e.target.value);
field.handleChange(val);
}}
type="number"
placeholder={props.placeholder}
/>
{showError && (
<Text className="mt-0.5 text-left" size={'sm'} variant={'danger'}>
{error}
</Text>
)}
</div>
);
if (props.label === null) return InputComponent;
return (
<LabelWrapper
label={props.label}
direction={props.direction}
labelClassName={props.labelClassName}
htmlFor={field.name}>
{InputComponent}
</LabelWrapper>
);
}
export function PasswordField({
@ -103,15 +165,32 @@ export function PasswordField({
placeholder
}: Parameters<typeof TextField>[0]) {
const field = useFieldContext<string>();
const {
name,
state: {
value,
meta: { errors, isTouched }
}
} = field;
const error = errors?.[0]?.message;
const isFormSubmitted = field.form.state.submissionAttempts > 1;
const showError = !!error && (isFormSubmitted || isTouched);
const InputComponent = (
<div className={cn('relative flex w-full flex-col', className)}>
<InputPassword
id={field.name}
id={name}
className={cn('flex-shrink', inputClassName)}
value={field.state.value}
value={value}
onChange={(e) => field.handleChange(e.target.value)}
placeholder={placeholder}
/>
{showError && (
<Text className="mt-0.5 text-left" size={'sm'} variant={'danger'}>
{error}
</Text>
)}
</div>
);
if (label === null) return InputComponent;