Skip to content

Instantly share code, notes, and snippets.

@razbakov
Last active June 4, 2024 19:10
Show Gist options
  • Save razbakov/4e3a91bfabe948c8dec4fb7f2f771e9c to your computer and use it in GitHub Desktop.
Save razbakov/4e3a91bfabe948c8dec4fb7f2f771e9c to your computer and use it in GitHub Desktop.
Form Builder: zod schema
<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>
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
})
}
<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