{# PR groups partial — rendered inside #prs-content (page and HTMX partial). With oob, it also refreshes the "last updated" line and the form's hidden ci input. #} {% macro ci_icon(pr, ci_url, time_ago) -%} {% set as_of = ' (as of ' ~ time_ago(pr.ci_fetched_at) ~ ')' if pr.ci_fetched_at else '' %} {% if pr.ci_state == "success" %} {% elif pr.ci_state == "failure" %} {% elif pr.ci_state == "pending" %} {% elif pr.ci_state == "none" %} {% else %} {% endif %} {%- endmacro %} {% macro stamp(dt, time_ago) -%} {{ time_ago(dt) }} {%- endmacro %} {% macro chip(key, label, count, icon_class, ci, base_path) -%} {% set active = (ci == key) if key else (not ci) %} {%- endmacro %} {% if oob %}

{% if last_refresh %}Last updated {{ last_refresh_ago }}{% endif %}

{% endif %} {% if not has_data %}
⏳

Waiting for the first refresh

Grimoire has not fetched any repository data yet. The pull requests appear here once the first refresh completes; use the Refresh button to start it now.

{% else %} {% if ci_warnings %}
CI status could not be fetched ({{ ci_warnings | length }} warning{{ '' if ci_warnings | length == 1 else 's' }}); affected rows show the last known state. Hover for details.
{% endif %} {# ── Summary chips (distinct PRs across the shown groups) ── #}
{{ chip('', 'Total', counts.total, '', ci, base_path) }} {{ chip('failure', 'Failing', counts.failure, 'fa-solid fa-xmark text-error', ci, base_path) }} {{ chip('pending', 'Pending', counts.pending, 'fa-solid fa-clock text-warning', ci, base_path) }} {{ chip('success', 'Passing', counts.success, 'fa-solid fa-check text-success', ci, base_path) }} {{ chip('none', 'No checks', counts.none, 'fa-solid fa-minus opacity-60', ci, base_path) }} {% if counts.unknown or ci == 'unknown' %} {{ chip('unknown', 'Unknown', counts.unknown, 'fa-solid fa-question opacity-60', ci, base_path) }} {% endif %}
{# ── Group sections ── #}
{% for g in groups %}
{{ g.name }} {{ g.total }} PR{{ '' if g.total == 1 else 's' }} in {{ g.repo_count }} repo{{ '' if g.repo_count == 1 else 's' }} {%- if g.failing %} · {{ g.failing }} failing{% endif -%} {%- if g.pending %} · {{ g.pending }} pending{% endif -%} {% if g.pull_requests %}
{% for item in g.pull_requests %} {% set pr = item.pr %} {% endfor %}
CI Repository Pull request Author Review Age Updated
{{ ci_icon(pr, item.ci_url, time_ago) }} {{ item.repo.split('/')[0] }}/{{ item.repo.split('/')[1] }} #{{ pr.number }} {{ pr.title }}
{% if pr.draft %}draft{% endif %} {% if pr.auto_merge %}auto-merge{% endif %} {% for label in pr.labels %}{{ label }}{% endfor %} {% if pr.head_ref %} {{ pr.head_ref }}{% endif %} {% if pr.base_ref and pr.base_ref != item.default_branch %} {{ pr.base_ref }}{% endif %}
{% if pr.author.endswith('[bot]') %} {% endif %}{{ normalize_author(pr.author) }} {% set rd = pr.review_decision %} {% set ms = pr.merge_state %}
{% if rd == 'approved' %}approved {% elif rd == 'changes_requested' %}changes requested {% elif rd == 'review_required' %}review required{% endif %} {% if ms == 'dirty' %}conflicts {% elif ms == 'behind' %}behind {% elif ms == 'blocked' and rd not in ('review_required', 'changes_requested') %}blocked {% elif ms == 'clean' %}ready{% endif %} {% if not rd and ms not in ('dirty', 'behind', 'blocked', 'clean') %}—{% endif %}
{{ stamp(pr.created_at, time_ago) }} {% if pr.updated_at %}{{ stamp(pr.updated_at, time_ago) }}{% else %}—{% endif %}
{% elif matched_totals[g.name] == 0 %}

No open PRs match this group 🎉

{% else %}

No PRs match the current filters.

{% endif %}
{% endfor %}
{% endif %}