- All balance changes (top-ups, sets, bulk ops, request approvals) are now logged to data/history.json - New "History" tab in admin sidebar - Search by email to see all changes for a user with totals - Partial name search shows matching users with summary stats - Each entry shows action type, amount, source, OMG job #, resulting balance, and timestamp Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
70 lines
1.9 KiB
JavaScript
70 lines
1.9 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const DATA_FILE = path.join(__dirname, '..', 'data', 'history.json');
|
|
|
|
function ensureDataDir() {
|
|
const dir = path.dirname(DATA_FILE);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
if (!fs.existsSync(DATA_FILE)) {
|
|
fs.writeFileSync(DATA_FILE, JSON.stringify([], null, 2));
|
|
}
|
|
}
|
|
|
|
function readAll() {
|
|
ensureDataDir();
|
|
const raw = fs.readFileSync(DATA_FILE, 'utf8');
|
|
return JSON.parse(raw);
|
|
}
|
|
|
|
function writeAll(data) {
|
|
ensureDataDir();
|
|
fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2));
|
|
}
|
|
|
|
function log(entry) {
|
|
const history = readAll();
|
|
history.push({
|
|
id: Date.now().toString(36) + Math.random().toString(36).slice(2, 7),
|
|
email: entry.email,
|
|
action: entry.action,
|
|
amount: entry.amount,
|
|
source: entry.source,
|
|
omgJobNumber: entry.omgJobNumber || null,
|
|
balanceAfter: entry.balanceAfter || null,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
writeAll(history);
|
|
}
|
|
|
|
function getByEmail(email) {
|
|
return readAll()
|
|
.filter(h => h.email.toLowerCase() === email.toLowerCase())
|
|
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
}
|
|
|
|
function searchByEmail(query) {
|
|
const regex = new RegExp(query, 'i');
|
|
const all = readAll();
|
|
const emails = [...new Set(all.filter(h => regex.test(h.email)).map(h => h.email))];
|
|
return emails.map(email => {
|
|
const entries = all.filter(h => h.email === email);
|
|
const totalAdded = entries
|
|
.filter(h => h.action === 'add' || h.action === 'approve')
|
|
.reduce((sum, h) => sum + (h.amount || 0), 0);
|
|
return {
|
|
email,
|
|
totalRequests: entries.length,
|
|
totalAdded,
|
|
lastActivity: entries.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))[0]?.timestamp,
|
|
};
|
|
});
|
|
}
|
|
|
|
function getAll() {
|
|
return readAll().sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
}
|
|
|
|
module.exports = { log, getByEmail, searchByEmail, getAll };
|