index.ts
1 year ago
useAmplitudeEvent.ts
3 months ago
useHostingData.ts
3 months ago
useModal.ts
1 year ago
useOverviewData.ts
9 months ago
useReachUrls.ts
1 month ago
useScrollLock.ts
11 months ago
useServerPagination.ts
10 months ago
useToast.ts
1 year ago
useServerPagination.ts
149 lines
| 1 | import { computed, ref } from 'vue'; |
| 2 | |
| 3 | import type { PaginatedResponse } from '@/data/repositories/pagesRepo'; |
| 4 | |
| 5 | export interface ServerPaginationOptions { |
| 6 | itemsPerPage?: number; |
| 7 | initialPage?: number; |
| 8 | autoLoad?: boolean; |
| 9 | } |
| 10 | |
| 11 | export interface ServerPaginationState { |
| 12 | currentPage: number; |
| 13 | totalPages: number; |
| 14 | totalItems: number; |
| 15 | itemsPerPage: number; |
| 16 | isLoading: boolean; |
| 17 | hasError: boolean; |
| 18 | error: unknown; |
| 19 | } |
| 20 | |
| 21 | export const useServerPagination = <T>( |
| 22 | fetchFunction: (page: number, perPage: number) => Promise<[PaginatedResponse<T[]> | null, unknown]>, |
| 23 | options: ServerPaginationOptions = {} |
| 24 | ) => { |
| 25 | const { itemsPerPage = 5, initialPage = 1, autoLoad = true } = options; |
| 26 | |
| 27 | const currentPage = ref(initialPage); |
| 28 | const totalPages = ref(0); |
| 29 | const totalItems = ref(0); |
| 30 | const items = ref<T[]>([]); |
| 31 | const isLoading = ref(false); |
| 32 | const hasError = ref(false); |
| 33 | const error = ref<unknown>(null); |
| 34 | |
| 35 | const hasPrevious = computed(() => currentPage.value > 1); |
| 36 | const hasNext = computed(() => currentPage.value < totalPages.value); |
| 37 | const isEmpty = computed(() => !isLoading.value && items.value.length === 0); |
| 38 | const startIndex = computed(() => (currentPage.value - 1) * itemsPerPage + 1); |
| 39 | const endIndex = computed(() => Math.min(currentPage.value * itemsPerPage, totalItems.value)); |
| 40 | |
| 41 | const pageRange = computed(() => ({ |
| 42 | from: startIndex.value, |
| 43 | to: endIndex.value, |
| 44 | total: totalItems.value |
| 45 | })); |
| 46 | |
| 47 | const loadData = async (page: number = currentPage.value) => { |
| 48 | isLoading.value = true; |
| 49 | hasError.value = false; |
| 50 | error.value = null; |
| 51 | |
| 52 | try { |
| 53 | const [result, err] = await fetchFunction(page, itemsPerPage); |
| 54 | |
| 55 | if (err) { |
| 56 | hasError.value = true; |
| 57 | error.value = err; |
| 58 | items.value = []; |
| 59 | |
| 60 | return false; |
| 61 | } |
| 62 | |
| 63 | if (!result) { |
| 64 | return false; |
| 65 | } |
| 66 | |
| 67 | items.value = result.data; |
| 68 | currentPage.value = result.pagination.currentPage; |
| 69 | totalPages.value = result.pagination.totalPages; |
| 70 | totalItems.value = result.pagination.totalItems; |
| 71 | |
| 72 | return true; |
| 73 | } catch (err) { |
| 74 | hasError.value = true; |
| 75 | error.value = err; |
| 76 | items.value = []; |
| 77 | |
| 78 | return false; |
| 79 | } finally { |
| 80 | isLoading.value = false; |
| 81 | } |
| 82 | }; |
| 83 | |
| 84 | const goToPage = async (page: number) => { |
| 85 | if (page >= 1 && page <= totalPages.value && page !== currentPage.value && !isLoading.value) { |
| 86 | await loadData(page); |
| 87 | } |
| 88 | }; |
| 89 | |
| 90 | const goToFirstPage = async () => { |
| 91 | await goToPage(1); |
| 92 | }; |
| 93 | |
| 94 | const goToLastPage = async () => { |
| 95 | await goToPage(totalPages.value); |
| 96 | }; |
| 97 | |
| 98 | const goToPreviousPage = async () => { |
| 99 | if (hasPrevious.value) { |
| 100 | await goToPage(currentPage.value - 1); |
| 101 | } |
| 102 | }; |
| 103 | |
| 104 | const goToNextPage = async () => { |
| 105 | if (hasNext.value) { |
| 106 | await goToPage(currentPage.value + 1); |
| 107 | } |
| 108 | }; |
| 109 | |
| 110 | const setPage = (page: number) => goToPage(page); |
| 111 | const nextPage = () => goToNextPage(); |
| 112 | const prevPage = () => goToPreviousPage(); |
| 113 | |
| 114 | const refresh = async () => { |
| 115 | await loadData(currentPage.value); |
| 116 | }; |
| 117 | |
| 118 | if (autoLoad) { |
| 119 | loadData(initialPage); |
| 120 | } |
| 121 | |
| 122 | return { |
| 123 | currentPage, |
| 124 | totalPages, |
| 125 | totalItems, |
| 126 | itemsPerPage, |
| 127 | items, |
| 128 | isLoading, |
| 129 | hasError, |
| 130 | error, |
| 131 | hasPrevious, |
| 132 | hasNext, |
| 133 | isEmpty, |
| 134 | startIndex, |
| 135 | endIndex, |
| 136 | pageRange, |
| 137 | loadData, |
| 138 | goToPage, |
| 139 | setPage, |
| 140 | nextPage, |
| 141 | prevPage, |
| 142 | goToFirstPage, |
| 143 | goToLastPage, |
| 144 | goToPreviousPage, |
| 145 | goToNextPage, |
| 146 | refresh |
| 147 | }; |
| 148 | }; |
| 149 |