- Zugangsdaten (SSH-Logins) als eigenstaendige, wiederverwendbare Entitaet statt direkt am Switch; inline Neuanlage beim Switch-Erstellen moeglich; automatische Migration bestehender Switch-Logins - Gruppen-Seite als Tabelle mit Aufklapp-Zeile fuer Rechte (Akkordeon), Admin als feste Systemzeile (Mitgliederverwaltung ueber is_admin), Standardgruppe 'Benutzer' mit allen Ansichtsrechten (devices.view, switches.view), automatische Zuordnung neuer/verwaister Benutzer - Users-Seite: Icon-Buttons statt Text, Bearbeiten+Passwortaenderung in einem Modal zusammengefuehrt, Gruppe/Admin-Zuweisung ueber eigenen Zuweisen-Button (wie Switch-Zuordnung bei Devices), Vor-/Nachname - Dashboard: einheitliche Kachelansicht mit/ohne Login, drei sortierte Abschnitte (Offline/Online/Deaktiviert), Kachel-Suchfilter, Bootstrap- artiges Grid (max. 6 Spalten), Aktivieren-Option im Popup fuer deaktivierte Geraete, Countdown serverseitig korrekt geseedet - Sidebar dauerhaft einklappbar (Desktop, persistent via localStorage) - Devices-Tabelle: Aktions-Buttons nebeneinander statt untereinander Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
151 lines
7.4 KiB
HTML
151 lines
7.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de" data-theme="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{ title or "PoE Manager" }}</title>
|
|
<link rel="icon" href="{{ url_for('static', filename='images/logo.png') }}">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
|
{% block extra_head %}{% endblock %}
|
|
</head>
|
|
<body>
|
|
|
|
{% set nav_items = [
|
|
("index", "Dashboard", "grid", true),
|
|
("devices", "Devices", "cpu", true),
|
|
("switches", "Switches", "share", false),
|
|
("users", "Users", "users", false),
|
|
("logs", "Live-Log", "terminal", false),
|
|
("settings", "Settings", "sliders", false),
|
|
] %}
|
|
|
|
{% set icons = {
|
|
"grid": '<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>',
|
|
"cpu": '<rect x="6" y="6" width="12" height="12" rx="1.5"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3"/>',
|
|
"share": '<circle cx="18" cy="5" r="2.5"/><circle cx="6" cy="12" r="2.5"/><circle cx="18" cy="19" r="2.5"/><path d="M8.2 10.7l7.6-4.4M8.2 13.3l7.6 4.4"/>',
|
|
"users": '<circle cx="9" cy="8" r="3.2"/><path d="M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6"/><circle cx="17.5" cy="8.5" r="2.4"/><path d="M15.8 14.2c2.7.3 4.7 2.4 4.7 5.3"/>',
|
|
"groups": '<rect x="3" y="4" width="8" height="7" rx="1.5"/><rect x="13" y="4" width="8" height="7" rx="1.5"/><rect x="3" y="13" width="8" height="7" rx="1.5"/><rect x="13" y="13" width="8" height="7" rx="1.5"/>',
|
|
"key": '<circle cx="8" cy="15" r="4"/><path d="M11 12l9-9M17 6l3 3M14 9l2 2"/>',
|
|
"terminal": '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 9l4 3-4 3M13 15h5"/>',
|
|
"sliders": '<path d="M4 6h9M17 6h3M4 12h3M11 12h9M4 18h13M20 18h0"/><circle cx="15" cy="6" r="2"/><circle cx="9" cy="12" r="2"/><circle cx="17" cy="18" r="2"/>',
|
|
"logout": '<path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><path d="M16 17l5-5-5-5"/><path d="M21 12H9"/>',
|
|
} %}
|
|
|
|
<div class="app-shell">
|
|
|
|
{% if current_user.is_authenticated %}
|
|
<div class="sidebar-backdrop" data-sidebar-toggle></div>
|
|
|
|
<aside class="sidebar">
|
|
<div class="sidebar-brand">
|
|
<div>
|
|
<span class="brand-name">PoE Manager</span>
|
|
<span class="brand-sub">Device Monitoring</span>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
<a href="{{ url_for('index') }}" class="nav-item {% if active_page == 'index' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['grid']|safe }}</svg>
|
|
Dashboard
|
|
</a>
|
|
{% if current_user.can_view_devices %}
|
|
<a href="{{ url_for('devices') }}" class="nav-item {% if active_page == 'devices' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['cpu']|safe }}</svg>
|
|
Devices
|
|
</a>
|
|
{% endif %}
|
|
{% if current_user.can_manage_switches %}
|
|
<a href="{{ url_for('switches') }}" class="nav-item {% if active_page == 'switches' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['share']|safe }}</svg>
|
|
Switches
|
|
</a>
|
|
<a href="{{ url_for('credentials') }}" class="nav-item {% if active_page == 'credentials' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['key']|safe }}</svg>
|
|
Zugangsdaten
|
|
</a>
|
|
{% endif %}
|
|
{% if current_user.is_admin %}
|
|
<a href="{{ url_for('users') }}" class="nav-item {% if active_page == 'users' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['users']|safe }}</svg>
|
|
Users
|
|
</a>
|
|
<a href="{{ url_for('groups') }}" class="nav-item {% if active_page == 'groups' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['groups']|safe }}</svg>
|
|
Gruppen
|
|
</a>
|
|
<a href="{{ url_for('logs') }}" class="nav-item {% if active_page == 'logs' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['terminal']|safe }}</svg>
|
|
Live-Log
|
|
</a>
|
|
<a href="{{ url_for('settings') }}" class="nav-item {% if active_page == 'settings' %}active{% endif %}">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">{{ icons['sliders']|safe }}</svg>
|
|
Settings
|
|
</a>
|
|
{% endif %}
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<div class="user-chip">
|
|
<div class="user-avatar">{{ current_user.username[:2]|upper }}</div>
|
|
<div class="user-meta">
|
|
<div class="u-name">{{ current_user.username }}</div>
|
|
<div class="u-role">{{ "Administrator" if current_user.is_admin else current_user.group_names or "Benutzer" }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="footer-actions">
|
|
<button type="button" class="icon-btn" data-theme-toggle title="Theme wechseln">
|
|
<span data-theme-icon></span>
|
|
</button>
|
|
<a href="{{ url_for('logout') }}" class="icon-btn" title="Abmelden" style="flex:1; display:flex;">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin:auto;">{{ icons['logout']|safe }}</svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
{% endif %}
|
|
|
|
<div class="main {% if not current_user.is_authenticated %}no-sidebar{% endif %}">
|
|
<div class="topbar">
|
|
<div class="topbar-left">
|
|
{% if current_user.is_authenticated %}
|
|
<button type="button" class="hamburger" data-sidebar-toggle aria-label="Menü">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
|
|
</button>
|
|
{% endif %}
|
|
<div>
|
|
<div class="topbar-title">{% block page_title %}Dashboard{% endblock %}</div>
|
|
{% block page_sub %}{% endblock %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="topbar-logo">
|
|
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="WiS">
|
|
</div>
|
|
|
|
<div class="topbar-right">
|
|
{% block topbar_right %}{% endblock %}
|
|
{% if not current_user.is_authenticated %}
|
|
<a href="{{ url_for('login') }}" 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="M15 3h4a2 2 0 012 2v14a2 2 0 01-2 2h-4"/><path d="M10 17l5-5-5-5"/><path d="M15 12H3"/></svg>
|
|
Login
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content">
|
|
{% block content %}{% endblock %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
<script type="application/json" id="flashed-data">{{ messages|tojson }}</script>
|
|
{% endwith %}
|
|
|
|
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
|
|
{% block scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|