Redesign Watchdog UI: remove Uptime & CRUD subnav, simplify Watchdog hierarchy columns, integrate Gruppen-Uebersicht and rich Monitor & Icon Edit Modal
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#F2622E"/>
|
||||
<path d="M50 20 L80 35 L80 65 L50 80 L20 65 L20 35 Z" fill="none" stroke="#FFF" stroke-width="6"/>
|
||||
<circle cx="50" cy="50" r="10" fill="#FFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 299 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#2496ED"/>
|
||||
<rect x="20" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="35" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="50" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="35" y="32" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="50" y="32" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="65" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<path d="M15 62 Q 50 82 85 62 Q 90 70 85 75 Q 45 92 10 70 Z" fill="#FFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 624 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#F2622E"/>
|
||||
<path d="M50 20 L80 35 L80 65 L50 80 L20 65 L20 35 Z" fill="none" stroke="#FFF" stroke-width="6"/>
|
||||
<circle cx="50" cy="50" r="10" fill="#FFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 299 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#2496ED"/>
|
||||
<rect x="20" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="35" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="50" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="35" y="32" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="50" y="32" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<rect x="65" y="45" width="12" height="10" rx="2" fill="#FFF"/>
|
||||
<path d="M15 62 Q 50 82 85 62 Q 90 70 85 75 Q 45 92 10 70 Z" fill="#FFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 624 B |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<circle cx="50" cy="50" r="48" fill="#FCC624"/>
|
||||
<ellipse cx="50" cy="42" rx="22" ry="26" fill="#000000"/>
|
||||
<ellipse cx="50" cy="45" rx="16" ry="20" fill="#FFFFFF"/>
|
||||
<circle cx="43" cy="35" r="4" fill="#000"/>
|
||||
<circle cx="57" cy="35" r="4" fill="#000"/>
|
||||
<polygon points="46,40 54,40 50,48" fill="#FFA500"/>
|
||||
<ellipse cx="50" cy="72" rx="24" ry="14" fill="#000000"/>
|
||||
<ellipse cx="36" cy="84" rx="12" ry="6" fill="#FFA500"/>
|
||||
<ellipse cx="64" cy="84" rx="12" ry="6" fill="#FFA500"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 588 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#00618A"/>
|
||||
<path d="M20 75 C 20 40, 45 25, 75 35 C 80 50, 70 70, 50 75 Z" fill="#E48E00"/>
|
||||
<circle cx="65" cy="40" r="4" fill="#FFF"/>
|
||||
<text x="50" y="88" font-size="18" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">MySQL</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<polygon points="50,5 92,28 92,72 50,95 8,72 8,28" fill="#009639"/>
|
||||
<path d="M30 70 L30 30 L65 70 L65 30" stroke="#FFF" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 295 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<polygon points="50,10 90,32 90,78 50,100 10,78 10,32" fill="#5FA04E"/>
|
||||
<text x="50" y="62" font-size="24" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">node</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<ellipse cx="50" cy="50" rx="46" ry="28" fill="#777BB4"/>
|
||||
<text x="50" y="58" font-size="28" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">php</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#E57000"/>
|
||||
<path d="M25 25 L75 25 L75 45 L45 45 L45 55 L75 55 L75 75 L25 75 Z" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 238 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<path d="M48 10 C 25 10, 25 22, 25 22 L 25 34 L 50 34 L 50 38 L 20 38 C 20 38, 8 38, 8 60 C 8 82, 20 82, 20 82 L 32 82 L 32 70 C 32 70, 32 56, 48 56 L 62 56 C 62 56, 75 56, 75 42 L 75 22 C 75 22, 75 10, 48 10 Z" fill="#3776AB"/>
|
||||
<circle cx="36" cy="22" r="4" fill="#FFF"/>
|
||||
<path d="M52 90 C 75 90, 75 78, 75 78 L 75 66 L 50 66 L 50 62 L 80 62 C 80 62, 92 62, 92 40 C 92 18, 80 18, 80 18 L 68 18 L 68 30 C 68 30, 68 44, 52 44 L 38 44 C 38 44, 25 44, 25 58 L 25 78 C 25 78, 25 90, 52 90 Z" fill="#FFD43B"/>
|
||||
<circle cx="64" cy="78" r="4" fill="#000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 650 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#D82C20"/>
|
||||
<polygon points="50,20 80,35 50,50 20,35" fill="#FFF"/>
|
||||
<polygon points="50,45 80,60 50,75 20,60" fill="#FFF" opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="16" fill="#1E232B"/>
|
||||
<rect x="15" y="15" width="70" height="30" rx="6" fill="#242A33" stroke="#F2622E" stroke-width="3"/>
|
||||
<circle cx="30" cy="30" r="4" fill="#37C26B"/>
|
||||
<circle cx="42" cy="30" r="4" fill="#37C26B"/>
|
||||
<rect x="15" y="55" width="70" height="30" rx="6" fill="#242A33" stroke="#F2622E" stroke-width="3"/>
|
||||
<circle cx="30" cy="70" r="4" fill="#37C26B"/>
|
||||
<circle cx="42" cy="70" r="4" fill="#37C26B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88" width="24" height="24">
|
||||
<path fill="#00ADEF" d="M0 12.5L35.7 7.6v33.4H0V12.5zm0 32.5h35.7v33.5L0 73.8V45zm39.5-38L88 0v41H39.5V7zm0 38H88v41l-48.5-6.8V45z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 227 B |
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<circle cx="50" cy="50" r="48" fill="#FCC624"/>
|
||||
<ellipse cx="50" cy="42" rx="22" ry="26" fill="#000000"/>
|
||||
<ellipse cx="50" cy="45" rx="16" ry="20" fill="#FFFFFF"/>
|
||||
<circle cx="43" cy="35" r="4" fill="#000"/>
|
||||
<circle cx="57" cy="35" r="4" fill="#000"/>
|
||||
<polygon points="46,40 54,40 50,48" fill="#FFA500"/>
|
||||
<ellipse cx="50" cy="72" rx="24" ry="14" fill="#000000"/>
|
||||
<ellipse cx="36" cy="84" rx="12" ry="6" fill="#FFA500"/>
|
||||
<ellipse cx="64" cy="84" rx="12" ry="6" fill="#FFA500"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 588 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#00618A"/>
|
||||
<path d="M20 75 C 20 40, 45 25, 75 35 C 80 50, 70 70, 50 75 Z" fill="#E48E00"/>
|
||||
<circle cx="65" cy="40" r="4" fill="#FFF"/>
|
||||
<text x="50" y="88" font-size="18" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">MySQL</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 406 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<polygon points="50,5 92,28 92,72 50,95 8,72 8,28" fill="#009639"/>
|
||||
<path d="M30 70 L30 30 L65 70 L65 30" stroke="#FFF" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 295 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<polygon points="50,10 90,32 90,78 50,100 10,78 10,32" fill="#5FA04E"/>
|
||||
<text x="50" y="62" font-size="24" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">node</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<ellipse cx="50" cy="50" rx="46" ry="28" fill="#777BB4"/>
|
||||
<text x="50" y="58" font-size="28" font-family="sans-serif" font-weight="bold" fill="#FFF" text-anchor="middle">php</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#E57000"/>
|
||||
<path d="M25 25 L75 25 L75 45 L45 45 L45 55 L75 55 L75 75 L25 75 Z" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 238 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<path d="M48 10 C 25 10, 25 22, 25 22 L 25 34 L 50 34 L 50 38 L 20 38 C 20 38, 8 38, 8 60 C 8 82, 20 82, 20 82 L 32 82 L 32 70 C 32 70, 32 56, 48 56 L 62 56 C 62 56, 75 56, 75 42 L 75 22 C 75 22, 75 10, 48 10 Z" fill="#3776AB"/>
|
||||
<circle cx="36" cy="22" r="4" fill="#FFF"/>
|
||||
<path d="M52 90 C 75 90, 75 78, 75 78 L 75 66 L 50 66 L 50 62 L 80 62 C 80 62, 92 62, 92 40 C 92 18, 80 18, 80 18 L 68 18 L 68 30 C 68 30, 68 44, 52 44 L 38 44 C 38 44, 25 44, 25 58 L 25 78 C 25 78, 25 90, 52 90 Z" fill="#FFD43B"/>
|
||||
<circle cx="64" cy="78" r="4" fill="#000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 650 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="20" fill="#D82C20"/>
|
||||
<polygon points="50,20 80,35 50,50 20,35" fill="#FFF"/>
|
||||
<polygon points="50,45 80,60 50,75 20,60" fill="#FFF" opacity="0.8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="24" height="24">
|
||||
<rect width="100" height="100" rx="16" fill="#1E232B"/>
|
||||
<rect x="15" y="15" width="70" height="30" rx="6" fill="#242A33" stroke="#F2622E" stroke-width="3"/>
|
||||
<circle cx="30" cy="30" r="4" fill="#37C26B"/>
|
||||
<circle cx="42" cy="30" r="4" fill="#37C26B"/>
|
||||
<rect x="15" y="55" width="70" height="30" rx="6" fill="#242A33" stroke="#F2622E" stroke-width="3"/>
|
||||
<circle cx="30" cy="70" r="4" fill="#37C26B"/>
|
||||
<circle cx="42" cy="70" r="4" fill="#37C26B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 88 88" width="24" height="24">
|
||||
<path fill="#00ADEF" d="M0 12.5L35.7 7.6v33.4H0V12.5zm0 32.5h35.7v33.5L0 73.8V45zm39.5-38L88 0v41H39.5V7zm0 38H88v41l-48.5-6.8V45z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 227 B |
@@ -285,20 +285,37 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
}
|
||||
|
||||
// Edit Watchdog Monitor (Full Update)
|
||||
// Edit Watchdog Monitor (Rich Edit with Icon Upload & Mute Switch matching screenshot)
|
||||
if ($action === 'edit_watchdog_monitor') {
|
||||
$oldSource = trim($_POST['old_source'] ?? '');
|
||||
$newSource = trim($_POST['new_source'] ?? $oldSource);
|
||||
$oldSource = trim($_POST['old_source'] ?? $_POST['source'] ?? '');
|
||||
$newSource = trim($_POST['new_source'] ?? $_POST['source'] ?? '');
|
||||
$type = trim($_POST['type'] ?? 'host');
|
||||
$group = trim($_POST['group_key'] ?? 'Default');
|
||||
$group = trim($_POST['group_key'] ?? $_POST['group'] ?? 'Default');
|
||||
$parent = trim($_POST['parent_source'] ?? '');
|
||||
$os = trim($_POST['os'] ?? '');
|
||||
$interval = (int)($_POST['interval'] ?? 60);
|
||||
$notes = trim($_POST['notes'] ?? '');
|
||||
$url = trim($_POST['url'] ?? '');
|
||||
$icon = trim($_POST['icon'] ?? '');
|
||||
$interval = (int)($_POST['expected_interval_sec'] ?? $_POST['interval'] ?? 60);
|
||||
$isMuted = isset($_POST['is_muted']) ? true : false;
|
||||
|
||||
// Custom icon file upload
|
||||
if (!empty($_FILES['custom_icon_file']['name'])) {
|
||||
$file = $_FILES['custom_icon_file'];
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if (in_array($ext, ['svg', 'png', 'jpg', 'jpeg', 'webp'], true)) {
|
||||
$customDir = __DIR__ . '/assets/icons/custom';
|
||||
if (!is_dir($customDir)) mkdir($customDir, 0777, true);
|
||||
$filename = 'icon_' . time() . '_' . preg_replace('/[^a-z0-9]/', '', strtolower(pathinfo($file['name'], PATHINFO_FILENAME))) . '.' . $ext;
|
||||
if (move_uploaded_file($file['tmp_name'], $customDir . '/' . $filename)) {
|
||||
$icon = 'custom/' . $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($oldSource && $newSource) {
|
||||
$monRepo = new MonitorRepo($pdo);
|
||||
$monRepo->updateMonitor($oldSource, $newSource, 'default', $type, $group, $parent, $os, $notes, null, $interval);
|
||||
$monRepo->updateMonitor($oldSource, $newSource, 'default', $type, $group, $parent, $os, $notes, $url, $interval, $icon, $isMuted);
|
||||
$msg = "Monitor '{$newSource}' wurde erfolgreich aktualisiert.";
|
||||
}
|
||||
}
|
||||
@@ -449,11 +466,43 @@ function buildMonitorTree(array $monitors): array {
|
||||
|
||||
$hierarchicalMonitors = buildMonitorTree($monitors);
|
||||
|
||||
// Calculate Monitor Groups Stats (for Dashboard Group Panel)
|
||||
$groupStats = [];
|
||||
$monitorsUp = 0; $monitorsWarning = 0; $monitorsDown = 0;
|
||||
foreach ($monitors as $m) {
|
||||
if ($m['state'] === 'up') $monitorsUp++;
|
||||
elseif ($m['state'] === 'warning') $monitorsWarning++;
|
||||
else $monitorsDown++;
|
||||
|
||||
$gKey = !empty($m['group_key']) ? $m['group_key'] : 'Default';
|
||||
if (!isset($groupStats[$gKey])) {
|
||||
$groupStats[$gKey] = ['total' => 0, 'ok' => 0];
|
||||
}
|
||||
$groupStats[$gKey]['total']++;
|
||||
if ($m['state'] === 'up') {
|
||||
$groupStats[$gKey]['ok']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Icon Resolver Helper
|
||||
function getMonitorIconUrl(?string $icon, ?string $source, ?string $os, ?string $type): string {
|
||||
if (!empty($icon) && $icon !== 'auto') {
|
||||
if (str_starts_with($icon, 'custom/')) return 'assets/icons/' . $icon;
|
||||
if (str_starts_with($icon, 'assets/')) return $icon;
|
||||
return 'assets/icons/' . $icon;
|
||||
}
|
||||
$s = strtolower(($source ?? '') . ' ' . ($os ?? '') . ' ' . ($type ?? ''));
|
||||
if (str_contains($s, 'proxmox') || str_contains($s, 'pve')) return 'assets/icons/proxmox.svg';
|
||||
if (str_contains($s, 'win')) return 'assets/icons/windows.svg';
|
||||
if (str_contains($s, 'linux') || str_contains($s, 'ubuntu') || str_contains($s, 'debian')) return 'assets/icons/linux.svg';
|
||||
if (str_contains($s, 'mysql') || str_contains($s, 'mariadb') || str_contains($s, 'db')) return 'assets/icons/mysql.svg';
|
||||
if (str_contains($s, 'docker')) return 'assets/icons/docker.svg';
|
||||
if (str_contains($s, 'nginx')) return 'assets/icons/nginx.svg';
|
||||
if (str_contains($s, 'redis')) return 'assets/icons/redis.svg';
|
||||
if (str_contains($s, 'python')) return 'assets/icons/python.svg';
|
||||
if (str_contains($s, 'php')) return 'assets/icons/php.svg';
|
||||
if (str_contains($s, 'node')) return 'assets/icons/node.svg';
|
||||
return 'assets/icons/server.svg';
|
||||
}
|
||||
|
||||
$eventLog = new EventLog($pdo);
|
||||
@@ -554,6 +603,17 @@ $baseUrl = $protocol . '://' . $host;
|
||||
.stat-header { display: flex; align-items: center; justify-content: space-between; color: var(--text-muted); font-size: 0.75rem; font-weight: 600; }
|
||||
.stat-value { font-size: 2rem; font-weight: 800; margin-top: 0.5rem; color: #fff; font-family: 'Roboto Mono', monospace; letter-spacing: -0.02em; }
|
||||
|
||||
/* Group Overview Cards Panel (Screenshot 2) */
|
||||
.group-cards-grid { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 0.75rem; }
|
||||
.group-card { background: rgba(255,255,255,0.03); border: 1px solid var(--border-glass); border-radius: 12px; padding: 0.85rem 1.1rem; display: flex; align-items: center; justify-content: space-between; min-width: 180px; gap: 1rem; }
|
||||
.group-card-name { font-size: 0.875rem; font-weight: 700; color: #fff; }
|
||||
|
||||
/* Icon Grid Selector in Edit Modal (Screenshot 1) */
|
||||
.icon-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; max-height: 180px; overflow-y: auto; padding-right: 4px; }
|
||||
.icon-option-btn { background: rgba(255,255,255,0.03); border: 1px solid var(--border-glass); border-radius: 10px; padding: 0.55rem 0.75rem; color: var(--text-main); font-size: 0.8rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; transition: all 0.2s; }
|
||||
.icon-option-btn:hover { background: rgba(255,255,255,0.08); }
|
||||
.icon-option-btn.selected { border-color: #FF7A45; background: rgba(242,98,46,0.15); color: #fff; box-shadow: 0 0 10px rgba(242,98,46,0.3); }
|
||||
|
||||
/* Cards & Tables */
|
||||
.card { background: var(--bg-card); border: 1px solid var(--border-glass); border-radius: 18px; padding: 1.5rem; margin-bottom: 1.75rem; backdrop-filter: blur(24px); box-shadow: 0 8px 30px rgba(0,0,0,0.25); }
|
||||
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; }
|
||||
@@ -586,7 +646,7 @@ $baseUrl = $protocol . '://' . $host;
|
||||
.form-input { background: rgba(255,255,255,0.04); border: 1px solid var(--border-glass); border-radius: 10px; padding: 0.65rem 0.85rem; color: #fff; font-size: 0.875rem; outline: none; font-family: 'Manrope', system-ui; transition: border-color 0.2s; }
|
||||
.form-input:focus { border-color: var(--primary); }
|
||||
|
||||
/* Dropdown Option List Styling Fix (Readable dark text/bg across all operating systems & browsers) */
|
||||
/* Dropdown Option List Styling Fix */
|
||||
select.form-input option {
|
||||
background-color: #0e121c;
|
||||
color: #EDEFF5;
|
||||
@@ -601,7 +661,7 @@ $baseUrl = $protocol . '://' . $host;
|
||||
/* Modals */
|
||||
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(10px); display: none; align-items: center; justify-content: center; z-index: 200; }
|
||||
.modal-backdrop.active { display: flex; }
|
||||
.modal-card { background: #0e121c; border: 1px solid var(--border-glass); border-radius: 18px; padding: 2rem; max-width: 550px; width: 90%; box-shadow: 0 20px 50px rgba(0,0,0,0.8); }
|
||||
.modal-card { background: #14171d; border: 1px solid rgba(255,255,255,0.12); border-radius: 18px; padding: 1.75rem; max-width: 600px; width: 95%; max-height: 90vh; overflow-y: auto; box-shadow: 0 20px 50px rgba(0,0,0,0.8); }
|
||||
|
||||
.tab-content, .subtab-content { display: none; }
|
||||
.tab-content.active, .subtab-content.active { display: block; }
|
||||
@@ -754,13 +814,15 @@ $baseUrl = $protocol . '://' . $host;
|
||||
$depth = (int)$m['depth'];
|
||||
$indentPx = $depth * 24;
|
||||
$isChild = ($depth > 0);
|
||||
$iconUrl = getMonitorIconUrl($m['icon']??null, $m['source']??null, $m['os']??null, $m['type']??null);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="display:flex; align-items:center; gap:6px; margin-left:<?= $indentPx ?>px; <?= $isChild ? 'border-left:2px solid var(--primary); padding-left:8px;' : '' ?>">
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-left:<?= $indentPx ?>px; <?= $isChild ? 'border-left:2px solid var(--primary); padding-left:8px;' : '' ?>">
|
||||
<?php if ($isChild): ?>
|
||||
<span style="color:var(--primary); font-family:'Roboto Mono'; font-size:13px; font-weight:bold"><?= $m['is_last'] ? '└─' : '├─' ?></span>
|
||||
<?php endif; ?>
|
||||
<img src="<?= htmlspecialchars($iconUrl) ?>" width="18" height="18" alt="icon">
|
||||
<strong><?= htmlspecialchars($m['source']) ?></strong>
|
||||
<span style="color:var(--text-muted); font-size:0.75rem;">(<?= htmlspecialchars($m['instance']) ?>)</span>
|
||||
</div>
|
||||
@@ -872,30 +934,57 @@ $baseUrl = $protocol . '://' . $host;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Watchdog Monitor Modal -->
|
||||
<!-- Rich Monitor Edit Modal (Matching Screenshot 1: Monitor & Icon bearbeiten) -->
|
||||
<div class="modal-backdrop" id="editMonitorModal">
|
||||
<div class="modal-card">
|
||||
<h2 style="color:var(--primary); margin-bottom:0.75rem;">✏️ Monitor Bearbeiten</h2>
|
||||
<form method="POST" action="index.php#sub-watchdog-uptime">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1.25rem;">
|
||||
<h2 style="font-size:1.15rem; font-weight:800; color:#fff; display:flex; align-items:center; gap:0.5rem;">✏️ Monitor & Icon bearbeiten</h2>
|
||||
<button type="button" onclick="closeEditMonitorModal()" style="background:none; border:none; color:var(--text-muted); font-size:1.25rem; cursor:pointer;">×</button>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="index.php#sub-watchdog-hierarchy" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="edit_watchdog_monitor">
|
||||
<input type="hidden" name="old_source" id="monEditOldSource">
|
||||
<input type="hidden" name="icon" id="monEditIconSelected" value="">
|
||||
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Monitor Name (source)</label>
|
||||
<input type="text" name="new_source" id="monEditNewSource" class="form-input" required>
|
||||
<input type="text" name="new_source" id="monEditNewSource" class="form-input" required placeholder="ClawdDotNet Test">
|
||||
</div>
|
||||
|
||||
<!-- Icon Grid Selector (Visual Preview) -->
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Typ</label>
|
||||
<select name="type" id="monEditType" class="form-input">
|
||||
<option value="host">Host / Server (Windows / Linux)</option>
|
||||
<option value="heartbeat">Dienst / Worker (Heartbeat)</option>
|
||||
<option value="hypervisor_node">Hypervisor Node (Proxmox)</option>
|
||||
<option value="guest">Guest VM / Container</option>
|
||||
</select>
|
||||
<label class="form-label">Icon wählen (Visuelle Vorschau):</label>
|
||||
<div class="icon-grid">
|
||||
<button type="button" class="icon-option-btn selected" data-icon="" onclick="selectIcon(this, '')">🪄 Auto-Detect</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/proxmox.svg" onclick="selectIcon(this, 'assets/icons/proxmox.svg')"><img src="assets/icons/proxmox.svg" width="16" height="16"> Proxmox</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/windows.svg" onclick="selectIcon(this, 'assets/icons/windows.svg')"><img src="assets/icons/windows.svg" width="16" height="16"> Windows</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/linux.svg" onclick="selectIcon(this, 'assets/icons/linux.svg')"><img src="assets/icons/linux.svg" width="16" height="16"> Linux</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/mysql.svg" onclick="selectIcon(this, 'assets/icons/mysql.svg')"><img src="assets/icons/mysql.svg" width="16" height="16"> MySQL</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/docker.svg" onclick="selectIcon(this, 'assets/icons/docker.svg')"><img src="assets/icons/docker.svg" width="16" height="16"> Docker</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/nginx.svg" onclick="selectIcon(this, 'assets/icons/nginx.svg')"><img src="assets/icons/nginx.svg" width="16" height="16"> Nginx</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/redis.svg" onclick="selectIcon(this, 'assets/icons/redis.svg')"><img src="assets/icons/redis.svg" width="16" height="16"> Redis</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/python.svg" onclick="selectIcon(this, 'assets/icons/python.svg')"><img src="assets/icons/python.svg" width="16" height="16"> Python</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/php.svg" onclick="selectIcon(this, 'assets/icons/php.svg')"><img src="assets/icons/php.svg" width="16" height="16"> PHP</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/node.svg" onclick="selectIcon(this, 'assets/icons/node.svg')"><img src="assets/icons/node.svg" width="16" height="16"> Node.js</button>
|
||||
<button type="button" class="icon-option-btn" data-icon="assets/icons/server.svg" onclick="selectIcon(this, 'assets/icons/server.svg')"><img src="assets/icons/server.svg" width="16" height="16"> Generischer</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Übergeordnete Entität (Parent Source)</label>
|
||||
<label class="form-label">Oder eigenes Icon hochladen (SVG, PNG, JPG, WEBP)</label>
|
||||
<input type="file" name="custom_icon_file" class="form-input" accept=".svg,.png,.jpg,.jpeg,.webp">
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Web-Backend URL (z. B. https://pve.example.com:8006)</label>
|
||||
<input type="url" name="url" id="monEditUrl" class="form-input" placeholder="https://...">
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Übergeordneter Host / Hypervisor (Parent)</label>
|
||||
<select name="parent_source" id="monEditParent" class="form-input">
|
||||
<option value="">-- Keine (Top Level) --</option>
|
||||
<option value="">-- Keine (Top-Level) --</option>
|
||||
<?php foreach ($monitors as $pm): ?>
|
||||
<?php if ($pm['type'] !== 'heartbeat'): ?>
|
||||
<option value="<?= htmlspecialchars($pm['source']) ?>"><?= htmlspecialchars($pm['source']) ?> (<?= htmlspecialchars($pm['type']) ?>)</option>
|
||||
@@ -903,28 +992,48 @@ $baseUrl = $protocol . '://' . $host;
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Gruppe</label>
|
||||
<input type="text" name="group_key" id="monEditGroup" class="form-input">
|
||||
<input type="text" name="group_key" id="monEditGroup" class="form-input" placeholder="Applications">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Betriebssystem / OS</label>
|
||||
<input type="text" name="os" id="monEditOs" class="form-input">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Intervall (Sekunden)</label>
|
||||
<input type="number" name="interval" id="monEditInterval" class="form-input" value="60">
|
||||
<label class="form-label">Erwartetes Intervall (Sekunden)</label>
|
||||
<input type="number" name="expected_interval_sec" id="monEditInterval" class="form-input" value="60">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:flex-end; gap:0.75rem; margin-top:1.25rem;">
|
||||
|
||||
<div class="form-group" style="margin-bottom:1rem;">
|
||||
<label class="form-label">Notizen / Dokumentation</label>
|
||||
<textarea name="notes" id="monEditNotes" class="form-input" rows="3" placeholder="Zusätzliche Notizen, IPs, Ports oder Ansprechpartner..."></textarea>
|
||||
</div>
|
||||
|
||||
<div style="background:rgba(255,255,255,0.03); border:1px solid var(--border-glass); border-radius:12px; padding:0.85rem 1rem; margin-bottom:1.25rem; display:flex; gap:0.75rem; align-items:flex-start;">
|
||||
<input type="checkbox" name="is_muted" id="monEditMuted" style="margin-top:3px;">
|
||||
<div>
|
||||
<label for="monEditMuted" style="font-weight:700; font-size:0.85rem; color:#fff; cursor:pointer;">🔕 Warnungen & Alarme stummschalten (Dev-Instanz / Wartung)</label>
|
||||
<div style="font-size:0.75rem; color:var(--text-muted); margin-top:2px;">Aktivieren für Dev- oder Test-Systeme. Der Monitor bleibt sichtbar, sendet aber keine Telegram-/Matrix-Alarme bei Ausfällen.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="triggerDeleteMonitorFromModal()">🗑️ Monitor Löschen</button>
|
||||
<div style="display:flex; gap:0.5rem;">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeEditMonitorModal()">Abbrechen</button>
|
||||
<button type="submit" class="btn">Änderungen Speichern</button>
|
||||
<button type="submit" class="btn" style="background:#F2622E;">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden delete form for modal trigger -->
|
||||
<form id="hiddenDeleteMonitorForm" method="POST" action="index.php#sub-watchdog-hierarchy" style="display:none">
|
||||
<input type="hidden" name="action" value="delete_watchdog_monitor">
|
||||
<input type="hidden" name="source" id="hiddenDeleteSource">
|
||||
</form>
|
||||
|
||||
<!-- ================= MODULE 2: LIZENZEN ================= -->
|
||||
<div id="tab-license" class="tab-content">
|
||||
|
||||
@@ -1217,20 +1326,37 @@ $baseUrl = $protocol . '://' . $host;
|
||||
<div class="stat-value" style="color:var(--danger);"><?= $monitorsDown ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gruppen-Übersicht Panel (Matching Screenshot 2) -->
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">📁 Gruppen-Übersicht</h2></div>
|
||||
<div class="group-cards-grid">
|
||||
<?php foreach ($groupStats as $gName => $gData): ?>
|
||||
<?php
|
||||
$isAllOk = ($gData['ok'] === $gData['total']);
|
||||
$pillClass = $isAllOk ? 'badge-up' : 'badge-down';
|
||||
?>
|
||||
<div class="group-card">
|
||||
<span class="group-card-name"><?= htmlspecialchars($gName) ?></span>
|
||||
<span class="badge <?= $pillClass ?>"><?= $gData['ok'] ?>/<?= $gData['total'] ?> OK</span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtab: Watchdog Hierarchie & Tree View (3 Ebenen Support & Direct Editing) -->
|
||||
<!-- Subtab: Watchdog Hierarchie & Tree View (Exact requested columns & Edit modal button) -->
|
||||
<div id="sub-watchdog-hierarchy" class="subtab-content">
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">🌳 Proxmox, Host & Worker System-Hierarchie (3 Ebenen)</h2></div>
|
||||
<div class="card-header"><h2 class="card-title">🌳 Watchdog System-Hierarchie</h2></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hierarchie / Entity Source</th>
|
||||
<th>Name</th>
|
||||
<th>Typ</th>
|
||||
<th>Parent Entity</th>
|
||||
<th>Agent Token</th>
|
||||
<th>Status</th>
|
||||
<th>Zuletzt gesehen</th>
|
||||
<th>Kurz-Info / Message</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1240,55 +1366,24 @@ $baseUrl = $protocol . '://' . $host;
|
||||
$depth = (int)$m['depth'];
|
||||
$indentPx = $depth * 24;
|
||||
$isChild = ($depth > 0);
|
||||
$hasToken = isset($tokensBySource[$m['source']]);
|
||||
$tokenData = $hasToken ? $tokensBySource[$m['source']] : null;
|
||||
$iconUrl = getMonitorIconUrl($m['icon']??null, $m['source']??null, $m['os']??null, $m['type']??null);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="display:flex; align-items:center; gap:6px; margin-left:<?= $indentPx ?>px; <?= $isChild ? 'border-left:2px solid var(--primary); padding-left:8px;' : '' ?>">
|
||||
<div style="display:flex; align-items:center; gap:8px; margin-left:<?= $indentPx ?>px; <?= $isChild ? 'border-left:2px solid var(--primary); padding-left:8px;' : '' ?>">
|
||||
<?php if ($isChild): ?>
|
||||
<span style="color:var(--primary); font-family:'Roboto Mono'; font-size:13px; font-weight:bold"><?= $m['is_last'] ? '└─' : '├─' ?></span>
|
||||
<?php endif; ?>
|
||||
<img src="<?= htmlspecialchars($iconUrl) ?>" width="18" height="18" alt="icon">
|
||||
<strong><?= htmlspecialchars($m['source']) ?></strong>
|
||||
</div>
|
||||
</td>
|
||||
<td><code><?= htmlspecialchars($m['type']) ?></code></td>
|
||||
<td>
|
||||
<form method="POST" action="index.php#sub-watchdog-hierarchy" style="display:flex; gap:0.35rem;">
|
||||
<input type="hidden" name="action" value="link_entity">
|
||||
<input type="hidden" name="source" value="<?= htmlspecialchars($m['source']) ?>">
|
||||
<select name="parent_source" class="form-input" style="padding:0.25rem 0.5rem; font-size:0.75rem;" onchange="this.form.submit()">
|
||||
<option value="">-- Keine (Top Level) --</option>
|
||||
<?php foreach ($monitors as $pCandidate): ?>
|
||||
<?php if ($pCandidate['source'] !== $m['source'] && $pCandidate['type'] !== 'heartbeat'): ?>
|
||||
<option value="<?= htmlspecialchars($pCandidate['source']) ?>" <?= $m['parent_source'] === $pCandidate['source'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($pCandidate['source']) ?> (<?= htmlspecialchars($pCandidate['type']) ?>)
|
||||
</option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($hasToken): ?>
|
||||
<code style="font-size:0.75rem; color:var(--success);"><?= htmlspecialchars(substr($tokenData['raw_token'], 0, 10)) ?>...</code>
|
||||
<?php else: ?>
|
||||
<form method="POST" action="index.php#sub-watchdog-hierarchy" style="display:inline">
|
||||
<input type="hidden" name="action" value="create_agent_token">
|
||||
<input type="hidden" name="token_source" value="<?= htmlspecialchars($m['source']) ?>">
|
||||
<input type="hidden" name="token_name" value="Token for <?= htmlspecialchars($m['source']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">🔑 Token Erstellen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="badge badge-<?= $m['state'] === 'up' ? 'up' : 'down' ?>"><?= strtoupper($m['state']) ?></span></td>
|
||||
<td><?= htmlspecialchars($m['last_seen_utc'] ?? 'Nie') ?></td>
|
||||
<td><span style="color:var(--text-muted); font-size:0.8rem;"><?= htmlspecialchars($m['last_message'] ?? '-') ?></span></td>
|
||||
<td style="display:flex; gap:0.35rem;">
|
||||
<button class="btn btn-sm btn-secondary" onclick="openEditMonitorModal(<?= htmlspecialchars(json_encode($m)) ?>)">✏️ Edit</button>
|
||||
<form method="POST" action="index.php#sub-watchdog-hierarchy" onsubmit="return confirm('Monitor \'<?= htmlspecialchars($m['source']) ?>\' wirklich löschen?');" style="display:inline">
|
||||
<input type="hidden" name="action" value="delete_watchdog_monitor">
|
||||
<input type="hidden" name="source" value="<?= htmlspecialchars($m['source']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger">🗑️</button>
|
||||
</form>
|
||||
<button class="btn btn-sm btn-secondary" onclick="openEditMonitorModal(<?= htmlspecialchars(json_encode($m)) ?>)">✏️ Bearbeiten</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
@@ -1350,62 +1445,6 @@ $baseUrl = $protocol . '://' . $host;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtab: Watchdog Uptime & Full CRUD -->
|
||||
<div id="sub-watchdog-uptime" class="subtab-content">
|
||||
<div class="card">
|
||||
<div class="card-header"><h2 class="card-title">⏱️ Uptime Monitor & Vollständige Verwaltung</h2></div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Monitor Source</th>
|
||||
<th>Typ</th>
|
||||
<th>Parent</th>
|
||||
<th>Agent Token</th>
|
||||
<th>Status</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($monitors as $m): ?>
|
||||
<?php
|
||||
$hasToken = isset($tokensBySource[$m['source']]);
|
||||
$tokenData = $hasToken ? $tokensBySource[$m['source']] : null;
|
||||
?>
|
||||
<tr>
|
||||
<td><strong><?= htmlspecialchars($m['source']) ?></strong></td>
|
||||
<td><code><?= htmlspecialchars($m['type']) ?></code></td>
|
||||
<td><?= htmlspecialchars($m['parent_source'] ?? '-') ?></td>
|
||||
<td>
|
||||
<?php if ($hasToken): ?>
|
||||
<code style="font-size:0.75rem; color:var(--success);"><?= htmlspecialchars(substr($tokenData['raw_token'], 0, 10)) ?>...</code>
|
||||
<?php else: ?>
|
||||
<form method="POST" action="index.php#sub-watchdog-uptime" style="display:inline">
|
||||
<input type="hidden" name="action" value="create_agent_token">
|
||||
<input type="hidden" name="token_source" value="<?= htmlspecialchars($m['source']) ?>">
|
||||
<input type="hidden" name="token_name" value="Token for <?= htmlspecialchars($m['source']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-secondary">🔑 Token Erstellen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="badge badge-<?= $m['state'] === 'up' ? 'up' : 'down' ?>"><?= strtoupper($m['state']) ?></span></td>
|
||||
<td style="display:flex; gap:0.35rem;">
|
||||
<button class="btn btn-sm btn-secondary" onclick="openEditMonitorModal(<?= htmlspecialchars(json_encode($m)) ?>)">✏️ Bearbeiten</button>
|
||||
<a href="index.php?action=download_agent&source=<?= urlencode($m['source']) ?>&os=windows" class="btn btn-sm btn-secondary">.ps1</a>
|
||||
<a href="index.php?action=download_agent&source=<?= urlencode($m['source']) ?>&os=linux" class="btn btn-sm btn-secondary">.sh</a>
|
||||
|
||||
<form method="POST" action="index.php#sub-watchdog-uptime" onsubmit="return confirm('Monitor \'<?= htmlspecialchars($m['source']) ?>\' wirklich löschen?');" style="display:inline">
|
||||
<input type="hidden" name="action" value="delete_watchdog_monitor">
|
||||
<input type="hidden" name="source" value="<?= htmlspecialchars($m['source']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger">🗑️</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtab: Watchdog Event Log -->
|
||||
<div id="sub-watchdog-eventlog" class="subtab-content">
|
||||
<div class="card">
|
||||
@@ -1659,9 +1698,8 @@ $baseUrl = $protocol . '://' . $host;
|
||||
],
|
||||
'watchdog': [
|
||||
{ id: 'sub-watchdog-dashboard', label: '📊 Dashboard', active: true },
|
||||
{ id: 'sub-watchdog-hierarchy', label: '🌳 System-Hierarchie (3 Ebenen)' },
|
||||
{ id: 'sub-watchdog-hierarchy', label: '🌳 System-Hierarchie' },
|
||||
{ id: 'sub-watchdog-add', label: '➕ Monitor Hinzufügen' },
|
||||
{ id: 'sub-watchdog-uptime', label: '⏱️ Uptime & CRUD' },
|
||||
{ id: 'sub-watchdog-eventlog', label: '📜 Event-Log' },
|
||||
{ id: 'sub-watchdog-tokens', label: '🔑 Agent-Tokens' }
|
||||
],
|
||||
@@ -1801,15 +1839,35 @@ $baseUrl = $protocol . '://' . $host;
|
||||
document.getElementById('deleteProjectModal').classList.remove('active');
|
||||
}
|
||||
|
||||
// Monitor Editing Functions
|
||||
// Monitor Icon Selector (Matching Screenshot 1)
|
||||
function selectIcon(btnEl, iconVal) {
|
||||
document.querySelectorAll('.icon-option-btn').forEach(b => b.classList.remove('selected'));
|
||||
btnEl.classList.add('selected');
|
||||
document.getElementById('monEditIconSelected').value = iconVal;
|
||||
}
|
||||
|
||||
// Rich Monitor Editing Modal Functions (Screenshot 1)
|
||||
function openEditMonitorModal(m) {
|
||||
document.getElementById('monEditOldSource').value = m.source;
|
||||
document.getElementById('monEditNewSource').value = m.source;
|
||||
document.getElementById('monEditType').value = m.type || 'host';
|
||||
document.getElementById('monEditParent').value = m.parent_source || '';
|
||||
document.getElementById('monEditGroup').value = m.group_key || 'Default';
|
||||
document.getElementById('monEditOs').value = m.os || '';
|
||||
document.getElementById('monEditGroup').value = m.group_key || 'Applications';
|
||||
document.getElementById('monEditInterval').value = m.expected_interval_sec || 60;
|
||||
document.getElementById('monEditNotes').value = m.notes || '';
|
||||
document.getElementById('monEditUrl').value = m.url || '';
|
||||
document.getElementById('monEditMuted').checked = parseInt(m.is_muted) === 1;
|
||||
|
||||
// Highlight selected icon preset or auto
|
||||
const iconVal = m.icon || '';
|
||||
document.getElementById('monEditIconSelected').value = iconVal;
|
||||
document.querySelectorAll('.icon-option-btn').forEach(b => {
|
||||
b.classList.remove('selected');
|
||||
if (b.getAttribute('data-icon') === iconVal) {
|
||||
b.classList.add('selected');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('editMonitorModal').classList.add('active');
|
||||
}
|
||||
|
||||
@@ -1817,6 +1875,14 @@ $baseUrl = $protocol . '://' . $host;
|
||||
document.getElementById('editMonitorModal').classList.remove('active');
|
||||
}
|
||||
|
||||
function triggerDeleteMonitorFromModal() {
|
||||
const source = document.getElementById('monEditOldSource').value;
|
||||
if (confirm(`Möchtest du den Monitor '${source}' wirklich unwiderruflich löschen?`)) {
|
||||
document.getElementById('hiddenDeleteSource').value = source;
|
||||
document.getElementById('hiddenDeleteMonitorForm').submit();
|
||||
}
|
||||
}
|
||||
|
||||
// License Editing Functions
|
||||
function openEditLicense(l) {
|
||||
switchMainTab('license', document.querySelector('.nav-link[onclick*="license"]'));
|
||||
|
||||