Skip to content

Commit 9807a44

Browse files
author
jacquesbach
committed
Gridstack editable only for admin
1 parent 059efdf commit 9807a44

3 files changed

Lines changed: 57 additions & 120 deletions

File tree

routes.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,20 +1065,25 @@ def get_layout():
10651065
def save_layout():
10661066
data = request.json
10671067
layout_json = data.get('layout')
1068-
password = data.get('pw') # Wir senden das PW zur Sicherheit mit
1068+
password = data.get('pw')
10691069

1070-
# Berechtigung prüfen (wie in deinem /api/auth)
1070+
# Berechtigung streng prüfen
10711071
if password != ADMIN_PASS:
10721072
return jsonify({"error": "Nicht autorisiert"}), 403
10731073

1074-
if not layout_json:
1075-
return jsonify({"error": "Kein Layout gesendet"}), 400
1076-
10771074
conn = get_db_connection()
10781075
c = conn.cursor()
1079-
# Speichern oder Überschreiben
1080-
c.execute("INSERT OR REPLACE INTO user_settings (key, value) VALUES (?, ?)",
1081-
('dashboard_layout', json.dumps(layout_json)))
1076+
1077+
# Wenn das Signal "RESET" kommt, löschen wir das gespeicherte Layout
1078+
if layout_json == "RESET":
1079+
c.execute("DELETE FROM user_settings WHERE key = 'dashboard_layout'")
1080+
elif layout_json is not None:
1081+
c.execute("INSERT OR REPLACE INTO user_settings (key, value) VALUES (?, ?)",
1082+
('dashboard_layout', json.dumps(layout_json)))
1083+
else:
1084+
conn.close()
1085+
return jsonify({"error": "Kein Layout gesendet"}), 400
1086+
10821087
conn.commit()
10831088
conn.close()
10841089

static/js/layout.js

Lines changed: 32 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,45 @@
11
let dashboardGrid;
2-
let currentPw = sessionStorage.getItem('admin_pw') || "";
2+
let currentPw = "";
33

44
// 1. Grid initialisieren
55
function initGridstack() {
6-
const isAdmin = sessionStorage.getItem('admin_pw') !== null;
7-
86
dashboardGrid = GridStack.init({
97
cellHeight: 110,
108
margin: 20,
119
animate: true,
12-
staticGrid: !isAdmin, // Wenn nicht Admin, dann gesperrt (kein Drag/Resize)
10+
staticGrid: true,
1311
disableOneColumnMode: false,
1412
oneColumnModeDomSort: true
1513
});
1614

17-
// Falls das PW schon im Speicher war (nach Refresh), UI direkt anpassen
18-
if (isAdmin) {
19-
currentPw = sessionStorage.getItem('admin_pw');
20-
document.getElementById('unlockBtn').style.display = 'none';
21-
document.getElementById('adminArea').style.display = 'flex';
22-
// Hier ggf. loadTariffs() aufrufen, falls script.js schon geladen ist
23-
}
24-
2515
dashboardGrid.on('change', function(event, items) {
2616
saveLayout();
2717
});
2818
}
2919

30-
// 3. Layout speichern (Auth-Check + DB vs. LocalStorage)
20+
// 2. Layout speichern (Nur in DB und nur wenn PW da ist)
3121
async function saveLayout() {
32-
if (!dashboardGrid) return;
22+
if (!dashboardGrid || !currentPw) return;
23+
3324
const layoutData = dashboardGrid.save();
3425

35-
// Wir schauen nach, ob ein Passwort im SessionStorage liegt
36-
// (Das müsstest du in deiner unlockAdmin() Funktion dort speichern)
37-
const storedPw = sessionStorage.getItem('admin_pw');
38-
39-
if (storedPw) {
40-
try {
41-
const response = await fetch('/api/layout', {
42-
method: 'POST',
43-
headers: { 'Content-Type': 'application/json' },
44-
body: JSON.stringify({
45-
layout: layoutData,
46-
pw: storedPw
47-
})
48-
});
49-
50-
if (response.ok) {
51-
console.log("Layout in DB gespeichert.");
52-
return;
53-
}
54-
} catch (e) { console.error("DB Save failed", e); }
55-
}
56-
57-
// Fallback: LocalStorage für Gäste
58-
localStorage.setItem('balkonkraftwerk_layout', JSON.stringify(layoutData));
59-
console.log("Layout lokal im Browser gespeichert.");
26+
try {
27+
const response = await fetch('/api/layout', {
28+
method: 'POST',
29+
headers: { 'Content-Type': 'application/json' },
30+
body: JSON.stringify({
31+
layout: layoutData,
32+
pw: currentPw
33+
})
34+
});
35+
36+
if (response.ok) {
37+
console.log("Layout in DB für alle Nutzer aktualisiert.");
38+
}
39+
} catch (e) { console.error("DB Save failed", e); }
6040
}
6141

62-
// 4. Layout beim Starten laden
42+
// 3. Layout beim Starten laden
6343
async function loadLayout() {
6444
let savedLayout = null;
6545
try {
@@ -70,85 +50,30 @@ async function loadLayout() {
7050
}
7151
} catch (e) { console.error("DB Load failed", e); }
7252

73-
if (!savedLayout) {
74-
const localData = localStorage.getItem('balkonkraftwerk_layout');
75-
if (localData) savedLayout = JSON.parse(localData);
76-
}
77-
53+
// Wenn ein Layout auf dem Server existiert, anwenden
7854
if (savedLayout && dashboardGrid) {
7955
dashboardGrid.removeAll();
8056
dashboardGrid.load(savedLayout);
81-
console.log("Layout sauber neu geladen.");
57+
console.log("Globales Layout erfolgreich geladen.");
8258
}
83-
84-
8559
}
8660

