Dashboard sends q, but users/classes/payments ignored it; accept q/search and normalize Persian digits.
94 lines
2.5 KiB
JavaScript
94 lines
2.5 KiB
JavaScript
// /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
|
||
};
|