forked from Yara724/api
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import type { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
|
|
|
const MAX_LIMIT = 100;
|
|
const DEFAULT_LIMIT_WHEN_PAGE_SET = 20;
|
|
|
|
export type ListItemGetters<T> = {
|
|
publicId: (row: T) => string | undefined;
|
|
createdAt: (row: T) => Date | string | number | undefined;
|
|
requestNo?: (row: T) => string | undefined;
|
|
status: (row: T) => string | undefined;
|
|
/** Extra strings to match against `search` (e.g. claimRequestId, vehicle). */
|
|
searchExtras?: (row: T) => string[];
|
|
};
|
|
|
|
function normalizeSearch(raw?: string): string | undefined {
|
|
const t = raw?.trim().toLowerCase();
|
|
return t || undefined;
|
|
}
|
|
|
|
function toTime(v: Date | string | number | undefined): number {
|
|
if (v == null) return 0;
|
|
const n = v instanceof Date ? v.getTime() : new Date(v as string | number).getTime();
|
|
return Number.isFinite(n) ? n : 0;
|
|
}
|
|
|
|
/**
|
|
* Filter, sort, and optionally paginate in-memory list results.
|
|
* Pagination applies only when `query.page` or `query.limit` is set.
|
|
*/
|
|
export function applyListQueryV2<T>(
|
|
rows: T[],
|
|
getters: ListItemGetters<T>,
|
|
query: ListQueryV2Dto,
|
|
): {
|
|
list: T[];
|
|
total: number;
|
|
page?: number;
|
|
limit?: number;
|
|
totalPages?: number;
|
|
} {
|
|
const search = normalizeSearch(query.search);
|
|
let filtered = rows;
|
|
if (search) {
|
|
filtered = rows.filter((row) => {
|
|
const chunks: string[] = [];
|
|
const pid = getters.publicId(row);
|
|
if (pid) chunks.push(pid);
|
|
const rno = getters.requestNo?.(row);
|
|
if (rno) chunks.push(rno);
|
|
const st = getters.status(row);
|
|
if (st) chunks.push(st);
|
|
const extras = getters.searchExtras?.(row) ?? [];
|
|
for (const e of extras) {
|
|
if (e) chunks.push(e);
|
|
}
|
|
const hay = chunks.join(" ").toLowerCase();
|
|
return hay.includes(search);
|
|
});
|
|
}
|
|
|
|
const sortBy = query.sortBy ?? "createdAt";
|
|
let sortOrder = query.sortOrder;
|
|
if (!sortOrder) {
|
|
sortOrder =
|
|
sortBy === "publicId" || sortBy === "requestNo" ? "asc" : "desc";
|
|
}
|
|
const dir = sortOrder === "asc" ? 1 : -1;
|
|
|
|
const sorted = [...filtered].sort((a, b) => {
|
|
switch (sortBy) {
|
|
case "publicId": {
|
|
const va = String(getters.publicId(a) ?? "");
|
|
const vb = String(getters.publicId(b) ?? "");
|
|
return dir * va.localeCompare(vb, "en", { numeric: true, sensitivity: "base" });
|
|
}
|
|
case "requestNo": {
|
|
const va = String(
|
|
getters.requestNo?.(a) ?? getters.publicId(a) ?? "",
|
|
);
|
|
const vb = String(
|
|
getters.requestNo?.(b) ?? getters.publicId(b) ?? "",
|
|
);
|
|
return dir * va.localeCompare(vb, "en", { numeric: true, sensitivity: "base" });
|
|
}
|
|
case "status": {
|
|
const va = String(getters.status(a) ?? "");
|
|
const vb = String(getters.status(b) ?? "");
|
|
return dir * va.localeCompare(vb, "en", { sensitivity: "base" });
|
|
}
|
|
case "createdAt":
|
|
default: {
|
|
const ta = toTime(getters.createdAt(a));
|
|
const tb = toTime(getters.createdAt(b));
|
|
return dir * (ta - tb);
|
|
}
|
|
}
|
|
});
|
|
|
|
const total = sorted.length;
|
|
const wantsPage = query.page != null;
|
|
const wantsLimit = query.limit != null;
|
|
if (!wantsPage && !wantsLimit) {
|
|
return { list: sorted, total };
|
|
}
|
|
|
|
const limit = Math.min(
|
|
MAX_LIMIT,
|
|
Math.max(1, query.limit ?? (wantsPage ? DEFAULT_LIMIT_WHEN_PAGE_SET : MAX_LIMIT)),
|
|
);
|
|
const page = Math.max(1, query.page ?? 1);
|
|
const skip = (page - 1) * limit;
|
|
const list = sorted.slice(skip, skip + limit);
|
|
const totalPages = Math.max(1, Math.ceil(total / limit));
|
|
|
|
return { list, total, page, limit, totalPages };
|
|
}
|