import { NotFoundError } from "backend/src/error/not-found-error"; import { Metadata } from "./types/metadata"; type FetchItemsFunction = (fetchItems: FetchItems) => Promise; export class Pagination { private itemsPerPage: number = 0; private totalItems: number = 0; private items: T[] | null = null; // Optional array to hold set items /** * Sets the number of items per page. * @param itemsPerPage - The number of items per page. * @returns the pagination */ setItemsPerPage(itemsPerPage: number): Pagination { this.itemsPerPage = itemsPerPage; return this; } /** * Sets the items to paginate. * @param items - The items to paginate. * @returns the pagination */ setItems(items: T[]): Pagination { this.items = items; this.totalItems = items.length; return this; } /** * Sets the total number of items. * @param totalItems - Total number of items. * @returns the pagination */ setTotalItems(totalItems: number): Pagination { this.totalItems = totalItems; return this; } /** * Gets a page of items, using either static items or a dynamic fetchItems callback. * @param page - The page number to retrieve. * @param fetchItems - The async function to fetch items if setItems was not used. * @returns A promise resolving to the page of items. * @throws throws an error if the page number is invalid. */ async getPage(page: number, fetchItems?: FetchItemsFunction): Promise> { const totalPages = Math.ceil(this.totalItems / this.itemsPerPage); if (page < 1 || page > totalPages) { throw new NotFoundError("Invalid page number"); } // Calculate the range of items to fetch for the current page const start = (page - 1) * this.itemsPerPage; const end = start + this.itemsPerPage; let pageItems: T[]; // Use set items if they are present, otherwise use fetchItems callback if (this.items) { pageItems = this.items.slice(start, end); } else if (fetchItems) { pageItems = await fetchItems(new FetchItems(start, end)); } else { throw new Error("Items function is not set and no fetchItems callback provided"); } return new Page(pageItems, new Metadata(totalPages, this.totalItems, page, this.itemsPerPage)); } } class FetchItems { readonly start: number; readonly end: number; constructor(start: number, end: number) { this.start = start; this.end = end; } } export class Page { readonly items: T[]; readonly metadata: Metadata; constructor(items: T[], metadata: Metadata) { this.items = items; this.metadata = metadata; } /** * Converts the page to a JSON object. */ toJSON() { return { items: this.items, metadata: this.metadata, }; } }