All files / munch-ease-frontend/src/composables usePagination.ts

0% Statements 0/107
0% Branches 0/1
0% Functions 0/1
0% Lines 0/107

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                                                                                                                                                                                                                       
import { ref, shallowRef, type Ref, type ShallowRef } from 'vue'
import type { Page } from '@/domain/pagination'

export type PageParams = { cursor?: string | null; limit?: number } & Record<string, unknown>
export type PageFetcher<T> = (params: PageParams) => Promise<Page<T>>

export function usePagination<T>(
  fetchPage: PageFetcher<T>,
  options?: { pageSize?: number }
): {
  items: Ref<T[]>
  next: Ref<string | null>
  prev: Ref<string | null>
  total: Ref<number | null>
  loading: Ref<boolean>
  error: Ref<unknown>
  load: (baseParams?: PageParams) => Promise<void>
  loadNext: () => Promise<void>
  loadPrev: () => Promise<void>
  reset: () => void
} {
  function makeItemsRef<U>(): ShallowRef<U[]> {
    return shallowRef<U[]>([])
  }
  const items: Ref<T[]> = makeItemsRef<T>()
  const next: Ref<string | null> = ref<string | null>(null)
  const prev: Ref<string | null> = ref<string | null>(null)
  const total: Ref<number | null> = ref<number | null>(null)
  const loading: Ref<boolean> = ref<boolean>(false)
  const error: Ref<unknown> = ref<unknown>(null)

  const pageSize = options?.pageSize
  const lastBaseParams = ref<PageParams>({})

  function assignFromPage(page: Page<T>) {
    items.value = page.items ?? []
    next.value = page.next ?? null
    prev.value = page.prev ?? null
    total.value = page.total ?? null
  }

  async function load(baseParams: PageParams = {}) {
    loading.value = true
    error.value = null
    // keep base params without cursor; limit comes from options unless explicitly provided
    const { limit: _l, ...rest } = baseParams
    lastBaseParams.value = rest
    try {
      const params: PageParams = { ...rest }
      if (typeof _l === 'number') params.limit = _l
      else if (typeof pageSize === 'number') params.limit = pageSize
      const page = await fetchPage(params)
      assignFromPage(page)
    } catch (e) {
      error.value = e
      items.value = []
      next.value = null
      prev.value = null
      total.value = null
    } finally {
      loading.value = false
    }
  }

  async function loadWithCursor(cursor: string | null) {
    if (!cursor) return
    loading.value = true
    error.value = null
    try {
      const params: PageParams = {
        ...lastBaseParams.value,
        cursor,
      }
      if (typeof pageSize === 'number') params.limit = pageSize
      const page = await fetchPage(params)
      assignFromPage(page)
    } catch (e) {
      error.value = e
      items.value = []
      next.value = null
      prev.value = null
      total.value = null
    } finally {
      loading.value = false
    }
  }

  async function loadNext() {
    await loadWithCursor(next.value)
  }

  async function loadPrev() {
    await loadWithCursor(prev.value)
  }

  function reset() {
    items.value = []
    next.value = null
    prev.value = null
    total.value = null
    loading.value = false
    error.value = null
    lastBaseParams.value = {}
  }

  return { items, next, prev, total, loading, error, load, loadNext, loadPrev, reset }
}