// props to claude for helping out with typescript tomfoolery // copyleft srizan tho 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; import { Path, PathValue, useForm } from 'react-hook-form'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { z } from 'zod'; import type { UniversalFormProps } from './types'; import { customDataSchema, githubSettingsSchema, githubTestIssueSchema, projectSettingsSchema, ratelimitChangeSchema, } from './zod'; import SubmitButton from '../SubmitButton/SubmitButton'; import { useActionState } from 'react'; import React from 'react'; import { toast } from 'sonner'; import { createSchema } from '@/lib/forms/zod'; export const schemaDb = [ { name: 'projectSettings', zod: projectSettingsSchema }, { name: 'ratelimitChange', zod: ratelimitChangeSchema }, { name: 'customData', zod: customDataSchema }, { name: 'create', zod: createSchema }, { name: 'githubSettings', zod: githubSettingsSchema }, { name: 'githubTestIssue', zod: githubTestIssueSchema }, ] as const; export function UniversalForm({ fields, schemaName, action, onActionComplete, defaultValues, submitText = 'Submit', submitClassname, }: UniversalFormProps) { // @ts-ignore idk why this error is happening, first apprearing on the react 19 update. const [state, formAction] = useActionState<{ success: boolean; error?: string }>(action, null); const schema = schemaDb.find((s) => s.name === schemaName)?.zod; if (!schema) { throw new Error(`Schema "${schemaName}" not found`); } // Initialize default values for all fields const initialValues = React.useMemo(() => { const values: Record = {}; fields.forEach((field) => { values[field.name] = field.value ?? ''; // Use empty string as fallback }); return { ...values, ...defaultValues }; }, [fields, defaultValues]); const form = useForm>({ resolver: zodResolver(schema), defaultValues: initialValues as z.infer, }); React.useEffect(() => { if (state && !state.success) { toast.error(state.error); } if (state) { onActionComplete?.(state); } }, [state, onActionComplete]); return (
{fields.map((field) => ( >} render={({ field: formField }) => ( {field.type !== 'hidden' && {field.label}} {field.description && {field.description}} )} /> ))} ); }