diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx index 45cbb7a..ee42df7 100644 --- a/ui/src/views/Forecast.jsx +++ b/ui/src/views/Forecast.jsx @@ -1622,7 +1622,13 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio window.dispatchEvent(new CustomEvent('pf-data-changed')) refreshLogMeta(versionId) if (opTag.trim()) refreshTags(versionId) - if (op === 'scale') { setScaleInputs({}); setScaleNote(''); fetchCurrentTotals(slices) } + // Every operation changes what the selection holds, so every operation + // has to re-count it. This used to fire for scale alone, and a recode or + // clone left the ledger showing the totals from before it ran -- the + // effect that normally recounts is keyed on `slices`, and none of these + // change the selection. + fetchCurrentTotals(slices) + if (op === 'scale') { setScaleInputs({}); setScaleNote('') } if (op === 'recode') { setRecodeNote('') } if (op === 'clone') { setCloneNote(''); setCloneScale('1') } } catch (err) { @@ -1821,6 +1827,15 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio if (removed?.length && tableRef.current) { await tableRef.current.remove(removed) } + // Recount the selection, now the rows are actually gone. Undone after the + // remove, not beside it: fetchCurrentTotals reads the table through its + // own views, so running it first would count what was just deleted. + // + // The walk lines come from the log, so that is re-read too -- an undone + // entry has to leave the ledger as well as the table. + window.dispatchEvent(new CustomEvent('pf-data-changed')) + refreshLogMeta(versionId) + if (slicesRef.current.length) fetchCurrentTotals(slicesRef.current) flash(`Undone — ${data.rows_deleted} rows removed`) } catch (err) { flash(err.message, 'error')