v0.13.0: UI polish fixes (8 improvements)

- Fix 1: Dashboard backup card border (verified already correct)
- Fix 2: Show auto-generated env values on deploy page with copy/reveal
- Fix 3: Temperature value pill for better visibility on dashboard
- Fix 4: Rework dashboard backup section (remove manual trigger, add Tier 2 summary)
- Fix 5: Scope HDD warning banner to dashboard and monitoring pages only
- Fix 6: Move Tárhely section up in monitoring page
- Fix 7: Snapshot table clarity (HOZZÁADOTT header, n/a instead of -)
- Fix 8: Restructure Tároló section into tiered storage view

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 13:30:21 +01:00
parent 6bfdd88d10
commit 90826dec7a
10 changed files with 328 additions and 126 deletions
+88 -50
View File
@@ -346,7 +346,7 @@
<tr>
<th>Azonosító</th>
<th>Időpont</th>
<th>Méret</th>
<th>Hozzáadott <span class="col-subtitle">(új adat)</span></th>
<th>Új fájl</th>
<th>Változott</th>
</tr>
@@ -356,9 +356,9 @@
<tr>
<td class="mono">{{shortID .SnapshotID}}</td>
<td class="mono">{{fmtTime .Time}}</td>
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{else}}{{end}}</td>
<td class="mono">{{if .HasStats}}{{.FilesNew}}{{else}}{{end}}</td>
<td class="mono">{{if .HasStats}}{{.FilesChanged}}{{else}}{{end}}</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>
</tr>
{{end}}
</tbody>
@@ -376,63 +376,101 @@
<!-- Section 6: Repository -->
<div class="repo-card">
<h3>Tároló</h3>
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Helyszín:</span>
<span class="repo-value mono">{{.Backup.RepoPath}} (helyi)</span>
<!-- Tier 1: Local restic backup -->
<div class="repo-tier">
<h4 class="repo-tier-title">1. mentés — Helyi mentés (restic)</h4>
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Helyszín:</span>
<span class="repo-value mono">{{.Backup.RepoPath}} <span class="relative-time">(helyi SSD)</span></span>
</div>
{{if .Backup.RepoStats}}
<div class="repo-info-row">
<span class="repo-label">Méret:</span>
<span class="repo-value">{{.Backup.RepoStats.TotalSize}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Pillanatképek:</span>
<span class="repo-value">{{.Backup.RepoStats.SnapshotCount}}</span>
</div>
{{end}}
<div class="repo-info-row">
<span class="repo-label">Integritás:</span>
<span class="repo-value">
{{if .Backup.LastCheckTime.IsZero}}
<span class="relative-time">Még nem ellenőrzött</span>
{{else if .Backup.LastCheckOK}}
<span class="backup-status-ok">Rendben</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
{{else}}
<span class="backup-status-fail">Hiba</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
{{end}}
</span>
</div>
</div>
{{if .Backup.RepoStats}}
<div class="repo-info-row">
<span class="repo-label">Méret:</span>
<span class="repo-value">{{.Backup.RepoStats.TotalSize}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Pillanatképek:</span>
<span class="repo-value">{{.Backup.RepoStats.SnapshotCount}}</span>
<!-- Encryption key -->
{{if $.ResticPassword}}
<div class="repo-encryption">
<span class="repo-label">Titkosítási kulcs:</span>
<div class="repo-encryption-row">
<input type="password" id="restic-pw" class="restic-pw-field mono" value="{{$.ResticPassword}}" readonly>
<button type="button" class="btn btn-sm" onclick="toggleResticPw()">Megjelenítés</button>
<button type="button" class="btn btn-sm" onclick="copyResticPw()">Másolás</button>
</div>
<div class="repo-encryption-warn">
Mentse el biztonságos helyre! A kulcs nélkül a biztonsági mentések NEM állíthatók vissza.
</div>
</div>
{{end}}
<div class="repo-info-row">
<span class="repo-label">Integritás:</span>
<span class="repo-value">
{{if .Backup.LastCheckTime.IsZero}}
<span class="relative-time">Még nem ellenőrzött</span>
{{else if .Backup.LastCheckOK}}
<span class="backup-status-ok">Rendben</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
{{else}}
<span class="backup-status-fail">Hiba</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
<div class="repo-paths">
<span class="repo-label">Mentett útvonalak (forrás):</span>
<ul class="repo-path-list">
{{range .Backup.BackupPaths}}
<li class="mono">{{.}}</li>
{{end}}
</span>
</ul>
</div>
</div>
<!-- Encryption key -->
{{if $.ResticPassword}}
<div class="repo-encryption">
<span class="repo-label">Titkosítási kulcs:</span>
<div class="repo-encryption-row">
<input type="password" id="restic-pw" class="restic-pw-field mono" value="{{$.ResticPassword}}" readonly>
<button type="button" class="btn btn-sm" onclick="toggleResticPw()">Megjelenítés</button>
<button type="button" class="btn btn-sm" onclick="copyResticPw()">Másolás</button>
</div>
<div class="repo-encryption-warn">
Mentse el biztonságos helyre! A kulcs nélkül a biztonsági mentések NEM állíthatók vissza.
<!-- Tier 2: Cross-drive backup destinations -->
{{if .Tier2Dests}}
<div class="repo-tier">
<h4 class="repo-tier-title">2. mentés — Másodlagos másolat</h4>
{{range .Tier2Dests}}
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Cél:</span>
<span class="repo-value mono">{{index . "Path"}}{{if index . "Label"}} <span class="relative-time">({{index . "Label"}})</span>{{end}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Módszer:</span>
<span class="repo-value">{{index . "Method"}}</span>
</div>
{{if index . "SizeHuman"}}
<div class="repo-info-row">
<span class="repo-label">Méret:</span>
<span class="repo-value">{{index . "SizeHuman"}}</span>
</div>
{{end}}
</div>
{{end}}
</div>
{{end}}
<div class="repo-paths">
<span class="repo-label">Mentett útvonalak:</span>
<ul class="repo-path-list">
{{range .Backup.BackupPaths}}
<li class="mono">{{.}}</li>
{{end}}
</ul>
</div>
<div class="repo-remote">
<span class="repo-label">Távoli másolat:</span>
<div class="repo-remote-status">
<span class="relative-time">Nincs beállítva</span>
<span class="relative-time">(B2/S3/SFTP támogatás hamarosan)</span>
<!-- DB dump storage -->
<div class="repo-tier">
<h4 class="repo-tier-title">Adatbázis mentések</h4>
<div class="repo-info-rows">
<div class="repo-info-row">
<span class="repo-label">Mappa:</span>
<span class="repo-value mono">{{.DBDumpDir}}</span>
</div>
<div class="repo-info-row">
<span class="repo-label">Fájlok:</span>
<span class="repo-value">{{if .Backup.DumpFiles}}{{len .Backup.DumpFiles}} dump fájl{{if gt .DBDumpTotalBytes 0}} — {{fmtBytes .DBDumpTotalBytes}}{{end}}{{else}}Nincs dump fájl{{end}}</span>
</div>
</div>
</div>
</div>
@@ -49,7 +49,7 @@
<span class="system-info-label">Hőmérséklet</span>
<span class="system-info-value">
<span class="temp-dot temp-dot-{{tempColor .SystemInfo.TemperatureCelsius}}"></span>
{{fmtTemp .SystemInfo.TemperatureCelsius}}
<span class="temp-value-pill temp-pill-{{tempColor .SystemInfo.TemperatureCelsius}}">{{fmtTemp .SystemInfo.TemperatureCelsius}}</span>
</span>
</div>
</div>
@@ -106,17 +106,24 @@
<span class="backup-value">{{len .DBDumpStatus.Results}} mentve</span>
</div>
{{end}}
{{if .BackupStatus}}{{if .BackupStatus.RepoStats}}
{{if gt .CrossDriveTotal 0}}
<div class="backup-info-row">
<span class="backup-label">Tároló méret:</span>
<span class="backup-value">{{.BackupStatus.RepoStats.TotalSize}} ({{.BackupStatus.RepoStats.SnapshotCount}} pillanatkép)</span>
<span class="backup-label">2. mentés:</span>
<span class="backup-value">
{{if eq .CrossDriveConfigured 0}}
<span style="color:var(--yellow)">⚠ Nincs beállítva</span>
{{else}}
<span class="{{if gt .CrossDriveFailed 0}}backup-status-fail{{else}}backup-status-ok{{end}}">{{.CrossDriveConfigured}} / {{.CrossDriveTotal}} alk.</span>
{{end}}
</span>
</div>
{{end}}{{end}}
<div class="backup-actions" style="margin-top: .75rem;">
<button class="btn btn-sm btn-primary" onclick="triggerBackup()" id="backup-btn">
{{if .BackupRunning}}Mentés folyamatban...{{else}}Mentés most{{end}}
</button>
{{end}}
{{if gt .CrossDriveFailed 0}}
<div class="backup-info-row">
<span class="backup-label" style="color:var(--red)">Figyelmeztetés:</span>
<span class="backup-value backup-status-fail">⚠ {{.CrossDriveFailed}} 2. mentés sikertelen</span>
</div>
{{end}}
</div>
{{end}}
@@ -163,28 +170,6 @@
{{end}}
</div>
<script>
function triggerBackup() {
const btn = document.getElementById('backup-btn');
btn.disabled = true;
btn.textContent = 'Mentés indítása...';
fetch('/api/backup/run', { method: 'POST' })
.then(r => r.json())
.then(data => {
if (data.ok) {
btn.textContent = 'Mentés folyamatban...';
btn.classList.add('loading');
} else {
btn.textContent = data.error || 'Hiba';
btn.disabled = false;
}
})
.catch(() => {
btn.textContent = 'Hiba';
btn.disabled = false;
});
}
</script>
{{template "layout_end" .}}
{{end}}
@@ -255,9 +255,26 @@
<div class="form-section">
<h4>Automatikusan generált értékek</h4>
<p class="form-section-desc">Ezek az értékek automatikusan létrejönnek a telepítéskor.</p>
{{$autoValues := .AutoFieldValues}}
{{$isDeployed := .AlreadyDeployed}}
{{range .AutoFields}}
{{$val := index $autoValues .EnvVar}}
<div class="form-group form-group-auto">
<label>{{.Label}}</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>
{{end}}
{{end}}
<span class="auto-generated-badge">✓ Automatikusan generálva</span>
</div>
{{end}}
@@ -438,6 +455,22 @@ document.addEventListener('DOMContentLoaded', function() {
if (sel) checkStorageSpace(sel);
});
function toggleAutoField(fieldId, btn) {
var el = document.getElementById(fieldId);
if (!el) return;
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,12 +31,14 @@
{{if .Alerts}}
<div class="alerts-container">
{{range .Alerts}}
{{if 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>
{{if .Link}}<a href="{{.Link}}" class="alert-link">{{.LinkText}} →</a>{{end}}
</div>
{{end}}
{{end}}
</div>
{{end}}
{{end}}
@@ -36,7 +36,36 @@
</div>
</div>
<!-- Section 1.5: Remote Monitoring Status -->
<!-- Section 1.5: Storage (moved here for visibility) -->
<div class="monitor-card">
<h3>Tárhely</h3>
<div class="storage-bars">
{{with .SystemInfo}}
<div class="storage-item">
<div class="storage-header">
<span class="storage-label">SSD (/)</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill {{usageColor .DiskPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .DiskPercent}}%"></div>
</div>
</div>
{{if .HDDConfigured}}
<div class="storage-item">
<div class="storage-header">
<span class="storage-label">Külső HDD</span>
<span class="storage-value">{{fmtGB .HDDUsedGB}} / {{fmtGB .HDDTotalGB}} ({{printf "%.0f" .HDDPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill {{usageColor .HDDPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .HDDPercent}}%"></div>
</div>
</div>
{{end}}
{{end}}
</div>
</div>
<!-- Section 2: Remote Monitoring Status -->
<div class="monitor-card">
<h3>Távoli monitoring</h3>
{{if not .MonitoringEnabled}}
@@ -67,7 +96,7 @@
{{end}}
</div>
<!-- Section 2: System Metrics Charts -->
<!-- Section 3: System Metrics Charts -->
<div class="monitor-card">
<div class="monitor-card-header">
<h3>Rendszer metrikák</h3>
@@ -102,7 +131,7 @@
</div>
</div>
<!-- Section 3: Container Resources -->
<!-- Section 4: Container Resources -->
<div class="monitor-card">
<h3>Alkalmazás erőforrások</h3>
<div class="container-charts-row" id="container-charts">
@@ -120,7 +149,7 @@
</div>
</div>
<!-- Section 4: Per-container detail (expandable) -->
<!-- Section 5: Per-container detail (expandable) -->
<div class="monitor-card" id="container-detail-panel" style="display:none">
<div class="monitor-card-header">
<h3 id="container-detail-title"></h3>
@@ -144,35 +173,6 @@
</div>
</div>
<!-- Section 5: Storage -->
<div class="monitor-card">
<h3>Tárhely</h3>
<div class="storage-bars">
{{with .SystemInfo}}
<div class="storage-item">
<div class="storage-header">
<span class="storage-label">SSD (/)</span>
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill {{usageColor .DiskPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .DiskPercent}}%"></div>
</div>
</div>
{{if .HDDConfigured}}
<div class="storage-item">
<div class="storage-header">
<span class="storage-label">Külső HDD</span>
<span class="storage-value">{{fmtGB .HDDUsedGB}} / {{fmtGB .HDDTotalGB}} ({{printf "%.0f" .HDDPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill {{usageColor .HDDPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .HDDPercent}}%"></div>
</div>
</div>
{{end}}
{{end}}
</div>
</div>
<script src="/static/chart.min.js"></script>
<script>
(function() {
+49 -5
View File
@@ -1240,15 +1240,27 @@ a.stat-card:hover {
/* Temperature dot */
.temp-dot {
display: inline-block;
width: 8px;
height: 8px;
width: 11px;
height: 11px;
border-radius: 50%;
margin-right: .25rem;
vertical-align: middle;
}
.temp-dot-green { background: var(--green); box-shadow: 0 0 4px rgba(35, 134, 54, 0.5); }
.temp-dot-yellow { background: var(--yellow); box-shadow: 0 0 4px rgba(210, 153, 34, 0.5); }
.temp-dot-red { background: var(--red); box-shadow: 0 0 4px rgba(218, 54, 51, 0.5); }
.temp-dot-green { background: var(--green); box-shadow: 0 0 5px rgba(35, 134, 54, 0.6); }
.temp-dot-yellow { background: var(--yellow); box-shadow: 0 0 5px rgba(210, 153, 34, 0.6); }
.temp-dot-red { background: var(--red); box-shadow: 0 0 5px rgba(218, 54, 51, 0.6); }
/* Temperature value pill */
.temp-value-pill {
display: inline-block;
padding: 1px 7px;
border-radius: 4px;
font-weight: 600;
font-size: .85rem;
}
.temp-pill-green { background: rgba(35,134,54,0.18); color: var(--green); }
.temp-pill-yellow { background: rgba(210,153,34,0.18); color: var(--yellow); }
.temp-pill-red { background: rgba(218,54,51,0.18); color: var(--red); }
.system-info-item-compact {
flex: 0 1 auto;
@@ -1461,6 +1473,20 @@ a.stat-card:hover {
color: var(--text-muted);
}
.col-subtitle {
font-size: .7rem;
font-weight: 400;
color: var(--text-muted);
text-transform: none;
letter-spacing: 0;
margin-left: .25rem;
}
.col-na {
color: var(--text-muted);
font-style: italic;
cursor: help;
}
.snapshot-footer {
padding: .75rem .75rem 0;
font-size: .8rem;
@@ -1524,6 +1550,24 @@ a.stat-card:hover {
border-top: 1px solid var(--border-color);
padding-top: .75rem;
}
.repo-tier {
border-top: 1px solid var(--border-color);
padding-top: .75rem;
margin-top: .75rem;
}
.repo-tier:first-child {
border-top: none;
padding-top: 0;
margin-top: 0;
}
.repo-tier-title {
font-size: .85rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: .5rem;
text-transform: uppercase;
letter-spacing: .3px;
}
.repo-remote-status {
margin-top: .25rem;
display: flex;