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 }