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:
@@ -1,5 +1,26 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### What was just completed (2026-02-18 session 47)
|
||||||
|
- **v0.13.0 — UI Polish Fixes (8 independent fixes):**
|
||||||
|
|
||||||
|
**Fix 1:** backup-status-card border already correct (verified same styling as system-info-card).
|
||||||
|
|
||||||
|
**Fix 2:** Deploy page auto-generated fields now show actual values for deployed apps (`deploy.html`, `handlers.go`). Secrets show as password fields with show/hide toggle; domain/plain values show as readonly text with copy button. JS helpers `toggleAutoField()` / `copyAutoField()` added.
|
||||||
|
|
||||||
|
**Fix 3:** Temperature display made more prominent (`dashboard.html`, `style.css`). Dot enlarged to 11px; value wrapped in colored pill badge (`.temp-value-pill` / `.temp-pill-{green|yellow|red}`).
|
||||||
|
|
||||||
|
**Fix 4:** Dashboard backup card reworked (`dashboard.html`, `handlers.go`). Removed "Mentés most" button and `triggerBackup()` JS. Removed "Tároló méret" line. Added Tier 2 status line (configured/total apps) + warning row for failed cross-drive backups. Handler now computes `CrossDriveTotal`, `CrossDriveConfigured`, `CrossDriveFailed`.
|
||||||
|
|
||||||
|
**Fix 5:** HDD warning banner scoped to dashboard + monitoring pages only (`alerts.go`, `layout.html`, `funcmap.go`). Added `PageOnly []string` field to `Alert` struct. Disk-related warnings (keywords "meghajtón", "adattároló") get stable ID `"disk-not-separate"` + `PageOnly: ["dashboard", "monitoring"]`. `pageMatch()` template function added. Layout renders alerts conditionally.
|
||||||
|
|
||||||
|
**Fix 6:** Tárhely section moved up in Rendszermonitor — now appears right after "Rendszer áttekintés", before "Távoli monitoring" (`monitoring.html`).
|
||||||
|
|
||||||
|
**Fix 7:** Snapshot table improvements (`backups.html`, `style.css`). "MÉRET" renamed to "HOZZÁADOTT (új adat)". `–` for unavailable data replaced with `n/a` (with tooltip explaining restic limitations). New `.col-subtitle` and `.col-na` CSS classes.
|
||||||
|
|
||||||
|
**Fix 8:** Tároló section restructured into tiers (`backups.html`, `handlers.go`, `style.css`). Tier 1 (restic local), Tier 2 (cross-drive, only shown if configured), DB dump directory + total size. Removed "Távoli másolat: Nincs beállítva" placeholder. Handler passes `DBDumpDir`, `DBDumpTotalBytes`, `Tier2Dests` (deduplicated). New `.repo-tier` / `.repo-tier-title` CSS.
|
||||||
|
|
||||||
|
**Files modified (9):** `alerts.go`, `funcmap.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 46)
|
### What was just completed (2026-02-18 session 46)
|
||||||
- **v0.12.9 — Tier 2 for All Apps + Status Dot Update:**
|
- **v0.12.9 — Tier 2 for All Apps + Status Dot Update:**
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||||
@@ -17,6 +18,7 @@ type Alert struct {
|
|||||||
Message string // Hungarian display text
|
Message string // Hungarian display text
|
||||||
Link string // optional link to relevant page
|
Link string // optional link to relevant page
|
||||||
LinkText string // link display text
|
LinkText string // link display text
|
||||||
|
PageOnly []string // if non-empty, only show on these pages (e.g., ["dashboard", "monitoring"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlertManager generates and stores dashboard alerts from health check results.
|
// AlertManager generates and stores dashboard alerts from health check results.
|
||||||
@@ -53,13 +55,19 @@ func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config
|
|||||||
|
|
||||||
// From health check warnings
|
// From health check warnings
|
||||||
for _, w := range report.Warnings {
|
for _, w := range report.Warnings {
|
||||||
alerts = append(alerts, Alert{
|
alert := Alert{
|
||||||
ID: "health-" + simpleHash(w),
|
ID: "health-" + simpleHash(w),
|
||||||
Level: "warning",
|
Level: "warning",
|
||||||
Message: w,
|
Message: w,
|
||||||
Link: "/monitoring",
|
Link: "/monitoring",
|
||||||
LinkText: "Rendszermonitor",
|
LinkText: "Rendszermonitor",
|
||||||
})
|
}
|
||||||
|
// Disk-related warnings only relevant on dashboard and monitoring pages
|
||||||
|
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"}
|
||||||
|
}
|
||||||
|
alerts = append(alerts, alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing ping UUIDs
|
// Missing ping UUIDs
|
||||||
|
|||||||
@@ -305,5 +305,15 @@ func (s *Server) templateFuncMap() template.FuncMap {
|
|||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
},
|
},
|
||||||
|
// pageMatch returns true if currentPage is in the pages slice.
|
||||||
|
// Used to filter page-specific alerts in layout.html.
|
||||||
|
"pageMatch": func(pages []string, currentPage string) bool {
|
||||||
|
for _, p := range pages {
|
||||||
|
if p == currentPage {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,28 @@ func (s *Server) dashboardHandler(w http.ResponseWriter, _ *http.Request) {
|
|||||||
data["BackupStatus"] = fullStatus.LastBackup
|
data["BackupStatus"] = fullStatus.LastBackup
|
||||||
data["BackupRunning"] = fullStatus.Running
|
data["BackupRunning"] = fullStatus.Running
|
||||||
data["BackupMaxAgeHours"] = s.cfg.Monitoring.Thresholds.BackupMaxAgeHours
|
data["BackupMaxAgeHours"] = s.cfg.Monitoring.Thresholds.BackupMaxAgeHours
|
||||||
|
|
||||||
|
// Cross-drive summary for dashboard Tier 2 status line
|
||||||
|
crossConfigs := s.settings.GetAllCrossDriveConfigs()
|
||||||
|
crossDriveTotal := 0
|
||||||
|
crossDriveConfigured := 0
|
||||||
|
crossDriveFailed := 0
|
||||||
|
for _, st := range deployedStacks {
|
||||||
|
if st.Protected {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
crossDriveTotal++
|
||||||
|
cfg, hasCfg := crossConfigs[st.Name]
|
||||||
|
if hasCfg && cfg != nil && cfg.Enabled {
|
||||||
|
crossDriveConfigured++
|
||||||
|
if cfg.LastStatus == "error" {
|
||||||
|
crossDriveFailed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["CrossDriveTotal"] = crossDriveTotal
|
||||||
|
data["CrossDriveConfigured"] = crossDriveConfigured
|
||||||
|
data["CrossDriveFailed"] = crossDriveFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
s.render(w, "dashboard", data)
|
s.render(w, "dashboard", data)
|
||||||
@@ -181,6 +203,16 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
|||||||
data["AppPageURL"] = s.cfg.AppPageURL(meta.Slug)
|
data["AppPageURL"] = s.cfg.AppPageURL(meta.Slug)
|
||||||
data["UserFields"] = meta.UserFacingFields()
|
data["UserFields"] = meta.UserFacingFields()
|
||||||
data["AutoFields"] = meta.AutoGeneratedFields()
|
data["AutoFields"] = meta.AutoGeneratedFields()
|
||||||
|
// Auto-generated field values for already-deployed apps
|
||||||
|
autoFieldValues := make(map[string]string)
|
||||||
|
if alreadyDeployed && appCfg != nil {
|
||||||
|
for _, f := range meta.AutoGeneratedFields() {
|
||||||
|
if val, ok := appCfg.Env[f.EnvVar]; ok {
|
||||||
|
autoFieldValues[f.EnvVar] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["AutoFieldValues"] = autoFieldValues
|
||||||
// Storage paths with free space info for deploy dropdown
|
// Storage paths with free space info for deploy dropdown
|
||||||
var deployPaths []DeployStoragePath
|
var deployPaths []DeployStoragePath
|
||||||
for _, sp := range s.settings.GetSchedulableStoragePaths() {
|
for _, sp := range s.settings.GetSchedulableStoragePaths() {
|
||||||
@@ -487,6 +519,35 @@ func (s *Server) backupsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if pw, err := s.backupMgr.GetResticPassword(); err == nil {
|
if pw, err := s.backupMgr.GetResticPassword(); err == nil {
|
||||||
data["ResticPassword"] = pw
|
data["ResticPassword"] = pw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tároló section: DB dump directory and total size
|
||||||
|
data["DBDumpDir"] = s.cfg.Paths.DBDumpDir
|
||||||
|
var dbDumpTotalBytes int64
|
||||||
|
for _, f := range fullStatus.DumpFiles {
|
||||||
|
dbDumpTotalBytes += f.Size
|
||||||
|
}
|
||||||
|
data["DBDumpTotalBytes"] = dbDumpTotalBytes
|
||||||
|
|
||||||
|
// Tároló section: deduplicated Tier 2 destination list
|
||||||
|
tier2DestMap := make(map[string]map[string]string)
|
||||||
|
for _, item := range fullStatus.CrossDriveSummary {
|
||||||
|
if item.DestPath == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := tier2DestMap[item.DestPath]; !exists {
|
||||||
|
tier2DestMap[item.DestPath] = map[string]string{
|
||||||
|
"Path": item.DestPath,
|
||||||
|
"Label": item.DestLabel,
|
||||||
|
"Method": item.MethodLabel,
|
||||||
|
"SizeHuman": item.SizeHuman,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tier2DestList []map[string]string
|
||||||
|
for _, d := range tier2DestMap {
|
||||||
|
tier2DestList = append(tier2DestList, d)
|
||||||
|
}
|
||||||
|
data["Tier2Dests"] = tier2DestList
|
||||||
} else {
|
} else {
|
||||||
data["Backup"] = nil
|
data["Backup"] = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -346,7 +346,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Azonosító</th>
|
<th>Azonosító</th>
|
||||||
<th>Időpont</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>Új fájl</th>
|
||||||
<th>Változott</th>
|
<th>Változott</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -356,9 +356,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="mono">{{shortID .SnapshotID}}</td>
|
<td class="mono">{{shortID .SnapshotID}}</td>
|
||||||
<td class="mono">{{fmtTime .Time}}</td>
|
<td class="mono">{{fmtTime .Time}}</td>
|
||||||
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{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}}–{{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}}–{{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>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -376,10 +376,14 @@
|
|||||||
<!-- Section 6: Repository -->
|
<!-- Section 6: Repository -->
|
||||||
<div class="repo-card">
|
<div class="repo-card">
|
||||||
<h3>Tároló</h3>
|
<h3>Tároló</h3>
|
||||||
|
|
||||||
|
<!-- 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-rows">
|
||||||
<div class="repo-info-row">
|
<div class="repo-info-row">
|
||||||
<span class="repo-label">Helyszín:</span>
|
<span class="repo-label">Helyszín:</span>
|
||||||
<span class="repo-value mono">{{.Backup.RepoPath}} (helyi)</span>
|
<span class="repo-value mono">{{.Backup.RepoPath}} <span class="relative-time">(helyi SSD)</span></span>
|
||||||
</div>
|
</div>
|
||||||
{{if .Backup.RepoStats}}
|
{{if .Backup.RepoStats}}
|
||||||
<div class="repo-info-row">
|
<div class="repo-info-row">
|
||||||
@@ -421,18 +425,52 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="repo-paths">
|
<div class="repo-paths">
|
||||||
<span class="repo-label">Mentett útvonalak:</span>
|
<span class="repo-label">Mentett útvonalak (forrás):</span>
|
||||||
<ul class="repo-path-list">
|
<ul class="repo-path-list">
|
||||||
{{range .Backup.BackupPaths}}
|
{{range .Backup.BackupPaths}}
|
||||||
<li class="mono">{{.}}</li>
|
<li class="mono">{{.}}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="repo-remote">
|
</div>
|
||||||
<span class="repo-label">Távoli másolat:</span>
|
|
||||||
<div class="repo-remote-status">
|
<!-- Tier 2: Cross-drive backup destinations -->
|
||||||
<span class="relative-time">Nincs beállítva</span>
|
{{if .Tier2Dests}}
|
||||||
<span class="relative-time">(B2/S3/SFTP támogatás hamarosan)</span>
|
<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}}
|
||||||
|
|
||||||
|
<!-- 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<span class="system-info-label">Hőmérséklet</span>
|
<span class="system-info-label">Hőmérséklet</span>
|
||||||
<span class="system-info-value">
|
<span class="system-info-value">
|
||||||
<span class="temp-dot temp-dot-{{tempColor .SystemInfo.TemperatureCelsius}}"></span>
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,17 +106,24 @@
|
|||||||
<span class="backup-value">{{len .DBDumpStatus.Results}} mentve</span>
|
<span class="backup-value">{{len .DBDumpStatus.Results}} mentve</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .BackupStatus}}{{if .BackupStatus.RepoStats}}
|
{{if gt .CrossDriveTotal 0}}
|
||||||
<div class="backup-info-row">
|
<div class="backup-info-row">
|
||||||
<span class="backup-label">Tároló méret:</span>
|
<span class="backup-label">2. mentés:</span>
|
||||||
<span class="backup-value">{{.BackupStatus.RepoStats.TotalSize}} ({{.BackupStatus.RepoStats.SnapshotCount}} pillanatkép)</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>
|
</div>
|
||||||
{{end}}{{end}}
|
{{end}}
|
||||||
<div class="backup-actions" style="margin-top: .75rem;">
|
{{if gt .CrossDriveFailed 0}}
|
||||||
<button class="btn btn-sm btn-primary" onclick="triggerBackup()" id="backup-btn">
|
<div class="backup-info-row">
|
||||||
{{if .BackupRunning}}Mentés folyamatban...{{else}}Mentés most{{end}}
|
<span class="backup-label" style="color:var(--red)">Figyelmeztetés:</span>
|
||||||
</button>
|
<span class="backup-value backup-status-fail">⚠ {{.CrossDriveFailed}} 2. mentés sikertelen</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
@@ -163,28 +170,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</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" .}}
|
{{template "layout_end" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -255,9 +255,26 @@
|
|||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<h4>Automatikusan generált értékek</h4>
|
<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>
|
<p class="form-section-desc">Ezek az értékek automatikusan létrejönnek a telepítéskor.</p>
|
||||||
|
{{$autoValues := .AutoFieldValues}}
|
||||||
|
{{$isDeployed := .AlreadyDeployed}}
|
||||||
{{range .AutoFields}}
|
{{range .AutoFields}}
|
||||||
|
{{$val := index $autoValues .EnvVar}}
|
||||||
<div class="form-group form-group-auto">
|
<div class="form-group form-group-auto">
|
||||||
<label>{{.Label}}</label>
|
<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>
|
<span class="auto-generated-badge">✓ Automatikusan generálva</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -438,6 +455,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (sel) checkStorageSpace(sel);
|
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) {
|
function generatePassword(fieldId) {
|
||||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
let pass = '';
|
let pass = '';
|
||||||
|
|||||||
@@ -31,12 +31,14 @@
|
|||||||
{{if .Alerts}}
|
{{if .Alerts}}
|
||||||
<div class="alerts-container">
|
<div class="alerts-container">
|
||||||
{{range .Alerts}}
|
{{range .Alerts}}
|
||||||
|
{{if or (not .PageOnly) (pageMatch .PageOnly $.Page)}}
|
||||||
<div class="alert-banner alert-banner-{{.Level}}">
|
<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-icon">{{if eq .Level "error"}}🔴{{else if eq .Level "warning"}}🟡{{else}}ℹ️{{end}}</span>
|
||||||
<span class="alert-message">{{.Message}}</span>
|
<span class="alert-message">{{.Message}}</span>
|
||||||
{{if .Link}}<a href="{{.Link}}" class="alert-link">{{.LinkText}} →</a>{{end}}
|
{{if .Link}}<a href="{{.Link}}" class="alert-link">{{.LinkText}} →</a>{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -36,7 +36,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="monitor-card">
|
||||||
<h3>Távoli monitoring</h3>
|
<h3>Távoli monitoring</h3>
|
||||||
{{if not .MonitoringEnabled}}
|
{{if not .MonitoringEnabled}}
|
||||||
@@ -67,7 +96,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section 2: System Metrics Charts -->
|
<!-- Section 3: System Metrics Charts -->
|
||||||
<div class="monitor-card">
|
<div class="monitor-card">
|
||||||
<div class="monitor-card-header">
|
<div class="monitor-card-header">
|
||||||
<h3>Rendszer metrikák</h3>
|
<h3>Rendszer metrikák</h3>
|
||||||
@@ -102,7 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section 3: Container Resources -->
|
<!-- Section 4: Container Resources -->
|
||||||
<div class="monitor-card">
|
<div class="monitor-card">
|
||||||
<h3>Alkalmazás erőforrások</h3>
|
<h3>Alkalmazás erőforrások</h3>
|
||||||
<div class="container-charts-row" id="container-charts">
|
<div class="container-charts-row" id="container-charts">
|
||||||
@@ -120,7 +149,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</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" id="container-detail-panel" style="display:none">
|
||||||
<div class="monitor-card-header">
|
<div class="monitor-card-header">
|
||||||
<h3 id="container-detail-title">–</h3>
|
<h3 id="container-detail-title">–</h3>
|
||||||
@@ -144,35 +173,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 src="/static/chart.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
|
|||||||
@@ -1240,15 +1240,27 @@ a.stat-card:hover {
|
|||||||
/* Temperature dot */
|
/* Temperature dot */
|
||||||
.temp-dot {
|
.temp-dot {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 8px;
|
width: 11px;
|
||||||
height: 8px;
|
height: 11px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: .25rem;
|
margin-right: .25rem;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.temp-dot-green { background: var(--green); box-shadow: 0 0 4px rgba(35, 134, 54, 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 4px rgba(210, 153, 34, 0.5); }
|
.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 4px rgba(218, 54, 51, 0.5); }
|
.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 {
|
.system-info-item-compact {
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
@@ -1461,6 +1473,20 @@ a.stat-card:hover {
|
|||||||
color: var(--text-muted);
|
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 {
|
.snapshot-footer {
|
||||||
padding: .75rem .75rem 0;
|
padding: .75rem .75rem 0;
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
@@ -1524,6 +1550,24 @@ a.stat-card:hover {
|
|||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
padding-top: .75rem;
|
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 {
|
.repo-remote-status {
|
||||||
margin-top: .25rem;
|
margin-top: .25rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user