From ca15266fb90e64b13f3de34b3f678453405fa6b9 Mon Sep 17 00:00:00 2001 From: nessi Date: Tue, 3 Feb 2026 18:46:33 +0100 Subject: [PATCH] Add local storage handling for chip selection in frontend Introduced functions to manage chip data in local storage, ensuring smoother handling of chip selections without backend dependency. Adjusted UI logic to display chip tags dynamically and modified "s" state handling to integrate local storage data seamlessly. --- frontend/src/App.jsx | 132 +++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index fad3ef8..0dad8b0 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -14,18 +14,43 @@ async function api(path, opts = {}) { } /** - * Tag-Rotation: - * null/"" -> i -> m -> s (öffnet Popup) -> (nach Auswahl) s.XX -> null + * Backend erlaubt: null | "i" | "m" | "s" + * Rotation: + * null -> i -> m -> s (Popup) -> null */ function cycleTag(tag) { if (!tag) return "i"; if (tag === "i") return "m"; if (tag === "m") return "s"; - if (tag === "s") return null; - if (typeof tag === "string" && tag.startsWith("s.")) return null; - return null; + return null; // "s" -> null } +/* ========= Chip localStorage (Frontend-only) ========= */ +function chipStorageKey(gameId, entryId) { + return `chip:${gameId}:${entryId}`; +} + +function getChipLS(gameId, entryId) { + try { + return localStorage.getItem(chipStorageKey(gameId, entryId)); + } catch { + return null; + } +} + +function setChipLS(gameId, entryId, chip) { + try { + localStorage.setItem(chipStorageKey(gameId, entryId), chip); + } catch {} +} + +function clearChipLS(gameId, entryId) { + try { + localStorage.removeItem(chipStorageKey(gameId, entryId)); + } catch {} +} + +/* ========= Admin Panel ========= */ function AdminPanel() { const [users, setUsers] = useState([]); @@ -158,6 +183,7 @@ function AdminPanel() { ); } +/* ========= App ========= */ export default function App() { const [me, setMe] = useState(null); const [loginEmail, setLoginEmail] = useState(""); @@ -169,7 +195,7 @@ export default function App() { const [sheet, setSheet] = useState(null); const [pulseId, setPulseId] = useState(null); - // ✅ Chip Modal State + // Chip popup const [chipOpen, setChipOpen] = useState(false); const [chipEntry, setChipEntry] = useState(null); @@ -220,7 +246,6 @@ export default function App() { @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes popIn { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: translateY(0) scale(1); } } @keyframes rowPulse { 0%{ transform: scale(1); } 50%{ transform: scale(1.01); } 100%{ transform: scale(1); } } - @keyframes candleGlow { 0% { opacity: .55; transform: translateY(0px) scale(1); filter: blur(16px); } 35% { opacity: .85; transform: translateY(-2px) scale(1.02); filter: blur(18px); } @@ -256,12 +281,10 @@ export default function App() { background: ${stylesTokens.pageBg}; color: ${stylesTokens.textMain}; } - body { overflow-x: hidden; -webkit-overflow-scrolling: touch; } - #root { background: transparent; } * { -webkit-tap-highlight-color: transparent; } `; @@ -340,22 +363,22 @@ export default function App() { setTimeout(() => setPulseId(null), 220); }; - /** - * Notiz-Button: - * - wenn m -> s: Popup öffnen und NICHT sofort ins Backend schreiben - * - wenn s.XX -> null: normal zurücksetzen - */ + // Notiz-Button: i -> m -> (Popup) s -> null const toggleTag = async (entry) => { const next = cycleTag(entry.note_tag); - // Wenn der nächste Schritt "s" wäre -> Popup öffnen + // Wenn wir zu "s" gehen würden -> Chip Popup öffnen, aber NICHT ins Backend schreiben if (next === "s") { setChipEntry(entry); setChipOpen(true); return; } - // normal schreiben (i, m, oder null) + // Wenn wir auf null gehen, Chip lokal löschen (weil s -> —) + if (next === null) { + clearChipLS(gameId, entry.entry_id); + } + await api(`/games/${gameId}/sheet/${entry.entry_id}`, { method: "PATCH", body: JSON.stringify({ note_tag: next }), @@ -363,13 +386,17 @@ export default function App() { await reloadSheet(); }; - // Chip auswählen -> note_tag wird s.XX + // Chip wählen: + // Backend: note_tag = "s" + // Frontend: Chip in localStorage const chooseChip = async (chip) => { if (!chipEntry) return; + setChipLS(gameId, chipEntry.entry_id, chip); + await api(`/games/${gameId}/sheet/${chipEntry.entry_id}`, { method: "PATCH", - body: JSON.stringify({ note_tag: `s.${chip}` }), + body: JSON.stringify({ note_tag: "s" }), }); setChipOpen(false); @@ -377,19 +404,35 @@ export default function App() { await reloadSheet(); }; - // X drücken: zurück auf — + // X im Modal: + // Backend zurück auf null und lokalen Chip löschen const closeChipModalToDash = async () => { if (chipEntry) { + clearChipLS(gameId, chipEntry.entry_id); + await api(`/games/${gameId}/sheet/${chipEntry.entry_id}`, { method: "PATCH", body: JSON.stringify({ note_tag: null }), }); + await reloadSheet(); } setChipOpen(false); setChipEntry(null); }; + // Anzeige im Tag-Button: + // - "s" wird zu "s.AL" (aus localStorage), sonst "s.XX" + const displayTag = (entry) => { + const t = entry.note_tag; + if (!t) return "—"; + if (t === "s") { + const chip = getChipLS(gameId, entry.entry_id); + return `s.${chip || "XX"}`; + } + return t; + }; + // --- helpers --- const getRowBg = (status) => { if (status === 1) return "rgba(255, 35, 35, 0.16)"; @@ -416,10 +459,7 @@ export default function App() { if (status === 2) return { color: "#baf3c9", background: "rgba(0,190,80,0.18)" }; if (status === 1) return { color: "#ffb3b3", background: "rgba(255,35,35,0.18)" }; if (status === 3) - return { - color: "rgba(233,216,166,0.85)", - background: "rgba(140,140,140,0.14)", - }; + return { color: "rgba(233,216,166,0.85)", background: "rgba(140,140,140,0.14)" }; return { color: "rgba(233,216,166,0.75)", background: "rgba(255,255,255,0.08)" }; }; @@ -438,9 +478,7 @@ export default function App() {
Zauber-Detektiv Notizbogen
-
- Melde dich an, um dein Cluedo-Magie-Sheet zu öffnen -
+
Melde dich an, um dein Cluedo-Magie-Sheet zu öffnen
@@ -564,13 +602,7 @@ export default function App() {
- +
@@ -578,13 +610,7 @@ export default function App() {
- +
@@ -624,7 +650,9 @@ export default function App() {
2) i / m / s Button (Notiz)
-
Rechts pro Zeile gibt es einen Button, der rotiert:
+
+ Rechts pro Zeile gibt es einen Button, der durch diese Werte rotiert: +
@@ -642,7 +670,7 @@ export default function App() {
s
- s = „Ein anderer Spieler hat diese Karte“ (Popup) + s = „Ein anderer Spieler hat diese Karte“ (Chip Auswahl)
@@ -664,7 +692,7 @@ export default function App() {
)} - {/* ✅ Chip Popup */} + {/* Chip Popup */} {chipOpen && (
e.stopPropagation()}> @@ -699,12 +727,10 @@ export default function App() {
{sec.entries.map((e) => { - // ✅ i oder s.XX => visuell wie rot (nur UI) - const isIorS = - e.note_tag === "i" || - (typeof e.note_tag === "string" && e.note_tag.startsWith("s.")); - + // UI "rot" wenn note_tag i oder s (Backend s wird als s.XX angezeigt) + const isIorS = e.note_tag === "i" || e.note_tag === "s"; const effectiveStatus = e.status === 0 && isIorS ? 1 : e.status; + const badge = getStatusBadge(effectiveStatus); return ( @@ -744,12 +770,8 @@ export default function App() {
-
); @@ -1204,13 +1226,13 @@ const styles = { filter: "saturate(0.9) contrast(1.05) brightness(0.55)", }, - // Chip Buttons chipGrid: { marginTop: 12, display: "grid", gridTemplateColumns: "repeat(5, minmax(0, 1fr))", gap: 8, }, + chipBtn: { padding: "10px 14px", borderRadius: 12,