418 lines
18 KiB
Vue
418 lines
18 KiB
Vue
<script setup lang="ts">
|
|
import { Icon } from '@iconify/vue';
|
|
import type { AntreanOperasi } from '~/types/antrean';
|
|
import { STATUS } from '~/types/antrean';
|
|
import { getAntrianOperasiById, updateStatusAntrianOperasi } from '@/services/antrean';
|
|
|
|
interface Props {
|
|
modelValue: boolean;
|
|
id: string | number;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: boolean): void;
|
|
(e: 'success'): void;
|
|
}>();
|
|
|
|
const dialog = computed({
|
|
get: () => props.modelValue,
|
|
set: (value) => emit('update:modelValue', value)
|
|
});
|
|
|
|
const loading = ref(false);
|
|
const loadingSubmit = ref(false);
|
|
const error = ref(false);
|
|
|
|
// Data pasien - struktur lengkap dari API
|
|
const pasienData = ref<any>(null);
|
|
|
|
// Form data
|
|
const formData = ref<{
|
|
statusOperasi: string;
|
|
tanggalSelesai: string;
|
|
keteranganStatus: string;
|
|
}>({
|
|
statusOperasi: STATUS.BELUM,
|
|
tanggalSelesai: '',
|
|
keteranganStatus: ''
|
|
});
|
|
|
|
// Snackbar
|
|
const snackbar = ref(false);
|
|
const snackbarMessage = ref('');
|
|
const snackbarColor = ref('success');
|
|
|
|
const showSnackbar = (message: string, color: string = 'success') => {
|
|
snackbarMessage.value = message;
|
|
snackbarColor.value = color;
|
|
snackbar.value = true;
|
|
};
|
|
|
|
// Fetch data pasien
|
|
const fetchData = async () => {
|
|
if (!props.id) return;
|
|
|
|
loading.value = true;
|
|
error.value = false;
|
|
|
|
try {
|
|
const response = await getAntrianOperasiById(props.id);
|
|
|
|
if (response.success && response.data) {
|
|
pasienData.value = response.data;
|
|
|
|
// Set initial form data dari status yang ada
|
|
if (pasienData.value.statusPasienData) {
|
|
formData.value.statusOperasi = pasienData.value.statusPasienData.statusOperasi || STATUS.BELUM;
|
|
formData.value.tanggalSelesai = pasienData.value.statusPasienData.tanggalSelesai || '';
|
|
formData.value.keteranganStatus = pasienData.value.statusPasienData.keteranganStatus || '';
|
|
} else {
|
|
formData.value.statusOperasi = STATUS.BELUM;
|
|
formData.value.tanggalSelesai = '';
|
|
formData.value.keteranganStatus = '';
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Error fetching data:', err);
|
|
error.value = true;
|
|
showSnackbar('Gagal mengambil data pasien', 'error');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
// Validation
|
|
const isFormValid = computed(() => {
|
|
if (formData.value.statusOperasi === STATUS.SELESAI && !formData.value.tanggalSelesai) {
|
|
return false;
|
|
}
|
|
if ((formData.value.statusOperasi === STATUS.TUNDA || formData.value.statusOperasi === STATUS.BATAL)
|
|
&& !formData.value.keteranganStatus.trim()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Submit handler
|
|
const handleSubmit = async () => {
|
|
if (!isFormValid.value) {
|
|
showSnackbar('Mohon lengkapi data yang diperlukan', 'error');
|
|
return;
|
|
}
|
|
|
|
loadingSubmit.value = true;
|
|
try {
|
|
const response = await updateStatusAntrianOperasi(props.id, {
|
|
statusOperasi: formData.value.statusOperasi,
|
|
tanggalSelesai: formData.value.tanggalSelesai ? new Date(formData.value.tanggalSelesai).toISOString() : null,
|
|
keteranganStatus: formData.value.keteranganStatus || null
|
|
});
|
|
|
|
if (response.success) {
|
|
showSnackbar('Status antrian berhasil diupdate', 'success');
|
|
emit('success');
|
|
dialog.value = false;
|
|
} else {
|
|
showSnackbar( 'Gagal mengupdate status antrian', 'error');
|
|
}
|
|
} catch (error: any) {
|
|
console.error('Error updating status:', error);
|
|
showSnackbar('Gagal mengupdate status antrian', 'error');
|
|
} finally {
|
|
loadingSubmit.value = false;
|
|
}
|
|
};
|
|
|
|
// Format date
|
|
const formatDate = (date: string) => {
|
|
if (!date) return '-';
|
|
return new Date(date).toLocaleDateString('id-ID', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
};
|
|
|
|
// Helper functions
|
|
const getGenderText = (gender: string) => {
|
|
return gender === 'L' ? 'Laki-laki' : 'Perempuan';
|
|
};
|
|
|
|
// Min date for tanggal selesai (same as tanggal daftar)
|
|
const minTanggalSelesai = computed(() => {
|
|
if (!pasienData.value?.rencanaOperasiData?.tanggalDaftar) return '';
|
|
return new Date(pasienData.value.rencanaOperasiData.tanggalDaftar).toISOString().slice(0, 16);
|
|
});
|
|
|
|
// Initialize form
|
|
const initializeForm = () => {
|
|
formData.value = {
|
|
statusOperasi: STATUS.BELUM,
|
|
tanggalSelesai: '',
|
|
keteranganStatus: ''
|
|
};
|
|
};
|
|
|
|
// Watch dialog - trigger saat modal dibuka
|
|
watch(() => props.modelValue, (isOpen) => {
|
|
if (isOpen && props.id) {
|
|
initializeForm();
|
|
fetchData();
|
|
}
|
|
}, { immediate: true });
|
|
</script>
|
|
|
|
<template>
|
|
<v-dialog v-model="dialog" max-width="800" persistent scrollable>
|
|
<v-card>
|
|
<v-card-title class="d-flex align-center justify-space-between bg-primary pa-4">
|
|
<div class="d-flex align-center">
|
|
<Icon icon="solar:clipboard-check-bold" height="24" class="mr-2" />
|
|
<span class="text-h5">Update Status Antrian Operasi</span>
|
|
</div>
|
|
<v-btn
|
|
icon
|
|
variant="text"
|
|
size="small"
|
|
@click="dialog = false"
|
|
:disabled="loadingSubmit"
|
|
>
|
|
<Icon icon="mdi-close" height="20" />
|
|
</v-btn>
|
|
</v-card-title>
|
|
<v-card-text class="pa-4" style="max-height: 600px;">
|
|
<!-- Loading State -->
|
|
<div v-if="loading" class="text-center py-8">
|
|
<v-progress-circular
|
|
indeterminate
|
|
color="primary"
|
|
size="50"
|
|
></v-progress-circular>
|
|
<p class="mt-4 text-medium-emphasis">Memuat data...</p>
|
|
</div>
|
|
|
|
<!-- Error State -->
|
|
<div v-else-if="error" class="text-center py-8">
|
|
<v-icon size="64" color="error" class="mb-4">mdi-alert-circle</v-icon>
|
|
<p class="text-h6 mb-2">Gagal memuat data</p>
|
|
<v-btn color="primary" @click="fetchData" variant="outlined">
|
|
<Icon icon="mdi-refresh" height="20" class="mr-2" />
|
|
Coba Lagi
|
|
</v-btn>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div v-else-if="pasienData">
|
|
<!-- Informasi Pasien -->
|
|
<v-card variant="outlined" class="mb-6">
|
|
<v-card-item>
|
|
<h6 class="text-h6 font-weight-bold mb-4">
|
|
<Icon icon="solar:user-bold" height="20" class="mr-2" />
|
|
Informasi Pasien
|
|
</h6>
|
|
</v-card-item>
|
|
<v-divider></v-divider>
|
|
<v-card-text >
|
|
<v-row>
|
|
<v-col cols="12" sm="6">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">No. Rekam Medis</div>
|
|
<div class="text-body-1 font-weight-medium">{{ pasienData.formData?.noRekamMedis || '-' }}</div>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Nama Pasien</div>
|
|
<div class="text-body-1 font-weight-medium">{{ pasienData.formData?.namaPasien || '-' }}</div>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Jenis Kelamin</div>
|
|
<div class="text-body-1">{{ getGenderText(pasienData.formData?.jenisKelamin) }}</div>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="6">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Tanggal Daftar</div>
|
|
<div class="text-body-1">{{ formatDate(pasienData.rencanaOperasiData?.tanggalDaftar) }}</div>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Kategori</div>
|
|
<v-chip size="small" color="primary" variant="tonal">
|
|
{{ pasienData.rencanaOperasiData?.kategoriName || '-' }}
|
|
</v-chip>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Spesialis</div>
|
|
<v-chip size="small" color="secondary" variant="tonal">
|
|
{{ pasienData.rencanaOperasiData?.SpesialisName || '-' }}
|
|
</v-chip>
|
|
</div>
|
|
</v-col>
|
|
<v-col cols="12" sm="4">
|
|
<div class="mb-3">
|
|
<div class="text-caption text-medium-emphasis">Sub Spesialis</div>
|
|
<v-chip size="small" color="info" variant="tonal">
|
|
{{ pasienData.rencanaOperasiData?.SubSpesialisName || '-' }}
|
|
</v-chip>
|
|
</div>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Form Update Status -->
|
|
<v-card variant="outlined">
|
|
<v-card-item>
|
|
<h6 class="text-h6 font-weight-bold mb-4">
|
|
<Icon icon="solar:settings-bold" height="20" class="mr-2" />
|
|
Update Status Operasi
|
|
</h6>
|
|
</v-card-item>
|
|
<v-divider></v-divider>
|
|
<v-card-text>
|
|
<v-row>
|
|
<!-- Status Operasi -->
|
|
<v-col cols="12">
|
|
<v-label class="mb-2 font-weight-medium">
|
|
Status Operasi <span class="text-error">*</span>
|
|
</v-label>
|
|
<v-row class="mt-2">
|
|
<v-col cols="12" sm="12">
|
|
<v-btn-group
|
|
v-model="formData.statusOperasi"
|
|
mandatory
|
|
class="w-100"
|
|
>
|
|
<v-btn
|
|
class="flex-grow-1"
|
|
size="large"
|
|
:variant="formData.statusOperasi === STATUS.BELUM ? 'flat' : 'outlined'"
|
|
:color="formData.statusOperasi === STATUS.BELUM ? 'primary' : 'default'"
|
|
@click="formData.statusOperasi = STATUS.BELUM"
|
|
>
|
|
Belum
|
|
</v-btn>
|
|
<v-btn
|
|
class="flex-grow-1"
|
|
size="large"
|
|
:variant="formData.statusOperasi === STATUS.SELESAI ? 'flat' : 'outlined'"
|
|
:color="formData.statusOperasi === STATUS.SELESAI ? 'success' : 'default'"
|
|
@click="formData.statusOperasi = STATUS.SELESAI"
|
|
>
|
|
Selesai
|
|
</v-btn>
|
|
<v-btn
|
|
class="flex-grow-1"
|
|
size="large"
|
|
:variant="formData.statusOperasi === STATUS.TUNDA ? 'flat' : 'outlined'"
|
|
:color="formData.statusOperasi === STATUS.TUNDA ? 'warning' : 'default'"
|
|
@click="formData.statusOperasi = STATUS.TUNDA"
|
|
>
|
|
Tunda
|
|
</v-btn>
|
|
<v-btn
|
|
class="flex-grow-1"
|
|
size="large"
|
|
:variant="formData.statusOperasi === STATUS.BATAL ? 'flat' : 'outlined'"
|
|
:color="formData.statusOperasi === STATUS.BATAL ? 'error' : 'default'"
|
|
@click="formData.statusOperasi = STATUS.BATAL"
|
|
>
|
|
Batal
|
|
</v-btn>
|
|
</v-btn-group>
|
|
</v-col>
|
|
</v-row>
|
|
</v-col>
|
|
|
|
<!-- Tanggal Selesai (shown only when status is Selesai) -->
|
|
<v-col v-if="formData.statusOperasi === STATUS.SELESAI" cols="12">
|
|
<v-label class="mb-2 font-weight-medium">
|
|
Tanggal Selesai Operasi <span class="text-error">*</span>
|
|
</v-label>
|
|
<v-text-field
|
|
v-model="formData.tanggalSelesai"
|
|
type="datetime-local"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
hide-details="auto"
|
|
:min="minTanggalSelesai"
|
|
:error-messages="!formData.tanggalSelesai ? ['Tanggal selesai wajib diisi'] : []"
|
|
></v-text-field>
|
|
</v-col>
|
|
|
|
<!-- Keterangan (shown only when status is Tunda or Batal) -->
|
|
<v-col v-if="formData.statusOperasi === STATUS.TUNDA || formData.statusOperasi === STATUS.BATAL" cols="12">
|
|
<v-label class="mb-2 font-weight-medium">
|
|
Keterangan <span class="text-error">*</span>
|
|
</v-label>
|
|
<v-textarea
|
|
v-model="formData.keteranganStatus"
|
|
variant="outlined"
|
|
rows="3"
|
|
hide-details="auto"
|
|
:placeholder="`Masukkan alasan ${formData.statusOperasi === STATUS.TUNDA ? 'penundaan' : 'pembatalan'} operasi`"
|
|
:error-messages="!formData.keteranganStatus.trim() ? ['Keterangan wajib diisi'] : []"
|
|
></v-textarea>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
</v-card>
|
|
</div>
|
|
</v-card-text>
|
|
|
|
<v-divider></v-divider>
|
|
|
|
<v-card-actions class="pa-4">
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
variant="outlined"
|
|
@click="dialog = false"
|
|
:disabled="loadingSubmit"
|
|
size="large"
|
|
prepend-icon="mdi-close"
|
|
>
|
|
Batal
|
|
</v-btn>
|
|
<v-btn
|
|
color="primary"
|
|
@click="handleSubmit"
|
|
:loading="loadingSubmit"
|
|
:disabled="loading || !isFormValid"
|
|
variant="flat"
|
|
size="large"
|
|
prepend-icon="mdi-content-save"
|
|
>
|
|
Simpan
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
|
|
<!-- Snackbar -->
|
|
<v-snackbar
|
|
v-model="snackbar"
|
|
:color="snackbarColor"
|
|
:timeout="3000"
|
|
location="top"
|
|
>
|
|
{{ snackbarMessage }}
|
|
<template #actions>
|
|
<v-btn
|
|
variant="text"
|
|
@click="snackbar = false"
|
|
>
|
|
Close
|
|
</v-btn>
|
|
</template>
|
|
</v-snackbar>
|
|
</v-dialog>
|
|
</template>
|