feat/encounter-adjustment: adjust menu

This commit is contained in:
2025-11-26 18:05:08 +07:00
parent 3a4822d5cf
commit 9bc999afc7
4 changed files with 265 additions and 36 deletions
@@ -36,7 +36,7 @@ const dpjp = computed(() => {
return '-'
})
// Tanggal Lahir dengan Umur
// Tgl. Lahir dengan Umur
const birthDateFormatted = computed(() => {
if (!props.data.patient.person.birthDate) {
return '-'
@@ -50,7 +50,7 @@ const birthDateFormatted = computed(() => {
}
})
// Tanggal Masuk RS dengan waktu
// Tgl. Masuk RS dengan waktu
const registeredDateFormatted = computed(() => {
const dateStr = props.data.registeredAt || props.data.visitDate
if (!dateStr) {
@@ -64,7 +64,7 @@ const registeredDateFormatted = computed(() => {
}
})
// Jenis Kelamin
// Jns. Kelamin
const genderLabel = computed(() => {
const code = props.data.patient.person.gender_code
if (!code) return '-'
@@ -77,7 +77,7 @@ const genderLabel = computed(() => {
return code
})
// Jenis Pembayaran
// Jns. Pembayaran
const paymentTypeLabel = computed(() => {
const code = props.data.paymentMethod_code
if (!code) return '-'
@@ -142,30 +142,31 @@ const bedNumber = computed(() => {
</script>
<template>
<h2 class="mb-4 font-semibold md:text-base 2xl:text-lg">Data Pasien:</h2>
<!-- 4 Column Grid Layout -->
<div :class="cn('grid grid-cols-1 gap-4', isGrid && 'sm:grid-cols-4')">
<div :class="cn('grid grid-cols-4 gap-4', isGrid && 'sm:grid-cols-4')">
<!-- No. RM -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. RM</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. RM</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ data.patient.number || '-' }}
</p>
</div>
<!-- Tanggal Lahir -->
<!-- Tgl. Lahir -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tanggal Lahir</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tgl. Lahir</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ birthDateFormatted }}
</p>
</div>
<!-- Jenis Pembayaran -->
<!-- Jns. Pembayaran -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Jenis Pembayaran</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Cara Bayar</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ paymentTypeLabel }}
</p>
@@ -173,8 +174,8 @@ const bedNumber = computed(() => {
<!-- No Bed -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No Bed</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No Bed</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ bedNumber }}
</p>
@@ -182,17 +183,17 @@ const bedNumber = computed(() => {
<!-- Nama Pasien -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Bed</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Bed</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ data.patient.person.name || '-' }}
</p>
</div>
<!-- Tanggal Masuk RS -->
<!-- Tgl. Masuk RS -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tanggal Masuk RS</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Tgl. Masuk RS</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ registeredDateFormatted }}
</p>
@@ -200,8 +201,8 @@ const bedNumber = computed(() => {
<!-- No. Billing -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Billing</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. Billing</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ billingNumber }}
</p>
@@ -209,8 +210,8 @@ const bedNumber = computed(() => {
<!-- DPJP -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">DPJP</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">DPJP</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ dpjp }}
</p>
@@ -218,17 +219,17 @@ const bedNumber = computed(() => {
<!-- Alamat -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Alamat</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Alamat</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ address }}
</p>
</div>
<!-- Jenis Kelamin -->
<!-- Jns. Kelamin -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Jenis Kelamin</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Jns. Kelamin</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ genderLabel }}
</p>
@@ -236,8 +237,8 @@ const bedNumber = computed(() => {
<!-- Nama Ruang -->
<div class="flex gap-1">
<label class="w-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Nama Ruang</label>
<label class="w-[20px] flex-none">:</label>
<label class="w-20 2xl:w-24 flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">Nama Ruang</label>
<label class="w-2 flex-none">:</label>
<p class="flex-1 text-sm text-gray-900 dark:text-gray-100">
{{ roomName }}
</p>
@@ -0,0 +1,130 @@
<script setup lang="ts">
// Components
import { Button } from '~/components/pub/ui/button'
const route = useRoute()
const router = useRouter()
// Track active menu item from query param
const activeMenu = computed(() => route.query.menu as string || '')
interface ButtonItems {
label: string
icon: string
value: string
type: 'icon' | 'image'
}
const itemsOne: ButtonItems[] = [
{
label: 'Data Pendaftaran',
icon: 'i-lucide-file',
value: 'register',
type: 'icon',
},
{
label: 'Status Pembayaran',
icon: 'i-lucide-banknote-arrow-down',
value: 'status',
type: 'icon',
},
{
label: 'Riwayat Pasien',
icon: 'i-lucide-history',
value: 'history',
type: 'icon',
},
{
label: 'Penunjang',
icon: 'i-lucide-library-big',
value: 'support',
type: 'icon',
},
{
label: 'Resep',
icon: 'i-lucide-pill',
value: 'receipt',
type: 'icon',
},
{
label: 'DPJP',
icon: 'i-lucide-stethoscope',
value: 'doctor',
type: 'icon',
},
{
label: 'I-Care BPJS',
icon: '/bpjs.png',
value: 'bpjs',
type: 'image',
},
{
label: 'File SEP',
icon: 'i-lucide-file',
value: 'sep',
type: 'icon',
},
]
const itemsTwo: ButtonItems[] = [
{
label: 'Tarif Tindakan',
icon: 'i-lucide-banknote-arrow-down',
value: 'price-list',
type: 'icon',
},
{
label: 'Tarif Tindakan Paket',
icon: 'i-lucide-banknote-arrow-down',
value: 'price-list-package',
type: 'icon',
},
]
function handleClick(value: string) {
router.replace({ path: route.path, query: { menu: value } })
}
</script>
<template>
<h2 class="mb-4 font-semibold md:text-base 2xl:text-lg">Menu Cepat:</h2>
<div class="flex flex-wrap gap-2 mb-2">
<Button
v-for="item in itemsOne"
:key="item.value"
:class="[
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
activeMenu === item.value
? 'border-orange-300 bg-orange-400 text-white'
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
]"
@click="handleClick(item.value)"
>
<Icon v-if="item.type === 'icon'"
:name="item.icon"
class="h-4 w-4"
/>
<img v-else-if="item.type === 'image'" :src="item.icon" class="h-[24px] w-[24px] object-contain" />
{{ item.label }}
</Button>
</div>
<div class="flex flex-wrap gap-2 mb-2">
<Button
v-for="item in itemsTwo"
:key="item.value"
:class="[
'flex items-center gap-2 rounded-lg border px-3 py-1 text-sm font-medium transition-colors',
activeMenu === item.value
? 'border-orange-300 bg-orange-400 text-white'
: 'border-orange-300 bg-white text-orange-500 hover:bg-orange-400 hover:text-white'
]"
@click="handleClick(item.value)"
>
<Icon
:name="item.icon"
class="h-4 w-4"
/>
{{ item.label }}
</Button>
</div>
</template>
@@ -1,10 +1,13 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
// Pubs components
import ContentSwitcher from '~/components/pub/my-ui/content-switcher/content-switcher.vue'
import { useSidebar } from '~/components/pub/ui/sidebar/utils'
// Components
import EncounterPatientInfo from '~/components/app/encounter/patient-info-collapsible.vue'
import EncounterHistoryButtonMenu from '~/components/app/encounter/history-button-menu.vue'
import EncounterPatientInfo from '~/components/app/encounter/quick-info-full.vue'
import EncounterHistoryButtonMenu from '~/components/app/encounter/quick-shortcut.vue'
import SubMenu from '~/components/pub/my-ui/menus/submenu.vue'
// Libraries
@@ -43,6 +46,9 @@ const id = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const data = ref<any>(genEncounter())
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
router.push('/')
}
@@ -113,8 +119,14 @@ onMounted(async () => {
<div class="mb-4">
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" @click="handleClick" />
</div>
<EncounterPatientInfo v-if="isShowPatient" :data="data" />
<EncounterHistoryButtonMenu v-if="isShowPatient" />
<ContentSwitcher :active="1" :height="200">
<template v-slot:content1>
<EncounterPatientInfo v-if="isShowPatient" :data="data" />
</template>
<template v-slot:content2>
<EncounterHistoryButtonMenu v-if="isShowPatient" />
</template>
</ContentSwitcher>
<SubMenu :data="menus" :initial-active-menu="activeMenu" @change-menu="activeMenu = $event" />
</div>
</template>
@@ -0,0 +1,86 @@
<script setup lang="ts">
const props = defineProps<{
height: 200
activeTab?: 1 | 2
}>()
const activeTab = ref(props.activeTab || 1)
function handleClick(value: 1 | 2) {
activeTab.value = value
}
</script>
<template>
<div class="content-switcher" :style="`height: ${height}px`">
<div :class="`${activeTab === 1 ? 'active' : 'inactive'}`">
<div class="content-wrapper">
<div>
<slot name="content1" />
</div>
</div>
<div class="content-nav">
<button @click="handleClick(1)">
<Icon name="i-lucide-chevron-right" />
</button>
</div>
</div>
<div :class="`${activeTab === 2 ? 'active' : 'inactive'}`">
<div class="content-nav">
<button @click="handleClick(2)">
<Icon name="i-lucide-chevron-left" />
</button>
</div>
<div class="content-wrapper">
<div>
<slot name="content2" />
</div>
</div>
</div>
</div>
</template>
<style>
.content-switcher {
@apply flex overflow-hidden gap-3
}
.content-switcher > * {
@apply border border-slate-300 rounded-md flex overflow-hidden
}
.content-wrapper {
@apply p-4 2xl:p-5 overflow-hidden
}
.inactive .content-wrapper {
@apply p-0 w-0
}
.content-nav {
@apply h-full flex flex-row items-center justify-center content-center !text-2xl overflow-hidden
}
.content-nav button {
@apply pt-2 px-2 h-full w-full
}
/* .content-switcher .inactive > .content-wrapper {
@apply w-0 p-0 opacity-0 transition-all duration-500 ease-in-out
} */
.content-switcher .inactive {
@apply w-16 transition-all duration-500 ease-in-out
}
.content-switcher .inactive > .content-nav {
@apply w-full transition-all duration-100 ease-in-out
}
.content-switcher .active {
@apply grow transition-all duration-500 ease-in-out
}
.content-switcher .active > .content-nav {
@apply w-0 transition-all duration-100 ease-in-out
}
/* .content-switcher .active > .content-wrapper {
@apply w-full delay-1000 transition-all duration-1000 ease-in-out
} */
</style>