fix: improve role classification logic and future scalability

This commit is contained in:
riefive
2025-11-19 14:50:35 +07:00
parent 3e4931cda2
commit 3ac1ec3a02
4 changed files with 338 additions and 3 deletions
+6 -1
View File
@@ -21,6 +21,8 @@ import Consultation from '~/components/content/consultation/list.vue'
import ProtocolList from '~/components/app/chemotherapy/list.protocol.vue'
import MedicineProtocolList from '~/components/app/chemotherapy/list.medicine.vue'
import { listItems } from '~/handlers/encounter-process.handler'
const route = useRoute()
const router = useRouter()
@@ -30,12 +32,15 @@ const props = defineProps<{
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
}>()
const { getActiveRole } = useUserStore()
const { user, getActiveRole } = useUserStore()
const activeRole = getActiveRole()
const activePosition = ref(getPositionAs(activeRole))
const tabs = ref([] as any)
const currentDisplay = ref(props.display ?? 'tab')
console.log(JSON.stringify(user, null, 4))
console.log(listItems)
// activeTab selalu sinkron dengan query param
const activeMenu = computed({
get: () => (route.query?.menu && typeof route.query.menu === 'string' ? route.query.menu : 'status'),
@@ -0,0 +1,94 @@
<script setup lang="ts">
//
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getDetail } from '~/services/encounter.service'
//
import type { TabItem } from '~/components/pub/my-ui/comp-tab/type'
import CompTab from '~/components/pub/my-ui/comp-tab/comp-tab.vue'
// 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 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 ControlLetterList from '~/components/content/control-letter/list.vue'
const route = useRoute()
const router = useRouter()
// activeTab selalu sinkron dengan query param
const activeTab = computed({
get: () => (route.query?.tab && typeof route.query.tab === 'string' ? route.query.tab : 'status'),
set: (val: string) => {
router.replace({ path: route.path, query: { tab: val } })
},
})
const id = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const dataRes = await getDetail(id, {
includes:
'patient,patient-person,patient-person-addresses,unit,Appointment_Doctor,Appointment_Doctor-employee,Appointment_Doctor-employee-person',
})
const dataResBody = dataRes.body ?? null
const data = dataResBody?.data ?? null
const tabs: TabItem[] = [
{ value: 'status', label: 'Status Masuk/Keluar', component: Status, props: { encounter: data } },
{
value: 'early-medical-assessment',
label: 'Pengkajian Awal Medis',
component: EarlyMedicalAssesmentList,
props: { encounter: data, type: 'early-medic', label: 'Pengkajian Awal Medis' },
},
{
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: 'consent', label: 'General Consent' },
{ value: 'patient-note', label: 'CPRJ' },
{ value: 'prescription', label: 'Order Obat', component: Prescription, props: { encounter_id: data.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' },
{ value: 'control', label: 'Surat Kontrol', component: ControlLetterList, props: { encounter: data } },
{ value: 'screening', label: 'Skrinning MPP' },
{ value: 'supporting-document', label: 'Upload Dokumen Pendukung' },
]
</script>
<template>
<div class="w-full">
<div class="mb-4">
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" />
</div>
<AppEncounterQuickInfo :data="data" />
<CompTab
:data="tabs"
:initial-active-tab="activeTab"
@change-tab="activeTab = $event"
/>
</div>
</template>
+236
View File
@@ -0,0 +1,236 @@
import { medicalPositions } from "~/lib/roles"
export interface EncounterItem {
id: string
title: string
classCode?: string[]
unit?: string,
afterId?: string
}
export const defaultItems: EncounterItem[] = [
{
id: 'status',
title: 'Status Masuk/Keluar',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'early-medical-assessment',
title: 'Pengkajian Awal Medis',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'rehab-medical-assessment',
title: 'Pengkajian Awal Medis Rehabilitasi Medis',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'rehab',
afterId: 'early-medical-assessment',
},
{
id: 'function-assessment',
title: 'Asesmen Fungsi',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'rehab',
afterId: 'rehab-medical-assessment',
},
{
id: 'therapy-protocol',
classCode: ['ambulatory'],
title: 'Protokol Terapi',
unit: 'rehab',
afterId: 'function-assessment',
},
{
id: 'chemotherapy-protocol',
title: 'Protokol Kemoterapi',
classCode: ['ambulatory'],
unit: 'chemo',
},
{
id: 'chemotherapy-medicine',
title: 'Protokol Obat Kemoterapi',
classCode: ['ambulatory'],
unit: 'chemo',
},
{
id: 'report',
title: 'Laporan Tindakan',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'patient-note',
title: 'CPRJ',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'education-assessment',
title: 'Asesmen Kebutuhan Edukasi',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'consent',
title: 'General Consent',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'prescription',
title: 'Order Obat',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'device',
title: 'Order Alkes',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'mcu-radiology',
title: 'Order Radiologi',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'mcu-lab-pc',
title: 'Order Lab PK',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'mcu-lab-micro',
title: 'Order Lab Mikro',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'mcu-lab-pa',
title: 'Order Lab PA',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'medical-action',
title: 'Order Ruang Tindakan',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'mcu-result',
title: 'Hasil Penunjang',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'consultation',
title: 'Konsultasi',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'resume',
title: 'Resume',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'control',
title: 'Surat Kontrol',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'screening',
title: 'Skrinning MPP',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
{
id: 'supporting-document',
title: 'Upload Dokumen Pendukung',
classCode: ['ambulatory'],
unit: 'rehab',
},
{
id: 'price-list',
title: 'Tarif Tindakan',
classCode: ['ambulatory', 'emergency', 'inpatient'],
unit: 'all',
},
]
const getItemsByClassCode = (classCode: string, items: EncounterItem[]) => {
return items.filter((item) => item.classCode?.includes(classCode))
}
const getItemsByUnit = (unit: string, items: EncounterItem[]) => {
return items.filter((item) => item.unit === unit)
}
const getItemsByIds = (ids: string[], items: EncounterItem[]) => {
return items.filter((item) => ids.includes(item.id))
}
const getIndexById = (id: string, items: EncounterItem[]) => {
return items.findIndex((item) => item.id === id)
}
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 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
}
export const getItemsAll = (classCode: string, unit: string, items: EncounterItem[]) => {
const prevItems = [...items]
let updateItems = getItemsByClassCode(classCode, prevItems)
updateItems = getItemsByUnit(unit, updateItems)
return updateItems
}
const listItemsForOutpatientRehab = mergeArrayAt(
getItemsAll('ambulatory', 'all', defaultItems),
getItemsByIds(['rehab-medical-assessment', 'function-assessment', 'therapy-protocol'], defaultItems),
)
export const listItems = {
'installation|outpatient': {
'unit|rehab': {
'items': listItemsForOutpatientRehab,
'roles': medicalPositions,
},
'all': getItemsAll('ambulatory', 'all', defaultItems),
},
'installation|emergency': {
'all': getItemsAll('emergency', 'all', defaultItems),
},
'installation|inpatient': {
'all': getItemsAll('inpatient', 'all', defaultItems),
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
const standartRoles = ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng']
export const medicalPositions = ['emp|doc', 'emp|nur', 'emp|nut', 'emp|mid', 'emp|lab', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng']
const verificatorRole = 'verificator'
export function getPositionAs(roleAccess: string): string {
if (roleAccess.includes('|')) {
if (standartRoles.includes(roleAccess)) {
if (medicalPositions.includes(roleAccess)) {
return 'medical'
}
if (roleAccess.includes(verificatorRole)) {