Files
simrsx-fe/app/components/content/sep/entry.vue
T
2025-11-07 15:49:55 +07:00

553 lines
20 KiB
Vue

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
// Components
import AppSepEntryForm from '~/components/app/sep/entry-form.vue'
import AppViewPatient from '~/components/app/patient/view-patient.vue'
import AppViewHistory from '~/components/app/sep/view-history.vue'
import AppViewLetter from '~/components/app/sep/view-letter.vue'
import { toast } from '~/components/pub/ui/toast'
// Types
import type { SepHistoryData } from '~/components/app/sep/list-cfg.history'
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
// Constants
import {
serviceTypes,
serviceAssessments,
registerMethods,
trafficAccidents,
supportCodes,
procedureTypes,
purposeOfVisits,
classLevels,
classLevelUpgrades,
classPaySources,
} from '~/lib/constants.vclaim'
// Services
import {
getList as getSpecialistList,
getValueTreeItems as getSpecialistTreeItems,
} from '~/services/specialist.service'
import { getValueLabelList as getProvinceList } from '~/services/vclaim-region-province.service'
import { getValueLabelList as getCityList } from '~/services/vclaim-region-city.service'
import { getValueLabelList as getDistrictList } from '~/services/vclaim-region-district.service'
import { getValueLabelList as getDoctorLabelList } from '~/services/vclaim-doctor.service'
import { getValueLabelList as getHealthFacilityLabelList } from '~/services/vclaim-healthcare.service'
import { getValueLabelList as getDiagnoseLabelList } from '~/services/vclaim-diagnose.service'
import { getList as getMemberList } from '~/services/vclaim-member.service'
import { getList as getHospitalLetterList } from '~/services/vclaim-reference-hospital-letter.service'
import { getList as getControlLetterList } from '~/services/vclaim-control-letter.service'
import { getList as getMonitoringHistoryList } from '~/services/vclaim-monitoring-history.service'
import { create as createSep, makeSepData } from '~/services/vclaim-sep.service'
// Handlers
import {
patients,
selectedPatient,
selectedPatientObject,
paginationMeta,
getPatientsList,
getPatientCurrent,
getPatientByIdentifierSearch,
} from '~/handlers/patient.handler'
const route = useRoute()
const openPatient = ref(false)
const openLetter = ref(false)
const openHistory = ref(false)
const selectedLetter = ref('')
const selectedObjects = ref<any>({})
const selectedServiceType = ref<string>('')
const selectedAdmissionType = ref<string>('')
const histories = ref<Array<SepHistoryData>>([])
const letters = ref<Array<any>>([])
const doctors = ref<Array<{ value: string | number; label: string }>>([])
const diagnoses = ref<Array<{ value: string | number; label: string }>>([])
const facilitiesFrom = ref<Array<{ value: string | number; label: string }>>([])
const facilitiesTo = ref<Array<{ value: string | number; label: string }>>([])
const supportCodesList = ref<Array<{ value: string; label: string }>>([])
const serviceTypesList = ref<Array<{ value: string; label: string }>>([])
const registerMethodsList = ref<Array<{ value: string; label: string }>>([])
const accidentsList = ref<Array<{ value: string; label: string }>>([])
const purposeOfVisitsList = ref<Array<{ value: string; label: string }>>([])
const proceduresList = ref<Array<{ value: string; label: string }>>([])
const assessmentsList = ref<Array<{ value: string; label: string }>>([])
const provincesList = ref<Array<{ value: string; label: string }>>([])
const citiesList = ref<Array<{ value: string; label: string }>>([])
const districtsList = ref<Array<{ value: string; label: string }>>([])
const classLevelsList = ref<Array<{ value: string; label: string }>>([])
const classLevelUpgradesList = ref<Array<{ value: string; label: string }>>([])
const classPaySourcesList = ref<Array<{ value: string; label: string }>>([])
const isServiceHidden = ref(false)
const isSaveLoading = ref(false)
const isLetterReadonly = ref(false)
const specialistsTree = ref<TreeItem[]>([])
const resourceType = ref('')
const resourcePath = ref('')
async function getMonitoringHistoryMappers() {
histories.value = []
const dateFirst = new Date()
const dateLast = new Date()
dateLast.setMonth(dateFirst.getMonth() - 3)
const cardNumber =
selectedPatientObject.value?.person?.residentIdentityNumber || selectedPatientObject.value?.number || ''
const result = await getMonitoringHistoryList({
cardNumber: cardNumber,
startDate: dateFirst.toISOString().substring(0, 10),
endDate: dateLast.toISOString().substring(0, 10),
})
if (result && result.success && result.body) {
const historiesRaw = result.body?.response?.histori || []
if (!historiesRaw) return
historiesRaw.forEach((result: any) => {
histories.value.push({
sepNumber: result.noSep,
sepDate: result.tglSep,
referralNumber: result.noRujukan,
diagnosis:
result.diagnosa && typeof result.diagnosa === 'string' && result.diagnosa.length > 20
? result.diagnosa.toString().substring(0, 17) + '...'
: '-',
serviceType: !result.jnsPelayanan ? '-' : result.jnsPelayanan === '1' ? 'Rawat Jalan' : 'Rawat Inap',
careClass: result.kelasRawat,
})
})
}
}
async function getLetterMappers(admissionType: string, search: string) {
letters.value = []
let result = null
if (admissionType !== '3') {
result = await getHospitalLetterList({
letterNumber: search,
})
} else {
result = await getControlLetterList({
letterNumber: search,
mode: 'by-control',
})
if (result && result.success && result.body) {
const lettersRaw = result.body?.response || null
if (!lettersRaw) {
result = await getControlLetterList({
letterNumber: search,
mode: 'by-card',
})
}
}
if (result && result.success && result.body) {
const lettersRaw = result.body?.response || null
if (!lettersRaw) {
result = await getControlLetterList({
letterNumber: search,
mode: 'by-sep',
})
}
}
}
if (result && result.success && result.body) {
const lettersRaw = result.body?.response || null
if (!lettersRaw) return
if (admissionType === '3') {
letters.value = [
{
letterNumber: lettersRaw.noSuratKontrol || '',
plannedDate: lettersRaw.tglRencanaKontrol || '',
sepNumber: lettersRaw.sep.noSep || '',
patientName: lettersRaw.sep.peserta.nama || '',
bpjsCardNo: lettersRaw.sep.peserta.noKartu,
clinic: lettersRaw.sep.poli || '',
doctor: lettersRaw.sep.namaDokter || '',
},
]
} else {
// integrate ke sep ---
// "rujukan": {
// "noRujukan": "0212R0300625B000006", // rujukan?.noKunjungan
// "ppkRujukan": "0212R030",
// "tglRujukan": "2025-06-26",
// "asalRujukan": "2" // asalFaskes
// },
// "jnsPelayanan": "2",
// "ppkPelayanan": "1323R001",
// "poli": {
// "tujuan": "URO", // rujukan?.poliRujukan?.kode
// },
// "klsRawat": {
// "pembiayaan": "",
// "klsRawatHak": "2", // peserta.hakKelas?.kode
// "klsRawatNaik": "",
// "penanggungJawab": ""
// },
letters.value = [
{
letterNumber: lettersRaw?.rujukan?.noKunjungan || '',
plannedDate: lettersRaw?.rujukan?.tglKunjungan || '',
sepNumber: lettersRaw?.rujukan?.informasi?.eSEP || '-',
patientName: lettersRaw?.rujukan?.peserta.nama || '',
bpjsCardNo: lettersRaw?.rujukan?.peserta.noKartu || '',
clinic: lettersRaw?.rujukan?.poliRujukan.nama || '',
doctor: '',
information: {
facility: lettersRaw?.asalFaskes || '',
diagnoses: lettersRaw?.rujukan?.diagnosa?.kode || '',
serviceType: lettersRaw?.rujukan?.pelayanan?.kode || '',
classLevel: lettersRaw?.rujukan?.peserta?.hakKelas?.kode || '',
poly: lettersRaw?.rujukan?.poliRujukan?.kode || '',
cardNumber: lettersRaw?.rujukan?.peserta?.noKartu || '',
patientName: lettersRaw?.rujukan?.peserta?.nama || '',
patientPhone: lettersRaw?.rujukan?.peserta?.mr?.noTelepon || '',
medicalRecordNumber: lettersRaw?.rujukan?.peserta?.mr?.noMR || '',
},
},
]
}
}
}
async function getPatientInternalMappers(id: string) {
try {
await getPatientCurrent(id)
if (selectedPatientObject.value) {
const patient = selectedPatientObject.value
selectedObjects.value['cardNumber'] = '-'
selectedObjects.value['nationalIdentity'] = patient?.person?.residentIdentityNumber || '-'
selectedObjects.value['medicalRecordNumber'] = patient?.number || '-'
selectedObjects.value['patientName'] = patient?.person?.name || '-'
selectedObjects.value['phoneNumber'] = patient?.person?.contacts?.[0]?.value || '-'
}
} catch (err) {
console.error('Failed to load patient from query params:', err)
}
}
async function getPatientExternalMappers(id: string, type: string) {
try {
const result = await getMemberList({
mode: type,
number: id,
date: new Date().toISOString().substring(0, 10),
})
if (result && result.success && result.body) {
const memberRaws = result.body?.response || null
selectedObjects.value['cardNumber'] = memberRaws?.peserta?.noKartu || ''
selectedObjects.value['nationalIdentity'] = memberRaws?.peserta?.nik || ''
selectedObjects.value['medicalRecordNumber'] = memberRaws?.peserta?.mr?.noMR || ''
selectedObjects.value['patientName'] = memberRaws?.peserta?.nama || ''
selectedObjects.value['phoneNumber'] = memberRaws?.peserta?.mr?.noTelepon || ''
selectedObjects.value['classLevel'] = memberRaws?.peserta?.hakKelas?.kode || ''
selectedObjects.value['status'] = memberRaws?.statusPeserta?.kode || ''
}
} catch (err) {
console.error('Failed to load patient from query params:', err)
}
}
function handleSaveLetter() {
// Find the selected letter and get its plannedDate
const selectedLetterData = letters.value.find((letter) => letter.letterNumber === selectedLetter.value)
if (selectedLetterData && selectedLetterData.plannedDate) {
selectedObjects.value['letterDate'] = selectedLetterData.plannedDate
}
}
async function handleSavePatient() {
selectedPatientObject.value = null
await getPatientInternalMappers(selectedPatient.value)
}
async function handleEvent(menu: string, value: any) {
if (menu === 'admission-type') {
selectedAdmissionType.value = value
return
}
if (menu === 'service-type') {
selectedServiceType.value = value
doctors.value = await getDoctorLabelList({
serviceType: selectedServiceType.value || 1,
serviceDate: new Date().toISOString().substring(0, 10),
specialistCode: 0,
})
}
if (menu === 'search-patient') {
getPatientsList({ 'page-size': 10, includes: 'person' }).then(() => {
openPatient.value = true
})
return
}
if (menu === 'search-patient-by-identifier') {
const text = value.text
const type = value.type
const prevCardNumber = selectedPatientObject.value?.person?.residentIdentityNumber || ''
if (type === 'indentity' && text !== prevCardNumber) {
await getPatientByIdentifierSearch(text)
await getPatientExternalMappers(text, 'by-identity')
}
if (type === 'cardNumber' && text !== prevCardNumber) {
await getPatientExternalMappers(text, 'by-card')
}
return
}
if (menu === 'search-letter') {
isLetterReadonly.value = false
getLetterMappers(value.admissionType, value.search).then(() => {
if (letters.value.length > 0) {
const copyObjects = { ...selectedObjects.value }
selectedObjects.value = {}
selectedLetter.value = letters.value[0].letterNumber
isLetterReadonly.value = true
setTimeout(() => {
selectedObjects.value = copyObjects
selectedObjects.value['letterDate'] = letters.value[0].plannedDate
}, 100)
}
})
return
}
if (menu === 'open-letter') {
openLetter.value = true
return
}
if (menu === 'history-sep') {
getMonitoringHistoryMappers().then(() => {
openHistory.value = true
})
return
}
if (menu === 'back') {
navigateTo('/integration/bpjs/sep')
}
if (menu === 'save-sep') {
isSaveLoading.value = true
// value.destinationClinic = value.destinationClinic || ''
createSep(makeSepData(value))
.then((res) => {
const body = res?.body
const code = body?.metaData?.code
const message = body?.metaData?.message
if (code && code !== '200') {
toast({ title: 'Gagal', description: message || 'Gagal membuat SEP', variant: 'destructive' })
return
}
toast({ title: 'Berhasil', description: 'SEP berhasil dibuat', variant: 'default' })
if (resourceType.value === 'encounter') {
navigateTo(resourcePath.value)
return
}
navigateTo('/integration/bpjs/sep')
})
.catch((err) => {
console.error('Failed to save SEP:', err)
toast({ title: 'Gagal', description: err?.message || 'Gagal membuat SEP', variant: 'destructive' })
})
.finally(() => {
isSaveLoading.value = false
})
}
}
async function handleFetch(params: any) {
const menu = params.menu || ''
const value = params.value || ''
if (menu === 'diagnosis') {
diagnoses.value = await getDiagnoseLabelList({ diagnosa: value })
}
if (menu === 'clinic-from') {
facilitiesFrom.value = await getHealthFacilityLabelList({
healthcare: value,
healthcareType: selectedServiceType.value || 1,
})
}
if (menu === 'clinic-to') {
facilitiesTo.value = await getHealthFacilityLabelList({
healthcare: value,
healthcareType: selectedServiceType.value || 1,
})
}
if (menu === 'province') {
citiesList.value = await getCityList({ province: value })
districtsList.value = []
}
if (menu === 'city') {
districtsList.value = await getDistrictList({ city: value })
}
}
async function handleFetchSpecialists() {
try {
const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
if (specialistsResult.success) {
const specialists = specialistsResult.body?.data || []
specialistsTree.value = getSpecialistTreeItems(specialists)
}
} catch (error) {
console.error('Error fetching specialist-subspecialist tree:', error)
}
}
async function handleInit() {
const facilities = await getHealthFacilityLabelList({
healthcare: 'Puskesmas',
healthcareType: selectedLetter.value || 1,
})
diagnoses.value = await getDiagnoseLabelList({ diagnosa: 'paru' })
facilitiesFrom.value = facilities
facilitiesTo.value = facilities
doctors.value = await getDoctorLabelList({
serviceType: selectedServiceType.value || 1,
serviceDate: new Date().toISOString().substring(0, 10),
specialistCode: 0,
})
provincesList.value = await getProvinceList()
serviceTypesList.value = Object.keys(serviceTypes).map((item) => ({
value: item.toString(),
label: serviceTypes[item],
})) as any
registerMethodsList.value = Object.keys(registerMethods)
.filter((item) => ![''].includes(item))
.map((item) => ({
value: item.toString(),
label: registerMethods[item],
})) as any
accidentsList.value = Object.keys(trafficAccidents).map((item) => ({
value: item.toString(),
label: trafficAccidents[item],
})) as any
purposeOfVisitsList.value = Object.keys(purposeOfVisits).map((item) => ({
value: item.toString(),
label: purposeOfVisits[item],
})) as any
proceduresList.value = Object.keys(procedureTypes).map((item) => ({
value: item.toString(),
label: procedureTypes[item],
})) as any
assessmentsList.value = Object.keys(serviceAssessments).map((item) => ({
value: item.toString(),
label: `${item.toString()} - ${serviceAssessments[item]}`,
})) as any
supportCodesList.value = Object.keys(supportCodes).map((item) => ({
value: item.toString(),
label: `${item.toString()} - ${supportCodes[item]}`,
})) as any
classLevelsList.value = Object.keys(classLevels).map((item) => ({
value: item.toString(),
label: classLevels[item],
})) as any
classLevelUpgradesList.value = Object.keys(classLevelUpgrades).map((item) => ({
value: item.toString(),
label: classLevelUpgrades[item],
})) as any
classPaySourcesList.value = Object.keys(classPaySources).map((item) => ({
value: item.toString(),
label: classPaySources[item],
})) as any
await handleFetchSpecialists()
if (route.query) {
const queries = route.query as any
isServiceHidden.value = queries['is-service'] === 'true'
selectedObjects.value = {}
if (queries['resource']) resourceType.value = queries['resource']
if (queries['resource-path']) resourcePath.value = queries['resource-path']
if (queries['doctor-code']) selectedObjects.value['doctorCode'] = queries['doctor-code']
if (queries['specialist-code']) selectedObjects.value['subSpecialistCode'] = queries['specialist-code']
if (queries['sub-specialist-code']) selectedObjects.value['subSpecialistCode'] = queries['sub-specialist-code']
if (queries['card-number']) selectedObjects.value['cardNumber'] = queries['card-number']
if (queries['register-date']) selectedObjects.value['registerDate'] = queries['register-date']
if (queries['sep-type']) selectedObjects.value['sepType'] = queries['sep-type']
if (queries['sep-number']) selectedObjects.value['sepNumber'] = queries['sep-number']
if (queries['register-date']) selectedObjects.value['registerDate'] = queries['register-date']
if (queries['payment-type']) selectedObjects.value['paymentType'] = queries['payment-type']
if (queries['patient-id']) {
await getPatientInternalMappers(queries['patient-id'])
}
if (queries['card-number']) {
const resultMember = await getMemberList({
mode: 'by-card',
number: queries['card-number'],
date: new Date().toISOString().substring(0, 10),
})
console.log(resultMember)
}
delete selectedObjects.value['is-service']
}
}
onMounted(async () => {
await handleInit()
})
</script>
<template>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<Icon
name="i-lucide-panel-bottom"
class="me-2"
/>
<span class="font-semibold">Tambah</span>
SEP
</div>
<AppSepEntryForm
:is-save-loading="isSaveLoading"
:is-service="isServiceHidden"
:doctors="doctors"
:diagnoses="diagnoses"
:facilities-from="facilitiesFrom"
:facilities-to="facilitiesTo"
:service-types="serviceTypesList"
:register-methods="registerMethodsList"
:accidents="accidentsList"
:purposes="purposeOfVisitsList"
:procedures="proceduresList"
:assessments="assessmentsList"
:support-codes="supportCodesList"
:provinces="provincesList"
:cities="citiesList"
:districts="districtsList"
:class-levels="classLevelsList"
:class-level-upgrades="classLevelUpgradesList"
:class-pay-sources="classPaySourcesList"
:specialists="specialistsTree"
:objects="selectedObjects"
@fetch="handleFetch"
@event="handleEvent"
/>
<AppViewPatient
v-model:open="openPatient"
v-model:selected="selectedPatient"
:patients="patients"
:pagination-meta="paginationMeta"
@fetch="
(value) => {
if (value.search && value.search.length >= 3) {
// Use identifier search for specific searches (NIK, RM, etc.)
getPatientByIdentifierSearch(value.search)
} else {
// Use regular search for general searches
getPatientsList({ ...value, 'page-size': 10, includes: 'person' })
}
}
"
@save="handleSavePatient"
/>
<AppViewHistory
v-model:open="openHistory"
:histories="histories"
/>
<AppViewLetter
v-model:open="openLetter"
:letters="letters"
:menu="selectedAdmissionType !== '3' ? 'control' : 'reference'"
:selected="selectedLetter"
:pagination-meta="{ recordCount: 0, page: 1, pageSize: 10, totalPage: 0 } as any"
@fetch="(value) => getLetterMappers(value.admissionType, value.search)"
@save="handleSaveLetter"
/>
</template>