127 lines
3.1 KiB
Vue
127 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
import { ref, provide, watch } from 'vue'
|
|
|
|
// Components
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
DialogFooter,
|
|
} from '~/components/pub/ui/dialog'
|
|
import { Button } from '~/components/pub/ui/button'
|
|
import { Input } from '~/components/pub/ui/input'
|
|
import ListPatient from './list-patient.vue'
|
|
|
|
// Types
|
|
import type { PatientData } from './list-cfg.patient'
|
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
|
|
|
// Helpers
|
|
import { refDebounced } from '@vueuse/core'
|
|
|
|
const props = defineProps<{
|
|
open: boolean
|
|
patients: Array<PatientData>
|
|
selected: string
|
|
paginationMeta: PaginationMeta
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:open', value: boolean): void
|
|
(e: 'update:selected', value: string): void
|
|
(e: 'fetch', value: any): void
|
|
(e: 'save'): void
|
|
}>()
|
|
|
|
const search = ref('')
|
|
const debouncedSearch = refDebounced(search, 500) // 500ms debounce
|
|
|
|
// Provide for radio selection - use selected prop directly
|
|
const recSelectId = ref<number>(Number(props.selected) || 0)
|
|
const recSelectMenu = ref<string>('patient')
|
|
|
|
provide('rec_select_id', recSelectId)
|
|
provide('rec_select_menu', recSelectMenu)
|
|
|
|
function saveSelection() {
|
|
// Validate that a patient is selected
|
|
if (!props.selected || props.selected === '') {
|
|
console.warn('No patient selected')
|
|
return
|
|
}
|
|
emit('save')
|
|
emit('update:open', false)
|
|
}
|
|
|
|
function handlePageChange(page: number) {
|
|
emit('fetch', { 'page-number': page })
|
|
}
|
|
|
|
// Watch for changes in recSelectId and emit update:selected
|
|
watch(recSelectId, (newValue) => {
|
|
if (newValue > 0) {
|
|
emit('update:selected', String(newValue))
|
|
}
|
|
})
|
|
|
|
// Watch for changes in selected prop
|
|
watch(() => props.selected, (newValue) => {
|
|
recSelectId.value = Number(newValue) || 0
|
|
})
|
|
|
|
watch(debouncedSearch, (newValue) => {
|
|
// Only search if 3+ characters or empty (to clear search)
|
|
if (newValue.length === 0 || newValue.length >= 3) {
|
|
emit('fetch', { search: newValue })
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog
|
|
:open="props.open"
|
|
@update:open="emit('update:open', $event)"
|
|
>
|
|
<DialogTrigger as-child></DialogTrigger>
|
|
<DialogContent class="max-w-3xl">
|
|
<DialogHeader>
|
|
<DialogTitle>Cari Pasien</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<!-- Input Search -->
|
|
<div class="max-w-[50%]">
|
|
<Input
|
|
v-model="search"
|
|
placeholder="Cari berdasarkan No. KTP / No. RM / Nomor Kartu"
|
|
/>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto rounded-lg border">
|
|
<ListPatient
|
|
:data="patients"
|
|
:selected="props.selected"
|
|
:pagination-meta="paginationMeta"
|
|
@page-change="handlePageChange"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<DialogFooter>
|
|
<Button
|
|
variant="default"
|
|
class="h-[40px] min-w-[120px] text-white"
|
|
@click="saveSelection"
|
|
>
|
|
<Icon
|
|
name="i-lucide-save"
|
|
class="h-5 w-5"
|
|
/>
|
|
Simpan
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</template>
|