- Dashboard zeigt jetzt in beiden Zustaenden (mit/ohne Login) Kacheln statt Tabelle; ohne Login nur aktive Geraete + Online/Offline/Gesamt, eingeloggt alle Geraete + zusaetzliche Deaktiviert-Kachel - Sortierung ueberall: erst alle Nicht-Online-Geraete, dann Online, jeweils alphabetisch - Neue Rechte devices.view / switches.view; Standardgruppe 'Benutzer' wird automatisch angelegt (alle Ansichtsrechte) und jedem neuen Benutzer zugeordnet; bestehende Benutzer ohne Gruppe werden migriert - Gruppen-Seite zeigt zusaetzlich virtuelle 'Admin'-Karte (informativ) und markiert die Standardgruppe (nicht loeschbar) - 'Naechste Pruefung'-Countdown wird serverseitig aus dem tatsaechlichen letzten Log-Eintrag geseedet statt bei jedem Reload neu zu starten - Deployment-Doku korrigiert: Checkout getrennt von /srv/poe_manager, damit kein verschachteltes srv/poe_manager/srv/poe_manager entsteht Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
158 lines
6.6 KiB
HTML
158 lines
6.6 KiB
HTML
{% extends "base.html" %}
|
|
{% set active_page = "groups" %}
|
|
{% block page_title %}Gruppen{% endblock %}
|
|
{% block page_sub %}<div class="topbar-sub">{{ groups|length + 1 }} Gruppen · Rechteverwaltung</div>{% endblock %}
|
|
{% block topbar_right %}
|
|
<button type="button" class="btn btn-primary" data-open-modal="addGroupModal">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
|
|
Neue Gruppe
|
|
</button>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<p class="text-faint" style="font-size:12.5px; margin-bottom:18px; max-width:720px;">
|
|
Admins dürfen immer alles. Über Gruppen lassen sich einzelne Verwaltungsrechte für
|
|
Devices und Switches gezielt an normale Benutzer vergeben, ohne sie zu Admins zu machen.
|
|
Ein Benutzer kann mehreren Gruppen angehören — die Rechte addieren sich.
|
|
</p>
|
|
|
|
{# Virtuelle Admin-"Gruppe": rein informativ, kein Formular — Admin-Status
|
|
wird ausschließlich über den is_admin-Schalter auf der Users-Seite gesetzt. #}
|
|
<div class="card card-pad" style="margin-bottom:16px; opacity:0.85;">
|
|
<div class="section-head">
|
|
<div class="flex gap-2" style="align-items:center;">
|
|
<h3 style="font-size:16px;">Admin</h3>
|
|
<span class="pill admin">Systemrolle</span>
|
|
</div>
|
|
</div>
|
|
<p class="text-faint" style="font-size:12px; margin:-8px 0 14px;">
|
|
Admins dürfen immer alles — Rolle wird über <a href="{{ url_for('users') }}" style="color:var(--accent-strong); font-weight:600;">Users</a> vergeben, nicht hier.
|
|
</p>
|
|
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:24px;">
|
|
{% for cat_key, cat in permission_catalog.items() %}
|
|
<div>
|
|
<div class="permission-group-title">{{ cat['label'] }}</div>
|
|
<div class="check-list">
|
|
{% for key, label in cat['items'].items() %}
|
|
<label class="check-row" style="cursor:default; color:var(--text-faint);">
|
|
<input type="checkbox" checked disabled>
|
|
{{ label }}
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
<div>
|
|
<div class="permission-group-title">Mitglieder</div>
|
|
<p class="text-dim" style="font-size:13px;">
|
|
{% if admin_virtual_group.member_names %}{{ admin_virtual_group.member_names|join(', ') }}{% else %}Keine Admins vorhanden.{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if groups %}
|
|
<div style="display:flex; flex-direction:column; gap:16px;">
|
|
{% for g in groups %}
|
|
<div class="card card-pad">
|
|
<form method="post">
|
|
<input type="hidden" name="save_group" value="1">
|
|
<input type="hidden" name="group_id" value="{{ g.id }}">
|
|
|
|
<div class="section-head">
|
|
<div class="field" style="margin-bottom:0; max-width:280px; flex:1;">
|
|
<label>Gruppenname</label>
|
|
<div class="flex gap-2" style="align-items:center;">
|
|
<input type="text" name="name" value="{{ g.name }}" required>
|
|
{% if g.is_default %}<span class="pill user" style="white-space:nowrap;">Standard</span>{% endif %}
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
{% if g.is_default %}
|
|
<p class="text-faint" style="font-size:11.5px; margin:-8px 0 8px;">
|
|
Standardgruppe — jeder neu angelegte Benutzer wird ihr automatisch zugeordnet. Kann nicht gelöscht werden.
|
|
</p>
|
|
{% endif %}
|
|
|
|
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:24px; margin-top:8px;">
|
|
{% for cat_key, cat in permission_catalog.items() %}
|
|
<div>
|
|
<div class="permission-group-title">{{ cat['label'] }}</div>
|
|
<div class="check-list">
|
|
{% for key, label in cat['items'].items() %}
|
|
<label class="check-row">
|
|
<input type="checkbox" name="permissions" value="{{ key }}" {% if key in g.permissions %}checked{% endif %}>
|
|
{{ label }}
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<div>
|
|
<div class="permission-group-title">Mitglieder</div>
|
|
<div class="check-list" style="max-height:180px; overflow-y:auto; padding-right:4px;">
|
|
{% for u in all_users %}
|
|
<label class="check-row">
|
|
<input type="checkbox" name="members" value="{{ u['id'] }}" {% if u['id'] in g.members %}checked{% endif %}>
|
|
{{ u['username'] }}
|
|
</label>
|
|
{% else %}
|
|
<p class="text-faint" style="font-size:12px;">Keine Nicht-Admin-Benutzer vorhanden.</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="flex" style="justify-content:space-between; align-items:center; margin-top:16px; padding-top:14px; border-top:1px solid var(--border-soft);">
|
|
<span class="text-faint" style="font-size:12px;">
|
|
{% if g.member_names %}Mitglieder: {{ g.member_names|join(', ') }}{% else %}Keine Mitglieder{% endif %}
|
|
</span>
|
|
{% if not g.is_default %}
|
|
<form method="post" data-confirm="Gruppe „{{ g.name }}“ wirklich löschen? Mitglieder verlieren die zugehörigen Rechte.">
|
|
<input type="hidden" name="delete_group" value="{{ g.id }}">
|
|
<button type="submit" class="btn btn-sm btn-danger">Gruppe löschen</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="card card-pad" style="text-align:center; color:var(--text-faint);">
|
|
Noch keine Gruppen angelegt.
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Modal: Neue Gruppe -->
|
|
<div class="modal-overlay" id="addGroupModal">
|
|
<div class="modal" style="max-width:380px;">
|
|
<form method="post">
|
|
<div class="modal-header">
|
|
<h3>Neue Gruppe</h3>
|
|
<button type="button" class="modal-close" data-close-modal>×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" name="add_group" value="1">
|
|
<div class="field">
|
|
<label>Name</label>
|
|
<input type="text" name="name" required placeholder="z.B. Facility-Team">
|
|
<div class="field-hint">Rechte und Mitglieder werden danach auf der Gruppenkarte eingestellt.</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Anlegen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|