Return inserted rows from change operations for incremental grid updates
Instead of re-fetching all forecast data after scale/recode/clone/reference, the routes now return the inserted rows directly. The frontend uses ag-Grid's applyTransaction to add only the new rows, eliminating the full reload round-trip. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
08dc415bfd
commit
cfee3e96b9
@ -84,7 +84,7 @@ ilog AS (
|
||||
WHERE ${q(dateCol)} BETWEEN '{{date_from}}' AND '{{date_to}}'
|
||||
RETURNING *
|
||||
)
|
||||
SELECT count(*) AS rows_affected FROM ins`.trim();
|
||||
SELECT * FROM ins`.trim();
|
||||
}
|
||||
|
||||
function buildScale() {
|
||||
@ -124,7 +124,7 @@ ilog AS (
|
||||
FROM base
|
||||
RETURNING *
|
||||
)
|
||||
SELECT count(*) AS rows_affected FROM ins`.trim();
|
||||
SELECT * FROM ins`.trim();
|
||||
}
|
||||
|
||||
function buildRecode() {
|
||||
@ -146,16 +146,16 @@ ilog AS (
|
||||
SELECT ${dimsJoined}, ${q(dateCol)}, ${effectiveValue ? `-${q(effectiveValue)}` : '0'}, ${effectiveUnits ? `-${q(effectiveUnits)}` : '0'},
|
||||
'recode', (SELECT id FROM ilog), '{{pf_user}}', now()
|
||||
FROM src
|
||||
RETURNING id
|
||||
RETURNING *
|
||||
)
|
||||
,ins AS (
|
||||
INSERT INTO {{fc_table}} (${insertCols})
|
||||
SELECT {{set_clause}}, ${q(dateCol)}, ${effectiveValue ? q(effectiveValue) : '0'}, ${effectiveUnits ? q(effectiveUnits) : '0'},
|
||||
'recode', (SELECT id FROM ilog), '{{pf_user}}', now()
|
||||
FROM src
|
||||
RETURNING id
|
||||
RETURNING *
|
||||
)
|
||||
SELECT (SELECT count(*) FROM neg) + (SELECT count(*) FROM ins) AS rows_affected`.trim();
|
||||
SELECT * FROM neg UNION ALL SELECT * FROM ins`.trim();
|
||||
}
|
||||
|
||||
function buildClone() {
|
||||
@ -179,7 +179,7 @@ ilog AS (
|
||||
{{exclude_clause}}
|
||||
RETURNING *
|
||||
)
|
||||
SELECT count(*) AS rows_affected FROM ins`.trim();
|
||||
SELECT * FROM ins`.trim();
|
||||
}
|
||||
|
||||
function buildUndo() {
|
||||
|
||||
@ -451,6 +451,17 @@ function openForecast() {
|
||||
/* ============================================================
|
||||
FORECAST VIEW — data loading
|
||||
============================================================ */
|
||||
function parseNumericRows(rows) {
|
||||
const numericCols = state.colMeta
|
||||
.filter(c => c.role === 'value' || c.role === 'units')
|
||||
.map(c => c.cname);
|
||||
return rows.map(row => {
|
||||
const r = { ...row };
|
||||
numericCols.forEach(col => { if (r[col] != null) r[col] = parseFloat(r[col]); });
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
async function loadForecastData() {
|
||||
if (!state.version) return;
|
||||
document.getElementById('forecast-label').textContent =
|
||||
@ -462,14 +473,7 @@ async function loadForecastData() {
|
||||
}
|
||||
showStatus('Loading forecast data...', 'info');
|
||||
const rawData = await api('GET', `/versions/${state.version.id}/data`);
|
||||
const numericCols = state.colMeta
|
||||
.filter(c => c.role === 'value' || c.role === 'units')
|
||||
.map(c => c.cname);
|
||||
const data = rawData.map(row => {
|
||||
const r = { ...row };
|
||||
numericCols.forEach(col => { if (r[col] != null) r[col] = parseFloat(r[col]); });
|
||||
return r;
|
||||
});
|
||||
const data = parseNumericRows(rawData);
|
||||
initPivotGrid(data);
|
||||
showStatus(`Loaded ${data.length.toLocaleString()} rows`, 'success');
|
||||
} catch (err) {
|
||||
@ -695,7 +699,7 @@ async function submitScale() {
|
||||
document.getElementById('scale-value-incr').value = '';
|
||||
document.getElementById('scale-units-incr').value = '';
|
||||
document.getElementById('scale-note').value = '';
|
||||
await loadForecastData();
|
||||
state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) });
|
||||
} catch (err) {
|
||||
showStatus(err.message, 'error');
|
||||
}
|
||||
@ -721,7 +725,7 @@ async function submitRecode() {
|
||||
});
|
||||
showStatus(`Recode applied — ${result.rows_affected} rows inserted`, 'success');
|
||||
document.querySelectorAll('#recode-fields input[data-col], #recode-fields select[data-col]').forEach(i => { i.value = ''; });
|
||||
await loadForecastData();
|
||||
state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) });
|
||||
} catch (err) {
|
||||
showStatus(err.message, 'error');
|
||||
}
|
||||
@ -750,7 +754,7 @@ async function submitClone() {
|
||||
showStatus(`Clone applied — ${result.rows_affected} rows inserted`, 'success');
|
||||
document.querySelectorAll('#clone-fields input[data-col], #clone-fields select[data-col]').forEach(i => { i.value = ''; });
|
||||
document.getElementById('clone-scale').value = '1';
|
||||
await loadForecastData();
|
||||
state.grids.pivot.applyTransaction({ add: parseNumericRows(result.rows) });
|
||||
} catch (err) {
|
||||
showStatus(err.message, 'error');
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ module.exports = function(pool) {
|
||||
});
|
||||
|
||||
const result = await runSQL(sql);
|
||||
res.json(result.rows[0]);
|
||||
res.json({ rows_affected: result.rows.length });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(err.status || 500).json({ error: err.message });
|
||||
@ -127,7 +127,7 @@ module.exports = function(pool) {
|
||||
});
|
||||
|
||||
const result = await runSQL(sql);
|
||||
res.json(result.rows[0]);
|
||||
res.json({ rows: result.rows, rows_affected: result.rows.length });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(err.status || 500).json({ error: err.message });
|
||||
@ -183,7 +183,7 @@ module.exports = function(pool) {
|
||||
});
|
||||
|
||||
const result = await runSQL(sql);
|
||||
res.json(result.rows[0]);
|
||||
res.json({ rows: result.rows, rows_affected: result.rows.length });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(err.status || 500).json({ error: err.message });
|
||||
@ -218,7 +218,7 @@ module.exports = function(pool) {
|
||||
});
|
||||
|
||||
const result = await runSQL(sql);
|
||||
res.json(result.rows[0]);
|
||||
res.json({ rows: result.rows, rows_affected: result.rows.length });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(err.status || 500).json({ error: err.message });
|
||||
@ -255,7 +255,7 @@ module.exports = function(pool) {
|
||||
});
|
||||
|
||||
const result = await runSQL(sql);
|
||||
res.json(result.rows[0]);
|
||||
res.json({ rows: result.rows, rows_affected: result.rows.length });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(err.status || 500).json({ error: err.message });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user