feat(encounter): provide cancel of encounter
This commit is contained in:
No files matched your search
@@ -0,0 +1,103 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { LinkItem, ListItemDto } from '~/components/pub/my-ui/data/types'
|
||||||
|
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: ListItemDto
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const recId = inject<Ref<number>>('rec_id')!
|
||||||
|
const recAction = inject<Ref<string>>('rec_action')!
|
||||||
|
const recItem = inject<Ref<any>>('rec_item')!
|
||||||
|
const activeKey = ref<string | null>(null)
|
||||||
|
const linkItems: LinkItem[] = [
|
||||||
|
{
|
||||||
|
label: 'Detail',
|
||||||
|
onClick: () => {
|
||||||
|
detail()
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-eye',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Edit',
|
||||||
|
onClick: () => {
|
||||||
|
edit()
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-pencil',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Print',
|
||||||
|
onClick: () => {
|
||||||
|
print()
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-printer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Batalkan',
|
||||||
|
onClick: () => {
|
||||||
|
cancel()
|
||||||
|
},
|
||||||
|
icon: 'i-lucide-circle-x',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function detail() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showDetail
|
||||||
|
recItem.value = props.rec
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<SidebarMenuButton
|
||||||
|
size="lg"
|
||||||
|
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="i-lucide-chevrons-up-down"
|
||||||
|
class="ml-auto size-4"
|
||||||
|
/>
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem
|
||||||
|
v-for="item in linkItems"
|
||||||
|
:key="item.label"
|
||||||
|
class="hover:bg-gray-100 dark:hover:bg-slate-700"
|
||||||
|
@click="item.onClick"
|
||||||
|
@mouseenter="activeKey = item.label"
|
||||||
|
@mouseleave="activeKey = null"
|
||||||
|
>
|
||||||
|
<Icon :name="item.icon ?? ''" />
|
||||||
|
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -6,7 +6,7 @@ import { getAge } from '~/lib/date'
|
|||||||
|
|
||||||
type SmallDetailDto = Encounter
|
type SmallDetailDto = Encounter
|
||||||
|
|
||||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-pdud.vue'))
|
const action = defineAsyncComponent(() => import('./dropdown-action.vue'))
|
||||||
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
const statusBadge = defineAsyncComponent(() => import('./status-badge.vue'))
|
||||||
|
|
||||||
export const config: Config = {
|
export const config: Config = {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types
|
|||||||
import { ActionEvents } 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 } from '~/services/encounter.service'
|
import { getList as getEncounterList, remove as removeEncounter, cancel as cancelEncounter } from '~/services/encounter.service'
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
import { toast } from '~/components/pub/ui/toast'
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
@@ -35,6 +35,7 @@ const recAction = ref<string>('')
|
|||||||
const recItem = ref<any>(null)
|
const recItem = ref<any>(null)
|
||||||
const isFormEntryDialogOpen = ref(false)
|
const isFormEntryDialogOpen = ref(false)
|
||||||
const isRecordConfirmationOpen = ref(false)
|
const isRecordConfirmationOpen = ref(false)
|
||||||
|
const isRecordCancelOpen = ref(false)
|
||||||
|
|
||||||
const hreaderPrep: HeaderPrep = {
|
const hreaderPrep: HeaderPrep = {
|
||||||
title: 'Kunjungan',
|
title: 'Kunjungan',
|
||||||
@@ -114,6 +115,43 @@ async function getPatientList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle confirmation result
|
||||||
|
async function handleConfirmCancel(record: any, action: string) {
|
||||||
|
if (action === 'delete' && record?.id) {
|
||||||
|
try {
|
||||||
|
const result = await cancelEncounter(record.id)
|
||||||
|
if (result.success) {
|
||||||
|
toast({
|
||||||
|
title: 'Berhasil',
|
||||||
|
description: 'Kunjungan berhasil dibatalkan',
|
||||||
|
variant: 'default',
|
||||||
|
})
|
||||||
|
await getPatientList() // Refresh list
|
||||||
|
} else {
|
||||||
|
const errorMessage = result.body?.message || 'Gagal membatalkan kunjungan'
|
||||||
|
toast({
|
||||||
|
title: 'Gagal',
|
||||||
|
description: errorMessage,
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Error cancellation encounter:', error)
|
||||||
|
toast({
|
||||||
|
title: 'Gagal',
|
||||||
|
description: error?.message || 'Gagal membatalkan kunjungan',
|
||||||
|
variant: 'destructive',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
// Reset state
|
||||||
|
recId.value = 0
|
||||||
|
recAction.value = ''
|
||||||
|
recItem.value = null
|
||||||
|
isRecordCancelOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle confirmation result
|
// Handle confirmation result
|
||||||
async function handleConfirmDelete(record: any, action: string) {
|
async function handleConfirmDelete(record: any, action: string) {
|
||||||
if (action === 'delete' && record?.id) {
|
if (action === 'delete' && record?.id) {
|
||||||
@@ -152,6 +190,14 @@ async function handleConfirmDelete(record: any, action: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleCancelConfirmation() {
|
function handleCancelConfirmation() {
|
||||||
|
// Reset record state when cancelled
|
||||||
|
recId.value = 0
|
||||||
|
recAction.value = ''
|
||||||
|
recItem.value = null
|
||||||
|
isRecordCancelOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemoveConfirmation() {
|
||||||
// Reset record state when cancelled
|
// Reset record state when cancelled
|
||||||
recId.value = 0
|
recId.value = 0
|
||||||
recAction.value = ''
|
recAction.value = ''
|
||||||
@@ -166,16 +212,21 @@ watch(
|
|||||||
isRecordConfirmationOpen.value = true
|
isRecordConfirmationOpen.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recAction.value === ActionEvents.showCancel) {
|
||||||
|
isRecordCancelOpen.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const basePath = getBasePath()
|
const basePath = getBasePath()
|
||||||
|
|
||||||
if (props.type === 'encounter') {
|
if (props.type === 'encounter') {
|
||||||
if (recAction.value === 'showDetail') {
|
if (recAction.value === 'showDetail') {
|
||||||
navigateTo(`${basePath}/${recId.value}/detail`)
|
navigateTo(`${basePath}/${recId.value}/process`)
|
||||||
} else if (recAction.value === 'showEdit') {
|
} else if (recAction.value === 'showEdit') {
|
||||||
navigateTo(`${basePath}/${recId.value}/edit`)
|
navigateTo(`${basePath}/${recId.value}/edit`)
|
||||||
} else if (recAction.value === 'showProcess') {
|
} else if (recAction.value === 'showPrint') {
|
||||||
navigateTo(`${basePath}/${recId.value}/process`)
|
console.log('print')
|
||||||
} else {
|
} else {
|
||||||
// handle other actions
|
// handle other actions
|
||||||
}
|
}
|
||||||
@@ -234,12 +285,34 @@ onMounted(() => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<!-- Record Confirmation Modal -->
|
<!-- Record Confirmation Modal -->
|
||||||
|
<RecordConfirmation
|
||||||
|
v-model:open="isRecordCancelOpen"
|
||||||
|
action="delete"
|
||||||
|
:record="recItem"
|
||||||
|
@confirm="handleConfirmCancel"
|
||||||
|
@cancel="handleCancelConfirmation"
|
||||||
|
>
|
||||||
|
<template #default="{ record }">
|
||||||
|
<div class="text-sm">
|
||||||
|
<p>Apakah anda yakin ingin membatalkan kunjungan pasien berikut?</p>
|
||||||
|
<p v-if="record?.patient?.person?.name">
|
||||||
|
<strong>Nama:</strong>
|
||||||
|
{{ record.patient.person.name }}
|
||||||
|
</p>
|
||||||
|
<p v-if="record?.medical_record_number">
|
||||||
|
<strong>No RM:</strong>
|
||||||
|
{{ record.medical_record_number }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RecordConfirmation>
|
||||||
|
|
||||||
<RecordConfirmation
|
<RecordConfirmation
|
||||||
v-model:open="isRecordConfirmationOpen"
|
v-model:open="isRecordConfirmationOpen"
|
||||||
action="delete"
|
action="delete"
|
||||||
:record="recItem"
|
:record="recItem"
|
||||||
@confirm="handleConfirmDelete"
|
@confirm="handleConfirmDelete"
|
||||||
@cancel="handleCancelConfirmation"
|
@cancel="handleRemoveConfirmation"
|
||||||
>
|
>
|
||||||
<template #default="{ record }">
|
<template #default="{ record }">
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ export const ActionEvents = {
|
|||||||
showEdit: 'showEdit',
|
showEdit: 'showEdit',
|
||||||
showDetail: 'showDetail',
|
showDetail: 'showDetail',
|
||||||
showProcess: 'showProcess',
|
showProcess: 'showProcess',
|
||||||
|
showCancel: 'showCancel',
|
||||||
|
showPrint: 'showPrint',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTableLoader {
|
export interface DataTableLoader {
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ export function remove(id: number | string) {
|
|||||||
return base.remove(path, id, name)
|
return base.remove(path, id, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cancel(id: number | string) {
|
||||||
|
let url = `${path}/${id}/cancel`
|
||||||
|
return base.update(url, id, {}, name)
|
||||||
|
}
|
||||||
|
|
||||||
export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> {
|
export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> {
|
||||||
let data: { value: string; label: string }[] = []
|
let data: { value: string; label: string }[] = []
|
||||||
const result = await getList(params)
|
const result = await getList(params)
|
||||||
|
|||||||
Reference in New Issue
Block a user