diff --git a/components/pendaftaran/BiodataPasien.vue b/components/pendaftaran/BiodataPasien.vue index eb35c35..5bcb582 100644 --- a/components/pendaftaran/BiodataPasien.vue +++ b/components/pendaftaran/BiodataPasien.vue @@ -85,6 +85,54 @@ const handlePatientSelect = (patient: PatientData | null) => { } }; +// Fetch patient by RM number (for edit mode) +const fetchPatientByRm = async (rm: string) => { + if (!rm) return; + + loadingPatients.value = true; + + try { + const response = await api.get('/reference/pasien', { + params: { + limit: 1, + search: rm + } + }); + + if (response.data?.data && response.data.data.length > 0) { + const patient = response.data.data[0]; + if (patient.nomr === rm) { + selectedPatient.value = patient; + patientItems.value = [patient]; + } + } + } catch (error) { + console.error('Error fetching patient by RM:', error); + } finally { + loadingPatients.value = false; + } +}; + +// Watch for formData changes to load patient details in edit mode +watchEffect(async () => { + const rm = formData.value.noRekamMedis; + const nama = formData.value.namaPasien; + + // Use nextTick to ensure all reactive updates are complete + await nextTick(); + + // If both RM and Nama are populated but selectedPatient is not, fetch patient details + if (rm && nama && !selectedPatient.value) { + await fetchPatientByRm(rm); + } + + // Clear selectedPatient if RM is cleared + if (!rm && selectedPatient.value) { + selectedPatient.value = null; + patientItems.value = []; + } +}); + // Phone validation rules const phoneLength = (value: string) => { if (!value) return true; @@ -164,7 +212,6 @@ const deletePhoneNumber = (index: number) => { No Rekam Medis * { Jenis Kelamin - + @@ -271,7 +320,7 @@ const deletePhoneNumber = (index: number) => { - + Nomor Telepon @@ -285,6 +334,7 @@ const deletePhoneNumber = (index: number) => { type="tel" hide-details="auto" @input="handlePhoneInput" + @keyup.enter="addPhoneNumber" :error-messages="phoneError !== true && newPhoneNumber ? [phoneError as string] : []" class="flex-1-1" > diff --git a/components/pendaftaran/MedisPasien.vue b/components/pendaftaran/MedisPasien.vue index 1164e77..6a788fc 100644 --- a/components/pendaftaran/MedisPasien.vue +++ b/components/pendaftaran/MedisPasien.vue @@ -10,14 +10,14 @@ const props = withDefaults(defineProps(), { const diagnosisItems = defineModel('diagnosisItems', { required: true }); const tindakanItems = defineModel('tindakanItems', { required: true }); -// Modal state for diagnosis -const showDiagnosisModal = ref(false); +// Form state for diagnosis const diagnosisForm = ref({ kodeDiagnosa: '', diagnosa: '', jenisDiagnosa: 'utama' }); const editingDiagnosisIndex = ref(null); +const diagnosisFormExpanded = ref(true); // Always show form const diagnosisList = ref([]); const loadingDiagnosis = ref(false); const diagnosisSearchTimeout = ref(null); @@ -75,14 +75,14 @@ const diagnosisOptions = computed(() => { })); }); -// Modal state for tindakan -const showTindakanModal = ref(false); +// Form state for tindakan const tindakanForm = ref({ kodeTindakan: '', tindakan: '', tindakanTambahan: '' }); const editingTindakanIndex = ref(null); +const tindakanFormExpanded = ref(true); // Always show form const tindakanList = ref([]); const loadingTindakan = ref(false); const tindakanSearchTimeout = ref(null); @@ -148,20 +148,22 @@ if (!Array.isArray(tindakanItems.value)) { tindakanItems.value = []; } -const openDiagnosisModal = () => { +const resetDiagnosisForm = () => { diagnosisForm.value = { kodeDiagnosa: '', diagnosa: '', jenisDiagnosa: 'utama' }; editingDiagnosisIndex.value = null; - showDiagnosisModal.value = true; +}; + +const openDiagnosisModal = () => { + resetDiagnosisForm(); }; const editDiagnosis = (index: number) => { diagnosisForm.value = { ...diagnosisItems.value[index] }; editingDiagnosisIndex.value = index; - showDiagnosisModal.value = true; }; const saveDiagnosis = () => { @@ -173,13 +175,11 @@ const saveDiagnosis = () => { diagnosisItems.value.push({ ...diagnosisForm.value }); } - showDiagnosisModal.value = false; - diagnosisForm.value = { - kodeDiagnosa: '', - diagnosa: '', - jenisDiagnosa: 'utama' - }; - editingDiagnosisIndex.value = null; + resetDiagnosisForm(); +}; + +const cancelEditDiagnosis = () => { + resetDiagnosisForm(); }; const deleteDiagnosis = (index: number) => { @@ -191,24 +191,25 @@ const handleKodeDiagnosaChange = (kode: string) => { const found = diagnosisList.value.find(d => d.kodeicd === kode); if (found) { diagnosisForm.value.diagnosa = found.keterangan; - // Keep jenisDiagnosa as is or default to current value } }; -const openTindakanModal = () => { +const resetTindakanForm = () => { tindakanForm.value = { kodeTindakan: '', tindakan: '', tindakanTambahan: '' }; editingTindakanIndex.value = null; - showTindakanModal.value = true; +}; + +const openTindakanModal = () => { + resetTindakanForm(); }; const editTindakan = (index: number) => { tindakanForm.value = { ...tindakanItems.value[index] }; editingTindakanIndex.value = index; - showTindakanModal.value = true; }; const saveTindakan = () => { @@ -220,13 +221,11 @@ const saveTindakan = () => { tindakanItems.value.push({ ...tindakanForm.value }); } - showTindakanModal.value = false; - tindakanForm.value = { - kodeTindakan: '', - tindakan: '', - tindakanTambahan: '' - }; - editingTindakanIndex.value = null; + resetTindakanForm(); +}; + +const cancelEditTindakan = () => { + resetTindakanForm(); }; const deleteTindakan = (index: number) => { @@ -238,296 +237,363 @@ const handleKodeTindakanChange = (kode: string) => { const found = tindakanList.value.find(t => t.kode === kode); if (found) { tindakanForm.value.tindakan = found.keterangan; - // Keep tindakanTambahan as is or user can input manually } }; diff --git a/components/pendaftaran/ModalDetailPendaftaran.vue b/components/pendaftaran/ModalDetailPendaftaran.vue new file mode 100644 index 0000000..8789cbb --- /dev/null +++ b/components/pendaftaran/ModalDetailPendaftaran.vue @@ -0,0 +1,676 @@ + + + + + diff --git a/components/pendaftaran/ModalPendaftaran.vue b/components/pendaftaran/ModalPendaftaran.vue index 78d4fbd..926f32a 100644 --- a/components/pendaftaran/ModalPendaftaran.vue +++ b/components/pendaftaran/ModalPendaftaran.vue @@ -3,7 +3,9 @@ import BiodataPasien from '@/components/pendaftaran/BiodataPasien.vue'; import MedisPasien from '@/components/pendaftaran/MedisPasien.vue'; import RencanaOperasi from '@/components/pendaftaran/RencanaOperasi.vue'; import StatusPasienOperasi from '@/components/pendaftaran/StatusPasienOperasi.vue'; +import LoadingState from '@/components/shared/LoadingState.vue'; import { usePendaftaranStore } from '@/store/pendaftaran'; +import { getAntrianOperasiById } from '@/services/antrean'; import { storeToRefs } from 'pinia'; import type { PageMode } from '~/types/common'; @@ -11,11 +13,17 @@ interface Props { modelValue: boolean; mode?: PageMode; id?: string | number; + initialKategori?: number; + initialSpesialis?: number; + initialSubSpesialis?: number; } const props = withDefaults(defineProps(), { mode: 'create', - id: undefined + id: undefined, + initialKategori: undefined, + initialSpesialis: undefined, + initialSubSpesialis: undefined }); const emit = defineEmits<{ @@ -38,6 +46,8 @@ const { const form = ref(); const valid = ref(true); +const loadingDetail = ref(false); +const errorDetail = ref(false); // Computed properties const isReadonly = computed(() => props.mode === 'view'); @@ -59,13 +69,93 @@ const showModal = computed({ }); // Initialize form when modal opens -watch(() => props.modelValue, (isOpen) => { +watch(() => props.modelValue, async (isOpen) => { if (isOpen) { - initializeForm(); + await initializeForm(); } -}); +}, { immediate: true }); // Add immediate to handle initial render -const initializeForm = () => { +// Fetch detail data for edit mode +const fetchDetailData = async () => { + if (!props.id) return; + + loadingDetail.value = true; + errorDetail.value = false; + + try { + const response = await getAntrianOperasiById(props.id); + + if (response.success && response.data) { + const data = response.data; + + // Set form data to store + if (data.formData) { + pendaftaranStore.formData = { ...data.formData }; + } + + // Set diagnosis items to store + if (data.diagnosisItems) { + pendaftaranStore.diagnosisItems = [...data.diagnosisItems]; + } + + // Set tindakan items to store + if (data.tindakanItems) { + pendaftaranStore.tindakanItems = [...data.tindakanItems]; + } + + // Set rencana operasi data to store + if (data.rencanaOperasiData) { + const rencanaData = { ...data.rencanaOperasiData }; + + // Convert tanggalDaftar from ISO format to datetime-local format + if (rencanaData.tanggalDaftar) { + // Remove 'Z' and convert to local datetime format + const date = new Date(rencanaData.tanggalDaftar); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + rencanaData.tanggalDaftar = `${year}-${month}-${day}T${hours}:${minutes}`; + } + + pendaftaranStore.rencanaOperasiData = rencanaData; + } + + // Set dokter pelaksana items to store with proper mapping + if (data.dokterPelaksanaItems && Array.isArray(data.dokterPelaksanaItems)) { + // Map the API response to match Dokter interface + pendaftaranStore.dokterPelaksanaItems = data.dokterPelaksanaItems.map((dokter: any) => ({ + id: dokter.id, + nip: dokter.nip, + nama_lengkap: dokter.nama || dokter.nama_lengkap, + hfis_code: dokter.hfis_code || null, + nama_ksm: dokter.satuan_kerja || dokter.nama_ksm || '' + })); + } + + // Set status pasien data to store + if (data.statusPasienData) { + pendaftaranStore.statusPasienData = { ...data.statusPasienData }; + } + } + } catch (err) { + console.error('Error fetching detail data:', err); + errorDetail.value = true; + pendaftaranStore.showSnackbar('Gagal mengambil data detail', 'error'); + } finally { + loadingDetail.value = false; + } +}; + +const initializeForm = async () => { + // If edit or view mode, load data by id first + if ((props.mode === 'edit' || props.mode === 'view') && props.id) { + await fetchDetailData(); + return; // Don't reset form for edit/view mode + } + + // For create mode, reset form and set defaults pendaftaranStore.resetForm(); // Set default tanggal daftar ke now @@ -77,10 +167,15 @@ const initializeForm = () => { const minutes = String(now.getMinutes()).padStart(2, '0'); pendaftaranStore.rencanaOperasiData.tanggalDaftar = `${year}-${month}-${day}T${hours}:${minutes}`; - // If edit or view mode, load data by id - if ((props.mode === 'edit' || props.mode === 'view') && props.id) { - // TODO: Load data from API by id - // await pendaftaranStore.loadData(props.id); + // Set initial values from parent context + if (props.initialKategori !== undefined) { + pendaftaranStore.rencanaOperasiData.kategoriOperasi = props.initialKategori; + } + if (props.initialSpesialis !== undefined) { + pendaftaranStore.rencanaOperasiData.spesialis = props.initialSpesialis; + } + if (props.initialSubSpesialis !== undefined) { + pendaftaranStore.rencanaOperasiData.subSpesialis = props.initialSubSpesialis; } }; @@ -101,8 +196,10 @@ const handleSimpan = async () => { return; } - // Submit form using store action - const result = await pendaftaranStore.submitForm(); + // Submit form using store action - pass id if edit mode + const result = props.mode === 'edit' && props.id + ? await pendaftaranStore.submitForm(props.id) + : await pendaftaranStore.submitForm(); if (result.success) { emit('success'); @@ -133,7 +230,7 @@ const handleReset = () => {