Merge branch 'dev' into feat/kfr-kemoterapi-174
This commit is contained in:
No files matched your search
@@ -0,0 +1,596 @@
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
// Components
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
|
||||
// Models
|
||||
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
||||
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
|
||||
import { paymentTypes, sepRefTypeCodes, participantGroups } from '~/lib/constants.vclaim'
|
||||
|
||||
// Stores
|
||||
import { useUserStore } from '~/stores/user'
|
||||
|
||||
// Services
|
||||
import {
|
||||
getList as getSpecialistList,
|
||||
getValueTreeItems as getSpecialistTreeItems,
|
||||
} 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'
|
||||
|
||||
// Handlers
|
||||
import {
|
||||
patients,
|
||||
selectedPatient,
|
||||
selectedPatientObject,
|
||||
paginationMeta,
|
||||
getPatientsList,
|
||||
getPatientCurrent,
|
||||
getPatientByIdentifierSearch,
|
||||
} from '~/handlers/patient.handler'
|
||||
|
||||
export function useEncounterEntry(props: {
|
||||
id: number
|
||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
||||
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||
}) {
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const openPatient = ref(false)
|
||||
const isLoading = reactive<DataTableLoader>({
|
||||
isTableLoading: false,
|
||||
})
|
||||
const paymentsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const sepsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const participantGroupsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const specialistsTree = ref<TreeItem[]>([])
|
||||
const specialistsData = ref<any[]>([])
|
||||
const doctorsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const recSelectId = ref<number | null>(null)
|
||||
const isSaving = ref(false)
|
||||
const isLoadingDetail = ref(false)
|
||||
const encounterData = ref<any>(null)
|
||||
const formObjects = ref<any>({})
|
||||
const isSepValid = ref(false)
|
||||
const isCheckingSep = ref(false)
|
||||
const sepNumber = ref('')
|
||||
const vclaimReference = ref<any>(null)
|
||||
|
||||
const isEditMode = computed(() => props.id > 0)
|
||||
|
||||
const isSaveDisabled = computed(() => {
|
||||
return !selectedPatient.value || !selectedPatientObject.value || isSaving.value || isLoadingDetail.value
|
||||
})
|
||||
|
||||
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'
|
||||
}
|
||||
return '/encounter'
|
||||
}
|
||||
|
||||
function toKebabCase(str: string): string {
|
||||
return str.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
}
|
||||
|
||||
function toNavigateSep(values: any) {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (values['subSpecialistCode']) {
|
||||
const isSub = getIsSubspecialist(values['subSpecialistCode'], specialistsTree.value)
|
||||
if (!isSub) {
|
||||
values['specialistCode'] = values['subSpecialistCode']
|
||||
delete values['subSpecialistCode']
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(values).forEach((field) => {
|
||||
if (values[field]) {
|
||||
queryParams.append(toKebabCase(field), values[field])
|
||||
}
|
||||
})
|
||||
|
||||
navigateTo('/integration/bpjs-vclaim/sep/add' + `?${queryParams.toString()}`)
|
||||
}
|
||||
|
||||
function getIsSubspecialist(value: string, items: TreeItem[]): boolean {
|
||||
for (const item of items) {
|
||||
if (item.value === value) {
|
||||
return false
|
||||
}
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
if (child.value === value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function getSpecialistCodeFromId(id: number | null | undefined): string | null {
|
||||
if (!id) return null
|
||||
|
||||
if (encounterData.value?.specialist?.id === id) {
|
||||
return encounterData.value.specialist.code || null
|
||||
}
|
||||
|
||||
for (const specialist of specialistsData.value) {
|
||||
if (specialist.id === id) {
|
||||
return specialist.code || null
|
||||
}
|
||||
if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
|
||||
for (const subspecialist of specialist.subspecialists) {
|
||||
if (subspecialist.id === id) {
|
||||
return subspecialist.code || null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getSubspecialistCodeFromId(id: number | null | undefined): string | null {
|
||||
if (!id) return null
|
||||
|
||||
if (encounterData.value?.subspecialist?.id === id) {
|
||||
return encounterData.value.subspecialist.code || null
|
||||
}
|
||||
|
||||
for (const specialist of specialistsData.value) {
|
||||
if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
|
||||
for (const subspecialist of specialist.subspecialists) {
|
||||
if (subspecialist.id === id) {
|
||||
return subspecialist.code || null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getSpecialistIdsFromCode(code: string): { specialist_id: number | null; subspecialist_id: number | null } {
|
||||
if (!code) {
|
||||
return { specialist_id: null, subspecialist_id: null }
|
||||
}
|
||||
|
||||
const isSub = getIsSubspecialist(code, specialistsTree.value)
|
||||
|
||||
if (isSub) {
|
||||
for (const specialist of specialistsData.value) {
|
||||
if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
|
||||
for (const subspecialist of specialist.subspecialists) {
|
||||
if (subspecialist.code === code) {
|
||||
return {
|
||||
specialist_id: specialist.id ? Number(specialist.id) : null,
|
||||
subspecialist_id: subspecialist.id ? Number(subspecialist.id) : null,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const specialist of specialistsData.value) {
|
||||
if (specialist.code === code) {
|
||||
return {
|
||||
specialist_id: specialist.id ? Number(specialist.id) : null,
|
||||
subspecialist_id: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { specialist_id: null, subspecialist_id: null }
|
||||
}
|
||||
|
||||
async function getValidateSepNumber(sepNumberValue: string) {
|
||||
vclaimReference.value = null
|
||||
if (!sepNumberValue || sepNumberValue.trim() === '') {
|
||||
isSepValid.value = false
|
||||
isCheckingSep.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isSepValid.value = false
|
||||
isCheckingSep.value = true
|
||||
const result = await getSepList({ number: sepNumberValue.trim() })
|
||||
if (result.success && result.body?.response !== null) {
|
||||
const response = result.body?.response || {}
|
||||
if (Object.keys(response).length > 0) {
|
||||
formObjects.value.patientName = response.peserta?.nama || '-'
|
||||
formObjects.value.medicalRecordNumber = response.peserta?.noMr || '-'
|
||||
formObjects.value.cardNumber = response.peserta?.noKartu || '-'
|
||||
formObjects.value.registerDate = response.tglSep || null
|
||||
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,
|
||||
catatan: response.catatan || '',
|
||||
diagRujukan: response.diagnosa || '',
|
||||
tipeRujukan: response.tujuanKunj?.kode ?? '0',
|
||||
poliRujukan: response.poli || '',
|
||||
user: '',
|
||||
}
|
||||
}
|
||||
isSepValid.value = result.body?.metaData?.code === '200'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking SEP:', error)
|
||||
isSepValid.value = false
|
||||
} finally {
|
||||
isCheckingSep.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFetchSpecialists() {
|
||||
try {
|
||||
const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
|
||||
if (specialistsResult.success) {
|
||||
const specialists = specialistsResult.body?.data || []
|
||||
specialistsData.value = specialists
|
||||
specialistsTree.value = getSpecialistTreeItems(specialists)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching specialist-subspecialist tree:', error)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFetchDoctors(subSpecialistId: string | null = null) {
|
||||
try {
|
||||
const filterParams: any = { 'page-size': 100, includes: 'employee-Person' }
|
||||
|
||||
if (!subSpecialistId) {
|
||||
const doctors = await getDoctorValueLabelList(filterParams, true)
|
||||
doctorsList.value = doctors
|
||||
return
|
||||
}
|
||||
|
||||
const isSub = getIsSubspecialist(subSpecialistId, specialistsTree.value)
|
||||
|
||||
if (isSub) {
|
||||
filterParams['subspecialist-id'] = subSpecialistId
|
||||
} else {
|
||||
filterParams['specialist-id'] = subSpecialistId
|
||||
}
|
||||
|
||||
const doctors = await getDoctorValueLabelList(filterParams, true)
|
||||
doctorsList.value = doctors
|
||||
} catch (error) {
|
||||
console.error('Error fetching doctors:', error)
|
||||
doctorsList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function handleInit() {
|
||||
selectedPatientObject.value = null
|
||||
paymentsList.value = Object.keys(paymentTypes).map((item) => ({
|
||||
value: item.toString(),
|
||||
label: paymentTypes[item],
|
||||
})) as any
|
||||
sepsList.value = Object.keys(sepRefTypeCodes).map((item) => ({
|
||||
value: item.toString(),
|
||||
label: sepRefTypeCodes[item],
|
||||
})) as any
|
||||
participantGroupsList.value = Object.keys(participantGroups).map((item) => ({
|
||||
value: item.toString(),
|
||||
label: participantGroups[item],
|
||||
})) as any
|
||||
await handleFetchDoctors()
|
||||
await handleFetchSpecialists()
|
||||
if (route.query) {
|
||||
formObjects.value = { ...formObjects.value }
|
||||
const queries = route.query as any
|
||||
if (queries['sep-number']) {
|
||||
formObjects.value.sepNumber = queries['sep-number']
|
||||
formObjects.value.paymentType = 'jkn'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadEncounterDetail() {
|
||||
if (!isEditMode.value || props.id <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isLoadingDetail.value = true
|
||||
const result = await getEncounterDetail(props.id, {
|
||||
includes: 'patient,patient-person,specialist,subspecialist',
|
||||
})
|
||||
if (result.success && result.body?.data) {
|
||||
encounterData.value = result.body.data
|
||||
await mapEncounterToForm(encounterData.value)
|
||||
isLoadingDetail.value = false
|
||||
} else {
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: 'Gagal memuat data kunjungan',
|
||||
variant: 'destructive',
|
||||
})
|
||||
await navigateTo(getListPath())
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error loading encounter detail:', error)
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: error?.message || 'Gagal memuat data kunjungan',
|
||||
variant: 'destructive',
|
||||
})
|
||||
await navigateTo(getListPath())
|
||||
} finally {
|
||||
isLoadingDetail.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function mapEncounterToForm(encounter: any) {
|
||||
if (!encounter) return
|
||||
|
||||
if (encounter.patient) {
|
||||
selectedPatient.value = String(encounter.patient.id)
|
||||
selectedPatientObject.value = encounter.patient
|
||||
if (!encounter.patient.person) {
|
||||
await getPatientCurrent(selectedPatient.value)
|
||||
}
|
||||
}
|
||||
|
||||
const formData: any = {}
|
||||
if (encounter.patient?.person) {
|
||||
formData.patientName = encounter.patient.person.name || ''
|
||||
formData.nationalIdentity = encounter.patient.person.residentIdentityNumber || ''
|
||||
formData.medicalRecordNumber = encounter.patient.number || ''
|
||||
} else if (selectedPatientObject.value?.person) {
|
||||
formData.patientName = selectedPatientObject.value.person.name || ''
|
||||
formData.nationalIdentity = selectedPatientObject.value.person.residentIdentityNumber || ''
|
||||
formData.medicalRecordNumber = selectedPatientObject.value.number || ''
|
||||
}
|
||||
|
||||
const doctorId = encounter.appointment_doctor_id || encounter.responsible_doctor_id
|
||||
if (doctorId) {
|
||||
formData.doctorId = String(doctorId)
|
||||
}
|
||||
|
||||
if (encounter.subspecialist_id) {
|
||||
const subspecialistCode = getSubspecialistCodeFromId(encounter.subspecialist_id)
|
||||
if (subspecialistCode) {
|
||||
formData.subSpecialistId = subspecialistCode
|
||||
}
|
||||
} else if (encounter.specialist_id) {
|
||||
const specialistCode = getSpecialistCodeFromId(encounter.specialist_id)
|
||||
if (specialistCode) {
|
||||
formData.subSpecialistId = specialistCode
|
||||
}
|
||||
}
|
||||
|
||||
if (!formData.subSpecialistId) {
|
||||
if (encounter.subspecialist?.code) {
|
||||
formData.subSpecialistId = encounter.subspecialist.code
|
||||
} else if (encounter.specialist?.code) {
|
||||
formData.subSpecialistId = encounter.specialist.code
|
||||
}
|
||||
}
|
||||
|
||||
if (encounter.registeredAt) {
|
||||
const date = new Date(encounter.registeredAt)
|
||||
formData.registerDate = date.toISOString().split('T')[0]
|
||||
} else if (encounter.visitDate) {
|
||||
const date = new Date(encounter.visitDate)
|
||||
formData.registerDate = date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
if (encounter.paymentMethod_code) {
|
||||
if (encounter.paymentMethod_code === 'insurance') {
|
||||
formData.paymentType = 'jkn'
|
||||
} else {
|
||||
const validPaymentTypes = ['jkn', 'jkmm', 'spm', 'pks']
|
||||
if (validPaymentTypes.includes(encounter.paymentMethod_code)) {
|
||||
formData.paymentType = encounter.paymentMethod_code
|
||||
} else {
|
||||
formData.paymentType = 'spm'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
formData.paymentType = 'spm'
|
||||
}
|
||||
|
||||
formData.cardNumber = encounter.member_number || ''
|
||||
formData.sepNumber = encounter.ref_number || ''
|
||||
formObjects.value = formData
|
||||
|
||||
if (formData.sepNumber) {
|
||||
sepNumber.value = formData.sepNumber
|
||||
}
|
||||
if (formData.subSpecialistId) {
|
||||
await handleFetchDoctors(formData.subSpecialistId)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveEncounter(formValues: any) {
|
||||
if (!selectedPatient.value || !selectedPatientObject.value) {
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: 'Pasien harus dipilih terlebih dahulu',
|
||||
variant: 'destructive',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isSaving.value = true
|
||||
|
||||
const employeeId = userStore.user?.employee_id || userStore.user?.employee?.id || 0
|
||||
|
||||
const formatDate = (dateString: string): string => {
|
||||
if (!dateString) return ''
|
||||
const date = new Date(dateString)
|
||||
return date.toISOString()
|
||||
}
|
||||
|
||||
const { specialist_id, subspecialist_id } = getSpecialistIdsFromCode(formValues.subSpecialistId || '')
|
||||
|
||||
const patientId = formValues.patient_id || selectedPatientObject.value?.id || Number(selectedPatient.value)
|
||||
|
||||
const registeredAtValue = formValues.registeredAt || formValues.registerDate || ''
|
||||
const visitDateValue = formValues.visitDate || formValues.registeredAt || formValues.registerDate || ''
|
||||
const memberNumber = formValues.member_number ?? formValues.cardNumber ?? formValues.memberNumber ?? null
|
||||
const refNumber = formValues.ref_number ?? formValues.sepNumber ?? formValues.refNumber ?? null
|
||||
|
||||
let paymentMethodCode = formValues.paymentMethod_code ?? null
|
||||
if (!paymentMethodCode) {
|
||||
if (formValues.paymentType === 'jkn' || formValues.paymentType === 'jkmm') {
|
||||
paymentMethodCode = 'insurance'
|
||||
} else if (formValues.paymentType === 'spm') {
|
||||
paymentMethodCode = 'cash'
|
||||
} else if (formValues.paymentType === 'pks') {
|
||||
paymentMethodCode = 'membership'
|
||||
} else {
|
||||
paymentMethodCode = 'cash'
|
||||
}
|
||||
}
|
||||
|
||||
const payload: any = {
|
||||
patient_id: patientId,
|
||||
appointment_doctor_code: formValues.doctorId || null,
|
||||
class_code: props.classCode || '',
|
||||
subClass_code: props.subClassCode || '',
|
||||
infra_id: formValues.infra_id ?? null,
|
||||
unit_code: userStore?.user?.unit_code ?? null,
|
||||
refSource_name: formValues.refSource_name ?? 'RSSA',
|
||||
refTypeCode: formValues.paymentType === 'jkn' ? 'bpjs' : '',
|
||||
vclaimReference: vclaimReference.value ?? null,
|
||||
paymentType: formValues.paymentType,
|
||||
registeredAt: formatDate(registeredAtValue),
|
||||
visitDate: formatDate(visitDateValue),
|
||||
}
|
||||
|
||||
if (props.classCode !== 'inpatient') {
|
||||
delete payload.infra_id
|
||||
}
|
||||
if (employeeId && employeeId > 0) {
|
||||
payload.adm_employee_id = employeeId
|
||||
}
|
||||
if (specialist_id) {
|
||||
payload.specialist_id = specialist_id
|
||||
}
|
||||
if (subspecialist_id) {
|
||||
payload.subspecialist_id = subspecialist_id
|
||||
}
|
||||
if (paymentMethodCode) {
|
||||
payload.paymentMethod_code = paymentMethodCode
|
||||
}
|
||||
|
||||
if (paymentMethodCode === 'insurance') {
|
||||
payload.insuranceCompany_id = formValues.insuranceCompany_id ?? null
|
||||
if (memberNumber) payload.member_number = memberNumber
|
||||
if (refNumber) payload.ref_number = refNumber
|
||||
if (formValues.refTypeCode) payload.refTypeCode = formValues.refTypeCode
|
||||
if (formValues.vclaimReference) payload.vclaimReference = formValues.vclaimReference
|
||||
} else {
|
||||
if (paymentMethodCode === 'membership' && memberNumber) {
|
||||
payload.member_number = memberNumber
|
||||
}
|
||||
if (refNumber) {
|
||||
payload.ref_number = refNumber
|
||||
}
|
||||
}
|
||||
|
||||
if (props.classCode === 'ambulatory') {
|
||||
payload.visitMode_code = 'adm'
|
||||
payload.allocatedVisitCount = 0
|
||||
}
|
||||
|
||||
let result
|
||||
if (isEditMode.value) {
|
||||
result = await updateEncounter(props.id, payload)
|
||||
} else {
|
||||
result = await createEncounter(payload)
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
toast({
|
||||
title: 'Berhasil',
|
||||
description: isEditMode.value ? 'Kunjungan berhasil diperbarui' : 'Kunjungan berhasil dibuat',
|
||||
variant: 'default',
|
||||
})
|
||||
await navigateTo(getListPath())
|
||||
} else {
|
||||
const errorMessage =
|
||||
result.body?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan')
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: errorMessage,
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error saving encounter:', error)
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: error?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan'),
|
||||
variant: 'destructive',
|
||||
})
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
patients,
|
||||
paymentsList,
|
||||
sepsList,
|
||||
sepNumber,
|
||||
participantGroupsList,
|
||||
specialistsTree,
|
||||
doctorsList,
|
||||
recSelectId,
|
||||
isSaving,
|
||||
isLoadingDetail,
|
||||
encounterData,
|
||||
formObjects,
|
||||
openPatient,
|
||||
isSepValid,
|
||||
isCheckingSep,
|
||||
isEditMode,
|
||||
isSaveDisabled,
|
||||
isLoading,
|
||||
selectedPatient,
|
||||
selectedPatientObject,
|
||||
paginationMeta,
|
||||
loadEncounterDetail,
|
||||
mapEncounterToForm,
|
||||
toKebabCase,
|
||||
toNavigateSep,
|
||||
getListPath,
|
||||
getSpecialistCodeFromId,
|
||||
getSubspecialistCodeFromId,
|
||||
getIsSubspecialist,
|
||||
getSpecialistIdsFromCode,
|
||||
getPatientsList,
|
||||
getPatientCurrent,
|
||||
getPatientByIdentifierSearch,
|
||||
getValidateSepNumber,
|
||||
handleFetchSpecialists,
|
||||
handleFetchDoctors,
|
||||
handleInit,
|
||||
handleSaveEncounter,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
import { isValidDate } from '~/lib/date'
|
||||
import { medicalRoles } from '~/const/common/role'
|
||||
|
||||
export interface EncounterItem {
|
||||
id: string
|
||||
title: string
|
||||
classCode?: string[]
|
||||
unit?: string
|
||||
afterId?: string
|
||||
component?: any
|
||||
props?: Record<string, any>
|
||||
}
|
||||
|
||||
export interface EncounterProps {
|
||||
classCode: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
||||
subClassCode: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||
}
|
||||
|
||||
export interface EncounterListData {
|
||||
encounter?: any
|
||||
status?: any
|
||||
medicalAssessment?: any
|
||||
medicalAssessmentRehab: any
|
||||
functionAssessment?: any
|
||||
protocolTheraphy?: any
|
||||
protocolChemotherapy?: any
|
||||
medicineProtocolChemotherapy?: any
|
||||
consultation?: any
|
||||
letterOfControl?: any
|
||||
}
|
||||
|
||||
const StatusAsync = defineAsyncComponent(() => import('~/components/content/encounter/status.vue'))
|
||||
const AssesmentFunctionListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||
const EarlyMedicalAssesmentListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||
const EarlyMedicalRehabListAsync = defineAsyncComponent(() => import('~/components/content/soapi/entry.vue'))
|
||||
const ChemoProtocolListAsync = defineAsyncComponent(() => import('~/components/app/chemotherapy/list.protocol.vue'))
|
||||
const ChemoMedicineProtocolListAsync = defineAsyncComponent(
|
||||
() => import('~/components/app/chemotherapy/list.medicine.vue'),
|
||||
)
|
||||
const DeviceOrderAsync = defineAsyncComponent(() => import('~/components/content/device-order/main.vue'))
|
||||
const PrescriptionAsync = defineAsyncComponent(() => import('~/components/content/prescription/main.vue'))
|
||||
const CpLabOrderAsync = defineAsyncComponent(() => import('~/components/content/cp-lab-order/main.vue'))
|
||||
const CprjAsync = defineAsyncComponent(() => import('~/components/content/cprj/entry.vue'))
|
||||
const RadiologyAsync = defineAsyncComponent(() => import('~/components/content/radiology-order/main.vue'))
|
||||
const ConsultationAsync = defineAsyncComponent(() => import('~/components/content/consultation/list.vue'))
|
||||
const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/list.vue'))
|
||||
const GeneralConsentListAsync = defineAsyncComponent(() => import('~/components/content/general-consent/entry.vue'))
|
||||
const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/list.vue'))
|
||||
const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/list.vue'))
|
||||
|
||||
const defaultKeys: Record<string, any> = {
|
||||
status: {
|
||||
id: 'status',
|
||||
title: 'Status Masuk/Keluar',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
earlyMedicalAssessment: {
|
||||
id: 'early-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
rehabMedicalAssessment: {
|
||||
id: 'rehab-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis Rehabilitasi Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'rehab',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
functionAssessment: {
|
||||
id: 'function-assessment',
|
||||
title: 'Asesmen Fungsi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'rehab',
|
||||
afterId: 'rehab-medical-assessment',
|
||||
},
|
||||
therapyProtocol: {
|
||||
id: 'therapy-protocol',
|
||||
classCode: ['ambulatory'],
|
||||
title: 'Protokol Terapi',
|
||||
unit: 'rehab',
|
||||
afterId: 'function-assessment',
|
||||
},
|
||||
chemotherapyProtocol: {
|
||||
id: 'chemotherapy-protocol',
|
||||
title: 'Protokol Kemoterapi',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'chemo',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
chemotherapyMedicine: {
|
||||
id: 'chemotherapy-medicine',
|
||||
title: 'Protokol Obat Kemoterapi',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'chemo',
|
||||
afterId: 'chemotherapy-protocol',
|
||||
},
|
||||
educationAssessment: {
|
||||
id: 'education-assessment',
|
||||
title: 'Asesmen Kebutuhan Edukasi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
consent: {
|
||||
id: 'consent',
|
||||
title: 'General Consent',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
patientNote: {
|
||||
id: 'patient-note',
|
||||
title: 'CPRJ',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
prescription: {
|
||||
id: 'prescription',
|
||||
title: 'Order Obat',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
device: {
|
||||
id: 'device-order',
|
||||
title: 'Order Alkes',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuRadiology: {
|
||||
id: 'mcu-radiology',
|
||||
title: 'Order Radiologi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabPc: {
|
||||
id: 'mcu-lab-pc',
|
||||
title: 'Order Lab PK',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabMicro: {
|
||||
id: 'mcu-lab-micro',
|
||||
title: 'Order Lab Mikro',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuLabPa: {
|
||||
id: 'mcu-lab-pa',
|
||||
title: 'Order Lab PA',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
medicalAction: {
|
||||
id: 'medical-action',
|
||||
title: 'Order Ruang Tindakan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
mcuResult: {
|
||||
id: 'mcu-result',
|
||||
title: 'Hasil Penunjang',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
consultation: {
|
||||
id: 'consultation',
|
||||
title: 'Konsultasi',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
resume: {
|
||||
id: 'resume',
|
||||
title: 'Resume',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
control: {
|
||||
id: 'control',
|
||||
title: 'Surat Kontrol',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
screening: {
|
||||
id: 'screening',
|
||||
title: 'Skrinning MPP',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
supportingDocument: {
|
||||
id: 'supporting-document',
|
||||
title: 'Upload Dokumen Pendukung',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
},
|
||||
priceList: {
|
||||
id: 'price-list',
|
||||
title: 'Tarif Tindakan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
}
|
||||
|
||||
export function getItemsByClassCode(classCode: string, items: EncounterItem[]) {
|
||||
return items.filter((item) => item.classCode?.includes(classCode))
|
||||
}
|
||||
|
||||
export function getItemsByUnit(unit: string, items: EncounterItem[]) {
|
||||
return items.filter((item) => item.unit === unit)
|
||||
}
|
||||
|
||||
export function getItemsByIds(ids: string[], items: EncounterItem[]) {
|
||||
return items.filter((item) => ids.includes(item.id))
|
||||
}
|
||||
|
||||
export function getIndexById(id: string, items: EncounterItem[]) {
|
||||
return items.findIndex((item) => item.id === id)
|
||||
}
|
||||
|
||||
export const getItemsAll = (classCode: string, unit: string, items: EncounterItem[]) => {
|
||||
const prevItems = [...items]
|
||||
let updateItems = getItemsByClassCode(classCode, prevItems)
|
||||
updateItems = getItemsByUnit(unit, updateItems)
|
||||
return updateItems
|
||||
}
|
||||
|
||||
export function insertItemByAfterId(id: string, items: EncounterItem[], newItem: EncounterItem) {
|
||||
const index = getIndexById(id, items)
|
||||
if (index > -1) {
|
||||
items.splice(index + 1, 0, newItem)
|
||||
}
|
||||
}
|
||||
|
||||
export function injectComponents(id: string | number, data: EncounterListData, meta: EncounterListData) {
|
||||
const currentKeys = { ...defaultKeys }
|
||||
if (currentKeys?.status) {
|
||||
currentKeys.status['component'] = StatusAsync
|
||||
currentKeys.status['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.earlyMedicalAssessment) {
|
||||
currentKeys.earlyMedicalAssessment['component'] = EarlyMedicalAssesmentListAsync
|
||||
currentKeys.earlyMedicalAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'early-medic',
|
||||
label: currentKeys.earlyMedicalAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.rehabMedicalAssessment) {
|
||||
currentKeys.rehabMedicalAssessment['component'] = EarlyMedicalRehabListAsync
|
||||
currentKeys.rehabMedicalAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'early-rehab',
|
||||
label: currentKeys.rehabMedicalAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.functionAssessment) {
|
||||
currentKeys.functionAssessment['component'] = AssesmentFunctionListAsync
|
||||
currentKeys.functionAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'function',
|
||||
label: currentKeys.functionAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.therapyProtocol) {
|
||||
// TODO: add component for therapyProtocol
|
||||
currentKeys.therapyProtocol['component'] = null
|
||||
currentKeys.therapyProtocol['props'] = {
|
||||
data: data?.encounter,
|
||||
paginationMeta: meta?.protocolTheraphy,
|
||||
}
|
||||
}
|
||||
if (currentKeys?.chemotherapyProtocol) {
|
||||
currentKeys.chemotherapyProtocol['component'] = ChemoProtocolListAsync
|
||||
currentKeys.chemotherapyProtocol['props'] = {
|
||||
data: data?.encounter,
|
||||
paginationMeta: meta?.protocolChemotherapy,
|
||||
}
|
||||
}
|
||||
if (currentKeys?.chemotherapyMedicine) {
|
||||
currentKeys.chemotherapyMedicine['component'] = ChemoMedicineProtocolListAsync
|
||||
currentKeys.chemotherapyMedicine['props'] = {
|
||||
data: data?.encounter,
|
||||
paginationMeta: meta?.medicineProtocolChemotherapy,
|
||||
}
|
||||
}
|
||||
if (currentKeys?.educationAssessment) {
|
||||
// TODO: add component for education assessment
|
||||
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?.patientNote) {
|
||||
currentKeys.patientNote['component'] = CprjAsync
|
||||
currentKeys.patientNote['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?.mcuRadiology) {
|
||||
currentKeys.mcuRadiology['component'] = RadiologyAsync
|
||||
currentKeys.mcuRadiology['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabPc) {
|
||||
currentKeys.mcuLabPc['component'] = CpLabOrderAsync
|
||||
currentKeys.mcuLabPc['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabMicro) {
|
||||
// TODO: add component for mcuLabMicro
|
||||
currentKeys.mcuLabMicro['component'] = null
|
||||
currentKeys.mcuLabMicro['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuLabPa) {
|
||||
// TODO: add component for mcuLabPa
|
||||
currentKeys.mcuLabPa['component'] = null
|
||||
currentKeys.mcuLabPa['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.medicalAction) {
|
||||
// TODO: add component for medicalAction
|
||||
currentKeys.medicalAction['component'] = null
|
||||
currentKeys.medicalAction['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.mcuResult) {
|
||||
// TODO: add component for mcuResult
|
||||
currentKeys.mcuResult['component'] = null
|
||||
currentKeys.mcuResult['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.consultation) {
|
||||
currentKeys.consultation['component'] = ConsultationAsync
|
||||
currentKeys.consultation['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.resume) {
|
||||
currentKeys.resume['component'] = ResumeListAsync
|
||||
currentKeys.resume['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.control) {
|
||||
currentKeys.control['component'] = ControlLetterListAsync
|
||||
currentKeys.control['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.screening) {
|
||||
// TODO: add component for screening
|
||||
currentKeys.screening['component'] = null
|
||||
currentKeys.screening['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.supportingDocument) {
|
||||
currentKeys.supportingDocument['component'] = DocUploadListAsync
|
||||
currentKeys.supportingDocument['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.priceList) {
|
||||
// TODO: add component for priceList
|
||||
currentKeys.priceList['component'] = null
|
||||
currentKeys.priceList['props'] = { encounter_id: id }
|
||||
}
|
||||
|
||||
return currentKeys
|
||||
}
|
||||
|
||||
export function mergeArrayAt<T>(arraysOne: T[], arraysTwo: T[] | T, deleteCount = 0): T[] {
|
||||
const prevItems = arraysOne.slice()
|
||||
if (!prevItems) return prevItems
|
||||
const nextItems = Array.isArray(arraysTwo) ? arraysTwo : [arraysTwo]
|
||||
if (nextItems.length === 0) return prevItems
|
||||
// determine insertion position using the first item's `id` if available
|
||||
const firstId = (nextItems[0] as any)?.afterId || (prevItems[0] as any)?.id
|
||||
let pos = prevItems.length
|
||||
if (typeof firstId === 'string') {
|
||||
const index = prevItems.findIndex((item: any) => item.id === firstId)
|
||||
pos = index < 0 ? Math.max(prevItems.length + index, 0) : Math.min(index, prevItems.length)
|
||||
}
|
||||
prevItems.splice(pos, deleteCount, ...nextItems)
|
||||
return prevItems
|
||||
}
|
||||
|
||||
// Function to map API response to Encounter structure
|
||||
export function mapResponseToEncounter(result: any): any {
|
||||
if (!result) return null
|
||||
|
||||
// Check if patient and patient.person exist (minimal validation)
|
||||
if (!result.patient || !result.patient.person) {
|
||||
return null
|
||||
}
|
||||
|
||||
const mapped: any = {
|
||||
id: result.id || 0,
|
||||
patient_id: result.patient_id || result.patient?.id || 0,
|
||||
patient: {
|
||||
id: result.patient?.id || 0,
|
||||
number: result.patient?.number || '',
|
||||
person: {
|
||||
id: result.patient?.person?.id || 0,
|
||||
name: result.patient?.person?.name || '',
|
||||
birthDate: result.patient?.person?.birthDate || null,
|
||||
gender_code: result.patient?.person?.gender_code || '',
|
||||
residentIdentityNumber: result.patient?.person?.residentIdentityNumber || null,
|
||||
frontTitle: result.patient?.person?.frontTitle || '',
|
||||
endTitle: result.patient?.person?.endTitle || '',
|
||||
addresses: result.patient?.person?.addresses || [],
|
||||
},
|
||||
},
|
||||
registeredAt: result.registeredAt || result.patient?.registeredAt || null,
|
||||
class_code: result.class_code || '',
|
||||
unit_id: result.unit_id || 0,
|
||||
unit: result.unit || null,
|
||||
specialist_id: result.specialist_id || null,
|
||||
subspecialist_id: result.subspecialist_id || null,
|
||||
visitDate: isValidDate(result.visitDate)
|
||||
? result.visitDate
|
||||
: result.registeredAt || result.patient?.registeredAt || null,
|
||||
adm_employee_id: result.adm_employee_id || 0,
|
||||
appointment_doctor_id: result.appointment_doctor_id || null,
|
||||
responsible_doctor_id: result.responsible_doctor_id || null,
|
||||
appointment_doctor: result.appointment_doctor || null,
|
||||
responsible_doctor: result.responsible_doctor || null,
|
||||
refSource_name: result.refSource_name || null,
|
||||
appointment_id: result.appointment_id || null,
|
||||
earlyEducation: result.earlyEducation || null,
|
||||
medicalDischargeEducation: result.medicalDischargeEducation || '',
|
||||
admDischargeEducation: result.admDischargeEducation || null,
|
||||
discharge_method_code: result.discharge_method_code || null,
|
||||
discharge_reason: result.dischargeReason || result.discharge_reason || null,
|
||||
discharge_date: result.discharge_date || null,
|
||||
status_code: result.status_code || '',
|
||||
// Payment related fields
|
||||
paymentMethod_code:
|
||||
result.paymentMethod_code && result.paymentMethod_code.trim() !== '' ? result.paymentMethod_code : null,
|
||||
trx_number: result.trx_number || null,
|
||||
member_number: result.member_number || null,
|
||||
ref_number: result.ref_number || null,
|
||||
}
|
||||
|
||||
return mapped
|
||||
}
|
||||
|
||||
export function getMenuItems(
|
||||
id: string | number,
|
||||
props: any,
|
||||
user: any,
|
||||
data: EncounterListData,
|
||||
meta: any,
|
||||
) {
|
||||
console.log(props)
|
||||
// const normalClassCode = props.classCode === 'ambulatory' ? 'outpatient' : props.classCode
|
||||
const normalClassCode = props.classCode === 'ambulatory' ? 'ambulatory' : props.classCode
|
||||
const currentKeys = injectComponents(id, data, meta)
|
||||
const defaultItems: EncounterItem[] = Object.values(currentKeys)
|
||||
const listItemsForOutpatientRehab = mergeArrayAt(
|
||||
getItemsAll('ambulatory', 'all', defaultItems),
|
||||
getItemsAll('ambulatory', 'rehab', defaultItems),
|
||||
)
|
||||
const listItemsForOutpatientChemo = mergeArrayAt(
|
||||
getItemsAll('ambulatory', 'all', defaultItems),
|
||||
getItemsAll('ambulatory', 'chemo', defaultItems),
|
||||
)
|
||||
const listItems: Record<string, Record<string, Record<string, any>>> = {
|
||||
'installation|ambulatory': {
|
||||
'unit|rehab': {
|
||||
items: listItemsForOutpatientRehab,
|
||||
roles: medicalRoles,
|
||||
},
|
||||
'unit|chemo': {
|
||||
items: listItemsForOutpatientChemo,
|
||||
roles: medicalRoles,
|
||||
},
|
||||
all: getItemsAll('ambulatory', 'all', defaultItems),
|
||||
},
|
||||
'installation|emergency': {
|
||||
all: getItemsAll('emergency', 'all', defaultItems),
|
||||
},
|
||||
'installation|inpatient': {
|
||||
all: getItemsAll('inpatient', 'all', defaultItems),
|
||||
},
|
||||
}
|
||||
const currentListItems = listItems[`installation|${normalClassCode}`]
|
||||
if (!currentListItems) return []
|
||||
const unitCode = user?.unit_code ? `unit|${user.unit_code}` : 'all'
|
||||
const currentUnitItems: any = currentListItems[`${unitCode}`]
|
||||
if (!currentUnitItems) return []
|
||||
let menus = []
|
||||
if (currentUnitItems.roles && currentUnitItems.roles?.includes(user.activeRole)) {
|
||||
menus = [...currentUnitItems.items]
|
||||
} else {
|
||||
menus = unitCode !== 'all' && currentUnitItems?.items ? [...currentUnitItems.items] : [...currentUnitItems]
|
||||
}
|
||||
return menus
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Services
|
||||
import { getDetail } from '~/services/encounter.service'
|
||||
|
||||
// Handlers
|
||||
import { mapResponseToEncounter } from '~/handlers/encounter-init.handler'
|
||||
|
||||
export async function getEncounterData(id: string | number) {
|
||||
let data = null
|
||||
try {
|
||||
const dataRes = await getDetail(id, {
|
||||
includes:
|
||||
'patient,patient-person,patient-person-addresses,unit,Appointment_Doctor,Appointment_Doctor-employee,Appointment_Doctor-employee-person,Responsible_Doctor,Responsible_Doctor-employee,Responsible_Doctor-employee-person',
|
||||
})
|
||||
const dataResBody = dataRes.body ?? null
|
||||
const result = dataResBody?.data ?? null
|
||||
|
||||
if (result) {
|
||||
const mappedData = mapResponseToEncounter(result)
|
||||
if (mappedData) {
|
||||
data = mappedData
|
||||
} else {
|
||||
data = null
|
||||
}
|
||||
} else {
|
||||
data = null
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching encounter data:', error)
|
||||
data = null
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
// Components
|
||||
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'
|
||||
|
||||
export function useIntegrationSepEntry() {
|
||||
const userStore = useUserStore()
|
||||
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 isLoadingPatient = ref(false)
|
||||
const specialistsTree = ref<TreeItem[]>([])
|
||||
const resourceType = ref('')
|
||||
const resourcePath = ref('')
|
||||
|
||||
/**
|
||||
* Map letter data to form fields for save-sep
|
||||
* Maps data from letters.value[0].information to selectedObjects and form values
|
||||
*/
|
||||
function mapLetterDataToForm(formValues: any): any {
|
||||
if (selectedAdmissionType.value === '3' || letters.value.length === 0) {
|
||||
return formValues
|
||||
}
|
||||
|
||||
const letterData = letters.value[0]
|
||||
const info = letterData.information || {}
|
||||
|
||||
// Map data to selectedObjects for form population
|
||||
if (info.cardNumber) {
|
||||
selectedObjects.value['cardNumber'] = info.cardNumber
|
||||
}
|
||||
if (info.medicalRecordNumber) {
|
||||
selectedObjects.value['medicalRecordNumber'] = info.medicalRecordNumber
|
||||
}
|
||||
if (info.patientPhone) {
|
||||
selectedObjects.value['phoneNumber'] = info.patientPhone
|
||||
}
|
||||
if (info.classLevel) {
|
||||
selectedObjects.value['classLevel'] = info.classLevel
|
||||
}
|
||||
|
||||
// Map data to formValues for makeSepData
|
||||
const mappedValues = { ...formValues }
|
||||
|
||||
// response.rujukan.peserta.noKartu → cardNumber (noKartu)
|
||||
if (info.cardNumber) {
|
||||
mappedValues.cardNumber = info.cardNumber
|
||||
}
|
||||
|
||||
// response.rujukan.tglKunjungan → referralLetterDate (rujukan.tglRujukan)
|
||||
if (letterData.plannedDate) {
|
||||
mappedValues.referralLetterDate = letterData.plannedDate
|
||||
}
|
||||
|
||||
// response.rujukan.noKunjungan → referralLetterNumber (rujukan.noRujukan)
|
||||
if (letterData.letterNumber) {
|
||||
mappedValues.referralLetterNumber = letterData.letterNumber
|
||||
}
|
||||
|
||||
// response.rujukan.provPerujuk.kode → fromClinic (rujukan.ppkRujukan)
|
||||
if (info.destination) {
|
||||
mappedValues.referralTo = info.destination
|
||||
}
|
||||
|
||||
// response.rujukan.poliRujukan.kode → polyCode
|
||||
if (info.poly) {
|
||||
mappedValues.polyCode = info.poly
|
||||
}
|
||||
|
||||
// response.asalFaskes → asalRujukan (1 = Faskes 1, 2 = Faskes RS)
|
||||
// Map facility to referralFrom (asalRujukan)
|
||||
if (info.facility) {
|
||||
mappedValues.referralFrom = info.facility
|
||||
}
|
||||
|
||||
// response.rujukan.diagnosa.kode → initialDiagnosis (diagAwal)
|
||||
if (info.diagnoses) {
|
||||
mappedValues.initialDiagnosis = info.diagnoses
|
||||
}
|
||||
|
||||
// response.rujukan.poliRujukan.kode → destinationClinic (poli.tujuan)
|
||||
if (info.poly) {
|
||||
mappedValues.destinationClinic = info.poly
|
||||
}
|
||||
|
||||
// response.rujukan.peserta.hakKelas.kode → classLevel (klsRawat.klsRawatHak)
|
||||
if (info.classLevel) {
|
||||
mappedValues.classLevel = info.classLevel
|
||||
}
|
||||
|
||||
// response.rujukan.peserta.mr.noMR → medicalRecordNumber (noMR)
|
||||
if (info.medicalRecordNumber) {
|
||||
mappedValues.medicalRecordNumber = info.medicalRecordNumber
|
||||
}
|
||||
|
||||
// response.rujukan.peserta.mr.noTelepon → phoneNumber (noTelp)
|
||||
if (info.patientPhone) {
|
||||
mappedValues.phoneNumber = info.patientPhone
|
||||
}
|
||||
|
||||
return mappedValues
|
||||
}
|
||||
|
||||
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 {
|
||||
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 || '',
|
||||
diagnose: 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 || '',
|
||||
identity: lettersRaw?.rujukan?.peserta?.nik || '',
|
||||
patientName: lettersRaw?.rujukan?.peserta?.nama || '',
|
||||
patientPhone: lettersRaw?.rujukan?.peserta?.mr?.noTelepon || '',
|
||||
medicalRecordNumber: lettersRaw?.rujukan?.peserta?.mr?.noMR || '',
|
||||
destination: lettersRaw?.rujukan?.provPerujuk?.kode || '',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
isLoadingPatient.value = true
|
||||
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 || ''
|
||||
}
|
||||
isLoadingPatient.value = false
|
||||
} catch (err) {
|
||||
console.error('Failed to load patient from query params:', err)
|
||||
isLoadingPatient.value = false
|
||||
}
|
||||
}
|
||||
|
||||
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 || '2',
|
||||
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') {
|
||||
if (isLoadingPatient.value) return
|
||||
const text = value.text
|
||||
const type = value.type
|
||||
const prevCardNumber = selectedObjects.value['cardNumber'] || ''
|
||||
const prevNationalIdentity = selectedObjects.value['nationalIdentity'] || ''
|
||||
if (type === 'indentity' && text !== prevNationalIdentity) {
|
||||
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(async () => {
|
||||
if (letters.value.length > 0) {
|
||||
const copyObjects = { ...selectedObjects.value }
|
||||
const letter = letters.value[0]
|
||||
selectedObjects.value = {}
|
||||
selectedLetter.value = letter.letterNumber
|
||||
isLetterReadonly.value = true
|
||||
if (letter.information || letter.clinic) {
|
||||
const poly = value.admissionType === '3' ? letter.clinic : letter.information?.poly
|
||||
if (poly) {
|
||||
const resultControl = await getControlLetterList({
|
||||
mode: 'by-schedule',
|
||||
controlDate: letter.plannedDate,
|
||||
controlType: selectedServiceType.value,
|
||||
polyCode: poly,
|
||||
})
|
||||
if (resultControl && resultControl.success && resultControl.body) {
|
||||
const resultData = resultControl.body?.response?.list || []
|
||||
const resultUnique = [...new Map(resultData.map((item: any) => [item.kodeDokter, item])).values()]
|
||||
const controlLetters = resultUnique.map((item: any) => ({
|
||||
value: item.kodeDokter ? String(item.kodeDokter) : '',
|
||||
label: `${item.kodeDokter} - ${item.namaDokter} - ${item.jadwalPraktek} (${item.kapasitas})`,
|
||||
}))
|
||||
doctors.value = controlLetters
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(async () => {
|
||||
selectedObjects.value = copyObjects
|
||||
selectedObjects.value['letterDate'] = letter.plannedDate
|
||||
selectedObjects.value['cardNumber'] = letter.information?.cardNumber || ''
|
||||
selectedObjects.value['nationalIdentity'] = letter.information?.identity || ''
|
||||
selectedObjects.value['medicalRecordNumber'] = letter.information?.medicalRecordNumber || ''
|
||||
selectedObjects.value['patientName'] = letter.information?.patientName || ''
|
||||
selectedObjects.value['phoneNumber'] = letter.information?.patientPhone || ''
|
||||
selectedObjects.value['facility'] = letter.information?.facility || ''
|
||||
selectedObjects.value['diagnose'] = letter.information?.diagnose || ''
|
||||
selectedObjects.value['serviceType'] = letter.information?.serviceType || ''
|
||||
selectedObjects.value['classLevel'] = letter.information?.classLevel || ''
|
||||
selectedObjects.value['poly'] = letter.information?.poly || ''
|
||||
selectedObjects.value['destination'] = letter.information?.destination || ''
|
||||
if (!!selectedObjects.value['diagnose']) {
|
||||
const diagnoseRes: any = await getDiagnoseLabelList({ diagnosa: selectedObjects.value['diagnose'] })
|
||||
diagnoses.value = diagnoseRes
|
||||
if (diagnoseRes && diagnoseRes.length > 0) {
|
||||
selectedObjects.value['diagnoseLabel'] = diagnoseRes[0].value
|
||||
}
|
||||
}
|
||||
}, 250)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
if (menu === 'open-letter') {
|
||||
openLetter.value = true
|
||||
return
|
||||
}
|
||||
if (menu === 'history-sep') {
|
||||
getMonitoringHistoryMappers().then(() => {
|
||||
openHistory.value = true
|
||||
})
|
||||
return
|
||||
}
|
||||
if (menu === 'sep-number-changed') {
|
||||
// Update sepNumber when it changes in form (only if different to prevent loop)
|
||||
}
|
||||
if (menu === 'back') {
|
||||
navigateTo('/integration/bpjs-vclaim/sep')
|
||||
}
|
||||
if (menu === 'save-sep') {
|
||||
isSaveLoading.value = true
|
||||
|
||||
// Map letter data to form if admissionType !== '3' and letters.value has data
|
||||
let mappedValues = value
|
||||
if (selectedAdmissionType.value !== '3') {
|
||||
if (letters.value.length > 0) {
|
||||
// Map data from letters.value to form values
|
||||
mappedValues = mapLetterDataToForm(value)
|
||||
} else {
|
||||
// Fallback: use getPatientExternalMappers if letters.value is empty
|
||||
// Get card number from form values or selectedObjects
|
||||
const cardNumberToSearch = value.cardNumber || selectedObjects.value['cardNumber'] || ''
|
||||
if (cardNumberToSearch && cardNumberToSearch !== '-') {
|
||||
await getPatientExternalMappers(cardNumberToSearch, 'by-card')
|
||||
// Update mappedValues with data from getPatientExternalMappers
|
||||
if (selectedObjects.value['cardNumber']) {
|
||||
mappedValues.cardNumber = selectedObjects.value['cardNumber']
|
||||
}
|
||||
if (selectedObjects.value['medicalRecordNumber']) {
|
||||
mappedValues.medicalRecordNumber = selectedObjects.value['medicalRecordNumber']
|
||||
}
|
||||
if (selectedObjects.value['phoneNumber']) {
|
||||
mappedValues.phoneNumber = selectedObjects.value['phoneNumber']
|
||||
}
|
||||
if (selectedObjects.value['classLevel']) {
|
||||
mappedValues.classLevel = selectedObjects.value['classLevel']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!value.destinationClinic) {
|
||||
mappedValues.destinationClinic = selectedObjects.value['destination'] || ''
|
||||
}
|
||||
if (!value.clinicExcecutive) {
|
||||
mappedValues.clinicExcecutive = 'no'
|
||||
}
|
||||
|
||||
mappedValues.userName = userStore.user?.user_name || ''
|
||||
|
||||
createSep(makeSepData(mappedValues))
|
||||
.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 (!!resourcePath.value) {
|
||||
navigateTo({ path: resourcePath.value, query: { 'sep-number': body?.response?.sep?.noSep || '-' } })
|
||||
return
|
||||
}
|
||||
navigateTo('/integration/bpjs-vclaim/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 || 2,
|
||||
})
|
||||
}
|
||||
if (menu === 'clinic-to') {
|
||||
facilitiesTo.value = await getHealthFacilityLabelList({
|
||||
healthcare: value,
|
||||
healthcareType: selectedServiceType.value || 2,
|
||||
})
|
||||
}
|
||||
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() {
|
||||
selectedServiceType.value = '2'
|
||||
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 || '2',
|
||||
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['source-path']) resourcePath.value = queries['source-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']
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
openPatient,
|
||||
openLetter,
|
||||
openHistory,
|
||||
selectedLetter,
|
||||
selectedObjects,
|
||||
selectedServiceType,
|
||||
selectedAdmissionType,
|
||||
histories,
|
||||
letters,
|
||||
doctors,
|
||||
diagnoses,
|
||||
facilitiesFrom,
|
||||
facilitiesTo,
|
||||
supportCodesList,
|
||||
serviceTypesList,
|
||||
registerMethodsList,
|
||||
accidentsList,
|
||||
purposeOfVisitsList,
|
||||
proceduresList,
|
||||
assessmentsList,
|
||||
provincesList,
|
||||
citiesList,
|
||||
districtsList,
|
||||
classLevelsList,
|
||||
classLevelUpgradesList,
|
||||
classPaySourcesList,
|
||||
isServiceHidden,
|
||||
isSaveLoading,
|
||||
isLetterReadonly,
|
||||
isLoadingPatient,
|
||||
specialistsTree,
|
||||
resourceType,
|
||||
resourcePath,
|
||||
patients,
|
||||
selectedPatient,
|
||||
paginationMeta,
|
||||
getMonitoringHistoryMappers,
|
||||
getLetterMappers,
|
||||
getPatientInternalMappers,
|
||||
getPatientExternalMappers,
|
||||
getPatientsList,
|
||||
getPatientByIdentifierSearch,
|
||||
handleSaveLetter,
|
||||
mapLetterDataToForm,
|
||||
handleSavePatient,
|
||||
handleEvent,
|
||||
handleFetch,
|
||||
handleFetchSpecialists,
|
||||
handleInit,
|
||||
}
|
||||
}
|
||||
|
||||
export default useIntegrationSepEntry
|
||||
@@ -0,0 +1,284 @@
|
||||
import { ref, reactive } from 'vue'
|
||||
// Components
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
// Types
|
||||
import type { Ref as VueRef } from 'vue'
|
||||
import type { DateRange } from 'radix-vue'
|
||||
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
import type { VclaimSepData } from '~/models/vclaim'
|
||||
// Libraries
|
||||
import { CalendarDate, getLocalTimeZone } from '@internationalized/date'
|
||||
import { getFormatDateId } from '~/lib/date'
|
||||
import { downloadCsv, downloadXls } from '~/lib/download'
|
||||
import { serviceTypes } from '~/lib/constants.vclaim'
|
||||
import { getList as geMonitoringVisitList } from '~/services/vclaim-monitoring-visit.service'
|
||||
import { remove as removeSepData, makeSepDataForRemove } from '~/services/vclaim-sep.service'
|
||||
|
||||
const headerKeys = [
|
||||
'letterDate',
|
||||
'letterNumber',
|
||||
'serviceType',
|
||||
'flow',
|
||||
'medicalRecordNumber',
|
||||
'patientName',
|
||||
'cardNumber',
|
||||
'controlLetterNumber',
|
||||
'controlLetterDate',
|
||||
'clinicDestination',
|
||||
'attendingDoctor',
|
||||
'diagnosis',
|
||||
'careClass',
|
||||
]
|
||||
|
||||
const headerLabels = [
|
||||
'Tanggal SEP',
|
||||
'No. SEP',
|
||||
'Jenis Pelayanan',
|
||||
'Alur',
|
||||
'No. Rekam Medis',
|
||||
'Nama Pasien',
|
||||
'No. Kartu BPJS',
|
||||
'No. Surat Kontrol',
|
||||
'Tgl Surat Kontrol',
|
||||
'Poli Tujuan',
|
||||
'Dokter Penanggung Jawab',
|
||||
'Diagnosa',
|
||||
'Kelas Perawatan',
|
||||
]
|
||||
|
||||
export function useIntegrationSepList() {
|
||||
const userStore = useUserStore()
|
||||
const today = new Date()
|
||||
const initCalDate = (d: Date) => new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
|
||||
|
||||
const recId = ref<number>(0)
|
||||
const recAction = ref<string>('')
|
||||
const recItem = ref<any>(null)
|
||||
|
||||
const data = ref<VclaimSepData[]>([])
|
||||
const dateSelection = ref({ start: initCalDate(today), end: initCalDate(today) }) as VueRef<DateRange>
|
||||
const dateRange = ref(`${getFormatDateId(today)} - ${getFormatDateId(today)}`)
|
||||
const serviceType = ref('2')
|
||||
const serviceTypesList = ref<any[]>([])
|
||||
const search = ref('')
|
||||
const open = ref(false)
|
||||
|
||||
const sepData = ref({
|
||||
sepNumber: '',
|
||||
cardNumber: '',
|
||||
patientName: '',
|
||||
})
|
||||
|
||||
const refSearchNav: RefSearchNav = {
|
||||
onClick: () => {},
|
||||
onInput: (_val: string) => {},
|
||||
onClear: () => {},
|
||||
}
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: 'Daftar SEP Prosedur',
|
||||
icon: 'i-lucide-panel-bottom',
|
||||
addNav: {
|
||||
label: 'Tambah',
|
||||
onClick: () => {
|
||||
navigateTo('/integration/bpjs-vclaim/sep/add')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const paginationMeta = reactive<PaginationMeta>({
|
||||
recordCount: 0,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 5,
|
||||
hasNext: false,
|
||||
hasPrev: false,
|
||||
})
|
||||
|
||||
const isLoading = reactive<DataTableLoader>({
|
||||
isTableLoading: false,
|
||||
})
|
||||
|
||||
const getDateFilter = () => {
|
||||
let dateFilter = ''
|
||||
const isTimeLocal = true
|
||||
const dateFirst =
|
||||
dateSelection.value && dateSelection.value.start
|
||||
? dateSelection.value.start.toDate(getLocalTimeZone())
|
||||
: new Date()
|
||||
if (isTimeLocal && dateSelection.value && dateSelection.value.end) {
|
||||
const { year, month, day } = dateSelection.value.end
|
||||
dateFilter = `${year}-${month}-${day}`
|
||||
} else {
|
||||
dateFilter = dateFirst.toISOString().substring(0, 10)
|
||||
}
|
||||
return dateFilter
|
||||
}
|
||||
|
||||
const getMonitoringVisitMappers = async () => {
|
||||
isLoading.dataListLoading = true
|
||||
data.value = []
|
||||
const dateFilter = getDateFilter()
|
||||
const result = await geMonitoringVisitList({
|
||||
date: dateFilter || '',
|
||||
serviceType: serviceType.value,
|
||||
})
|
||||
|
||||
if (result && result.success && result.body) {
|
||||
const visitsRaw = result.body?.response?.sep || []
|
||||
if (!visitsRaw) {
|
||||
isLoading.dataListLoading = false
|
||||
return
|
||||
}
|
||||
visitsRaw.forEach((result: any) => {
|
||||
let st = result.jnsPelayanan || '-'
|
||||
if (st === 'R.Inap') st = 'Rawat Inap'
|
||||
else if (st === '1' || st === 'R.Jalan') st = 'Rawat Jalan'
|
||||
|
||||
data.value.push({
|
||||
letterDate: result.tglSep || '-',
|
||||
letterNumber: result.noSep || '-',
|
||||
serviceType: st,
|
||||
flow: '-',
|
||||
medicalRecordNumber: '-',
|
||||
patientName: result.nama || '-',
|
||||
cardNumber: result.noKartu || '-',
|
||||
controlLetterNumber: result.noRujukan || '-',
|
||||
controlLetterDate: result.tglPlgSep || '-',
|
||||
clinicDestination: result.poli || '-',
|
||||
attendingDoctor: '-',
|
||||
diagnosis: result.diagnosa || '-',
|
||||
careClass: result.kelasRawat || '-',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
isLoading.dataListLoading = false
|
||||
}
|
||||
|
||||
const getSepList = async () => {
|
||||
await getMonitoringVisitMappers()
|
||||
}
|
||||
|
||||
const setServiceTypes = () => {
|
||||
serviceTypesList.value = Object.keys(serviceTypes).map((item) => ({
|
||||
value: item.toString(),
|
||||
label: serviceTypes[item],
|
||||
})) as any
|
||||
}
|
||||
|
||||
const setDateRange = () => {
|
||||
const startCal = dateSelection.value.start
|
||||
const endCal = dateSelection.value.end
|
||||
const s = startCal ? startCal.toDate(getLocalTimeZone()) : today
|
||||
const e = endCal ? endCal.toDate(getLocalTimeZone()) : today
|
||||
dateRange.value = `${getFormatDateId(s)} - ${getFormatDateId(e)}`
|
||||
}
|
||||
|
||||
const handleExportCsv = () => {
|
||||
if (!data.value || data.value.length === 0) {
|
||||
toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
|
||||
return
|
||||
}
|
||||
|
||||
const yyyy = today.getFullYear()
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const dd = String(today.getDate()).padStart(2, '0')
|
||||
const dateStr = `${yyyy}-${mm}-${dd}`
|
||||
const filename = `file-sep-${dateStr}.csv`
|
||||
downloadCsv(headerKeys, headerLabels, data.value, filename, ',', true)
|
||||
}
|
||||
|
||||
const handleExportExcel = async () => {
|
||||
if (!data.value || data.value.length === 0) {
|
||||
toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
|
||||
return
|
||||
}
|
||||
|
||||
const yyyy = today.getFullYear()
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const dd = String(today.getDate()).padStart(2, '0')
|
||||
const dateStr = `${yyyy}-${mm}-${dd}`
|
||||
const filename = `file-sep-${dateStr}.xlsx`
|
||||
try {
|
||||
await downloadXls(headerKeys, headerLabels, data.value, filename, 'SEP Data')
|
||||
} catch (err: any) {
|
||||
console.error('exportExcel error', err)
|
||||
toast({ title: 'Gagal', description: err?.message || 'Gagal mengekspor data ke Excel', variant: 'destructive' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleRowSelected = (row: any) => {
|
||||
if (!row) return
|
||||
sepData.value.sepNumber = row.letterNumber || ''
|
||||
sepData.value.cardNumber = row.cardNumber || ''
|
||||
sepData.value.patientName = row.patientName || ''
|
||||
recItem.value = row
|
||||
recId.value = (row && (row.id || row.recId)) || 0
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('pageChange', page)
|
||||
}
|
||||
|
||||
const handleRemove = async () => {
|
||||
try {
|
||||
const result = await removeSepData(
|
||||
makeSepDataForRemove({ ...sepData.value, userName: userStore.user?.user_name }),
|
||||
)
|
||||
const backendMessage = result?.body?.message || result?.message || null
|
||||
const backendStatus = result?.body?.status || result?.status || null
|
||||
|
||||
if (
|
||||
backendMessage === 'success' ||
|
||||
(backendStatus === 'error' && backendMessage === 'Decrypt failed: illegal base64 data at input byte 16')
|
||||
) {
|
||||
await getSepList()
|
||||
toast({ title: 'Berhasil', description: backendMessage || 'Data berhasil dihapus', variant: 'default' })
|
||||
} else {
|
||||
toast({ title: 'Gagal', description: backendMessage || 'Gagal menghapus data', variant: 'destructive' })
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('handleRemove error', err)
|
||||
toast({
|
||||
title: 'Gagal',
|
||||
description: err?.message || 'Terjadi kesalahan saat menghapus data',
|
||||
variant: 'destructive',
|
||||
})
|
||||
} finally {
|
||||
recId.value = 0
|
||||
recAction.value = ''
|
||||
open.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
data,
|
||||
dateSelection,
|
||||
dateRange,
|
||||
serviceType,
|
||||
serviceTypesList,
|
||||
search,
|
||||
open,
|
||||
sepData,
|
||||
headerPrep,
|
||||
refSearchNav,
|
||||
paginationMeta,
|
||||
isLoading,
|
||||
getSepList,
|
||||
setServiceTypes,
|
||||
setDateRange,
|
||||
handleExportCsv,
|
||||
handleExportExcel,
|
||||
handleRowSelected,
|
||||
handlePageChange,
|
||||
handleRemove,
|
||||
}
|
||||
}
|
||||
|
||||
export default useIntegrationSepList
|
||||
Reference in New Issue
Block a user