The tabs were drawn in each page's content block, which base.html renders after the breadcrumb, so on every nested Ansible page the strip moved down a line. base.html now has a section_tabs block above the breadcrumb, and every Ansible page fills it. A guard in test_breadcrumbs.py fails any page that calls a tabs macro outside that block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
2.8 KiB
HTML
58 lines
2.8 KiB
HTML
{# ansible/playbooks.html — the Playbooks tab (#5331): every playbook across
|
|
its sources in one list, what each says it does and how it last ran; the
|
|
whole row opens the playbook's page, where it is run. A source with no
|
|
playbooks is one quiet line under the list. #}
|
|
{% extends "base.html" %}
|
|
{% from "_macros.html" import crumbs, page_header, status_badge, empty_state %}
|
|
{% from "ansible/_tabs.html" import ansible_tabs %}
|
|
{% block title %}Playbooks — Steward{% endblock %}
|
|
{% block breadcrumb %}{{ crumbs([("Ansible", "/ansible/"), ("Playbooks", "")]) }}{% endblock %}
|
|
{% block section_tabs %}{{ ansible_tabs("playbooks") }}{% endblock %}
|
|
{% block content %}
|
|
{% call page_header("Playbooks") %}
|
|
{% if can_add %}<a href="/ansible/playbooks/new" class="btn">New playbook</a>{% endif %}
|
|
{% endcall %}
|
|
|
|
{% if not sources %}
|
|
<div class="card">{{ empty_state("No playbook sources are set up.", "Add one in Settings", "/settings/ansible/") }}</div>
|
|
{% else %}
|
|
{% set n_src = rows|map(attribute="source")|unique|list|length %}
|
|
{% set n_bad = rows|selectattr("confirm")|list|length %}
|
|
<p class="page-verdict">
|
|
{{- rows|length }} playbook{{ 's' if rows|length != 1 }}{% if n_src %} from {{ n_src }} source{{ 's' if n_src != 1 }}{% endif %}
|
|
{%- if n_bad %} · <span class="tone-warn">{{ n_bad }} flagged destructive by {{ 'their authors' if n_bad > 1 else 'its author' }}</span>{% endif -%}
|
|
</p>
|
|
|
|
{% if rows %}
|
|
<div class="card-flush">
|
|
<table class="table">
|
|
<thead><tr><th>Playbook</th><th>Source</th><th>Last run</th></tr></thead>
|
|
<tbody>
|
|
{% for r in rows %}
|
|
<tr class="row-link">
|
|
<td class="cell-name playbook-cell">
|
|
<a class="row-target mono" href="/ansible/playbooks/{{ r.source }}/{{ r.path }}" title="{{ r.path }}">{{ r.path }}</a>
|
|
{% if r.description %}<div class="cell-sub" title="{{ r.description }}">{{ r.description }}</div>{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="tone-muted">{{ r.source }}</span>
|
|
{%- if r.category %} <span class="chip">{{ r.category }}</span>{% endif %}
|
|
{%- if r.confirm %} {{ status_badge('warn', 'destructive', 'Its author flagged it as making significant or destructive changes: a real run asks for confirmation') }}{% endif %}
|
|
</td>
|
|
<td class="nowrap">
|
|
{%- if r.last %}{{ status_badge(r.last.status) }} <span class="tone-muted">{{ r.last.started_at|ago }}</span>
|
|
{%- else %}<span class="tone-dim">never run</span>{% endif -%}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
{% for s in empty_sources %}
|
|
<p class="field-hint">{{ s.name }} (<span class="mono">{{ s.path }}</span>) has no playbooks yet.
|
|
{%- if can_add and s.editable %} <a href="/ansible/playbooks/new">Write one</a>{% endif %}</p>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endblock %}
|