1
0
forked from Yara724/api

Fix SMS, and added pagination+sorting+searching for GETs

This commit is contained in:
SepehrYahyaee
2026-05-20 14:57:10 +03:30
parent e4dfe7c572
commit 44723259d6
18 changed files with 519 additions and 30 deletions

View File

@@ -0,0 +1,116 @@
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 };
}