Merge fix/BUG-05-save-indicator: Speichern-Button weg, Save-Status-Indikator
This commit is contained in:
+81
-14
@@ -330,6 +330,38 @@
|
||||
display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
/* BUG-05: Save-Status-Indikator (ersetzt den redundanten Speichern-Button) */
|
||||
.save-indicator {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
margin-left: auto; /* rechtsbündig im toolbar */
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.82em;
|
||||
color: var(--fg-muted);
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
.save-indicator .save-dot {
|
||||
width: 8px; height: 8px; border-radius: 50%;
|
||||
background: var(--ok);
|
||||
box-shadow: 0 0 6px rgba(74, 222, 128, 0.5);
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease;
|
||||
}
|
||||
/* Zustände */
|
||||
.save-indicator[data-state="idle"] .save-dot { background: var(--ok); }
|
||||
.save-indicator[data-state="pending"] .save-dot {
|
||||
background: var(--warn); animation: savePulse 1s ease-in-out infinite;
|
||||
}
|
||||
.save-indicator[data-state="saving"] .save-dot {
|
||||
background: var(--info); animation: savePulse 0.6s ease-in-out infinite;
|
||||
}
|
||||
.save-indicator[data-state="error"] .save-dot {
|
||||
background: var(--alert); box-shadow: 0 0 6px rgba(248, 113, 113, 0.5);
|
||||
}
|
||||
.save-indicator[data-state="error"] { color: var(--alert); }
|
||||
@keyframes savePulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.35; }
|
||||
}
|
||||
.add-form {
|
||||
display: flex; gap: 8px; align-items: center;
|
||||
background: var(--surface-2);
|
||||
@@ -1037,7 +1069,12 @@
|
||||
<button type="submit" class="primary">+ Hinzufügen</button>
|
||||
</form>
|
||||
<button onclick="packAll()" title="Alle Items automatisch anordnen">Auto-Pack</button>
|
||||
<button onclick="saveLayout()" class="primary" title="Layout auf Server speichern">Speichern</button>
|
||||
<!-- BUG-05: Speichern-Button entfernt (redundant zu debouncedSave).
|
||||
Stattdessen subtiler Save-Status-Indikator rechts. -->
|
||||
<div class="save-indicator" id="saveIndicator" data-state="idle">
|
||||
<span class="save-dot"></span>
|
||||
<span class="save-label">Gespeichert</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gridPreview" class="grid-preview"></div>
|
||||
<div class="layout-hint">
|
||||
@@ -1280,8 +1317,50 @@
|
||||
updateClock();
|
||||
|
||||
// ============ DRAG & DROP LAYOUT EDITOR ============
|
||||
async function saveLayout() {
|
||||
// BUG-05: Status-Indikator statt redundantem "Speichern"-Button.
|
||||
// Status-Lebenszyklus:
|
||||
// pending → saving → idle (success) | error
|
||||
setSaveState('saving');
|
||||
const r = await fetch('/api/layout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ items: layoutItems }),
|
||||
});
|
||||
const j = await r.json();
|
||||
if (!j.ok) {
|
||||
setSaveState('error', 'Fehler: ' + (j.error || '?'));
|
||||
toast('Layout speichern fehlgeschlagen: ' + (j.error || '?'), 'error', 5000);
|
||||
return;
|
||||
}
|
||||
layoutItems = j.items;
|
||||
renderGrid();
|
||||
setTimeout(refreshPreview, 500);
|
||||
setSaveState('idle', 'Gespeichert');
|
||||
// nicht mehr länger toasten — der Indicator reicht
|
||||
}
|
||||
|
||||
// BUG-05: Save-Status-Indikator Helper
|
||||
function setSaveState(state, label) {
|
||||
const el = document.getElementById('saveIndicator');
|
||||
if (!el) return;
|
||||
el.dataset.state = state;
|
||||
const lbl = el.querySelector('.save-label');
|
||||
if (lbl && label) lbl.textContent = label;
|
||||
// Idle-Reset: nach kurzer Anzeige "Gespeichert" zurück
|
||||
if (state === 'saving' && !label) {
|
||||
const lblEl = el.querySelector('.save-label');
|
||||
if (lblEl) lblEl.textContent = 'Speichern…';
|
||||
}
|
||||
if (state === 'idle' && !label) {
|
||||
const lblEl = el.querySelector('.save-label');
|
||||
if (lblEl) lblEl.textContent = 'Gespeichert';
|
||||
}
|
||||
}
|
||||
// BUG-05: debouncedSave setzt pending-Status
|
||||
let saveTimer = null;
|
||||
function debouncedSave() {
|
||||
setSaveState('pending', 'Ungespeichert…');
|
||||
if (saveTimer) clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(saveLayout, 500);
|
||||
}
|
||||
@@ -1714,19 +1793,7 @@
|
||||
renderGrid();
|
||||
toast('Widget hinzugefügt', 'success', 2000);
|
||||
}
|
||||
async function saveLayout() {
|
||||
const r = await fetch('/api/layout', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ items: layoutItems }),
|
||||
});
|
||||
const j = await r.json();
|
||||
if (!j.ok) { toast('Layout speichern fehlgeschlagen: ' + (j.error || '?'), 'error', 5000); return; }
|
||||
layoutItems = j.items;
|
||||
renderGrid();
|
||||
setTimeout(refreshPreview, 500);
|
||||
toast('Layout gespeichert', 'success');
|
||||
}
|
||||
// BUG-05: alte saveLayout entfernt — siehe oben (mit setSaveState).
|
||||
async function packAll() {
|
||||
const ok = await modalConfirm({
|
||||
icon: 'warn',
|
||||
|
||||
Reference in New Issue
Block a user