Files
kavehhn 7394829d49 fix: honor table search q param on users and related lists
Dashboard sends q, but users/classes/payments ignored it; accept q/search and normalize Persian digits.
2026-08-15 02:57:19 +03:30

94 lines
2.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /utils/pagination.js
const parsePaginationAndSort = (query, defaultSortBy = 'createdAt', defaultSortOrder = 'desc', maxLimit = 100) => {
const page = Math.max(1, parseInt(query.page, 10) || 1);
let limit = parseInt(query.limit, 10) || 20;
if (limit <= 0) limit = 20;
if (limit > maxLimit) limit = maxLimit;
const skip = (page - 1) * limit;
const sortBy = query.sortBy || defaultSortBy;
const sortOrder = (query.sortOrder || defaultSortOrder).toLowerCase() === 'asc' ? 1 : -1;
const sort = {};
sort[sortBy] = sortOrder;
return {
page,
limit,
skip,
sort
};
};
const escapeRegex = (value) => String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const toLatinDigits = (value) => {
if (value == null) return '';
const persian = '۰۱۲۳۴۵۶۷۸۹';
const arabic = '٠١٢٣٤٥٦٧٨٩';
return String(value).replace(/[۰-۹٠-٩]/g, (ch) => {
const p = persian.indexOf(ch);
if (p >= 0) return String(p);
const a = arabic.indexOf(ch);
return a >= 0 ? String(a) : ch;
});
};
const getSearchTerm = (query) => {
const raw = query?.q || query?.search;
if (raw == null || String(raw).trim() === '') return '';
return toLatinDigits(String(raw).trim());
};
const buildFilterQuery = (query, searchFields = [], excludedKeys = ['page', 'limit', 'sortBy', 'sortOrder', 'q', 'search', 'lang']) => {
const filter = {};
// Build field-based exact or boolean filters
Object.keys(query).forEach((key) => {
if (!excludedKeys.includes(key) && query[key] !== undefined && query[key] !== '') {
const val = query[key];
if (val === 'true') {
filter[key] = true;
} else if (val === 'false') {
filter[key] = false;
} else if (!isNaN(val) && String(Number(val)) === val) {
filter[key] = Number(val);
} else {
filter[key] = val;
}
}
});
// Build regex search for ?q= or ?search= across defined text fields
const searchTerm = getSearchTerm(query);
if (searchTerm && searchFields.length > 0) {
const searchRegex = new RegExp(escapeRegex(searchTerm), 'i');
filter.$or = searchFields.map((field) => ({
[field]: searchRegex
}));
}
return filter;
};
const calculateMeta = (totalCount, page, limit) => {
const totalPages = Math.ceil(totalCount / limit) || 0;
return {
totalCount,
totalPages,
currentPage: page,
limit
};
};
module.exports = {
parsePaginationAndSort,
buildFilterQuery,
calculateMeta,
escapeRegex,
toLatinDigits,
getSearchTerm
};