Files
antrean-operasi/components/pendaftaran/ModalDetailPendaftaran.vue
T
2026-03-05 10:34:59 +07:00

617 lines
31 KiB
Vue

<script setup lang="ts">
import LoadingState from '@/components/shared/LoadingState.vue';
import { getAntrianOperasiById } from '@/services/antrean';
import { Icon } from '@iconify/vue';
import { STATUS } from '~/types/antrean';
interface Props {
modelValue: boolean;
id: string | number;
}
const props = defineProps<Props>();
const emit = defineEmits<{
'update:modelValue': [value: boolean];
}>();
const loading = ref(false);
const error = ref(false);
// Form data
const formData = ref({
noRekamMedis: '',
noKtp: '',
namaPasien: '',
jenisKelamin: '',
tanggalLahir: '',
umur: '',
alamat: '',
nomorTelepon: [] as string[]
});
const diagnosisItems = ref<any[]>([]);
const tindakanItems = ref<any[]>([]);
const rencanaOperasiData = ref({
spesialis: null as number | null,
SpesialisName: '',
subSpesialis: null as number | null,
SubSpesialisName: '',
tanggalDaftar: '',
kategoriOperasi: null as number | null,
kategoriName: '',
rencanaOperasi: null,
keterangan: null
});
const dokterPelaksanaItems = ref<any[]>([]);
const statusPasienData = ref({
tanggalSelesai: null,
statusOperasi: '',
keteranganStatus: null
});
const showModal = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});
// Helper functions
const formatDate = (dateString: string) => {
if (!dateString) return '-';
return new Date(dateString).toLocaleDateString('id-ID', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
const formatDateTime = (dateString: string) => {
if (!dateString) return '-';
return new Date(dateString).toLocaleDateString('id-ID', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
const getGenderIcon = (gender: string) => {
return gender === 'L' ? 'mdi-gender-male' : 'mdi-gender-female';
};
const getGenderColor = (gender: string) => {
return gender === 'L' ? 'info' : 'error';
};
const getGenderText = (gender: string) => {
return gender === 'L' ? 'Laki-laki' : 'Perempuan';
};
const getStatusColor = (status: string) => {
switch (status) {
case STATUS.BELUM: return 'primary';
case STATUS.SELESAI: return 'success';
case STATUS.TUNDA: return 'warning';
case STATUS.BATAL: return 'error';
default: return 'grey';
}
};
const getStatusText = (status: string) => {
switch (status) {
case STATUS.BELUM: return 'Belum';
case STATUS.SELESAI: return 'Selesai';
case STATUS.TUNDA: return 'Tunda';
case STATUS.BATAL: return 'Batal';
default: return 'Unknown';
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case STATUS.BELUM: return 'mdi-clock-outline';
case STATUS.SELESAI: return 'mdi-check-circle';
case STATUS.TUNDA: return 'mdi-pause-circle';
case STATUS.BATAL: return 'mdi-close-circle';
default: return 'mdi-help-circle';
}
};
const initializeForm = () => {
// Reset all data
formData.value = {
noRekamMedis: '',
noKtp: '',
namaPasien: '',
jenisKelamin: '',
tanggalLahir: '',
umur: '',
alamat: '',
nomorTelepon: []
};
diagnosisItems.value = [];
tindakanItems.value = [];
rencanaOperasiData.value = {
spesialis: null,
SpesialisName: '',
subSpesialis: null,
SubSpesialisName: '',
tanggalDaftar: '',
kategoriOperasi: null,
kategoriName: '',
rencanaOperasi: null,
keterangan: null
};
dokterPelaksanaItems.value = [];
statusPasienData.value = {
tanggalSelesai: null,
statusOperasi: '',
keteranganStatus: null
};
};
// Fetch detail data
const fetchDetailData = async () => {
if (!props.id) return;
loading.value = true;
error.value = false;
try {
const response = await getAntrianOperasiById(props.id);
if (response.success && response.data) {
const data = response.data;
// Set form data
if (data.formData) {
formData.value = { ...data.formData };
}
// Set diagnosis items
if (data.diagnosisItems) {
diagnosisItems.value = data.diagnosisItems;
}
// Set tindakan items
if (data.tindakanItems) {
tindakanItems.value = data.tindakanItems;
}
// Set rencana operasi data
if (data.rencanaOperasiData) {
rencanaOperasiData.value = { ...data.rencanaOperasiData };
}
// Set dokter pelaksana items
if (data.dokterPelaksanaItems) {
dokterPelaksanaItems.value = data.dokterPelaksanaItems;
}
// Set status pasien data
if (data.statusPasienData) {
statusPasienData.value = { ...data.statusPasienData };
}
}
} catch (err) {
console.error('Error fetching detail data:', err);
error.value = true;
} finally {
loading.value = false;
}
};
// Initialize when modal opens
watch(() => props.modelValue, (isOpen) => {
if (isOpen && props.id) {
//reset form dulu diawal
initializeForm();
fetchDetailData();
}
}, { immediate: true });
const closeModal = () => {
showModal.value = false;
};
</script>
<template>
<v-dialog v-model="showModal" max-width="1200px" persistent scrollable>
<v-card>
<v-card-title class="d-flex align-center justify-space-between pa-4">
<div class="d-flex align-center">
<Icon icon="solar:clipboard-text-bold-duotone" height="28" class="text-primary mr-3" />
<span class="text-h6">Detail Pendaftaran Antrean Operasi</span>
</div>
<v-btn icon variant="text" @click="closeModal">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="pa-5 bg-background" style="max-height: 75vh; overflow-y: auto;">
<LoadingState :loading="loading" :error="error" loading-text="Memuat detail pendaftaran..."
error-text="Gagal memuat data detail" @retry="fetchDetailData">
<v-row v-if="!loading && !error">
<v-col cols="6">
<!-- Informasi Pasien -->
<v-card elevation="10" class="mb-4" style="height: 100%;">
<v-card-title class="bg-blue-lighten-5 pa-4">
<div class="d-flex align-center">
<Icon icon="solar:user-bold-duotone" height="24" class="mr-2 text-primary" />
<span class="text-h6">Biodata Pasien</span>
</div>
</v-card-title>
<v-card-text class="pa-5">
<v-row>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="mdi-card-account-details" height="20" class="mr-2" />
No Rekam Medis
</div>
<div class="info-value font-weight-bold">
{{ formData.noRekamMedis || '-' }}
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="mdi-credit-card" height="20" class="mr-2" />
No KTP
</div>
<div class="info-value">
{{ formData.noKtp || '-' }}
</div>
</div>
</v-col>
<v-col cols="12" md="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:user-heart-bold" height="20" class="mr-2" />
Nama Pasien
</div>
<div class="info-value font-weight-bold">
{{ formData.namaPasien || '-' }}
<v-chip :color="getGenderColor(formData.jenisKelamin)" size="small"
variant="flat" class="ml-2">
<Icon :icon="getGenderIcon(formData.jenisKelamin)"
height="16" />
</v-chip>
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:calendar-bold" height="20" class="mr-2" />
Tanggal Lahir
</div>
<div class="info-value">
{{ formatDate(formData.tanggalLahir) }}
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="mdi-cake-variant" height="20" class="mr-2" />
Umur
</div>
<div class="info-value">
{{ formData.umur || '-' }}
</div>
</div>
</v-col>
<v-col cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:map-point-bold" height="20" class="mr-2" />
Alamat
</div>
<div class="info-value">
{{ formData.alamat || '-' }}
</div>
</div>
</v-col>
<v-col cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:phone-bold" height="20" class="mr-2" />
Nomor Telepon
</div>
<div class="info-value">
<v-chip v-for="(phone, index) in formData.nomorTelepon" :key="index"
color="success" variant="tonal" size="small" class="mr-2 mb-2">
<Icon icon="mdi-phone" height="16" class="mr-1" />
{{ phone }}
</v-chip>
<span
v-if="!formData.nomorTelepon || formData.nomorTelepon.length === 0">-</span>
</div>
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="6">
<!-- Rencana Operasi -->
<v-card elevation="10" class="mb-4" style="height: 100%;">
<v-card-title class="bg-orange-lighten-5 pa-4">
<div class="d-flex align-center">
<Icon icon="solar:calendar-mark-bold-duotone" height="24"
class="mr-2 text-warning" />
<span class="text-h6">Rencana Operasi</span>
</div>
</v-card-title>
<v-card-text class="pa-4">
<v-row>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:stethoscope-bold" height="20" class="mr-2" />
Spesialis
</div>
<div class="info-value font-weight-bold">
{{ rencanaOperasiData.SpesialisName || '-' }}
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:health-bold" height="20" class="mr-2" />
Sub Spesialis
</div>
<div class="info-value font-weight-bold">
{{ rencanaOperasiData.SubSpesialisName || '-' }}
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:calendar-date-bold" height="20" class="mr-2" />
Tanggal Daftar
</div>
<div class="info-value">
{{ formatDateTime(rencanaOperasiData.tanggalDaftar) }}
</div>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:clipboard-list-bold" height="20" class="mr-2" />
Kategori Operasi
</div>
<div class="info-value">
<v-chip color="orange" size="small" variant="flat">
{{ rencanaOperasiData.kategoriName.split('-')[1]?.trim() || '-' }}
</v-chip>
</div>
</div>
</v-col>
<v-col v-if="rencanaOperasiData.rencanaOperasi" cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:notes-bold" height="20" class="mr-2" />
Rencana Operasi
</div>
<div class="info-value">
{{ rencanaOperasiData.rencanaOperasi }}
</div>
</div>
</v-col>
<v-col v-if="rencanaOperasiData.keterangan" cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:notebook-bold" height="20" class="mr-2" />
Keterangan
</div>
<div class="info-value">
{{ rencanaOperasiData.keterangan }}
</div>
</div>
</v-col>
<v-col v-if="dokterPelaksanaItems.length > 0" cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:user-id-bold-duotone" height="20" class="mr-2" />
Dokter Pelaksana
</div>
<v-list v-if="dokterPelaksanaItems.length > 0" class="pa-0">
<v-list-item v-for="(dokter, index) in dokterPelaksanaItems"
:key="index" class="pa-2 mb-2 rounded-lg border">
<template #prepend>
<v-avatar color="indigo" size="48">
<Icon icon="solar:user-bold" height="24" />
</v-avatar>
</template>
<v-list-item-title class="font-weight-bold">
{{ dokter.nama }}
</v-list-item-title>
<v-list-item-subtitle>
<div class="mt-1">
<v-chip size="x-small" color="indigo" variant="outlined"
class="mr-2">
NIP: {{ dokter.nip }}
</v-chip>
<v-chip size="x-small" color="indigo" variant="tonal">
{{ dokter.satuan_kerja }}
</v-chip>
</div>
</v-list-item-subtitle>
</v-list-item>
</v-list>
<v-alert v-else type="info" variant="tonal" density="compact">
Tidak ada data dokter pelaksana
</v-alert>
</div>
</v-col>
<v-col cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="mdi-information" height="20" class="mr-2" />
Status
</div>
<div class="info-value">
<v-chip :color="getStatusColor(statusPasienData.statusOperasi)"
variant="flat" size="small">
<Icon :icon="getStatusIcon(statusPasienData.statusOperasi)"
height="16" class="mr-1" />
{{ getStatusText(statusPasienData.statusOperasi) }}
</v-chip>
</div>
</div>
</v-col>
<v-col v-if="statusPasienData.tanggalSelesai" cols="12" md="6">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:calendar-check-bold" height="20" class="mr-2" />
Tanggal Selesai
</div>
<div class="info-value">
{{ formatDateTime(statusPasienData.tanggalSelesai) }}
</div>
</div>
</v-col>
<v-col v-if="statusPasienData.keteranganStatus" cols="12">
<div class="info-item">
<div class="info-label">
<Icon icon="solar:notes-bold" height="20" class="mr-2" />
Keterangan Status
</div>
<div class="info-value">
{{ statusPasienData.keteranganStatus }}
</div>
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
<v-col cols="6">
<!-- Diagnosis -->
<v-card elevation="10" class="mb-4" style="height: 100%;">
<v-card-title class="bg-green-lighten-5 pa-4">
<div class="d-flex align-center">
<Icon icon="solar:health-bold-duotone" height="24" class="mr-2 text-success" />
<span class="text-h6">Diagnosis</span>
</div>
</v-card-title>
<v-card-text class="pa-4">
<v-list v-if="diagnosisItems.length > 0" class="pa-0">
<v-list-item v-for="(item, index) in diagnosisItems" :key="index"
class="px-0 mb-3">
<v-card variant="tonal" color="success">
<v-card-text class="pa-3">
<div class="d-flex align-start">
<v-avatar color="success" size="32" class="mr-3">
{{ index + 1 }}
</v-avatar>
<div class="flex-1-1">
<div class="font-weight-medium mb-1">
{{ item.diagnosa }}
</div>
<div class="text-caption text-medium-emphasis mb-1">
{{ item.jenisDiagnosa }}
</div>
<v-chip size="x-small" color="success" variant="outlined">
{{ item.kodeDiagnosa }}
</v-chip>
</div>
</div>
</v-card-text>
</v-card>
</v-list-item>
</v-list>
<v-alert v-else type="info" variant="tonal" density="compact">
Tidak ada data diagnosis
</v-alert>
</v-card-text>
</v-card>
</v-col>
<v-col cols="6">
<!-- Tindakan -->
<v-card elevation="10" class="mb-4" style="height: 100%;">
<v-card-title class="bg-purple-lighten-5 pa-4">
<div class="d-flex align-center">
<Icon icon="solar:medical-kit-bold-duotone" height="24"
class="mr-2 text-secondary" />
<span class="text-h6">Tindakan Operasi</span>
</div>
</v-card-title>
<v-card-text class="pa-4">
<v-list v-if="tindakanItems.length > 0" class="pa-0">
<v-list-item v-for="(item, index) in tindakanItems" :key="index"
class="px-0 mb-3">
<v-card variant="tonal" color="secondary">
<v-card-text class="pa-3">
<div class="d-flex align-start">
<v-avatar color="secondary" size="32" class="mr-3">
{{ index + 1 }}
</v-avatar>
<div class="flex-1-1">
<div class="font-weight-medium mb-1">
{{ item.tindakan }}
</div>
<div class="text-body-2 text-medium-emphasis mb-2">
{{ item.tindakanTambahan || '-' }}
</div>
<v-chip size="x-small" color="purple" variant="outlined">
{{ item.kodeTindakan || '-' }}
</v-chip>
</div>
</div>
</v-card-text>
</v-card>
</v-list-item>
</v-list>
<v-alert v-else type="info" variant="tonal" density="compact">
Tidak ada data tindakan
</v-alert>
</v-card-text>
</v-card>
</v-col>
</v-row>
</LoadingState>
</v-card-text>
</v-card>
</v-dialog>
</template>
<style scoped>
.info-item {
margin-bottom: 1rem;
}
.info-label {
display: flex;
align-items: center;
font-size: 0.875rem;
color: rgb(var(--v-theme-on-surface));
opacity: 0.7;
margin-bottom: 0.5rem;
font-weight: 500;
}
.info-value {
font-size: 1rem;
color: rgb(var(--v-theme-on-surface));
padding-left: 28px;
}
</style>