OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

admin.php

38 KB
<!DOCTYPE html>
<html lang="<?= $this->e($this->lang()) ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="robots" content="noindex, nofollow">
    <meta name="csrf-token" content="<?= $this->csrf() ?>">
    <meta name="money-format" content="<?= $this->e(json_encode($this->moneyJs(), JSON_UNESCAPED_UNICODE)) ?>">
    <title><?= $this->te($page_title ?? 'Administration') ?> - OpenNIT Admin</title>

    <link rel="icon" href="<?= !empty($app_favicon) ? $this->e($app_favicon) : '/assets/img/favicon-default.png' ?>">

    <link rel="stylesheet" href="/assets/css/fonts.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/css/fonts.css') ?>">
    <link rel="stylesheet" href="/assets/css/tokens.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/css/tokens.css') ?>">
    <link rel="stylesheet" href="/assets/css/app.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/css/app.css') ?>">
    <link rel="stylesheet" href="/assets/css/admin.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/css/admin.css') ?>">
    <link rel="stylesheet" href="/assets/fontawesome/css/fontawesome.min.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/fontawesome/css/fontawesome.min.css') ?>">
    <link rel="stylesheet" href="/assets/fontawesome/css/solid.min.css?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/fontawesome/css/solid.min.css') ?>">

    <script src="/assets/js/app.js?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/js/app.js') ?>" defer></script>
    <script src="/assets/js/admin.js?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/js/admin.js') ?>" defer></script>
    <script src="/assets/js/dialog.js?v=<?= filemtime(dirname(__DIR__, 2) . '/public/assets/js/dialog.js') ?>" defer></script>

    <?php if (isset($extra_css)): ?>
        <?php foreach ($extra_css as $css): ?>
            <link rel="stylesheet" href="<?= $this->asset($css) ?>">
        <?php endforeach; ?>
    <?php endif; ?>
