ad: transactions province filter
This commit is contained in:
72
index.js
72
index.js
@@ -1084,6 +1084,7 @@ app.get("/all-payments/data", requireAllPaymentsAuth, async (req, res) => {
|
|||||||
let dateTo = (req.query.dateTo || "").trim();
|
let dateTo = (req.query.dateTo || "").trim();
|
||||||
if (dateFrom && !dateTo) dateTo = dateFrom;
|
if (dateFrom && !dateTo) dateTo = dateFrom;
|
||||||
const search = (req.query.search || "").trim();
|
const search = (req.query.search || "").trim();
|
||||||
|
const province = (req.query.province || "").trim();
|
||||||
|
|
||||||
if (dateFrom || dateTo) {
|
if (dateFrom || dateTo) {
|
||||||
console.log("all-payments/data date filter:", { 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) {
|
if (search) {
|
||||||
filter.$or = [
|
filter.$or = [
|
||||||
{ amountRaw: new RegExp(escapeRegex(search), "i") },
|
{ 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") },
|
{ provincecode: new RegExp(escapeRegex(search), "i") },
|
||||||
{ resNum: 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))) {
|
if (!isNaN(parseInt(search, 10))) {
|
||||||
filter.$or.push({ amount: 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; }
|
* { 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; }
|
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; }
|
.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; }
|
.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; }
|
.filters { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; flex: 1; }
|
||||||
.filter-group { display: flex; align-items: center; gap: 8px; }
|
.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"]: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); }
|
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 { 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:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
.btn-primary { background: var(--primary); color: #fff; }
|
.btn-primary { background: var(--primary); color: #fff; }
|
||||||
@@ -1295,7 +1329,13 @@ app.get("/all-payments", async (req, res) => {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<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="toolbar">
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
@@ -1309,12 +1349,19 @@ app.get("/all-payments", async (req, res) => {
|
|||||||
<input type="hidden" id="dateToGregorian" />
|
<input type="hidden" id="dateToGregorian" />
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-group">
|
<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>
|
</div>
|
||||||
<button type="button" class="btn btn-ghost" id="btn-apply">اعمال فیلتر</button>
|
<button type="button" class="btn btn-ghost" id="btn-apply">اعمال فیلتر</button>
|
||||||
</div>
|
</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>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="table-wrap">
|
<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 src="https://cdn.jsdelivr.net/npm/persian-datepicker@1.2.0/dist/js/persian-datepicker.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(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 contentEl = document.getElementById('table-content');
|
||||||
var paginationEl = document.getElementById('pagination');
|
var paginationEl = document.getElementById('pagination');
|
||||||
|
|
||||||
@@ -1357,14 +1404,18 @@ app.get("/all-payments", async (req, res) => {
|
|||||||
var q = 'page=' + state.page + '&limit=' + state.limit;
|
var q = 'page=' + state.page + '&limit=' + state.limit;
|
||||||
if (state.dateFrom) q += '&dateFrom=' + encodeURIComponent(state.dateFrom);
|
if (state.dateFrom) q += '&dateFrom=' + encodeURIComponent(state.dateFrom);
|
||||||
if (state.dateTo) q += '&dateTo=' + encodeURIComponent(state.dateTo);
|
if (state.dateTo) q += '&dateTo=' + encodeURIComponent(state.dateTo);
|
||||||
|
if (state.province) q += '&province=' + encodeURIComponent(state.province);
|
||||||
if (state.search) q += '&search=' + encodeURIComponent(state.search);
|
if (state.search) q += '&search=' + encodeURIComponent(state.search);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRow(item) {
|
function renderRow(item, index) {
|
||||||
var createdAt = item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-';
|
var createdAt = item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-';
|
||||||
var id = item._id;
|
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) {
|
function bindRowEvents(fragment) {
|
||||||
@@ -1443,8 +1494,8 @@ app.get("/all-payments", async (req, res) => {
|
|||||||
paginationEl.style.display = 'none';
|
paginationEl.style.display = 'none';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var thead = '<table><thead><tr><th>مبلغ</th><th>استان</th><th>لینک</th><th>موبایل</th><th>تاریخ</th><th>عملیات</th></tr></thead><tbody>';
|
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(renderRow).join('');
|
var rows = list.map(function(item, i) { return renderRow(item, i); }).join('');
|
||||||
contentEl.innerHTML = thead + rows + '</tbody></table>';
|
contentEl.innerHTML = thead + rows + '</tbody></table>';
|
||||||
bindRowEvents(contentEl);
|
bindRowEvents(contentEl);
|
||||||
renderPagination(data);
|
renderPagination(data);
|
||||||
@@ -1503,6 +1554,7 @@ app.get("/all-payments", async (req, res) => {
|
|||||||
state.dateFrom = (document.getElementById('dateFromGregorian').value || '').trim();
|
state.dateFrom = (document.getElementById('dateFromGregorian').value || '').trim();
|
||||||
state.dateTo = (document.getElementById('dateToGregorian').value || '').trim();
|
state.dateTo = (document.getElementById('dateToGregorian').value || '').trim();
|
||||||
if (state.dateFrom && !state.dateTo) state.dateTo = state.dateFrom;
|
if (state.dateFrom && !state.dateTo) state.dateTo = state.dateFrom;
|
||||||
|
state.province = (document.getElementById('province').value || '').trim();
|
||||||
state.search = document.getElementById('search').value.trim();
|
state.search = document.getElementById('search').value.trim();
|
||||||
state.page = 1;
|
state.page = 1;
|
||||||
load();
|
load();
|
||||||
|
|||||||
Reference in New Issue
Block a user