{# ---- Heroicons (outline, MIT) as inline SVG - no build step needed ---- #} {# The sidebar's class list - ONE definition shared by the page shell and the OOB refresh, so a live update can't morph the drawer styling out from under an open drawer. Off-canvas below md; the open state lives on . #} {% macro sidebar_classes() -%} w-72 shrink-0 border-r border-line overflow-y-auto p-3 max-md:fixed max-md:inset-y-0 max-md:left-0 max-md:z-40 max-md:bg-ink max-md:-translate-x-full max-md:transition-transform max-md:duration-200 [.sidebar-open_&]:max-md:translate-x-0 {%- endmacro %} {# One badge style everywhere (paused, draining, state chips, counts) - the hand-rolled copies had drifted between px-1.5 and px-2. #} {% macro badge(tone, extra='') -%} text-xs px-1.5 py-0.5 rounded-md bg-{{ tone }}/10 text-{{ tone }} {{ extra }} {%- endmacro %} {% macro icon(name, cls="w-4 h-4") -%} {%- endmacro %} {# The shared empty/absent state: a centred icon + message (and optional detail), so "no jobs", "no schedulers", "no workers" and "no results" all read the same. #} {% macro empty_state(icon_name='inbox', message='Nothing here', detail=None, bordered=false) -%}
{{ icon(icon_name, 'w-7 h-7') }} {{ message }} {% if detail %}{{ detail }}{% endif %}
{%- endmacro %} {# One job as a native
accordion row. `state` keys the per-row actions (use the job's own state for search results that span states). #} {% macro job_row(name, j, state, page=1) -%}
{# One cell per grid track - check · chevron · id · name · data · progress · time · attempts · actions, in exactly this order; the subgrid does the aligning (.jobs-table in input.css). #} {{ icon('chevron', 'w-4 h-4') }} #{{ j.id }} {# The name is what operators scan; the full name is one hover away. #} {{ j.name }} {% if j.id.startswith('repeat:') %} {# A scheduler's materialized next run. It's a real delayed job (so it counts and can be inspected/promoted); the badge explains why it also shows under Schedulers and names the schedule it came from. #} {{ icon('clock', 'w-3 h-3') }} scheduled occurrence from scheduler '{{ j.id.split(':')[1] }}' {% endif %} {% if state == 'failed' and j.failed_reason %} {# Failed rows lead with WHY - the payload is one expand away, the reason is what an operator scans for. #} {{ j.failed_reason }} {% else %} {{ j.data|tojson }} {% endif %} {% if j.progress is number %} {% endif %} {# The state-relevant moment: finished / started / due / queued. Relative for scanning; the absolute moment lives on the hover title. #} {% if state == 'delayed' %} {{ (j.timestamp + (j.delay or 0))|due }} {% else %} {% set ts = j.finished_on if state in ('completed', 'failed') else (j.processed_on if state == 'active' else j.timestamp) %} {{ ts|ago }} {% endif %} {{ j.attempts_made }}× {% if state == 'delayed' %} {% endif %} {% if state == 'failed' %} {% endif %}
{%- endmacro %} {# state -> semantic status token (one token per state; see styles/input.css) #} {% macro state_token(s) -%} {%- if s == 'active' -%}info {%- elif s == 'completed' -%}success {%- elif s == 'failed' -%}danger {%- elif s == 'delayed' -%}warning {%- else -%}muted {%- endif -%} {%- endmacro %} {# state -> solid progress-bar color #} {% macro bar_color(s) -%}bg-{{ state_token(s) }}{%- endmacro %} {# state -> pill colors (one token, three fixed intensities; both themes) #} {% macro state_color(s) -%} {%- set c = state_token(s) -%}text-{{ c }} bg-{{ c }}/10 ring-{{ c }}/30 {%- endmacro %} {# A clickable job-id chip → the standalone job page. Import `with context` (url_for). #} {% macro job_chip(queue, jid) -%} {% set href = url_for('job_page', name=queue, job_id=jid).path %} #{{ jid }} {%- endmacro %} {# A pagination link → queue_view at page `p`. Import `with context` (url_for + the `name`/`state` of the current panel). #} {% macro pglink(p, label, disabled=false, active=false) -%} {{ label }} {%- endmacro %}