/* Deep pipeline flow explorer */
const FLOW_TABS = ['Uitleg', 'Code', 'Data', 'Artefacten', 'Runtime'];
function nodeById(catalog, id) {
return (catalog.nodes || []).find(n => n.id === id) || null;
}
function nodeVisibleInMode(node, flowMode) {
const modes = node.modes || ['pand_scan'];
return modes.includes(flowMode);
}
function groupNodes(catalog, groupId, flowMode) {
return (catalog.nodes || []).filter(n => n.group === groupId && nodeVisibleInMode(n, flowMode));
}
function FlowNode({ node, selected, traceRows, onClick }) {
const trace = traceRows.find(r => r.node_id === node.id);
const status = trace ? trace.status : null;
const cls = [
'bd-flow-node',
selected ? 'is-selected' : '',
status ? 'has-trace' : '',
status ? `is-${status}` : '',
].filter(Boolean).join(' ');
return (
);
}
function FlowDetail({ node, activeTab, setActiveTab, traceRows }) {
if (!node) return ;
const rows = traceRows.filter(r => r.node_id === node.id);
const latest = rows[rows.length - 1] || null;
return (
);
}
function SchermFlow() {
const [catalog, setCatalog] = useState({groups: [], nodes: [], edges: []});
const [flowMode, setFlowMode] = useState('pand_scan');
const [selectedNodeId, setSelectedNodeId] = useState(null);
const [activeGroup, setActiveGroup] = useState('foto');
const [activeTab, setActiveTab] = useState('Uitleg');
const [traceRows, setTraceRows] = useState([]);
const [selectedTaakId, setSelectedTaakId] = useState('');
const [selectedPandId, setSelectedPandId] = useState('');
const [traceLaden, setTraceLaden] = useState(false);
const [fout, setFout] = useState(null);
useEffect(() => {
fetch(`${API}/pipeline/flow-catalog`)
.then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
.then(data => {
setCatalog(data);
const first = (data.nodes || []).find(n => n.id === 'foto.haal_luchtfoto') || (data.nodes || [])[0];
if (first) setSelectedNodeId(first.id);
})
.catch(e => setFout(e.message || 'Flow-catalogus laden mislukt'));
}, []);
const selectedNode = nodeById(catalog, selectedNodeId);
const visibleGroups = (catalog.groups || []).filter(g => groupNodes(catalog, g.id, flowMode).length > 0);
const visibleNodes = groupNodes(catalog, activeGroup, flowMode);
useEffect(() => {
const firstGroup = visibleGroups[0];
if (firstGroup && !visibleGroups.some(g => g.id === activeGroup)) {
setActiveGroup(firstGroup.id);
return;
}
if (!selectedNode || !nodeVisibleInMode(selectedNode, flowMode)) {
const firstNode = (catalog.nodes || []).find(n => nodeVisibleInMode(n, flowMode));
if (firstNode) setSelectedNodeId(firstNode.id);
}
}, [flowMode, catalog, activeGroup, selectedNodeId]);
async function laadTrace() {
if (!selectedTaakId) {
setTraceRows([]);
return;
}
setTraceLaden(true);
setFout(null);
try {
const params = new URLSearchParams();
if (selectedPandId.trim()) params.set('pand_id', selectedPandId.trim());
const qs = params.toString();
const data = await fetch(`${API}/pipeline/${selectedTaakId}/trace${qs ? `?${qs}` : ''}`)
.then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)));
setTraceRows(data || []);
} catch (e) {
setFout(e.message || 'Trace laden mislukt');
setTraceRows([]);
} finally {
setTraceLaden(false);
}
}
return (
Flow
Bekijk wat de pipeline per stap doet, van BAG-pand tot AI-resultaat.
{fout &&
{fout}}
{visibleNodes.map(node => (
))}
);
}
window.SchermFlow = SchermFlow;