61+
// 4. Reset-Funktion für den Button im Admin-Bereich
8762
async function resetDatabaseLayout() {
88-
if (!confirm("Möchtest du das Layout wirklich auf Standard zurücksetzen?")) return;
63+
if (!confirm("Möchtest du das Layout für ALLE Nutzer auf den Standard zurücksetzen?")) return;
8964

90-
const storedPw = sessionStorage.getItem('admin_pw');
91-
if (!storedPw) return alert("Bitte erst einloggen!");
65+
if (!currentPw) return alert("Bitte erst als Admin einloggen!");
9266

93-
// Wir senden ein leeres Array oder null, damit der Server es löscht/leert
94-
await fetch('/api/layout', {
67+
const res = await fetch('/api/layout', {
9568
method: 'POST',
9669
headers: { 'Content-Type': 'application/json' },
9770
body: JSON.stringify({
98-
layout: [], // Leeres Layout erzwingen
99-
pw: storedPw
71+
layout: "RESET", // Signalwort für das Python-Backend
72+
pw: currentPw
10073
})
10174
});
102-
location.reload();
103-
}
104-
105-
document.addEventListener("DOMContentLoaded", async () => {
106-
// --- PHASE 1: Das Gerüst aufbauen ---
107-
initGridstack();
108-
await loadLayout(); // Wartet, bis Boxen aus DB oder LocalStorage da sind
109-
110-
// --- PHASE 2: Startwerte für Datumsfelder setzen ---
111-
const t = new Date().toISOString().split('T')[0];
112-
const startInput = document.getElementById('start');
113-
const endInput = document.getElementById('end');
114-
115-
if (startInput && endInput) {
116-
startInput.value = t;
117-
endInput.value = t;
118-
startInput.addEventListener('change', updateQuickButtonsActiveState);
119-
endInput.addEventListener('change', updateQuickButtonsActiveState);
120-
}
121-
122-
// --- PHASE 3: Daten in die Boxen pumpen ---
123-
// Wir prüfen bei jeder Funktion, ob sie existiert, um Fehler zu vermeiden
124-
try {
125-
if (typeof fetchData === "function") await fetchData();
126-
if (typeof updateWeather === "function") updateWeather();
127-
if (typeof updateLive === "function") updateLive();
128-
if (typeof updatePeaks === "function") updatePeaks();
129-
if (typeof updateQuickButtonsActiveState === "function") updateQuickButtonsActiveState();
130-
131-
// ML-Funktionen
132-
if (typeof loadForecast === "function") loadForecast();
133-
if (typeof loadGlobalShap === "function") loadGlobalShap();
134-
if (typeof loadFeatureImportance === "function") loadFeatureImportance();
135-
136-
// Heatmaps
137-
if (typeof initHeatmapYears === "function") initHeatmapYears();
138-
if (typeof initHourlyHeatmap === "function") initHourlyHeatmap();
139-
140-
} catch (err) {
141-
console.error("Fehler beim initialen Daten-Load:", err);
75+
76+
if (res.ok) {
77+
location.reload(); // Seite neu laden, um Standard-HTML zu zeigen
14278
}
143-
144-
// --- PHASE 4: Intervalle für Updates starten ---
145-
setInterval(() => { if (typeof updateLive === "function") updateLive(); }, 5000);
146-
setInterval(() => { if (typeof fetchData === "function") fetchData(); }, 60000);
147-
setInterval(() => { if (typeof updatePeaks === "function") updatePeaks(); }, 60000);
148-
setInterval(() => { if (typeof checkLoadingStatus === "function") checkLoadingStatus(); }, 500);
149-
150-
// WICHTIG: Einmal kräftig schütteln, damit Charts ihre Größe im neuen Grid finden
151-
setTimeout(() => {
152-
window.dispatchEvent(new Event('resize'));
153-
}, 200);
154-
});
79+
}

static/js/script.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ async function unlockAdmin() {
3636
});
3737

3838
if (res.ok) {
39-
currentPw = pw;
40-
sessionStorage.setItem('admin_pw', pw);
39+
currentPw = pw; // PW nur in den flüchtigen RAM laden
4140

4241
document.getElementById('unlockBtn').style.display = 'none';
4342
document.getElementById('adminArea').style.display = 'flex';
4443

4544
if (dashboardGrid) {
46-
dashboardGrid.enable();
45+
// Grid bearbeitbar machen
46+
dashboardGrid.setStatic(false);
4747
document.getElementById('dashboard-grid').classList.add('edit-mode');
4848
}
4949

@@ -54,8 +54,15 @@ async function unlockAdmin() {
5454
}
5555

5656
function closeAdmin() {
57-
document.getElementById('adminArea').style.display = 'none';
58-
document.getElementById('unlockBtn').style.display = 'block';
57+
currentPw = "";
58+
document.getElementById('adminArea').style.display = 'none';
59+
document.getElementById('unlockBtn').style.display = 'block';
60+
61+
if (dashboardGrid) {
62+
// Grid wieder für alle sperren
63+
dashboardGrid.setStatic(true);
64+
document.getElementById('dashboard-grid').classList.remove('edit-mode');
65+
}
5966
}
6067

6168
async function loadTariffs() {

0 commit comments

Comments
 (0)