feat: add patient info components and update navigation for encounter details
This commit is contained in:
@@ -29,7 +29,7 @@ const linkItems: LinkItem[] = [
|
|||||||
icon: 'i-lucide-eye',
|
icon: 'i-lucide-eye',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Edit',
|
label: 'Process',
|
||||||
value: 'edit',
|
value: 'edit',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
edit()
|
edit()
|
||||||
|
|||||||
+3
-7
@@ -13,18 +13,14 @@ const props = defineProps<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
<!-- Data Pasien -->
|
<!-- Data Pasien -->
|
||||||
<Accordion
|
<Accordion type="single" defaultValue="item-patient" collapsible>
|
||||||
type="single"
|
|
||||||
defaultValue="item-patient"
|
|
||||||
collapsible
|
|
||||||
>
|
|
||||||
<AccordionItem value="item-patient" class="border-none">
|
<AccordionItem value="item-patient" class="border-none">
|
||||||
<AccordionTrigger class="focus:outline-none focus:ring-0">
|
<AccordionTrigger class="focus:outline-none focus:ring-0">
|
||||||
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Data Pasien:</h2>
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Data Pasien:</h2>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<EncounterQuickInfoFull :data="props.data" />
|
<EncounterQuickInfoFull :data="props.data" :is-grid="true" />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Types
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import EncounterQuickInfoFull from '~/components/app/encounter/quick-info-full.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: Encounter
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
|
<h2 class="mb-4 text-base font-semibold md:text-lg 2xl:text-xl">Data Pasien:</h2>
|
||||||
|
<EncounterQuickInfoFull :data="props.data" :is-grid="false" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import { format, parseISO } from 'date-fns'
|
import { format, parseISO } from 'date-fns'
|
||||||
import { id as localeID } from 'date-fns/locale'
|
import { id as localeID } from 'date-fns/locale'
|
||||||
import { getAge } from '~/lib/date'
|
import { getAge } from '~/lib/date'
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import type { Encounter } from '~/models/encounter'
|
import type { Encounter } from '~/models/encounter'
|
||||||
@@ -10,8 +11,11 @@ import { paymentTypes } from '~/lib/constants.vclaim'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: Encounter
|
data: Encounter
|
||||||
|
isGrid?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const isGrid = props.isGrid !== undefined ? props.isGrid : true
|
||||||
|
|
||||||
// Address
|
// Address
|
||||||
const address = computed(() => {
|
const address = computed(() => {
|
||||||
if (props.data.patient.person.addresses && props.data.patient.person.addresses.length > 0) {
|
if (props.data.patient.person.addresses && props.data.patient.person.addresses.length > 0) {
|
||||||
@@ -139,7 +143,7 @@ const bedNumber = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 4 Column Grid Layout -->
|
<!-- 4 Column Grid Layout -->
|
||||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-4">
|
<div :class="cn('grid grid-cols-1 gap-4', isGrid && 'sm:grid-cols-4')">
|
||||||
<!-- No. RM -->
|
<!-- No. RM -->
|
||||||
<div class="flex gap-1">
|
<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-[150px] flex-none text-sm font-semibold text-gray-700 dark:text-gray-300">No. RM</label>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, onMounted } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import EncounterPatientInfo from '~/components/app/encounter/patient-info.vue'
|
||||||
|
|
||||||
|
// Models
|
||||||
|
import { genEncounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
import { getEncounterData } from '~/handlers/encounter-process.handler'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
async function getData() {
|
||||||
|
data.value = await getEncounterData(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getData()
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClick(type: string) {
|
||||||
|
if (type === 'draft') {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="mb-4">
|
||||||
|
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" @click="handleClick" />
|
||||||
|
</div>
|
||||||
|
<EncounterPatientInfo v-if="isShowPatient" :data="data" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -228,9 +228,9 @@ watch(
|
|||||||
|
|
||||||
if (props.type === 'encounter') {
|
if (props.type === 'encounter') {
|
||||||
if (recAction.value === 'showDetail') {
|
if (recAction.value === 'showDetail') {
|
||||||
navigateTo(`${basePath}/${recId.value}/process`)
|
navigateTo(`${basePath}/${recId.value}/detail`)
|
||||||
} else if (recAction.value === 'showEdit') {
|
} else if (recAction.value === 'showEdit') {
|
||||||
navigateTo(`${basePath}/${recId.value}/edit`)
|
navigateTo(`${basePath}/${recId.value}/process`)
|
||||||
} else if (recAction.value === 'showPrint') {
|
} else if (recAction.value === 'showPrint') {
|
||||||
console.log('print')
|
console.log('print')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { computed } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import EncounterPatientInfo from '~/components/app/encounter/collapsible-patient-info.vue'
|
import EncounterPatientInfo from '~/components/app/encounter/patient-info-collapsible.vue'
|
||||||
import EncounterHistoryButtonMenu from '~/components/app/encounter/history-button-menu.vue'
|
import EncounterHistoryButtonMenu from '~/components/app/encounter/history-button-menu.vue'
|
||||||
import SubMenu from '~/components/pub/my-ui/menus/submenu.vue'
|
import SubMenu from '~/components/pub/my-ui/menus/submenu.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/emergency'
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
import Content from '~/components/content/encounter/detail.vue'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
|
||||||
|
title: 'Detail Kunjungan',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Preps role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/emergency/encounter'] || {}
|
||||||
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
if (!hasAccess) {
|
||||||
|
navigateTo('/403')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
|
||||||
|
// Page needs
|
||||||
|
const route = useRoute()
|
||||||
|
useHead({
|
||||||
|
title: () => `${route.meta.title}`,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<Content />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/inpatient'
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
import Content from '~/components/content/encounter/detail.vue'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
|
||||||
|
title: 'Detail Kunjungan',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Preps role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/inpatient/encounter'] || {}
|
||||||
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
if (!hasAccess) {
|
||||||
|
navigateTo('/403')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
|
||||||
|
// Page needs
|
||||||
|
const route = useRoute()
|
||||||
|
useHead({
|
||||||
|
title: () => `${route.meta.title}`,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<Content />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/outpatient'
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
import Content from '~/components/content/encounter/detail.vue'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
|
||||||
|
title: 'Detail Kunjungan',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Preps role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
||||||
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
if (!hasAccess) {
|
||||||
|
navigateTo('/403')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
|
||||||
|
// Page needs
|
||||||
|
const route = useRoute()
|
||||||
|
useHead({
|
||||||
|
title: () => `${route.meta.title}`,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<Content />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user