diff --git a/web/src/api/asset_interfaces/datasources/interfaces.ts b/web/src/api/asset_interfaces/datasources/interfaces.ts index abc7544c7..535d3ff5b 100644 --- a/web/src/api/asset_interfaces/datasources/interfaces.ts +++ b/web/src/api/asset_interfaces/datasources/interfaces.ts @@ -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( diff --git a/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx b/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx index 132a12d67..fc5876984 100644 --- a/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx +++ b/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx @@ -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[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<{ ); }; - -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[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
Test2
; -}; diff --git a/web/src/components/ui/form/FormBase.tsx b/web/src/components/ui/form/FormBase.tsx index c4b72825e..8bfc01930 100644 --- a/web/src/components/ui/form/FormBase.tsx +++ b/web/src/components/ui/form/FormBase.tsx @@ -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[0]['type']; }) { const field = useFieldContext(); + 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 = ( - field.handleChange(e.target.value)} - type={type} - placeholder={placeholder} - /> +
+ field.handleChange(e.target.value)} + type={type} + placeholder={placeholder} + /> + {showError && ( + + {error} + + )} +
); if (label === null) return InputComponent; @@ -91,7 +109,51 @@ export function TextField({ } export function NumberField(props: Parameters[0]) { - return ; + const field = useFieldContext(); + 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 = ( +
+ { + const val = e.target.value === '' ? 0 : Number(e.target.value); + field.handleChange(val); + }} + type="number" + placeholder={props.placeholder} + /> + {showError && ( + + {error} + + )} +
+ ); + + if (props.label === null) return InputComponent; + + return ( + + {InputComponent} + + ); } export function PasswordField({ @@ -103,15 +165,32 @@ export function PasswordField({ placeholder }: Parameters[0]) { const field = useFieldContext(); + 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 = ( - field.handleChange(e.target.value)} - placeholder={placeholder} - /> +
+ field.handleChange(e.target.value)} + placeholder={placeholder} + /> + {showError && ( + + {error} + + )} +
); if (label === null) return InputComponent;