Merge branch 'dev' into feat/move-kai-ui-to-sidebar-195

This commit is contained in:
Muhammad Hasyim Chaidir Ali
2025-12-04 11:53:23 +07:00
committed by GitHub
76 changed files with 2860 additions and 819 deletions

No files matched your search

+66 -40
View File
@@ -3,14 +3,14 @@
import { Button } from '~/components/pub/ui/button'
import AppEncounterEntryForm from '~/components/app/encounter/entry-form.vue'
import AppViewPatient from '~/components/app/patient/view-patient.vue'
import AppViewHistory from '~/components/app/sep/view-history.vue'
// Helpers
import { refDebounced } from '@vueuse/core'
// Handlers
import { getDetail as getDoctorDetail } from '~/services/doctor.service'
import { useEncounterEntry } from '~/handlers/encounter-entry.handler'
import { genDoctor, type Doctor } from '~/models/doctor'
import { useIntegrationSepEntry } from '~/handlers/integration-sep-entry.handler'
// Props
const props = defineProps<{
@@ -34,54 +34,33 @@ const {
isLoadingDetail,
formObjects,
openPatient,
isMemberValid,
isSepValid,
isCheckingSep,
isSaveDisabled,
isSaving,
isLoading,
patients,
selectedDoctor,
selectedPatient,
selectedPatientObject,
paginationMeta,
toNavigateSep,
getListPath,
handleInit,
loadEncounterDetail,
getFetchEncounterDetail,
handleSaveEncounter,
getPatientsList,
getPatientCurrent,
getPatientByIdentifierSearch,
getIsSubspecialist,
// getIsSubspecialist,
getDoctorInfo,
getValidateMember,
getValidateSepNumber,
handleFetchDoctors,
} = useEncounterEntry(props)
const { recSepId, openHistory, histories, getMonitoringHistoryMappers } = useIntegrationSepEntry()
const debouncedSepNumber = refDebounced(sepNumber, 500)
const selectedDoctor = ref<Doctor>(genDoctor())
provide('rec_select_id', recSelectId)
provide('table_data_loader', isLoading)
watch(debouncedSepNumber, async (newValue) => {
await getValidateSepNumber(newValue)
})
watch(
() => formObjects.value?.paymentType,
(newValue) => {
isSepValid.value = false
if (newValue !== 'jkn') {
sepNumber.value = ''
}
},
)
onMounted(async () => {
await handleInit()
if (props.id > 0) {
await loadEncounterDetail()
}
})
///// Functions
function handleSavePatient() {
@@ -116,12 +95,28 @@ async function handleEvent(menu: string, value?: any) {
}
toNavigateSep({
isService: 'false',
encounterId: props.id || null,
sourcePath: route.path,
resource: `${props.classCode}-${props.subClassCode}`,
...value,
})
} else if (menu === 'sep-number-changed') {
await getValidateSepNumber(String(value || ''))
const sepNumberText = String(value || '').trim()
if (sepNumberText.length > 5) {
await getValidateSepNumber(sepNumberText)
}
} else if (menu === 'member-changed') {
const memberText = String(value || '').trim()
if (memberText.length > 5) {
await getValidateMember(memberText)
}
} else if (menu === 'search-sep') {
const memberText = String(value?.cardNumber || '').trim()
if (memberText.length < 5) return
getMonitoringHistoryMappers(memberText).then(() => {
openHistory.value = true
})
return
} else if (menu === 'save') {
await handleSaveEncounter(value)
} else if (menu === 'cancel') {
@@ -129,13 +124,39 @@ async function handleEvent(menu: string, value?: any) {
}
}
async function getDoctorInfo(value: string) {
const resp = await getDoctorDetail(value, { includes: 'unit,specialist,subspecialist'})
if (resp.success) {
selectedDoctor.value = resp.body.data
// console.log(selectedDoctor.value)
provide('rec_select_id', recSelectId)
provide('rec_sep_id', recSepId)
provide('table_data_loader', isLoading)
watch(debouncedSepNumber, async (newValue) => {
await getValidateSepNumber(newValue)
})
watch(
() => formObjects.value?.paymentType,
(newValue) => {
isSepValid.value = false
if (newValue !== 'jkn') {
sepNumber.value = ''
}
},
)
watch(
() => props.id,
async (newId) => {
if (props.formType === 'edit' && newId > 0) {
await getFetchEncounterDetail()
}
},
)
onMounted(async () => {
await handleInit()
if (props.formType === 'edit' && props.id > 0) {
await getFetchEncounterDetail()
}
}
})
</script>
<template>
@@ -144,13 +165,15 @@ async function getDoctorInfo(value: string) {
name="i-lucide-user"
class="me-2"
/>
<span class="font-semibold">{{ props.formType }}</span>
<span class="font-semibold">{{ props.formType === 'add' ? 'Tambah' : 'Ubah' }}</span>
Kunjungan
</div>
<AppEncounterEntryForm
ref="formRef"
:mode="props.formType"
:is-loading="isLoadingDetail"
:is-member-valid="isMemberValid"
:is-sep-valid="isSepValid"
:is-checking-sep="isCheckingSep"
:payments="paymentsList"
@@ -165,7 +188,6 @@ async function getDoctorInfo(value: string) {
@event="handleEvent"
@fetch="handleFetch"
/>
<AppViewPatient
v-model:open="openPatient"
v-model:selected="selectedPatient"
@@ -184,7 +206,11 @@ async function getDoctorInfo(value: string) {
"
@save="handleSavePatient"
/>
<AppViewHistory
v-model:open="openHistory"
:is-action="true"
:histories="histories"
/>
<!-- Footer Actions -->
<div class="mt-6 flex justify-end gap-2 border-t border-t-slate-300 pt-4">
<Button
+15 -11
View File
@@ -34,8 +34,13 @@ const props = defineProps<{
const { setOpen } = useSidebar()
setOpen(true)
// Role reactivities
const { getActiveRole } = useUserStore()
// Main data
const data = ref([])
const dataFiltered = ref([])
const activeServicePosition = ref(getServicePosition(getActiveRole()))
const isLoading = reactive<DataTableLoader>({
summary: false,
isTableLoading: false,
@@ -87,29 +92,25 @@ const filter = ref<{
schema: {},
})
// Role reactivities
const { getActiveRole } = useUserStore()
const activeServicePosition = ref(getServicePosition(getActiveRole()))
provide('activeServicePosition', activeServicePosition)
watch(getActiveRole, (role? : string) => {
activeServicePosition.value = getServicePosition(role)
})
// Recrod reactivities
provide('activeServicePosition', activeServicePosition)
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
watch(getActiveRole, (role? : string) => {
activeServicePosition.value = getServicePosition(role)
})
watch(() => recAction.value, () => {
const basePath = `/${props.classCode}/encounter`
// console.log(`${basePath}/${recId.value}`, recAction.value)
// return
if (recAction.value === ActionEvents.showConfirmDelete) {
isRecordConfirmationOpen.value = true
} else if (recAction.value === ActionEvents.showCancel) {
isRecordCancelOpen.value = true
} else if (recAction.value === ActionEvents.showDetail) {
navigateTo(`${basePath}/${recId.value}`)
navigateTo(`${basePath}/${recId.value}/detail`)
} else if (recAction.value === ActionEvents.showEdit) {
navigateTo(`${basePath}/${recId.value}/edit`)
} else if (recAction.value === ActionEvents.showProcess) {
@@ -138,6 +139,7 @@ async function getPatientList() {
const result = await getEncounterList(params)
if (result.success) {
data.value = result.body?.data || []
dataFiltered.value = [...data.value]
}
} catch (error) {
console.error('Error fetching encounter list:', error)
@@ -240,6 +242,7 @@ function handleRemoveConfirmation() {
<template>
<CH.ContentHeader v-bind="hreaderPrep">
<FilterNav
:active-positon="activeServicePosition"
@onFilterClick="() => isFilterFormDialogOpen = true"
@onExportPdf="() => {}"
@onExportExcel="() => {}"
@@ -261,6 +264,7 @@ function handleRemoveConfirmation() {
<!-- Batal -->
<RecordConfirmation
v-if="canDelete"
v-model:open="isRecordCancelOpen"
custom-title="Batalkan Kunjungan"
custom-message="Apakah anda yakin ingin membatalkan kunjungan pasien berikut?"
+89 -14
View File
@@ -16,8 +16,24 @@ import { genEncounter, type Encounter } from '~/models/encounter'
// Handlers
import type { EncounterProps } from '~/handlers/encounter-init.handler'
import { getEncounterData } from '~/handlers/encounter-process.handler'
import { getMenuItems } from "~/handlers/encounter-init.handler"
import { getMenuItems } from '~/handlers/encounter-init.handler'
// PLASE ORDER BY TAB POSITION
import Status from '~/components/content/encounter/status.vue'
import AssesmentFunctionList from '~/components/content/soapi/entry.vue'
import EarlyMedicalAssesmentList from '~/components/content/soapi/entry.vue'
import EarlyMedicalRehabList from '~/components/content/soapi/entry.vue'
import DeviceOrder from '~/components/content/device-order/main.vue'
import Prescription from '~/components/content/prescription/main.vue'
import CpLabOrder from '~/components/content/cp-lab-order/main.vue'
import Radiology from '~/components/content/radiology-order/main.vue'
import Consultation from '~/components/content/consultation/list.vue'
import Cprj from '~/components/content/cprj/entry.vue'
import DocUploadList from '~/components/content/document-upload/list.vue'
import GeneralConsentList from '~/components/content/general-consent/entry.vue'
import ResumeList from '~/components/content/resume/list.vue'
import ControlLetterList from '~/components/content/control-letter/list.vue'
import InitialNursingStudy from '~/components/content/initial-nursing/entry.vue'
// App Components
import EncounterPatientInfo from '~/components/app/encounter/quick-info.vue'
import EncounterHistoryButtonMenu from '~/components/app/encounter/quick-shortcut.vue'
@@ -51,7 +67,8 @@ const isShowPatient = computed(() => data.value && data.value?.patient?.person)
const { setOpen } = useSidebar()
setOpen(false)
if (activePosition.value === 'none') { // if user position is none, redirect to home page
if (activePosition.value === 'none') {
// if user position is none, redirect to home page
router.push('/')
}
@@ -66,6 +83,45 @@ const protocolRows = [
action: '',
},
{
value: 'rehab-medical-assessment',
label: 'Pengkajian Awal Medis Rehabilitasi Medis',
component: EarlyMedicalRehabList,
props: { encounter: data, type: 'early-rehab', label: 'Pengkajian Awal Medis Rehabilitasi Medis' },
},
{
value: 'function-assessment',
label: 'Asesmen Fungsi',
component: AssesmentFunctionList,
props: { encounter: data, type: 'function', label: 'Asesmen Fungsi' },
},
{ value: 'therapy-protocol', label: 'Protokol Terapi' },
{ value: 'education-assessment', label: 'Asesmen Kebutuhan Edukasi' },
{ value: 'patient-note', label: 'CPRJ', component: Cprj, props: { encounter: data } },
{ value: 'consent', label: 'General Consent', component: GeneralConsentList, props: { encounter: data } },
{
value: 'initial-nursing-study',
label: 'Kajian Awal Keperawatan',
component: InitialNursingStudy,
props: { encounter: data },
},
{ value: 'prescription', label: 'Order Obat', component: Prescription, props: { encounter_id: data.value.id } },
{ value: 'device-order', label: 'Order Alkes', component: DeviceOrder, props: { encounter_id: data.value.id } },
{ value: 'device', label: 'Order Alkes' },
{ value: 'mcu-radiology', label: 'Order Radiologi', component: Radiology, props: { encounter_id: data.id } },
{ value: 'mcu-lab-cp', label: 'Order Lab PK', component: CpLabOrder, props: { encounter_id: data.id } },
{ value: 'mcu-lab-micro', label: 'Order Lab Mikro' },
{ value: 'mcu-lab-pa', label: 'Order Lab PA' },
{ value: 'medical-action', label: 'Order Ruang Tindakan' },
{ value: 'mcu-result', label: 'Hasil Penunjang' },
{ value: 'consultation', label: 'Konsultasi', component: Consultation, props: { encounter: data } },
{ value: 'resume', label: 'Resume', component: ResumeList, props: { encounter: data } },
{ value: 'control', label: 'Surat Kontrol', component: ControlLetterList, props: { encounter: data } },
{ value: 'screening', label: 'Skrinning MPP' },
{
value: 'supporting-document',
label: 'Upload Dokumen Pendukung',
component: DocUploadList,
props: { encounter: data },
number: '2',
tanggal: new Date().toISOString().substring(0, 10),
siklus: 'II',
@@ -103,13 +159,19 @@ function handleClick(type: string) {
}
function initMenus() {
menus.value = getMenuItems(id, props, user, {
encounter: data.value
} as any, {
protocolTheraphy: paginationMeta,
protocolChemotherapy: paginationMeta,
medicineProtocolChemotherapy: paginationMeta,
})
menus.value = getMenuItems(
id,
props,
user,
{
encounter: data.value,
} as any,
{
protocolTheraphy: paginationMeta,
protocolChemotherapy: paginationMeta,
medicineProtocolChemotherapy: paginationMeta,
},
)
}
async function getData() {
@@ -119,24 +181,37 @@ async function getData() {
<template>
<div class="w-full">
<div class="bg-white dark:bg-slate-800 p-4 2xl:p-5">
<div class="bg-white p-4 dark:bg-slate-800 2xl:p-5">
<div class="mb-4 flex">
<div>
<ContentNavBa label="Kembali" @click="handleClick" />
<ContentNavBa
label="Kembali"
@click="handleClick"
/>
</div>
<!-- <div class="ms-auto pe-3 pt-1 text-end text-xl 2xl:text-2xl font-semibold">
Pasien: {{ data.patient.person.name }} --- No. RM: {{ data.patient.number }}
</div> -->
</div>
<ContentSwitcher :active="1" :height="150">
<ContentSwitcher
:active="1"
:height="150"
>
<template v-slot:content1>
<EncounterPatientInfo v-if="isShowPatient" :data="data" />
<EncounterPatientInfo
v-if="isShowPatient"
:data="data"
/>
</template>
<template v-slot:content2>
<EncounterHistoryButtonMenu v-if="isShowPatient" />
</template>
</ContentSwitcher>
</div>
<SubMenu :data="menus" :initial-active-menu="activeMenu" @change-menu="activeMenu = $event" />
<SubMenu
:data="menus"
:initial-active-menu="activeMenu"
@change-menu="activeMenu = $event"
/>
</div>
</template>