Files
edr-platform/apps/edr-freight-api/src/common/utils/pagination.util.ts
Marshal 4b7f6d2548 enhance contract and booking services with server-side search and validation improvements
- Added  parameter to  and  for server-side free-text search on contract reference, company name, and booking details.
- Introduced new validation errors in  for container clashes and space issues when creating bookings.
- Implemented paginated dropdown settings retrieval in .
- Updated  to fetch active yards using a new method that handles pagination.
- Enhanced  with a  method to fetch all records by walking through pages.
- Refactored  to support filtering and pagination in schedule listings.
- Improved  to return a paginated list of facilities.
- Updated UI components in  and  to utilize debounced search inputs for better performance.
- Added alerts in  to inform users about booking constraints related to splits and capacity.
- Enhanced  to display notifications for split bookings and capacity usage.
2026-07-12 10:51:31 +00:00

86 lines
2.5 KiB
TypeScript

import { PaginatedResponse, PaginationMeta } from '@edr/types';
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
/** Raw page/pageSize as they arrive from a query DTO (both optional). */
export interface PageRequest {
page?: number;
pageSize?: number;
}
export interface PaginationOptions {
defaultPageSize?: number;
maxPageSize?: number;
}
export interface NormalizedPage {
page: number;
pageSize: number;
skip: number;
take: number;
}
const DEFAULT_PAGE_SIZE = 20;
const MAX_PAGE_SIZE = 100;
/** Clamp raw query values into a safe page window (page ≥ 1, pageSize capped). */
export function normalizePagination(
request: PageRequest,
options: PaginationOptions = {},
): NormalizedPage {
const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
const maxPageSize = options.maxPageSize ?? MAX_PAGE_SIZE;
const page = Math.max(1, Math.floor(request.page ?? 1) || 1);
const requested = Math.floor(request.pageSize ?? defaultPageSize) || defaultPageSize;
const pageSize = Math.min(Math.max(1, requested), maxPageSize);
return { page, pageSize, skip: (page - 1) * pageSize, take: pageSize };
}
export function buildPaginationMeta(
total: number,
page: number,
pageSize: number,
): PaginationMeta {
const totalPages = pageSize > 0 ? Math.ceil(total / pageSize) : 0;
return {
page,
pageSize,
total,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
};
}
/**
* Apply skip/take to a query builder, run it, and wrap the result in the
* shared `PaginatedResponse` envelope. Ordering and filtering must already be
* applied by the caller.
*/
export async function paginateQuery<T extends ObjectLiteral>(
qb: SelectQueryBuilder<T>,
request: PageRequest,
options?: PaginationOptions,
): Promise<PaginatedResponse<T>> {
const { page, pageSize, skip, take } = normalizePagination(request, options);
const [items, total] = await qb.skip(skip).take(take).getManyAndCount();
return { items, meta: buildPaginationMeta(total, page, pageSize) };
}
/**
* Paginate an already-materialized array. Prefer `paginateQuery` (DB-level
* LIMIT/OFFSET); use this only for lists that are inherently in-memory.
*/
export function paginateArray<T>(
rows: readonly T[],
request: PageRequest,
options?: PaginationOptions,
): PaginatedResponse<T> {
const { page, pageSize, skip } = normalizePagination(request, options);
return {
items: rows.slice(skip, skip + pageSize),
meta: buildPaginationMeta(rows.length, page, pageSize),
};
}