</head>
<body class="admin-body">
<a class="skip-link" href="#main-content"><?= $this->te('Zum Inhalt springen') ?></a>
    <header class="admin-header">
        <div class="header-left">
            <button class="sidebar-toggle" id="sidebarToggle" aria-label="<?= $this->te('Menü öffnen') ?>">
                <i class="fas fa-bars"></i>
            </button>
            <a href="/admin" class="logo">
                <span class="logo-icon"><i class="fas fa-cog"></i></span>
                <span class="logo-text"><?= $this->te('Administration') ?></span>
            </a>
        </div>

        <div class="header-right">
            <a href="/" class="btn btn-sm btn-secondary">
                <i class="fas fa-arrow-left"></i> <?= $this->te('Admin verlassen') ?>
            </a>

            <?php if (!empty($_global_running_timer)): ?>
            <?php include __DIR__ . '/../_timer_pill.php'; ?>
            <?php endif; ?>

            <div class="user-menu">
                <button class="user-menu-trigger" aria-expanded="false">
                    <span class="user-avatar admin-avatar">
                        <?= strtoupper(substr($current_user->display_name ?? 'A', 0, 1)) ?>
                    </span>
                </button>
                <div class="user-menu-dropdown">
                    <div class="user-menu-header">
                        <strong><?= $this->e($current_user->display_name ?? '') ?></strong>
                        <small><?= $this->te('Administrator') ?></small>
                    </div>
                    <hr>
                    <form action="/auth/logout" method="POST" class="logout-form">
                        <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
                        <button type="submit" class="dropdown-item logout-btn"><?= $this->te('Abmelden') ?></button>
                    </form>
                </div>
            </div>
        </div>
    </header>

    <div class="sidebar-overlay" id="sidebarOverlay"></div>

    <div class="admin-layout">
        <aside class="admin-sidebar" id="adminSidebar">
            <nav class="admin-nav" aria-label="<?= $this->te('Administrationsmenü') ?>">
                <div class="nav-search">
                    <i class="fas fa-search nav-search-icon" aria-hidden="true"></i>
                    <input type="text" id="navSearch" class="nav-search-input" placeholder="<?= $this->te('Menü durchsuchen') ?>&hellip;" autocomplete="off" spellcheck="false" aria-label="<?= $this->te('Menü durchsuchen') ?>">
                </div>
                <div class="nav-search-empty" id="navSearchEmpty" hidden><?= $this->te('Keine Treffer') ?></div>
                <?php
                /* Einklappbare Navigationsgruppen: Jede Gruppe wird gepuffert
                   gerendert und nur ausgegeben, wenn sie mindestens einen fuer
                   den Benutzer sichtbaren Eintrag enthaelt (leere Gruppen samt
                   Ueberschrift verschwinden). Die Gruppe mit der aktiven Seite
                   klappt automatisch auf. Die Modul-/Rechtepruefung pro Eintrag
                   bleibt unveraendert wie zuvor. */
                $navActive = $current_admin_page ?? '';
                $navGroup = function (string $title, string $key, string $html): void {
                    $html = trim($html);
                    if ($html === '') { return; }
                    $open = str_contains($html, 'nav-item active') ? ' open' : '';
                    echo '<details class="nav-group"' . $open . ' data-nav-group="' . $key . '">';
                    echo '<summary class="nav-group-title"><span>' . $title . '</span>'
                       . '<i class="fas fa-chevron-down nav-group-caret"></i></summary>';
                    echo '<div class="nav-group-body">' . $html . '</div></details>';
                };
                ?>

                <?php ob_start(); ?>
                    <a href="/admin" class="nav-item <?= $navActive === 'dashboard' ? 'active' : '' ?>">
                        <i class="fas fa-tachometer-alt nav-icon"></i> <?= $this->te('Dashboard') ?>
                    </a>
                    <?php if (($modules['deadlines'] ?? true) && $this->hasCapability('view_deadlines')): ?>
                    <a href="/admin/deadlines/settings" class="nav-item <?= $navActive === 'deadlines-digest' ? 'active' : '' ?>">
                        <i class="fas fa-hourglass-half nav-icon"></i> <?= $this->te('Fristen-Digest') ?>
                    </a>
                    <?php endif; ?>
                    <a href="/admin/statistics" class="nav-item <?= $navActive === 'statistics' ? 'active' : '' ?>">
                        <i class="fas fa-chart-bar nav-icon"></i> <?= $this->te('Statistiken') ?>
                    </a>
                    <?php if ($this->hasCapability('admin_system')): ?>
                    <a href="/admin/system-health" class="nav-item <?= $navActive === 'system-health' ? 'active' : '' ?>">
                        <i class="fas fa-heartbeat nav-icon"></i> <?= $this->te('System-Health') ?>
                    </a>
                    <a href="/admin/logs" class="nav-item <?= $navActive === 'logs' ? 'active' : '' ?>">
                        <i class="fas fa-file-alt nav-icon"></i> <?= $this->te('System-Logs') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Übersicht'), 'overview', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if ($this->hasCapability('manage_users')): ?>
                    <a href="/admin/users" class="nav-item <?= $navActive === 'users' ? 'active' : '' ?>">
                        <i class="fas fa-users nav-icon"></i> <?= $this->te('Benutzer') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('manage_roles')): ?>
                    <a href="/admin/roles" class="nav-item <?= $navActive === 'roles' ? 'active' : '' ?>">
                        <i class="fas fa-user-shield nav-icon"></i> <?= $this->te('Rollen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('manage_all_permissions')): ?>
                    <a href="/admin/permissions" class="nav-item <?= $navActive === 'permissions' ? 'active' : '' ?>">
                        <i class="fas fa-key nav-icon"></i> <?= $this->te('Berechtigungen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('view_audit_log')): ?>
                    <a href="/admin/audit" class="nav-item <?= $navActive === 'audit' ? 'active' : '' ?>">
                        <i class="fas fa-file-alt nav-icon"></i> <?= $this->te('Audit-Log') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['vault'] ?? true) && $this->hasCapability('admin_vault_teams')): ?>
                    <a href="/admin/vault/teams" class="nav-item <?= $navActive === 'vault-teams' ? 'active' : '' ?>">
                        <i class="fas fa-key nav-icon"></i> <?= $this->te('Team-Tresore') ?>
                    </a>
                    <a href="/admin/vault/extension" class="nav-item <?= $navActive === 'vault-extension' ? 'active' : '' ?>">
                        <i class="fas fa-puzzle-piece nav-icon"></i> <?= $this->te('Vault-Erweiterung') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('selfservice_portal')): ?>
                    <a href="/admin/portal" class="nav-item <?= $navActive === 'portal' ? 'active' : '' ?>">
                        <i class="fas fa-concierge-bell nav-icon"></i> <?= $this->te('Self-Service-Portal') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Benutzer & Sicherheit'), 'security', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if ($this->hasCapability('admin_tickets') || $this->hasCapability('admin_assets')): ?>
                    <a href="/admin/locations" class="nav-item <?= $navActive === 'locations' ? 'active' : '' ?>">
                        <i class="fas fa-map-marker-alt nav-icon"></i> <?= $this->te('Standorte') ?>
                    </a>
                    <a href="/admin/directory" class="nav-item <?= $navActive === 'directory' ? 'active' : '' ?>">
                        <i class="fas fa-address-book nav-icon"></i> <?= $this->te('Verzeichnis & Personen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_email')): ?>
                    <a href="/admin/email" class="nav-item <?= $navActive === 'email' ? 'active' : '' ?>">
                        <i class="fas fa-envelope nav-icon"></i> <?= $this->te('E-Mail-Vorlagen') ?>
                    </a>
                    <a href="/admin/notifications" class="nav-item <?= $navActive === 'notifications' ? 'active' : '' ?>">
                        <i class="fas fa-bell nav-icon"></i> <?= $this->te('Benachrichtigungen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_docs')): ?>
                    <a href="/admin/page-templates" class="nav-item <?= $navActive === 'page-templates' ? 'active' : '' ?>">
                        <i class="fas fa-clipboard-list nav-icon"></i> <?= $this->te('Seitenvorlagen') ?>
                    </a>
                    <a href="/admin/tags" class="nav-item <?= $navActive === 'tags' ? 'active' : '' ?>">
                        <i class="fas fa-tags nav-icon"></i> <?= $this->te('Tags') ?>
                    </a>
                    <a href="/admin/archived-pages" class="nav-item <?= $navActive === 'archived' ? 'active' : '' ?>">
                        <i class="fas fa-archive nav-icon"></i> <?= $this->te('Archivierte Seiten') ?>
                    </a>
                    <a href="/admin/tinymce" class="nav-item <?= $navActive === 'tinymce' ? 'active' : '' ?>">
                        <i class="fas fa-edit nav-icon"></i> <?= $this->te('Editor (TinyMCE)') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Inhalte & Kommunikation'), 'content', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if (($modules['tickets'] ?? true) && $this->hasCapability('admin_tickets')): ?>
                    <a href="/admin/tickets/settings" class="nav-item <?= $navActive === 'tickets-settings' ? 'active' : '' ?>">
                        <i class="fas fa-cog nav-icon"></i> <?= $this->te('Einstellungen') ?>
                    </a>
                    <a href="/admin/tickets/categories" class="nav-item <?= $navActive === 'tickets-categories' ? 'active' : '' ?>">
                        <i class="fas fa-folder nav-icon"></i> <?= $this->te('Kategorien') ?>
                    </a>
                    <a href="/admin/tickets/canned-responses" class="nav-item <?= $navActive === 'tickets-canned-responses' ? 'active' : '' ?>">
                        <i class="fas fa-comment-dots nav-icon"></i> <?= $this->te('Antwortvorlagen') ?>
                    </a>
                    <a href="/admin/tickets/features" class="nav-item <?= $navActive === 'tickets-features' ? 'active' : '' ?>">
                        <i class="fas fa-sliders-h nav-icon"></i> <?= $this->te('Features & SLA') ?>
                    </a>
                    <a href="/admin/tickets/automation" class="nav-item <?= $navActive === 'tickets-automation' ? 'active' : '' ?>">
                        <i class="fas fa-robot nav-icon"></i> <?= $this->te('Automatisierung') ?>
                    </a>
                    <a href="/admin/tickets/recurring" class="nav-item <?= $navActive === 'tickets-recurring' ? 'active' : '' ?>">
                        <i class="fas fa-redo nav-icon"></i> <?= $this->te('Wiederkehrende Tickets') ?>
                    </a>
                    <a href="/admin/tickets/alerts" class="nav-item <?= $navActive === 'tickets-alerts' ? 'active' : '' ?>">
                        <i class="fas fa-bolt nav-icon"></i> <?= $this->te('Alarm-Tickets') ?>
                    </a>
                    <a href="/admin/tickets/custom-fields" class="nav-item <?= $navActive === 'tickets-custom-fields' ? 'active' : '' ?>">
                        <i class="fas fa-list-ul nav-icon"></i> <?= $this->te('Eigene Felder') ?>
                    </a>
                    <a href="/admin/tickets/service-reports" class="nav-item <?= $navActive === 'tickets-service-reports' ? 'active' : '' ?>">
                        <i class="fas fa-file-signature nav-icon"></i> <?= $this->te('Leistungsnachweise') ?>
                    </a>
                    <a href="/admin/tickets/csat" class="nav-item <?= $navActive === 'tickets-csat' ? 'active' : '' ?>">
                        <i class="fas fa-star nav-icon"></i> <?= $this->te('CSAT-Bewertungen') ?>
                    </a>
                    <a href="/admin/tickets/stats" class="nav-item <?= $navActive === 'tickets-stats' ? 'active' : '' ?>">
                        <i class="fas fa-chart-line nav-icon"></i> <?= $this->te('Statistiken') ?>
                    </a>
                    <a href="/admin/tickets/mail-queue" class="nav-item <?= $navActive === 'tickets-mail-queue' ? 'active' : '' ?>">
                        <i class="fas fa-inbox nav-icon"></i> <?= $this->te('E-Mail-Queue') ?>
                    </a>
                    <a href="/admin/tickets/templates" class="nav-item <?= $navActive === 'tickets-templates' ? 'active' : '' ?>">
                        <i class="fas fa-thumbtack nav-icon"></i> <?= $this->te('Ticket-E-Mails') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['tickets'] ?? true) && $this->hasCapability('report_ticket_time')): ?>
                    <a href="/admin/tickets/time-report" class="nav-item <?= $navActive === 'tickets-time-report' ? 'active' : '' ?>">
                        <i class="fas fa-stopwatch nav-icon"></i> <?= $this->te('Zeitauswertung') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Ticketsystem'), 'tickets', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if (($modules['files'] ?? false) && $this->hasCapability('admin_files')): ?>
                    <a href="/admin/files" class="nav-item <?= $navActive === 'files-settings' ? 'active' : '' ?>">
                        <i class="fas fa-cog nav-icon"></i> <?= $this->te('Einstellungen') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Datei-Explorer'), 'files', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if (($modules['assets'] ?? true) && $this->hasCapability('admin_assets')): ?>
                    <a href="/admin/inventory/sync" class="nav-item <?= $navActive === 'assets-sync' ? 'active' : '' ?>">
                        <i class="fas fa-sync-alt nav-icon"></i> <?= $this->te('Asset-Synchronisation') ?>
                    </a>
                    <a href="/admin/inventory/unmatched-devices" class="nav-item <?= $navActive === 'assets-unmatched' ? 'active' : '' ?>">
                        <i class="fas fa-question-circle nav-icon"></i> <?= $this->te('Unbekannte Geräte') ?>
                    </a>
                    <a href="/admin/assets/categories" class="nav-item <?= $navActive === 'assets' ? 'active' : '' ?>">
                        <i class="fas fa-folder nav-icon"></i> <?= $this->te('Kategorien') ?>
                    </a>
                    <a href="/admin/assets/valuation" class="nav-item <?= $navActive === 'assets-valuation' ? 'active' : '' ?>">
                        <i class="fas fa-coins nav-icon"></i> <?= $this->te('Wert & Nutzungsdauer') ?>
                    </a>
                    <a href="/admin/inventory/import/glpi" class="nav-item <?= $navActive === 'assets-glpi-import' ? 'active' : '' ?>">
                        <i class="fas fa-file-import nav-icon"></i> <?= $this->te('GLPI Import') ?>
                    </a>
                    <a href="/admin/assets/handover-settings" class="nav-item <?= $navActive === 'assets-handover' ? 'active' : '' ?>">
                        <i class="fas fa-file-signature nav-icon"></i> <?= $this->te('Geräte-Übergabe') ?>
                    </a>
                    <a href="/admin/settings/label-print" class="nav-item <?= $navActive === 'assets-settings-labelprint' ? 'active' : '' ?>">
                        <i class="fas fa-tag nav-icon"></i> <?= $this->te('Etikettendruck') ?>
                    </a>
                    <?php if ($this->hasCapability('admin_assets')): ?>
                    <a href="/admin/inventory/intune" class="nav-item <?= $navActive === 'intune-mdm' ? 'active' : '' ?>">
                        <i class="fas fa-cloud nav-icon"></i> <?= $this->te('Microsoft Intune') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_assets')): ?>
                    <a href="/admin/inventory/relution" class="nav-item <?= $navActive === 'relution-mdm' ? 'active' : '' ?>">
                        <i class="fas fa-mobile-alt nav-icon"></i> <?= $this->te('Relution MDM') ?>
                    </a>
                    <a href="/admin/settings/dell" class="nav-item <?= $navActive === 'assets-settings-dell' ? 'active' : '' ?>">
                        <i class="fas fa-wrench nav-icon"></i> <?= $this->te('Dell API') ?>
                    </a>
                    <a href="/admin/settings/hp" class="nav-item <?= $navActive === 'assets-settings-hp' ? 'active' : '' ?>">
                        <i class="fas fa-wrench nav-icon"></i> <?= $this->te('HP API') ?>
                    </a>
                    <?php endif; ?>
                    <?php endif; ?>
                <?php $navGroup($this->te('Inventar & Assets'), 'assets', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if (($modules['contracts'] ?? true) && $this->hasCapability('admin_contracts')): ?>
                    <a href="/admin/contracts/settings" class="nav-item <?= $navActive === 'contracts-settings' ? 'active' : '' ?>">
                        <i class="fas fa-file-contract nav-icon"></i> <?= $this->te('Verträge') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['stock'] ?? true) && $this->hasCapability('admin_stock')): ?>
                    <a href="/admin/stock/categories" class="nav-item <?= $navActive === 'stock-categories' ? 'active' : '' ?>">
                        <i class="fas fa-boxes nav-icon"></i> <?= $this->te('Lager') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['loans'] ?? false) && $this->hasCapability('admin_loans')): ?>
                    <a href="/admin/loans" class="nav-item <?= $navActive === 'loans-settings' ? 'active' : '' ?>">
                        <i class="fas fa-hand-holding nav-icon"></i> <?= $this->te('Leihgeräte') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['orders'] ?? false) && $this->hasCapability('admin_orders')): ?>
                    <a href="/admin/orders" class="nav-item <?= $navActive === 'orders' ? 'active' : '' ?>">
                        <i class="fas fa-shopping-cart nav-icon"></i> <?= $this->te('Bestellungen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (!empty($modules['licenses']) && $this->hasCapability('admin_licenses')): ?>
                    <a href="/admin/licenses/sync" class="nav-item <?= $navActive === 'license-sync' ? 'active' : '' ?>">
                        <i class="fas fa-sync-alt nav-icon"></i> <?= $this->te('Lizenz-Sync (Microsoft 365)') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['workflows'] ?? false) && $this->hasCapability('admin_workflows')): ?>
                    <a href="/admin/workflows" class="nav-item <?= in_array($navActive, ['workflows', 'workflows-settings'], true) ? 'active' : '' ?>">
                        <i class="fas fa-clipboard-check nav-icon"></i> <?= $this->te('Anträge & Genehmigungen') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Geschäftsprozesse'), 'business', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if (($modules['vms'] ?? true) && $this->hasCapability('admin_vms')): ?>
                    <a href="/admin/vms/hypervisor" class="nav-item <?= $navActive === 'vm-hypervisor' ? 'active' : '' ?>">
                        <i class="fas fa-server nav-icon"></i> <?= $this->te('Hypervisor-APIs') ?>
                    </a>
                    <a href="/admin/vms/setup-guide" class="nav-item <?= $navActive === 'vm-setup-guide' ? 'active' : '' ?>">
                        <i class="fas fa-book-open nav-icon"></i> <?= $this->te('VM-Einrichtungsanleitung') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['netdiagram'] ?? true) && $this->hasCapability('admin_netdiagram')): ?>
                    <a href="/admin/netdiagram/drawio" class="nav-item <?= $navActive === 'netdiagram-drawio' ? 'active' : '' ?>">
                        <i class="fas fa-project-diagram nav-icon"></i> <?= $this->te('Netzwerkdiagramme') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['ipam'] ?? true) && $this->hasCapability('admin_ipam')): ?>
                    <a href="/admin/ipam-scan-agent" class="nav-item <?= $navActive === 'ipam-scan-agent' ? 'active' : '' ?>">
                        <i class="fas fa-network-wired nav-icon"></i> <?= $this->te('IPAM-Scan-Agent') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['backup'] ?? false) && $this->hasCapability('admin_backup')): ?>
                    <a href="/admin/backup/settings" class="nav-item <?= $navActive === 'backup-settings' ? 'active' : '' ?>">
                        <i class="fas fa-save nav-icon"></i> <?= $this->te('Backup (Veeam)') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['unifi'] ?? false) && $this->hasCapability('admin_unifi')): ?>
                    <a href="/admin/settings/unifi" class="nav-item <?= $navActive === 'unifi-settings' ? 'active' : '' ?>">
                        <i class="fas fa-wifi nav-icon"></i> <?= $this->te('UniFi-Controller') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['budget'] ?? false) && $this->hasCapability('manage_budgets')): ?>
                    <a href="/admin/budget" class="nav-item <?= $navActive === 'budget-settings' ? 'active' : '' ?>">
                        <i class="fas fa-money-bill-wave nav-icon"></i> <?= $this->te('Budget') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['cve'] ?? false) && $this->hasCapability('admin_cve')): ?>
                    <a href="/admin/cve/settings" class="nav-item <?= $navActive === 'cve-settings' ? 'active' : '' ?>">
                        <i class="fas fa-shield-alt nav-icon"></i> <?= $this->te('CVE-Meldungen') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['maintenance'] ?? false) && $this->hasCapability('manage_maintenance')): ?>
                    <a href="/admin/maintenance/types" class="nav-item <?= $navActive === 'maintenance-types' ? 'active' : '' ?>">
                        <i class="fas fa-calendar-check nav-icon"></i> <?= $this->te('Terminkategorien') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['dns'] ?? false) && $this->hasCapability('admin_dns')): ?>
                    <a href="/admin/dns-agent" class="nav-item <?= $navActive === 'dns-agent' ? 'active' : '' ?>">
                        <i class="fas fa-sitemap nav-icon"></i> <?= $this->te('DNS-Agent') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['xdr'] ?? false) && $this->hasCapability('admin_xdr')): ?>
                    <a href="/admin/xdr/settings" class="nav-item <?= $navActive === 'xdr-settings' ? 'active' : '' ?>">
                        <i class="fas fa-shield-virus nav-icon"></i> <?= $this->te('XDR-Warnungen') ?>
                    </a>
                    <a href="/admin/xdr/rules" class="nav-item <?= $navActive === 'xdr-rules' ? 'active' : '' ?>">
                        <i class="fas fa-filter nav-icon"></i> <?= $this->te('XDR-Regeln') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['monitoring'] ?? true) && $this->hasCapability('admin_monitoring')): ?>
                    <a href="/admin/monitoring/settings" class="nav-item <?= $navActive === 'monitoring-settings' ? 'active' : '' ?>">
                        <i class="fas fa-chart-area nav-icon"></i> <?= $this->te('Monitoring') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['statuspage'] ?? false) && $this->hasCapability('admin_statuspage')): ?>
                    <a href="/admin/statuspage/settings" class="nav-item <?= $navActive === 'statuspage-settings' ? 'active' : '' ?>">
                        <i class="fas fa-traffic-light nav-icon"></i> <?= $this->te('Öffentliche Statusseite') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['software'] ?? false) && $this->hasCapability('manage_software')): ?>
                    <a href="/admin/software/settings" class="nav-item <?= $navActive === 'software-settings' ? 'active' : '' ?>">
                        <i class="fas fa-box-open nav-icon"></i> <?= $this->te('Software-Inventar') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('Infrastruktur'), 'infrastructure', ob_get_clean()); ?>

                <?php ob_start(); ?>
                    <?php if ($this->hasCapability('admin_settings')): ?>
                    <a href="/admin/settings" class="nav-item <?= $navActive === 'settings' ? 'active' : '' ?>">
                        <i class="fas fa-cog nav-icon"></i> <?= $this->te('Einstellungen') ?>
                    </a>
                    <?php if ((($modules['ai_chat'] ?? false) || ($modules['api_auto'] ?? false)) && $this->hasCapability('admin_settings')): ?>
                    <a href="/admin/settings/ai-chat" class="nav-item <?= $navActive === 'ai-chat' ? 'active' : '' ?>">
                        <i class="fas fa-robot nav-icon"></i> <?= $this->te('KI-Integration') ?>
                    </a>
                    <?php endif; ?>
                    <?php if (($modules['api_auto'] ?? false) && $this->hasCapability('admin_settings')): ?>
                    <a href="/admin/api-auto" class="nav-item <?= $navActive === 'api-auto' ? 'active' : '' ?>">
                        <i class="fas fa-plug nav-icon"></i> <?= $this->te('API-Automatisierung') ?>
                    </a>
                    <?php endif; ?>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_system')): ?>
                    <a href="/admin/cronjobs" class="nav-item <?= $navActive === 'cronjobs' ? 'active' : '' ?>">
                        <i class="fas fa-clock nav-icon"></i> <?= $this->te('Cronjobs') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('manage_selfbackup')): ?>
                    <a href="/admin/selfbackup" class="nav-item <?= $navActive === 'selfbackup' ? 'active' : '' ?>">
                        <i class="fas fa-database nav-icon"></i> <?= $this->te('Datensicherung') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_system')): ?>
                    <a href="/admin/image-optimization" class="nav-item <?= $navActive === 'image-optimization' ? 'active' : '' ?>">
                        <i class="fas fa-compress nav-icon"></i> <?= $this->te('Bild-Optimierung') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('system_admin')): ?>
                    <a href="/admin/update" class="nav-item <?= $navActive === 'update' ? 'active' : '' ?>">
                        <i class="fas fa-sync-alt nav-icon"></i> <?= $this->te('System-Update') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('admin_api')): ?>
                    <a href="/admin/api" class="nav-item <?= $navActive === 'api' ? 'active' : '' ?>">
                        <i class="fas fa-plug nav-icon"></i> <?= $this->te('REST-API & Tokens') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('view_manual')): ?>
                    <a href="/admin/manual" class="nav-item <?= $navActive === 'manual' ? 'active' : '' ?>">
                        <i class="fas fa-book nav-icon"></i> <?= $this->te('Systemhandbuch') ?>
                    </a>
                    <?php endif; ?>
                    <?php if ($this->hasCapability('view_audit_log')): ?>
                    <a href="/admin/compliance" class="nav-item <?= $navActive === 'compliance' ? 'active' : '' ?>">
                        <i class="fas fa-clipboard-check nav-icon"></i> <?= $this->te('Audit-Bericht') ?>
                    </a>
                    <?php endif; ?>
                <?php $navGroup($this->te('System'), 'system', ob_get_clean()); ?>

            </nav>
        </aside>

        <main id="main-content" tabindex="-1" class="admin-main">
            <?php if ($flash = $this->flash()): ?>
                <div class="flash-messages">
                    <?php foreach ($flash as $msg): ?>
                        <div class="flash-message flash-<?= $this->e($msg['type'] ?? 'info') ?>">
                            <?= $this->e($msg['message'] ?? '') ?>
                            <button type="button" class="flash-close" aria-label="<?= $this->te('Schlie&szlig;en') ?>">&times;</button>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>

            <div class="content-header">
                <div>
                    <?php if (empty($hide_page_title)): ?>
                    <h1><?= $this->te($page_title ?? 'Administration') ?></h1>
                    <?php endif; ?>
                    <?php if (!empty($breadcrumbs)): ?>
                    <ol class="breadcrumb">
                        <li class="breadcrumb-item"><a href="/admin"><?= $this->te('Admin') ?></a></li>
                        <?php foreach ($breadcrumbs as $crumb): ?>
                            <?php if (!empty($crumb['url'])): ?>
                                <li class="breadcrumb-item"><a href="<?= $this->e($crumb['url']) ?>"><?= $this->te($crumb['label']) ?></a></li>
                            <?php else: ?>
                                <li class="breadcrumb-item active"><?= $this->te($crumb['label']) ?></li>
                            <?php endif; ?>
                        <?php endforeach; ?>
                    </ol>
                    <?php endif; ?>
                </div>
                <?php if (isset($page_actions)): ?>
                    <div class="content-header-actions">
                        <?= $page_actions ?>
                    </div>
                <?php endif; ?>
            </div>

            <div class="admin-content">
                <?= $content ?>
            </div>
        </main>
    </div>

    <script>
    (function() {
        var toggle = document.getElementById('sidebarToggle');
        var sidebar = document.getElementById('adminSidebar');
        var overlay = document.getElementById('sidebarOverlay');
        if (!toggle || !sidebar || !overlay) return;
        function openSidebar() {
            sidebar.classList.add('open');
            overlay.classList.add('active');
            toggle.setAttribute('aria-label', 'Menü schließen');
        }
        function closeSidebar() {
            sidebar.classList.remove('open');
            overlay.classList.remove('active');
            toggle.setAttribute('aria-label', 'Menü öffnen');
        }
        toggle.addEventListener('click', function() {
            sidebar.classList.contains('open') ? closeSidebar() : openSidebar();
        });
        overlay.addEventListener('click', closeSidebar);
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') closeSidebar();
        });
    })();

    // Navigationsgruppen: Einklappen merken (pro Browser) + Live-Suche.
    // Die Gruppe mit der aktiven Seite bleibt immer offen (server-seitig
    // gesetzt) und ueberschreibt den gespeicherten Zustand nicht.
    (function() {
        var nav = document.querySelector('.admin-nav');
        if (!nav) return;
        var groups = nav.querySelectorAll('.nav-group[data-nav-group]');
        if (!groups.length) return;
        var KEY = 'opennit_admin_nav';
        var state = {};
        try { state = JSON.parse(localStorage.getItem(KEY) || '{}') || {}; } catch (e) { state = {}; }

        function applyCollapse() {
            groups.forEach(function(g) {
                var key = g.getAttribute('data-nav-group');
                if (g.querySelector('.nav-item.active')) { g.open = true; return; }
                if (Object.prototype.hasOwnProperty.call(state, key)) g.open = !!state[key];
            });
        }

        groups.forEach(function(g) {
            var key = g.getAttribute('data-nav-group');
            g.addEventListener('toggle', function() {
                // Waehrend der Suche erzwingen wir das Aufklappen — diesen
                // Zustand nicht persistieren.
                if (nav.classList.contains('nav-searching')) return;
                state[key] = g.open;
                try { localStorage.setItem(KEY, JSON.stringify(state)); } catch (e) {}
            });
        });
        applyCollapse();

        var input = document.getElementById('navSearch');
        var empty = document.getElementById('navSearchEmpty');
        if (input) {
            var norm = function(s) { return (s || '').toLowerCase().trim(); };
            input.addEventListener('input', function() {
                var q = norm(input.value);
                if (q === '') {
                    nav.classList.remove('nav-searching');
                    groups.forEach(function(g) {
                        g.classList.remove('nav-group-hidden');
                        g.querySelectorAll('.nav-item').forEach(function(it) { it.classList.remove('nav-item-hidden'); });
                    });
                    if (empty) empty.hidden = true;
                    applyCollapse();
                    return;
                }
                nav.classList.add('nav-searching');
                var totalVisible = 0;
                groups.forEach(function(g) {
                    var any = false;
                    g.querySelectorAll('.nav-item').forEach(function(it) {
                        var match = norm(it.textContent).indexOf(q) !== -1;
                        it.classList.toggle('nav-item-hidden', !match);
                        if (match) { any = true; totalVisible++; }
                    });
                    g.classList.toggle('nav-group-hidden', !any);
                    if (any) g.open = true;
                });
                if (empty) empty.hidden = totalVisible > 0;
            });
            input.addEventListener('keydown', function(e) {
                if (e.key === 'Escape' && input.value !== '') {
                    e.stopPropagation();
                    input.value = '';
                    input.dispatchEvent(new Event('input'));
                }
            });
        }
    })();
    </script>

    <?php if (isset($extra_js)): ?>
        <?php foreach ($extra_js as $js): ?>
            <script src="<?= $this->asset($js) ?>"></script>
        <?php endforeach; ?>
    <?php endif; ?>
    <?php include __DIR__ . '/_agpl_notice.php'; ?>
</body>
</html>