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 recAction = inject<Ref<string>>('rec_action')!
|
||||
const recItem = inject<Ref<any>>('rec_item')!
|
||||
const activeServicePosition = inject<Ref<string>>('activeServicePosition')! // previously: activePosition
|
||||
|
||||
const activeKey = ref<string | null>(null)
|
||||
const activePosition = inject<Ref<string>>('position')!
|
||||
const linkItemsFiltered = ref<LinkItem[]>([])
|
||||
const linkItemsBase: LinkItem[] = [
|
||||
{
|
||||
label: 'Nothing',
|
||||
value: 'nothing',
|
||||
icon: 'i-lucide-file',
|
||||
},
|
||||
]
|
||||
const linkItems: LinkItem[] = [
|
||||
const baseLinkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Detail',
|
||||
value: 'detail',
|
||||
onClick: () => {
|
||||
detail()
|
||||
proceedItem(ActionEvents.showDetail)
|
||||
},
|
||||
icon: 'i-lucide-eye',
|
||||
},
|
||||
]
|
||||
|
||||
const medicalLinkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Process',
|
||||
value: 'edit',
|
||||
value: 'process',
|
||||
onClick: () => {
|
||||
edit()
|
||||
proceedItem(ActionEvents.showProcess)
|
||||
},
|
||||
icon: 'i-lucide-pencil',
|
||||
icon: 'i-lucide-shuffle',
|
||||
},
|
||||
]
|
||||
|
||||
const regLinkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Print',
|
||||
value: 'print',
|
||||
onClick: () => {
|
||||
print()
|
||||
proceedItem(ActionEvents.showPrint)
|
||||
},
|
||||
icon: 'i-lucide-printer',
|
||||
},
|
||||
@@ -48,7 +48,7 @@ const linkItems: LinkItem[] = [
|
||||
label: 'Batalkan',
|
||||
value: 'cancel',
|
||||
onClick: () => {
|
||||
cancel()
|
||||
proceedItem(ActionEvents.showCancel)
|
||||
},
|
||||
icon: 'i-lucide-circle-x',
|
||||
},
|
||||
@@ -56,65 +56,49 @@ const linkItems: LinkItem[] = [
|
||||
label: 'Hapus',
|
||||
value: 'remove',
|
||||
onClick: () => {
|
||||
remove()
|
||||
proceedItem(ActionEvents.showConfirmDelete)
|
||||
},
|
||||
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
|
||||
recAction.value = ActionEvents.showDetail
|
||||
recItem.value = props.rec
|
||||
recAction.value = action
|
||||
}
|
||||
|
||||
function edit() {
|
||||
recId.value = props.rec.id || 0
|
||||
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) {
|
||||
function getLinks() {
|
||||
switch (activeServicePosition.value) {
|
||||
case 'medical':
|
||||
linkItemsFiltered.value = [...linkItems]
|
||||
linkItemsFiltered.value = [...baseLinkItems, ...medicalLinkItems]
|
||||
break
|
||||
case 'verificator':
|
||||
linkItemsFiltered.value = [
|
||||
...linkItems.filter((item) => ['detail', 'print'].includes(item.value || '')),
|
||||
]
|
||||
case 'registration':
|
||||
linkItemsFiltered.value = [...baseLinkItems, ...regLinkItems]
|
||||
case 'unit|resp':
|
||||
linkItemsFiltered.value = [...baseLinkItems]
|
||||
break
|
||||
default:
|
||||
linkItemsFiltered.value = [...linkItemsBase]
|
||||
linkItemsFiltered.value = voidLinkItems
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
getLinks(activePosition.value)
|
||||
|
||||
watch(activePosition, () => {
|
||||
getLinks(activePosition.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<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">
|
||||
import DataTable from '~/components/pub/my-ui/data-table/data-table.vue'
|
||||
import { config } from './list.cfg'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -7,7 +8,7 @@ const props = defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PubMyUiDataTable
|
||||
<DataTable
|
||||
v-bind="config"
|
||||
:rows="props.data"
|
||||
/>
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import { genderCodes } from '~/const/key-val/person';
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
const props = defineProps<{
|
||||
data: Encounter
|
||||
}>()
|
||||
|
||||
let address = ''
|
||||
let address = ref('')
|
||||
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) {
|
||||
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) {
|
||||
dpjp = props.data.appointment_doctor.employee.person.name
|
||||
dpjp.value = props.data.appointment_doctor.employee.person.name
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||
<div class="w-full">
|
||||
<!-- Data Pasien -->
|
||||
<h2 class="mb-2 font-semibold md:text-base 2xl:text-lg">
|
||||
{{ data.patient.person.name }} - {{ data.patient.number }}
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-3">
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<DE.Cell>
|
||||
<DE.Label class="font-semibold">No. RM</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ data.patient.person.birthDate?.substring(0, 10) }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ data.patient.person.gender_code }}
|
||||
{{ genderCodes[data.patient.person.gender_code as keyof typeof genderCodes] }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label class="font-semibold">Alamat</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
<div v-html="address"></div>
|
||||
</DE.Field>
|
||||
@@ -54,35 +54,39 @@ if (props.data.responsible_doctor) {
|
||||
</DE.Block>
|
||||
</div>
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<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>
|
||||
{{ data.visitDate.substring(0, 10) }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ data.unit?.name }}
|
||||
</DE.Field>
|
||||
</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>
|
||||
{{ dpjp }}
|
||||
{{ data.unit?.name }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
<div>
|
||||
<DE.Block
|
||||
mode="preview"
|
||||
labelSize="large"
|
||||
>
|
||||
<DE.Block mode="preview">
|
||||
<DE.Cell>
|
||||
<DE.Label position="dynamic" class="font-semibold">DPJP</DE.Label>
|
||||
<DE.Colon />
|
||||
<DE.Field>
|
||||
{{ dpjp }}
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
<DE.Cell>
|
||||
<DE.Label
|
||||
position="dynamic"
|
||||
@@ -90,6 +94,7 @@ if (props.data.responsible_doctor) {
|
||||
>
|
||||
Billing
|
||||
</DE.Label>
|
||||
<DE.Colon class="pt-1" />
|
||||
<DE.Field class="text-base 2xl:text-lg">
|
||||
Rp. 000.000
|
||||
<!-- {{ data }} -->
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import { Calendar, Hospital, UserCheck, UsersRound } from 'lucide-vue-next'
|
||||
import SummaryCard from '~/components/pub/my-ui/summary-card/summary-card.vue'
|
||||
///// Imports
|
||||
// Pub components
|
||||
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 Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
|
||||
import * as CH from '~/components/pub/my-ui/content-header'
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { useSidebar } from '~/components/pub/ui/sidebar/utils'
|
||||
|
||||
// Libs
|
||||
import { getPositionAs } from '~/lib/roles'
|
||||
|
||||
// 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'
|
||||
// App libs
|
||||
import { getServicePosition } from '~/lib/roles' // previously getPositionAs
|
||||
|
||||
// Services
|
||||
import { getList as getEncounterList, remove as removeEncounter, cancel as cancelEncounter } from '~/services/encounter.service'
|
||||
|
||||
// UI
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
// Apps
|
||||
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()
|
||||
setOpen(true)
|
||||
|
||||
const { getActiveRole } = useUserStore()
|
||||
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
|
||||
}>()
|
||||
|
||||
// Main data
|
||||
const data = ref([])
|
||||
const isLoading = reactive<DataTableLoader>({
|
||||
summary: false,
|
||||
@@ -44,67 +43,88 @@ const isLoading = reactive<DataTableLoader>({
|
||||
const recId = ref<number>(0)
|
||||
const recAction = ref<string>('')
|
||||
const recItem = ref<any>(null)
|
||||
const isFormEntryDialogOpen = ref(false)
|
||||
const isFilterFormDialogOpen = ref(false)
|
||||
const isRecordConfirmationOpen = ref(false)
|
||||
const isRecordCancelOpen = ref(false)
|
||||
|
||||
const hreaderPrep: HeaderPrep = {
|
||||
// Headers
|
||||
const hreaderPrep: CH.Config = {
|
||||
title: 'Kunjungan',
|
||||
icon: 'i-lucide-users',
|
||||
addNav: {
|
||||
label: 'Tambah',
|
||||
onClick: () => {
|
||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
|
||||
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')
|
||||
}
|
||||
navigateTo(`/${props.classCode}/encounter/add`)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const refSearchNav: RefSearchNav = {
|
||||
onClick: () => {
|
||||
// open filter modal
|
||||
isFormEntryDialogOpen.value = true
|
||||
console.log(' 1open filter modal')
|
||||
},
|
||||
onInput: (_val: string) => {
|
||||
// filter patient list
|
||||
},
|
||||
onClear: () => {
|
||||
// clear url param
|
||||
},
|
||||
if (!props.canCreate) {
|
||||
delete hreaderPrep.addNav
|
||||
}
|
||||
|
||||
// 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: {},
|
||||
})
|
||||
|
||||
/**
|
||||
* Get base path for encounter routes based on classCode and subClassCode
|
||||
*/
|
||||
function getBasePath(): string {
|
||||
if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
|
||||
return '/rehab/encounter'
|
||||
}
|
||||
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
|
||||
}
|
||||
// Role reactivities
|
||||
const { getActiveRole } = useUserStore()
|
||||
const activeServicePosition = ref(getServicePosition(getActiveRole()))
|
||||
provide('activeServicePosition', activeServicePosition)
|
||||
watch(getActiveRole, (role? : string) => {
|
||||
activeServicePosition.value = getServicePosition(role)
|
||||
})
|
||||
|
||||
// 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() {
|
||||
isLoading.isTableLoading = true
|
||||
try {
|
||||
@@ -215,94 +235,32 @@ function handleRemoveConfirmation() {
|
||||
recItem.value = null
|
||||
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>
|
||||
|
||||
<template>
|
||||
<Header
|
||||
:prep="{ ...hreaderPrep }"
|
||||
:ref-search-nav="refSearchNav"
|
||||
/>
|
||||
<Separator class="my-4 xl:my-5" />
|
||||
<CH.ContentHeader v-bind="hreaderPrep">
|
||||
<FilterNav
|
||||
@onFilterClick="() => isFilterFormDialogOpen = true"
|
||||
@onExportPdf="() => {}"
|
||||
@onExportExcel="() => {}"
|
||||
@nExportCsv="() => {}"
|
||||
/>
|
||||
</CH.ContentHeader>
|
||||
|
||||
<Filter
|
||||
:prep="hreaderPrep"
|
||||
:ref-search-nav="refSearchNav"
|
||||
/>
|
||||
|
||||
<AppEncounterList :data="data" />
|
||||
<Content :data="data" />
|
||||
|
||||
<!-- Filter -->
|
||||
<Dialog
|
||||
v-model:open="isFormEntryDialogOpen"
|
||||
v-model:open="isFilterFormDialogOpen"
|
||||
title="Filter"
|
||||
size="lg"
|
||||
prevent-outside
|
||||
>
|
||||
<AppEncounterFilter
|
||||
:installation="{
|
||||
msg: { placeholder: 'Pilih' },
|
||||
items: [],
|
||||
}"
|
||||
:schema="{}"
|
||||
/>
|
||||
<FilterForm v-bind="filter" />
|
||||
</Dialog>
|
||||
|
||||
<!-- Record Confirmation Modal -->
|
||||
<RecordConfirmation
|
||||
<!-- Batal -->
|
||||
<RecordConfirmation
|
||||
v-model:open="isRecordCancelOpen"
|
||||
custom-title="Batalkan Kunjungan"
|
||||
custom-message="Apakah anda yakin ingin membatalkan kunjungan pasien berikut?"
|
||||
@@ -325,7 +283,9 @@ onMounted(() => {
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
|
||||
<!-- Hapus -->
|
||||
<RecordConfirmation
|
||||
v-if="canDelete"
|
||||
v-model:open="isRecordConfirmationOpen"
|
||||
action="delete"
|
||||
:record="recItem"
|
||||
@@ -349,4 +309,11 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
<Dialog
|
||||
title="Hapus data"
|
||||
size="md"
|
||||
v-model:open="isRecordConfirmationOpen"
|
||||
>
|
||||
Hak akses tidak memenuhi kriteria untuk proses ini.
|
||||
</Dialog>
|
||||
</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">
|
||||
//
|
||||
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
|
||||
|
||||
//
|
||||
import type { TabItem } from '~/components/pub/my-ui/comp-tab/type'
|
||||
import CompTab from '~/components/pub/my-ui/comp-tab/comp-tab.vue'
|
||||
// App Models
|
||||
import { genEncounter, type Encounter } from '~/models/encounter'
|
||||
|
||||
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
|
||||
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'
|
||||
// App Components
|
||||
import EncounterPatientInfo from '~/components/app/encounter/quick-info.vue'
|
||||
import EncounterHistoryButtonMenu from '~/components/app/encounter/quick-shortcut.vue'
|
||||
|
||||
///// Declarations and Flows
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
classCode: EncounterProps['classCode']
|
||||
subClassCode?: EncounterProps['subClassCode']
|
||||
}>()
|
||||
|
||||
// Common preparations
|
||||
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 { user, userActiveRole, getActiveRole } = useUserStore()
|
||||
const activeRole = getActiveRole()
|
||||
const activePosition = ref(getServicePosition(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(genEncounter())
|
||||
const data = ref<Encounter>(genEncounter())
|
||||
const isShowPatient = computed(() => data.value && data.value?.patient?.person)
|
||||
|
||||
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
|
||||
const { setOpen } = useSidebar()
|
||||
setOpen(false)
|
||||
|
||||
if (activePosition.value === 'none') { // if user position is none, redirect to home page
|
||||
router.push('/')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDetail()
|
||||
})
|
||||
|
||||
const tabs: TabItem[] = [
|
||||
{ value: 'status', label: 'Status Masuk/Keluar', component: Status, props: { encounter: data } },
|
||||
// Dummy rows for ProtocolList (matches keys expected by list-cfg.protocol)
|
||||
const protocolRows = [
|
||||
{
|
||||
value: 'early-medical-assessment',
|
||||
label: 'Pengkajian Awal Medis',
|
||||
component: EarlyMedicalAssesmentList,
|
||||
props: { encounter: data, type: 'early-medic', label: 'Pengkajian Awal Medis' },
|
||||
number: '1',
|
||||
tanggal: new Date().toISOString().substring(0, 10),
|
||||
siklus: 'I',
|
||||
periode: 'Siklus I',
|
||||
kehadiran: 'Hadir',
|
||||
action: '',
|
||||
},
|
||||
{
|
||||
value: 'rehab-medical-assessment',
|
||||
label: 'Pengkajian Awal Medis Rehabilitasi Medis',
|
||||
component: EarlyMedicalRehabList,
|
||||
props: { encounter: data, type: 'early-rehab', label: 'Pengkajian Awal Medis Rehabilitasi Medis' },
|
||||
},
|
||||
{
|
||||
value: 'function-assessment',
|
||||
label: 'Asesmen Fungsi',
|
||||
component: AssesmentFunctionList,
|
||||
props: { encounter: data, type: 'function', label: 'Asesmen Fungsi' },
|
||||
},
|
||||
{ value: 'therapy-protocol', label: 'Protokol Terapi' },
|
||||
{ value: 'education-assessment', label: 'Asesmen Kebutuhan Edukasi' },
|
||||
{ value: 'patient-note', label: 'CPRJ', component: Cprj, props: { encounter: data } },
|
||||
{ value: 'consent', label: 'General Consent', component: GeneralConsentList, props: { encounter: data } },
|
||||
{ value: 'prescription', label: 'Order Obat', component: Prescription, props: { encounter_id: data.value.id } },
|
||||
{ value: 'device-order', label: 'Order Alkes', component: DeviceOrder, props: { encounter_id: data.value.id } },
|
||||
{ value: 'device', label: 'Order Alkes' },
|
||||
{ value: 'mcu-radiology', label: 'Order Radiologi', component: Radiology, props: { encounter_id: data.id } },
|
||||
{ value: 'mcu-lab-cp', label: 'Order Lab PK', component: CpLabOrder, props: { encounter_id: data.id } },
|
||||
{ value: 'mcu-lab-micro', label: 'Order Lab Mikro' },
|
||||
{ value: 'mcu-lab-pa', label: 'Order Lab PA' },
|
||||
{ value: 'medical-action', label: 'Order Ruang Tindakan' },
|
||||
{ value: 'mcu-result', label: 'Hasil Penunjang' },
|
||||
{ value: 'consultation', label: 'Konsultasi', component: Consultation, props: { encounter: data } },
|
||||
{ value: 'resume', label: 'Resume', component: ResumeList, props: { encounter: data } },
|
||||
{ value: 'control', label: 'Surat Kontrol', component: ControlLetterList, props: { encounter: data } },
|
||||
{ value: 'screening', label: 'Skrinning MPP' },
|
||||
{
|
||||
value: 'supporting-document',
|
||||
label: 'Upload Dokumen Pendukung',
|
||||
component: DocUploadList,
|
||||
props: { encounter: data },
|
||||
number: '2',
|
||||
tanggal: new Date().toISOString().substring(0, 10),
|
||||
siklus: 'II',
|
||||
periode: 'Siklus II',
|
||||
kehadiran: 'Tidak Hadir',
|
||||
action: '',
|
||||
},
|
||||
]
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="mb-4">
|
||||
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" />
|
||||
<div class="bg-white dark:bg-slate-800 p-4 2xl:p-5">
|
||||
<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>
|
||||
<AppEncounterQuickInfo :data="data" />
|
||||
<CompTab
|
||||
:data="tabs"
|
||||
:initial-active-tab="activeTab"
|
||||
@change-tab="activeTab = $event"
|
||||
/>
|
||||
<SubMenu :data="menus" :initial-active-menu="activeMenu" @change-menu="activeMenu = $event" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -48,9 +48,18 @@ async function setMenu() {
|
||||
const activeRoleParts = activeRole ? activeRole.split('|') : []
|
||||
const role = activeRoleParts[0]+(activeRoleParts.length > 1 ? `-${activeRoleParts[1]}` : '')
|
||||
try {
|
||||
|
||||
const res = await fetch(`/side-menu-items/${role.toLowerCase()}.json`)
|
||||
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) {
|
||||
const res = await fetch(`/side-menu-items/blank.json`)
|
||||
const rawMenu = await res.text()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
height: 200
|
||||
height?: number
|
||||
activeTab?: 1 | 2
|
||||
}>()
|
||||
|
||||
@@ -12,7 +12,7 @@ function handleClick(value: 1 | 2) {
|
||||
</script>
|
||||
|
||||
<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="content-wrapper">
|
||||
<div>
|
||||
@@ -49,7 +49,7 @@ function handleClick(value: 1 | 2) {
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
@apply p-4 2xl:p-5 overflow-hidden
|
||||
@apply p-4 2xl:p-5 overflow-hidden grow
|
||||
}
|
||||
.inactive .content-wrapper {
|
||||
@apply p-0 w-0
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
const { class: classProp } = defineProps<{
|
||||
class?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-5 text-center">:</div>
|
||||
<div :class="`w-4 ps-1 ${classProp}`">:</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { type EncounterItem } from "../../../../handlers/encounter-process.handler";
|
||||
import { type EncounterItem } from "~/handlers/encounter-init.handler";
|
||||
|
||||
const props = defineProps<{
|
||||
initialActiveMenu: string
|
||||
@@ -18,22 +18,28 @@ function changeMenu(value: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4 flex gap-4">
|
||||
<div class="flex">
|
||||
<!-- 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 class="max-h-[calc(100vh-12rem)] overflow-y-auto p-2">
|
||||
<button 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-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"
|
||||
<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 px-2 py-3">
|
||||
<button
|
||||
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)">
|
||||
{{ menu.title }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Active Menu Content -->
|
||||
<div v-if="data.find((m) => m.id === activeMenu)?.component"
|
||||
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||
<component :is="data.find((m) => m.id === activeMenu)?.component"
|
||||
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
||||
<div class="p-4 2xl:p-5 flex-grow">
|
||||
<div v-if="data.find((m) => m.id === activeMenu)?.component"
|
||||
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||
<component
|
||||
:is="data.find((m) => m.id === activeMenu)?.component"
|
||||
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,7 +20,7 @@ function onClick(type: ClickType) {
|
||||
@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"
|
||||
>
|
||||
<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" />
|
||||
</svg>
|
||||
{{ props.label }}
|
||||
|
||||
@@ -37,6 +37,10 @@ function btnClick() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<!-- For components as slots -->
|
||||
<slot />
|
||||
|
||||
<!-- For components passed by props -->
|
||||
<div v-if="prep.components">
|
||||
<template v-for="cwp in prep.components">
|
||||
<component
|
||||
|
||||
@@ -32,7 +32,7 @@ function btnClick() {
|
||||
/>
|
||||
</div>
|
||||
<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" />
|
||||
{{ prep.addNav.label }}
|
||||
</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 {
|
||||
canRead: boolean
|
||||
@@ -7,7 +8,6 @@ export interface PageOperationPermission {
|
||||
canDelete: boolean
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
const authStore = useUserStore()
|
||||
|
||||
const checkRole = (roleAccess: RoleAccess, _userRoles?: string[]): boolean => {
|
||||
const roles = authStore.userRole
|
||||
return roles.some((role: string) => role === 'system' || (role in roleAccess)) // system by-passes this check
|
||||
const checkRole = (roleAccesses: RoleAccesses, _userRoles?: string[]): boolean => {
|
||||
const activeRole = authStore.getActiveRole() || ''
|
||||
if (activeRole === systemCode) {
|
||||
return true
|
||||
}
|
||||
return (activeRole in roleAccesses);
|
||||
}
|
||||
|
||||
const checkPermission = (roleAccess: RoleAccess, permission: Permission, _userRoles?: string[]): boolean => {
|
||||
const roles = authStore.userRole
|
||||
return roles.some((role: string) => role === 'system' || roleAccess[role]?.includes(permission)) // system by-passes this check
|
||||
const checkPermission = (roleAccesses: RoleAccesses, permission: Permission, _userRoles?: string[]): boolean => {
|
||||
const activeRole = authStore.getActiveRole() || ''
|
||||
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 roles = authStore.userRole
|
||||
// const roles = ['admisi']
|
||||
const getUserPermissions = (roleAccess: RoleAccesses, _userRoles?: string[]): Permission[] => {
|
||||
const roles = authStore.userRoles
|
||||
const permissions = new Set<Permission>()
|
||||
|
||||
roles.forEach((role: string) => {
|
||||
@@ -39,12 +47,12 @@ export function useRBAC() {
|
||||
return Array.from(permissions)
|
||||
}
|
||||
|
||||
const hasCreateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'C')
|
||||
const hasReadAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'R')
|
||||
const hasUpdateAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'U')
|
||||
const hasDeleteAccess = (roleAccess: RoleAccess) => checkPermission(roleAccess, 'D')
|
||||
const hasCreateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'C')
|
||||
const hasReadAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'R')
|
||||
const hasUpdateAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'U')
|
||||
const hasDeleteAccess = (roleAccess: RoleAccesses) => checkPermission(roleAccess, 'D')
|
||||
|
||||
const getPagePermissions = (roleAccess: RoleAccess): PageOperationPermission => ({
|
||||
const getPagePermissions = (roleAccess: RoleAccesses): PageOperationPermission => ({
|
||||
canRead : hasReadAccess(roleAccess),
|
||||
canCreate: hasCreateAccess(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 const permissions: Record<string, Record<string, Permission[]>> = {
|
||||
'/outpatient/registration-queue': {
|
||||
'/ambulatory/registration-queue': {
|
||||
'emp|reg': ['R', 'U', 'D'],
|
||||
},
|
||||
'/outpatient/encounter-queue': {
|
||||
'/ambulatory/encounter-queue': {
|
||||
'emp|nur': ['R', 'U', 'D'],
|
||||
},
|
||||
'/outpatient/encounter': {
|
||||
'/ambulatory/encounter': {
|
||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||
'emp|doc': ['R'],
|
||||
'emp|nur': ['R'],
|
||||
@@ -21,10 +21,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||
'emp|lab': ['R'],
|
||||
'emp|rad': ['R'],
|
||||
},
|
||||
'/outpatient/encounter/add': {
|
||||
'/ambulatory/encounter/add': {
|
||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||
},
|
||||
'/outpatient/encounter/[id]/detail': {
|
||||
'/ambulatory/encounter/[id]': {
|
||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||
'emp|doc': ['R'],
|
||||
'emp|nur': ['R'],
|
||||
@@ -35,10 +35,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||
'emp|lab': ['R'],
|
||||
'emp|rad': ['R'],
|
||||
},
|
||||
'/outpatient/encounter/[id]/edit': {
|
||||
'/ambulatory/encounter/[id]/edit': {
|
||||
'emp|reg': ['C', 'R', 'U', 'D'],
|
||||
},
|
||||
'/outpatient/encounter/[id]/process': {
|
||||
'/ambulatory/encounter/[id]/process': {
|
||||
'emp|doc': ['R', 'U'],
|
||||
'emp|nur': ['R', 'U'],
|
||||
'emp|thr': ['R', 'U'],
|
||||
@@ -48,10 +48,10 @@ export const permissions: Record<string, Record<string, Permission[]>> = {
|
||||
'emp|lab': ['R', 'U'],
|
||||
'emp|rad': ['R', 'U'],
|
||||
},
|
||||
'/outpatient/consulation': {
|
||||
'/ambulatory/consulation': {
|
||||
'emp|doc': ['R'],
|
||||
},
|
||||
'/outpatient/consulation/[id]/process': {
|
||||
'/ambulatory/consulation/[id]/process': {
|
||||
'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 { medicalPositions } from '~/lib/roles'
|
||||
import { medicalRoles } from '~/const/common/role'
|
||||
|
||||
export interface EncounterItem {
|
||||
id: string
|
||||
@@ -444,7 +444,9 @@ export function getMenuItems(
|
||||
data: EncounterListData,
|
||||
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 defaultItems: EncounterItem[] = Object.values(currentKeys)
|
||||
const listItemsForOutpatientRehab = mergeArrayAt(
|
||||
@@ -456,14 +458,14 @@ export function getMenuItems(
|
||||
getItemsAll('ambulatory', 'chemo', defaultItems),
|
||||
)
|
||||
const listItems: Record<string, Record<string, Record<string, any>>> = {
|
||||
'installation|outpatient': {
|
||||
'installation|ambulatory': {
|
||||
'unit|rehab': {
|
||||
items: listItemsForOutpatientRehab,
|
||||
roles: medicalPositions,
|
||||
roles: medicalRoles,
|
||||
},
|
||||
'unit|chemo': {
|
||||
items: listItemsForOutpatientChemo,
|
||||
roles: medicalPositions,
|
||||
roles: medicalRoles,
|
||||
},
|
||||
all: getItemsAll('ambulatory', 'all', defaultItems),
|
||||
},
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
import CardContent from '~/components/pub/ui/card/CardContent.vue'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
|
||||
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(() => {
|
||||
switch (contentFrame.value) {
|
||||
case 'cf-container-2xl':
|
||||
@@ -28,13 +33,14 @@ const contentFrameClass = computed(() => {
|
||||
<LayoutAppSidebar />
|
||||
<SidebarInset>
|
||||
<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'">
|
||||
<Card>
|
||||
<Card v-if="contentUseCard">
|
||||
<CardContent>
|
||||
<slot />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<slot v-else />
|
||||
</div>
|
||||
<slot v-else />
|
||||
</div>
|
||||
|
||||
+13
-11
@@ -1,14 +1,16 @@
|
||||
export const medicalPositions = ['emp|doc', 'emp|lab', 'emp|mid', 'emp|nur', 'emp|nut', 'emp|pha', 'emp|reg']
|
||||
const verificatorRole = 'verificator'
|
||||
import { medicalRoles, respPosCode } from '~/const/common/role'
|
||||
|
||||
export function getPositionAs(roleAccess: string): string {
|
||||
if (roleAccess.includes('|')) {
|
||||
if (medicalPositions.includes(roleAccess)) {
|
||||
return 'medical'
|
||||
}
|
||||
if (roleAccess.includes(verificatorRole)) {
|
||||
return 'verificator'
|
||||
}
|
||||
export function getServicePosition(role?: string): string {
|
||||
if(!role) {
|
||||
return 'none'
|
||||
}
|
||||
if (medicalRoles.includes(role)) {
|
||||
return 'medical'
|
||||
} 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[]
|
||||
if (requiredRoles && requiredRoles.length > 0) {
|
||||
// FIXME: change this dummy roles, when api is ready
|
||||
const userRoles = authStore.userRole
|
||||
const userRoles = authStore.userRoles
|
||||
// const userRoles = ['admisi']
|
||||
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 interface RoleAccess {
|
||||
export interface RoleAccesses {
|
||||
[role: string]: Permission[]
|
||||
}
|
||||
|
||||
export type PagePath = keyof typeof PAGE_PERMISSIONS
|
||||
export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
|
||||
// export type PagePath = keyof typeof PAGE_PERMISSIONS
|
||||
// export type PagePermission = (typeof PAGE_PERMISSIONS)[PagePath]
|
||||
|
||||
+22
-15
@@ -1,50 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import type { PagePermission } from '~/models/role'
|
||||
// Pubs
|
||||
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({
|
||||
middleware: ['rbac'],
|
||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||
roles: ['emp|reg'],
|
||||
title: 'Edit Kunjungan',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
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()
|
||||
|
||||
// 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)
|
||||
if (!hasAccess) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
statusMessage: 'Access denied',
|
||||
})
|
||||
navigateTo('/403')
|
||||
}
|
||||
|
||||
// Define permission-based computed properties
|
||||
const canUpdate = hasUpdateAccess(roleAccess)
|
||||
|
||||
// Get encounter ID from route params
|
||||
const encounterId = computed(() => {
|
||||
const encounter_id = computed(() => {
|
||||
const id = route.params.id
|
||||
return typeof id === 'string' ? parseInt(id) : 0
|
||||
})
|
||||
|
||||
// User info
|
||||
const { user } = useUserStore()
|
||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="canUpdate">
|
||||
<ContentEncounterEntry
|
||||
:id="encounterId"
|
||||
<Content
|
||||
:id="encounter_id"
|
||||
class-code="ambulatory"
|
||||
sub-class-code="reg"
|
||||
:sub-class-code="subClassCode"
|
||||
form-type="Edit"
|
||||
/>
|
||||
</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">
|
||||
import type { Permission } from '~/models/role'
|
||||
import { permissions } from '~/const/page-permission/outpatient'
|
||||
// Pubs
|
||||
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({
|
||||
middleware: ['rbac'],
|
||||
roles: ['emp|doc', 'emp|nur', 'emp|reg', 'emp|pha', 'emp|pay', 'emp|mng'],
|
||||
title: 'Tambah Kunjungan',
|
||||
roles: ['emp|doc', 'emp|nur', 'emp|miw', 'emp|nut', 'emp|lab', 'emp|pha', 'emp|thr'],
|
||||
title: 'Proses Kunjungan',
|
||||
contentFrame: 'cf-full-width',
|
||||
contentPadding: 'p-0',
|
||||
contentUseCard: false
|
||||
})
|
||||
|
||||
// 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()
|
||||
|
||||
// Check if user has access to this page
|
||||
@@ -29,11 +34,12 @@ const route = useRoute()
|
||||
useHead({
|
||||
title: () => `${route.meta.title}`,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="canRead">
|
||||
<Content class-code="ambulatory" sub-class-code="reg" />
|
||||
<Content class-code="ambulatory" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</template>
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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 Content from '~/components/content/encounter/entry.vue'
|
||||
@@ -13,7 +13,7 @@ definePageMeta({
|
||||
})
|
||||
|
||||
// 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()
|
||||
|
||||
// Check if user has access to this page
|
||||
+20
-18
@@ -1,43 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import type { Permission } from '~/models/role'
|
||||
import { permissions } from '~/const/page-permission/outpatient'
|
||||
// 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'
|
||||
|
||||
// Apps
|
||||
import Content from '~/components/content/encounter/list.vue'
|
||||
|
||||
// Page meta
|
||||
definePageMeta({
|
||||
middleware: ['rbac'],
|
||||
roles: ['emp|reg', 'emp|nur', 'emp|doc', 'emp|miw', 'emp|thr', 'emp|nut', 'emp|pha', 'emp|lab'],
|
||||
// middleware: ['rbac'],
|
||||
// roles: ['emp|reg', 'emp|nur', 'emp|doc', 'emp|miw', 'emp|thr', 'emp|nut', 'emp|pha', 'emp|lab'],
|
||||
title: 'Daftar Kunjungan',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
// Define common things
|
||||
const route = useRoute()
|
||||
|
||||
// Preps role checking
|
||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
||||
const { checkRole, hasReadAccess } = useRBAC()
|
||||
const roleAccess: Record<string, Permission[]> = permissions[route.path] || {}
|
||||
const { checkRole, hasCreateAccess, hasReadAccess } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
if (!hasAccess) {
|
||||
const canRead = hasReadAccess(roleAccess)
|
||||
if (!hasAccess || !canRead) {
|
||||
navigateTo('/403')
|
||||
}
|
||||
|
||||
// Define permission-based computed properties
|
||||
const canRead = hasReadAccess(roleAccess)
|
||||
const canCreate = hasCreateAccess(roleAccess)
|
||||
|
||||
// Page needs
|
||||
const route = useRoute()
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const { user, getActiveRole } = 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] : ''
|
||||
// User info
|
||||
const { user } = useUserStore()
|
||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -46,7 +48,7 @@ const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
<Content
|
||||
class-code="ambulatory"
|
||||
:sub-class-code="subClassCode"
|
||||
type="encounter"
|
||||
:can-create="canCreate"
|
||||
/>
|
||||
</div>
|
||||
<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">
|
||||
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 { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||
|
||||
definePageMeta({
|
||||
middleware: ['rbac'],
|
||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||
title: 'Detail Surat Kontrol',
|
||||
contentFrame: 'cf-container-md',
|
||||
title: 'Daftar Kempterapi',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
@@ -16,25 +16,24 @@ useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
||||
const roleAccess: Record<string, Permission[]> = permissions['/chemotherapy'] || {}
|
||||
|
||||
const { checkRole, hasReadAccess } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
if (!hasAccess) {
|
||||
navigateTo('/403')
|
||||
}
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
const canRead = hasReadAccess(roleAccess)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentOutpatientEncounterDetail :patient-id="Number(route.params.id)" />
|
||||
<ContentChemotherapyList />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</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">
|
||||
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 Content from '~/components/content/encounter/entry.vue'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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 Content from '~/components/content/encounter/list.vue'
|
||||
@@ -13,7 +13,8 @@ definePageMeta({
|
||||
})
|
||||
|
||||
// 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()
|
||||
|
||||
// Check if user has access to this page
|
||||
@@ -26,18 +27,12 @@ if (!hasAccess) {
|
||||
const canRead = hasReadAccess(roleAccess)
|
||||
|
||||
// Page needs
|
||||
const route = useRoute()
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const { user, getActiveRole } = 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 { user } = useUserStore()
|
||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -45,7 +40,7 @@ const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
<div v-if="canRead">
|
||||
<Content
|
||||
class-code="emergency"
|
||||
sub-class-code="reg"
|
||||
:sub-class-code="subClassCode"
|
||||
type="encounter"
|
||||
/>
|
||||
</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">
|
||||
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 Content from '~/components/content/encounter/entry.vue'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
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 Content from '~/components/content/encounter/list.vue'
|
||||
@@ -13,7 +13,7 @@ definePageMeta({
|
||||
})
|
||||
|
||||
// Preps role checking
|
||||
const roleAccess: Record<string, Permission[]> = permissions['/outpatient/encounter'] || {}
|
||||
const roleAccess: Record<string, Permission[]> = permissions['/inpatient/encounter'] || {}
|
||||
const { checkRole, hasReadAccess } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
@@ -31,11 +31,7 @@ useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const { user, getActiveRole } = 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 { user } = useUserStore()
|
||||
const subClassCode = user.unit_code == 'rehab' ? 'rehab' : 'reg'
|
||||
|
||||
</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({
|
||||
middleware: ['rbac'],
|
||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||
roles: [],
|
||||
title: 'Daftar Dokter',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
@@ -16,7 +16,7 @@ useHead({
|
||||
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()
|
||||
|
||||
|
||||
+12
-7
@@ -1,19 +1,21 @@
|
||||
export const useUserStore = defineStore(
|
||||
'user',
|
||||
() => {
|
||||
// should be auth type
|
||||
const user = ref<any | null>(null)
|
||||
// const token = useCookie('authentication')
|
||||
|
||||
const isAuthenticated = computed(() => !!user.value)
|
||||
|
||||
const userRole = computed(() => {
|
||||
const userRoles = computed(() => {
|
||||
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) => {
|
||||
user.value = userData
|
||||
}
|
||||
@@ -25,10 +27,11 @@ export const useUserStore = defineStore(
|
||||
const setActiveRole = (role: string) => {
|
||||
if (user.value && user.value.roles.includes(role)) {
|
||||
user.value.activeRole = role
|
||||
userActiveRole.value = role
|
||||
}
|
||||
}
|
||||
|
||||
const getActiveRole = () => {
|
||||
const getActiveRole = (): string | undefined => {
|
||||
if (user.value?.activeRole) {
|
||||
return user.value.activeRole
|
||||
}
|
||||
@@ -36,12 +39,14 @@ export const useUserStore = defineStore(
|
||||
user.value.activeRole = user.value.roles[0]
|
||||
return user.value.activeRole
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
isAuthenticated,
|
||||
userRole,
|
||||
userRoles,
|
||||
userActiveRole,
|
||||
login,
|
||||
logout,
|
||||
setActiveRole,
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
{
|
||||
"title": "Kunjungan",
|
||||
"icon": "i-lucide-stethoscope",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
},
|
||||
{
|
||||
"title": "Konsultasi",
|
||||
"icon": "i-lucide-building-2",
|
||||
"link": "/outpatient/consultation"
|
||||
"link": "/ambulatory/consultation"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{
|
||||
"title": "Rawat Jalan",
|
||||
"icon": "i-lucide-stethoscope",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
},
|
||||
{
|
||||
"title": "IGD",
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "Antrian Poliklinik",
|
||||
"link": "/outpatient/encounter-queue"
|
||||
"link": "/ambulatory/encounter-queue"
|
||||
},
|
||||
{
|
||||
"title": "Kunjungan",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{
|
||||
"title": "Rawat Jalan",
|
||||
"icon": "i-lucide-stethoscope",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
},
|
||||
{
|
||||
"title": "IGD",
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "Antrian Pendaftaran",
|
||||
"link": "/outpatient/registration-queue"
|
||||
"link": "/ambulatory/registration-queue"
|
||||
},
|
||||
{
|
||||
"title": "Kunjungan",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "Antrian Pendaftaran",
|
||||
"link": "/outpatient/registration-queue"
|
||||
"link": "/ambulatory/registration-queue"
|
||||
},
|
||||
{
|
||||
"title": "Antrian Poliklinik",
|
||||
"link": "/outpatient/encounter-queue"
|
||||
"link": "/ambulatory/encounter-queue"
|
||||
},
|
||||
{
|
||||
"title": "Kunjungan",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
},
|
||||
{
|
||||
"title": "Konsultasi",
|
||||
"link": "/outpatient/consultation"
|
||||
"link": "/ambulatory/consultation"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
"children": [
|
||||
{
|
||||
"title": "Antrian Pendaftaran",
|
||||
"link": "/outpatient/registration-queue"
|
||||
"link": "/ambulatory/registration-queue"
|
||||
},
|
||||
{
|
||||
"title": "Antrian Poliklinik",
|
||||
"link": "/outpatient/encounter-queue"
|
||||
"link": "/ambulatory/encounter-queue"
|
||||
},
|
||||
{
|
||||
"title": "Kunjungan",
|
||||
"link": "/outpatient/encounter"
|
||||
"link": "/ambulatory/encounter"
|
||||
},
|
||||
{
|
||||
"title": "Konsultasi",
|
||||
"link": "/outpatient/consultation"
|
||||
"link": "/ambulatory/consultation"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -389,4 +389,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user