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(() => {
|
const opts = computed(() => {
|
||||||
return doctors.map((doc) => ({
|
return doctors.map((doc) => ({
|
||||||
value: doc.id.toString(),
|
value: doc.id,
|
||||||
label: parseName(doc.employee.person as Person),
|
label: parseName(doc.employee.person as Person),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useForm } from 'vee-validate'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
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
|
// type
|
||||||
import type { Doctor } from '~/models/doctor'
|
import type { Doctor } from '~/models/doctor'
|
||||||
|
|
||||||
@@ -16,18 +19,50 @@ import { SelectDoctor } from '~/components/app/doctor/fields'
|
|||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
// #region Props & Emits
|
// #region Props & Emits
|
||||||
|
interface FormData extends TreatmentReportFormData {
|
||||||
|
hiddenNyc: number
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
mode?: 'create' | 'update' | 'view'
|
mode?: 'create' | 'update' | 'view'
|
||||||
initialValues?: any
|
initialValues?: Partial<FormData>
|
||||||
|
|
||||||
// form related
|
// form related
|
||||||
doctors: Doctor[]
|
doctors: Doctor[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
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
|
// #endregion
|
||||||
|
|
||||||
// #region State & Computed
|
// #region State & Computed
|
||||||
@@ -40,28 +75,12 @@ const { mode = 'create' } = props
|
|||||||
// #endregion region
|
// #endregion region
|
||||||
|
|
||||||
// #region Utilities & event handlers
|
// #region Utilities & event handlers
|
||||||
|
const onSubmit = handleSubmit((formValues: FormData) => emit('submit', formValues))
|
||||||
// #endregion
|
// #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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Form
|
<form @submit="onSubmit">
|
||||||
ref="formRef"
|
|
||||||
as=""
|
|
||||||
keep-values
|
|
||||||
:validation-schema="formSchema"
|
|
||||||
:validate-on-mount="false"
|
|
||||||
:initial-values="initialValues ? initialValues : {}"
|
|
||||||
validation-mode="onSubmit"
|
|
||||||
>
|
|
||||||
<Fragment
|
<Fragment
|
||||||
v-slot="{ section }"
|
v-slot="{ section }"
|
||||||
title="Tim Pelaksana Tindakan"
|
title="Tim Pelaksana Tindakan"
|
||||||
@@ -73,13 +92,14 @@ defineExpose({
|
|||||||
:cell-flex="false"
|
:cell-flex="false"
|
||||||
>
|
>
|
||||||
<SelectDoctor
|
<SelectDoctor
|
||||||
:doctors="doctors"
|
fieldName="operatorTeam.dpjpId"
|
||||||
fieldName="dpjp"
|
|
||||||
label="Dokter Pemeriksa"
|
label="Dokter Pemeriksa"
|
||||||
placeholder="Pilih dokter"
|
placeholder="Pilih dokter"
|
||||||
|
:doctors="doctors"
|
||||||
|
:is-disabled="isReadonly"
|
||||||
/>
|
/>
|
||||||
<InputBase
|
<InputBase
|
||||||
field-name="operatorId"
|
field-name="operatorTeam.operatorId"
|
||||||
label="Operator"
|
label="Operator"
|
||||||
placeholder="Masukkan operator"
|
placeholder="Masukkan operator"
|
||||||
/>
|
/>
|
||||||
@@ -160,5 +180,14 @@ defineExpose({
|
|||||||
/>
|
/>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</Fragment>
|
</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>
|
</template>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { genDoctor, type Doctor } from '~/models/doctor'
|
|||||||
|
|
||||||
// components
|
// components
|
||||||
import AppTreatmentReportEntry from '~/components/app/treatment-report/entry-form.vue'
|
import AppTreatmentReportEntry from '~/components/app/treatment-report/entry-form.vue'
|
||||||
|
import type { TreatmentReportFormData } from '~/schemas/treatment-report.schema'
|
||||||
|
|
||||||
const doctors = ref<Doctor[]>([])
|
const doctors = ref<Doctor[]>([])
|
||||||
|
|
||||||
@@ -17,5 +18,12 @@ const doctors = ref<Doctor[]>([])
|
|||||||
<AppTreatmentReportEntry
|
<AppTreatmentReportEntry
|
||||||
:isLoading="false"
|
:isLoading="false"
|
||||||
:doctors="doctors"
|
:doctors="doctors"
|
||||||
|
:initialValues="
|
||||||
|
{
|
||||||
|
operatorTeam: {
|
||||||
|
// dpjpId: -1,
|
||||||
|
},
|
||||||
|
} as TreatmentReportFormData
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { type Item } from './index'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id?: string
|
id?: string
|
||||||
modelValue?: string
|
modelValue?: string | number
|
||||||
items: Item[]
|
items: Item[]
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
searchPlaceholder?: string
|
searchPlaceholder?: string
|
||||||
@@ -15,8 +15,8 @@ const props = defineProps<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:modelValue': [value: string]
|
'update:modelValue': [value: string | number]
|
||||||
'update:searchText': [value: string]
|
'update:searchText': [value: string | number]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export interface Item {
|
export interface Item {
|
||||||
value: string
|
value: string | number
|
||||||
label: string
|
label: string
|
||||||
code?: string
|
code?: string
|
||||||
priority?: number
|
priority?: number
|
||||||
@@ -7,12 +7,12 @@ export interface Item {
|
|||||||
|
|
||||||
export function recStrToItem(input: Record<string, string>): Item[] {
|
export function recStrToItem(input: Record<string, string>): Item[] {
|
||||||
const items: Item[] = []
|
const items: Item[] = []
|
||||||
let idx = 0;
|
let idx = 0
|
||||||
for (const key in input) {
|
for (const key in input) {
|
||||||
if (input.hasOwnProperty(key)) {
|
if (input.hasOwnProperty(key)) {
|
||||||
items.push({
|
items.push({
|
||||||
value: key || ('unknown-' + idx),
|
value: key || 'unknown-' + idx,
|
||||||
label: input[key] || ('unknown-' + idx),
|
label: input[key] || 'unknown-' + idx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export interface Base {
|
export interface Base {
|
||||||
id: number
|
id: number
|
||||||
createdAt: string | null
|
createdAt: string | null
|
||||||
@@ -20,7 +19,9 @@ export interface TreeItem {
|
|||||||
|
|
||||||
export function genBase(): Base {
|
export function genBase(): Base {
|
||||||
return {
|
return {
|
||||||
id: 0,
|
// -1 buat mock data
|
||||||
|
// backend harusnya non-negative/ > 0 (untuk auto increment constraint) jadi harusnya aman ya
|
||||||
|
id: -1,
|
||||||
createdAt: '',
|
createdAt: '',
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,18 @@ const isoDateTime = z.string().min(1, 'Tanggal / waktu wajib diisi')
|
|||||||
const positiveInt = z.number().int().nonnegative()
|
const positiveInt = z.number().int().nonnegative()
|
||||||
|
|
||||||
const OperatorTeamSchema = z.object({
|
const OperatorTeamSchema = z.object({
|
||||||
dpjpId: z.number().int(),
|
dpjpId: z.coerce
|
||||||
operatorId: z.number().int(),
|
.number({
|
||||||
assistantOperatorId: z.number().int().optional().nullable(),
|
invalid_type_error: 'Dokter Pemeriksa wajib diisi',
|
||||||
instrumentNurseId: z.number().int().optional().nullable(),
|
})
|
||||||
|
.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,
|
surgeryDate: isoDateTime,
|
||||||
actionDiagnosis: z.string().min(1),
|
actionDiagnosis: z.string().min(1),
|
||||||
|
|||||||
Reference in New Issue
Block a user