Last active
June 4, 2024 19:10
-
-
Save razbakov/4e3a91bfabe948c8dec4fb7f2f771e9c to your computer and use it in GitHub Desktop.
Form Builder: zod schema
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<AdminForm | |
v-model:item="item" | |
:collection="collection" | |
:title="edit" | |
:fields="fields" | |
:schema="schema" | |
@close="editing = false" | |
/> | |
</template> | |
<script setup lang="ts"> | |
const schema = z.object({ | |
name: z.string(), | |
description: z.string().optional().default(''), | |
startDate: z.coerce.date(), | |
endDate: z.coerce.date(), | |
capacity: z.number(), | |
}) | |
const fields = getFieldsDef(schema) | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function getFieldsDef( | |
schema: z.ZodObject<any>, | |
extend?: Record<string, any> | |
): FieldDef[] { | |
return Object.keys(schema.shape).map((key) => { | |
let field: FieldDef = { | |
accessorKey: key, | |
label: deCamelCase(key), | |
as: Input, | |
bind: {}, | |
} | |
if (schema.shape[key]._def.typeName === 'ZodDate') { | |
field.as = InputDate | |
} | |
if (schema.shape[key]._def.typeName === 'ZodNumber') { | |
field.bind.type = 'number' | |
} | |
if (schema.shape[key]._def.typeName === 'ZodEnum') { | |
field.as = InputSelect | |
field.bind.options = schema.shape[key]._def.options | |
} | |
if (extend && extend[key]?.bind) { | |
field.bind = { ...field.bind, ...extend[key].bind } | |
} | |
return column | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<Sheet open @update:open="(v) => v || emit('close')"> | |
<SheetContent> | |
<form @submit.prevent="onSubmit"> | |
<SheetHeader> | |
<SheetTitle>{{ title }}</SheetTitle> | |
<SheetDescription>{{ description }}</SheetDescription> | |
</SheetHeader> | |
<template v-for="field in fields" :key="field.accessorKey"> | |
<FormField v-slot="{ componentField }" :name="field.accessorKey"> | |
<FormItem> | |
<FormLabel>{{ field.label }}</FormLabel> | |
<FormControl> | |
<component | |
:is="field.as" | |
v-bind="{ ...componentField, ...field.bind }" | |
/> | |
</FormControl> | |
<FormDescription /> | |
<FormMessage /> | |
</FormItem> | |
</FormField> | |
</template> | |
<SheetFooter class="justify-end"> | |
<Button type="submit">{{ submit }}</Button> | |
</SheetFooter> | |
</form> | |
</SheetContent> | |
</Sheet> | |
</template> | |
<script setup lang="ts"> | |
import { useForm, configure } from 'vee-validate' | |
import { toTypedSchema } from '@vee-validate/zod' | |
import { toast } from '~/components/ui/toast/use-toast' | |
const props = defineProps({ | |
title: { | |
type: String, | |
default: 'Edit', | |
}, | |
description: { | |
type: String, | |
default: '', | |
}, | |
submit: { | |
type: String, | |
default: 'Save', | |
}, | |
fields: { | |
type: Array, | |
default: () => [], | |
}, | |
collection: { | |
type: String, | |
required: true, | |
}, | |
schema: { | |
type: Object, | |
required: true, | |
}, | |
}) | |
const emit = defineEmits(['close']) | |
const item = defineModel('item', { type: Object, default: {} }) | |
configure({ | |
validateOnBlur: false, | |
}) | |
const { handleSubmit } = useForm({ | |
validationSchema: toTypedSchema(props.schema), | |
initialValues: item.value, | |
}) | |
const onSubmit = handleSubmit(async (values: any) => { | |
try { | |
// save | |
} catch (error: any) { | |
toast({ | |
title: 'Error', | |
description: error.message, | |
variant: 'destructive', | |
}) | |
} | |
emit('close') | |
}) | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment