// Shared helpers for building paginated list response state. function normalizePageNumber(value) { const pageNumber = Math.floor(Number(value) || 1); return Math.max(1, pageNumber); } function buildQueryString(query) { const searchParams = new URLSearchParams(); Object.keys(query || {}).forEach(function (key) { const value = query[key]; if (value === undefined || value === null || value === '') { return; } searchParams.set(key, String(value)); }); const queryString = searchParams.toString(); return queryString ? `?${queryString}` : ''; } function createPageEntry(pageNumber, currentPage, pageParam, queryState) { const nextQuery = Object.assign({}, queryState, { [pageParam]: pageNumber }); return { number: pageNumber, active: pageNumber === currentPage, url: buildQueryString(nextQuery) }; } function createEllipsisEntry() { return { ellipsis: true }; } function buildPaginationPages(totalPages, currentPage, pageParam, queryState, maxVisiblePages) { const pages = []; const visiblePageCount = Math.max(1, Math.min(Number(maxVisiblePages) || 7, totalPages)); const firstPage = 1; const lastPage = totalPages; if (totalPages <= visiblePageCount) { for (let pageNumber = firstPage; pageNumber <= lastPage; pageNumber += 1) { pages.push(createPageEntry(pageNumber, currentPage, pageParam, queryState)); } return pages; } const innerPageCount = Math.max(1, visiblePageCount - 2); let startPage = Math.max(2, currentPage - Math.floor(innerPageCount / 2)); let endPage = startPage + innerPageCount - 1; if (endPage > lastPage - 1) { endPage = lastPage - 1; startPage = Math.max(2, endPage - innerPageCount + 1); } pages.push(createPageEntry(firstPage, currentPage, pageParam, queryState)); if (startPage > 2) { pages.push(createEllipsisEntry()); } for (let pageNumber = startPage; pageNumber <= endPage; pageNumber += 1) { pages.push(createPageEntry(pageNumber, currentPage, pageParam, queryState)); } if (endPage < lastPage - 1) { pages.push(createEllipsisEntry()); } pages.push(createPageEntry(lastPage, currentPage, pageParam, queryState)); return pages; } function buildPagination(totalItems, currentPage, pageParam, queryState, pageSize, itemLabel, ariaLabel) { const normalizedPageParam = String(pageParam || 'page').trim() || 'page'; const normalizedPageSize = Math.max(1, Number(pageSize) || 10); const totalPages = Math.max(1, Math.ceil(Number(totalItems) / normalizedPageSize)); const safeCurrentPage = Math.min(normalizePageNumber(currentPage), totalPages); const startIndex = totalItems <= 0 ? 0 : (safeCurrentPage - 1) * normalizedPageSize; const endIndex = totalItems <= 0 ? 0 : Math.min(totalItems, startIndex + normalizedPageSize); const pages = buildPaginationPages(totalPages, safeCurrentPage, normalizedPageParam, queryState, 7); const previousQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage - 1 }); const nextQuery = Object.assign({}, queryState, { [normalizedPageParam]: safeCurrentPage + 1 }); const firstQuery = Object.assign({}, queryState, { [normalizedPageParam]: 1 }); const lastQuery = Object.assign({}, queryState, { [normalizedPageParam]: totalPages }); let normalizedItemLabel = String(itemLabel || 'items'); const total = Number(totalItems) || 0; if (total === 1 && normalizedItemLabel.endsWith('s')) { normalizedItemLabel = normalizedItemLabel.slice(0, -1); } else if (total > 1 && !normalizedItemLabel.endsWith('s')) { normalizedItemLabel += 's'; } return { currentPage: safeCurrentPage, totalPages: totalPages, totalItems: Number(totalItems) || 0, hasMultiplePages: totalPages > 1, startItem: startIndex + 1, endItem: endIndex, hasFirst: safeCurrentPage > 1, hasPrevious: safeCurrentPage > 1, hasNext: safeCurrentPage < totalPages, hasLast: safeCurrentPage < totalPages, firstUrl: buildQueryString(firstQuery), lastUrl: buildQueryString(lastQuery), previousUrl: buildQueryString(previousQuery), nextUrl: buildQueryString(nextQuery), pages: pages, pageSize: normalizedPageSize, pageParam: normalizedPageParam, itemLabel: String(normalizedItemLabel || 'item'), ariaLabel: String(ariaLabel || 'Pagination') }; } module.exports = { normalizePageNumber, buildQueryString, buildPaginationPages, buildPagination };