refactor(glob:form): update form handling and type definitions
- Migrate from Form component to vee-validate useForm - Update combobox component to support number values - Modify base model ID type for mock data - Improve type safety in treatment report schema - Add proper form submission handling
This commit is contained in:
@@ -27,7 +27,7 @@ const { class: containerClass, labelClass, colSpan = 1, doctors = [] } = props
|
||||
|
||||
const opts = computed(() => {
|
||||
return doctors.map((doc) => ({
|
||||
value: doc.id.toString(),
|
||||
value: doc.id,
|
||||
label: parseName(doc.employee.person as Person),
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { z } from 'zod'
|
||||
import { TreatmentReportSchema } from '~/schemas/treatment-report.schema'
|
||||
|
||||
// schema
|
||||
import { type TreatmentReportFormData, TreatmentReportSchema } from '~/schemas/treatment-report.schema'
|
||||
|
||||
// type
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
|
||||
@@ -16,18 +19,50 @@ import { SelectDoctor } from '~/components/app/doctor/fields'
|
||||
// Helpers
|
||||
|
||||
// #region Props & Emits
|
||||
interface FormData extends TreatmentReportFormData {
|
||||
hiddenNyc: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
isLoading: boolean
|
||||
mode?: 'create' | 'update' | 'view'
|
||||
initialValues?: any
|
||||
initialValues?: Partial<FormData>
|
||||
|
||||
// form related
|
||||
doctors: Doctor[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const { mode = 'create' } = props
|
||||
const emit = defineEmits<{
|
||||
(e: 'submit', payload: FormData): void
|
||||
}>()
|
||||
const { isLoading, mode = 'create' } = props
|
||||
const isReadonly = computed(() => {
|
||||
if (isLoading) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (mode === 'view') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
const formSchema = toTypedSchema(TreatmentReportSchema)
|
||||
|
||||
const { handleSubmit, values, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'treatmentReportForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues ? props.initialValues : {},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
@@ -40,28 +75,12 @@ const { mode = 'create' } = props
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
const onSubmit = handleSubmit((formValues: FormData) => emit('submit', formValues))
|
||||
// #endregion
|
||||
const formSchema = toTypedSchema(TreatmentReportSchema)
|
||||
const formRef = ref()
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
:initial-values="initialValues ? initialValues : {}"
|
||||
validation-mode="onSubmit"
|
||||
>
|
||||
<form @submit="onSubmit">
|
||||
<Fragment
|
||||
v-slot="{ section }"
|
||||
title="Tim Pelaksana Tindakan"
|
||||
@@ -73,13 +92,14 @@ defineExpose({
|
||||
:cell-flex="false"
|
||||
>
|
||||
<SelectDoctor
|
||||
:doctors="doctors"
|
||||
fieldName="dpjp"
|
||||
fieldName="operatorTeam.dpjpId"
|
||||
label="Dokter Pemeriksa"
|
||||
placeholder="Pilih dokter"
|
||||
:doctors="doctors"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="operatorId"
|
||||
field-name="operatorTeam.operatorId"
|
||||
label="Operator"
|
||||
placeholder="Masukkan operator"
|
||||
/>
|
||||
@@ -160,5 +180,14 @@ defineExpose({
|
||||
/>
|
||||
</DE.Block>
|
||||
</Fragment>
|
||||
</Form>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
class="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
|
||||
:disabled="isLoading"
|
||||
>
|
||||
{{ isLoading ? 'Menyimpan...' : 'Simpan' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { genDoctor, type Doctor } from '~/models/doctor'
|
||||
|
||||
// components
|
||||
import AppTreatmentReportEntry from '~/components/app/treatment-report/entry-form.vue'
|
||||
import type { TreatmentReportFormData } from '~/schemas/treatment-report.schema'
|
||||
|
||||
const doctors = ref<Doctor[]>([])
|
||||
|
||||
@@ -17,5 +18,12 @@ const doctors = ref<Doctor[]>([])
|
||||
<AppTreatmentReportEntry
|
||||
:isLoading="false"
|
||||
:doctors="doctors"
|
||||
:initialValues="
|
||||
{
|
||||
operatorTeam: {
|
||||
// dpjpId: -1,
|
||||
},
|
||||
} as TreatmentReportFormData
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { type Item } from './index'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string
|
||||
modelValue?: string
|
||||
modelValue?: string | number
|
||||
items: Item[]
|
||||
placeholder?: string
|
||||
searchPlaceholder?: string
|
||||
@@ -15,8 +15,8 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
'update:searchText': [value: string]
|
||||
'update:modelValue': [value: string | number]
|
||||
'update:searchText': [value: string | number]
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface Item {
|
||||
value: string
|
||||
value: string | number
|
||||
label: string
|
||||
code?: string
|
||||
priority?: number
|
||||
@@ -7,12 +7,12 @@ export interface Item {
|
||||
|
||||
export function recStrToItem(input: Record<string, string>): Item[] {
|
||||
const items: Item[] = []
|
||||
let idx = 0;
|
||||
let idx = 0
|
||||
for (const key in input) {
|
||||
if (input.hasOwnProperty(key)) {
|
||||
items.push({
|
||||
value: key || ('unknown-' + idx),
|
||||
label: input[key] || ('unknown-' + idx),
|
||||
value: key || 'unknown-' + idx,
|
||||
label: input[key] || 'unknown-' + idx,
|
||||
})
|
||||
}
|
||||
idx++
|
||||
|
||||
+3
-2
@@ -1,4 +1,3 @@
|
||||
|
||||
export interface Base {
|
||||
id: number
|
||||
createdAt: string | null
|
||||
@@ -20,7 +19,9 @@ export interface TreeItem {
|
||||
|
||||
export function genBase(): Base {
|
||||
return {
|
||||
id: 0,
|
||||
// -1 buat mock data
|
||||
// backend harusnya non-negative/ > 0 (untuk auto increment constraint) jadi harusnya aman ya
|
||||
id: -1,
|
||||
createdAt: '',
|
||||
updatedAt: '',
|
||||
}
|
||||
|
||||
@@ -4,10 +4,18 @@ const isoDateTime = z.string().min(1, 'Tanggal / waktu wajib diisi')
|
||||
const positiveInt = z.number().int().nonnegative()
|
||||
|
||||
const OperatorTeamSchema = z.object({
|
||||
dpjpId: z.number().int(),
|
||||
operatorId: z.number().int(),
|
||||
assistantOperatorId: z.number().int().optional().nullable(),
|
||||
instrumentNurseId: z.number().int().optional().nullable(),
|
||||
dpjpId: z.coerce
|
||||
.number({
|
||||
invalid_type_error: 'Dokter Pemeriksa wajib diisi',
|
||||
})
|
||||
.int(),
|
||||
operatorId: z.coerce
|
||||
.number({
|
||||
invalid_type_error: 'Operator wajib diisi',
|
||||
})
|
||||
.int(),
|
||||
assistantOperatorId: z.coerce.number().int().optional().nullable(),
|
||||
instrumentNurseId: z.coerce.number().int().optional().nullable(),
|
||||
|
||||
surgeryDate: isoDateTime,
|
||||
actionDiagnosis: z.string().min(1),
|
||||
|
||||
Reference in New Issue
Block a user