617 lines
31 KiB
Vue
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>
|