Files
crowdsec-dashy/web/templates/layouts/base.html
T

111 lines
4.2 KiB
HTML

{{define "base"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}} — CrowdSec Dashy</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;600&display=swap">
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<div style="display:flex;height:100vh;overflow:hidden">
<aside class="sidebar" id="sidebar">
<div class="sidebar-logo">
<div class="logo-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#00d4ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div>
<span class="logo-name">CrowdSec</span>
<span class="logo-sub">Dashy</span>
</div>
</div>
<nav class="sidebar-nav">
{{range .Nav}}
{{if .Divider}}<div class="nav-divider"></div>{{end}}
<a href="{{.Path}}" class="nav-item{{if eq $.CurrentPath .Path}} nav-item--active{{end}}">
<span class="nav-icon">{{safeHTML .Icon}}</span>
{{.Label}}
</a>
{{end}}
</nav>
<div class="sidebar-footer">
{{if .CLIAvailable}}
<div class="cli-badge cli-badge--ok">cscli: available</div>
{{else}}
<div class="cli-badge cli-badge--warn">cscli: unavailable</div>
{{end}}
<form method="POST" action="/logout" style="margin-top:10px">
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
<button type="submit" class="btn-ghost-sm" style="width:100%;text-align:center;padding:6px 0">Sign out</button>
</form>
</div>
</aside>
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<div style="display:flex;flex-direction:column;flex:1;overflow:hidden">
<header class="topbar">
<button class="topbar-menu-btn" id="menu-btn" aria-label="Toggle sidebar">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
</button>
<div class="topbar-breadcrumb">
<span class="topbar-page">{{.Title}}</span>
</div>
<div id="health-badge" class="status-pill status-pill--loading">
<span class="status-dot"></span>checking
</div>
</header>
{{if .Flash.Message}}
<div class="flash flash--{{.Flash.Type}}" id="flash-msg">
<span class="flash-text">{{.Flash.Message}}</span>
<button class="flash-close" onclick="document.getElementById('flash-msg').remove()">dismiss</button>
</div>
{{end}}
<main style="flex:1;overflow-y:auto;padding:20px">
{{template "content" .}}
</main>
</div>
</div>
<script src="/static/js/modal.js"></script>
<script>
(function() {
document.body.classList.add('ready');
var btn = document.getElementById('menu-btn');
var sb = document.getElementById('sidebar');
var ov = document.getElementById('sidebar-overlay');
if (btn) {
btn.addEventListener('click', function() { sb.classList.toggle('open'); ov.classList.toggle('open'); });
ov.addEventListener('click', function() { sb.classList.remove('open'); ov.classList.remove('open'); });
}
function checkHealth() {
var badge = document.getElementById('health-badge');
if (!badge) return;
fetch('/api/v1/health').then(function(r) { return r.json(); }).then(function(d) {
if (d.healthy) {
badge.className = 'status-pill status-pill--healthy';
badge.innerHTML = '<span class="status-dot"></span>healthy';
} else {
badge.className = 'status-pill status-pill--unhealthy';
badge.innerHTML = '<span class="status-dot"></span>unhealthy';
}
}).catch(function() {
badge.className = 'status-pill status-pill--unhealthy';
badge.innerHTML = '<span class="status-dot"></span>offline';
});
}
checkHealth();
setInterval(checkHealth, 30000);
})();
</script>
{{block "scripts" .}}{{end}}
</body>
</html>
{{end}}