Files
My-KOPKB/fe/src/modules/user/composables/useUserList.ts
T

160 lines
4.2 KiB
TypeScript

import { computed, onMounted, ref, watch } from 'vue'
import debounce from 'lodash/debounce'
import type { SortConfig } from '@/components/ui/usage/DataTable.vue'
import { useApiPagination } from '@/composables/useApiPagination'
import { getApiErrorMessage } from '@/core/utils/getApiErrorMessage'
import { getUserStats, listUsers } from '../services/user.service'
import type { UserListItem } from '../types/user.types'
export function useUserList() {
const users = ref<UserListItem[]>([])
const loading = ref(false)
const statsLoading = ref(false)
const error = ref<string | null>(null)
const statsError = ref<string | null>(null)
const search = ref('')
const statusFilter = ref('')
const joinDateFrom = ref('')
const joinDateTo = ref('')
const leaveDateFrom = ref('')
const leaveDateTo = ref('')
const sortBy = ref<SortConfig[]>([{ key: 'name', order: 'asc' }])
const page = ref(1)
const itemsPerPage = ref(10)
const stats = ref<{ total: number; joined_this_month: number }>({ total: 0, joined_this_month: 0 })
const { pagination, applyPagination } = useApiPagination({ per_page: 10 })
const hasJoinDateFilters = computed(() => Boolean(joinDateFrom.value || joinDateTo.value))
const hasLeaveDateFilters = computed(() => Boolean(leaveDateFrom.value || leaveDateTo.value))
function clearJoinDateFilters() {
joinDateFrom.value = ''
joinDateTo.value = ''
}
function clearLeaveDateFilters() {
leaveDateFrom.value = ''
leaveDateTo.value = ''
}
async function fetchUsers(requestPage = page.value) {
loading.value = true
error.value = null
try {
const activeSort = sortBy.value[0]
const data = await listUsers({
page: requestPage,
per_page: itemsPerPage.value,
sort_by: activeSort?.key ?? 'name',
sort_order: activeSort?.order ?? 'asc',
search: search.value.trim() || undefined,
status: statusFilter.value.trim() || undefined,
join_date_from: joinDateFrom.value || undefined,
join_date_to: joinDateTo.value || undefined,
leave_date_from: leaveDateFrom.value || undefined,
leave_date_to: leaveDateTo.value || undefined,
})
users.value = data.data
applyPagination(data.pagination)
page.value = data.pagination.current_page
} catch (err) {
error.value = getApiErrorMessage(err, 'Gagal memuatkan senarai pengguna.')
} finally {
loading.value = false
}
}
async function fetchStats() {
statsLoading.value = true
statsError.value = null
try {
const res = await getUserStats({
search: search.value.trim() || undefined,
status: statusFilter.value.trim() || undefined,
join_date_from: joinDateFrom.value || undefined,
join_date_to: joinDateTo.value || undefined,
leave_date_from: leaveDateFrom.value || undefined,
leave_date_to: leaveDateTo.value || undefined,
})
stats.value = res.data
} catch (err) {
statsError.value = getApiErrorMessage(err, 'Gagal memuatkan statistik pengguna.')
stats.value = { total: 0, joined_this_month: 0 }
} finally {
statsLoading.value = false
}
}
function handleSortUpdate(value: SortConfig[]) {
sortBy.value = value
fetchUsers(1)
}
const debouncedSearch = debounce(() => {
fetchUsers(1)
fetchStats()
}, 400)
watch(search, () => {
debouncedSearch()
})
watch(statusFilter, () => {
fetchUsers(1)
fetchStats()
})
watch([joinDateFrom, joinDateTo, leaveDateFrom, leaveDateTo], () => {
fetchUsers(1)
fetchStats()
})
watch(page, (nextPage, previousPage) => {
if (nextPage !== previousPage) {
fetchUsers(nextPage)
}
})
watch(itemsPerPage, (nextValue, previousValue) => {
if (nextValue !== previousValue) {
fetchUsers(1)
}
})
onMounted(() => {
fetchUsers(1)
fetchStats()
})
return {
users,
loading,
stats,
statsLoading,
statsError,
error,
search,
statusFilter,
joinDateFrom,
joinDateTo,
leaveDateFrom,
leaveDateTo,
hasJoinDateFilters,
hasLeaveDateFilters,
clearJoinDateFilters,
clearLeaveDateFilters,
sortBy,
page,
itemsPerPage,
pagination,
handleSortUpdate,
fetchUsers,
fetchStats,
}
}