/* eslint-disable */ 'use client' import React from 'react' import { zodResolver } from '@hookform/resolvers/zod' import { Plus, Trash } from 'lucide-react' import { ControllerRenderProps, DefaultValues, FieldValues, useFieldArray, useForm } from 'react-hook-form' import { z } from 'zod' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { cn } from '@/lib/utils' import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './accordion' import { Button } from './button' import { Checkbox } from './checkbox' import { DatePicker } from './date-picker' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from './form' import { Input } from './input' import { RadioGroup, RadioGroupItem } from './radio-group' import { Separator } from './separator' import { Switch } from './switch' import { Textarea } from './textarea' /** * Beautify a camelCase string. * e.g. "myString" -> "My String" */ function beautifyObjectName(string: string) { let output = string.replace(/([A-Z])/g, ' $1') output = output.charAt(0).toUpperCase() + output.slice(1) return output } /** * Get the lowest level Zod type. * This will unpack optionals, refinements, etc. */ function getBaseSchema(schema: z.ZodAny | z.ZodEffects): z.ZodAny { if ('innerType' in schema._def) { return getBaseSchema(schema._def.innerType as z.ZodAny) } if ('schema' in schema._def) { return getBaseSchema(schema._def.schema) } return schema as z.ZodAny } /** * Get the type name of the lowest level Zod type. * This will unpack optionals, refinements, etc. */ function getBaseType(schema: z.ZodAny): string { return getBaseSchema(schema)._def.typeName } /** * Search for a "ZodDefult" in the Zod stack and return its value. */ function getDefaultValueInZodStack(schema: z.ZodAny): any { const typedSchema = schema as unknown as z.ZodDefault if (typedSchema._def.typeName === 'ZodDefault') { return typedSchema._def.defaultValue() } if ('innerType' in typedSchema._def) { return getDefaultValueInZodStack(typedSchema._def.innerType as unknown as z.ZodAny) } if ('schema' in typedSchema._def) { return getDefaultValueInZodStack((typedSchema._def as any).schema as z.ZodAny) } return undefined } /** * Get all default values from a Zod schema. */ function getDefaultValues>(schema: Schema) { const { shape } = schema type DefaultValuesType = DefaultValues>> const defaultValues = {} as DefaultValuesType for (const key of Object.keys(shape)) { const item = shape[key] as z.ZodAny if (getBaseType(item) === 'ZodObject') { const defaultItems = getDefaultValues(item as unknown as z.ZodObject) for (const defaultItemKey of Object.keys(defaultItems)) { const pathKey = `${key}.${defaultItemKey}` as keyof DefaultValuesType defaultValues[pathKey] = defaultItems[defaultItemKey] } } else { const defaultValue = getDefaultValueInZodStack(item) if (defaultValue !== undefined) { defaultValues[key as keyof DefaultValuesType] = defaultValue } } } return defaultValues } function getObjectFormSchema(schema: ZodObjectOrWrapped): z.ZodObject { if (schema._def.typeName === 'ZodEffects') { const typedSchema = schema as z.ZodEffects> return getObjectFormSchema(typedSchema._def.schema) } return schema as z.ZodObject } /** * Convert a Zod schema to HTML input props to give direct feedback to the user. * Once submitted, the schema will be validated completely. */ function zodToHtmlInputProps( schema: z.ZodNumber | z.ZodString | z.ZodOptional | any ): React.InputHTMLAttributes { if (['ZodOptional', 'ZodNullable'].includes(schema._def.typeName)) { const typedSchema = schema as z.ZodOptional return { ...zodToHtmlInputProps(typedSchema._def.innerType), required: false, } } const typedSchema = schema as z.ZodNumber | z.ZodString if (!('checks' in typedSchema._def)) return {} const { checks } = typedSchema._def const inputProps: React.InputHTMLAttributes = { required: true, } const type = getBaseType(schema) for (const check of checks) { if (check.kind === 'min') { if (type === 'ZodString') { inputProps.minLength = check.value } else { inputProps.min = check.value } } if (check.kind === 'max') { if (type === 'ZodString') { inputProps.maxLength = check.value } else { inputProps.max = check.value } } } return inputProps } export type FieldConfigItem = { description?: React.ReactNode inputProps?: React.InputHTMLAttributes fieldType?: keyof typeof INPUT_COMPONENTS | React.FC renderParent?: (props: { children: React.ReactNode }) => React.ReactElement | null } export type FieldConfig>> = { // If SchemaType.key is an object, create a nested FieldConfig, otherwise FieldConfigItem [Key in keyof SchemaType]?: SchemaType[Key] extends object ? FieldConfig> : FieldConfigItem } /** * A FormInput component can handle a specific Zod type (e.g. "ZodBoolean") */ export type AutoFormInputComponentProps = { zodInputProps: React.InputHTMLAttributes field: ControllerRenderProps fieldConfigItem: FieldConfigItem label: string isRequired: boolean fieldProps: any zodItem: z.ZodAny } function AutoFormInput({ label, isRequired, fieldConfigItem, fieldProps }: AutoFormInputComponentProps) { return ( {label} {isRequired && *} {fieldConfigItem.description && {fieldConfigItem.description}} ) } function AutoFormNumber({ fieldProps, ...props }: AutoFormInputComponentProps) { return ( ) } function AutoFormTextarea({ label, isRequired, fieldConfigItem, fieldProps }: AutoFormInputComponentProps) { return ( {label} {isRequired && *}