14 KiB
TASK — v0.11.9 UI Polish Fixes
Context
These are UI-only fixes for the deploy/settings page backup section introduced in v0.11.8. No backend changes needed. All changes are in deploy.html and style.css.
Important design principle: Use minimal emojis in the UI. The project prefers a clean, professional look. Replace emoji indicators with text or CSS-styled elements instead.
Files to modify
internal/web/templates/deploy.htmlinternal/web/templates/style.cssinternal/web/templates/backups.html(emoji cleanup in cross-drive summary section)
Fix 1: Spacing between cards and "Automatikusan generált értékek"
Problem: No clear visual separation between the last card above the deploy form (deploy-cross-drive or deploy-stale-data) and the "Automatikusan generált értékek" section inside .deploy-form.
Root cause: .deploy-cross-drive has margin-bottom: 1rem which doesn't provide enough separation before the next card. When stale data card exists without cross-drive, it's also tight.
Fix in style.css:
/* Change margin-bottom from 1rem to 1.5rem */
.deploy-cross-drive {
/* ... existing ... */
margin-bottom: 1.5rem; /* was 1rem */
}
No change needed for .deploy-stale-data — it already has margin-bottom: 1.5rem. But verify the margin is actually applied (check if another element is overriding it or if the stale data card is inside a container that collapses margins).
Fix 2: Rename restic method + add info tooltip on "Módszer"
Problem: "Verziózott mentés (restic)" doesn't highlight the most important differentiator (encryption). Users should also understand the tradeoffs before picking.
Fix in deploy.html — method dropdown (around line 16934-16942):
Replace:
<div class="settings-row">
<span class="settings-label">Módszer</span>
<select name="cross_drive_method" class="form-control" style="max-width:20rem">
<option value="rsync" {{if and .CrossDriveConfig (eq .CrossDriveConfig.Method "rsync")}}selected{{end}}>
Egyszerű másolat (rsync)
</option>
<option value="restic" {{if and .CrossDriveConfig (eq .CrossDriveConfig.Method "restic")}}selected{{end}}>
Verziózott mentés (restic)
</option>
</select>
</div>
With:
<div class="settings-row">
<span class="settings-label">
Módszer
<span class="info-tooltip" tabindex="0">
<span class="info-icon">i</span>
<span class="info-tooltip-text">
<strong>Egyszerű másolat (rsync):</strong> Tükörszerű másolat, a fájlok közvetlenül böngészhetők.
Nem titkosított, nem verziózott — mindig a legfrissebb állapotot tartalmazza.
<br><br>
<strong>Titkosított mentés (restic):</strong> Titkosított, tömörített, verziózott mentés.
Korábbi állapotok visszaállíthatók. Nem böngészhető közvetlenül —
visszaállításhoz a vezérlőpult szükséges.
</span>
</span>
</span>
<select name="cross_drive_method" class="form-control" style="max-width:20rem">
<option value="rsync" {{if and .CrossDriveConfig (eq .CrossDriveConfig.Method "rsync")}}selected{{end}}>
Egyszerű másolat (rsync)
</option>
<option value="restic" {{if and .CrossDriveConfig (eq .CrossDriveConfig.Method "restic")}}selected{{end}}>
Titkosított mentés (restic)
</option>
</select>
</div>
Add to style.css:
/* Info tooltip (i icon with hover popup) */
.info-tooltip {
position: relative;
display: inline-flex;
align-items: center;
margin-left: .35rem;
cursor: help;
}
.info-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid var(--text-muted);
color: var(--text-muted);
font-size: .65rem;
font-weight: 700;
font-style: italic;
font-family: Georgia, serif;
line-height: 1;
}
.info-tooltip-text {
display: none;
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
width: 320px;
padding: .75rem 1rem;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: var(--radius);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
font-size: .8rem;
font-weight: 400;
line-height: 1.5;
color: var(--text-secondary);
z-index: 100;
white-space: normal;
}
/* Show on hover or focus (for keyboard) */
.info-tooltip:hover .info-tooltip-text,
.info-tooltip:focus .info-tooltip-text,
.info-tooltip:focus-within .info-tooltip-text {
display: block;
}
/* Arrow pointing down from tooltip */
.info-tooltip-text::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: var(--border-color);
}
Fix 3: Cursor on "Napi mentésbe foglalás" label
Problem: The <label class="toggle"> wrapping the disabled checkbox uses cursor: pointer from the .toggle class, making it look clickable when it's not.
Fix in deploy.html (around line 16886-16890):
Replace:
<div class="cross-drive-nightly">
<label class="toggle">
<input type="checkbox" id="app-backup-enabled" {{if .AppBackupEnabled}}checked{{end}} disabled>
<span class="toggle-label">Napi mentésbe foglalás (restic, helyi)</span>
</label>
With:
<div class="cross-drive-nightly">
<div class="cross-drive-nightly-status">
{{if .AppBackupEnabled}}
<span class="nightly-status-indicator nightly-enabled"></span>
{{else}}
<span class="nightly-status-indicator nightly-disabled"></span>
{{end}}
<span class="toggle-label">Napi mentésbe foglalás (restic, helyi)</span>
</div>
This replaces the disabled checkbox with a simple colored dot indicator — green if enabled, gray if not. Not clickable, not a checkbox, no confusing cursor.
Add to style.css:
/* Nightly backup status indicator (non-interactive) */
.cross-drive-nightly-status {
display: flex;
align-items: center;
gap: .5rem;
}
.nightly-status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.nightly-enabled {
background: var(--green);
box-shadow: 0 0 4px rgba(35, 134, 54, 0.4);
}
.nightly-disabled {
background: var(--text-muted);
opacity: 0.5;
}
Fix 4: Progressive disclosure — disable config fields until enabled
Problem: Users see the destination/method/schedule dropdowns and might think the backup is already configured/active, even though the "Engedélyezve" checkbox is unchecked.
Fix in deploy.html — add JS to toggle field states:
Add onchange handler to the enabled checkbox and disabled attribute to the select fields:
<div class="settings-row">
<span class="settings-label">Engedélyezve</span>
<label class="toggle" style="margin:0">
<input type="checkbox" name="cross_drive_enabled" id="cross-drive-enabled"
{{if and .CrossDriveConfig .CrossDriveConfig.Enabled}}checked{{end}}
onchange="toggleCrossDriveFields()">
<span class="toggle-label">Igen</span>
</label>
</div>
<div class="settings-row">
<span class="settings-label">Cél tárhely</span>
<select name="cross_drive_dest" id="cd-dest" class="form-control cross-drive-field" style="max-width:20rem"
{{if not (and .CrossDriveConfig .CrossDriveConfig.Enabled)}}disabled{{end}}>
...options...
</select>
</div>
<div class="settings-row">
<span class="settings-label">
Módszer
<span class="info-tooltip" tabindex="0">...</span>
</span>
<select name="cross_drive_method" id="cd-method" class="form-control cross-drive-field" style="max-width:20rem"
{{if not (and .CrossDriveConfig .CrossDriveConfig.Enabled)}}disabled{{end}}>
...options...
</select>
</div>
<div class="settings-row">
<span class="settings-label">Ütemezés</span>
<select name="cross_drive_schedule" id="cd-schedule" class="form-control cross-drive-field" style="max-width:20rem"
{{if not (and .CrossDriveConfig .CrossDriveConfig.Enabled)}}disabled{{end}}>
...options...
</select>
</div>
Add JS function (inside the existing <script> block at the bottom of deploy.html):
function toggleCrossDriveFields() {
var enabled = document.getElementById('cross-drive-enabled').checked;
var fields = document.querySelectorAll('.cross-drive-field');
for (var i = 0; i < fields.length; i++) {
fields[i].disabled = !enabled;
}
}
Important: The disabled attribute prevents form submission of those fields. The backend handler for POST /settings/cross-backup/{name} must handle missing form values gracefully — if cross_drive_enabled is unchecked (not in form data), set Enabled: false and preserve existing dest/method/schedule values from settings (don't reset them to empty strings just because they weren't submitted).
Check internal/web/handlers.go (or internal/api/router.go) — the saveCrossBackupConfig handler. If it reads form values like:
cfg.DestinationPath = req.FormValue("cross_drive_dest")
cfg.Method = req.FormValue("cross_drive_method")
Then when disabled fields aren't submitted, these would be empty strings. Fix: only update those fields if enabled is true, otherwise preserve existing config:
enabled := req.FormValue("cross_drive_enabled") == "on"
// Load existing config to preserve values when disabled
existing := s.settings.GetCrossDriveConfig(name)
cfg := settings.CrossDriveBackup{
Enabled: enabled,
}
if enabled {
// Read from form
cfg.DestinationPath = req.FormValue("cross_drive_dest")
cfg.Method = req.FormValue("cross_drive_method")
cfg.Schedule = req.FormValue("cross_drive_schedule")
} else if existing != nil {
// Preserve existing settings when disabling
cfg.DestinationPath = existing.DestinationPath
cfg.Method = existing.Method
cfg.Schedule = existing.Schedule
}
// Always preserve runtime state
if existing != nil {
cfg.LastRun = existing.LastRun
cfg.LastStatus = existing.LastStatus
cfg.LastError = existing.LastError
cfg.LastDuration = existing.LastDuration
cfg.LastSizeHuman = existing.LastSizeHuman
}
Fix 5: Remove/reduce emojis
Problem: Too many emoji throughout the UI. Replace with text or CSS-styled elements.
deploy.html changes:
| Location | Current | Replace with |
|---|---|---|
| Line ~16884 h4 | 🔒 Biztonsági mentés |
Biztonsági mentés |
| Line ~16902 warning | ⚠️ {{.BackupDestWarning}} |
{{.BackupDestWarning}} (the .alert-warning class already visually marks it as warning) |
| Line ~16964 status ok | ✅ Sikeres |
Sikeres |
| Line ~16964 status error | ❌ Hiba: |
Hiba: |
| Line ~16964 status running | ⏳ Fut... |
Fut... |
| Line ~16982 bottom hint | ⚠️ A cél meghajtó legyen... |
A cél meghajtó legyen... (already inside form-hint, visually distinct) |
| Stale data delete button | 🗑️ Korábbi adatok törlése |
Korábbi adatok törlése |
| Stale data h4 | 🗑️ Korábbi adatok (if emoji present) |
Korábbi adatok |
backups.html cross-drive summary section changes:
| Location | Current | Replace with |
|---|---|---|
| Status ok badge | ✅ {{.LastRunShort}} |
{{.LastRunShort}} (use .meta-badge-ok class for green) |
| Status error badge | ❌ Hiba |
Hiba (use .meta-badge-fail class for red) |
| Running badge | ⏳ Fut... |
Fut... |
| Schedule badge | ⏰ {{.ScheduleLabel}} |
{{.ScheduleLabel}} |
| Unconfigured apps warning | ⚠️ {{len ...}} |
{{len ...}} (already in yellow-colored div) |
Note: The .alert-warning class already provides visual differentiation (yellow/orange background/border), and .meta-badge-ok/.meta-badge-fail provide green/red colors. Emojis are redundant with these CSS classes.
Also check for any remaining emoji in:
stacks.html— if any status emojis were leftbackups.html— the existing sections (db dumps, etc.)
Fix 6 (bonus): "Automatikusan generálva" badge also uses emoji
In the auto-generated values section (line ~17031):
<span class="auto-generated-badge">✓ Automatikusan generálva</span>
This is fine — the checkmark (✓) is a Unicode character, not an emoji. Leave as-is.
Summary of visual outcome
Before (v0.11.8):
- Disabled checkbox that looks clickable
- Pointer cursor on non-interactive label
- All form fields visible/enabled even when backup disabled
- Emoji scattered throughout
- No explanation of rsync vs restic
- Tight spacing between sections
After (v0.11.9):
- Clean green/gray dot indicator for nightly backup status
- Default cursor on non-interactive elements
- Dropdowns grayed out until "Engedélyezve" is checked → clear progressive disclosure
- No emoji — clean professional look using CSS classes for visual feedback
- Info tooltip on "Módszer" explaining both options clearly
- Proper spacing between all sections
- "Titkosított mentés" label better communicates restic's key advantage
Testing
- Visit deployed app page (e.g.,
/stacks/immich/deploy) - Verify spacing between all cards is consistent
- Verify no emoji visible in the backup section
- Verify nightly backup shows green dot (if enabled) or gray dot (if not)
- Verify cursor doesn't change to pointer on the nightly status line
- Uncheck "Engedélyezve" → dropdowns should become disabled/grayed
- Check "Engedélyezve" → dropdowns should become active
- Save with "Engedélyezve" unchecked → verify existing config preserved (check settings.json)
- Hover/focus on the (i) icon next to "Módszer" → tooltip appears with explanation
- Check backup page → no emoji in cross-drive summary section