feat: add member validation and enhance encounter entry form with additional fields
This commit is contained in:
@@ -23,11 +23,12 @@ import { useForm } from 'vee-validate'
|
||||
import { refDebounced } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
mode?: string
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
isSepValid?: boolean
|
||||
isMemberValid?: boolean
|
||||
isCheckingSep?: boolean
|
||||
mode?: string
|
||||
doctor?: any[]
|
||||
subSpecialist?: any[]
|
||||
specialists?: TreeItem[]
|
||||
@@ -61,6 +62,12 @@ const [patientName, patientNameAttrs] = defineField('patientName')
|
||||
const [nationalIdentity, nationalIdentityAttrs] = defineField('nationalIdentity')
|
||||
const [medicalRecordNumber, medicalRecordNumberAttrs] = defineField('medicalRecordNumber')
|
||||
const patientId = ref('')
|
||||
const sepReference = ref('')
|
||||
const sepControlDate = ref('')
|
||||
const sepTrafficStatus = ref('')
|
||||
const diagnosis = ref('')
|
||||
const noteReference = ref('Hanya diperlukan jika pembayaran jenis JKN')
|
||||
const noteFile = ref('Gunakan file [.pdf, .jpg, .png] dengan ukuran maksimal 1MB')
|
||||
|
||||
const isLoading = props.isLoading !== undefined ? props.isLoading : false
|
||||
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||
@@ -76,6 +83,8 @@ const doctorOpts = computed(() => {
|
||||
return [...defaultOption, ...doctors]
|
||||
})
|
||||
const isJKNPayment = computed(() => paymentType.value === 'jkn')
|
||||
const debouncedSepNumber = refDebounced(sepNumber, 500)
|
||||
const debouncedCardNumber = refDebounced(cardNumber, 500)
|
||||
|
||||
if (mode === 'add') {
|
||||
// Set default sepDate to current date in YYYY-MM-DD format
|
||||
@@ -101,12 +110,14 @@ watch(subSpecialistId, async (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)
|
||||
})
|
||||
|
||||
watch(debouncedCardNumber, (newValue) => {
|
||||
emit('event', 'member-changed', newValue)
|
||||
})
|
||||
|
||||
// Sync props to form fields
|
||||
watch(
|
||||
() => props.objects,
|
||||
@@ -123,6 +134,10 @@ watch(
|
||||
cardNumber.value = objects?.cardNumber || ''
|
||||
sepType.value = objects?.sepType || ''
|
||||
sepNumber.value = objects?.sepNumber || ''
|
||||
sepReference.value = objects?.sepReference || ''
|
||||
sepControlDate.value = objects?.sepControlDate || ''
|
||||
sepTrafficStatus.value = objects?.sepTrafficStatus || ''
|
||||
diagnosis.value = objects?.diagnosis || ''
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
@@ -141,6 +156,20 @@ watch(
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.isSepValid,
|
||||
(value) => {
|
||||
if (!value) return
|
||||
const objects = props.objects
|
||||
if (objects && Object.keys(objects).length > 0) {
|
||||
sepReference.value = objects?.sepReference || ''
|
||||
sepControlDate.value = objects?.sepControlDate || ''
|
||||
sepTrafficStatus.value = objects?.sepTrafficStatus || ''
|
||||
diagnosis.value = objects?.diagnosis || ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
function onAddSep() {
|
||||
const formValues = {
|
||||
patientId: patientId.value || '',
|
||||
@@ -391,6 +420,9 @@ defineExpose({
|
||||
placeholder="Pilih Kelompok Peserta"
|
||||
/>
|
||||
</Field>
|
||||
<span class="text-sm text-gray-500">
|
||||
{{ noteReference }}
|
||||
</span>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
@@ -407,6 +439,26 @@ defineExpose({
|
||||
placeholder="Masukkan nomor kartu BPJS"
|
||||
/>
|
||||
</Field>
|
||||
<div
|
||||
v-if="isMemberValid"
|
||||
class="mt-1 flex items-center gap-2"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-badge-check"
|
||||
class="h-4 w-4 bg-green-500 text-white"
|
||||
/>
|
||||
<span class="text-sm text-green-500">Aktif</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isMemberValid"
|
||||
class="mt-1 flex items-center gap-2"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-x"
|
||||
class="h-4 w-4 bg-red-500 text-white"
|
||||
/>
|
||||
<span class="text-sm text-red-500">Tidak aktif</span>
|
||||
</div>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
@@ -469,7 +521,19 @@ defineExpose({
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-else
|
||||
v-if="!isSepValid"
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="bg-primary"
|
||||
size="sm"
|
||||
>
|
||||
<Icon
|
||||
name="i-lucide-search"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
v-if="isSepValid"
|
||||
variant="outline"
|
||||
type="button"
|
||||
class="bg-green-500 text-white hover:bg-green-600"
|
||||
@@ -483,23 +547,115 @@ defineExpose({
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
<span class="text-sm text-gray-500">
|
||||
{{ noteReference }}
|
||||
</span>
|
||||
</Cell>
|
||||
|
||||
<FileUpload
|
||||
field-name="sepFile"
|
||||
label="Dokumen SEP"
|
||||
placeholder="Unggah dokumen SEP"
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
<Cell>
|
||||
<FileUpload
|
||||
field-name="sepFile"
|
||||
label="Dokumen SEP"
|
||||
placeholder=""
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
<span class="mt-1 text-sm text-gray-500">
|
||||
{{ noteFile }}
|
||||
</span>
|
||||
</Cell>
|
||||
|
||||
<FileUpload
|
||||
field-name="sippFile"
|
||||
label="Dokumen SIPP"
|
||||
placeholder="Unggah dokumen SIPP"
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
<Cell>
|
||||
<FileUpload
|
||||
field-name="sippFile"
|
||||
label="Dokumen SIPP"
|
||||
placeholder=""
|
||||
:accept="['pdf', 'jpg', 'png']"
|
||||
:max-size-mb="1"
|
||||
/>
|
||||
<span class="mt-1 text-sm text-gray-500">
|
||||
{{ noteFile }}
|
||||
</span>
|
||||
</Cell>
|
||||
</Block>
|
||||
</template>
|
||||
|
||||
<template v-if="isSepValid">
|
||||
<hr />
|
||||
<!-- Data SEP -->
|
||||
<h3 class="text-lg font-semibold">Data SEP</h3>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Dengan Rujukan / Surat Kontrol</Label>
|
||||
<Field>
|
||||
<Input
|
||||
id="sepReference"
|
||||
v-model="sepReference"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">No. Rujukan / Surat Kontrol</Label>
|
||||
<Field>
|
||||
<Input
|
||||
id="sepReference"
|
||||
v-model="sepNumber"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
Tanggal Rujukan / Surat Kontrol
|
||||
<span class="ml-1 text-red-500">*</span>
|
||||
</Label>
|
||||
<Field>
|
||||
<DatepickerSingle
|
||||
id="sepControlDate"
|
||||
v-model="sepControlDate"
|
||||
:disabled="true"
|
||||
placeholder="Pilih tanggal sep"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Diagnosis</Label>
|
||||
<Field>
|
||||
<Input
|
||||
id="diagnosis"
|
||||
v-model="diagnosis"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
|
||||
<Cell>
|
||||
<Label height="compact">Status Kecelakaan</Label>
|
||||
<Field>
|
||||
<Input
|
||||
id="sepTrafficStatus"
|
||||
v-model="sepTrafficStatus"
|
||||
:disabled="true"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
</template>
|
||||
</form>
|
||||
|
||||
@@ -31,10 +31,10 @@ const {
|
||||
isLoadingDetail,
|
||||
formObjects,
|
||||
openPatient,
|
||||
isMemberValid,
|
||||
isSepValid,
|
||||
isCheckingSep,
|
||||
isSaveDisabled,
|
||||
isSaving,
|
||||
isLoading,
|
||||
patients,
|
||||
selectedPatient,
|
||||
@@ -49,6 +49,7 @@ const {
|
||||
getPatientCurrent,
|
||||
getPatientByIdentifierSearch,
|
||||
getIsSubspecialist,
|
||||
getValidateMember,
|
||||
getValidateSepNumber,
|
||||
handleFetchDoctors,
|
||||
} = useEncounterEntry(props)
|
||||
@@ -93,6 +94,8 @@ async function handleEvent(menu: string, value?: any) {
|
||||
})
|
||||
} else if (menu === 'sep-number-changed') {
|
||||
await getValidateSepNumber(String(value || ''))
|
||||
} else if (menu === 'member-changed') {
|
||||
await getValidateMember(String(value || ''))
|
||||
} else if (menu === 'save') {
|
||||
await handleSaveEncounter(value)
|
||||
} else if (menu === 'cancel') {
|
||||
@@ -139,6 +142,7 @@ onMounted(async () => {
|
||||
ref="formRef"
|
||||
:mode="props.formType"
|
||||
:is-loading="isLoadingDetail"
|
||||
:is-member-valid="isMemberValid"
|
||||
:is-sep-valid="isSepValid"
|
||||
:is-checking-sep="isCheckingSep"
|
||||
:payments="paymentsList"
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
getDetail as getEncounterDetail,
|
||||
update as updateEncounter,
|
||||
} from '~/services/encounter.service'
|
||||
import { getList as getMemberList } from '~/services/vclaim-member.service'
|
||||
import { getList as getSepList } from '~/services/vclaim-sep.service'
|
||||
|
||||
// Handlers
|
||||
@@ -59,6 +60,7 @@ export function useEncounterEntry(props: {
|
||||
const encounterData = ref<any>(null)
|
||||
const formObjects = ref<any>({})
|
||||
const isSepValid = ref(false)
|
||||
const isMemberValid = ref(false)
|
||||
const isCheckingSep = ref(false)
|
||||
const sepNumber = ref('')
|
||||
const vclaimReference = ref<any>(null)
|
||||
@@ -201,6 +203,19 @@ export function useEncounterEntry(props: {
|
||||
return { specialist_id: null, subspecialist_id: null }
|
||||
}
|
||||
|
||||
async function getValidateMember(member: string) {
|
||||
if (isCheckingSep.value) return
|
||||
isMemberValid.value = false
|
||||
try {
|
||||
const result = await getMemberList({ mode: 'by-card', number: member, date: new Date().toISOString().split('T')[0] })
|
||||
if (result.success && result.body?.response !== null) {
|
||||
isMemberValid.value = result.body?.metaData?.code === '200'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking member:', error)
|
||||
}
|
||||
}
|
||||
|
||||
async function getValidateSepNumber(sepNumberValue: string) {
|
||||
vclaimReference.value = null
|
||||
if (!sepNumberValue || sepNumberValue.trim() === '') {
|
||||
@@ -220,11 +235,16 @@ export function useEncounterEntry(props: {
|
||||
formObjects.value.medicalRecordNumber = response.peserta?.noMr || '-'
|
||||
formObjects.value.cardNumber = response.peserta?.noKartu || '-'
|
||||
formObjects.value.registerDate = response.tglSep || null
|
||||
formObjects.value.sepReference = response.noSep || '-'
|
||||
formObjects.value.sepControlDate = response.tglSep || null
|
||||
formObjects.value.sepTrafficStatus = response.nmstatusKecelakaan || '-'
|
||||
formObjects.value.diagnosis = response.diagnosa || '-'
|
||||
vclaimReference.value = {
|
||||
noSep: response.noSep || sepNumberValue.trim(),
|
||||
tglRujukan: response.tglSep ? new Date(response.tglSep).toISOString() : null,
|
||||
ppkDirujuk: response.noRujukan || 'rssa',
|
||||
jnsPelayanan: response.jnsPelayanan === 'Rawat Jalan' ? '2' : response.jnsPelayanan === 'Rawat Inap' ? '1' : null,
|
||||
jnsPelayanan:
|
||||
response.jnsPelayanan === 'Rawat Jalan' ? '2' : response.jnsPelayanan === 'Rawat Inap' ? '1' : null,
|
||||
catatan: response.catatan || '',
|
||||
diagRujukan: response.diagnosa || '',
|
||||
tipeRujukan: response.tujuanKunj?.kode ?? '0',
|
||||
@@ -233,6 +253,7 @@ export function useEncounterEntry(props: {
|
||||
}
|
||||
}
|
||||
isSepValid.value = result.body?.metaData?.code === '200'
|
||||
isMemberValid.value = isSepValid.value
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking SEP:', error)
|
||||
@@ -497,7 +518,7 @@ export function useEncounterEntry(props: {
|
||||
if (paymentMethodCode) {
|
||||
payload.paymentMethod_code = paymentMethodCode
|
||||
}
|
||||
|
||||
|
||||
if (paymentMethodCode === 'insurance') {
|
||||
payload.insuranceCompany_id = formValues.insuranceCompany_id ?? null
|
||||
if (memberNumber) payload.member_number = memberNumber
|
||||
@@ -565,6 +586,7 @@ export function useEncounterEntry(props: {
|
||||
encounterData,
|
||||
formObjects,
|
||||
openPatient,
|
||||
isMemberValid,
|
||||
isSepValid,
|
||||
isCheckingSep,
|
||||
isEditMode,
|
||||
@@ -585,6 +607,7 @@ export function useEncounterEntry(props: {
|
||||
getPatientsList,
|
||||
getPatientCurrent,
|
||||
getPatientByIdentifierSearch,
|
||||
getValidateMember,
|
||||
getValidateSepNumber,
|
||||
handleFetchSpecialists,
|
||||
handleFetchDoctors,
|
||||
|
||||
Reference in New Issue
Block a user