fix : add filter dokter,kategori antrean
This commit is contained in:
No files matched your search
+102
-7
@@ -10,6 +10,9 @@ import { Icon } from '@iconify/vue';
|
||||
import type { AntreanOperasi } from '~/types/antrean';
|
||||
import { STATUS, statusOptions } from '~/types/antrean';
|
||||
import { usePendaftaranStore } from '~/store/pendaftaran';
|
||||
import type { Dokter } from '~/types/pendaftaran';
|
||||
import type { KategoriOperasi } from '~/types/antrean';
|
||||
import api from '~/services/api';
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
@@ -17,15 +20,31 @@ definePageMeta({
|
||||
});
|
||||
|
||||
const pendaftaranStore = usePendaftaranStore();
|
||||
const search = ref('');
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const getSearchFromRoute = () => {
|
||||
const q = route.query.search;
|
||||
if (Array.isArray(q)) return q[0] ?? '';
|
||||
return typeof q === 'string' ? q : '';
|
||||
};
|
||||
|
||||
const search = ref(getSearchFromRoute());
|
||||
type SelectOption = { label: string; value: any };
|
||||
const filterValues = ref<{
|
||||
statusFilter: string | null;
|
||||
sortOption: string | null;
|
||||
DokterFilter: SelectOption | null;
|
||||
KategoriFilter: SelectOption | null;
|
||||
}>({
|
||||
statusFilter: STATUS.BELUM,
|
||||
sortOption: null
|
||||
sortOption: null,
|
||||
DokterFilter: null,
|
||||
KategoriFilter: null
|
||||
});
|
||||
const antreanList = ref<any[]>([]);
|
||||
const kategoriOptions = ref<SelectOption[]>([]);
|
||||
const dokterOptions = ref<SelectOption[]>([]);
|
||||
const loading = ref(false);
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPage = ref(10);
|
||||
@@ -37,8 +56,6 @@ const effectiveItemsPerPage = computed(() => {
|
||||
return itemsPerPage.value;
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// Modal state
|
||||
const showModal = ref(false);
|
||||
const showEditModal = ref(false);
|
||||
@@ -49,7 +66,7 @@ const selectedIdForEdit = ref<string | number | null>(null);
|
||||
const selectedIdForStatus = ref<string | number | null>(null);
|
||||
|
||||
// Define filter options
|
||||
const filterOptions = [
|
||||
const filterOptions = computed(() => [
|
||||
{
|
||||
type: 'btn-group' as const,
|
||||
label: 'Filter Status',
|
||||
@@ -58,6 +75,20 @@ const filterOptions = [
|
||||
options: statusOptions,
|
||||
defaultValue: STATUS.BELUM
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Dokter',
|
||||
modelKey: 'DokterFilter',
|
||||
options: dokterOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Kategori',
|
||||
modelKey: 'KategoriFilter',
|
||||
options: kategoriOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'select' as const,
|
||||
label: 'Urutkan',
|
||||
@@ -69,7 +100,7 @@ const filterOptions = [
|
||||
],
|
||||
defaultValue: null
|
||||
}
|
||||
];
|
||||
]);
|
||||
|
||||
|
||||
// Fetch data from API
|
||||
@@ -93,7 +124,9 @@ const fetchData = async (append: boolean = false) => {
|
||||
status: filterValues.value.statusFilter || undefined,
|
||||
search: search.value || undefined,
|
||||
sort_by: sortBy,
|
||||
sort_order: sortOrder
|
||||
sort_order: sortOrder,
|
||||
kategori_id: filterValues.value.KategoriFilter?.value || undefined,
|
||||
dokter_id: filterValues.value.DokterFilter?.value || undefined
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
@@ -116,6 +149,35 @@ const fetchData = async (append: boolean = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchKategoriOptions = async () => {
|
||||
try {
|
||||
const response = await api.get(`/reference/kategori`);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
kategoriOptions.value = response.data.data.map((kategori: KategoriOperasi) => ({
|
||||
label: kategori.kategori.split('-')[1]?.trim() || kategori.kategori,
|
||||
value: kategori.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching kategori options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDokterOptions = async (searchQuery: string = '') => {
|
||||
try {
|
||||
const response = await api.get(`/reference/dokter?limit=50&offset=0&search=${searchQuery}`);
|
||||
if (response.data.success && response.data.data) {
|
||||
dokterOptions.value = response.data.data.map((dokter: Dokter) => ({
|
||||
label: dokter.nama_lengkap,
|
||||
value: dokter.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching dokter options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load more for infinite scroll
|
||||
const handleLoadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
@@ -153,6 +215,15 @@ watch(search, () => {
|
||||
}, 500); // 500ms debounce
|
||||
});
|
||||
|
||||
// Keep input in sync if query param changes (e.g. back/forward navigation)
|
||||
watch(
|
||||
() => route.query.search,
|
||||
() => {
|
||||
const next = getSearchFromRoute();
|
||||
if (next !== search.value) search.value = next;
|
||||
}
|
||||
);
|
||||
|
||||
// Watch for sort changes
|
||||
watch(() => filterValues.value.sortOption, () => {
|
||||
currentPage.value = 1;
|
||||
@@ -161,9 +232,27 @@ watch(() => filterValues.value.sortOption, () => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Kategori filter changes
|
||||
watch(() => filterValues.value.KategoriFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Dokter filter changes
|
||||
watch(() => filterValues.value.DokterFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Initial fetch
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
fetchKategoriOptions();
|
||||
fetchDokterOptions();
|
||||
});
|
||||
|
||||
const showSnackbar = (message: string, color: string = 'success') => {
|
||||
@@ -255,6 +344,11 @@ const handleEditModalSuccess = () => {
|
||||
const handleUpdateStatusSuccess = () => {
|
||||
fetchData(); // Refresh the table after status update
|
||||
};
|
||||
const handleFilterSearch = ({ modelKey, query }: { modelKey: string, query: string }) => {
|
||||
if (modelKey === 'DokterFilter') {
|
||||
fetchDokterOptions(query);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<v-row>
|
||||
@@ -275,6 +369,7 @@ const handleUpdateStatusSuccess = () => {
|
||||
<FilterSortMenu
|
||||
v-model="filterValues"
|
||||
:filters="filterOptions"
|
||||
@search="handleFilterSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,20 +9,25 @@ import ModalPendaftaran from '@/components/pendaftaran/ModalPendaftaranV2.vue';
|
||||
import ModalDetailPendaftaran from '@/components/pendaftaran/ModalDetailPendaftaran.vue';
|
||||
import ModalUpdateStatus from '@/components/pendaftaran/ModalUpdateStatus.vue';
|
||||
import { usePendaftaranStore } from '~/store/pendaftaran';
|
||||
import type { Dokter } from '~/types/pendaftaran';
|
||||
import api from '~/services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const idKategori = route.params.id as string;
|
||||
const categoryName = route.query.category as string || 'Kategori';
|
||||
const pendaftaranStore = usePendaftaranStore();
|
||||
type SelectOption = { label: string; value: any };
|
||||
|
||||
// State
|
||||
const search = ref('');
|
||||
const antreanList = ref<any[]>([]);
|
||||
const filterValues = ref<{
|
||||
statusFilter: string | null;
|
||||
dokterFilter: SelectOption | null;
|
||||
sortOption: string | null;
|
||||
}>({
|
||||
statusFilter: STATUS.BELUM,
|
||||
dokterFilter: null,
|
||||
sortOption: null
|
||||
});
|
||||
const loading = ref(false);
|
||||
@@ -40,13 +45,15 @@ 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 dokterOptions = ref<SelectOption[]>([]);
|
||||
|
||||
|
||||
const showSnackbar = (message: string, color: string = 'success') => {
|
||||
pendaftaranStore.showSnackbar(message, color);
|
||||
};
|
||||
|
||||
// Define filter options
|
||||
const filterOptions = [
|
||||
const filterOptions = computed(() => [
|
||||
{
|
||||
type: 'btn-group' as const,
|
||||
label: 'Filter Status',
|
||||
@@ -55,6 +62,13 @@ const filterOptions = [
|
||||
options: statusOptions,
|
||||
defaultValue: STATUS.BELUM
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Dokter',
|
||||
modelKey: 'dokterFilter',
|
||||
options: dokterOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'select' as const,
|
||||
label: 'Urutkan',
|
||||
@@ -66,7 +80,7 @@ const filterOptions = [
|
||||
],
|
||||
defaultValue: null
|
||||
}
|
||||
];
|
||||
]);
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
@@ -101,7 +115,8 @@ const fetchData = async (append: boolean = false) => {
|
||||
search: search.value || undefined,
|
||||
type_id: idKategori,
|
||||
sort_by: sortBy,
|
||||
sort_order: sortOrder
|
||||
sort_order: sortOrder,
|
||||
dokter_id: filterValues.value.dokterFilter?.value || undefined
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
@@ -124,6 +139,20 @@ const fetchData = async (append: boolean = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDokterOptions = async (searchQuery: string = '') => {
|
||||
try {
|
||||
const response = await api.get(`/reference/dokter?limit=50&offset=0&search=${searchQuery}`);
|
||||
if (response.data.success && response.data.data) {
|
||||
dokterOptions.value = response.data.data.map((dokter: Dokter) => ({
|
||||
label: dokter.nama_lengkap,
|
||||
value: dokter.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching dokter options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load more for infinite scroll
|
||||
const handleLoadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
@@ -140,6 +169,14 @@ watch(() => filterValues.value.statusFilter, () => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Dokter filter changes
|
||||
watch(() => filterValues.value.dokterFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for search with debounce
|
||||
let searchTimeout: NodeJS.Timeout | null = null;
|
||||
watch(search, () => {
|
||||
@@ -165,6 +202,7 @@ watch(() => filterValues.value.sortOption, () => {
|
||||
// Initial fetch
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
fetchDokterOptions();
|
||||
});
|
||||
|
||||
// Function to get actions based on item status
|
||||
@@ -184,6 +222,12 @@ const getActionsForItem = (item: AntreanOperasi) => {
|
||||
return allActions;
|
||||
};
|
||||
|
||||
const handleFilterSearch = ({ modelKey, query }: { modelKey: string, query: string }) => {
|
||||
if (modelKey === 'dokterFilter') {
|
||||
fetchDokterOptions(query);
|
||||
}
|
||||
};
|
||||
|
||||
const handleView = async (item: unknown) => {
|
||||
const data = item as AntreanOperasi;
|
||||
selectedId.value = data.id;
|
||||
@@ -258,7 +302,7 @@ const handleUpdateStatusSuccess = () => {
|
||||
<v-text-field v-model="search" placeholder="Cari..." variant="outlined" density="compact" class="bg-white"
|
||||
hide-details prepend-inner-icon="mdi-magnify" style="max-width: 300px;"></v-text-field>
|
||||
|
||||
<FilterSortMenu v-model="filterValues" :filters="filterOptions" />
|
||||
<FilterSortMenu v-model="filterValues" :filters="filterOptions" @search="handleFilterSearch" />
|
||||
</div>
|
||||
|
||||
<v-btn color="primary" size="large" elevation="2" @click="showModal = true">
|
||||
|
||||
@@ -10,11 +10,15 @@ import ModalPendaftaran from '@/components/pendaftaran/ModalPendaftaranV2.vue';
|
||||
import ModalDetailPendaftaran from '@/components/pendaftaran/ModalDetailPendaftaran.vue';
|
||||
import ModalUpdateStatus from '@/components/pendaftaran/ModalUpdateStatus.vue';
|
||||
import { usePendaftaranStore } from '~/store/pendaftaran';
|
||||
import type { Dokter } from '~/types/pendaftaran';
|
||||
import type { KategoriOperasi } from '~/types/antrean';
|
||||
import api from '~/services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const kodeSpesialis = route.params.kode as string;
|
||||
const spesialisName = route.query.spesialis as string || 'Spesialis';
|
||||
const pendaftaranStore = usePendaftaranStore();
|
||||
type SelectOption = { label: string; value: any };
|
||||
|
||||
// State
|
||||
const search = ref('');
|
||||
@@ -22,15 +26,21 @@ const antreanList = ref<any[]>([]);
|
||||
const filterValues = ref<{
|
||||
statusFilter: string | null;
|
||||
sortOption: string | null;
|
||||
DokterFilter: SelectOption | null;
|
||||
KategoriFilter: SelectOption | null;
|
||||
}>({
|
||||
statusFilter: STATUS.BELUM,
|
||||
sortOption: null
|
||||
sortOption: null,
|
||||
DokterFilter: null,
|
||||
KategoriFilter: null
|
||||
});
|
||||
const loading = ref(false);
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPage = ref(12); // Set to 12 for card view
|
||||
const totalItems = ref(0);
|
||||
const hasMore = ref(true);
|
||||
const kategoriOptions = ref<SelectOption[]>([]);
|
||||
const dokterOptions = ref<SelectOption[]>([]);
|
||||
|
||||
// Modal state
|
||||
const showModal = ref(false);
|
||||
@@ -46,7 +56,7 @@ const showSnackbar = (message: string, color: string = 'success') => {
|
||||
};
|
||||
|
||||
// Define filter options
|
||||
const filterOptions = [
|
||||
const filterOptions = computed(() => [
|
||||
{
|
||||
type: 'btn-group' as const,
|
||||
label: 'Filter Status',
|
||||
@@ -55,6 +65,20 @@ const filterOptions = [
|
||||
options: statusOptions,
|
||||
defaultValue: STATUS.BELUM
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Dokter',
|
||||
modelKey: 'DokterFilter',
|
||||
options: dokterOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Kategori',
|
||||
modelKey: 'KategoriFilter',
|
||||
options: kategoriOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'select' as const,
|
||||
label: 'Urutkan',
|
||||
@@ -66,7 +90,7 @@ const filterOptions = [
|
||||
],
|
||||
defaultValue: null
|
||||
}
|
||||
];
|
||||
]);
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
@@ -101,7 +125,9 @@ const fetchData = async (append: boolean = false) => {
|
||||
search: search.value || undefined,
|
||||
type_id: kodeSpesialis,
|
||||
sort_by: sortBy,
|
||||
sort_order: sortOrder
|
||||
sort_order: sortOrder,
|
||||
kategori_id: filterValues.value.KategoriFilter?.value || undefined,
|
||||
dokter_id: filterValues.value.DokterFilter?.value || undefined
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
@@ -124,6 +150,34 @@ const fetchData = async (append: boolean = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchKategoriOptions = async () => {
|
||||
try {
|
||||
const response = await api.get(`/reference/kategori`);
|
||||
if (response.data.success && response.data.data) {
|
||||
kategoriOptions.value = response.data.data.map((kategori: KategoriOperasi) => ({
|
||||
label: kategori.kategori.split('-')[1]?.trim() || kategori.kategori,
|
||||
value: kategori.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching kategori options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDokterOptions = async (searchQuery: string = '') => {
|
||||
try {
|
||||
const response = await api.get(`/reference/dokter?limit=50&offset=0&search=${searchQuery}`);
|
||||
if (response.data.success && response.data.data) {
|
||||
dokterOptions.value = response.data.data.map((dokter: Dokter) => ({
|
||||
label: dokter.nama_lengkap,
|
||||
value: dokter.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching dokter options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load more for infinite scroll
|
||||
const handleLoadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
@@ -162,9 +216,27 @@ watch(() => filterValues.value.sortOption, () => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Kategori filter changes
|
||||
watch(() => filterValues.value.KategoriFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Dokter filter changes
|
||||
watch(() => filterValues.value.DokterFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Initial fetch
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
fetchKategoriOptions();
|
||||
fetchDokterOptions();
|
||||
});
|
||||
|
||||
|
||||
@@ -185,6 +257,12 @@ const getActionsForItem = (item: AntreanOperasi) => {
|
||||
return allActions;
|
||||
};
|
||||
|
||||
const handleFilterSearch = ({ modelKey, query }: { modelKey: string, query: string }) => {
|
||||
if (modelKey === 'DokterFilter') {
|
||||
fetchDokterOptions(query);
|
||||
}
|
||||
};
|
||||
|
||||
const handleView = async (item: unknown) => {
|
||||
const data = item as AntreanOperasi;
|
||||
selectedId.value = data.id;
|
||||
@@ -263,6 +341,7 @@ const handleUpdateStatusSuccess = () => {
|
||||
<FilterSortMenu
|
||||
v-model="filterValues"
|
||||
:filters="filterOptions"
|
||||
@search="handleFilterSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@ import ModalPendaftaran from '@/components/pendaftaran/ModalPendaftaranV2.vue';
|
||||
import ModalDetailPendaftaran from '@/components/pendaftaran/ModalDetailPendaftaran.vue';
|
||||
import ModalUpdateStatus from '@/components/pendaftaran/ModalUpdateStatus.vue';
|
||||
import { usePendaftaranStore } from '~/store/pendaftaran';
|
||||
import type { Dokter } from '~/types/pendaftaran';
|
||||
import type { KategoriOperasi } from '~/types/antrean';
|
||||
import api from '~/services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const kodeSubSpesialis = route.params.kode as string;
|
||||
const subSpesialisName = route.query.subspecialis as string || 'Sub Spesialis';
|
||||
const spesialisId = route.query.spesialis_id as string || null;
|
||||
const pendaftaranStore = usePendaftaranStore();
|
||||
type SelectOption = { label: string; value: any };
|
||||
|
||||
// State
|
||||
const search = ref('');
|
||||
@@ -22,15 +26,21 @@ const antreanList = ref<any[]>([]);
|
||||
const filterValues = ref<{
|
||||
statusFilter: string | null;
|
||||
sortOption: string | null;
|
||||
DokterFilter: SelectOption | null;
|
||||
KategoriFilter: SelectOption | null;
|
||||
}>({
|
||||
statusFilter: STATUS.BELUM,
|
||||
sortOption: null
|
||||
sortOption: null,
|
||||
DokterFilter: null,
|
||||
KategoriFilter: null
|
||||
});
|
||||
const loading = ref(false);
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPage = ref(12); // Set to 12 for card view
|
||||
const totalItems = ref(0);
|
||||
const hasMore = ref(true);
|
||||
const kategoriOptions = ref<SelectOption[]>([]);
|
||||
const dokterOptions = ref<SelectOption[]>([]);
|
||||
|
||||
// Modal state
|
||||
const showModal = ref(false);
|
||||
@@ -46,7 +56,7 @@ const showSnackbar = (message: string, color: string = 'success') => {
|
||||
};
|
||||
|
||||
// Define filter options
|
||||
const filterOptions = [
|
||||
const filterOptions = computed(() => [
|
||||
{
|
||||
type: 'btn-group' as const,
|
||||
label: 'Filter Status',
|
||||
@@ -55,6 +65,20 @@ const filterOptions = [
|
||||
options: statusOptions,
|
||||
defaultValue: STATUS.BELUM
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Dokter',
|
||||
modelKey: 'DokterFilter',
|
||||
options: dokterOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'autocomplete' as const,
|
||||
label: 'Filter Kategori',
|
||||
modelKey: 'KategoriFilter',
|
||||
options: kategoriOptions.value,
|
||||
defaultValue: null
|
||||
},
|
||||
{
|
||||
type: 'select' as const,
|
||||
label: 'Urutkan',
|
||||
@@ -66,7 +90,7 @@ const filterOptions = [
|
||||
],
|
||||
defaultValue: null
|
||||
}
|
||||
];
|
||||
]);
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
@@ -101,7 +125,9 @@ const fetchData = async (append: boolean = false) => {
|
||||
search: search.value || undefined,
|
||||
type_id: kodeSubSpesialis,
|
||||
sort_by: sortBy,
|
||||
sort_order: sortOrder
|
||||
sort_order: sortOrder,
|
||||
kategori_id: filterValues.value.KategoriFilter?.value || undefined,
|
||||
dokter_id: filterValues.value.DokterFilter?.value || undefined
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
@@ -124,6 +150,35 @@ const fetchData = async (append: boolean = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchKategoriOptions = async () => {
|
||||
try {
|
||||
const response = await api.get(`/reference/kategori`);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
kategoriOptions.value = response.data.data.map((kategori: KategoriOperasi) => ({
|
||||
label: kategori.kategori.split('-')[1]?.trim() || kategori.kategori,
|
||||
value: kategori.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching kategori options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchDokterOptions = async (searchQuery: string = '') => {
|
||||
try {
|
||||
const response = await api.get(`/reference/dokter?limit=50&offset=0&search=${searchQuery}`);
|
||||
if (response.data.success && response.data.data) {
|
||||
dokterOptions.value = response.data.data.map((dokter: Dokter) => ({
|
||||
label: dokter.nama_lengkap,
|
||||
value: dokter.id
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching dokter options:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Load more for infinite scroll
|
||||
const handleLoadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
@@ -162,9 +217,27 @@ watch(() => filterValues.value.sortOption, () => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Kategori filter changes
|
||||
watch(() => filterValues.value.KategoriFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Watch for Dokter filter changes
|
||||
watch(() => filterValues.value.DokterFilter, () => {
|
||||
currentPage.value = 1;
|
||||
antreanList.value = [];
|
||||
hasMore.value = true;
|
||||
fetchData();
|
||||
});
|
||||
|
||||
// Initial fetch
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
fetchKategoriOptions();
|
||||
fetchDokterOptions();
|
||||
});
|
||||
|
||||
// Function to get actions based on item status
|
||||
@@ -184,6 +257,12 @@ const getActionsForItem = (item: AntreanOperasi) => {
|
||||
return allActions;
|
||||
};
|
||||
|
||||
const handleFilterSearch = ({ modelKey, query }: { modelKey: string, query: string }) => {
|
||||
if (modelKey === 'DokterFilter') {
|
||||
fetchDokterOptions(query);
|
||||
}
|
||||
};
|
||||
|
||||
const handleView = async (item: unknown) => {
|
||||
const data = item as AntreanOperasi;
|
||||
selectedId.value = data.id;
|
||||
@@ -263,6 +342,7 @@ const handleUpdateStatusSuccess = () => {
|
||||
<FilterSortMenu
|
||||
v-model="filterValues"
|
||||
:filters="filterOptions"
|
||||
@search="handleFilterSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
+48
-19
@@ -42,6 +42,7 @@ const hasDashboardAccess = computed(() => {
|
||||
// Filter bulan
|
||||
const selectedMonth = ref(new Date().getMonth());
|
||||
const selectedYear = ref(new Date().getFullYear());
|
||||
const useAllPeriod = ref(false);
|
||||
|
||||
const months = [
|
||||
{ value: 0, text: 'Januari' },
|
||||
@@ -62,6 +63,21 @@ const years = ref([
|
||||
yearNow -3, yearNow - 2, yearNow -1,yearNow
|
||||
]);
|
||||
|
||||
const dashboardParams = computed(() => {
|
||||
if (useAllPeriod.value) return {};
|
||||
return {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
};
|
||||
});
|
||||
|
||||
const periodKey = computed(() => (useAllPeriod.value ? 'all' : `${selectedYear.value}-${selectedMonth.value}`));
|
||||
|
||||
const periodLabel = computed(() => {
|
||||
if (useAllPeriod.value) return 'Semua Periode';
|
||||
return `${months[selectedMonth.value].text} ${selectedYear.value}`;
|
||||
});
|
||||
|
||||
// Modal state
|
||||
const showModal = ref(false);
|
||||
|
||||
@@ -83,8 +99,7 @@ const fetchStatusAntrian = async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/perbandingan-status-antrian', {
|
||||
params: {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
...dashboardParams.value
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,8 +120,7 @@ const fetchKategoriAntrian = async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/perbandingan-kategori-antrian', {
|
||||
params: {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
...dashboardParams.value
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,8 +141,7 @@ const fetchAntrianPerHari = async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/antrian-per-hari', {
|
||||
params: {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
...dashboardParams.value
|
||||
}
|
||||
});
|
||||
|
||||
@@ -149,8 +162,7 @@ const fetchAntrianPerSpesialis = async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/table-antrian-per-spesialis', {
|
||||
params: {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
...dashboardParams.value
|
||||
}
|
||||
});
|
||||
|
||||
@@ -179,8 +191,7 @@ const fetchAntrianPerSubspesialis = async () => {
|
||||
try {
|
||||
const response = await api.get('/dashboard/table-antrian-per-subspesialis', {
|
||||
params: {
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value + 1 // API expects 1-12, not 0-11
|
||||
...dashboardParams.value
|
||||
}
|
||||
});
|
||||
|
||||
@@ -322,13 +333,12 @@ const pieKategoriChartSeries = computed(() => {
|
||||
|
||||
// Data untuk Column Chart - Antrian Per Hari dalam 1 Bulan
|
||||
const lineChartOptions = computed(() => {
|
||||
const monthName = months[selectedMonth.value].text.substring(0, 3);
|
||||
|
||||
// Get categories from API data or generate default
|
||||
const categories = antrianPerHariData.value.length > 0
|
||||
? antrianPerHariData.value.map(item => {
|
||||
const date = new Date(item.TanggalDaftar);
|
||||
return `${date.getDate()} ${monthName}`;
|
||||
const shortMonth = date.toLocaleDateString('id-ID', { month: 'short' });
|
||||
return `${date.getDate()} ${shortMonth}`;
|
||||
})
|
||||
: [];
|
||||
|
||||
@@ -597,6 +607,17 @@ const subspesialisChartSeries = computed(() => [
|
||||
|
||||
// Watch untuk refetch data saat bulan atau tahun berubah
|
||||
watch([selectedMonth, selectedYear], () => {
|
||||
if (hasDashboardAccess.value) {
|
||||
if (useAllPeriod.value) return;
|
||||
fetchStatusAntrian();
|
||||
fetchKategoriAntrian();
|
||||
fetchAntrianPerHari();
|
||||
fetchAntrianPerSpesialis();
|
||||
fetchAntrianPerSubspesialis();
|
||||
}
|
||||
});
|
||||
|
||||
watch(useAllPeriod, () => {
|
||||
if (hasDashboardAccess.value) {
|
||||
fetchStatusAntrian();
|
||||
fetchKategoriAntrian();
|
||||
@@ -710,6 +731,7 @@ definePageMeta({
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:disabled="useAllPeriod"
|
||||
style="min-width: 140px;"
|
||||
prepend-inner-icon="mdi-calendar-month"
|
||||
></v-select>
|
||||
@@ -719,8 +741,15 @@ definePageMeta({
|
||||
density="compact"
|
||||
variant="outlined"
|
||||
hide-details
|
||||
:disabled="useAllPeriod"
|
||||
style="min-width: 100px;"
|
||||
></v-select>
|
||||
<v-checkbox
|
||||
v-model="useAllPeriod"
|
||||
label="Semua data"
|
||||
density="compact"
|
||||
hide-details
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<v-btn
|
||||
@@ -850,7 +879,7 @@ definePageMeta({
|
||||
</div>
|
||||
<ClientOnly v-else>
|
||||
<VueApexCharts
|
||||
:key="`pie-status-${selectedMonth}-${selectedYear}`"
|
||||
:key="`pie-status-${periodKey}`"
|
||||
type="donut"
|
||||
height="350"
|
||||
:options="pieChartOptions"
|
||||
@@ -879,7 +908,7 @@ definePageMeta({
|
||||
</div>
|
||||
<ClientOnly v-else>
|
||||
<VueApexCharts
|
||||
:key="`pie-kategori-${selectedMonth}-${selectedYear}`"
|
||||
:key="`pie-kategori-${periodKey}`"
|
||||
type="donut"
|
||||
height="350"
|
||||
:options="pieKategoriChartOptions"
|
||||
@@ -895,7 +924,7 @@ definePageMeta({
|
||||
<v-card elevation="10">
|
||||
<v-card-item>
|
||||
<div class="d-flex align-center justify-space-between mb-4">
|
||||
<h5 class="text-h5 font-weight-bold">Antrean Per Hari ({{ months[selectedMonth].text }} {{ selectedYear }})</h5>
|
||||
<h5 class="text-h5 font-weight-bold">Antrean Per Hari ({{ periodLabel }})</h5>
|
||||
<v-avatar size="40" class="rounded-md bg-lightsecondary">
|
||||
<Icon icon="solar:chart-outline" class="text-secondary" height="22" />
|
||||
</v-avatar>
|
||||
@@ -908,7 +937,7 @@ definePageMeta({
|
||||
</div>
|
||||
<ClientOnly v-else>
|
||||
<VueApexCharts
|
||||
:key="`column-${selectedMonth}-${selectedYear}`"
|
||||
:key="`column-${periodKey}`"
|
||||
type="bar"
|
||||
height="350"
|
||||
:options="lineChartOptions"
|
||||
@@ -943,7 +972,7 @@ definePageMeta({
|
||||
</div>
|
||||
<ClientOnly v-else>
|
||||
<VueApexCharts
|
||||
:key="`spesialis-${selectedMonth}-${selectedYear}`"
|
||||
:key="`spesialis-${periodKey}`"
|
||||
type="bar"
|
||||
height="400"
|
||||
:options="spesialisChartOptions"
|
||||
@@ -978,7 +1007,7 @@ definePageMeta({
|
||||
</div>
|
||||
<ClientOnly v-else>
|
||||
<VueApexCharts
|
||||
:key="`subspesialis-${selectedMonth}-${selectedYear}`"
|
||||
:key="`subspesialis-${periodKey}`"
|
||||
type="bar"
|
||||
height="400"
|
||||
:options="subspesialisChartOptions"
|
||||
|
||||
Reference in New Issue
Block a user