646 lines
37 KiB
HTML
646 lines
37 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
<title>CluPilot — Dashboard</title>
|
|
<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:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root{
|
|
--fg:#1a1a22;
|
|
--muted:#5c5c6b;
|
|
--muted-2:#9596a6;
|
|
--accent:#3b6ff2;
|
|
--accent-2:#7c5cff;
|
|
--success:#15a06a;
|
|
--warning:#c2820f;
|
|
--danger:#db3b3b;
|
|
--success-soft:rgba(21,160,106,0.12);
|
|
--warning-soft:rgba(194,130,15,0.12);
|
|
--danger-soft:rgba(219,59,59,0.12);
|
|
--accent-soft:rgba(59,111,242,0.12);
|
|
|
|
/* glass */
|
|
--glass:rgba(255,255,255,0.55);
|
|
--glass-strong:rgba(255,255,255,0.72);
|
|
--glass-soft:rgba(255,255,255,0.38);
|
|
--glass-border:rgba(255,255,255,0.75);
|
|
--glass-shadow:0 10px 34px -10px rgba(40,52,92,0.20), 0 2px 8px -2px rgba(40,52,92,0.08);
|
|
--glass-inset:inset 0 1px 0 rgba(255,255,255,0.95), inset 0 -1px 0 rgba(255,255,255,0.25);
|
|
--hairline:rgba(30,35,60,0.08);
|
|
|
|
--radius:20px;
|
|
--radius-sm:13px;
|
|
--radius-xs:9px;
|
|
--mono:'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
}
|
|
*{box-sizing:border-box}
|
|
html,body{margin:0;padding:0;color:var(--fg);font-family:'Inter',system-ui,-apple-system,sans-serif;font-size:13px;line-height:1.45;-webkit-font-smoothing:antialiased}
|
|
a{color:inherit;text-decoration:none}
|
|
button{font-family:inherit;color:inherit;background:none;border:0;cursor:pointer}
|
|
|
|
/* ---------- Aurora background ---------- */
|
|
body{
|
|
min-height:100vh;
|
|
background:
|
|
radial-gradient(60% 55% at 12% 8%, rgba(99,140,255,0.30), transparent 60%),
|
|
radial-gradient(55% 50% at 88% 0%, rgba(140,108,255,0.26), transparent 58%),
|
|
radial-gradient(50% 60% at 78% 92%, rgba(64,196,210,0.22), transparent 60%),
|
|
radial-gradient(45% 50% at 25% 100%, rgba(120,170,255,0.20), transparent 60%),
|
|
linear-gradient(160deg,#eef1fb 0%,#f4f3fb 50%,#eaf2f8 100%);
|
|
background-attachment:fixed;
|
|
}
|
|
|
|
/* ---------- App layout ---------- */
|
|
.app{display:grid;grid-template-columns:236px 1fr;min-height:100vh;gap:0}
|
|
|
|
/* ---------- Glass primitive ---------- */
|
|
.glass{
|
|
background:var(--glass);
|
|
backdrop-filter:blur(26px) saturate(170%);
|
|
-webkit-backdrop-filter:blur(26px) saturate(170%);
|
|
border:1px solid var(--glass-border);
|
|
box-shadow:var(--glass-shadow), var(--glass-inset);
|
|
}
|
|
|
|
/* ---------- Sidebar ---------- */
|
|
.sidebar{
|
|
position:sticky;top:0;height:100vh;margin:14px 0 14px 14px;
|
|
border-radius:var(--radius);
|
|
display:flex;flex-direction:column;padding:18px 14px;gap:18px;
|
|
background:var(--glass-strong);
|
|
backdrop-filter:blur(30px) saturate(180%);
|
|
-webkit-backdrop-filter:blur(30px) saturate(180%);
|
|
border:1px solid var(--glass-border);
|
|
box-shadow:var(--glass-shadow), var(--glass-inset);
|
|
}
|
|
.brand{display:flex;align-items:center;gap:10px;padding:2px 4px}
|
|
.brand-mark{width:30px;height:30px;border-radius:9px;background:linear-gradient(135deg,#5a8bff 0%,#8a63ff 100%);position:relative;display:grid;place-items:center;box-shadow:0 0 0 1px rgba(255,255,255,0.4) inset, 0 6px 16px -4px rgba(90,139,255,0.6)}
|
|
.brand-mark::before{content:"";width:11px;height:11px;border-radius:50%;background:rgba(255,255,255,0.96);box-shadow:inset 0 0 0 2px #5a8bff}
|
|
.brand-name{font-weight:600;font-size:15px;letter-spacing:-0.01em}
|
|
.brand-name span{color:var(--muted-2);font-weight:500;margin-left:5px;font-size:11px}
|
|
|
|
.ws-switch{display:flex;align-items:center;gap:10px;padding:9px 10px;border:1px solid var(--hairline);border-radius:var(--radius-sm);background:var(--glass-soft);cursor:pointer;transition:background .18s, border-color .18s}
|
|
.ws-switch:hover{background:rgba(255,255,255,0.6);border-color:var(--glass-border)}
|
|
.ws-avatar{width:24px;height:24px;border-radius:7px;background:linear-gradient(135deg,#3ecf8e,#1f8f60);display:grid;place-items:center;color:#fff;font-weight:700;font-size:11px;flex:none;box-shadow:0 2px 6px -1px rgba(31,143,96,0.5)}
|
|
.ws-meta{flex:1;min-width:0}
|
|
.ws-name{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
.ws-plan{font-size:10.5px;color:var(--muted);font-family:var(--mono)}
|
|
.ws-chev{color:var(--muted-2)}
|
|
|
|
.nav{display:flex;flex-direction:column;gap:2px;flex:1}
|
|
.nav-title{font-size:10px;text-transform:uppercase;letter-spacing:0.09em;color:var(--muted-2);padding:14px 10px 5px;font-weight:600}
|
|
.nav-item{display:flex;align-items:center;gap:11px;padding:9px 11px;border-radius:var(--radius-xs);color:#3a3a47;cursor:pointer;font-size:13px;font-weight:500;transition:background .15s,color .15s,box-shadow .15s;position:relative}
|
|
.nav-item:hover{background:rgba(255,255,255,0.55);color:var(--fg)}
|
|
.nav-item.active{
|
|
color:var(--fg);
|
|
background:linear-gradient(180deg,rgba(255,255,255,0.92),rgba(255,255,255,0.66));
|
|
box-shadow:0 4px 14px -6px rgba(40,52,92,0.30), inset 0 1px 0 rgba(255,255,255,0.9);
|
|
}
|
|
.nav-item .ico{width:17px;height:17px;color:var(--muted);flex:none}
|
|
.nav-item.active .ico{color:var(--accent)}
|
|
.nav-item .count{margin-left:auto;font-size:10.5px;font-family:var(--mono);color:var(--muted);background:rgba(255,255,255,0.7);padding:1px 7px;border-radius:10px;border:1px solid var(--hairline)}
|
|
|
|
.plan-box{margin-top:8px;padding:13px;border:1px solid var(--glass-border);border-radius:var(--radius-sm);background:linear-gradient(180deg,rgba(255,255,255,0.7),rgba(255,255,255,0.4));position:relative;overflow:hidden}
|
|
.plan-row{display:flex;align-items:center;justify-content:space-between;font-size:12px}
|
|
.plan-name{font-weight:600}
|
|
.plan-tag{font-family:var(--mono);font-size:10px;color:var(--accent);background:var(--accent-soft);padding:2px 7px;border-radius:5px;border:1px solid rgba(59,111,242,0.25)}
|
|
.plan-meter{margin-top:10px;height:6px;border-radius:4px;background:rgba(30,35,60,0.08);overflow:hidden}
|
|
.plan-meter span{display:block;height:100%;width:62%;background:linear-gradient(90deg,#3b6ff2,#7c5cff);border-radius:4px}
|
|
.plan-stats{display:flex;justify-content:space-between;margin-top:7px;font-size:10.5px;color:var(--muted);font-family:var(--mono)}
|
|
|
|
/* ---------- Main ---------- */
|
|
.main{display:flex;flex-direction:column;min-width:0;padding:14px 14px 0}
|
|
|
|
/* Topbar */
|
|
.topbar{
|
|
position:sticky;top:14px;z-index:20;display:flex;align-items:center;gap:14px;
|
|
padding:12px 18px;border-radius:var(--radius);margin-bottom:18px;
|
|
background:var(--glass-strong);
|
|
backdrop-filter:blur(30px) saturate(180%);
|
|
-webkit-backdrop-filter:blur(30px) saturate(180%);
|
|
border:1px solid var(--glass-border);
|
|
box-shadow:var(--glass-shadow), var(--glass-inset);
|
|
}
|
|
.page-title{font-size:16px;font-weight:600;letter-spacing:-0.015em}
|
|
.status-chip{display:inline-flex;align-items:center;gap:7px;padding:4px 11px 4px 9px;border:1px solid rgba(21,160,106,0.28);background:var(--success-soft);color:var(--success);border-radius:20px;font-size:11px;font-weight:600;font-family:var(--mono)}
|
|
.pulse-dot{width:6px;height:6px;border-radius:50%;background:currentColor;position:relative;flex:none}
|
|
.pulse-dot::after{content:"";position:absolute;inset:-3px;border-radius:50%;background:currentColor;opacity:0.35;animation:pulse 1.8s ease-out infinite}
|
|
@keyframes pulse{0%{transform:scale(0.6);opacity:.5}100%{transform:scale(2.2);opacity:0}}
|
|
|
|
.top-spacer{flex:1}
|
|
.search{display:flex;align-items:center;gap:9px;padding:8px 12px;background:rgba(255,255,255,0.6);border:1px solid var(--hairline);border-radius:11px;min-width:260px;color:var(--muted);transition:border-color .15s,box-shadow .15s}
|
|
.search:focus-within{border-color:rgba(59,111,242,0.5);box-shadow:0 0 0 3px rgba(59,111,242,0.1)}
|
|
.search input{flex:1;background:none;border:0;outline:0;color:var(--fg);font-size:12.5px;font-family:inherit;min-width:0}
|
|
.search input::placeholder{color:var(--muted)}
|
|
.kbd{font-family:var(--mono);font-size:10.5px;color:var(--muted);padding:1px 6px;border:1px solid var(--hairline);border-radius:5px;background:rgba(255,255,255,0.5)}
|
|
|
|
.icon-btn{width:36px;height:36px;border:1px solid var(--hairline);border-radius:11px;display:grid;place-items:center;color:var(--muted);background:rgba(255,255,255,0.55);transition:all .15s;position:relative}
|
|
.icon-btn:hover{color:var(--fg);border-color:var(--glass-border);background:rgba(255,255,255,0.85)}
|
|
.icon-btn .badge{position:absolute;top:-4px;right:-4px;background:var(--danger);color:#fff;font-size:9.5px;font-weight:600;min-width:16px;height:16px;border-radius:8px;display:grid;place-items:center;padding:0 4px;border:2px solid #f1f2fb;font-family:var(--mono)}
|
|
|
|
.btn-primary{display:inline-flex;align-items:center;gap:7px;padding:9px 15px;background:linear-gradient(180deg,#4f7dff,#3b6ff2);color:#fff;border-radius:11px;font-weight:600;font-size:12.5px;transition:filter .15s;box-shadow:0 1px 0 rgba(255,255,255,0.3) inset, 0 6px 16px -5px rgba(59,111,242,0.65)}
|
|
.btn-primary:hover{filter:brightness(1.06)}
|
|
|
|
/* Content */
|
|
.content{display:flex;flex-direction:column;gap:18px;max-width:1500px;width:100%;margin:0 auto;padding-bottom:22px}
|
|
|
|
/* ---------- Metric tiles ---------- */
|
|
.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
|
|
.metric{
|
|
position:relative;border-radius:var(--radius);padding:18px 18px 16px;overflow:hidden;
|
|
background:var(--glass);
|
|
backdrop-filter:blur(22px) saturate(160%);
|
|
-webkit-backdrop-filter:blur(22px) saturate(160%);
|
|
border:1px solid var(--glass-border);
|
|
box-shadow:var(--glass-shadow), var(--glass-inset);
|
|
transition:transform .18s, box-shadow .18s;
|
|
}
|
|
.metric:hover{transform:translateY(-2px);box-shadow:0 16px 40px -12px rgba(40,52,92,0.26), var(--glass-inset)}
|
|
.metric::after{content:"";position:absolute;inset:0;border-radius:inherit;background:linear-gradient(135deg,rgba(255,255,255,0.5),transparent 42%);pointer-events:none}
|
|
.metric-top{display:flex;align-items:center;justify-content:space-between}
|
|
.metric-label{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:12px;font-weight:600}
|
|
.m-ico{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none;color:var(--accent);background:var(--accent-soft);border:1px solid rgba(59,111,242,0.2)}
|
|
.m-ico svg{width:15px;height:15px}
|
|
.m-ico.success{color:var(--success);background:var(--success-soft);border-color:rgba(21,160,106,0.22)}
|
|
.m-ico.warning{color:var(--warning);background:var(--warning-soft);border-color:rgba(194,130,15,0.22)}
|
|
.m-ico.danger{color:var(--danger);background:var(--danger-soft);border-color:rgba(219,59,59,0.22)}
|
|
.metric-value{margin-top:14px;font-family:var(--mono);font-size:30px;font-weight:600;letter-spacing:-0.02em;line-height:1;position:relative}
|
|
.metric-value .sub{font-size:15px;color:var(--muted-2);font-weight:500;margin-left:3px}
|
|
.metric-delta{margin-top:9px;font-size:11.5px;display:flex;align-items:center;gap:6px;color:var(--muted);position:relative}
|
|
.chg{font-family:var(--mono);font-weight:600}
|
|
.chg.up{color:var(--success)} .chg.down{color:var(--danger)} .chg.warn{color:var(--warning)}
|
|
|
|
/* ---------- Card ---------- */
|
|
.card{
|
|
border-radius:var(--radius);overflow:hidden;
|
|
background:var(--glass);
|
|
backdrop-filter:blur(24px) saturate(165%);
|
|
-webkit-backdrop-filter:blur(24px) saturate(165%);
|
|
border:1px solid var(--glass-border);
|
|
box-shadow:var(--glass-shadow), var(--glass-inset);
|
|
}
|
|
.card-head{display:flex;align-items:center;justify-content:space-between;padding:15px 18px 14px;border-bottom:1px solid var(--hairline)}
|
|
.card-head h3{margin:0;font-size:13.5px;font-weight:600;letter-spacing:-0.005em;display:flex;align-items:center;gap:9px}
|
|
.card-head h3 .hico{color:var(--muted)}
|
|
.card-head .meta{font-size:11.5px;color:var(--muted);font-family:var(--mono)}
|
|
.live-tag{font-size:10.5px;color:var(--success);font-family:var(--mono);display:inline-flex;align-items:center;gap:5px;font-weight:600}
|
|
.card-tabs{display:flex;gap:2px;background:rgba(255,255,255,0.45);border:1px solid var(--hairline);border-radius:9px;padding:2px}
|
|
.card-tabs button{padding:5px 11px;font-size:11.5px;color:var(--muted);border-radius:7px;transition:all .15s;font-weight:500}
|
|
.card-tabs button:hover{color:var(--fg)}
|
|
.card-tabs button.active{color:var(--fg);background:#fff;box-shadow:0 2px 6px -2px rgba(40,52,92,0.2)}
|
|
.ghost-btn{padding:6px 11px;font-size:11.5px;color:var(--muted);border:1px solid var(--hairline);border-radius:9px;background:rgba(255,255,255,0.5);transition:all .15s;font-weight:500}
|
|
.ghost-btn:hover{color:var(--fg);border-color:var(--glass-border);background:rgba(255,255,255,0.85)}
|
|
|
|
/* ---------- 2-col row ---------- */
|
|
.row2{display:grid;grid-template-columns:1.55fr 1fr;gap:18px}
|
|
|
|
/* Chart */
|
|
.chart-wrap{padding:16px 18px 10px}
|
|
.chart-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:8px}
|
|
.chart-figure{font-family:var(--mono);font-size:26px;font-weight:600;letter-spacing:-0.02em}
|
|
.chart-figure .unit{font-size:14px;color:var(--muted-2);margin-left:4px;font-weight:500}
|
|
.chart-sub{font-size:11.5px;color:var(--muted);margin-top:3px}
|
|
.legend{display:flex;gap:14px;font-size:11px;color:var(--muted);font-family:var(--mono)}
|
|
.legend i{display:inline-block;width:8px;height:8px;border-radius:3px;margin-right:5px;vertical-align:middle}
|
|
.svg-chart{width:100%;height:172px;display:block}
|
|
.chart-axis{font-family:var(--mono);font-size:9.5px;fill:var(--muted-2)}
|
|
|
|
/* Activity feed */
|
|
.feed{padding:4px 0;max-height:340px;overflow-y:auto}
|
|
.feed-row{display:flex;gap:12px;padding:11px 18px;align-items:flex-start;transition:background .12s}
|
|
.feed-row:hover{background:rgba(255,255,255,0.45)}
|
|
.feed-ico{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;flex:none;color:var(--muted);background:rgba(255,255,255,0.6);border:1px solid var(--hairline)}
|
|
.feed-ico.success{color:var(--success);background:var(--success-soft);border-color:rgba(21,160,106,0.22)}
|
|
.feed-ico.warning{color:var(--warning);background:var(--warning-soft);border-color:rgba(194,130,15,0.22)}
|
|
.feed-ico.danger{color:var(--danger);background:var(--danger-soft);border-color:rgba(219,59,59,0.22)}
|
|
.feed-ico.accent{color:var(--accent);background:var(--accent-soft);border-color:rgba(59,111,242,0.22)}
|
|
.feed-body{flex:1;min-width:0;font-size:12.5px}
|
|
.feed-body b{font-weight:600;color:var(--fg)}
|
|
.feed-body .target{font-family:var(--mono);color:var(--fg);background:rgba(255,255,255,0.7);padding:1px 6px;border-radius:5px;font-size:11px;border:1px solid var(--hairline)}
|
|
.feed-meta{color:var(--muted-2);font-size:10.5px;margin-top:3px;font-family:var(--mono)}
|
|
|
|
/* ---------- Sites table ---------- */
|
|
.table{width:100%;border-collapse:collapse;font-size:12.5px}
|
|
.table th{text-align:left;font-weight:600;color:var(--muted-2);font-size:10.5px;letter-spacing:0.05em;text-transform:uppercase;padding:11px 18px;border-bottom:1px solid var(--hairline)}
|
|
.table th.num{text-align:right}
|
|
.table td{padding:13px 18px;border-bottom:1px solid var(--hairline);vertical-align:middle}
|
|
.table tbody tr{transition:background .12s}
|
|
.table tbody tr:hover{background:rgba(255,255,255,0.5)}
|
|
.table tbody tr:last-child td{border-bottom:0}
|
|
.table td.num{text-align:right;font-family:var(--mono)}
|
|
|
|
.site-cell{display:flex;align-items:center;gap:11px;min-width:0}
|
|
.favicon{width:30px;height:30px;border-radius:8px;display:grid;place-items:center;font-weight:600;font-size:12px;color:#fff;flex:none;font-family:var(--mono);box-shadow:0 3px 8px -2px rgba(40,52,92,0.35)}
|
|
.site-name{font-weight:600;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}
|
|
.site-meta{font-family:var(--mono);font-size:10.5px;color:var(--muted-2)}
|
|
|
|
.pill{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;font-size:10.5px;font-weight:600;border-radius:7px;font-family:var(--mono);border:1px solid var(--hairline);background:rgba(255,255,255,0.6);color:var(--muted);line-height:1.4}
|
|
.pill.success{color:var(--success);background:var(--success-soft);border-color:rgba(21,160,106,0.25)}
|
|
.pill.warning{color:var(--warning);background:var(--warning-soft);border-color:rgba(194,130,15,0.25)}
|
|
.pill.danger{color:var(--danger);background:var(--danger-soft);border-color:rgba(219,59,59,0.25)}
|
|
|
|
.health{display:inline-flex;align-items:center;gap:8px}
|
|
.health-dot{width:8px;height:8px;border-radius:50%;flex:none;box-shadow:0 0 0 3px rgba(255,255,255,0.5)}
|
|
.health-dot.ok{background:var(--success)} .health-dot.warn{background:var(--warning)} .health-dot.bad{background:var(--danger)}
|
|
.health-label{font-size:11.5px;color:var(--muted)}
|
|
|
|
.row-actions{display:flex;gap:4px;justify-content:flex-end;opacity:0;transition:opacity .15s}
|
|
tr:hover .row-actions{opacity:1}
|
|
.row-actions button{width:28px;height:28px;border-radius:8px;color:var(--muted);display:grid;place-items:center;transition:all .15s}
|
|
.row-actions button:hover{color:var(--fg);background:rgba(255,255,255,0.85);box-shadow:0 2px 6px -2px rgba(40,52,92,0.2)}
|
|
|
|
.foot{text-align:center;color:var(--muted-2);font-size:11px;font-family:var(--mono);padding:4px 0 8px;letter-spacing:0.04em}
|
|
|
|
/* scrollbars */
|
|
.feed::-webkit-scrollbar{width:7px}
|
|
.feed::-webkit-scrollbar-thumb{background:rgba(30,35,60,0.14);border-radius:4px}
|
|
|
|
/* ---------- Responsive ---------- */
|
|
@media (max-width:1240px){
|
|
.metrics{grid-template-columns:repeat(2,1fr)}
|
|
.row2{grid-template-columns:1fr}
|
|
}
|
|
@media (max-width:920px){
|
|
.app{grid-template-columns:1fr}
|
|
.sidebar{display:none}
|
|
.main{padding:12px}
|
|
}
|
|
@media (max-width:640px){
|
|
.metrics{grid-template-columns:1fr}
|
|
.search{display:none}
|
|
.scroll-x{overflow-x:auto}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app">
|
|
<!-- ============ SIDEBAR ============ -->
|
|
<aside class="sidebar" data-screen-label="Sidebar">
|
|
<div class="brand">
|
|
<div class="brand-mark" aria-hidden="true"></div>
|
|
<div class="brand-name">CluPilot<span>v2.4</span></div>
|
|
</div>
|
|
|
|
<button class="ws-switch" aria-label="Workspace wechseln">
|
|
<div class="ws-avatar">AC</div>
|
|
<div class="ws-meta">
|
|
<div class="ws-name">Acme Cluster</div>
|
|
<div class="ws-plan">team · 14 user</div>
|
|
</div>
|
|
<svg class="ws-chev" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 9l4-4 4 4M8 15l4 4 4-4"/></svg>
|
|
</button>
|
|
|
|
<nav class="nav">
|
|
<a class="nav-item active" href="CluPilot Dashboard Glass.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="7" height="9" rx="1.5"/><rect x="14" y="3" width="7" height="5" rx="1.5"/><rect x="14" y="12" width="7" height="9" rx="1.5"/><rect x="3" y="16" width="7" height="5" rx="1.5"/></svg>
|
|
<span>Dashboard</span>
|
|
</a>
|
|
<a class="nav-item" href="Server.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="1.5"/><rect x="3" y="13" width="18" height="7" rx="1.5"/><circle cx="7" cy="7.5" r="0.7" fill="currentColor"/><circle cx="7" cy="16.5" r="0.7" fill="currentColor"/></svg>
|
|
<span>Server</span>
|
|
<span class="count">6</span>
|
|
</a>
|
|
<a class="nav-item" href="Sites.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
|
|
<span>Sites</span>
|
|
<span class="count">87</span>
|
|
</a>
|
|
<a class="nav-item" href="Backups.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 12c0 5-9 9-9 9s-9-4-9-9V5l9-3 9 3v7z"/><path d="M9 11l2 2 4-4"/></svg>
|
|
<span>Backups</span>
|
|
</a>
|
|
<a class="nav-item" href="Sicherheit.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7l8-4z"/></svg>
|
|
<span>Sicherheit</span>
|
|
<span class="count" style="color:var(--warning);background:var(--warning-soft);border-color:rgba(194,130,15,0.3)">3</span>
|
|
</a>
|
|
|
|
<div class="nav-title">Konto</div>
|
|
<a class="nav-item" href="Team.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8"/></svg>
|
|
<span>Team</span>
|
|
</a>
|
|
<a class="nav-item" href="Einstellungen.html">
|
|
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>
|
|
<span>Einstellungen</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<div class="plan-box">
|
|
<div class="plan-row">
|
|
<div class="plan-name">Team Plan</div>
|
|
<div class="plan-tag">PRO</div>
|
|
</div>
|
|
<div class="plan-meter"><span></span></div>
|
|
<div class="plan-stats">
|
|
<span>87 / 140 sites</span>
|
|
<span>62%</span>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- ============ MAIN ============ -->
|
|
<main class="main" data-screen-label="Dashboard">
|
|
<!-- Topbar -->
|
|
<div class="topbar">
|
|
<span class="page-title">Dashboard</span>
|
|
<span class="status-chip"><span class="pulse-dot"></span> Alle Systeme aktiv</span>
|
|
<div class="top-spacer"></div>
|
|
<div class="search">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
|
<input placeholder="Suchen…" />
|
|
<span class="kbd">⌘K</span>
|
|
</div>
|
|
<button class="icon-btn" aria-label="Benachrichtigungen">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9M10 21a2 2 0 0 0 4 0"/></svg>
|
|
<span class="badge">3</span>
|
|
</button>
|
|
<a class="btn-primary" href="Cluster anlegen.html">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6"><path d="M12 5v14M5 12h14"/></svg>
|
|
Cluster anlegen
|
|
</a>
|
|
</div>
|
|
|
|
<div class="content">
|
|
|
|
<!-- ============ METRICS ============ -->
|
|
<div class="metrics">
|
|
<div class="metric">
|
|
<div class="metric-top">
|
|
<div class="metric-label">Sites Online</div>
|
|
<div class="m-ico success">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M9 12l2 2 4-4"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="metric-value">87<span class="sub">/ 87</span></div>
|
|
<div class="metric-delta"><span class="chg up">100%</span> Verfügbarkeit · 24 h</div>
|
|
</div>
|
|
|
|
<div class="metric">
|
|
<div class="metric-top">
|
|
<div class="metric-label">Ø Response</div>
|
|
<div class="m-ico">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="metric-value">142<span class="sub">ms</span></div>
|
|
<div class="metric-delta"><span class="chg up">↓ 8,4%</span> vs. letzte Woche</div>
|
|
</div>
|
|
|
|
<div class="metric">
|
|
<div class="metric-top">
|
|
<div class="metric-label">Updates offen</div>
|
|
<div class="m-ico warning">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7M21 4v5h-5"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="metric-value">24</div>
|
|
<div class="metric-delta"><span class="chg warn">7 kritisch</span> · 17 minor</div>
|
|
</div>
|
|
|
|
<div class="metric">
|
|
<div class="metric-top">
|
|
<div class="metric-label">Security Events</div>
|
|
<div class="m-ico danger">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7l8-4z"/><path d="M12 8v5M12 16h.01"/></svg>
|
|
</div>
|
|
</div>
|
|
<div class="metric-value">128</div>
|
|
<div class="metric-delta"><span class="chg down">↑ 23%</span> Angriffe blockiert</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============ ROW: CHART + FEED ============ -->
|
|
<div class="row2">
|
|
<!-- Traffic chart -->
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<h3>
|
|
<svg class="hico" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 17l5-5 4 4 7-8M14 8h6v6"/></svg>
|
|
Traffic
|
|
<span class="live-tag" style="margin-left:2px"><span class="pulse-dot" style="color:var(--success)"></span>live</span>
|
|
</h3>
|
|
<div class="card-tabs">
|
|
<button>15m</button>
|
|
<button class="active">60m</button>
|
|
<button>24h</button>
|
|
<button>7d</button>
|
|
</div>
|
|
</div>
|
|
<div class="chart-wrap">
|
|
<div class="chart-head">
|
|
<div>
|
|
<div class="chart-figure">14,2k<span class="unit">req/min</span></div>
|
|
<div class="chart-sub">Peak vor 12 min · 16,8k req/min</div>
|
|
</div>
|
|
<div class="legend">
|
|
<span><i style="background:#3b6ff2"></i>Requests</span>
|
|
<span><i style="background:#7c5cff"></i>Cache hit</span>
|
|
</div>
|
|
</div>
|
|
<svg class="svg-chart" viewBox="0 0 600 172" preserveAspectRatio="none">
|
|
<defs>
|
|
<linearGradient id="grad-req" x1="0" x2="0" y1="0" y2="1">
|
|
<stop offset="0%" stop-color="#3b6ff2" stop-opacity="0.28"/>
|
|
<stop offset="100%" stop-color="#3b6ff2" stop-opacity="0"/>
|
|
</linearGradient>
|
|
<linearGradient id="grad-cache" x1="0" x2="0" y1="0" y2="1">
|
|
<stop offset="0%" stop-color="#7c5cff" stop-opacity="0.16"/>
|
|
<stop offset="100%" stop-color="#7c5cff" stop-opacity="0"/>
|
|
</linearGradient>
|
|
</defs>
|
|
<g stroke="rgba(30,35,60,0.06)" stroke-width="1">
|
|
<line x1="30" y1="34" x2="595" y2="34"/>
|
|
<line x1="30" y1="74" x2="595" y2="74"/>
|
|
<line x1="30" y1="114" x2="595" y2="114"/>
|
|
</g>
|
|
<text class="chart-axis" x="4" y="38">20k</text>
|
|
<text class="chart-axis" x="4" y="78">15k</text>
|
|
<text class="chart-axis" x="4" y="118">10k</text>
|
|
<path id="cache-area" d="" fill="url(#grad-cache)" />
|
|
<path id="cache-line" d="" stroke="#7c5cff" stroke-width="1.6" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<path id="req-area" d="" fill="url(#grad-req)" />
|
|
<path id="req-line" d="" stroke="#3b6ff2" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
<circle id="live-dot" cx="0" cy="0" r="3.5" fill="#3b6ff2" stroke="#ffffff" stroke-width="2"/>
|
|
<text class="chart-axis" x="34" y="166">-60m</text>
|
|
<text class="chart-axis" x="300" y="166">-30m</text>
|
|
<text class="chart-axis" x="560" y="166">jetzt</text>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Activity feed -->
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<h3>
|
|
<svg class="hico" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
|
|
Aktivität
|
|
<span class="live-tag" style="margin-left:2px"><span class="pulse-dot" style="color:var(--success)"></span>live</span>
|
|
</h3>
|
|
<span class="meta">30 min</span>
|
|
</div>
|
|
<div class="feed" id="feed-body"></div>
|
|
</section>
|
|
</div>
|
|
|
|
<!-- ============ SITES TABLE ============ -->
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<h3>
|
|
<svg class="hico" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
|
|
WordPress-Sites
|
|
<span class="pill" style="margin-left:4px">8 von 87</span>
|
|
</h3>
|
|
<div style="display:flex;gap:8px">
|
|
<button class="ghost-btn">Filter</button>
|
|
<button class="ghost-btn">Alle anzeigen →</button>
|
|
</div>
|
|
</div>
|
|
<div class="scroll-x">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Site</th>
|
|
<th>Server</th>
|
|
<th>WordPress</th>
|
|
<th class="num">Besucher / 24h</th>
|
|
<th>Status</th>
|
|
<th class="num"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="sites-body"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="foot">CluPilot · region eu-central-1 · uptime 99,984%</div>
|
|
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script>
|
|
const Icon = {
|
|
terminal:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 17l5-5-5-5M13 19h7"/></svg>',
|
|
ext:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 3h7v7M21 3l-9 9M19 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/></svg>',
|
|
more:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></svg>',
|
|
};
|
|
|
|
/* ============ SITES ============ */
|
|
const palette = ['#3b6ff2','#15a06a','#c2820f','#7c5cff','#db3b3b','#0ea5b7','#c026d3','#0d9488'];
|
|
const sites = [
|
|
{url:'kunde-mueller.de', server:'eu-prod-01', wp:'6.5.2', wpOk:true, visitors:'12,4k', health:'ok', label:'Optimal'},
|
|
{url:'shop.bergmann-coffee.com', server:'eu-prod-01', wp:'6.4.3', wpOk:false, visitors:'48,7k', health:'ok', label:'3 Updates'},
|
|
{url:'blog.fintech-radar.io', server:'eu-prod-02', wp:'6.5.2', wpOk:true, visitors:'9,1k', health:'ok', label:'Optimal'},
|
|
{url:'praxis-dr-schwarz.de', server:'eu-prod-02', wp:'6.2.1', wpOk:false, visitors:'1,2k', health:'warn', label:'Wartung nötig'},
|
|
{url:'agentur-helio.com', server:'eu-edge-amsterdam', wp:'6.5.2', wpOk:true, visitors:'18,2k', health:'ok', label:'Optimal'},
|
|
{url:'docs.opencrate.dev', server:'us-east-prod-01', wp:'6.5.1', wpOk:true, visitors:'7,8k', health:'ok', label:'2 Updates'},
|
|
{url:'staging.opencrate.dev', server:'eu-staging', wp:'6.6-RC1', wpOk:true, visitors:'82', health:'warn', label:'Backup spät'},
|
|
{url:'classic.tonhoff-bau.de', server:'us-east-prod-01', wp:'5.9.7', wpOk:false, visitors:'3,4k', health:'bad', label:'Kritisch'},
|
|
];
|
|
const sitesBody = document.getElementById('sites-body');
|
|
sites.forEach((s,i)=>{
|
|
const initial = s.url[0].toUpperCase();
|
|
const color = palette[i % palette.length];
|
|
const wpPill = s.wpOk ? 'success' : 'warning';
|
|
sitesBody.insertAdjacentHTML('beforeend',`
|
|
<tr>
|
|
<td>
|
|
<div class="site-cell">
|
|
<div class="favicon" style="background:${color}">${initial}</div>
|
|
<div style="min-width:0">
|
|
<a class="site-name" href="Site Details.html" style="display:block">${s.url}</a>
|
|
<div class="site-meta">ssl ✓ · php 8.2</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td><span class="pill">${s.server}</span></td>
|
|
<td><span class="pill ${wpPill}">WP ${s.wp}</span></td>
|
|
<td class="num"><span style="color:var(--fg)">${s.visitors}</span></td>
|
|
<td>
|
|
<span class="health">
|
|
<span class="health-dot ${s.health}"></span>
|
|
<span class="health-label">${s.label}</span>
|
|
</span>
|
|
</td>
|
|
<td class="num">
|
|
<div class="row-actions">
|
|
<button title="Site öffnen">${Icon.ext}</button>
|
|
<button title="WP-Admin">${Icon.terminal}</button>
|
|
<button title="Mehr">${Icon.more}</button>
|
|
</div>
|
|
</td>
|
|
</tr>`);
|
|
});
|
|
|
|
/* ============ ACTIVITY FEED ============ */
|
|
const feed = [
|
|
{ic:'success', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M5 12l5 5L20 7"/></svg>',
|
|
html:'Backup erstellt für <span class="target">agentur-helio.com</span>', meta:'vor 12 sek · 248 MB'},
|
|
{ic:'danger', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7l8-4z"/></svg>',
|
|
html:'<b>17 Login-Versuche</b> blockiert auf <span class="target">classic.tonhoff-bau.de</span>', meta:'vor 1 min · TOR exit'},
|
|
{ic:'accent', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M21 12a9 9 0 1 1-3-6.7M21 4v5h-5"/></svg>',
|
|
html:'Plugin-Update <span class="target">Yoast SEO 22.5</span> auf 3 Sites', meta:'vor 4 min · von m.weber'},
|
|
{ic:'warning', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 9v4M12 17h.01"/><circle cx="12" cy="12" r="9"/></svg>',
|
|
html:'CPU > 80% auf <span class="target">us-east-prod-01</span>', meta:'vor 7 min · 4m 12s'},
|
|
{ic:'success', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><rect x="5" y="11" width="14" height="9" rx="1.5"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>',
|
|
html:'SSL-Zertifikat erneuert · <span class="target">*.kunde-mueller.de</span>', meta:'vor 14 min · gültig bis 08/26'},
|
|
{ic:'accent', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M4 17l5-5-5-5M13 19h7"/></svg>',
|
|
html:'SSH-Session geöffnet auf <span class="target">eu-prod-01</span>', meta:'vor 19 min · user j.koch'},
|
|
{ic:'success', icon:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M5 12l5 5L20 7"/></svg>',
|
|
html:'Health-Check OK · <b>87/87 Sites</b>', meta:'vor 23 min · run #4421'},
|
|
];
|
|
const feedBody = document.getElementById('feed-body');
|
|
feed.forEach(f=>{
|
|
feedBody.insertAdjacentHTML('beforeend',`
|
|
<div class="feed-row">
|
|
<div class="feed-ico ${f.ic}">${f.icon}</div>
|
|
<div class="feed-body">
|
|
<div>${f.html}</div>
|
|
<div class="feed-meta">${f.meta}</div>
|
|
</div>
|
|
</div>`);
|
|
});
|
|
|
|
/* ============ TRAFFIC CHART ============ */
|
|
(function(){
|
|
const W = 600, H = 172, padL = 30, padR = 8, padT = 12, padB = 30;
|
|
const N = 60;
|
|
const reqData = [], cacheData = [];
|
|
let v = 11;
|
|
for(let i=0;i<N;i++){
|
|
v += (Math.random()-0.45)*1.6;
|
|
if(i>40 && i<52) v += Math.sin((i-40)/12*Math.PI)*3.5;
|
|
v = Math.max(5, Math.min(19, v));
|
|
reqData.push(v);
|
|
cacheData.push(v * (0.55 + Math.random()*0.15));
|
|
}
|
|
const x = i => padL + (W-padL-padR) * (i/(N-1));
|
|
const y = vv => { const t = vv/20; return H - padB - t*(H-padT-padB); };
|
|
function pathFor(arr){
|
|
let d = `M ${x(0)} ${y(arr[0])}`;
|
|
for(let i=1;i<arr.length;i++){
|
|
const xc = (x(i-1)+x(i))/2;
|
|
d += ` Q ${x(i-1)} ${y(arr[i-1])} ${xc} ${(y(arr[i-1])+y(arr[i]))/2}`;
|
|
}
|
|
d += ` T ${x(arr.length-1)} ${y(arr[arr.length-1])}`;
|
|
return d;
|
|
}
|
|
const areaFor = arr => pathFor(arr) + ` L ${x(arr.length-1)} ${H-padB} L ${x(0)} ${H-padB} Z`;
|
|
document.getElementById('req-line').setAttribute('d', pathFor(reqData));
|
|
document.getElementById('req-area').setAttribute('d', areaFor(reqData));
|
|
document.getElementById('cache-line').setAttribute('d', pathFor(cacheData));
|
|
document.getElementById('cache-area').setAttribute('d', areaFor(cacheData));
|
|
const dot = document.getElementById('live-dot');
|
|
dot.setAttribute('cx', x(N-1));
|
|
dot.setAttribute('cy', y(reqData[N-1]));
|
|
let t0 = 0;
|
|
(function tick(){ t0 += 0.04; dot.setAttribute('r', 3.5 + Math.sin(t0)*0.8); requestAnimationFrame(tick); })();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|