diff --git a/ui/src/App.jsx b/ui/src/App.jsx
index 50abb9f..bac0aeb 100644
--- a/ui/src/App.jsx
+++ b/ui/src/App.jsx
@@ -33,6 +33,7 @@ export default function App() {
// Sets of names whose dfv view is out of sync with current definitions
const [staleSources, setStaleSources] = useState(new Set())
const [staleStacks, setStaleStacks] = useState(new Set())
+ const [reprocessSources, setReprocessSources] = useState(new Set())
const [generating, setGenerating] = useState({}) // { 'source:name': true }
async function handleLogin(user, pass) {
@@ -56,6 +57,7 @@ export default function App() {
setSources([])
setStaleSources(new Set())
setStaleStacks(new Set())
+ setReprocessSources(new Set())
}
// Load initial stale state from DB once on login
@@ -70,6 +72,17 @@ export default function App() {
function markSourceStale(name) {
setStaleSources(prev => new Set([...prev, name]))
}
+ function markNeedsReprocess(name) {
+ setReprocessSources(prev => new Set([...prev, name]))
+ }
+ async function handleReprocessSource(name) {
+ setGenerating(g => ({ ...g, [`rp:${name}`]: true }))
+ try {
+ await api.reprocess(name)
+ setReprocessSources(prev => { const n = new Set(prev); n.delete(name); return n })
+ } catch (e) { alert(e.message) }
+ finally { setGenerating(g => { const n = { ...g }; delete n[`rp:${name}`]; return n }) }
+ }
function markStackStale(name) {
setStaleStacks(prev => new Set([...prev, name]))
}
@@ -214,6 +227,23 @@ export default function App() {
))}
)}
+ {reprocessSources.size > 0 && (
+
+ Mappings updated:
+ {[...reprocessSources].map(name => (
+
+ {name}
+
+
+ ))}
+
+ )}
@@ -221,7 +251,7 @@ export default function App() {
} />
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/ui/src/pages/Mappings.jsx b/ui/src/pages/Mappings.jsx
index 1cc2d26..cde4670 100644
--- a/ui/src/pages/Mappings.jsx
+++ b/ui/src/pages/Mappings.jsx
@@ -109,7 +109,7 @@ function SortHeader({ col, label, sortBy, onSort, className = '' }) {
)
}
-export default function Mappings({ source, onStale }) {
+export default function Mappings({ source, onNeedsReprocess }) {
const [rules, setRules] = useState([])
const [selectedRule, setSelectedRule] = useState('')
const [allValues, setAllValues] = useState([])
@@ -268,7 +268,7 @@ export default function Mappings({ source, onStale }) {
valueKey(x.extracted_value) === k ? { ...x, is_mapped: true, mapping_id: created.id, output } : x
))
}
- onStale?.(source)
+ onNeedsReprocess?.(source)
setDrafts(d => { const n = { ...d }; delete n[k]; return n })
} catch (err) {
alert(err.message)
@@ -315,7 +315,7 @@ export default function Mappings({ source, onStale }) {
setSaving(s => ({ ...s, [k]: false }))
}
}))
- onStale?.(source)
+ onNeedsReprocess?.(source)
setSelected(new Set())
setBulkDraft({})
}
@@ -324,7 +324,7 @@ export default function Mappings({ source, onStale }) {
if (!row.mapping_id) return
try {
await api.deleteMapping(row.mapping_id)
- onStale?.(source)
+ onNeedsReprocess?.(source)
setAllValues(av => av.map(x =>
valueKey(x.extracted_value) === valueKey(row.extracted_value)
? { ...x, is_mapped: false, mapping_id: null, output: null }