diff --git a/app/components/app/encounter/entry-form.vue b/app/components/app/encounter/entry-form.vue index 29a75302..c4ada1db 100644 --- a/app/components/app/encounter/entry-form.vue +++ b/app/components/app/encounter/entry-form.vue @@ -24,6 +24,8 @@ import { useForm } from 'vee-validate' const props = defineProps<{ isLoading?: boolean isReadonly?: boolean + isSepValid?: boolean + isCheckingSep?: boolean doctor?: any[] subSpecialist?: any[] specialists?: TreeItem[] @@ -61,6 +63,10 @@ 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: '' }] @@ -86,6 +92,11 @@ watch(subSpecialistId, async (newValue) => { } }) +// Watch SEP number changes to notify parent +watch(sepNumber, (newValue) => { + emit('event', 'sep-number-changed', newValue) +}) + // Sync props to form fields watch( () => props.objects, @@ -403,13 +414,33 @@ defineExpose({ :disabled="isLoading || isReadonly" /> + diff --git a/app/components/content/encounter/entry.vue b/app/components/content/encounter/entry.vue index 580031d9..8b88c388 100644 --- a/app/components/content/encounter/entry.vue +++ b/app/components/content/encounter/entry.vue @@ -19,6 +19,10 @@ import { } from '~/services/specialist.service' import { getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service' import { create as createEncounter, getDetail as getEncounterDetail, update as updateEncounter } from '~/services/encounter.service' +import { getList as getSepList } from '~/services/vclaim-sep.service' + +// Helpers +import { refDebounced } from '@vueuse/core' // Handlers import { @@ -60,6 +64,12 @@ const formRef = ref | null>(null) const encounterData = ref(null) const formObjects = ref({}) +// SEP validation state +const isSepValid = ref(false) +const isCheckingSep = ref(false) +const sepNumber = ref('') +const debouncedSepNumber = refDebounced(sepNumber, 500) + // Computed for edit mode const isEditMode = computed(() => props.id > 0) @@ -236,6 +246,10 @@ async function handleEvent(menu: string, value?: any) { } else if (menu === 'add') { navigateTo('/client/patient/add') } else if (menu === 'add-sep') { + // If SEP is already valid, don't navigate + if (isSepValid.value) { + return + } recSelectId.value = null toNavigateSep({ isService: 'false', @@ -243,6 +257,11 @@ async function handleEvent(menu: string, value?: any) { resource: `${props.classCode}-${props.subClassCode}`, ...value, }) + } else if (menu === 'sep-number-changed') { + // Update sepNumber when it changes in form (only if different to prevent loop) + if (sepNumber.value !== value) { + sepNumber.value = value || '' + } } else if (menu === 'save') { await handleSaveEncounter(value) } else if (menu === 'cancel') { @@ -250,6 +269,63 @@ async function handleEvent(menu: string, value?: any) { } } +/** + * Validate SEP number + */ +async function validateSepNumber(sepNumberValue: string) { + // Reset validation if SEP number is empty + if (!sepNumberValue || sepNumberValue.trim() === '') { + isSepValid.value = false + isCheckingSep.value = false + return + } + + // Only check if payment type is JKN + // We need to check from formObjects + const paymentType = formObjects.value?.paymentType + if (paymentType !== 'jkn') { + isSepValid.value = false + return + } + + try { + isCheckingSep.value = true + const result = await getSepList({ number: sepNumberValue.trim() }) + + // Check if SEP is found + // If response is not null, SEP is found + // If response is null with metaData code "201", SEP is not found + if (result.success && result.body?.response !== null) { + isSepValid.value = true + } else { + // SEP not found (response null with metaData code "201") + isSepValid.value = false + } + } catch (error) { + console.error('Error checking SEP:', error) + isSepValid.value = false + } finally { + isCheckingSep.value = false + } +} + +// Watch debounced SEP number to validate +watch(debouncedSepNumber, async (newValue) => { + await validateSepNumber(newValue) +}) + +// Watch payment type to reset SEP validation +watch( + () => formObjects.value?.paymentType, + (newValue) => { + isSepValid.value = false + // If payment type is not JKN, clear SEP number + if (newValue !== 'jkn') { + sepNumber.value = '' + } + }, +) + async function handleFetchSpecialists() { try { const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' }) @@ -583,6 +659,11 @@ async function mapEncounterToForm(encounter: any) { // Set form objects for the form component formObjects.value = formData + // Update sepNumber for validation + if (formData.sepNumber) { + sepNumber.value = formData.sepNumber + } + // Fetch doctors based on specialist/subspecialist selection if (formData.subSpecialistId) { await handleFetchDoctors(formData.subSpecialistId) @@ -613,6 +694,9 @@ onMounted(async () => { { :doctor="doctorsList" :patient="selectedPatientObject" :objects="formObjects" - :is-loading="isLoadingDetail" @event="handleEvent" @fetch="handleFetch" /> diff --git a/app/services/vclaim-sep.service.ts b/app/services/vclaim-sep.service.ts index 5a19d7c9..fdccc9c4 100644 --- a/app/services/vclaim-sep.service.ts +++ b/app/services/vclaim-sep.service.ts @@ -11,6 +11,15 @@ export function create(data: any) { return base.create(path, data, name) } +export function getList(params: any = null) { + let url = path + if (params?.number) { + url += `/${params.number}` + delete params.number + } + return base.getList(url, params, name) +} + export function makeSepData( data: IntegrationBpjsFormData & { referralFrom?: string @@ -20,7 +29,7 @@ export function makeSepData( }, ) { const content = { - noKartu: data.bpjsNumber || '', + noKartu: data.cardNumber || '', tglSep: data.sepDate, ppkPelayanan: data.fromClinic || '', jnsPelayanan: data.admissionType ? String(data.admissionType) : '1',