v0.13.1: UI polish fixes round 2 (4 fixes)
- Fix 1: deploy-cross-drive card uses correct CSS vars (--bg-secondary, --border-color) - Fix 2: Auto-generated env values — badge inline with label, remove copy buttons, muted readonly inputs - Fix 3: Snapshot table shows 0 instead of n/a; remove unused .col-na CSS - Fix 4: Disk warnings moved inline under storage bars (Inline alert field, GetInlineAlerts, inline-warning CSS) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
## Changelog
|
||||
|
||||
### What was just completed (2026-02-18 session 48)
|
||||
- **v0.13.1 — UI Polish Fixes Round 2 (4 fixes):**
|
||||
|
||||
**Fix 1:** Deploy page "Biztonsági mentés" section now has proper card border. Root cause: `.deploy-cross-drive` used undefined CSS variables `--card-bg` and `--border` (only `--bg-secondary` and `--border-color` exist). Fixed by using correct vars (`style.css`).
|
||||
|
||||
**Fix 2:** Auto-generated env values section cleaned up (`deploy.html`, `style.css`). Badge moved inline with label. "Másolás" buttons removed (native select+copy sufficient). Secret fields keep show/hide toggle. Non-secret fields now plain readonly input without button wrapper. Removed `copyAutoField()` JS. CSS updated: `.form-group-auto` now block layout (was flex row), label uses `display: flex; gap: .5rem`, badge downsized to `0.75rem / normal weight`, readonly inputs get muted background.
|
||||
|
||||
**Fix 3:** Snapshot table n/a → 0 (`backups.html`). Replaced `<span class="col-na" title="...">n/a</span>` with plain `0` in all three stats columns. Removed `.col-na` CSS class (no longer used).
|
||||
|
||||
**Fix 4:** Disk warnings moved from top banner to inline under storage bars (`alerts.go`, `layout.html`, `handlers.go`, `dashboard.html`, `monitoring.html`, `style.css`). Added `Inline bool` field to `Alert` struct. Disk-related warnings set `Inline: true`. Layout banner skips inline alerts. New `GetInlineAlerts(page)` method on `AlertManager`. Dashboard and monitoring handlers pass `DiskWarnings`. Inline warning block rendered below storage bars. New `.inline-warning*` CSS classes (compact, subtle, colored).
|
||||
|
||||
**Files modified (8):** `alerts.go`, `handlers.go`, `templates/style.css`, `templates/dashboard.html`, `templates/backups.html`, `templates/deploy.html`, `templates/monitoring.html`, `templates/layout.html`
|
||||
|
||||
### What was just completed (2026-02-18 session 47)
|
||||
- **v0.13.0 — UI Polish Fixes (8 independent fixes):**
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type Alert struct {
|
||||
Link string // optional link to relevant page
|
||||
LinkText string // link display text
|
||||
PageOnly []string // if non-empty, only show on these pages (e.g., ["dashboard", "monitoring"])
|
||||
Inline bool // if true, rendered by page template inline, not in layout banner
|
||||
}
|
||||
|
||||
// AlertManager generates and stores dashboard alerts from health check results.
|
||||
@@ -62,10 +63,11 @@ func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config
|
||||
Link: "/monitoring",
|
||||
LinkText: "Rendszermonitor",
|
||||
}
|
||||
// Disk-related warnings only relevant on dashboard and monitoring pages
|
||||
// Disk-related warnings rendered inline under storage bars, not in top banner
|
||||
if strings.Contains(w, "meghajtón") || strings.Contains(w, "adattároló") || strings.Contains(w, "meghajtó") {
|
||||
alert.ID = "disk-not-separate"
|
||||
alert.PageOnly = []string{"dashboard", "monitoring"}
|
||||
alert.Inline = true
|
||||
}
|
||||
alerts = append(alerts, alert)
|
||||
}
|
||||
@@ -143,6 +145,30 @@ func (am *AlertManager) GetAlerts(excludeIDs ...string) []Alert {
|
||||
return result
|
||||
}
|
||||
|
||||
// GetInlineAlerts returns alerts marked as Inline for a specific page.
|
||||
func (am *AlertManager) GetInlineAlerts(page string) []Alert {
|
||||
am.mu.RLock()
|
||||
defer am.mu.RUnlock()
|
||||
|
||||
var result []Alert
|
||||
for _, a := range am.alerts {
|
||||
if !a.Inline {
|
||||
continue
|
||||
}
|
||||
if len(a.PageOnly) == 0 {
|
||||
result = append(result, a)
|
||||
continue
|
||||
}
|
||||
for _, p := range a.PageOnly {
|
||||
if p == page {
|
||||
result = append(result, a)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// countMissingPings counts how many ping UUIDs are not configured.
|
||||
func countMissingPings(cfg *config.Config) int {
|
||||
count := 0
|
||||
|
||||
@@ -124,6 +124,10 @@ func (s *Server) dashboardHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
data["CrossDriveFailed"] = crossDriveFailed
|
||||
}
|
||||
|
||||
if s.alertManager != nil {
|
||||
data["DiskWarnings"] = s.alertManager.GetInlineAlerts("dashboard")
|
||||
}
|
||||
|
||||
s.render(w, "dashboard", data)
|
||||
}
|
||||
|
||||
@@ -363,6 +367,7 @@ func (s *Server) monitoringHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
// On monitoring page, exclude the "pings-missing" alert since the detailed table is visible
|
||||
if s.alertManager != nil {
|
||||
data["Alerts"] = s.alertManager.GetAlerts("pings-missing")
|
||||
data["DiskWarnings"] = s.alertManager.GetInlineAlerts("monitoring")
|
||||
}
|
||||
|
||||
// Ping status section
|
||||
|
||||
@@ -356,9 +356,9 @@
|
||||
<tr>
|
||||
<td class="mono">{{shortID .SnapshotID}}</td>
|
||||
<td class="mono">{{fmtTime .Time}}</td>
|
||||
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{else}}<span class="col-na" title="A restic pillanatképek nem tartalmaznak méretadatot — csak az utolsó mentés adatai állnak rendelkezésre.">n/a</span>{{end}}</td>
|
||||
<td class="mono">{{if .HasStats}}{{.FilesNew}}{{else}}<span class="col-na" title="A restic pillanatképek nem tartalmaznak fájlszámot — csak az utolsó mentés adatai állnak rendelkezésre.">n/a</span>{{end}}</td>
|
||||
<td class="mono">{{if .HasStats}}{{.FilesChanged}}{{else}}<span class="col-na" title="A restic pillanatképek nem tartalmaznak fájlszámot — csak az utolsó mentés adatai állnak rendelkezésre.">n/a</span>{{end}}</td>
|
||||
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{else}}0{{end}}</td>
|
||||
<td class="mono">{{if .HasStats}}{{.FilesNew}}{{else}}0{{end}}</td>
|
||||
<td class="mono">{{if .HasStats}}{{.FilesChanged}}{{else}}0{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
||||
@@ -77,6 +77,17 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .DiskWarnings}}
|
||||
<div class="inline-warnings">
|
||||
{{range .DiskWarnings}}
|
||||
<div class="inline-warning inline-warning-{{.Level}}">
|
||||
<span class="inline-warning-dot">●</span>
|
||||
<span class="inline-warning-text">{{.Message}}</span>
|
||||
{{if .Link}}<a href="{{.Link}}" class="inline-warning-link">{{.LinkText}} →</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -260,22 +260,17 @@
|
||||
{{range .AutoFields}}
|
||||
{{$val := index $autoValues .EnvVar}}
|
||||
<div class="form-group form-group-auto">
|
||||
<label>{{.Label}}</label>
|
||||
<label>{{.Label}} <span class="auto-generated-badge">✓ Automatikusan generálva</span></label>
|
||||
{{if and $isDeployed $val}}
|
||||
{{if eq .Type "secret"}}
|
||||
<div class="input-with-button">
|
||||
<input type="password" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="toggleAutoField('auto-field-{{.EnvVar}}', this)">Megjelenítés</button>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="copyAutoField('auto-field-{{.EnvVar}}', this)">Másolás</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="input-with-button">
|
||||
<input type="text" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="copyAutoField('auto-field-{{.EnvVar}}', this)">Másolás</button>
|
||||
</div>
|
||||
<input type="text" id="auto-field-{{.EnvVar}}" class="form-control" value="{{$val}}" readonly>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<span class="auto-generated-badge">✓ Automatikusan generálva</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
@@ -461,16 +456,6 @@ function toggleAutoField(fieldId, btn) {
|
||||
el.type = el.type === 'password' ? 'text' : 'password';
|
||||
btn.textContent = el.type === 'password' ? 'Megjelenítés' : 'Elrejtés';
|
||||
}
|
||||
function copyAutoField(fieldId, btn) {
|
||||
var el = document.getElementById(fieldId);
|
||||
if (!el) return;
|
||||
navigator.clipboard.writeText(el.value).then(function() {
|
||||
var orig = btn.textContent;
|
||||
btn.textContent = 'Másolva!';
|
||||
setTimeout(function() { btn.textContent = orig; }, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function generatePassword(fieldId) {
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let pass = '';
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
{{if .Alerts}}
|
||||
<div class="alerts-container">
|
||||
{{range .Alerts}}
|
||||
{{if or (not .PageOnly) (pageMatch .PageOnly $.Page)}}
|
||||
{{if and (not .Inline) (or (not .PageOnly) (pageMatch .PageOnly $.Page))}}
|
||||
<div class="alert-banner alert-banner-{{.Level}}">
|
||||
<span class="alert-icon">{{if eq .Level "error"}}🔴{{else if eq .Level "warning"}}🟡{{else}}ℹ️{{end}}</span>
|
||||
<span class="alert-message">{{.Message}}</span>
|
||||
|
||||
@@ -63,6 +63,17 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .DiskWarnings}}
|
||||
<div class="inline-warnings">
|
||||
{{range .DiskWarnings}}
|
||||
<div class="inline-warning inline-warning-{{.Level}}">
|
||||
<span class="inline-warning-dot">●</span>
|
||||
<span class="inline-warning-text">{{.Message}}</span>
|
||||
{{if .Link}}<a href="{{.Link}}" class="inline-warning-link">{{.LinkText}} →</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- Section 2: Remote Monitoring Status -->
|
||||
|
||||
@@ -518,16 +518,14 @@ h3 {
|
||||
.form-group { margin-bottom: 1rem; }
|
||||
.form-group label { display: block; font-size: .85rem; font-weight: 500; margin-bottom: .4rem; color: var(--text-primary); }
|
||||
.form-group-auto {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: .5rem .75rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.form-group-auto label { margin: 0; }
|
||||
.auto-generated-badge { color: var(--green); font-size: .8rem; font-weight: 500; }
|
||||
.form-group-auto label { display: flex; align-items: center; gap: .5rem; margin-bottom: .4rem; }
|
||||
.form-group-auto .form-control[readonly] { background: var(--bg-primary); }
|
||||
.auto-generated-badge { color: var(--green); font-size: .75rem; font-weight: normal; }
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -695,6 +693,37 @@ select.form-control option { background: var(--bg-secondary); color: var(--text-
|
||||
.alert-message { flex: 1; }
|
||||
.alert-link { margin-left: auto; white-space: nowrap; }
|
||||
|
||||
/* Inline storage warnings (under storage bars on dashboard and monitoring) */
|
||||
.inline-warnings { margin-top: 0.75rem; }
|
||||
.inline-warning {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.75rem;
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.inline-warning-warning {
|
||||
color: var(--yellow);
|
||||
background: rgba(250, 204, 21, 0.06);
|
||||
border: 1px solid rgba(250, 204, 21, 0.15);
|
||||
}
|
||||
.inline-warning-error {
|
||||
color: var(--red);
|
||||
background: rgba(218, 54, 51, 0.06);
|
||||
border: 1px solid rgba(218, 54, 51, 0.15);
|
||||
}
|
||||
.inline-warning-dot { font-size: 0.6rem; flex-shrink: 0; }
|
||||
.inline-warning-text { flex: 1; }
|
||||
.inline-warning-link {
|
||||
color: inherit;
|
||||
opacity: 0.8;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
}
|
||||
.inline-warning-link:hover { opacity: 1; text-decoration: underline; }
|
||||
|
||||
/* Memory summary on deploy page */
|
||||
.memory-summary {
|
||||
background: var(--bg-card);
|
||||
@@ -1481,11 +1510,7 @@ a.stat-card:hover {
|
||||
letter-spacing: 0;
|
||||
margin-left: .25rem;
|
||||
}
|
||||
.col-na {
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
|
||||
.snapshot-footer {
|
||||
padding: .75rem .75rem 0;
|
||||
@@ -2343,8 +2368,8 @@ a.stat-card:hover {
|
||||
|
||||
/* Cross-drive backup card on deploy page */
|
||||
.deploy-cross-drive {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
Reference in New Issue
Block a user