Files
server-manager/views/layouts/main.php

240 lines
14 KiB
PHP
Executable File

<?php
$notifications = $notifications ?? [];
$unreadCount = $unreadCount ?? 0;
$teamId = $teamId ?? null;
$title = $title ?? 'ServerManager';
$content = $content ?? '';
$currentRoute = $_SERVER['REQUEST_URI'] ?? '/';
if (!isset($user) || $user === null) {
$user = auth();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($title) ?> — ServerManager</title>
<meta name="api-token" content="<?= htmlspecialchars($user['api_token'] ?? '') ?>">
<meta name="csrf-token" content="<?= $this->csrfToken() ?>">
<link rel="stylesheet" href="/assets/css/style.css?v=<?= asset_version() ?>">
<link rel="icon" type="image/svg+xml" href="/assets/img/server.svg">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#0f1117">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="apple-touch-icon" href="/assets/img/icon-192.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600;14..32,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<div class="app-layout">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<a href="/dashboard" class="sidebar-brand">
<i class="fas fa-cubes"></i>
<span>ServerManager</span>
</a>
</div>
<nav class="sidebar-nav">
<!-- Dashboard -->
<a href="/dashboard" class="nav-item <?= str_starts_with($currentRoute, '/dashboard') ? 'active' : '' ?>">
<i class="fas fa-chart-pie"></i>
<span>Dashboard</span>
</a>
<!-- Servers -->
<div class="nav-item-accordion <?= str_starts_with($currentRoute, '/servers') || str_starts_with($currentRoute, '/terminal') ? 'expanded' : '' ?>">
<a href="#" class="nav-item nav-item-toggle <?= str_starts_with($currentRoute, '/servers') ? 'active' : '' ?>" onclick="toggleServerList(event)">
<i class="fas fa-server"></i>
<span>Servers</span>
<i class="fas fa-chevron-right nav-chevron"></i>
</a>
<div class="nav-sublist" id="serverList" style="<?= str_starts_with($currentRoute, '/servers') || str_starts_with($currentRoute, '/terminal') ? '' : '' ?>">
<a href="/servers" class="nav-sub-item"><i class="fas fa-list"></i> All Servers</a>
</div>
</div>
<!-- Administration -->
<?php if (is_super_admin()): ?>
<hr class="nav-divider">
<div class="nav-section">Administration</div>
<a href="/admin" class="nav-item <?= str_starts_with($currentRoute, '/admin') ? 'active' : '' ?>">
<i class="fas fa-cog"></i>
<span>Administration</span>
</a>
<?php elseif (is_admin()): ?>
<hr class="nav-divider">
<div class="nav-section">Administration</div>
<a href="/teams" class="nav-item <?= str_starts_with($currentRoute, '/teams') ? 'active' : '' ?>">
<i class="fas fa-users-cog"></i>
<span>Teams</span>
</a>
<?php endif; ?>
<!-- Mobile -->
<hr class="nav-divider">
<div class="nav-section">Mobile</div>
<a href="#" class="nav-item" onclick="showDownloadModal(); return false;">
<i class="fas fa-download"></i>
<span>Download APK</span>
</a>
</nav>
</aside>
<!-- Main Content -->
<div class="main-area">
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="btn btn-icon" id="sidebarToggle" onclick="document.getElementById('sidebar').classList.toggle('collapsed')">
<i class="fas fa-bars"></i>
</button>
</div>
<div class="topbar-right">
<div class="notification-btn-wrapper">
<button class="btn btn-icon notification-btn" onclick="toggleNotifications()" title="Notifications">
<i class="fas fa-bell"></i>
<span class="notification-badge" id="notificationBadge" style="display:<?= $unreadCount > 0 ? 'inline-flex' : 'none' ?>">
<?= $unreadCount > 99 ? '99+' : $unreadCount ?>
</span>
</button>
<div class="notification-dropdown" id="notifDropdown">
<div class="notif-header">
<h3>Notifications</h3>
<button class="btn btn-text btn-sm" id="markAllReadBtn" onclick="markAllRead()"
style="display:<?= $unreadCount > 0 ? 'inline-flex' : 'none' ?>">
<i class="fas fa-check-double"></i> Mark all read
</button>
</div>
<div class="notif-list" id="notifList">
<div style="padding:40px 20px;text-align:center;color:var(--text-muted)">
<i class="fas fa-spinner fa-spin" style="font-size:1.5em;margin-bottom:10px;display:block"></i>
<span>Loading notifications...</span>
</div>
</div>
</div>
</div>
<div class="dropdown topbar-user-dropdown">
<button class="topbar-user" onclick="toggleUserMenu(event)">
<i class="fas fa-user-circle"></i>
<?= htmlspecialchars($user['username'] ?? 'Guest') ?>
<i class="fas fa-chevron-down" style="font-size:0.7rem;margin-left:4px"></i>
</button>
<div class="dropdown-menu dropdown-menu-right" id="userMenu">
<div class="dropdown-header">
<strong><?= htmlspecialchars($user['username'] ?? 'Guest') ?></strong>
<small><?= htmlspecialchars(ucfirst(str_replace('_', ' ', $user['role'] ?? 'guest'))) ?></small>
</div>
<div class="dropdown-divider"></div>
<a href="/profile" class="dropdown-item"><i class="fas fa-user-cog"></i> My Account</a>
<div class="dropdown-divider"></div>
<form method="POST" action="/logout" style="padding:0;margin:0">
<input type="hidden" name="_csrf_token" value="<?= $this->csrfToken() ?>">
<button type="submit" class="dropdown-item" style="width:100%;text-align:left;border:none;background:none;cursor:pointer;font:inherit">
<i class="fas fa-sign-out-alt"></i> Log out
</button>
</form>
</div>
</div>
</div>
</header>
<!-- Page Content -->
<main class="page-content">
<div class="container">
<?php if (isset($_SESSION['_flash'])): ?>
<?php foreach ($_SESSION['_flash'] as $type => $messages): ?>
<?php foreach ($messages as $msg): ?>
<div class="alert alert-<?= $type === 'error' ? 'danger' : $type ?>">
<i class="fas fa-<?= $type === 'success' ? 'check-circle' : ($type === 'error' ? 'exclamation-circle' : 'info-circle') ?>"></i>
<?= htmlspecialchars($msg) ?>
</div>
<?php endforeach; ?>
<?php endforeach; ?>
<?php unset($_SESSION['_flash']); ?>
<?php endif; ?>
<?= $content ?? '' ?>
</div>
</main>
</div>
</div>
<div id="toastContainer" class="toast-container"></div>
<!-- Download Modal -->
<div class="modal" id="downloadModal" onclick="if (event.target === this) closeDownloadModal()">
<div class="modal-dialog" style="max-width: 440px;">
<div class="modal-header">
<h3><i class="fas fa-download" style="color:var(--accent);margin-right:0.5rem"></i>Download ServerManager</h3>
<button class="modal-close" onclick="closeDownloadModal()">&times;</button>
</div>
<div class="modal-body">
<p style="margin-bottom:1rem;font-size:0.92rem;line-height:1.6">
The Android companion app lets you manage your servers on the go:
</p>
<ul style="list-style:none;padding:0;margin:0 0 1.25rem;font-size:0.88rem;display:flex;flex-direction:column;gap:0.6rem">
<li><i class="fas fa-check-circle" style="color:var(--success);width:20px;text-align:center;margin-right:0.5rem"></i> Monitor server status and metrics</li>
<li><i class="fas fa-check-circle" style="color:var(--success);width:20px;text-align:center;margin-right:0.5rem"></i> Receive push notifications for alerts</li>
<li><i class="fas fa-check-circle" style="color:var(--success);width:20px;text-align:center;margin-right:0.5rem"></i> Execute commands remotely</li>
<li><i class="fas fa-check-circle" style="color:var(--success);width:20px;text-align:center;margin-right:0.5rem"></i> Manage services and processes</li>
</ul>
<div style="padding:0.75rem;background:var(--bg-tertiary);border-radius:var(--radius-sm);font-size:0.82rem;color:var(--text-muted);margin-bottom:1rem">
<strong style="color:var(--text-secondary);display:block;margin-bottom:0.4rem"><i class="fas fa-shield-alt" style="margin-right:0.35rem"></i>Required Permissions</strong>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:0.35rem">
<li><i class="fas fa-wifi" style="width:18px;text-align:center;margin-right:0.4rem;font-size:0.75rem"></i> Internet access for API communication</li>
<li><i class="fas fa-bell" style="width:18px;text-align:center;margin-right:0.4rem;font-size:0.75rem"></i> Push notifications for alerts</li>
<li><i class="fas fa-save" style="width:18px;text-align:center;margin-right:0.4rem;font-size:0.75rem"></i> Storage for offline data cache</li>
</ul>
</div>
<label class="checkbox-label" style="margin-bottom:0.75rem;font-size:0.82rem">
<input type="checkbox" id="agreeTerms" onchange="toggleDownloadBtn()">
<span class="checkmark"><i class="fas fa-check"></i></span>
I agree to the <a href="/terms" target="_blank" style="color:var(--accent);text-decoration:underline">Terms &amp; Conditions</a>
and <a href="/privacy" target="_blank" style="color:var(--accent);text-decoration:underline">Privacy Policy</a>
</label>
<div id="downloadProgressWrap" style="display:none">
<div style="display:flex;justify-content:space-between;font-size:0.8rem;color:var(--text-muted);margin-bottom:0.3rem">
<span>Downloading...</span>
<span id="downloadPercent">0%</span>
</div>
<div style="height:6px;background:var(--bg-tertiary);border-radius:3px;overflow:hidden">
<div id="downloadProgressBar" style="height:100%;width:0%;background:linear-gradient(90deg,var(--accent),var(--accent-hover));border-radius:3px;transition:width 0.3s ease"></div>
</div>
</div>
</div>
<div class="modal-footer" id="downloadModalFooter">
<button class="btn btn-secondary" onclick="closeDownloadModal()">Cancel</button>
<button class="btn btn-primary" id="downloadConfirmBtn" onclick="startDownload()" disabled>
<i class="fas fa-download"></i> Download APK
</button>
</div>
</div>
</div>
<script src="/assets/js/app.js?v=<?= asset_version() ?>"></script>
<?php if (isset($scripts)): ?>
<?php foreach ($scripts as $script): ?>
<script src="<?= htmlspecialchars($script) ?>"></script>
<?php endforeach; ?>
<?php endif; ?>
<script src="/assets/js/main.js?v=<?= asset_version() ?>"></script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
</body>
</html>