ad: transactions province filter

This commit is contained in:
2026-02-01 10:47:38 +03:30
parent d1c0112b43
commit 6f7d04829d

View File

@@ -1084,6 +1084,7 @@ app.get("/all-payments/data", requireAllPaymentsAuth, async (req, res) => {
let dateTo = (req.query.dateTo || "").trim();
if (dateFrom && !dateTo) dateTo = dateFrom;
const search = (req.query.search || "").trim();
const province = (req.query.province || "").trim();
if (dateFrom || dateTo) {
console.log("all-payments/data date filter:", { dateFrom, dateTo });
@@ -1106,6 +1107,25 @@ app.get("/all-payments/data", requireAllPaymentsAuth, async (req, res) => {
}
}
if (province) {
const code = String(province).trim();
const prefixRegex = new RegExp("^" + escapeRegex(code));
filter.$and = filter.$and || [];
filter.$and.push({
$or: [
{ provincecode: prefixRegex },
{
$expr: {
$regexMatch: {
input: { $toString: "$provincecode" },
regex: "^" + escapeRegex(code),
},
},
},
],
});
}
if (search) {
filter.$or = [
{ amountRaw: new RegExp(escapeRegex(search), "i") },
@@ -1113,6 +1133,13 @@ app.get("/all-payments/data", requireAllPaymentsAuth, async (req, res) => {
{ provincecode: new RegExp(escapeRegex(search), "i") },
{ resNum: new RegExp(escapeRegex(search), "i") },
];
// Province name filter: match search text against province names (e.g. همدان, تست)
Object.keys(PROVINCE_NAMES).forEach((code) => {
if (PROVINCE_NAMES[code].indexOf(search) !== -1) {
filter.$or.push({ provincecode: code });
filter.$or.push({ provincecode: parseInt(code, 10) });
}
});
if (!isNaN(parseInt(search, 10))) {
filter.$or.push({ amount: parseInt(search, 10) });
}
@@ -1250,7 +1277,9 @@ app.get("/all-payments", async (req, res) => {
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Inter', -apple-system, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; padding: 24px; line-height: 1.5; }
.page { max-width: 1200px; margin: 0 auto; }
h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 24px; letter-spacing: -0.02em; }
.page-header { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 24px; }
.page-header h1 { font-size: 1.75rem; font-weight: 700; margin: 0; letter-spacing: -0.02em; }
.header-actions { display: flex; gap: 10px; align-items: center; }
.toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 20px; }
.filters { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; flex: 1; }
.filter-group { display: flex; align-items: center; gap: 8px; }
@@ -1261,6 +1290,11 @@ app.get("/all-payments", async (req, res) => {
}
input[type="text"]:focus, input[type="date"]:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px rgba(99,102,241,0.2); }
input[type="text"]::placeholder { color: var(--text-muted); }
select {
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
color: var(--text); padding: 10px 14px; font-size: 0.875rem; min-width: 140px; cursor: pointer;
}
select:focus { outline: none; border-color: var(--primary); }
.btn { border: none; border-radius: var(--radius-sm); padding: 10px 18px; font-size: 0.875rem; font-weight: 500; cursor: pointer; transition: background 0.15s, opacity 0.15s; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--primary); color: #fff; }
@@ -1295,7 +1329,13 @@ app.get("/all-payments", async (req, res) => {
</head>
<body>
<div class="page">
<h1>همه پرداخت‌ها</h1>
<div class="page-header">
<h1>همه پرداخت‌ها</h1>
<div class="header-actions">
<button type="button" class="btn btn-danger" id="btn-remove-all">حذف همه</button>
<button type="button" class="btn btn-ghost" id="btn-logout">خروج</button>
</div>
</div>
<div class="toolbar">
<div class="filters">
<div class="filter-group">
@@ -1309,12 +1349,19 @@ app.get("/all-payments", async (req, res) => {
<input type="hidden" id="dateToGregorian" />
</div>
<div class="filter-group">
<input type="text" id="search" placeholder="جستجو (مبلغ، موبایل، استان...) " />
<label>استان</label>
<select id="province">
<option value="">همه استان‌ها</option>
${Object.keys(PROVINCE_NAMES)
.map((c) => `<option value="${c}">${PROVINCE_NAMES[c]}</option>`)
.join("")}
</select>
</div>
<div class="filter-group">
<input type="text" id="search" placeholder="جستجو" />
</div>
<button type="button" class="btn btn-ghost" id="btn-apply">اعمال فیلتر</button>
</div>
<button type="button" class="btn btn-danger" id="btn-remove-all">حذف همه</button>
<button type="button" class="btn btn-ghost" id="btn-logout">خروج</button>
</div>
<div class="card">
<div class="table-wrap">
@@ -1328,7 +1375,7 @@ app.get("/all-payments", async (req, res) => {
<script src="https://cdn.jsdelivr.net/npm/persian-datepicker@1.2.0/dist/js/persian-datepicker.min.js"></script>
<script>
(function() {
var state = { page: 1, limit: 20, dateFrom: '', dateTo: '', search: '' };
var state = { page: 1, limit: 20, dateFrom: '', dateTo: '', search: '', province: '' };
var contentEl = document.getElementById('table-content');
var paginationEl = document.getElementById('pagination');
@@ -1357,14 +1404,18 @@ app.get("/all-payments", async (req, res) => {
var q = 'page=' + state.page + '&limit=' + state.limit;
if (state.dateFrom) q += '&dateFrom=' + encodeURIComponent(state.dateFrom);
if (state.dateTo) q += '&dateTo=' + encodeURIComponent(state.dateTo);
if (state.province) q += '&province=' + encodeURIComponent(state.province);
if (state.search) q += '&search=' + encodeURIComponent(state.search);
return q;
}
function renderRow(item) {
function renderRow(item, index) {
var createdAt = item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-';
var id = item._id;
return '<tr data-id="' + id + '"><td>' + (item.amountRaw || item.amount) + '</td><td>' + (item.provinceName || '-') + '</td><td>' + (item.isLink ? 'بله' : 'خیر') + '</td><td>' + (item.phone || '-') + '</td><td>' + createdAt + '</td><td><div class="actions-cell"><button type="button" class="btn btn-primary btn-sm btn-send" data-id="' + id + '">ارسال به سرور</button><button type="button" class="btn btn-danger btn-sm btn-remove" data-id="' + id + '">حذف</button><div class="cell-msg" id="msg-' + id + '"></div></div></td></tr>';
var rowNum = (state.page - 1) * state.limit + (index + 1);
var amountVal = item.amountRaw != null ? item.amountRaw : item.amount;
var amountStr = amountVal != null ? Number(amountVal).toLocaleString('fa-IR') : '-';
return '<tr data-id="' + id + '"><td>' + rowNum + '</td><td>' + amountStr + '</td><td>' + (item.provinceName || '-') + '</td><td>' + (item.isLink ? 'بله' : 'خیر') + '</td><td>' + (item.phone || '-') + '</td><td>' + createdAt + '</td><td><div class="actions-cell"><button type="button" class="btn btn-primary btn-sm btn-send" data-id="' + id + '">ارسال به سرور</button><button type="button" class="btn btn-danger btn-sm btn-remove" data-id="' + id + '">حذف</button><div class="cell-msg" id="msg-' + id + '"></div></div></td></tr>';
}
function bindRowEvents(fragment) {
@@ -1443,8 +1494,8 @@ app.get("/all-payments", async (req, res) => {
paginationEl.style.display = 'none';
return;
}
var thead = '<table><thead><tr><th>مبلغ</th><th>استان</th><th>لینک</th><th>موبایل</th><th>تاریخ</th><th>عملیات</th></tr></thead><tbody>';
var rows = list.map(renderRow).join('');
var thead = '<table><thead><tr><th>ردیف</th><th>مبلغ</th><th>استان</th><th>لینک</th><th>موبایل</th><th>تاریخ</th><th>عملیات</th></tr></thead><tbody>';
var rows = list.map(function(item, i) { return renderRow(item, i); }).join('');
contentEl.innerHTML = thead + rows + '</tbody></table>';
bindRowEvents(contentEl);
renderPagination(data);
@@ -1503,6 +1554,7 @@ app.get("/all-payments", async (req, res) => {
state.dateFrom = (document.getElementById('dateFromGregorian').value || '').trim();
state.dateTo = (document.getElementById('dateToGregorian').value || '').trim();
if (state.dateFrom && !state.dateTo) state.dateTo = state.dateFrom;
state.province = (document.getElementById('province').value || '').trim();
state.search = document.getElementById('search').value.trim();
state.page = 1;
load();