+ submitOp(activeOp)} busy={!!opBusy}>
+ {opBusy ? OP_BUSY_LABEL[opBusy] : OP_LABEL[activeOp]}
+
+ {opBusy && (
+
+ Writing rows — the pivot updates when it finishes.
+
+ )}
diff --git a/ui/src/views/Forecast.jsx b/ui/src/views/Forecast.jsx
index 8f9fa72..ce2416a 100644
--- a/ui/src/views/Forecast.jsx
+++ b/ui/src/views/Forecast.jsx
@@ -81,6 +81,8 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
// the same filter, for display: the panel has to show the scope it is acting
// inside or the slice it prints is not the slice that gets written
const [viewScope, setViewScope] = useState([])
+ // which operation is in flight, so the panel can say so and refuse a second click
+ const [opBusy, setOpBusy] = useState(null)
const [msg, setMsg] = useState(null)
// layouts
@@ -1462,6 +1464,11 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
}
}
+ // A large scale writes a row per coordinate and can run for a while, and
+ // until now the button stayed live and said nothing -- so the only feedback
+ // that anything was happening was the absence of feedback, and clicking
+ // twice would apply it twice.
+ setOpBusy(op)
try {
const res = await fetch(`/api/versions/${versionId}/${op}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body)
@@ -1483,7 +1490,11 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
if (op === 'scale') { setScaleInputs({}); setScaleNote(''); fetchCurrentTotals(slices) }
if (op === 'recode') { setRecodeNote('') }
if (op === 'clone') { setCloneNote(''); setCloneScale('1') }
- } catch (err) { flash(err.message, 'error') }
+ } catch (err) {
+ flash(err.message, 'error')
+ } finally {
+ setOpBusy(null)
+ }
}
async function lookupDerivedCols(col, value, setter) {
@@ -1728,6 +1739,7 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
dock,
slices, setSlices,
viewScope,
+ opBusy,
applyMode, setApplyMode,
currentTotals,
activeOp, setActiveOp,