149 lines
3.8 KiB
Vue
149 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
// types
|
|
import type z from 'zod'
|
|
import type { MaterialFormData } from '~/schemas/material'
|
|
// helpers
|
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
import { useForm } from 'vee-validate'
|
|
// components
|
|
|
|
interface Props {
|
|
schema: z.ZodSchema<any>
|
|
uoms: any[]
|
|
items: any[]
|
|
}
|
|
|
|
const isLoading = ref(false)
|
|
const props = defineProps<Props>()
|
|
const emit = defineEmits<{
|
|
submit: [values: MaterialFormData, resetForm: () => void]
|
|
cancel: [resetForm: () => void]
|
|
}>()
|
|
|
|
const { handleSubmit, defineField, errors } = useForm({
|
|
validationSchema: toTypedSchema(props.schema),
|
|
initialValues: {
|
|
code: '',
|
|
name: '',
|
|
uom_code: '',
|
|
item_id: '',
|
|
stock: 0,
|
|
} as Partial<MaterialFormData>,
|
|
})
|
|
|
|
const [code, codeAttrs] = defineField('code')
|
|
const [name, nameAttrs] = defineField('name')
|
|
const [uom, uomAttrs] = defineField('uom_code')
|
|
const [item, itemAttrs] = defineField('item_id')
|
|
const [stock, stockAttrs] = defineField('stock')
|
|
|
|
const resetForm = () => {
|
|
code.value = ''
|
|
name.value = ''
|
|
uom.value = ''
|
|
item.value = ''
|
|
stock.value = 0
|
|
}
|
|
|
|
// Form submission handler
|
|
function onSubmitForm(values: any) {
|
|
const formData: MaterialFormData = {
|
|
name: values.name || '',
|
|
code: values.code || '',
|
|
uom_code: values.uom_code || '',
|
|
item_id: values.item_id || '',
|
|
stock: values.stock || 0,
|
|
}
|
|
emit('submit', formData, resetForm)
|
|
}
|
|
|
|
// Form cancel handler
|
|
function onCancelForm() {
|
|
emit('cancel', resetForm)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<form class="grid gap-2" @submit="handleSubmit(onSubmitForm)">
|
|
<div class="grid gap-2">
|
|
<label for="code">Kode</label>
|
|
<Input
|
|
id="code"
|
|
v-model="code"
|
|
v-bind="codeAttrs"
|
|
:disabled="isLoading"
|
|
:class="{ 'border-red-500': errors.code }"
|
|
/>
|
|
<span v-if="errors.code" class="text-sm text-red-500">
|
|
{{ errors.code }}
|
|
</span>
|
|
</div>
|
|
<div class="grid gap-2">
|
|
<label for="name">Nama</label>
|
|
<Input
|
|
id="name"
|
|
v-model="name"
|
|
v-bind="nameAttrs"
|
|
:disabled="isLoading"
|
|
:class="{ 'border-red-500': errors.name }"
|
|
/>
|
|
<span v-if="errors.name" class="text-sm text-red-500">
|
|
{{ errors.name }}
|
|
</span>
|
|
</div>
|
|
<div class="grid gap-2">
|
|
<label for="uom">Satuan</label>
|
|
<Select
|
|
id="uom"
|
|
v-model="uom"
|
|
icon-name="i-lucide-chevron-down"
|
|
placeholder="Pilih satuan"
|
|
v-bind="uomAttrs"
|
|
:items="uoms"
|
|
:disabled="isLoading"
|
|
:class="{ 'border-red-500': errors.uom_code }"
|
|
/>
|
|
<span v-if="errors.uom_code" class="text-sm text-red-500">
|
|
{{ errors.uom_code }}
|
|
</span>
|
|
</div>
|
|
<div class="grid gap-2">
|
|
<label for="item">Item</label>
|
|
<Select
|
|
id="item"
|
|
v-model="item"
|
|
icon-name="i-lucide-chevron-down"
|
|
placeholder="Pilih item"
|
|
v-bind="itemAttrs"
|
|
:items="items"
|
|
:disabled="isLoading"
|
|
:class="{ 'border-red-500': errors.item_id }"
|
|
/>
|
|
<span v-if="errors.item_id" class="text-sm text-red-500">
|
|
{{ errors.item_id }}
|
|
</span>
|
|
</div>
|
|
<div class="grid gap-2">
|
|
<label for="stock">Stok</label>
|
|
<Input
|
|
id="stock"
|
|
v-model="stock"
|
|
type="number"
|
|
v-bind="stockAttrs"
|
|
:disabled="isLoading"
|
|
:class="{ 'border-red-500': errors.stock }"
|
|
/>
|
|
<span v-if="errors.stock" class="text-sm text-red-500">
|
|
{{ errors.stock }}
|
|
</span>
|
|
</div>
|
|
<div class="my-2 flex justify-end gap-2 py-2">
|
|
<Button variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
|
|
<Button type="submit" class="w-[120px]">
|
|
<Loader2 v-if="isLoading" class="mr-2 h-4 w-4 animate-spin" />
|
|
Simpan
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</template>
|