Files
antrean-operasi/pages/antrean/all.vue
T
2026-02-18 08:21:58 +07:00

337 lines
11 KiB
Vue

<script setup lang="ts">
import BaseBreadcrumb from '@/components/shared/BaseBreadcrumb.vue';
import TableAntrian from '@/components/pendaftaran/TableAntrian.vue';
import ModalPendaftaran from '@/components/pendaftaran/ModalPendaftaran.vue';
import ModalDetailPendaftaran from '@/components/pendaftaran/ModalDetailPendaftaran.vue';
import ModalUpdateStatus from '@/components/pendaftaran/ModalUpdateStatus.vue';
import { getAntrianOperasi, deleteAntrianOperasi } from '~/services/antrean';
import { Icon } from '@iconify/vue';
import type { AntreanOperasi } from '~/types/antrean';
import { STATUS } from '~/types/antrean';
definePageMeta({
middleware: 'auth',
pageTitle: 'Semua Antrean Operasi',
});
const search = ref('');
const statusFilter = ref<string | null>(null);
const antreanList = ref<any[]>([]);
const loading = ref(false);
const currentPage = ref(1);
const itemsPerPage = ref(10);
const totalItems = ref(0);
const router = useRouter();
// Modal state
const showModal = ref(false);
const showEditModal = ref(false);
const showDetailModal = ref(false);
const showUpdateStatusModal = ref(false);
const selectedId = ref<string | number | null>(null);
const selectedIdForEdit = ref<string | number | null>(null);
const selectedIdForStatus = ref<string | number | null>(null);
const statusOptions = [
{ value: null, label: 'Semua Status' },
{ value: STATUS.BELUM, label: 'Belum' },
{ value: STATUS.SELESAI, label: 'Selesai' },
{ value: STATUS.TUNDA, label: 'Tunda' },
{ value: STATUS.BATAL, label: 'Batal' }
];
// Fetch data from API
const fetchData = async () => {
loading.value = true;
try {
const offset = (currentPage.value - 1) * itemsPerPage.value;
const response = await getAntrianOperasi({
type: 'all',
limit: itemsPerPage.value,
offset: offset,
status: statusFilter.value || undefined,
search: search.value || undefined
});
if (response.success) {
antreanList.value = response.data;
totalItems.value = response.Paginate.Total;
}
} catch (error) {
console.error('Error fetching antrian operasi:', error);
showSnackbar('Gagal mengambil data antrian operasi', 'error');
} finally {
loading.value = false;
}
};
// Watch for page changes
watch(currentPage, () => {
fetchData();
});
watch(itemsPerPage, () => {
currentPage.value = 1;
fetchData();
});
// Watch for status filter changes
watch(statusFilter, () => {
currentPage.value = 1;
fetchData();
});
// Watch for search with debounce
let searchTimeout: NodeJS.Timeout | null = null;
watch(search, () => {
if (searchTimeout) {
clearTimeout(searchTimeout);
}
searchTimeout = setTimeout(() => {
currentPage.value = 1;
fetchData();
}, 500); // 500ms debounce
});
// Initial fetch
onMounted(() => {
fetchData();
});
// Snackbar state
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;
};
const allHeaders = [
{ title: 'Nomor', key: 'NoUrutKategori', width: '50px', align: 'center' as const, sortable: false },
{ title: 'Nomor Spesialis', key: 'NoUrutSpesialis', width: '100px', align: 'center' as const, sortable: false },
{ title: 'Nomor Sub Spesialis', key: 'NoUrutSubSpesialis', width: '140px', align: 'center' as const, sortable: false },
{ title: 'Tanggal Daftar', key: 'TglDaftar', sortable: false, width: '150px' },
{ title: 'No Rekam Medis', key: 'NoRekamMedis', sortable: false, width: '180px' },
{ title: 'Nama Pasien', key: 'NamaPasien', sortable: false },
{ title: 'Jenis kelamin', key: 'JenisKelamin', sortable: false },
{ title: 'Status', key: 'StatusOperasi', sortable: false, width: '150px' },
{ title: 'Actions', key: 'actions', sortable: false, width: '120px' }
];
// Hide queue number columns when filtering
const headers = computed(() => {
const isFiltering = search.value || statusFilter.value;
if (isFiltering) {
return allHeaders.filter(header =>
!['NoUrutKategori', 'NoUrutSpesialis', 'NoUrutSubSpesialis'].includes(header.key)
);
}
return allHeaders;
});
// Function to get actions based on item status
const getActionsForItem = (item: AntreanOperasi) => {
const allActions = [
{ icon: 'mdi-eye', color: 'info', tooltip: 'View', event: 'view' },
{ icon: 'mdi-pencil', color: 'primary', tooltip: 'Edit', event: 'edit' },
{ icon: 'mdi-clipboard-check', color: 'success', tooltip: 'Update Status', event: 'updateStatus' },
{ icon: 'mdi-delete', color: 'error', tooltip: 'Delete', event: 'delete' }
];
// If status is Selesai or Batal, only show View action
if (item.StatusOperasi === STATUS.SELESAI || item.StatusOperasi === STATUS.BATAL) {
return allActions.filter(action => action.event === 'view');
}
// For other statuses (Belum, Tunda), show all actions
return allActions;
};
const handleView = async (item: unknown) => {
const data = item as AntreanOperasi;
selectedId.value = data.id;
// Wait for DOM update before opening modal
await nextTick();
showDetailModal.value = true;
};
const handleEdit = async (item: unknown) => {
const data = item as AntreanOperasi;
selectedIdForEdit.value = data.id;
// Wait for DOM update before opening modal
await nextTick();
showEditModal.value = true;
};
const handleUpdateStatus = async (item: unknown) => {
const data = item as AntreanOperasi;
selectedIdForStatus.value = data.id;
// Wait for DOM update before opening modal
await nextTick();
showUpdateStatusModal.value = true;
};
const handleDelete = async (item: unknown) => {
const data = item as AntreanOperasi;
if(confirm('Apakah Anda yakin ingin menghapus data ini?')) {
try {
const response = await deleteAntrianOperasi(data.id);
if (response.success) {
showSnackbar('Data berhasil dihapus.', 'success');
fetchData(); // Refresh the table after deletion
} else {
showSnackbar(response.message || 'Gagal menghapus data', 'error');
}
} catch (error: any) {
console.error('Error deleting antrian operasi:', error);
showSnackbar(error.response?.data?.message || 'Gagal menghapus data', 'error');
}
}
};
const handlePageUpdate = (page: unknown) => {
currentPage.value = page as number;
};
const handleItemsPerPageUpdate = (items: unknown) => {
itemsPerPage.value = items as number;
};
// Modal handlers
const openModal = () => {
showModal.value = true;
};
const handleModalSuccess = () => {
fetchData(); // Refresh the table after successful submission
};
const handleEditModalSuccess = () => {
fetchData(); // Refresh the table after successful edit
};
const handleUpdateStatusSuccess = () => {
fetchData(); // Refresh the table after status update
};
</script>
<template>
<v-row>
<v-col cols="12">
<v-card elevation="10">
<v-card-text>
<div class="d-flex justify-space-between align-center mb-4">
<div class="d-flex align-center w-50 ga-4">
<v-text-field
v-model="search"
placeholder="Search..."
variant="outlined"
density="compact"
hide-details
prepend-inner-icon="mdi-magnify"
style="max-width: 300px;"
></v-text-field>
<v-select
v-model="statusFilter"
:items="statusOptions"
item-title="label"
item-value="value"
variant="outlined"
density="compact"
hide-details
placeholder="Filter Status"
style="max-width: 200px;"
></v-select>
</div>
<v-btn
color="primary"
size="large"
elevation="2"
@click="openModal"
>
<Icon icon="solar:add-circle-bold" height="20" class="mr-2" />
Pendaftaran Operasi Baru
</v-btn>
</div>
<TableAntrian
:headers="headers"
:items="antreanList"
:get-actions="getActionsForItem"
:server-side="true"
:total-items="totalItems"
:current-page="currentPage"
:items-per-page="itemsPerPage"
:loading="loading"
min-width="1500px"
@view="handleView"
@edit="handleEdit"
@updateStatus="handleUpdateStatus"
@delete="handleDelete"
@update:page="handlePageUpdate"
@update:itemsPerPage="handleItemsPerPageUpdate"
>
<template #item.TglDaftar="{ item }">
{{ new Date(item.TglDaftar).toLocaleDateString('id-ID', { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }) }}
</template>
</TableAntrian>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- Snackbar for notifications -->
<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>
<!-- Modal Pendaftaran -->
<ModalPendaftaran
v-model="showModal"
mode="create"
@success="handleModalSuccess"
/>
<!-- Modal Edit Pendaftaran -->
<ModalPendaftaran
v-if="selectedIdForEdit"
v-model="showEditModal"
mode="edit"
:id="selectedIdForEdit"
@success="handleEditModalSuccess"
/>
<!-- Modal Detail Pendaftaran -->
<ModalDetailPendaftaran
v-if="selectedId"
v-model="showDetailModal"
:id="selectedId"
/>
<!-- Modal Update Status -->
<ModalUpdateStatus
v-if="selectedIdForStatus"
v-model="showUpdateStatusModal"
:id="selectedIdForStatus"
@success="handleUpdateStatusSuccess"
/>
</template>