Merge pull request #194 from dikstub-rssa/feat/page-cleaning
Feat/page cleaning
This commit is contained in:
@@ -1,43 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { Button } from '~/components/pub/ui/button'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
import Select from '~/components/pub/ui/select/Select.vue'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import * as CB from '~/components/pub/my-ui/combobox'
|
||||
|
||||
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
|
||||
import TreeSelect from '~/components/pub/my-ui/select-tree/tree-select.vue'
|
||||
import FileUpload from '~/components/pub/my-ui/form/file-field.vue'
|
||||
|
||||
// Types
|
||||
import { IntegrationEncounterSchema, type IntegrationEncounterFormData } from '~/schemas/integration-encounter.schema'
|
||||
import type { PatientEntity } from '~/models/patient'
|
||||
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
||||
|
||||
// Helpers
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
|
||||
// References
|
||||
import { paymentMethodCodes } from '~/const/key-val/common'
|
||||
|
||||
// App things
|
||||
import { genEncounter, type Encounter } from '~/models/encounter'
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
isSepValid?: boolean
|
||||
isCheckingSep?: boolean
|
||||
doctor?: any[]
|
||||
subSpecialist?: any[]
|
||||
specialists?: TreeItem[]
|
||||
payments: any[]
|
||||
doctorItems?: CB.Item[]
|
||||
selectedDoctor: Doctor
|
||||
// subSpecialist?: any[]
|
||||
// specialists?: TreeItem[]
|
||||
// paymentMethods: PaymentMethodCode[]
|
||||
participantGroups?: any[]
|
||||
seps: any[]
|
||||
patient?: PatientEntity | null | undefined
|
||||
objects?: any
|
||||
// objects?: any
|
||||
}>()
|
||||
|
||||
// Model
|
||||
const model = defineModel<Encounter>()
|
||||
model.value = genEncounter()
|
||||
|
||||
// Common preparation
|
||||
const defaultCBItems = [{ label: 'Pilih', value: '' }];
|
||||
const paymentMethodItems = CB.recStrToItem(paymentMethodCodes)
|
||||
|
||||
// Emit preparation
|
||||
const emit = defineEmits<{
|
||||
(e: 'onSelectDoctor', code: string): void
|
||||
(e: 'event', menu: string, value?: any): void
|
||||
(e: 'fetch', value?: any): void
|
||||
}>()
|
||||
@@ -48,10 +62,10 @@ const { handleSubmit, errors, defineField, meta } = useForm<IntegrationEncounter
|
||||
})
|
||||
|
||||
// Bind fields and extract attrs
|
||||
const [doctorId, doctorIdAttrs] = defineField('doctorId')
|
||||
const [subSpecialistId, subSpecialistIdAttrs] = defineField('subSpecialistId')
|
||||
const [doctorCode, doctorCodeAttrs] = defineField('doctor_code')
|
||||
const [unitCode, unitCodeAttrs] = defineField('unit_code')
|
||||
const [registerDate, registerDateAttrs] = defineField('registerDate')
|
||||
const [paymentType, paymentTypeAttrs] = defineField('paymentType')
|
||||
const [paymentMethodCode, paymentMethodCodeAttrs] = defineField('paymentMethod_code')
|
||||
const [patientCategory, patientCategoryAttrs] = defineField('patientCategory')
|
||||
const [cardNumber, cardNumberAttrs] = defineField('cardNumber')
|
||||
const [sepType, sepTypeAttrs] = defineField('sepType')
|
||||
@@ -68,30 +82,50 @@ const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||
const isSepValid = computed(() => props.isSepValid || false)
|
||||
const isCheckingSep = computed(() => props.isCheckingSep || false)
|
||||
|
||||
const doctorOpts = computed(() => {
|
||||
// Add default option
|
||||
const defaultOption = [{ label: 'Pilih', value: '' }]
|
||||
// Add doctors from props
|
||||
const doctors = props.doctor || []
|
||||
return [...defaultOption, ...doctors]
|
||||
})
|
||||
// Unit, specialist, subspecialist
|
||||
const unitFullName = ref('')
|
||||
watch(() => props.selectedDoctor, (doctor) => {
|
||||
unitFullName.value = doctor.subspecialist?.name ??
|
||||
doctor.specialist?.name ??
|
||||
doctor.unit?.name ??
|
||||
'tidak diketahui'
|
||||
model.value!.unit_code = doctor.unit_code || ''
|
||||
model.value!.specialist_code = doctor.specialist_code || ''
|
||||
model.value!.subspecialist_code = doctor.subspecialist_code || ''
|
||||
},
|
||||
)
|
||||
// const doctorOpts = computed(() => {
|
||||
// const defaultOption = [{ label: 'Pilih', value: '' }]
|
||||
// const doctors = props.doctors || []
|
||||
// return [...defaultOption, ...doctors]
|
||||
// })
|
||||
// watch(doctorCode, (newValue) => {
|
||||
// // doctor.value = props.doctors?.find(doc => doc.code === newValue)
|
||||
// unitFullName.value = doctor.value?.subspecialist?.name ??
|
||||
// doctor.value?.specialist?.name ??
|
||||
// doctor.value?.unit?.name ??
|
||||
// 'tidak diketahui'
|
||||
// model.value!.responsible_doctor_code = doctor.value?.code
|
||||
// // const unitName = selectedDoctor?.specialist?.name || ''
|
||||
// // emit('event', 'unit-changed', unitName)
|
||||
// })
|
||||
|
||||
const isJKNPayment = computed(() => paymentType.value === 'jkn')
|
||||
const isJKNPayment = computed(() => paymentMethodCode.value === 'jkn')
|
||||
|
||||
async function onFetchChildren(parentId: string): Promise<void> {
|
||||
console.log('onFetchChildren', parentId)
|
||||
}
|
||||
// async function onFetchChildren(parentId: string): Promise<void> {
|
||||
// console.log('onFetchChildren', parentId)
|
||||
// }
|
||||
|
||||
// Watch specialist/subspecialist selection to fetch doctors
|
||||
watch(subSpecialistId, async (newValue) => {
|
||||
if (newValue) {
|
||||
console.log('SubSpecialist changed:', newValue)
|
||||
// Reset doctor selection
|
||||
doctorId.value = ''
|
||||
// Emit fetch event to parent
|
||||
emit('fetch', { subSpecialistId: newValue })
|
||||
}
|
||||
})
|
||||
// watch(subSpecialistCode, async (newValue) => {
|
||||
// if (newValue) {
|
||||
// console.log('SubSpecialist changed:', newValue)
|
||||
// // Reset doctor selection
|
||||
// doctorCode.value = ''
|
||||
// // Emit fetch event to parent
|
||||
// emit('fetch', { subSpecialistCode: newValue })
|
||||
// }
|
||||
// })
|
||||
|
||||
// Debounced SEP number watcher: emit change only after user stops typing
|
||||
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||
@@ -100,25 +134,25 @@ watch(debouncedSepNumber, (newValue) => {
|
||||
})
|
||||
|
||||
// Sync props to form fields
|
||||
watch(
|
||||
() => props.objects,
|
||||
(objects) => {
|
||||
if (objects && Object.keys(objects).length > 0) {
|
||||
patientName.value = objects?.patientName || ''
|
||||
nationalIdentity.value = objects?.nationalIdentity || ''
|
||||
medicalRecordNumber.value = objects?.medicalRecordNumber || ''
|
||||
doctorId.value = objects?.doctorId || ''
|
||||
subSpecialistId.value = objects?.subSpecialistId || ''
|
||||
registerDate.value = objects?.registerDate || ''
|
||||
paymentType.value = objects?.paymentType || ''
|
||||
patientCategory.value = objects?.patientCategory || ''
|
||||
cardNumber.value = objects?.cardNumber || ''
|
||||
sepType.value = objects?.sepType || ''
|
||||
sepNumber.value = objects?.sepNumber || ''
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
// watch(
|
||||
// () => props.objects,
|
||||
// (objects) => {
|
||||
// if (objects && Object.keys(objects).length > 0) {
|
||||
// patientName.value = objects?.patientName || ''
|
||||
// nationalIdentity.value = objects?.nationalIdentity || ''
|
||||
// medicalRecordNumber.value = objects?.medicalRecordNumber || ''
|
||||
// doctorCode.value = objects?.doctorCode || ''
|
||||
// subSpecialistCode.value = objects?.subSpecialistCode || ''
|
||||
// registerDate.value = objects?.registerDate || ''
|
||||
// paymentMethodCode.value = objects?.paymentMethodCode || ''
|
||||
// patientCategory.value = objects?.patientCategory || ''
|
||||
// cardNumber.value = objects?.cardNumber || ''
|
||||
// sepType.value = objects?.sepType || ''
|
||||
// sepNumber.value = objects?.sepNumber || ''
|
||||
// }
|
||||
// },
|
||||
// { deep: true, immediate: true },
|
||||
// )
|
||||
|
||||
watch(
|
||||
() => props.patient,
|
||||
@@ -136,11 +170,11 @@ watch(
|
||||
function onAddSep() {
|
||||
const formValues = {
|
||||
patientId: patientId.value || '',
|
||||
doctorCode: doctorId.value,
|
||||
subSpecialistCode: subSpecialistId.value,
|
||||
doctorCode: doctorCode.value,
|
||||
// subSpecialistCode: subSpecialistCode.value,
|
||||
registerDate: registerDate.value,
|
||||
cardNumber: cardNumber.value,
|
||||
paymentType: paymentType.value,
|
||||
paymentMethodCode: paymentMethodCode.value,
|
||||
sepType: sepType.value
|
||||
}
|
||||
emit('event', 'add-sep', formValues)
|
||||
@@ -158,10 +192,10 @@ const formRef = ref<HTMLFormElement | null>(null)
|
||||
function submitForm() {
|
||||
console.log('🔵 submitForm called, formRef:', formRef.value)
|
||||
console.log('🔵 Form values:', {
|
||||
doctorId: doctorId.value,
|
||||
subSpecialistId: subSpecialistId.value,
|
||||
doctorCode: doctorCode.value,
|
||||
// subSpecialistCode: subSpecialistCode.value,
|
||||
registerDate: registerDate.value,
|
||||
paymentType: paymentType.value,
|
||||
paymentMethodCode: paymentMethodCode.value,
|
||||
})
|
||||
console.log('🔵 Form errors:', errors.value)
|
||||
console.log('🔵 Form meta:', meta.value)
|
||||
@@ -231,149 +265,151 @@ defineExpose({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Block
|
||||
<DE.Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Nama Pasien</Label>
|
||||
<Field :errMessage="errors.patientName">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">Nama Pasien</DE.Label>
|
||||
<DE.Field :errMessage="errors.patientName">
|
||||
<Input
|
||||
id="patientName"
|
||||
v-model="patientName"
|
||||
v-bind="patientNameAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">NIK</Label>
|
||||
<Field :errMessage="errors.nationalIdentity">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">NIK</DE.Label>
|
||||
<DE.Field :errMessage="errors.nationalIdentity">
|
||||
<Input
|
||||
id="nationalIdentity"
|
||||
v-model="nationalIdentity"
|
||||
v-bind="nationalIdentityAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">No. RM</Label>
|
||||
<Field :errMessage="errors.medicalRecordNumber">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">No. RM</DE.Label>
|
||||
<DE.Field :errMessage="errors.medicalRecordNumber">
|
||||
<Input
|
||||
id="medicalRecordNumber"
|
||||
v-model="medicalRecordNumber"
|
||||
v-bind="medicalRecordNumberAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- Data Kunjungan -->
|
||||
<h3 class="text-lg font-semibold">Data Kunjungan</h3>
|
||||
|
||||
<Block
|
||||
<DE.Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Dokter
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.doctorId">
|
||||
<Combobox
|
||||
id="doctorId"
|
||||
v-model="doctorId"
|
||||
v-bind="doctorIdAttrs"
|
||||
:items="doctorOpts"
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.doctor_code">
|
||||
<CB.Combobox
|
||||
id="doctorCode"
|
||||
v-model="doctorCode"
|
||||
v-bind="doctorCodeAttrs"
|
||||
:items="[...defaultCBItems, ...doctorItems]"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Dokter"
|
||||
search-placeholder="Cari Dokter"
|
||||
empty-message="Dokter tidak ditemukan"
|
||||
@update:model-value="(value) => emit('onSelectDoctor', value)"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Spesialis / Subspesialis
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.subSpecialistId">
|
||||
<TreeSelect
|
||||
id="subSpecialistId"
|
||||
v-model="subSpecialistId"
|
||||
v-bind="subSpecialistIdAttrs"
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.unit_code">
|
||||
<Input :value="unitFullName"/>
|
||||
<!-- <TreeSelect
|
||||
id="subSpecialistCode"
|
||||
v-model="subSpecialistCode"
|
||||
v-bind="subSpecialistCodeAttrs"
|
||||
:data="specialists || []"
|
||||
:on-fetch-children="onFetchChildren"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
/> -->
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
|
||||
<Block
|
||||
<DE.Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Tanggal Daftar
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.registerDate">
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.registerDate">
|
||||
<DatepickerSingle
|
||||
id="registerDate"
|
||||
v-model="registerDate"
|
||||
v-bind="registerDateAttrs"
|
||||
placeholder="Pilih tanggal"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Jenis Pembayaran
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.paymentType">
|
||||
<Select
|
||||
id="paymentType"
|
||||
v-model="paymentType"
|
||||
v-bind="paymentTypeAttrs"
|
||||
:items="payments"
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.paymentMethod_code">
|
||||
<CB.Combobox
|
||||
id="paymentMethodCode"
|
||||
v-model="paymentMethodCode"
|
||||
v-bind="paymentMethodCodeAttrs"
|
||||
:items="paymentMethodItems"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Jenis Pembayaran"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
|
||||
<!-- BPJS Fields (conditional) -->
|
||||
<template v-if="isJKNPayment">
|
||||
<Block
|
||||
<DE.Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Kelompok Peserta
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.patientCategory">
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.patientCategory">
|
||||
<Select
|
||||
id="patientCategory"
|
||||
v-model="patientCategory"
|
||||
@@ -382,15 +418,15 @@ defineExpose({
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Kelompok Peserta"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
No. Kartu BPJS
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.cardNumber">
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.cardNumber">
|
||||
<Input
|
||||
id="cardNumber"
|
||||
v-model="cardNumber"
|
||||
@@ -398,15 +434,15 @@ defineExpose({
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan nomor kartu BPJS"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
Jenis SEP
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.sepType">
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.sepType">
|
||||
<Select
|
||||
id="sepType"
|
||||
v-model="sepType"
|
||||
@@ -415,22 +451,22 @@ defineExpose({
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Jenis SEP"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
|
||||
<Block
|
||||
<DE.Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
<DE.Cell>
|
||||
<DE.Label height="compact">
|
||||
No. SEP
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.sepNumber">
|
||||
</DE.Label>
|
||||
<DE.Field :errMessage="errors.sepNumber">
|
||||
<div class="flex gap-2">
|
||||
<Input
|
||||
id="sepNumber"
|
||||
@@ -474,8 +510,8 @@ defineExpose({
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
</Cell>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
|
||||
<FileUpload
|
||||
field-name="sepFile"
|
||||
@@ -492,7 +528,7 @@ defineExpose({
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
</Block>
|
||||
</DE.Block>
|
||||
</template>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
||||
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
||||
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
||||
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
||||
import { Button } from '~/components/pub/ui/button'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
import Select from '~/components/pub/ui/select/Select.vue'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
|
||||
import TreeSelect from '~/components/pub/my-ui/select-tree/tree-select.vue'
|
||||
import FileUpload from '~/components/pub/my-ui/form/file-field.vue'
|
||||
|
||||
// Types
|
||||
import { IntegrationEncounterSchema, type IntegrationEncounterFormData } from '~/schemas/integration-encounter.schema'
|
||||
import type { PatientEntity } from '~/models/patient'
|
||||
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
||||
|
||||
// Helpers
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
isSepValid?: boolean
|
||||
isCheckingSep?: boolean
|
||||
doctor?: any[]
|
||||
subSpecialist?: any[]
|
||||
specialists?: TreeItem[]
|
||||
payments: any[]
|
||||
participantGroups?: any[]
|
||||
seps: any[]
|
||||
patient?: PatientEntity | null | undefined
|
||||
objects?: any
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'event', menu: string, value?: any): void
|
||||
(e: 'fetch', value?: any): void
|
||||
}>()
|
||||
|
||||
// Validation schema
|
||||
const { handleSubmit, errors, defineField, meta } = useForm<IntegrationEncounterFormData>({
|
||||
validationSchema: toTypedSchema(IntegrationEncounterSchema),
|
||||
})
|
||||
|
||||
// Bind fields and extract attrs
|
||||
const [doctorId, doctorIdAttrs] = defineField('doctorId')
|
||||
const [subSpecialistId, subSpecialistIdAttrs] = defineField('subSpecialistId')
|
||||
const [registerDate, registerDateAttrs] = defineField('registerDate')
|
||||
const [paymentType, paymentTypeAttrs] = defineField('paymentType')
|
||||
const [patientCategory, patientCategoryAttrs] = defineField('patientCategory')
|
||||
const [cardNumber, cardNumberAttrs] = defineField('cardNumber')
|
||||
const [sepType, sepTypeAttrs] = defineField('sepType')
|
||||
const [sepNumber, sepNumberAttrs] = defineField('sepNumber')
|
||||
const [patientName, patientNameAttrs] = defineField('patientName')
|
||||
const [nationalIdentity, nationalIdentityAttrs] = defineField('nationalIdentity')
|
||||
const [medicalRecordNumber, medicalRecordNumberAttrs] = defineField('medicalRecordNumber')
|
||||
const patientId = ref('')
|
||||
|
||||
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||
|
||||
// SEP validation state from props
|
||||
const isSepValid = computed(() => props.isSepValid || false)
|
||||
const isCheckingSep = computed(() => props.isCheckingSep || false)
|
||||
|
||||
const doctorOpts = computed(() => {
|
||||
// Add default option
|
||||
const defaultOption = [{ label: 'Pilih', value: '' }]
|
||||
// Add doctors from props
|
||||
const doctors = props.doctor || []
|
||||
return [...defaultOption, ...doctors]
|
||||
})
|
||||
|
||||
const isJKNPayment = computed(() => paymentType.value === 'jkn')
|
||||
|
||||
async function onFetchChildren(parentId: string): Promise<void> {
|
||||
console.log('onFetchChildren', parentId)
|
||||
}
|
||||
|
||||
// Watch specialist/subspecialist selection to fetch doctors
|
||||
watch(subSpecialistId, async (newValue) => {
|
||||
if (newValue) {
|
||||
console.log('SubSpecialist changed:', newValue)
|
||||
// Reset doctor selection
|
||||
doctorId.value = ''
|
||||
// Emit fetch event to parent
|
||||
emit('fetch', { subSpecialistId: newValue })
|
||||
}
|
||||
})
|
||||
|
||||
// Debounced SEP number watcher: emit change only after user stops typing
|
||||
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||
watch(debouncedSepNumber, (newValue) => {
|
||||
emit('event', 'sep-number-changed', newValue)
|
||||
})
|
||||
|
||||
// Sync props to form fields
|
||||
watch(
|
||||
() => props.objects,
|
||||
(objects) => {
|
||||
if (objects && Object.keys(objects).length > 0) {
|
||||
patientName.value = objects?.patientName || ''
|
||||
nationalIdentity.value = objects?.nationalIdentity || ''
|
||||
medicalRecordNumber.value = objects?.medicalRecordNumber || ''
|
||||
doctorId.value = objects?.doctorId || ''
|
||||
subSpecialistId.value = objects?.subSpecialistId || ''
|
||||
registerDate.value = objects?.registerDate || ''
|
||||
paymentType.value = objects?.paymentType || ''
|
||||
patientCategory.value = objects?.patientCategory || ''
|
||||
cardNumber.value = objects?.cardNumber || ''
|
||||
sepType.value = objects?.sepType || ''
|
||||
sepNumber.value = objects?.sepNumber || ''
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.patient,
|
||||
(patient) => {
|
||||
if (patient && Object.keys(patient).length > 0) {
|
||||
patientId.value = patient?.id ? String(patient.id) : ''
|
||||
patientName.value = patient?.person?.name || ''
|
||||
nationalIdentity.value = patient?.person?.residentIdentityNumber || ''
|
||||
medicalRecordNumber.value = patient?.number || ''
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
function onAddSep() {
|
||||
const formValues = {
|
||||
patientId: patientId.value || '',
|
||||
doctorCode: doctorId.value,
|
||||
subSpecialistCode: subSpecialistId.value,
|
||||
registerDate: registerDate.value,
|
||||
cardNumber: cardNumber.value,
|
||||
paymentType: paymentType.value,
|
||||
sepType: sepType.value
|
||||
}
|
||||
emit('event', 'add-sep', formValues)
|
||||
}
|
||||
|
||||
// Submit handler
|
||||
const onSubmit = handleSubmit((values) => {
|
||||
console.log('✅ Validated form values:', JSON.stringify(values, null, 2))
|
||||
emit('event', 'save', values)
|
||||
})
|
||||
|
||||
// Expose submit method for parent component
|
||||
const formRef = ref<HTMLFormElement | null>(null)
|
||||
|
||||
function submitForm() {
|
||||
console.log('🔵 submitForm called, formRef:', formRef.value)
|
||||
console.log('🔵 Form values:', {
|
||||
doctorId: doctorId.value,
|
||||
subSpecialistId: subSpecialistId.value,
|
||||
registerDate: registerDate.value,
|
||||
paymentType: paymentType.value,
|
||||
})
|
||||
console.log('🔵 Form errors:', errors.value)
|
||||
console.log('🔵 Form meta:', meta.value)
|
||||
|
||||
// Trigger form submit using native form submit
|
||||
// This will trigger validation and onSubmit handler
|
||||
if (formRef.value) {
|
||||
console.log('🔵 Calling formRef.value.requestSubmit()')
|
||||
formRef.value.requestSubmit()
|
||||
} else {
|
||||
console.warn('⚠️ formRef.value is null, cannot submit form')
|
||||
// Fallback: directly call onSubmit handler
|
||||
// Create a mock event object
|
||||
const mockEvent = {
|
||||
preventDefault: () => {},
|
||||
target: formRef.value || {},
|
||||
} as SubmitEvent
|
||||
|
||||
// Call onSubmit directly
|
||||
console.log('🔵 Calling onSubmit with mock event')
|
||||
onSubmit(mockEvent)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
submitForm,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mx-auto w-full">
|
||||
<form
|
||||
ref="formRef"
|
||||
@submit.prevent="onSubmit"
|
||||
class="grid gap-6 p-4"
|
||||
>
|
||||
<!-- Data Pasien -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="text-lg font-semibold">Data Pasien</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm">sudah pernah terdaftar sebagai pasien?</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="h-[40px] rounded-md border-orange-400 text-orange-400 hover:bg-green-50"
|
||||
@click="emit('event', 'search')"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-search"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
Cari Pasien
|
||||
</Button>
|
||||
<span class="text-sm">belum pernah terdaftar sebagai pasien?</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="h-[40px] rounded-md border-orange-400 text-orange-400 hover:bg-green-50"
|
||||
@click="emit('event', 'add')"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-plus"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
Tambah Pasien Baru
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Nama Pasien</Label>
|
||||
<Field :errMessage="errors.patientName">
|
||||
<Input
|
||||
id="patientName"
|
||||
v-model="patientName"
|
||||
v-bind="patientNameAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">NIK</Label>
|
||||
<Field :errMessage="errors.nationalIdentity">
|
||||
<Input
|
||||
id="nationalIdentity"
|
||||
v-model="nationalIdentity"
|
||||
v-bind="nationalIdentityAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">No. RM</Label>
|
||||
<Field :errMessage="errors.medicalRecordNumber">
|
||||
<Input
|
||||
id="medicalRecordNumber"
|
||||
v-model="medicalRecordNumber"
|
||||
v-bind="medicalRecordNumberAttrs"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- Data Kunjungan -->
|
||||
<h3 class="text-lg font-semibold">Data Kunjungan</h3>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Dokter
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.doctorId">
|
||||
<Combobox
|
||||
id="doctorId"
|
||||
v-model="doctorId"
|
||||
v-bind="doctorIdAttrs"
|
||||
:items="doctorOpts"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Dokter"
|
||||
search-placeholder="Cari Dokter"
|
||||
empty-message="Dokter tidak ditemukan"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Spesialis / Subspesialis
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.subSpecialistId">
|
||||
<TreeSelect
|
||||
id="subSpecialistId"
|
||||
v-model="subSpecialistId"
|
||||
v-bind="subSpecialistIdAttrs"
|
||||
:data="specialists || []"
|
||||
:on-fetch-children="onFetchChildren"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Tanggal Daftar
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.registerDate">
|
||||
<DatepickerSingle
|
||||
id="registerDate"
|
||||
v-model="registerDate"
|
||||
v-bind="registerDateAttrs"
|
||||
placeholder="Pilih tanggal"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Jenis Pembayaran
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.paymentType">
|
||||
<Select
|
||||
id="paymentType"
|
||||
v-model="paymentType"
|
||||
v-bind="paymentTypeAttrs"
|
||||
:items="payments"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Jenis Pembayaran"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<!-- BPJS Fields (conditional) -->
|
||||
<template v-if="isJKNPayment">
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Kelompok Peserta
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.patientCategory">
|
||||
<Select
|
||||
id="patientCategory"
|
||||
v-model="patientCategory"
|
||||
v-bind="patientCategoryAttrs"
|
||||
:items="participantGroups || []"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Kelompok Peserta"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
No. Kartu BPJS
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.cardNumber">
|
||||
<Input
|
||||
id="cardNumber"
|
||||
v-model="cardNumber"
|
||||
v-bind="cardNumberAttrs"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Masukkan nomor kartu BPJS"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Jenis SEP
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.sepType">
|
||||
<Select
|
||||
id="sepType"
|
||||
v-model="sepType"
|
||||
v-bind="sepTypeAttrs"
|
||||
:items="seps"
|
||||
:disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Jenis SEP"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
No. SEP
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.sepNumber">
|
||||
<div class="flex gap-2">
|
||||
<Input
|
||||
id="sepNumber"
|
||||
v-model="sepNumber"
|
||||
v-bind="sepNumberAttrs"
|
||||
placeholder="Tambah SEP terlebih dahulu"
|
||||
class="flex-1"
|
||||
:disabled="isLoading || isReadonly"
|
||||
/>
|
||||
<Button
|
||||
v-if="!isSepValid"
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="bg-primary"
|
||||
size="sm"
|
||||
:disabled="isCheckingSep || isLoading || isReadonly"
|
||||
@click="onAddSep"
|
||||
>
|
||||
<Icon
|
||||
v-if="isCheckingSep"
|
||||
name="i-lucide-loader-2"
|
||||
class="h-4 w-4 animate-spin"
|
||||
/>
|
||||
<Icon
|
||||
v-else
|
||||
name="i-lucide-plus"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-else
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="bg-green-500 text-white hover:bg-green-600"
|
||||
size="sm"
|
||||
disabled
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-check"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<FileUpload
|
||||
field-name="sepFile"
|
||||
label="Dokumen SEP"
|
||||
placeholder="Unggah dokumen SEP"
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
|
||||
<FileUpload
|
||||
field-name="sippFile"
|
||||
label="Dokumen SIPP"
|
||||
placeholder="Unggah dokumen SIPP"
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
</Block>
|
||||
</template>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
@@ -7,17 +7,37 @@ const props = defineProps<{
|
||||
data: Encounter
|
||||
}>()
|
||||
|
||||
let address = ref('')
|
||||
if (props.data.patient.person.addresses) {
|
||||
address.value = props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
||||
}
|
||||
const addressText = computed(() => {
|
||||
if (props.data.patient.person.addresses && props.data.patient.person.addresses.length > 0) {
|
||||
return props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
||||
}
|
||||
return '-'
|
||||
})
|
||||
|
||||
let dpjp = ref('')
|
||||
const paymentMethodText = computed(() => {
|
||||
const code = props.data.paymentMethod_code
|
||||
if (!code) return '-'
|
||||
|
||||
// Map payment method codes
|
||||
if (code === 'insurance') {
|
||||
return 'JKN'
|
||||
} else if (code === 'jkn') {
|
||||
return 'JKN'
|
||||
} else if (code === 'jkmm') {
|
||||
return 'JKMM'
|
||||
} else if (code === 'spm') {
|
||||
return 'SPM'
|
||||
} else if (code === 'pks') {
|
||||
return 'PKS'
|
||||
}
|
||||
})
|
||||
|
||||
let dpjpText = ref('')
|
||||
if (props.data.responsible_doctor) {
|
||||
const dp = props.data.responsible_doctor.employee.person
|
||||
dpjp.value = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
||||
dpjpText.value = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
||||
} else if (props.data.appointment_doctor) {
|
||||
dpjp.value = props.data.appointment_doctor.employee.person.name
|
||||
dpjpText.value = props.data.appointment_doctor.employee.person.name
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -31,7 +51,7 @@ if (props.data.responsible_doctor) {
|
||||
<div>
|
||||
<DE.Block mode="preview">
|
||||
<DE.Cell>
|
||||
<DE.Label class="font-semibold">No. RM</DE.Label>
|
||||
<DE.Label class="font-semibold">Tgl. Lahir</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
||||
@@ -48,7 +68,7 @@ if (props.data.responsible_doctor) {
|
||||
<DE.Label class="font-semibold">Alamat</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
<div v-html="address"></div>
|
||||
{{ addressText }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
@@ -70,7 +90,7 @@ if (props.data.responsible_doctor) {
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label position="dynamic" class="font-semibold">Klinik</DE.Label>
|
||||
<DE.Label position="dynamic" class="font-semibold">Diagnosa</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ data.unit?.name }}
|
||||
@@ -84,17 +104,21 @@ if (props.data.responsible_doctor) {
|
||||
<DE.Label position="dynamic" class="font-semibold">DPJP</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ dpjp }}
|
||||
{{ dpjpText }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label
|
||||
position="dynamic"
|
||||
class="!text-base font-semibold 2xl:!text-lg"
|
||||
>
|
||||
<DE.Label position="dynamic" class="font-semibold">Pembayaran</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ paymentMethodText }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label position="dynamic" class="!text-base !font-semibold 2xl:!text-lg">
|
||||
Billing
|
||||
</DE.Label>
|
||||
<DE.Colon class="pt-1" />
|
||||
<DE.Colon class="pt-1"/>
|
||||
<DE.Field class="text-base 2xl:text-lg">
|
||||
Rp. 000.000
|
||||
<!-- {{ data }} -->
|
||||
|
||||
@@ -8,8 +8,11 @@ import AppViewPatient from '~/components/app/patient/view-patient.vue'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
|
||||
// Handlers
|
||||
import { getDetail as getDoctorDetail } from '~/services/doctor.service'
|
||||
import { useEncounterEntry } from '~/handlers/encounter-entry.handler'
|
||||
import { genDoctor, type Doctor } from '~/models/doctor'
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
id: number
|
||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
||||
@@ -54,7 +57,33 @@ const {
|
||||
} = useEncounterEntry(props)
|
||||
|
||||
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||
const selectedDoctor = ref<Doctor>(genDoctor())
|
||||
|
||||
provide('rec_select_id', recSelectId)
|
||||
provide('table_data_loader', isLoading)
|
||||
|
||||
watch(debouncedSepNumber, async (newValue) => {
|
||||
await getValidateSepNumber(newValue)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => formObjects.value?.paymentType,
|
||||
(newValue) => {
|
||||
isSepValid.value = false
|
||||
if (newValue !== 'jkn') {
|
||||
sepNumber.value = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
await handleInit()
|
||||
if (props.id > 0) {
|
||||
await loadEncounterDetail()
|
||||
}
|
||||
})
|
||||
|
||||
///// Functions
|
||||
function handleSavePatient() {
|
||||
selectedPatientObject.value = null
|
||||
setTimeout(() => {
|
||||
@@ -100,29 +129,13 @@ async function handleEvent(menu: string, value?: any) {
|
||||
}
|
||||
}
|
||||
|
||||
provide('rec_select_id', recSelectId)
|
||||
provide('table_data_loader', isLoading)
|
||||
|
||||
watch(debouncedSepNumber, async (newValue) => {
|
||||
await getValidateSepNumber(newValue)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => formObjects.value?.paymentType,
|
||||
(newValue) => {
|
||||
isSepValid.value = false
|
||||
if (newValue !== 'jkn') {
|
||||
sepNumber.value = ''
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
await handleInit()
|
||||
if (props.id > 0) {
|
||||
await loadEncounterDetail()
|
||||
async function getDoctorInfo(value: string) {
|
||||
const resp = await getDoctorDetail(value, { includes: 'unit,specialist,subspecialist'})
|
||||
if (resp.success) {
|
||||
selectedDoctor.value = resp.body.data
|
||||
// console.log(selectedDoctor.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -144,9 +157,11 @@ onMounted(async () => {
|
||||
:seps="sepsList"
|
||||
:participant-groups="participantGroupsList"
|
||||
:specialists="specialistsTree"
|
||||
:doctor="doctorsList"
|
||||
:doctorItems="doctorsList"
|
||||
:selectedDoctor="selectedDoctor"
|
||||
:patient="selectedPatientObject"
|
||||
:objects="formObjects"
|
||||
@on-select-doctor="getDoctorInfo"
|
||||
@event="handleEvent"
|
||||
@fetch="handleFetch"
|
||||
/>
|
||||
|
||||
@@ -70,18 +70,18 @@ export function useEncounterEntry(props: {
|
||||
})
|
||||
|
||||
function getListPath(): string {
|
||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
|
||||
return '/rehab/encounter'
|
||||
}
|
||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'reg') {
|
||||
return '/outpatient/encounter'
|
||||
}
|
||||
if (props.classCode === 'emergency') {
|
||||
return '/emergency/encounter'
|
||||
}
|
||||
if (props.classCode === 'inpatient') {
|
||||
return '/inpatient/encounter'
|
||||
if (props.classCode === 'ambulatory') {
|
||||
return '/ambulatory/encounter'
|
||||
}
|
||||
// if (props.classCode === 'ambulatory' && props.subClassCode === 'reg') {
|
||||
// return '/outpatient/encounter'
|
||||
// }
|
||||
// if (props.classCode === 'emergency') {
|
||||
// return '/emergency/encounter'
|
||||
// }
|
||||
// if (props.classCode === 'inpatient') {
|
||||
// return '/inpatient/encounter'
|
||||
// }
|
||||
return '/encounter'
|
||||
}
|
||||
|
||||
@@ -257,11 +257,10 @@ export function useEncounterEntry(props: {
|
||||
|
||||
async function handleFetchDoctors(subSpecialistId: string | null = null) {
|
||||
try {
|
||||
const filterParams: any = { 'page-size': 100, includes: 'employee-Person' }
|
||||
const filterParams: any = { 'page-size': 100, includes: 'employee-Person,unit,specialist,subspecialist' }
|
||||
|
||||
if (!subSpecialistId) {
|
||||
const doctors = await getDoctorValueLabelList(filterParams, true)
|
||||
doctorsList.value = doctors
|
||||
doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -273,8 +272,7 @@ export function useEncounterEntry(props: {
|
||||
filterParams['specialist-id'] = subSpecialistId
|
||||
}
|
||||
|
||||
const doctors = await getDoctorValueLabelList(filterParams, true)
|
||||
doctorsList.value = doctors
|
||||
doctorsList.value = await getDoctorValueLabelList(filterParams, true)
|
||||
} catch (error) {
|
||||
console.error('Error fetching doctors:', error)
|
||||
doctorsList.value = []
|
||||
|
||||
@@ -55,23 +55,29 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
earlyNurseryAssessment: {
|
||||
id: 'early-nursery-assessment',
|
||||
title: 'Pengkajian Awal Keperawatan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
earlyMedicalAssessment: {
|
||||
id: 'early-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
rehabMedicalAssessment: {
|
||||
earlyMedicalRehabAssessment: {
|
||||
id: 'rehab-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis Rehabilitasi Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
functionAssessment: {
|
||||
id: 'function-assessment',
|
||||
title: 'Asesmen Fungsi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
afterId: 'rehab-medical-assessment',
|
||||
},
|
||||
@@ -102,16 +108,22 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
consent: {
|
||||
id: 'consent',
|
||||
generalConsent: {
|
||||
id: 'general-consent',
|
||||
title: 'General Consent',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
patientNote: {
|
||||
id: 'patient-note',
|
||||
patientAmbNote: {
|
||||
id: 'patient-amb-note',
|
||||
title: 'CPRJ',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
classCode: ['ambulatory', 'emergency'],
|
||||
unit: 'all',
|
||||
},
|
||||
patientDevNote: {
|
||||
id: 'patient-dev-note',
|
||||
title: 'CPP',
|
||||
classCode: ['inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
prescription: {
|
||||
@@ -120,38 +132,38 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
device: {
|
||||
deviceOrder: {
|
||||
id: 'device-order',
|
||||
title: 'Order Alkes',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuRadiology: {
|
||||
id: 'mcu-radiology',
|
||||
radiologyOrder: {
|
||||
id: 'radiology-order',
|
||||
title: 'Order Radiologi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabPc: {
|
||||
id: 'mcu-lab-pc',
|
||||
cpLabOrder: {
|
||||
id: 'cp-lab-order',
|
||||
title: 'Order Lab PK',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabMicro: {
|
||||
id: 'mcu-lab-micro',
|
||||
microLabOrder: {
|
||||
id: 'micro-lab-order',
|
||||
title: 'Order Lab Mikro',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabPa: {
|
||||
id: 'mcu-lab-pa',
|
||||
paLabOrder: {
|
||||
id: 'pa-lab-order',
|
||||
title: 'Order Lab PA',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
medicalAction: {
|
||||
id: 'medical-action',
|
||||
actionRoomOrder: {
|
||||
id: 'action-room-order',
|
||||
title: 'Order Ruang Tindakan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
@@ -162,21 +174,45 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
actionReport: {
|
||||
id: 'action-report',
|
||||
title: 'Laporan Tindakan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
surgeryReport: {
|
||||
id: 'surgery-report',
|
||||
title: 'Laporan Operasi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
vacsinData: {
|
||||
id: 'vacsin-data',
|
||||
title: 'Data Vaksin',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
consultation: {
|
||||
id: 'consultation',
|
||||
title: 'Konsultasi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
resume: {
|
||||
id: 'resume',
|
||||
title: 'Resume',
|
||||
controlLetter: {
|
||||
id: 'control-letter',
|
||||
title: 'Surat Kontrol',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
control: {
|
||||
id: 'control',
|
||||
title: 'Surat Kontrol',
|
||||
inpatientLetter: {
|
||||
id: 'inpatient-letter',
|
||||
title: 'SPRI',
|
||||
classCode: ['ambulatory', 'emergency'],
|
||||
unit: 'all',
|
||||
},
|
||||
refBack: {
|
||||
id: 'reference-back',
|
||||
title: 'PRB',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
@@ -190,7 +226,19 @@ const defaultKeys: Record<string, any> = {
|
||||
id: 'supporting-document',
|
||||
title: 'Upload Dokumen Pendukung',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
unit: 'all',
|
||||
},
|
||||
resume: {
|
||||
id: 'resume',
|
||||
title: 'Resume',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
ambResume: {
|
||||
id: 'amb-resume',
|
||||
title: 'Resume Medis Rawat Jalan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
priceList: {
|
||||
id: 'price-list',
|
||||
@@ -244,12 +292,12 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
label: currentKeys.earlyMedicalAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.rehabMedicalAssessment) {
|
||||
currentKeys.rehabMedicalAssessment['component'] = EarlyMedicalRehabListAsync
|
||||
currentKeys.rehabMedicalAssessment['props'] = {
|
||||
if (currentKeys?.earlyMedicalRehabAssessment) {
|
||||
currentKeys.earlyMedicalRehabAssessment['component'] = EarlyMedicalRehabListAsync
|
||||
currentKeys.earlyMedicalRehabAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'early-rehab',
|
||||
label: currentKeys.rehabMedicalAssessment['title'],
|
||||
label: currentKeys.earlyMedicalRehabAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.functionAssessment) {
|
||||
@@ -287,44 +335,44 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
currentKeys.educationAssessment['component'] = null
|
||||
currentKeys.educationAssessment['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.consent) {
|
||||
currentKeys.consent['component'] = GeneralConsentListAsync
|
||||
currentKeys.consent['props'] = { encounter_id: id }
|
||||
if (currentKeys?.generalConsent) {
|
||||
currentKeys.generalConsent['component'] = GeneralConsentListAsync
|
||||
currentKeys.generalConsent['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.patientNote) {
|
||||
currentKeys.patientNote['component'] = CprjAsync
|
||||
currentKeys.patientNote['props'] = { encounter_id: id }
|
||||
if (currentKeys?.patientAmbNote) {
|
||||
currentKeys.patientAmbNote['component'] = CprjAsync
|
||||
currentKeys.patientAmbNote['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.prescription) {
|
||||
currentKeys.prescription['component'] = PrescriptionAsync
|
||||
currentKeys.prescription['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.device) {
|
||||
currentKeys.device['component'] = DeviceOrderAsync
|
||||
currentKeys.device['props'] = { encounter_id: id }
|
||||
if (currentKeys?.deviceOrder) {
|
||||
currentKeys.deviceOrder['component'] = DeviceOrderAsync
|
||||
currentKeys.deviceOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuRadiology) {
|
||||
currentKeys.mcuRadiology['component'] = RadiologyAsync
|
||||
currentKeys.mcuRadiology['props'] = { encounter_id: id }
|
||||
if (currentKeys?.radiologyOrder) {
|
||||
currentKeys.radiologyOrder['component'] = RadiologyAsync
|
||||
currentKeys.radiologyOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabPc) {
|
||||
currentKeys.mcuLabPc['component'] = CpLabOrderAsync
|
||||
currentKeys.mcuLabPc['props'] = { encounter_id: id }
|
||||
if (currentKeys?.cpLabOrder) {
|
||||
currentKeys.cpLabOrder['component'] = CpLabOrderAsync
|
||||
currentKeys.cpLabOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabMicro) {
|
||||
// TODO: add component for mcuLabMicro
|
||||
currentKeys.mcuLabMicro['component'] = null
|
||||
currentKeys.mcuLabMicro['props'] = { encounter_id: id }
|
||||
if (currentKeys?.microLabOrder) {
|
||||
// TODO: add component for microLabOrder
|
||||
currentKeys.microLabOrder['component'] = null
|
||||
currentKeys.microLabOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabPa) {
|
||||
// TODO: add component for mcuLabPa
|
||||
currentKeys.mcuLabPa['component'] = null
|
||||
currentKeys.mcuLabPa['props'] = { encounter_id: id }
|
||||
if (currentKeys?.paLabOrder) {
|
||||
// TODO: add component for paLabOrder
|
||||
currentKeys.paLabOrder['component'] = null
|
||||
currentKeys.paLabOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.medicalAction) {
|
||||
// TODO: add component for medicalAction
|
||||
currentKeys.medicalAction['component'] = null
|
||||
currentKeys.medicalAction['props'] = { encounter_id: id }
|
||||
if (currentKeys?.actionRoomOrder) {
|
||||
// TODO: add component for actionRoomOrder
|
||||
currentKeys.actionRoomOrder['component'] = null
|
||||
currentKeys.actionRoomOrder['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuResult) {
|
||||
// TODO: add component for mcuResult
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import { type Employee, genEmployee } from "./employee"
|
||||
import type { Unit } from "./unit"
|
||||
import type { Specialist } from "./specialist"
|
||||
import type { Subspecialist } from "./subspecialist"
|
||||
|
||||
@@ -9,10 +10,11 @@ export interface Doctor extends Base {
|
||||
ihs_number: string
|
||||
sip_number: string
|
||||
code?: string
|
||||
unit_icode?: number
|
||||
specialist_icode?: number
|
||||
unit_code?: string
|
||||
unit?: Unit
|
||||
specialist_code?: string
|
||||
specialist?: Specialist
|
||||
subspecialist_icode?: number
|
||||
subspecialist_code?: string
|
||||
subspecialist?: Subspecialist
|
||||
bpjs_code?: string
|
||||
}
|
||||
|
||||
+10
-7
@@ -3,6 +3,7 @@ import { type Doctor, genDoctor } from "./doctor"
|
||||
import { genEmployee, type Employee } from "./employee"
|
||||
import type { EncounterDocument } from "./encounter-document"
|
||||
import type { InternalReference } from "./internal-reference"
|
||||
import type { Nurse } from "./nurse"
|
||||
import { type Patient, genPatient } from "./patient"
|
||||
import type { Specialist } from "./specialist"
|
||||
import type { Subspecialist } from "./subspecialist"
|
||||
@@ -14,18 +15,20 @@ export interface Encounter {
|
||||
patient: Patient
|
||||
registeredAt: string
|
||||
class_code: string
|
||||
unit_id: number
|
||||
unit_code: string
|
||||
unit: Unit
|
||||
specialist_id?: number
|
||||
specialist_code?: string
|
||||
specilist?: Specialist
|
||||
subspecialist_id?: number
|
||||
subspecialist_code?: string
|
||||
subspecialist?: Subspecialist
|
||||
visitDate: string
|
||||
adm_employee_id: number
|
||||
adm_employee: Employee
|
||||
appointment_doctor_id: number
|
||||
responsible_nurse_code?: string
|
||||
responsible_nurse?: Nurse
|
||||
appointment_doctor_code: string
|
||||
appointment_doctor: Doctor
|
||||
responsible_doctor_id?: number
|
||||
responsible_doctor_code?: string
|
||||
responsible_doctor?: Doctor
|
||||
refSource_name?: string
|
||||
appointment_id?: number
|
||||
@@ -49,12 +52,12 @@ export function genEncounter(): Encounter {
|
||||
patient: genPatient(),
|
||||
registeredAt: '',
|
||||
class_code: '',
|
||||
unit_id: 0,
|
||||
unit_code: 0,
|
||||
unit: genUnit(),
|
||||
visitDate: '',
|
||||
adm_employee_id: 0,
|
||||
adm_employee: genEmployee(),
|
||||
appointment_doctor_id: 0,
|
||||
appointment_doctor_code: '',
|
||||
appointment_doctor: genDoctor(),
|
||||
medicalDischargeEducation: '',
|
||||
status_code: '',
|
||||
|
||||
@@ -2,10 +2,12 @@ import { z } from 'zod'
|
||||
|
||||
const ERROR_MESSAGES = {
|
||||
required: {
|
||||
doctorId: 'Dokter wajib diisi',
|
||||
doctor_code: 'Dokter wajib diisi',
|
||||
registerDate: 'Tanggal Daftar wajib diisi',
|
||||
paymentType: 'Jenis Pembayaran wajib diisi',
|
||||
subSpecialistId: 'Subspesialis wajib diisi',
|
||||
paymentMethod_code: 'Jenis Pembayaran wajib diisi',
|
||||
unit_code: 'Spesialis wajib diisi',
|
||||
// specialist_code: 'Spesialis wajib diisi',
|
||||
// subSpecialist_code: 'Subspesialis wajib diisi',
|
||||
patientCategory: 'Kelompok Peserta wajib diisi',
|
||||
cardNumber: 'No. Kartu BPJS wajib diisi',
|
||||
sepType: 'Jenis SEP wajib diisi',
|
||||
@@ -23,19 +25,19 @@ const IntegrationEncounterSchema = z
|
||||
medicalRecordNumber: z.string().optional(),
|
||||
|
||||
// Visit data
|
||||
doctorId: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.doctorId })
|
||||
.min(1, ERROR_MESSAGES.required.doctorId),
|
||||
subSpecialistId: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.subSpecialistId })
|
||||
.min(1, ERROR_MESSAGES.required.subSpecialistId)
|
||||
doctor_code: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.doctor_code })
|
||||
.min(1, ERROR_MESSAGES.required.doctor_code),
|
||||
unit_code: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.unit_code })
|
||||
.min(1, ERROR_MESSAGES.required.unit_code)
|
||||
.optional(),
|
||||
registerDate: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.registerDate })
|
||||
.min(1, ERROR_MESSAGES.required.registerDate),
|
||||
paymentType: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.paymentType })
|
||||
.min(1, ERROR_MESSAGES.required.paymentType),
|
||||
paymentMethod_code: z
|
||||
.string({ required_error: ERROR_MESSAGES.required.paymentMethod_code })
|
||||
.min(1, ERROR_MESSAGES.required.paymentMethod_code),
|
||||
|
||||
// BPJS related fields
|
||||
patientCategory: z
|
||||
@@ -76,7 +78,7 @@ const IntegrationEncounterSchema = z
|
||||
.refine(
|
||||
(data) => {
|
||||
// If payment type is jkn, then patient category is required
|
||||
if (data.paymentType === 'jkn') {
|
||||
if (data.paymentMethod_code === 'jkn') {
|
||||
return data.patientCategory && data.patientCategory.trim() !== ''
|
||||
}
|
||||
return true
|
||||
@@ -89,7 +91,7 @@ const IntegrationEncounterSchema = z
|
||||
.refine(
|
||||
(data) => {
|
||||
// If payment type is jkn, then card number is required
|
||||
if (data.paymentType === 'jkn') {
|
||||
if (data.paymentMethod_code === 'jkn') {
|
||||
return data.cardNumber && data.cardNumber.trim() !== ''
|
||||
}
|
||||
return true
|
||||
@@ -102,7 +104,7 @@ const IntegrationEncounterSchema = z
|
||||
.refine(
|
||||
(data) => {
|
||||
// If payment type is jkn, then SEP type is required
|
||||
if (data.paymentType === 'jkn') {
|
||||
if (data.paymentMethod_code === 'jkn') {
|
||||
return data.sepType && data.sepType.trim() !== ''
|
||||
}
|
||||
return true
|
||||
@@ -115,7 +117,7 @@ const IntegrationEncounterSchema = z
|
||||
.refine(
|
||||
(data) => {
|
||||
// If payment type is jkn and SEP type is selected, then SEP number is required
|
||||
if (data.paymentType === 'jkn' && data.sepType && data.sepType.trim() !== '') {
|
||||
if (data.paymentMethod_code === 'jkn' && data.sepType && data.sepType.trim() !== '') {
|
||||
return data.sepNumber && data.sepNumber.trim() !== ''
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -13,8 +13,8 @@ export function getList(params: any = null) {
|
||||
return base.getList(path, params, name)
|
||||
}
|
||||
|
||||
export function getDetail(id: number | string) {
|
||||
return base.getDetail(path, id, name)
|
||||
export function getDetail(id: number | string, params?: any) {
|
||||
return base.getDetail(path, id, name, params)
|
||||
}
|
||||
|
||||
export function update(id: number | string, data: any) {
|
||||
|
||||
Reference in New Issue
Block a user