Merge pull request #191 from dikstub-rssa/feat/page-cleaning
Feat/page cleaning
This commit is contained in:
@@ -9,38 +9,38 @@ const props = defineProps<{
|
|||||||
const recId = inject<Ref<number>>('rec_id')!
|
const recId = inject<Ref<number>>('rec_id')!
|
||||||
const recAction = inject<Ref<string>>('rec_action')!
|
const recAction = inject<Ref<string>>('rec_action')!
|
||||||
const recItem = inject<Ref<any>>('rec_item')!
|
const recItem = inject<Ref<any>>('rec_item')!
|
||||||
|
const activeServicePosition = inject<Ref<string>>('activeServicePosition')! // previously: activePosition
|
||||||
|
|
||||||
const activeKey = ref<string | null>(null)
|
const activeKey = ref<string | null>(null)
|
||||||
const activePosition = inject<Ref<string>>('position')!
|
|
||||||
const linkItemsFiltered = ref<LinkItem[]>([])
|
const linkItemsFiltered = ref<LinkItem[]>([])
|
||||||
const linkItemsBase: LinkItem[] = [
|
const baseLinkItems: LinkItem[] = [
|
||||||
{
|
|
||||||
label: 'Nothing',
|
|
||||||
value: 'nothing',
|
|
||||||
icon: 'i-lucide-file',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
const linkItems: LinkItem[] = [
|
|
||||||
{
|
{
|
||||||
label: 'Detail',
|
label: 'Detail',
|
||||||
value: 'detail',
|
value: 'detail',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
detail()
|
proceedItem(ActionEvents.showDetail)
|
||||||
},
|
},
|
||||||
icon: 'i-lucide-eye',
|
icon: 'i-lucide-eye',
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const medicalLinkItems: LinkItem[] = [
|
||||||
{
|
{
|
||||||
label: 'Process',
|
label: 'Process',
|
||||||
value: 'edit',
|
value: 'process',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
edit()
|
proceedItem(ActionEvents.showProcess)
|
||||||
},
|
},
|
||||||
icon: 'i-lucide-pencil',
|
icon: 'i-lucide-shuffle',
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const regLinkItems: LinkItem[] = [
|
||||||
{
|
{
|
||||||
label: 'Print',
|
label: 'Print',
|
||||||
value: 'print',
|
value: 'print',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
print()
|
proceedItem(ActionEvents.showPrint)
|
||||||
},
|
},
|
||||||
icon: 'i-lucide-printer',
|
icon: 'i-lucide-printer',
|
||||||
},
|
},
|
||||||
@@ -48,7 +48,7 @@ const linkItems: LinkItem[] = [
|
|||||||
label: 'Batalkan',
|
label: 'Batalkan',
|
||||||
value: 'cancel',
|
value: 'cancel',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
cancel()
|
proceedItem(ActionEvents.showCancel)
|
||||||
},
|
},
|
||||||
icon: 'i-lucide-circle-x',
|
icon: 'i-lucide-circle-x',
|
||||||
},
|
},
|
||||||
@@ -56,65 +56,49 @@ const linkItems: LinkItem[] = [
|
|||||||
label: 'Hapus',
|
label: 'Hapus',
|
||||||
value: 'remove',
|
value: 'remove',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
remove()
|
proceedItem(ActionEvents.showConfirmDelete)
|
||||||
},
|
},
|
||||||
icon: 'i-lucide-trash',
|
icon: 'i-lucide-trash',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
function detail() {
|
const voidLinkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Nothing',
|
||||||
|
value: 'nothing',
|
||||||
|
icon: 'i-lucide-file',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
linkItemsFiltered.value = [...baseLinkItems]
|
||||||
|
|
||||||
|
getLinks()
|
||||||
|
|
||||||
|
watch(activeServicePosition, () => {
|
||||||
|
getLinks()
|
||||||
|
})
|
||||||
|
|
||||||
|
function proceedItem(action: string) {
|
||||||
recId.value = props.rec.id || 0
|
recId.value = props.rec.id || 0
|
||||||
recAction.value = ActionEvents.showDetail
|
|
||||||
recItem.value = props.rec
|
recItem.value = props.rec
|
||||||
|
recAction.value = action
|
||||||
}
|
}
|
||||||
|
|
||||||
function edit() {
|
function getLinks() {
|
||||||
recId.value = props.rec.id || 0
|
switch (activeServicePosition.value) {
|
||||||
recAction.value = ActionEvents.showEdit
|
|
||||||
recItem.value = props.rec
|
|
||||||
}
|
|
||||||
|
|
||||||
function print() {
|
|
||||||
recId.value = props.rec.id || 0
|
|
||||||
recAction.value = ActionEvents.showPrint
|
|
||||||
recItem.value = props.rec
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancel() {
|
|
||||||
recId.value = props.rec.id || 0
|
|
||||||
recAction.value = ActionEvents.showCancel
|
|
||||||
recItem.value = props.rec
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove() {
|
|
||||||
recId.value = props.rec.id || 0
|
|
||||||
recAction.value = ActionEvents.showConfirmDelete
|
|
||||||
recItem.value = props.rec
|
|
||||||
}
|
|
||||||
|
|
||||||
linkItemsFiltered.value = [...linkItemsBase]
|
|
||||||
|
|
||||||
function getLinks(position: string) {
|
|
||||||
switch (position) {
|
|
||||||
case 'medical':
|
case 'medical':
|
||||||
linkItemsFiltered.value = [...linkItems]
|
linkItemsFiltered.value = [...baseLinkItems, ...medicalLinkItems]
|
||||||
break
|
break
|
||||||
case 'verificator':
|
case 'registration':
|
||||||
linkItemsFiltered.value = [
|
linkItemsFiltered.value = [...baseLinkItems, ...regLinkItems]
|
||||||
...linkItems.filter((item) => ['detail', 'print'].includes(item.value || '')),
|
case 'unit|resp':
|
||||||
]
|
linkItemsFiltered.value = [...baseLinkItems]
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
linkItemsFiltered.value = [...linkItemsBase]
|
linkItemsFiltered.value = voidLinkItems
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinks(activePosition.value)
|
|
||||||
|
|
||||||
watch(activePosition, () => {
|
|
||||||
getLinks(activePosition.value)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Calendar as CalendarIcon, Filter as FilterIcon, Search } from 'lucide-vue-next'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import type { RefExportNav, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
refSearchNav?: RefSearchNav
|
||||||
|
enableExport?: boolean
|
||||||
|
refExportNav?: RefExportNav
|
||||||
|
onFilterClick?: () => void
|
||||||
|
onExportPdf?: () => void
|
||||||
|
onExportExcel?: () => void
|
||||||
|
onExportCsv?: () => void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// function emitSearchNavClick() {
|
||||||
|
// props.refSearchNav?.onClick()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function onInput(event: Event) {
|
||||||
|
// props.refSearchNav?.onInput((event.target as HTMLInputElement).value)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function btnClick() {
|
||||||
|
// props.prep?.addNav?.onClick?.()
|
||||||
|
// }
|
||||||
|
|
||||||
|
const searchQuery = ref('')
|
||||||
|
const dateRange = ref<{ from: Date | null; to: Date | null }>({
|
||||||
|
from: new Date(),
|
||||||
|
to: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const df = new DateFormatter('en-US', {
|
||||||
|
dateStyle: 'medium',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get current date
|
||||||
|
const today = new Date()
|
||||||
|
const todayCalendar = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate())
|
||||||
|
|
||||||
|
// Get date 1 month ago
|
||||||
|
const oneMonthAgo = new Date(today)
|
||||||
|
oneMonthAgo.setMonth(today.getMonth() - 1)
|
||||||
|
const oneMonthAgoCalendar = new CalendarDate(oneMonthAgo.getFullYear(), oneMonthAgo.getMonth() + 1, oneMonthAgo.getDate())
|
||||||
|
|
||||||
|
const value = ref({
|
||||||
|
start: oneMonthAgoCalendar,
|
||||||
|
end: todayCalendar,
|
||||||
|
}) as Ref<DateRange>
|
||||||
|
|
||||||
|
// function onFilterClick() {
|
||||||
|
// console.log('Search:', searchQuery.value)
|
||||||
|
// console.log('Date Range:', dateRange.value)
|
||||||
|
// props.refSearchNav?.onClick()
|
||||||
|
// }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative w-64">
|
||||||
|
<Search class="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-gray-400" />
|
||||||
|
<Input v-model="searchQuery" type="text" placeholder="Cari Nama /No.RM" class="pl-9" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
:class="cn('w-[200px] justify-start text-left font-normal', !value && 'text-muted-foreground')"
|
||||||
|
>
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="value.start">
|
||||||
|
<template v-if="value.end">
|
||||||
|
{{ df.format(value.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(value.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(value.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar
|
||||||
|
v-model="value"
|
||||||
|
initial-focus
|
||||||
|
:number-of-months="2"
|
||||||
|
@update:start-value="(startDate) => (value.start = startDate)"
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<Button variant="outline" class="border-orange-500 text-orange-600 hover:bg-orange-50" @click="onFilterClick">
|
||||||
|
<FilterIcon class="mr-2 size-4" />
|
||||||
|
Filter
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<DropdownMenu v-show="props.enableExport">
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="outline" class="ml-auto border-orange-500 text-orange-600 hover:bg-orange-50">
|
||||||
|
<Icon name="i-lucide-download" class="h-4 w-4" />
|
||||||
|
Ekspor
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
<DropdownMenuItem @click="onExportPdf">
|
||||||
|
Ekspor PDF
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem @click="onExportCsv">
|
||||||
|
Ekspor CSV
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem @click="onExportExcel">
|
||||||
|
Ekspor Excel
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</template>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||||
import { config } from './list.cfg'
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -7,7 +8,7 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PubMyUiDataTable
|
<DataTable
|
||||||
v-bind="config"
|
v-bind="config"
|
||||||
:rows="props.data"
|
:rows="props.data"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import { genderCodes } from '~/const/key-val/person';
|
||||||
import type { Encounter } from '~/models/encounter'
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: Encounter
|
data: Encounter
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let address = ''
|
let address = ref('')
|
||||||
if (props.data.patient.person.addresses) {
|
if (props.data.patient.person.addresses) {
|
||||||
address = props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
address.value = props.data.patient.person.addresses.map((a) => a.address).join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
let dpjp = ''
|
let dpjp = ref('')
|
||||||
if (props.data.responsible_doctor) {
|
if (props.data.responsible_doctor) {
|
||||||
const dp = props.data.responsible_doctor.employee.person
|
const dp = props.data.responsible_doctor.employee.person
|
||||||
dpjp = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
dpjp.value = `${dp.frontTitle} ${dp.name} ${dp.endTitle}`
|
||||||
} else if (props.data.appointment_doctor) {
|
} else if (props.data.appointment_doctor) {
|
||||||
dpjp = props.data.appointment_doctor.employee.person.name
|
dpjp.value = props.data.appointment_doctor.employee.person.name
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
<div class="w-full">
|
||||||
<!-- Data Pasien -->
|
<!-- Data Pasien -->
|
||||||
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
||||||
{{ data.patient.person.name }} - {{ data.patient.number }}
|
{{ data.patient.person.name }} - {{ data.patient.number }}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="grid grid-cols-3">
|
<div class="grid grid-cols-3">
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
|
||||||
labelSize="large"
|
|
||||||
>
|
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">No. RM</DE.Label>
|
<DE.Label class="font-semibold">No. RM</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Jenis Kelamin</DE.Label>
|
<DE.Label class="font-semibold">Jns. Kelamin</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.patient.person.gender_code }}
|
{{ genderCodes[data.patient.person.gender_code as keyof typeof genderCodes] }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Alamat</DE.Label>
|
<DE.Label class="font-semibold">Alamat</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
<div v-html="address"></div>
|
<div v-html="address"></div>
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
@@ -54,35 +54,39 @@ if (props.data.responsible_doctor) {
|
|||||||
</DE.Block>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
|
||||||
labelSize="large"
|
|
||||||
>
|
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Tgl. Kunjungan</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Tgl. Masuk</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.visitDate.substring(0, 10) }}
|
{{ data.visitDate.substring(0, 10) }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">Klinik</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Poliklinik</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ data.unit?.name }}
|
{{ data.unit?.name }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label class="font-semibold">DPJP</DE.Label>
|
<DE.Label position="dynamic" class="font-semibold">Klinik</DE.Label>
|
||||||
|
<DE.Colon />
|
||||||
<DE.Field>
|
<DE.Field>
|
||||||
{{ dpjp }}
|
{{ data.unit?.name }}
|
||||||
</DE.Field>
|
</DE.Field>
|
||||||
</DE.Cell>
|
</DE.Cell>
|
||||||
</DE.Block>
|
</DE.Block>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<DE.Block
|
<DE.Block mode="preview">
|
||||||
mode="preview"
|
<DE.Cell>
|
||||||
labelSize="large"
|
<DE.Label position="dynamic" class="font-semibold">DPJP</DE.Label>
|
||||||
>
|
<DE.Colon />
|
||||||
|
<DE.Field>
|
||||||
|
{{ dpjp }}
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
<DE.Cell>
|
<DE.Cell>
|
||||||
<DE.Label
|
<DE.Label
|
||||||
position="dynamic"
|
position="dynamic"
|
||||||
@@ -90,6 +94,7 @@ if (props.data.responsible_doctor) {
|
|||||||
>
|
>
|
||||||
Billing
|
Billing
|
||||||
</DE.Label>
|
</DE.Label>
|
||||||
|
<DE.Colon class="pt-1" />
|
||||||
<DE.Field class="text-base 2xl:text-lg">
|
<DE.Field class="text-base 2xl:text-lg">
|
||||||
Rp. 000.000
|
Rp. 000.000
|
||||||
<!-- {{ data }} -->
|
<!-- {{ data }} -->
|
||||||
|
|||||||
@@ -1,41 +1,40 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// Components
|
///// Imports
|
||||||
import { Calendar, Hospital, UserCheck, UsersRound } from 'lucide-vue-next'
|
// Pub components
|
||||||
import SummaryCard from '~/components/pub/my-ui/summary-card/summary-card.vue'
|
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
import * as CH from '~/components/pub/my-ui/content-header'
|
||||||
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
|
|
||||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||||
import { useSidebar } from '~/components/pub/ui/sidebar/utils'
|
import { useSidebar } from '~/components/pub/ui/sidebar/utils'
|
||||||
|
|
||||||
// Libs
|
// App libs
|
||||||
import { getPositionAs } from '~/lib/roles'
|
import { getServicePosition } from '~/lib/roles' // previously getPositionAs
|
||||||
|
|
||||||
// Types
|
|
||||||
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
|
|
||||||
import type { Summary } from '~/components/pub/my-ui/summary-card/type'
|
|
||||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
|
||||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getList as getEncounterList, remove as removeEncounter, cancel as cancelEncounter } from '~/services/encounter.service'
|
import { getList as getEncounterList, remove as removeEncounter, cancel as cancelEncounter } from '~/services/encounter.service'
|
||||||
|
|
||||||
// UI
|
// Apps
|
||||||
import { toast } from '~/components/pub/ui/toast'
|
import Content from '~/components/app/encounter/list.vue'
|
||||||
|
import FilterNav from '~/components/app/encounter/filter-nav.vue'
|
||||||
|
import FilterForm from '~/components/app/encounter/filter-form.vue'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps<{
|
||||||
|
classCode?: 'ambulatory' | 'emergency' | 'inpatient'
|
||||||
|
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
||||||
|
canCreate?: boolean
|
||||||
|
canUpdate?: boolean
|
||||||
|
canDelete?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
///// Declarations and Flows
|
||||||
|
// Sidebar automation
|
||||||
const { setOpen } = useSidebar()
|
const { setOpen } = useSidebar()
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
|
|
||||||
const { getActiveRole } = useUserStore()
|
// Main data
|
||||||
const activeRole = getActiveRole()
|
|
||||||
const activePosition = ref(getPositionAs(activeRole))
|
|
||||||
const props = defineProps<{
|
|
||||||
classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
|
|
||||||
subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
|
|
||||||
type: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const data = ref([])
|
const data = ref([])
|
||||||
const isLoading = reactive<DataTableLoader>({
|
const isLoading = reactive<DataTableLoader>({
|
||||||
summary: false,
|
summary: false,
|
||||||
@@ -44,67 +43,88 @@ const isLoading = reactive<DataTableLoader>({
|
|||||||
const recId = ref<number>(0)
|
const recId = ref<number>(0)
|
||||||
const recAction = ref<string>('')
|
const recAction = ref<string>('')
|
||||||
const recItem = ref<any>(null)
|
const recItem = ref<any>(null)
|
||||||
const isFormEntryDialogOpen = ref(false)
|
const isFilterFormDialogOpen = ref(false)
|
||||||
const isRecordConfirmationOpen = ref(false)
|
const isRecordConfirmationOpen = ref(false)
|
||||||
const isRecordCancelOpen = ref(false)
|
const isRecordCancelOpen = ref(false)
|
||||||
|
|
||||||
const hreaderPrep: HeaderPrep = {
|
// Headers
|
||||||
|
const hreaderPrep: CH.Config = {
|
||||||
title: 'Kunjungan',
|
title: 'Kunjungan',
|
||||||
icon: 'i-lucide-users',
|
icon: 'i-lucide-users',
|
||||||
addNav: {
|
addNav: {
|
||||||
label: 'Tambah',
|
label: 'Tambah',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
|
navigateTo(`/${props.classCode}/encounter/add`)
|
||||||
navigateTo('/rehab/encounter/add')
|
|
||||||
}
|
|
||||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'reg') {
|
|
||||||
navigateTo('/outpatient/encounter/add')
|
|
||||||
}
|
|
||||||
if (props.classCode === 'emergency') {
|
|
||||||
navigateTo('/emergency/encounter/add')
|
|
||||||
}
|
|
||||||
if (props.classCode === 'inpatient') {
|
|
||||||
navigateTo('/inpatient/encounter/add')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if (!props.canCreate) {
|
||||||
const refSearchNav: RefSearchNav = {
|
delete hreaderPrep.addNav
|
||||||
onClick: () => {
|
|
||||||
// open filter modal
|
|
||||||
isFormEntryDialogOpen.value = true
|
|
||||||
console.log(' 1open filter modal')
|
|
||||||
},
|
|
||||||
onInput: (_val: string) => {
|
|
||||||
// filter patient list
|
|
||||||
},
|
|
||||||
onClear: () => {
|
|
||||||
// clear url param
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading state management
|
// Filters
|
||||||
|
const filter = ref<{
|
||||||
|
installation: {
|
||||||
|
msg: {
|
||||||
|
placeholder: string
|
||||||
|
}
|
||||||
|
items: {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
code: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
schema: any
|
||||||
|
initialValues?: Partial<any>
|
||||||
|
errors?: any
|
||||||
|
}>({
|
||||||
|
installation: {
|
||||||
|
msg: {
|
||||||
|
placeholder: 'Pilih',
|
||||||
|
},
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
schema: {},
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
// Role reactivities
|
||||||
* Get base path for encounter routes based on classCode and subClassCode
|
const { getActiveRole } = useUserStore()
|
||||||
*/
|
const activeServicePosition = ref(getServicePosition(getActiveRole()))
|
||||||
function getBasePath(): string {
|
provide('activeServicePosition', activeServicePosition)
|
||||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
|
watch(getActiveRole, (role? : string) => {
|
||||||
return '/rehab/encounter'
|
activeServicePosition.value = getServicePosition(role)
|
||||||
}
|
})
|
||||||
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' // fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Recrod reactivities
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
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}`)
|
||||||
|
} else if (recAction.value === ActionEvents.showEdit) {
|
||||||
|
navigateTo(`${basePath}/${recId.value}/edit`)
|
||||||
|
} else if (recAction.value === ActionEvents.showProcess) {
|
||||||
|
navigateTo(`${basePath}/${recId.value}/process`)
|
||||||
|
} else if (recAction.value === ActionEvents.showConfirmDelete) {
|
||||||
|
isRecordConfirmationOpen.value = true
|
||||||
|
}
|
||||||
|
recAction.value = '' // reset
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPatientList()
|
||||||
|
})
|
||||||
|
|
||||||
|
/////// Functions
|
||||||
async function getPatientList() {
|
async function getPatientList() {
|
||||||
isLoading.isTableLoading = true
|
isLoading.isTableLoading = true
|
||||||
try {
|
try {
|
||||||
@@ -215,94 +235,32 @@ function handleRemoveConfirmation() {
|
|||||||
recItem.value = null
|
recItem.value = null
|
||||||
isRecordConfirmationOpen.value = false
|
isRecordConfirmationOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => recAction.value,
|
|
||||||
() => {
|
|
||||||
if (recAction.value === ActionEvents.showConfirmDelete) {
|
|
||||||
isRecordConfirmationOpen.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recAction.value === ActionEvents.showCancel) {
|
|
||||||
isRecordCancelOpen.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const basePath = getBasePath()
|
|
||||||
|
|
||||||
if (props.type === 'encounter') {
|
|
||||||
if (recAction.value === 'showDetail') {
|
|
||||||
navigateTo(`${basePath}/${recId.value}/detail`)
|
|
||||||
} else if (recAction.value === 'showEdit') {
|
|
||||||
navigateTo(`${basePath}/${recId.value}/process`)
|
|
||||||
} else if (recAction.value === 'showPrint') {
|
|
||||||
console.log('print')
|
|
||||||
} else {
|
|
||||||
// handle other actions
|
|
||||||
}
|
|
||||||
} else if (props.type === 'registration') {
|
|
||||||
// Handle registration type if needed
|
|
||||||
if (recAction.value === 'showDetail') {
|
|
||||||
navigateTo(`${basePath.replace('/encounter', '/registration')}/${recId.value}/detail`)
|
|
||||||
} else if (recAction.value === 'showEdit') {
|
|
||||||
navigateTo(`${basePath.replace('/encounter', '/registration')}/${recId.value}/edit`)
|
|
||||||
} else if (recAction.value === 'showProcess') {
|
|
||||||
navigateTo(`${basePath.replace('/encounter', '/registration')}/${recId.value}/process`)
|
|
||||||
} else {
|
|
||||||
// handle other actions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(getActiveRole, () => {
|
|
||||||
const activeRole = getActiveRole()
|
|
||||||
activePosition.value = getPositionAs(activeRole)
|
|
||||||
})
|
|
||||||
|
|
||||||
provide('rec_id', recId)
|
|
||||||
provide('rec_action', recAction)
|
|
||||||
provide('rec_item', recItem)
|
|
||||||
provide('table_data_loader', isLoading)
|
|
||||||
provide('position', activePosition)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getPatientList()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Header
|
<CH.ContentHeader v-bind="hreaderPrep">
|
||||||
:prep="{ ...hreaderPrep }"
|
<FilterNav
|
||||||
:ref-search-nav="refSearchNav"
|
@onFilterClick="() => isFilterFormDialogOpen = true"
|
||||||
/>
|
@onExportPdf="() => {}"
|
||||||
<Separator class="my-4 xl:my-5" />
|
@onExportExcel="() => {}"
|
||||||
|
@nExportCsv="() => {}"
|
||||||
|
/>
|
||||||
|
</CH.ContentHeader>
|
||||||
|
|
||||||
<Filter
|
<Content :data="data" />
|
||||||
:prep="hreaderPrep"
|
|
||||||
:ref-search-nav="refSearchNav"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AppEncounterList :data="data" />
|
|
||||||
|
|
||||||
|
<!-- Filter -->
|
||||||
<Dialog
|
<Dialog
|
||||||
v-model:open="isFormEntryDialogOpen"
|
v-model:open="isFilterFormDialogOpen"
|
||||||
title="Filter"
|
title="Filter"
|
||||||
size="lg"
|
size="lg"
|
||||||
prevent-outside
|
prevent-outside
|
||||||
>
|
>
|
||||||
<AppEncounterFilter
|
<FilterForm v-bind="filter" />
|
||||||
:installation="{
|
|
||||||
msg: { placeholder: 'Pilih' },
|
|
||||||
items: [],
|
|
||||||
}"
|
|
||||||
:schema="{}"
|
|
||||||
/>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<!-- Record Confirmation Modal -->
|
<!-- Batal -->
|
||||||
<RecordConfirmation
|
<RecordConfirmation
|
||||||
v-model:open="isRecordCancelOpen"
|
v-model:open="isRecordCancelOpen"
|
||||||
custom-title="Batalkan Kunjungan"
|
custom-title="Batalkan Kunjungan"
|
||||||
custom-message="Apakah anda yakin ingin membatalkan kunjungan pasien berikut?"
|
custom-message="Apakah anda yakin ingin membatalkan kunjungan pasien berikut?"
|
||||||
@@ -325,7 +283,9 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</RecordConfirmation>
|
</RecordConfirmation>
|
||||||
|
|
||||||
|
<!-- Hapus -->
|
||||||
<RecordConfirmation
|
<RecordConfirmation
|
||||||
|
v-if="canDelete"
|
||||||
v-model:open="isRecordConfirmationOpen"
|
v-model:open="isRecordConfirmationOpen"
|
||||||
action="delete"
|
action="delete"
|
||||||
:record="recItem"
|
:record="recItem"
|
||||||
@@ -349,4 +309,11 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</RecordConfirmation>
|
</RecordConfirmation>
|
||||||
|
<Dialog
|
||||||
|
title="Hapus data"
|
||||||
|
size="md"
|
||||||
|
v-model:open="isRecordConfirmationOpen"
|
||||||
|
>
|
||||||
|
Hak akses tidak memenuhi kriteria untuk proses ini.
|
||||||
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<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'
|
||||||
|
|
||||||
|
import { genEncounter } from '~/models/encounter'
|
||||||
|
|
||||||
|
// 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'
|
||||||
|
|
||||||
|
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 data = ref(genEncounter())
|
||||||
|
|
||||||
|
async function fetchDetail() {
|
||||||
|
const res = await getDetail(id, {
|
||||||
|
includes:
|
||||||
|
'patient,patient-person,patient-person-addresses,unit,Appointment_Doctor,Appointment_Doctor-employee,Appointment_Doctor-employee-person,EncounterDocuments',
|
||||||
|
})
|
||||||
|
if (res.body?.data) data.value = res.body?.data
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchDetail()
|
||||||
|
})
|
||||||
|
|
||||||
|
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: 'patient-note', label: 'CPRJ', component: Cprj, props: { encounter: data } },
|
||||||
|
{ value: 'consent', label: 'General Consent', component: GeneralConsentList, 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 },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</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>
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
// 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/quick-info-full.vue'
|
|
||||||
import EncounterHistoryButtonMenu from '~/components/app/encounter/quick-shortcut.vue'
|
|
||||||
import SubMenu from '~/components/pub/my-ui/menus/submenu.vue'
|
|
||||||
|
|
||||||
// Libraries
|
|
||||||
import { getPositionAs } from '~/lib/roles'
|
|
||||||
|
|
||||||
// Models
|
|
||||||
import { genEncounter } from '~/models/encounter'
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import type { EncounterProps } from '~/handlers/encounter-init.handler'
|
|
||||||
|
|
||||||
// Handlers
|
|
||||||
import { getEncounterData } from '~/handlers/encounter-process.handler'
|
|
||||||
import { getMenuItems } from "~/handlers/encounter-init.handler"
|
|
||||||
|
|
||||||
const { user, getActiveRole } = useUserStore()
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
classCode?: EncounterProps['classCode']
|
|
||||||
subClassCode?: EncounterProps['subClassCode']
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const activeRole = getActiveRole()
|
|
||||||
const activePosition = ref(getPositionAs(activeRole))
|
|
||||||
const menus = ref([] as any)
|
|
||||||
const activeMenu = computed({
|
|
||||||
get: () => (route.query?.menu && typeof route.query.menu === 'string' ? route.query.menu : 'status'),
|
|
||||||
set: (value: string) => {
|
|
||||||
router.replace({ path: route.path, query: { menu: value } })
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
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('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dummy rows for ProtocolList (matches keys expected by list-cfg.protocol)
|
|
||||||
const protocolRows = [
|
|
||||||
{
|
|
||||||
number: '1',
|
|
||||||
tanggal: new Date().toISOString().substring(0, 10),
|
|
||||||
siklus: 'I',
|
|
||||||
periode: 'Siklus I',
|
|
||||||
kehadiran: 'Hadir',
|
|
||||||
action: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
number: '2',
|
|
||||||
tanggal: new Date().toISOString().substring(0, 10),
|
|
||||||
siklus: 'II',
|
|
||||||
periode: 'Siklus II',
|
|
||||||
kehadiran: 'Tidak Hadir',
|
|
||||||
action: '',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const paginationMeta = {
|
|
||||||
recordCount: protocolRows.length,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
totalPage: 1,
|
|
||||||
hasNext: false,
|
|
||||||
hasPrev: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClick(type: string) {
|
|
||||||
if (type === 'draft') {
|
|
||||||
router.back()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initMenus() {
|
|
||||||
menus.value = getMenuItems(id, props, user, {
|
|
||||||
encounter: data.value
|
|
||||||
} as any, {
|
|
||||||
protocolTheraphy: paginationMeta,
|
|
||||||
protocolChemotherapy: paginationMeta,
|
|
||||||
medicineProtocolChemotherapy: paginationMeta,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getData() {
|
|
||||||
data.value = await getEncounterData(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(getActiveRole, () => {
|
|
||||||
const activeRole = getActiveRole()
|
|
||||||
activePosition.value = getPositionAs(activeRole)
|
|
||||||
initMenus()
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await getData()
|
|
||||||
initMenus()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="w-full">
|
|
||||||
<div class="mb-4">
|
|
||||||
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" @click="handleClick" />
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
@@ -1,114 +1,142 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
//
|
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
///// Imports
|
||||||
|
// Pubs components
|
||||||
|
import ContentSwitcher from '~/components/pub/my-ui/content-switcher/content-switcher.vue'
|
||||||
|
import { useSidebar } from '~/components/pub/ui/sidebar/utils'
|
||||||
|
import SubMenu from '~/components/pub/my-ui/menus/submenu.vue'
|
||||||
|
import ContentNavBa from '~/components/pub/my-ui/nav-content/ba.vue'
|
||||||
|
|
||||||
import { getDetail } from '~/services/encounter.service'
|
// App libs
|
||||||
|
import { getServicePosition } from '~/lib/roles' // previously getPositionAs
|
||||||
|
|
||||||
//
|
// App Models
|
||||||
import type { TabItem } from '~/components/pub/my-ui/comp-tab/type'
|
import { genEncounter, type Encounter } from '~/models/encounter'
|
||||||
import CompTab from '~/components/pub/my-ui/comp-tab/comp-tab.vue'
|
|
||||||
|
|
||||||
import { genEncounter } 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"
|
||||||
|
|
||||||
// PLASE ORDER BY TAB POSITION
|
// App Components
|
||||||
import Status from '~/components/content/encounter/status.vue'
|
import EncounterPatientInfo from '~/components/app/encounter/quick-info.vue'
|
||||||
import AssesmentFunctionList from '~/components/content/soapi/entry.vue'
|
import EncounterHistoryButtonMenu from '~/components/app/encounter/quick-shortcut.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'
|
|
||||||
|
|
||||||
|
///// Declarations and Flows
|
||||||
|
// Props
|
||||||
|
const props = defineProps<{
|
||||||
|
classCode: EncounterProps['classCode']
|
||||||
|
subClassCode?: EncounterProps['subClassCode']
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Common preparations
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
// activeTab selalu sinkron dengan query param
|
const { user, userActiveRole, getActiveRole } = useUserStore()
|
||||||
const activeTab = computed({
|
const activeRole = getActiveRole()
|
||||||
get: () => (route.query?.tab && typeof route.query.tab === 'string' ? route.query.tab : 'status'),
|
const activePosition = ref(getServicePosition(activeRole))
|
||||||
set: (val: string) => {
|
const menus = ref([] as any)
|
||||||
router.replace({ path: route.path, query: { tab: val } })
|
const activeMenu = computed({
|
||||||
|
get: () => (route.query?.menu && typeof route.query.menu === 'string' ? route.query.menu : 'status'),
|
||||||
|
set: (value: string) => {
|
||||||
|
router.replace({ path: route.path, query: { menu: value } })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const id = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
const id = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||||
const data = ref(genEncounter())
|
const data = ref<Encounter>(genEncounter())
|
||||||
|
const isShowPatient = computed(() => data.value && data.value?.patient?.person)
|
||||||
|
|
||||||
async function fetchDetail() {
|
const { setOpen } = useSidebar()
|
||||||
const res = await getDetail(id, {
|
setOpen(false)
|
||||||
includes:
|
|
||||||
'patient,patient-person,patient-person-addresses,unit,Appointment_Doctor,Appointment_Doctor-employee,Appointment_Doctor-employee-person,EncounterDocuments',
|
if (activePosition.value === 'none') { // if user position is none, redirect to home page
|
||||||
})
|
router.push('/')
|
||||||
if (res.body?.data) data.value = res.body?.data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
// Dummy rows for ProtocolList (matches keys expected by list-cfg.protocol)
|
||||||
fetchDetail()
|
const protocolRows = [
|
||||||
})
|
|
||||||
|
|
||||||
const tabs: TabItem[] = [
|
|
||||||
{ value: 'status', label: 'Status Masuk/Keluar', component: Status, props: { encounter: data } },
|
|
||||||
{
|
{
|
||||||
value: 'early-medical-assessment',
|
number: '1',
|
||||||
label: 'Pengkajian Awal Medis',
|
tanggal: new Date().toISOString().substring(0, 10),
|
||||||
component: EarlyMedicalAssesmentList,
|
siklus: 'I',
|
||||||
props: { encounter: data, type: 'early-medic', label: 'Pengkajian Awal Medis' },
|
periode: 'Siklus I',
|
||||||
|
kehadiran: 'Hadir',
|
||||||
|
action: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'rehab-medical-assessment',
|
number: '2',
|
||||||
label: 'Pengkajian Awal Medis Rehabilitasi Medis',
|
tanggal: new Date().toISOString().substring(0, 10),
|
||||||
component: EarlyMedicalRehabList,
|
siklus: 'II',
|
||||||
props: { encounter: data, type: 'early-rehab', label: 'Pengkajian Awal Medis Rehabilitasi Medis' },
|
periode: 'Siklus II',
|
||||||
},
|
kehadiran: 'Tidak Hadir',
|
||||||
{
|
action: '',
|
||||||
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: '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 },
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const paginationMeta = {
|
||||||
|
recordCount: protocolRows.length,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
totalPage: 1,
|
||||||
|
hasNext: false,
|
||||||
|
hasPrev: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reacrtivities
|
||||||
|
watch(getActiveRole, () => {
|
||||||
|
activePosition.value = getServicePosition(userActiveRole)
|
||||||
|
initMenus()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getData()
|
||||||
|
initMenus()
|
||||||
|
})
|
||||||
|
|
||||||
|
///// Functions
|
||||||
|
function handleClick(type: string) {
|
||||||
|
if (type === 'draft') {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initMenus() {
|
||||||
|
menus.value = getMenuItems(id, props, user, {
|
||||||
|
encounter: data.value
|
||||||
|
} as any, {
|
||||||
|
protocolTheraphy: paginationMeta,
|
||||||
|
protocolChemotherapy: paginationMeta,
|
||||||
|
medicineProtocolChemotherapy: paginationMeta,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getData() {
|
||||||
|
data.value = await getEncounterData(id)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="mb-4">
|
<div class="bg-white dark:bg-slate-800 p-4 2xl:p-5">
|
||||||
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" />
|
<div class="mb-4 flex">
|
||||||
|
<div>
|
||||||
|
<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">
|
||||||
|
<template v-slot:content1>
|
||||||
|
<EncounterPatientInfo v-if="isShowPatient" :data="data" />
|
||||||
|
</template>
|
||||||
|
<template v-slot:content2>
|
||||||
|
<EncounterHistoryButtonMenu v-if="isShowPatient" />
|
||||||
|
</template>
|
||||||
|
</ContentSwitcher>
|
||||||
</div>
|
</div>
|
||||||
<AppEncounterQuickInfo :data="data" />
|
<SubMenu :data="menus" :initial-active-menu="activeMenu" @change-menu="activeMenu = $event" />
|
||||||
<CompTab
|
|
||||||
:data="tabs"
|
|
||||||
:initial-active-tab="activeTab"
|
|
||||||
@change-tab="activeTab = $event"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -48,9 +48,18 @@ async function setMenu() {
|
|||||||
const activeRoleParts = activeRole ? activeRole.split('|') : []
|
const activeRoleParts = activeRole ? activeRole.split('|') : []
|
||||||
const role = activeRoleParts[0]+(activeRoleParts.length > 1 ? `-${activeRoleParts[1]}` : '')
|
const role = activeRoleParts[0]+(activeRoleParts.length > 1 ? `-${activeRoleParts[1]}` : '')
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const res = await fetch(`/side-menu-items/${role.toLowerCase()}.json`)
|
const res = await fetch(`/side-menu-items/${role.toLowerCase()}.json`)
|
||||||
const rawMenu = await res.text()
|
const rawMenu = await res.text()
|
||||||
navMenu.value = JSON.parse(rawMenu)
|
const parsedMenu = JSON.parse(rawMenu)
|
||||||
|
|
||||||
|
const { user } = useUserStore()
|
||||||
|
if(user.unit_code == 'rehab') {
|
||||||
|
parsedMenu[0].heading = 'Rehab Medik'
|
||||||
|
parsedMenu[0].items = parsedMenu[0].items.filter((item: any) => item.title != 'IGD')
|
||||||
|
}
|
||||||
|
|
||||||
|
navMenu.value = parsedMenu
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const res = await fetch(`/side-menu-items/blank.json`)
|
const res = await fetch(`/side-menu-items/blank.json`)
|
||||||
const rawMenu = await res.text()
|
const rawMenu = await res.text()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
height: 200
|
height?: number
|
||||||
activeTab?: 1 | 2
|
activeTab?: 1 | 2
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ function handleClick(value: 1 | 2) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="content-switcher" :style="`height: ${height}px`">
|
<div class="content-switcher" :style="`height: ${height || 200}px`">
|
||||||
<div :class="`${activeTab === 1 ? 'active' : 'inactive'}`">
|
<div :class="`${activeTab === 1 ? 'active' : 'inactive'}`">
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<div>
|
<div>
|
||||||
@@ -49,7 +49,7 @@ function handleClick(value: 1 | 2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
@apply p-4 2xl:p-5 overflow-hidden
|
@apply p-4 2xl:p-5 overflow-hidden grow
|
||||||
}
|
}
|
||||||
.inactive .content-wrapper {
|
.inactive .content-wrapper {
|
||||||
@apply p-0 w-0
|
@apply p-0 w-0
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
const { class: classProp } = defineProps<{
|
||||||
|
class?: string
|
||||||
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-5 text-center">:</div>
|
<div :class="`w-4 ps-1 ${classProp}`">:</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type EncounterItem } from "../../../../handlers/encounter-process.handler";
|
import { type EncounterItem } from "~/handlers/encounter-init.handler";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialActiveMenu: string
|
initialActiveMenu: string
|
||||||
@@ -18,22 +18,28 @@ function changeMenu(value: string) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-4 flex gap-4">
|
<div class="flex">
|
||||||
<!-- Menu Sidebar -->
|
<!-- Menu Sidebar -->
|
||||||
<div v-if="data.length > 0" class="w-72 flex-shrink-0 rounded-md border bg-white shadow-sm dark:bg-neutral-950">
|
<div v-if="data.length > 0" class="w-56 2xl:w-64 flex-shrink-0 rounded-md border bg-white dark:bg-slate-800 shadow-sm">
|
||||||
<div class="max-h-[calc(100vh-12rem)] overflow-y-auto p-2">
|
<div class="max-h-[calc(100vh-12rem)] overflow-y-auto px-2 py-3">
|
||||||
<button v-for="menu in data" :key="menu.id" :data-active="activeMenu === menu.id"
|
<button
|
||||||
class="w-full rounded-md px-4 py-3 text-left text-sm transition-colors data-[active=false]:text-gray-700 data-[active=false]:hover:bg-gray-100 data-[active=true]:bg-primary data-[active=true]:text-white dark:data-[active=false]:text-gray-300 dark:data-[active=false]:hover:bg-neutral-800"
|
v-for="menu in data"
|
||||||
|
:key="menu.id"
|
||||||
|
:data-active="activeMenu === menu.id"
|
||||||
|
class="w-full rounded-md px-4 py-3 text-left text-xs 2xl:text-sm transition-colors data-[active=false]:text-gray-700 data-[active=false]:hover:bg-gray-100 data-[active=true]:bg-primary data-[active=true]:text-white dark:data-[active=false]:text-gray-300 dark:data-[active=false]:hover:bg-neutral-800"
|
||||||
@click="changeMenu(menu.id)">
|
@click="changeMenu(menu.id)">
|
||||||
{{ menu.title }}
|
{{ menu.title }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Active Menu Content -->
|
<!-- Active Menu Content -->
|
||||||
<div v-if="data.find((m) => m.id === activeMenu)?.component"
|
<div class="p-4 2xl:p-5 flex-grow">
|
||||||
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
<div v-if="data.find((m) => m.id === activeMenu)?.component"
|
||||||
<component :is="data.find((m) => m.id === activeMenu)?.component"
|
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||||
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
<component
|
||||||
|
:is="data.find((m) => m.id === activeMenu)?.component"
|
||||||
|
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function onClick(type: ClickType) {
|
|||||||
@click="onClick('draft')"
|
@click="onClick('draft')"
|
||||||
class="flex items-center gap-2 rounded-full border border-orange-400 bg-orange-50 px-3 py-1 text-sm font-medium text-orange-600 hover:bg-orange-100"
|
class="flex items-center gap-2 rounded-full border border-orange-400 bg-orange-50 px-3 py-1 text-sm font-medium text-orange-600 hover:bg-orange-100"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||||
</svg>
|
</svg>
|
||||||
{{ props.label }}
|
{{ props.label }}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ function btnClick() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
<!-- For components as slots -->
|
||||||
|
<slot />
|
||||||
|
|
||||||
|
<!-- For components passed by props -->
|
||||||
<div v-if="prep.components">
|
<div v-if="prep.components">
|
||||||
<template v-for="cwp in prep.components">
|
<template v-for="cwp in prep.components">
|
||||||
<component
|
<component
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function btnClick() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="prep.addNav" class="m-2 flex items-center">
|
<div v-if="prep.addNav" class="m-2 flex items-center">
|
||||||
<Button size="md" class="rounded-md border border-gray-300 px-4 py-2 text-white sm:text-sm" @click="btnClick">
|
<Button size="default" class="rounded-md border border-gray-300 px-4 py-2 text-white sm:text-sm" @click="btnClick">
|
||||||
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle" />
|
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle" />
|
||||||
{{ prep.addNav.label }}
|
{{ prep.addNav.label }}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
+24
-16
@@ -1,4 +1,5 @@
|
|||||||
import type { Permission, RoleAccess } from '~/models/role'
|
import type { Permission, RoleAccesses } from '~/models/role'
|
||||||
|
import { systemCode } from '~/const/common/role'
|
||||||
|
|
||||||
export interface PageOperationPermission {
|
export interface PageOperationPermission {
|
||||||
canRead: boolean
|
canRead: boolean
|
||||||
@@ -7,7 +8,6 @@ export interface PageOperationPermission {
|
|||||||
canDelete: boolean
|
canDelete: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if user has access to a page
|
* Check if user has access to a page
|
||||||
*/
|
*/
|
||||||
@@ -15,19 +15,27 @@ export function useRBAC() {
|
|||||||
// NOTE: this roles was dummy for testing only, it should taken from the user store
|
// NOTE: this roles was dummy for testing only, it should taken from the user store
|
||||||
const authStore = useUserStore()
|
const authStore = useUserStore()
|
||||||
|
|
||||||
const checkRole = (roleAccess: RoleAccess, _userRoles?: string[]): boolean => {
|
const checkRole = (roleAccesses: RoleAccesses, _userRoles?: string[]): boolean => {
|
||||||
const roles = authStore.userRole
|
const activeRole = authStore.getActiveRole() || ''
|
||||||
return roles.some((role: string) => role === 'system' || (role in roleAccess)) // system by-passes this check
|
if (activeRole === systemCode) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return (activeRole in roleAccesses);
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkPermission = (roleAccess: RoleAccess, permission: Permission, _userRoles?: string[]): boolean => {
|
const checkPermission = (roleAccesses: RoleAccesses, permission: Permission, _userRoles?: string[]): boolean => {
|
||||||
const roles = authStore.userRole
|
const activeRole = authStore.getActiveRole() || ''
|
||||||
return roles.some((role: string) => role === 'system' || roleAccess[role]?.includes(permission)) // system by-passes this check
|
if (activeRole === systemCode) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (activeRole in roleAccesses && roleAccesses[activeRole]) {
|
||||||
|
return roleAccesses[activeRole].includes(permission)
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserPermissions = (roleAccess: RoleAccess, _userRoles?: string[]): Permission[] => {
|
const getUserPermissions = (roleAccess: RoleAccesses, _userRoles?: string[]): Permission[] => {
|
||||||
const roles = authStore.userRole
|
const roles = authStore.userRoles
|
||||||
// const roles = ['admisi']
|
|
||||||
const permissions = new Set<Permission>()
|
const permissions = new Set<Permission>()
|
||||||
|
|
||||||
roles.forEach((role: string) => {
|
roles.forEach((role: string) => {
|
||||||
@@ -39,12 +47,12 @@ export function useRBAC() {
|
|||||||
return Array.from(permissions)
|
return Array.from(permissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasCreateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'C')
|
const hasCreateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'C')
|
||||||
const hasReadAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'R')
|
const hasReadAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'R')
|
||||||
const hasUpdateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'U')
|
const hasUpdateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'U')
|
||||||
const hasDeleteAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'D')
|
const hasDeleteAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'D')
|
||||||
|
|
||||||
const getPagePermissions = (roleAccess: RoleAccess): PageOperationPermission => ({
|
const getPagePermissions = (roleAccess: RoleAccesses): PageOperationPermission => ({
|
||||||
canRead : hasReadAccess(roleAccess),
|
canRead : hasReadAccess(roleAccess),
|
||||||
canCreate: hasCreateAccess(roleAccess),
|
canCreate: hasCreateAccess(roleAccess),
|
||||||
canUpdate: hasUpdateAccess(roleAccess),
|
canUpdate: hasUpdateAccess(roleAccess),
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
export const systemCode = 'system'
|
||||||
|
|
||||||
|
export const rehabInstCode = 'rehab'
|
||||||
|
export const rehabUnitCode = 'rehab'
|
||||||
|
|
||||||
|
export const headPosCode = 'head' // head position
|
||||||
|
export const respPosCode = 'resp' // responsible position, verificator
|
||||||
|
|
||||||
|
export type UnitLevel =
|
||||||
|
'inst' | // installation
|
||||||
|
'unit' | // unit / poly
|
||||||
|
'spec' | // specialist
|
||||||
|
'subspec' // subspecialist
|
||||||
|
|
||||||
|
export const medicalRoles = [
|
||||||
|
'emp|doc', // doctor
|
||||||
|
'emp|nur', // nurse
|
||||||
|
'emp|miw', // midwife
|
||||||
|
'emp|thr', // therapist
|
||||||
|
'emp|nut', // nutritionist
|
||||||
|
'emp|pha', // pharmacy
|
||||||
|
'emp|lab' // laborant
|
||||||
|
]
|
||||||
|
|
||||||
|
export const serviceRoles = [
|
||||||
|
'emp|reg',
|
||||||
|
...medicalRoles,
|
||||||
|
]
|
||||||
|
|
||||||
|
export function genSpecHeadCode(unit_level: UnitLevel, unit_code: string): string {
|
||||||
|
return `${unit_level}|${unit_code}|${headPosCode}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function genUnitRespCode(unit_level: UnitLevel, unit_code: string): string {
|
||||||
|
return `${unit_level}|${unit_code}|${respPosCode}`
|
||||||
|
}
|
||||||
@@ -4,13 +4,13 @@ import type { Permission } from "~/models/role";
|
|||||||
// export type Keys = 'key1' | 'key2' | 'key3' | etc
|
// export type Keys = 'key1' | 'key2' | 'key3' | etc
|
||||||
|
|
||||||
export const permissions: Record<string, Record<string, Permission[]>> = {
|
export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||||
'/outpatient/registration-queue': {
|
'/ambulatory/registration-queue': {
|
||||||
'emp|reg': ['R', 'U', 'D'],
|
'emp|reg': ['R', 'U', 'D'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter-queue': {
|
'/ambulatory/encounter-queue': {
|
||||||
'emp|nur': ['R', 'U', 'D'],
|
'emp|nur': ['R', 'U', 'D'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter': {
|
'/ambulatory/encounter': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
'emp|nur': ['R'],
|
'emp|nur': ['R'],
|
||||||
@@ -21,10 +21,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter/add': {
|
'/ambulatory/encounter/add': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter/[id]/detail': {
|
'/ambulatory/encounter/[id]': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
'emp|nur': ['R'],
|
'emp|nur': ['R'],
|
||||||
@@ -35,10 +35,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|lab': ['R'],
|
'emp|lab': ['R'],
|
||||||
'emp|rad': ['R'],
|
'emp|rad': ['R'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter/[id]/edit': {
|
'/ambulatory/encounter/[id]/edit': {
|
||||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||||
},
|
},
|
||||||
'/outpatient/encounter/[id]/process': {
|
'/ambulatory/encounter/[id]/process': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
'emp|nur': ['R', 'U'],
|
'emp|nur': ['R', 'U'],
|
||||||
'emp|thr': ['R', 'U'],
|
'emp|thr': ['R', 'U'],
|
||||||
@@ -48,10 +48,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
|||||||
'emp|lab': ['R', 'U'],
|
'emp|lab': ['R', 'U'],
|
||||||
'emp|rad': ['R', 'U'],
|
'emp|rad': ['R', 'U'],
|
||||||
},
|
},
|
||||||
'/outpatient/consulation': {
|
'/ambulatory/consulation': {
|
||||||
'emp|doc': ['R'],
|
'emp|doc': ['R'],
|
||||||
},
|
},
|
||||||
'/outpatient/consulation/[id]/process': {
|
'/ambulatory/consulation/[id]/process': {
|
||||||
'emp|doc': ['R', 'U'],
|
'emp|doc': ['R', 'U'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Permission } from "~/models/role";
|
||||||
|
|
||||||
|
// Should we define the keys first?
|
||||||
|
// export type Keys = 'key1' | 'key2' | 'key3' | etc
|
||||||
|
|
||||||
|
export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||||
|
'/chemotherapy/adm': {
|
||||||
|
'emp|reg': ['R'],
|
||||||
|
'emp|nur': ['R', 'U', 'D'],
|
||||||
|
},
|
||||||
|
'/outpatient/series': {
|
||||||
|
'emp|nur': ['R', 'U', 'D'],
|
||||||
|
'emp|doc': ['R', 'U', 'D'],
|
||||||
|
'emp|thr': ['R', 'U', 'D'],
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { isValidDate } from '~/lib/date'
|
import { isValidDate } from '~/lib/date'
|
||||||
import { medicalPositions } from '~/lib/roles'
|
import { medicalRoles } from '~/const/common/role'
|
||||||
|
|
||||||
export interface EncounterItem {
|
export interface EncounterItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -444,7 +444,9 @@ export function getMenuItems(
|
|||||||
data: EncounterListData,
|
data: EncounterListData,
|
||||||
meta: any,
|
meta: any,
|
||||||
) {
|
) {
|
||||||
const normalClassCode = props.classCode === 'ambulatory' ? 'outpatient' : props.classCode
|
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 currentKeys = injectComponents(id, data, meta)
|
||||||
const defaultItems: EncounterItem[] = Object.values(currentKeys)
|
const defaultItems: EncounterItem[] = Object.values(currentKeys)
|
||||||
const listItemsForOutpatientRehab = mergeArrayAt(
|
const listItemsForOutpatientRehab = mergeArrayAt(
|
||||||
@@ -456,14 +458,14 @@ export function getMenuItems(
|
|||||||
getItemsAll('ambulatory', 'chemo', defaultItems),
|
getItemsAll('ambulatory', 'chemo', defaultItems),
|
||||||
)
|
)
|
||||||
const listItems: Record<string, Record<string, Record<string, any>>> = {
|
const listItems: Record<string, Record<string, Record<string, any>>> = {
|
||||||
'installation|outpatient': {
|
'installation|ambulatory': {
|
||||||
'unit|rehab': {
|
'unit|rehab': {
|
||||||
items: listItemsForOutpatientRehab,
|
items: listItemsForOutpatientRehab,
|
||||||
roles: medicalPositions,
|
roles: medicalRoles,
|
||||||
},
|
},
|
||||||
'unit|chemo': {
|
'unit|chemo': {
|
||||||
items: listItemsForOutpatientChemo,
|
items: listItemsForOutpatientChemo,
|
||||||
roles: medicalPositions,
|
roles: medicalRoles,
|
||||||
},
|
},
|
||||||
all: getItemsAll('ambulatory', 'all', defaultItems),
|
all: getItemsAll('ambulatory', 'all', defaultItems),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
import CardContent from '~/components/pub/ui/card/CardContent.vue'
|
import CardContent from '~/components/pub/ui/card/CardContent.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
|
||||||
const contentFrame = computed(() => route.meta.contentFrame)
|
const contentFrame = computed(() => route.meta.contentFrame)
|
||||||
|
const contentPadding = computed(() => route.meta.contentPadding || 'p-4 2xl:p-5')
|
||||||
|
const contentUseCard = computed(() => route.meta.contentUseCard === false ? false : true)
|
||||||
|
console.log(route.meta.contentUseCard,contentUseCard)
|
||||||
const contentFrameClass = computed(() => {
|
const contentFrameClass = computed(() => {
|
||||||
switch (contentFrame.value) {
|
switch (contentFrame.value) {
|
||||||
case 'cf-container-2xl':
|
case 'cf-container-2xl':
|
||||||
@@ -28,13 +33,14 @@ const contentFrameClass = computed(() => {
|
|||||||
<LayoutAppSidebar />
|
<LayoutAppSidebar />
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<LayoutHeader />
|
<LayoutHeader />
|
||||||
<div :class="`w-full p-4 2xl:p-5 flex justify-center ${contentFrameClass}`">
|
<div :class="`w-full flex justify-center ${contentPadding} ${contentFrameClass}`">
|
||||||
<div v-if="contentFrame !== 'cf-no-frame'">
|
<div v-if="contentFrame !== 'cf-no-frame'">
|
||||||
<Card>
|
<Card v-if="contentUseCard">
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<slot />
|
<slot />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
<slot v-else />
|
||||||
</div>
|
</div>
|
||||||
<slot v-else />
|
<slot v-else />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+13
-11
@@ -1,14 +1,16 @@
|
|||||||
export const medicalPositions = ['emp|doc', 'emp|lab', 'emp|mid', 'emp|nur', 'emp|nut', 'emp|pha', 'emp|reg']
|
import { medicalRoles, respPosCode } from '~/const/common/role'
|
||||||
const verificatorRole = 'verificator'
|
|
||||||
|
|
||||||
export function getPositionAs(roleAccess: string): string {
|
export function getServicePosition(role?: string): string {
|
||||||
if (roleAccess.includes('|')) {
|
if(!role) {
|
||||||
if (medicalPositions.includes(roleAccess)) {
|
return 'none'
|
||||||
return 'medical'
|
}
|
||||||
}
|
if (medicalRoles.includes(role)) {
|
||||||
if (roleAccess.includes(verificatorRole)) {
|
return 'medical'
|
||||||
return 'verificator'
|
} else if (role === 'emp|reg') {
|
||||||
}
|
return 'registration'
|
||||||
|
} else if (role.includes('|resp')) {
|
||||||
|
return 'verificator'
|
||||||
|
} else {
|
||||||
|
return 'none'
|
||||||
}
|
}
|
||||||
return 'none'
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default defineNuxtRouteMiddleware((to) => {
|
|||||||
const requiredRoles = to.meta.roles as string[]
|
const requiredRoles = to.meta.roles as string[]
|
||||||
if (requiredRoles && requiredRoles.length > 0) {
|
if (requiredRoles && requiredRoles.length > 0) {
|
||||||
// FIXME: change this dummy roles, when api is ready
|
// FIXME: change this dummy roles, when api is ready
|
||||||
const userRoles = authStore.userRole
|
const userRoles = authStore.userRoles
|
||||||
// const userRoles = ['admisi']
|
// const userRoles = ['admisi']
|
||||||
const hasRequiredRole = requiredRoles.some((role) => userRoles.includes(role))
|
const hasRequiredRole = requiredRoles.some((role) => userRoles.includes(role))
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -14,9 +14,9 @@ export interface AuthState {
|
|||||||
|
|
||||||
export type Permission = 'C' | 'R' | 'U' | 'D'
|
export type Permission = 'C' | 'R' | 'U' | 'D'
|
||||||
|
|
||||||
export interface RoleAccess {
|
export interface RoleAccesses {
|
||||||
[role: string]: Permission[]
|
[role: string]: Permission[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PagePath = keyof typeof PAGE_PERMISSIONS
|
// export type PagePath = keyof typeof PAGE_PERMISSIONS
|
||||||
export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
|
// export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
|
||||||
|
|||||||
+22
-15
@@ -1,50 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PagePermission } from '~/models/role'
|
// Pubs
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
|
// Models & Consts
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
|
|
||||||
|
// Apps
|
||||||
|
import Content from '~/components/content/encounter/entry.vue'
|
||||||
|
|
||||||
|
// Page meta
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
roles: ['emp|reg'],
|
||||||
title: 'Edit Kunjungan',
|
title: 'Edit Kunjungan',
|
||||||
contentFrame: 'cf-full-width',
|
contentFrame: 'cf-full-width',
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
||||||
})
|
})
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/outpatient/encounter']
|
// Preps role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/[id]/edit'] || {}
|
||||||
const { checkRole, hasUpdateAccess } = useRBAC()
|
const { checkRole, hasUpdateAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page, need to use try - catch for proper handling
|
||||||
const hasAccess = checkRole(roleAccess)
|
const hasAccess = checkRole(roleAccess)
|
||||||
if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
throw createError({
|
navigateTo('/403')
|
||||||
statusCode: 403,
|
|
||||||
statusMessage: 'Access denied',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define permission-based computed properties
|
// Define permission-based computed properties
|
||||||
const canUpdate = hasUpdateAccess(roleAccess)
|
const canUpdate = hasUpdateAccess(roleAccess)
|
||||||
|
|
||||||
// Get encounter ID from route params
|
// Get encounter ID from route params
|
||||||
const encounterId = computed(() => {
|
const encounter_id = computed(() => {
|
||||||
const id = route.params.id
|
const id = route.params.id
|
||||||
return typeof id === 'string' ? parseInt(id) : 0
|
return typeof id === 'string' ? parseInt(id) : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// User info
|
||||||
|
const { user } = useUserStore()
|
||||||
|
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="canUpdate">
|
<div v-if="canUpdate">
|
||||||
<ContentEncounterEntry
|
<Content
|
||||||
:id="encounterId"
|
:id="encounter_id"
|
||||||
class-code="ambulatory"
|
class-code="ambulatory"
|
||||||
sub-class-code="reg"
|
:sub-class-code="subClassCode"
|
||||||
form-type="Edit"
|
form-type="Edit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Pubs
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
|
// Models & Consts
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
|
import { medicalRoles } from '~/const/common/role'
|
||||||
|
|
||||||
|
// Page meta
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: medicalRoles,
|
||||||
|
title: 'Detail Kunjungan',
|
||||||
|
contentFrame: 'cf-container-md',
|
||||||
|
})
|
||||||
|
|
||||||
|
// Define common things
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// Prep role checking
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/[id]'] || {}
|
||||||
|
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
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<ContentOutpatientEncounterDetail :patient-id="Number(route.params.id)" />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
+13
-7
@@ -1,18 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
// Pubs
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
import Content from '~/components/content/encounter/process-next.vue'
|
|
||||||
|
// AppS
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
|
import Content from '~/components/content/encounter/process.vue'
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
|
roles: ['emp|doc', 'emp|nur', 'emp|miw', 'emp|nut', 'emp|lab', 'emp|pha', 'emp|thr'],
|
||||||
title: 'Tambah Kunjungan',
|
title: 'Proses Kunjungan',
|
||||||
contentFrame: 'cf-full-width',
|
contentFrame: 'cf-full-width',
|
||||||
|
contentPadding: 'p-0',
|
||||||
|
contentUseCard: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/[id]/process'] || {}
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
@@ -29,11 +34,12 @@ const route = useRoute()
|
|||||||
useHead({
|
useHead({
|
||||||
title: () => `${route.meta.title}`,
|
title: () => `${route.meta.title}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="canRead">
|
<div v-if="canRead">
|
||||||
<Content class-code="ambulatory" sub-class-code="reg" />
|
<Content class-code="ambulatory" />
|
||||||
</div>
|
</div>
|
||||||
<Error v-else :status-code="403" />
|
<Error v-else :status-code="403" />
|
||||||
</template>
|
</template>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
import Content from '~/components/content/encounter/entry.vue'
|
import Content from '~/components/content/encounter/entry.vue'
|
||||||
@@ -13,7 +13,7 @@ definePageMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
const roleAccess: Record<string, Permission[]> = permissions['/ambulatory/encounter/add'] || {}
|
||||||
const { checkRole, hasReadAccess, hasCreateAccess } = useRBAC()
|
const { checkRole, hasReadAccess, hasCreateAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
+20
-18
@@ -1,43 +1,45 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
// Pubs
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
|
// Models & Consts
|
||||||
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
|
|
||||||
|
// Apps
|
||||||
import Content from '~/components/content/encounter/list.vue'
|
import Content from '~/components/content/encounter/list.vue'
|
||||||
|
|
||||||
|
// Page meta
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
// middleware: ['rbac'],
|
||||||
roles: ['emp|reg', 'emp|nur', 'emp|doc', 'emp|miw', 'emp|thr', 'emp|nut', 'emp|pha', 'emp|lab'],
|
// roles: ['emp|reg', 'emp|nur', 'emp|doc', 'emp|miw', 'emp|thr', 'emp|nut', 'emp|pha', 'emp|lab'],
|
||||||
title: 'Daftar Kunjungan',
|
title: 'Daftar Kunjungan',
|
||||||
contentFrame: 'cf-full-width',
|
contentFrame: 'cf-full-width',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Define common things
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
const roleAccess: Record<string, Permission[]> = permissions[route.path] || {}
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasCreateAccess, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
const hasAccess = checkRole(roleAccess)
|
const hasAccess = checkRole(roleAccess)
|
||||||
if (!hasAccess) {
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
if (!hasAccess || !canRead) {
|
||||||
navigateTo('/403')
|
navigateTo('/403')
|
||||||
}
|
}
|
||||||
|
const canCreate = hasCreateAccess(roleAccess)
|
||||||
// Define permission-based computed properties
|
|
||||||
const canRead = hasReadAccess(roleAccess)
|
|
||||||
|
|
||||||
// Page needs
|
// Page needs
|
||||||
const route = useRoute()
|
|
||||||
useHead({
|
useHead({
|
||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, getActiveRole } = useUserStore()
|
// User info
|
||||||
// const activeRole = getActiveRole()
|
const { user } = useUserStore()
|
||||||
// const activeRoleParts = activeRole ? activeRole.split('|') : ['', '']
|
|
||||||
// const activeRolePos = activeRoleParts[0] // reaching this page means it is already set
|
|
||||||
// const activeRoleType = activeRoleParts[1] == 'rehab' ? activeRoleParts[1] : ''
|
|
||||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -46,7 +48,7 @@ const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
|||||||
<Content
|
<Content
|
||||||
class-code="ambulatory"
|
class-code="ambulatory"
|
||||||
:sub-class-code="subClassCode"
|
:sub-class-code="subClassCode"
|
||||||
type="encounter"
|
:can-create="canCreate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Error
|
<Error
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PagePermission } from '~/models/role'
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||||
|
import ContentChemotherapyAdminList from '~/components/content/chemotherapy/admin-list.vue'
|
||||||
|
import ContentChemotherapyVerification from '~/components/content/chemotherapy/verification.vue'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||||
|
title: 'Kemoterapi Admin',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => 'Verifikasi Jadwal Pasien',
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor'] || {}
|
||||||
|
|
||||||
|
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 = true // hasReadAccess(roleAccess)
|
||||||
|
|
||||||
|
const mode = computed(() => route.params.mode as string)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<ContentChemotherapyVerification />
|
||||||
|
</div>
|
||||||
|
<Error
|
||||||
|
v-else
|
||||||
|
:status-code="403"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PagePermission } from '~/models/role'
|
||||||
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||||
|
import ContentChemotherapyAdminList from '~/components/content/chemotherapy/admin-list.vue'
|
||||||
|
import ContentChemotherapyVerification from '~/components/content/chemotherapy/verification.vue'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||||
|
title: 'Kemoterapi Admin',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => {
|
||||||
|
const mode = route.params.mode as string
|
||||||
|
if (mode === 'admin') {
|
||||||
|
return 'Administrasi Pasien Rawat Jalan Kemoterapi'
|
||||||
|
} else if (mode === 'series') {
|
||||||
|
return 'Proses Jadwal Pasien'
|
||||||
|
}
|
||||||
|
return route.meta.title as string
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor'] || {}
|
||||||
|
|
||||||
|
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 = true // hasReadAccess(roleAccess)
|
||||||
|
|
||||||
|
const mode = computed(() => route.params.mode as string)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<ContentChemotherapyAdminList v-if="mode === 'admin'" />
|
||||||
|
<ContentChemotherapyProcess v-else-if="mode === 'series'" />
|
||||||
|
<Error v-else :status-code="404" status-message="Mode tidak ditemukan" />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// Redirect to list page - the process page is now at [id]/index.vue
|
||||||
|
const route = useRoute()
|
||||||
|
navigateTo('/outpation-action/chemotherapy/list')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div />
|
||||||
|
</template>
|
||||||
+10
-11
@@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PagePermission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
|
import { permissions } from '~/const/page-permission/chemoteraphy'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||||
title: 'Detail Surat Kontrol',
|
title: 'Daftar Kempterapi',
|
||||||
contentFrame: 'cf-container-md',
|
contentFrame: 'cf-full-width',
|
||||||
})
|
})
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -16,25 +16,24 @@ useHead({
|
|||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
const roleAccess: Record<string, Permission[]> = permissions['/chemotherapy'] || {}
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
const hasAccess = checkRole(roleAccess)
|
const hasAccess = checkRole(roleAccess)
|
||||||
// if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
// navigateTo('/403')
|
navigateTo('/403')
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Define permission-based computed properties
|
// Define permission-based computed properties
|
||||||
// const canRead = hasReadAccess(roleAccess)
|
const canRead = hasReadAccess(roleAccess)
|
||||||
const canRead = true
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="canRead">
|
<div v-if="canRead">
|
||||||
<ContentOutpatientEncounterDetail :patient-id="Number(route.params.id)" />
|
<ContentChemotherapyList />
|
||||||
</div>
|
</div>
|
||||||
<Error v-else :status-code="403" />
|
<Error v-else :status-code="403" />
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">
|
||||||
|
<div class="mb-5 text-base font-semibold">Hello world!!</div>
|
||||||
|
<div>You are accessing "{{ route.fullPath }}"</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
import Content from '~/components/content/encounter/entry.vue'
|
import Content from '~/components/content/encounter/entry.vue'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
import { permissions } from '~/const/page-permission/emergency'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
import Content from '~/components/content/encounter/list.vue'
|
import Content from '~/components/content/encounter/list.vue'
|
||||||
@@ -13,7 +13,8 @@ definePageMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
const route = useRoute()
|
||||||
|
const roleAccess: Record<string, Permission[]> = permissions[route.path] || {}
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
@@ -26,18 +27,12 @@ if (!hasAccess) {
|
|||||||
const canRead = hasReadAccess(roleAccess)
|
const canRead = hasReadAccess(roleAccess)
|
||||||
|
|
||||||
// Page needs
|
// Page needs
|
||||||
const route = useRoute()
|
|
||||||
useHead({
|
useHead({
|
||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, getActiveRole } = useUserStore()
|
const { user } = useUserStore()
|
||||||
// const activeRole = getActiveRole()
|
|
||||||
// const activeRoleParts = activeRole ? activeRole.split('|') : ['', '']
|
|
||||||
// const activeRolePos = activeRoleParts[0] // reaching this page means it is already set
|
|
||||||
// const activeRoleType = activeRoleParts[1] == 'rehab' ? activeRoleParts[1] : ''
|
|
||||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -45,7 +40,7 @@ const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
|||||||
<div v-if="canRead">
|
<div v-if="canRead">
|
||||||
<Content
|
<Content
|
||||||
class-code="emergency"
|
class-code="emergency"
|
||||||
sub-class-code="reg"
|
:sub-class-code="subClassCode"
|
||||||
type="encounter"
|
type="encounter"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">
|
||||||
|
<div class="mb-5 text-base font-semibold">Hello world!!</div>
|
||||||
|
<div>You are accessing "{{ route.fullPath }}"</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
import { permissions } from '~/const/page-permission/ambulatory'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
import Content from '~/components/content/encounter/entry.vue'
|
import Content from '~/components/content/encounter/entry.vue'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Permission } from '~/models/role'
|
import type { Permission } from '~/models/role'
|
||||||
import { permissions } from '~/const/page-permission/outpatient'
|
import { permissions } from '~/const/page-permission/inpatient'
|
||||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||||
|
|
||||||
import Content from '~/components/content/encounter/list.vue'
|
import Content from '~/components/content/encounter/list.vue'
|
||||||
@@ -13,7 +13,7 @@ definePageMeta({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Preps role checking
|
// Preps role checking
|
||||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
const roleAccess: Record<string, Permission[]> = permissions['/inpatient/encounter'] || {}
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
// Check if user has access to this page
|
// Check if user has access to this page
|
||||||
@@ -31,11 +31,7 @@ useHead({
|
|||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user, getActiveRole } = useUserStore()
|
const { user } = useUserStore()
|
||||||
// const activeRole = getActiveRole()
|
|
||||||
// const activeRoleParts = activeRole ? activeRole.split('|') : ['', '']
|
|
||||||
// const activeRolePos = activeRoleParts[0] // reaching this page means it is already set
|
|
||||||
// const activeRoleType = activeRoleParts[1] == 'rehab' ? activeRoleParts[1] : ''
|
|
||||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<FlowRehabRegistrationHome />
|
|
||||||
</template>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { PagePermission } from '~/models/role'
|
|
||||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
middleware: ['rbac'],
|
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
|
||||||
title: 'Tambah SEP Prosedur',
|
|
||||||
contentFrame: 'cf-full-width',
|
|
||||||
})
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
useHead({
|
|
||||||
title: () => route.meta.title as string,
|
|
||||||
})
|
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
|
||||||
|
|
||||||
const { checkRole, hasCreateAccess } = useRBAC()
|
|
||||||
|
|
||||||
// Check if user has access to this page
|
|
||||||
const hasAccess = checkRole(roleAccess)
|
|
||||||
if (!hasAccess) {
|
|
||||||
throw createError({
|
|
||||||
statusCode: 403,
|
|
||||||
statusMessage: 'Access denied',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define permission-based computed properties
|
|
||||||
const canCreate = hasCreateAccess(roleAccess)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div v-if="canCreate">
|
|
||||||
<ContentDoctorAdd />
|
|
||||||
</div>
|
|
||||||
<PubMyUiError v-else :status-code="403" />
|
|
||||||
</template>
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">
|
||||||
|
<div class="mb-5 text-base font-semibold">Hello world!!</div>
|
||||||
|
<div>You are accessing "{{ route.fullPath }}"</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-10 text-center">
|
||||||
|
<div class="mb-5 text-base font-semibold">Hello world!!</div>
|
||||||
|
<div>You are accessing "{{ route.fullPath }}"</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -5,7 +5,7 @@ import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
|||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: ['rbac'],
|
middleware: ['rbac'],
|
||||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
roles: [],
|
||||||
title: 'Daftar Dokter',
|
title: 'Daftar Dokter',
|
||||||
contentFrame: 'cf-full-width',
|
contentFrame: 'cf-full-width',
|
||||||
})
|
})
|
||||||
@@ -16,7 +16,7 @@ useHead({
|
|||||||
title: () => route.meta.title as string,
|
title: () => route.meta.title as string,
|
||||||
})
|
})
|
||||||
|
|
||||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/tools-equipment-src/medicine']!
|
||||||
|
|
||||||
const { checkRole, hasReadAccess } = useRBAC()
|
const { checkRole, hasReadAccess } = useRBAC()
|
||||||
|
|
||||||
|
|||||||
+12
-7
@@ -1,19 +1,21 @@
|
|||||||
export const useUserStore = defineStore(
|
export const useUserStore = defineStore(
|
||||||
'user',
|
'user',
|
||||||
() => {
|
() => {
|
||||||
|
// should be auth type
|
||||||
const user = ref<any | null>(null)
|
const user = ref<any | null>(null)
|
||||||
// const token = useCookie('authentication')
|
// const token = useCookie('authentication')
|
||||||
|
|
||||||
const isAuthenticated = computed(() => !!user.value)
|
const isAuthenticated = computed(() => !!user.value)
|
||||||
|
|
||||||
const userRole = computed(() => {
|
const userRoles = computed(() => {
|
||||||
return user.value?.roles || []
|
return user.value?.roles || []
|
||||||
// return roles.map((input: string) => {
|
|
||||||
// const parts = input.split('|')
|
|
||||||
// return parts.length > 1 ? parts[1]: parts[0]
|
|
||||||
// })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const userActiveRole = ref('') // watched user failed, so create a ref here
|
||||||
|
// computed(() => {
|
||||||
|
// // return user.value?.activeRole || ''
|
||||||
|
// })
|
||||||
|
|
||||||
const login = async (userData: any) => {
|
const login = async (userData: any) => {
|
||||||
user.value = userData
|
user.value = userData
|
||||||
}
|
}
|
||||||
@@ -25,10 +27,11 @@ export const useUserStore = defineStore(
|
|||||||
const setActiveRole = (role: string) => {
|
const setActiveRole = (role: string) => {
|
||||||
if (user.value && user.value.roles.includes(role)) {
|
if (user.value && user.value.roles.includes(role)) {
|
||||||
user.value.activeRole = role
|
user.value.activeRole = role
|
||||||
|
userActiveRole.value = role
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getActiveRole = () => {
|
const getActiveRole = (): string | undefined => {
|
||||||
if (user.value?.activeRole) {
|
if (user.value?.activeRole) {
|
||||||
return user.value.activeRole
|
return user.value.activeRole
|
||||||
}
|
}
|
||||||
@@ -36,12 +39,14 @@ export const useUserStore = defineStore(
|
|||||||
user.value.activeRole = user.value.roles[0]
|
user.value.activeRole = user.value.roles[0]
|
||||||
return user.value.activeRole
|
return user.value.activeRole
|
||||||
}
|
}
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
userRole,
|
userRoles,
|
||||||
|
userActiveRole,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
setActiveRole,
|
setActiveRole,
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"icon": "i-lucide-stethoscope",
|
"icon": "i-lucide-stethoscope",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Konsultasi",
|
"title": "Konsultasi",
|
||||||
"icon": "i-lucide-building-2",
|
"icon": "i-lucide-building-2",
|
||||||
"link": "/outpatient/consultation"
|
"link": "/ambulatory/consultation"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Rawat Jalan",
|
"title": "Rawat Jalan",
|
||||||
"icon": "i-lucide-stethoscope",
|
"icon": "i-lucide-stethoscope",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IGD",
|
"title": "IGD",
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"title": "Antrian Poliklinik",
|
"title": "Antrian Poliklinik",
|
||||||
"link": "/outpatient/encounter-queue"
|
"link": "/ambulatory/encounter-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Rawat Jalan",
|
"title": "Rawat Jalan",
|
||||||
"icon": "i-lucide-stethoscope",
|
"icon": "i-lucide-stethoscope",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IGD",
|
"title": "IGD",
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"title": "Antrian Pendaftaran",
|
"title": "Antrian Pendaftaran",
|
||||||
"link": "/outpatient/registration-queue"
|
"link": "/ambulatory/registration-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,19 +13,19 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"title": "Antrian Pendaftaran",
|
"title": "Antrian Pendaftaran",
|
||||||
"link": "/outpatient/registration-queue"
|
"link": "/ambulatory/registration-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Antrian Poliklinik",
|
"title": "Antrian Poliklinik",
|
||||||
"link": "/outpatient/encounter-queue"
|
"link": "/ambulatory/encounter-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Konsultasi",
|
"title": "Konsultasi",
|
||||||
"link": "/outpatient/consultation"
|
"link": "/ambulatory/consultation"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,19 +13,19 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"title": "Antrian Pendaftaran",
|
"title": "Antrian Pendaftaran",
|
||||||
"link": "/outpatient/registration-queue"
|
"link": "/ambulatory/registration-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Antrian Poliklinik",
|
"title": "Antrian Poliklinik",
|
||||||
"link": "/outpatient/encounter-queue"
|
"link": "/ambulatory/encounter-queue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kunjungan",
|
"title": "Kunjungan",
|
||||||
"link": "/outpatient/encounter"
|
"link": "/ambulatory/encounter"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Konsultasi",
|
"title": "Konsultasi",
|
||||||
"link": "/outpatient/consultation"
|
"link": "/ambulatory/consultation"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -389,4 +389,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user