{% from "macros.html" import icon, job_chip, flow_tree with context %} {# Flow content only. The #flow-section wrapper (in flow_section.html) is the stable live region; while the flow is live it morphs THIS body in on each job event - the wrapper, the page chrome and the data/opts wells never move. Same shape as workers_list.html inside #workers-list. Guard with the SAME condition flow_section uses: the live region re-fetches this body directly (flow_fragment route), so a parent removed/cancelled between events arrives here as job=None or flow=None with children_total=0 - render nothing rather than divide by zero. #} {% if job and job.flow %}
{# fan-in progress + the whole tree, states at a glance #}
{{ icon('flow', 'w-3.5 h-3.5') }} flow · {{ job.children_done }}/{{ job.children_total }} children done {% if job.children_failed %} · {{ job.children_failed }} failed {% endif %} {% if job.state == 'failed' %} {# one click re-drives the whole subtree: failed parent re-parks, failed children re-join its barrier - completed children keep their results #} {% endif %}
{# Segmented fan-in bar (done green, failed red, pending empty); floored so the two segments never sum past 100 and a failed flow never reads green. #} {% set done_pct = job.children_done * 100 // job.children_total %} {% set failed_pct = job.children_failed * 100 // job.children_total %}
{{ done_pct }}%
{{ flow_tree(job.queue or name, [job.flow], job.id, cls='ftree well px-3 py-2 max-h-72') }}
{% if job.children_results %}
children results
{{ job.children_results|pretty }}
{% endif %} {% if job.children_failures %}
child failures
{% for cid, reason in job.children_failures.items() %}
{{ job_chip(job.queue or name, cid) }} {{ reason }}
{% endfor %}
{% endif %}
{% endif %}