Files
antrean-operasi/components/pendaftaran/ModalUpdateStatus.vue
T
2026-02-18 08:21:58 +07:00

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>