Files
komp_ac/web/templates/pages/permissions/grants/grants.html
2026-08-15 12:44:36 +02:00

141 lines
6.3 KiB
HTML

{#
GET /permissions/grants — crate::pages::permissions::grants::ui::GrantsTemplate
One matrix per profile: objects down the side, actions across the top. Every
button on the page — a single cell, a whole row, a whole profile, everything —
posts the same form to /permissions/grants/apply and differs only in the list
of object|action pairs it carries. The lists are built in Rust
(grants::state), so the page never asks the server to work out what "all"
meant.
#}
{% extends "ui/base.html" %}
{% block title %}{{ nav.tr("perm-tab-access") }}{% endblock %}
{% macro apply(role, mode, pairs, label, style, hint) %}
<form hx-post="/permissions/grants/apply" hx-target="#permission-status">
<input type="hidden" name="role" value="{{ role }}">
<input type="hidden" name="mode" value="{{ mode }}">
{% for pair in pairs %}<input type="hidden" name="pair" value="{{ pair }}">{% endfor %}
<button type="submit" class="{{ style }}" title="{{ hint }}">{{ label }}</button>
</form>
{% endmacro %}
{% block content %}
<main>
<section class="heading">
<div>
<p class="eyebrow">{{ nav.tr("perm-eyebrow") }}</p>
<h1>{{ nav.tr("perm-tab-access") }}</h1>
<p>{{ nav.tr("grants-description") }}</p>
</div>
</section>
{% include "pages/permissions/tabs.html" %}
{% if page.updated %}<p class="notice">{{ nav.tr("grants-updated-notice") }}</p>{% endif %}
{% if page.roles.is_empty() %}
<section class="panel">
<h2>{{ nav.tr("grants-no-role") }}</h2>
<p class="hint">{{ nav.tr("grants-no-role-hint")|safe }}</p>
</section>
{% else %}
<section class="panel">
<h2>{{ nav.tr("grants-role-heading") }}</h2>
<form method="get" action="/permissions/grants" class="form-grid">
<label>{{ nav.tr("grants-editing") }}
<select name="role" onchange="this.form.submit()">
{% for role in page.roles %}
<option value="{{ role.name }}" {% if page.selected(role.name.as_str()) %}selected{% endif %}>{{ role.name }}{% if !role.parent.is_empty() %} {{ nav.tr("grants-inherits-prefix") }} {{ role.parent }}{% endif %}</option>
{% endfor %}
</select>
</label>
</form>
<p class="hint">
{% if !page.selected_parent.is_empty() %}
{{ nav.tr_args_html("grants-inherits-parent", [("role", page.selected_role.clone()), ("parent", page.selected_parent.clone())])|safe }}
{% else %}
{{ nav.tr_args_html("grants-inherits-none", [("role", page.selected_role.clone())])|safe }}
{% endif %}
{% if page.selected_is_structural %}
{{ nav.tr("grants-structural-hint") }}
{% endif %}
</p>
<h3 class="panel-subhead">{{ nav.tr("grants-shortcuts") }}</h3>
<p class="hint">{{ nav.tr("grants-shortcuts-hint") }}</p>
<div class="actions shortcut-row">
{% call apply(page.selected_role, "grant", page.everything_read_pairs(), nav.tr("roles-access-read"), "secondary", nav.tr("grants-read-everything-hint")) %}{% endcall %}
{% call apply(page.selected_role, "grant", page.everything_pairs(), nav.tr("grants-full-access-everything"), "secondary", nav.tr("grants-full-everything-hint")) %}{% endcall %}
{% if page.has_direct() %}
{% call apply(page.selected_role, "revoke", page.direct_pairs(), nav.tr("grants-remove-all-access"), "danger", nav.tr("grants-remove-all-hint")) %}{% endcall %}
{% endif %}
</div>
</section>
{% for group in page.groups %}
<section class="panel">
<h2>{% if group.global %}{{ nav.tr("grants-everything-everywhere") }}{% else %}{{ nav.tr("grants-profile-prefix") }} {{ group.title }}{% endif %}<span class="count">{{ group.rows.len() }}</span></h2>
<div class="actions shortcut-row">
{% if group.has_wildcards() %}
{% call apply(page.selected_role, "grant", group.read_pairs(), nav.tr("grants-read-only"), "secondary", nav.tr("grants-read-only-hint")) %}{% endcall %}
{% call apply(page.selected_role, "grant", group.all_pairs(), nav.tr("grants-full-access"), "secondary", nav.tr("grants-full-access-hint")) %}{% endcall %}
{% endif %}
{% if group.has_direct() %}
{% call apply(page.selected_role, "revoke", group.direct_pairs(), nav.tr("grants-clear"), "danger", nav.tr("grants-clear-hint")) %}{% endcall %}
{% endif %}
</div>
<div class="table-scroll">
<table class="builder-table grant-matrix">
<thead>
<tr>
<th>{{ nav.tr("grants-th-object") }}</th>
{% for action in page.actions() %}<th>{{ action }}</th>{% endfor %}
<th>{{ nav.tr("grants-th-row") }}</th>
</tr>
</thead>
<tbody>
{% for row in group.rows %}
<tr>
<td>
<span class="object-label">{{ row.label }}</span>
{% if !row.note.is_empty() %}<small>{{ row.note }}</small>{% endif %}
<code>{{ row.object }}</code>
</td>
{% for cell in row.cells %}
<td class="grant-cell">
{% if !cell.allowed %}
<span class="cell-closed" title="{{ nav.tr("grants-cell-unavailable") }}">·</span>
{% else if cell.direct %}
{% call apply(page.selected_role, "revoke", row.one_pair(cell.action.as_str()), "✓", "cell-on", nav.tr("grants-cell-direct")) %}{% endcall %}
{% else if cell.inherited %}
<span class="cell-inherited" title="{{ nav.tr("grants-cell-inherited") }}"></span>
{% else %}
{% call apply(page.selected_role, "grant", row.one_pair(cell.action.as_str()), "+", "cell-off", nav.tr("grants-cell-grant")) %}{% endcall %}
{% endif %}
</td>
{% endfor %}
<td class="grant-cell">
<div class="actions">
{% call apply(page.selected_role, "grant", row.all_pairs(), nav.tr("grants-all"), "secondary", nav.tr("grants-all-hint")) %}{% endcall %}
{% if row.has_direct() %}
{% call apply(page.selected_role, "revoke", row.direct_pairs(), nav.tr("grants-none"), "danger", nav.tr("grants-none-hint")) %}{% endcall %}
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
{% endfor %}
{% endif %}
<div id="permission-status" aria-live="polite"></div>
</main>
{% endblock %}