Commit 8f6e76b9 by tdgiang

Add admin transaction history page and wire up all admin routes

Adds listTransactions controller action and its Swig view, and creates
app/routes/admin.server.routes.js to finally wire every admin controller
function (Tasks 7-9) to real HTTP routes via the app's glob-based route
loader. basicAuth protects /admin/transactions and /admin/transactions/new;
the pay page, epay return, and epay IPN endpoints stay public.
Co-Authored-By: 's avatarClaude Sonnet 5 <noreply@anthropic.com>
parent 8dcdfabb
...@@ -159,3 +159,34 @@ exports.epayIPN = function (req, res) { ...@@ -159,3 +159,34 @@ exports.epayIPN = function (req, res) {
); );
}); });
}; };
exports.listTransactions = function (req, res) {
var page = parseInt(req.query.page) || 1;
var perPage = 20;
AdminTransaction.find({})
.sort({ createdAt: -1 })
.skip((page - 1) * perPage)
.limit(perPage)
.exec(function (err, list) {
if (err) {
return res.status(500).send("Lỗi cơ sở dữ liệu");
}
var transactions = list.map(function (tx) {
return {
merTrxId: tx.merTrxId,
customerName: tx.customerName,
customerPhone: tx.customerPhone,
amount: tx.amount,
status: tx.status,
resultMsg: tx.resultMsg,
createdAt: moment(tx.createdAt).format("DD/MM/YYYY HH:mm"),
paymentUrl: config.epay.req_domain + "/admin/pay/" + tx.merTrxId,
};
});
res.render("admin/transactions-list", {
transactions: transactions,
page: page,
});
});
};
"use strict";
module.exports = function (app) {
var admin = require("../../app/controllers/admin.server.controller");
var basicAuth = require("../middlewares/basicAuth");
app.route("/admin/transactions")
.get(basicAuth, admin.listTransactions)
.post(basicAuth, admin.createTransaction);
app.route("/admin/transactions/new").get(basicAuth, admin.newTransactionForm);
app.route("/admin/pay/:merTrxId").get(admin.payPage);
app.route("/admin/epay/return").get(admin.epayReturn);
app.route("/admin/epay/ipn").post(admin.epayIPN);
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Lịch sử giao dịch</title>
<style>
body { font-family: Arial, sans-serif; max-width: 900px; margin: 40px auto; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; font-size: 14px; }
th { background: #f0f0f0; }
.status-pending { color: #b8860b; }
.status-success { color: #276b45; }
.status-failed { color: #a3352a; }
</style>
</head>
<body>
<h2>Lịch sử giao dịch</h2>
<p><a href="/admin/transactions/new">+ Thêm mới giao dịch</a></p>
<table>
<thead>
<tr>
<th>Thời gian</th>
<th>Khách hàng</th>
<th>SĐT</th>
<th>Số tiền</th>
<th>Trạng thái</th>
<th>Link thanh toán</th>
</tr>
</thead>
<tbody>
{% for tx in transactions %}
<tr>
<td>{{tx.createdAt}}</td>
<td>{{tx.customerName}}</td>
<td>{{tx.customerPhone}}</td>
<td>{{tx.amount}}</td>
<td class="status-{{tx.status}}">{{tx.status}}</td>
<td><a href="{{tx.paymentUrl}}" target="_blank">Link</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment