feat: UI redesign (CSS system, DNS widget, Traefik dashboard widget)
- Replace base.html style block with full CSS design system using custom properties (dark theme, cards, tables, badges, dots, widgets) - Add DNS blueprint (fablednetmon/dns/) with /dns/ and /dns/rows routes + templates - Add Traefik /widget route and widget.html fragment for dashboard - Update dashboard: DNS + Traefik widgets, traefik_enabled config detection - Update all templates to use new CSS classes (page-title, section-title, card-flush, table, badge-*, dot-*, btn-*) - Register dns_bp in app.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,43 +6,129 @@
|
||||
<title>{% block title %}FabledNetMon{% endblock %}</title>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.min.js"></script>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: system-ui, sans-serif; background: #0f0f0f; color: #e0e0e0; }
|
||||
nav { background: #1a1a2e; padding: 0.75rem 1.5rem; display: flex; align-items: center; gap: 1rem; border-bottom: 1px solid #2a2a4a; }
|
||||
nav a { color: #a0a0ff; text-decoration: none; font-size: 0.9rem; }
|
||||
nav .brand { font-weight: bold; color: #c0c0ff; font-size: 1.1rem; margin-right: auto; }
|
||||
main { padding: 1.5rem; max-width: 1200px; margin: 0 auto; }
|
||||
.alert { padding: 0.75rem 1rem; border-radius: 4px; margin-bottom: 1rem; }
|
||||
.alert-error { background: #3a1a1a; border: 1px solid #8b2020; color: #ffaaaa; }
|
||||
.card { background: #1a1a2e; border: 1px solid #2a2a4a; border-radius: 6px; padding: 1.25rem; margin-bottom: 1rem; }
|
||||
.form-group { margin-bottom: 1rem; }
|
||||
label { display: block; margin-bottom: 0.3rem; font-size: 0.9rem; color: #a0a0c0; }
|
||||
input[type=text], input[type=email], input[type=password] {
|
||||
width: 100%; padding: 0.5rem 0.75rem; background: #0f0f1e; border: 1px solid #3a3a5a;
|
||||
border-radius: 4px; color: #e0e0e0; font-size: 1rem;
|
||||
}
|
||||
button[type=submit], .btn {
|
||||
padding: 0.5rem 1.25rem; background: #4040a0; color: #fff; border: none;
|
||||
border-radius: 4px; cursor: pointer; font-size: 0.95rem;
|
||||
}
|
||||
button[type=submit]:hover, .btn:hover { background: #5050c0; }
|
||||
/* Ping pills */
|
||||
.ping-card { padding: 0.75rem 1.25rem; }
|
||||
.ping-row { display:flex; align-items:center; gap:1rem; padding:0.45rem 0; border-bottom:1px solid #161626; }
|
||||
.ping-row:last-child { border-bottom:none; }
|
||||
.ping-meta { min-width:160px; flex-shrink:0; }
|
||||
.ping-name { font-size:0.9rem; }
|
||||
.ping-addr { display:block; color:#505070; font-size:0.75rem; }
|
||||
.ping-pills { display:flex; gap:2px; flex:1; align-items:center; }
|
||||
.pill { display:inline-block; width:7px; height:22px; border-radius:2px; cursor:default; transition:opacity .1s; }
|
||||
.pill:hover { opacity:.75; }
|
||||
.pill-empty { background:#1a1a28; }
|
||||
.ping-cur { min-width:72px; text-align:right; font-size:0.85rem; font-variant-numeric:tabular-nums; }
|
||||
.ping-cur-good { color:#22aa44; }
|
||||
.ping-cur-warn { color:#ccaa20; }
|
||||
.ping-cur-bad { color:#dd6020; }
|
||||
.ping-cur-down { color:#cc2020; font-weight:bold; }
|
||||
.ping-cur-nd { color:#404060; }
|
||||
:root {
|
||||
--bg: #09091a;
|
||||
--bg-card: #111126;
|
||||
--bg-elevated: #17173a;
|
||||
--border: #21213d;
|
||||
--border-mid: #2a2a4a;
|
||||
--text: #d4d8f0;
|
||||
--text-muted: #6870a0;
|
||||
--text-dim: #30305a;
|
||||
--accent: #5b5ef4;
|
||||
--accent-hover: #7274f8;
|
||||
--green: #26d96b;
|
||||
--green-dim: #0e3a1e;
|
||||
--yellow: #e6b818;
|
||||
--yellow-dim: #3a3000;
|
||||
--orange: #e07828;
|
||||
--orange-dim: #3a1e00;
|
||||
--red: #e83848;
|
||||
--red-dim: #3a0e14;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--text); line-height: 1.5; }
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { color: var(--accent-hover); }
|
||||
code { font-family: ui-monospace, monospace; font-size: 0.875em; background: var(--bg-elevated); padding: 0.1em 0.35em; border-radius: 3px; }
|
||||
|
||||
/* Nav */
|
||||
nav { background: var(--bg-card); padding: 0 1.5rem; display: flex; align-items: center; gap: 0; border-bottom: 1px solid var(--border-mid); height: 48px; }
|
||||
nav .brand { font-weight: 700; color: #a0a8f8; font-size: 1rem; margin-right: 2rem; letter-spacing: -0.02em; }
|
||||
nav a { color: var(--text-muted); font-size: 0.875rem; padding: 0 0.875rem; height: 48px; display: flex; align-items: center; border-bottom: 2px solid transparent; transition: color .15s, border-color .15s; }
|
||||
nav a:hover { color: var(--text); border-bottom-color: var(--border-mid); }
|
||||
nav a.nav-end { margin-left: auto; }
|
||||
|
||||
/* Layout */
|
||||
main { padding: 1.5rem; max-width: 1280px; margin: 0 auto; }
|
||||
|
||||
/* Alerts */
|
||||
.alert { padding: 0.75rem 1rem; border-radius: 6px; margin-bottom: 1rem; font-size: 0.9rem; }
|
||||
.alert-error { background: var(--red-dim); border: 1px solid #6a1820; color: #ffb0b8; }
|
||||
.alert-success { background: var(--green-dim); border: 1px solid #1a5a28; color: #90f0b0; }
|
||||
|
||||
/* Cards */
|
||||
.card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; margin-bottom: 1rem; }
|
||||
.card-flush { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; margin-bottom: 1rem; }
|
||||
|
||||
/* Typography */
|
||||
.page-title { font-size: 1.5rem; font-weight: 600; color: #b0b8f8; margin-bottom: 1.5rem; letter-spacing: -0.02em; }
|
||||
.section-title { font-size: 0.8rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
|
||||
|
||||
/* Tables */
|
||||
.table { width: 100%; border-collapse: collapse; }
|
||||
.table th { text-align: left; padding: 0.6rem 1rem; color: var(--text-muted); font-weight: 500; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; border-bottom: 1px solid var(--border-mid); }
|
||||
.table td { padding: 0.7rem 1rem; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
|
||||
.table tbody tr:last-child td { border-bottom: none; }
|
||||
.table tbody tr:hover td { background: var(--bg-elevated); }
|
||||
.table .td-actions { text-align: right; white-space: nowrap; }
|
||||
|
||||
/* Forms */
|
||||
.form-group { margin-bottom: 1rem; }
|
||||
label { display: block; margin-bottom: 0.3rem; font-size: 0.85rem; color: var(--text-muted); }
|
||||
input[type=text], input[type=email], input[type=password], input[type=number], input[type=url], select, textarea {
|
||||
width: 100%; padding: 0.5rem 0.75rem; background: var(--bg); border: 1px solid var(--border-mid);
|
||||
border-radius: 5px; color: var(--text); font-size: 0.9rem; font-family: inherit;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
|
||||
select { cursor: pointer; }
|
||||
textarea { resize: vertical; }
|
||||
|
||||
/* Buttons */
|
||||
.btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.45rem 1rem; background: var(--accent); color: #fff; border: none; border-radius: 5px; cursor: pointer; font-size: 0.875rem; font-family: inherit; font-weight: 500; transition: background .15s; text-decoration: none; }
|
||||
.btn:hover { background: var(--accent-hover); color: #fff; }
|
||||
.btn-sm { padding: 0.3rem 0.65rem; font-size: 0.8rem; }
|
||||
.btn-ghost { background: transparent; color: var(--text-muted); border: 1px solid var(--border-mid); }
|
||||
.btn-ghost:hover { background: var(--bg-elevated); color: var(--text); }
|
||||
.btn-danger { background: #7a1820; color: #ffb0b0; }
|
||||
.btn-danger:hover { background: #9a2030; }
|
||||
.btn-warn { background: var(--yellow-dim); color: var(--yellow); border: 1px solid #6a5000; }
|
||||
.btn-warn:hover { background: #4a4000; }
|
||||
|
||||
/* Badges / Status */
|
||||
.badge { display: inline-block; padding: 0.2em 0.55em; border-radius: 4px; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.03em; }
|
||||
.badge-green { background: var(--green-dim); color: var(--green); }
|
||||
.badge-red { background: var(--red-dim); color: #ff8090; }
|
||||
.badge-yellow { background: var(--yellow-dim); color: var(--yellow); }
|
||||
.badge-orange { background: var(--orange-dim); color: var(--orange); }
|
||||
.badge-dim { background: var(--bg-elevated); color: var(--text-muted); }
|
||||
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
||||
.dot-up { background: var(--green); box-shadow: 0 0 4px var(--green); }
|
||||
.dot-down { background: var(--red); }
|
||||
.dot-warn { background: var(--yellow); }
|
||||
.dot-dim { background: var(--text-dim); }
|
||||
|
||||
/* Empty state */
|
||||
.empty { color: var(--text-muted); font-size: 0.9rem; padding: 2rem; text-align: center; }
|
||||
|
||||
/* Stat cards (dashboard) */
|
||||
.stat-val { font-size: 2rem; font-weight: 700; line-height: 1; }
|
||||
.stat-label { font-size: 0.8rem; color: var(--text-muted); margin-left: 0.3rem; }
|
||||
|
||||
/* Ping pills */
|
||||
.ping-card { padding: 0.75rem 1.25rem; }
|
||||
.ping-row { display:flex; align-items:center; gap:1rem; padding:0.45rem 0; border-bottom:1px solid var(--border); }
|
||||
.ping-row:last-child { border-bottom:none; }
|
||||
.ping-meta { min-width:160px; flex-shrink:0; }
|
||||
.ping-name { font-size:0.875rem; font-weight: 500; }
|
||||
.ping-addr { display:block; color:var(--text-muted); font-size:0.75rem; }
|
||||
.ping-pills { display:flex; gap:2px; flex:1; align-items:center; }
|
||||
.pill { display:inline-block; width:7px; height:22px; border-radius:2px; cursor:default; transition:opacity .1s; }
|
||||
.pill:hover { opacity:.75; }
|
||||
.pill-empty { background:var(--bg-elevated); }
|
||||
.ping-cur { min-width:72px; text-align:right; font-size:0.85rem; font-variant-numeric:tabular-nums; }
|
||||
.ping-cur-good { color:var(--green); }
|
||||
.ping-cur-warn { color:var(--yellow); }
|
||||
.ping-cur-bad { color:var(--orange); }
|
||||
.ping-cur-down { color:var(--red); font-weight:bold; }
|
||||
.ping-cur-nd { color:var(--text-dim); }
|
||||
|
||||
/* Widget headers */
|
||||
.widget-header { display:flex; justify-content:space-between; align-items:center; margin-bottom:0.75rem; }
|
||||
.widget-title { font-size:0.8rem; font-weight:600; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.06em; }
|
||||
.widget-link { font-size:0.8rem; color:var(--text-muted); }
|
||||
.widget-link:hover { color:var(--text); }
|
||||
</style>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
@@ -53,12 +139,13 @@
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/hosts/">Hosts</a>
|
||||
<a href="/ping/">Ping</a>
|
||||
<a href="/dns/">DNS</a>
|
||||
<a href="/alerts/">Alerts</a>
|
||||
<a href="/ansible/">Ansible</a>
|
||||
{% if session.user_role == 'admin' %}
|
||||
<a href="/settings/">Settings</a>
|
||||
{% endif %}
|
||||
<a href="/logout">Logout ({{ session.username }})</a>
|
||||
<a href="/logout" class="nav-end">{{ session.username }}</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
<main>
|
||||
|
||||
Reference in New Issue
Block a user