Add UI improvements: date formatting, breadcrumbs, clickable rows, and structured menu

- Format dates as YYYY-MM-DD with full timestamp on hover
- Add breadcrumb navigation with hospital icon (🏥) across all pages
- Restructure main menu with grouped dropdowns:
  * Hardware (Assets, Components)
  * Health (Tickets, Failures, Analytics)
  * Settings (Ingest)
- Make table rows clickable on Dashboard, Assets, and Components pages
- Add new Customers page with list view
- Improve dropdown menu stability with JS hover delay

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 08:46:07 +03:00
parent 849235be22
commit 5f96ff2273
12 changed files with 327 additions and 29 deletions

View File

@@ -64,15 +64,77 @@
letter-spacing: 0.08em;
font-weight: 600;
}
.nav a {
.nav-item {
position: relative;
}
.nav a, .nav-group-label {
text-decoration: none;
color: var(--ink);
opacity: 0.7;
cursor: pointer;
display: block;
}
.nav a.active {
.nav a.active, .nav-group-label.active {
opacity: 1;
color: var(--accent);
}
.nav-group {
position: relative;
}
.nav-group-label {
display: flex;
align-items: center;
gap: 4px;
}
.nav-group-label::after {
content: '▾';
font-size: 10px;
opacity: 0.5;
}
.nav-submenu {
display: none;
position: absolute;
top: 100%;
left: 0;
background: white;
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: var(--shadow);
margin-top: 4px;
min-width: 160px;
z-index: 100;
padding: 8px 0;
}
.nav-submenu::before {
content: '';
position: absolute;
top: -4px;
left: 0;
right: 0;
height: 4px;
background: transparent;
}
.nav-group:hover .nav-submenu,
.nav-submenu:hover {
display: block;
}
.nav-submenu a {
padding: 8px 16px;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
opacity: 0.7;
transition: all 0.2s;
}
.nav-submenu a:hover {
opacity: 1;
background: var(--accent-soft);
}
.nav-submenu a.active {
opacity: 1;
color: var(--accent);
background: var(--accent-soft);
}
.container {
max-width: 1100px;
margin: 28px auto;
@@ -137,6 +199,13 @@
.table tr:last-child td {
border-bottom: none;
}
.table tr.clickable {
cursor: pointer;
transition: background-color 0.15s;
}
.table tr.clickable:hover {
background: var(--accent-soft);
}
.meta {
color: var(--muted);
font-size: 12px;
@@ -261,6 +330,36 @@
font-weight: 600;
color: #0f172a;
}
.breadcrumbs {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 32px;
background: white;
border-bottom: 1px solid var(--border);
font-size: 13px;
}
.breadcrumbs a {
text-decoration: none;
color: var(--muted);
transition: color 0.2s;
}
.breadcrumbs a:hover {
color: var(--accent);
}
.breadcrumbs .home-icon {
font-size: 16px;
line-height: 1;
}
.breadcrumbs .separator {
color: var(--muted);
opacity: 0.5;
font-size: 11px;
}
.breadcrumbs .current {
color: var(--ink);
font-weight: 600;
}
@media (max-width: 720px) {
.topbar {
flex-direction: column;
@@ -275,6 +374,31 @@
}
}
</style>
<script>
function navigateToRow(url) {
window.location.href = url;
}
// Улучшенная работа выпадающих меню
document.addEventListener('DOMContentLoaded', function() {
const navGroups = document.querySelectorAll('.nav-group');
navGroups.forEach(group => {
let hideTimer;
group.addEventListener('mouseenter', function() {
clearTimeout(hideTimer);
this.querySelector('.nav-submenu').style.display = 'block';
});
group.addEventListener('mouseleave', function() {
const submenu = this.querySelector('.nav-submenu');
hideTimer = setTimeout(() => {
submenu.style.display = 'none';
}, 150);
});
});
});
</script>
</head>
{{end}}
@@ -288,12 +412,48 @@
{{if .HeroTag}}<div class="pill">{{.HeroTag}}</div>{{end}}
</header>
<nav class="nav">
<a href="/ui" class="{{if eq .ActiveNav "dashboard"}}active{{end}}">Dashboard</a>
<a href="/ui/assets" class="{{if eq .ActiveNav "assets"}}active{{end}}">Assets</a>
<a href="/ui/components" class="{{if eq .ActiveNav "components"}}active{{end}}">Components</a>
<a href="/ui/tickets" class="{{if eq .ActiveNav "tickets"}}active{{end}}">Tickets</a>
<a href="/ui/failures" class="{{if eq .ActiveNav "failures"}}active{{end}}">Failures</a>
<a href="/ui/ingest" class="{{if eq .ActiveNav "ingest"}}active{{end}}">Ingest</a>
<a href="/ui/analytics" class="{{if eq .ActiveNav "analytics"}}active{{end}}">Analytics</a>
<div class="nav-item">
<a href="/ui" class="{{if eq .ActiveNav "dashboard"}}active{{end}}">Dashboard</a>
</div>
<div class="nav-item">
<a href="/ui/customers" class="{{if eq .ActiveNav "customers"}}active{{end}}">Customers</a>
</div>
<div class="nav-item nav-group">
<span class="nav-group-label {{if or (eq .ActiveNav "assets") (eq .ActiveNav "components")}}active{{end}}">Hardware</span>
<div class="nav-submenu">
<a href="/ui/assets" class="{{if eq .ActiveNav "assets"}}active{{end}}">Assets</a>
<a href="/ui/components" class="{{if eq .ActiveNav "components"}}active{{end}}">Components</a>
</div>
</div>
<div class="nav-item nav-group">
<span class="nav-group-label {{if or (eq .ActiveNav "tickets") (eq .ActiveNav "failures") (eq .ActiveNav "analytics")}}active{{end}}">Health</span>
<div class="nav-submenu">
<a href="/ui/tickets" class="{{if eq .ActiveNav "tickets"}}active{{end}}">Tickets</a>
<a href="/ui/failures" class="{{if eq .ActiveNav "failures"}}active{{end}}">Failures</a>
<a href="/ui/analytics" class="{{if eq .ActiveNav "analytics"}}active{{end}}">Analytics</a>
</div>
</div>
<div class="nav-item nav-group">
<span class="nav-group-label {{if eq .ActiveNav "ingest"}}active{{end}}">Settings</span>
<div class="nav-submenu">
<a href="/ui/ingest" class="{{if eq .ActiveNav "ingest"}}active{{end}}">Ingest</a>
</div>
</div>
</nav>
{{end}}
{{define "breadcrumbs"}}
{{if .Breadcrumbs}}
<nav class="breadcrumbs">
<a href="/ui" class="home-icon">🏥</a>
{{range $i, $crumb := .Breadcrumbs}}
<span class="separator"></span>
{{if $crumb.URL}}
<a href="{{$crumb.URL}}">{{$crumb.Label}}</a>
{{else}}
<span class="current">{{$crumb.Label}}</span>
{{end}}
{{end}}
</nav>
{{end}}
{{end}}