- Sources page: left column with stacked DB tables + registered sources panels, right column as full-height column mapping workbench - Add compact table search, column search, table preview button, delete source button - Rename fc_table system columns to pf_ prefix (pf_id, pf_iter, pf_logid, pf_created_at) to avoid collisions with source table columns like 'id' - Remove 'filter' col_meta role — any non-ignore column usable in baseline filters - Replace structured filter row builder with free-form SQL WHERE clause textarea and clickable column chips for insertion; fully flexible AND/OR logic - Baseline segment cards now display raw WHERE clause text + offset Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
const express = require('express');
|
|
const { fcTable } = require('../lib/utils');
|
|
|
|
module.exports = function(pool) {
|
|
const router = express.Router();
|
|
|
|
// list all log entries for a version, newest first
|
|
router.get('/versions/:id/log', async (req, res) => {
|
|
try {
|
|
const result = await pool.query(
|
|
`SELECT * FROM pf.log WHERE version_id = $1 ORDER BY stamp DESC`,
|
|
[req.params.id]
|
|
);
|
|
res.json(result.rows);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// undo an operation — deletes all forecast rows with this logid, then the log entry
|
|
// two separate queries in a transaction to avoid FK ordering issues
|
|
router.delete('/log/:logid', async (req, res) => {
|
|
const logid = parseInt(req.params.logid);
|
|
const client = await pool.connect();
|
|
try {
|
|
// look up the log entry to find the version and fc_table name
|
|
const logResult = await client.query(`
|
|
SELECT l.*, v.id AS version_id, s.tname
|
|
FROM pf.log l
|
|
JOIN pf.version v ON v.id = l.version_id
|
|
JOIN pf.source s ON s.id = v.source_id
|
|
WHERE l.id = $1
|
|
`, [logid]);
|
|
|
|
if (logResult.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Log entry not found' });
|
|
}
|
|
|
|
const { tname, version_id } = logResult.rows[0];
|
|
const table = fcTable(tname, version_id);
|
|
|
|
await client.query('BEGIN');
|
|
// delete forecast rows first (logid has no FK constraint — managed by app)
|
|
const del = await client.query(
|
|
`DELETE FROM ${table} WHERE pf_logid = $1 RETURNING pf_id`,
|
|
[logid]
|
|
);
|
|
await client.query(`DELETE FROM pf.log WHERE id = $1`, [logid]);
|
|
await client.query('COMMIT');
|
|
|
|
res.json({ message: 'Operation undone', rows_deleted: del.rowCount });
|
|
} catch (err) {
|
|
await client.query('ROLLBACK');
|
|
console.error(err);
|
|
res.status(500).json({ error: err.message });
|
|
} finally {
|
|
client.release();
|
|
}
|
|
});
|
|
|
|
return router;
|
|
};
|