From 6a5ff44135ff3ccb580d671f84457e8bcd0ea194 Mon Sep 17 00:00:00 2001 From: nessi Date: Tue, 3 Feb 2026 15:00:31 +0100 Subject: [PATCH] Add chip selection feature for "s" tags Implemented a chip selection modal that appears when cycling to the "s" tag. Users can now assign specific chips (e.g., "s.AL") to entries, improving interactivity and flexibility in tagging. --- frontend/src/App.jsx | 113 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 41e2392..fc6645c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; const API = "/api"; +const CHIP_LIST = ["AL", "JG", "JN", "SN", "TL"]; async function api(path, opts = {}) { const res = await fetch(API + path, { @@ -12,10 +13,22 @@ async function api(path, opts = {}) { return res.json(); } -function cycleTag(tag) { - if (!tag) return "i"; - if (tag === "i") return "m"; - if (tag === "m") return "s"; +function parseTag(tag) { + // erlaubt: null | "i" | "m" | "s" | "s.AL" + if (!tag) return { base: null, chip: null }; + if (tag === "i") return { base: "i", chip: null }; + if (tag === "m") return { base: "m", chip: null }; + if (tag === "s") return { base: "s", chip: null }; + if (tag.startsWith("s.")) return { base: "s", chip: tag.slice(2) || null }; + return { base: tag, chip: null }; +} + +function nextBaseTag(tag) { + const { base } = parseTag(tag); + if (!base) return "i"; + if (base === "i") return "m"; + if (base === "m") return "s"; // hier öffnen wir dann das Popup + // wenn s (egal ob s oder s.XY), dann zurück auf leer return null; } @@ -162,6 +175,9 @@ export default function App() { const [sheet, setSheet] = useState(null); const [pulseId, setPulseId] = useState(null); + const [chipPickOpen, setChipPickOpen] = useState(false); + const [chipPickEntry, setChipPickEntry] = useState(null); + const [helpOpen, setHelpOpen] = useState(false); const load = async () => { @@ -332,11 +348,38 @@ export default function App() { }; const toggleTag = async (entry) => { - const next = cycleTag(entry.note_tag); + const next = nextBaseTag(entry.note_tag); + + // Wenn wir bei "s" angekommen sind -> Popup öffnen statt sofort setzen + if (next === "s") { + setChipPickEntry(entry); + setChipPickOpen(true); + return; + } + + // normal setzen (— / i / m) await api(`/games/${gameId}/sheet/${entry.entry_id}`, { method: "PATCH", body: JSON.stringify({ note_tag: next }), }); + + await reloadSheet(); + }; + + const closeChipPick = () => { + setChipPickOpen(false); + setChipPickEntry(null); + }; + + const chooseChip = async (chip) => { + if (!chipPickEntry) return; + + await api(`/games/${gameId}/sheet/${chipPickEntry.entry_id}`, { + method: "PATCH", + body: JSON.stringify({ note_tag: `s.${chip}` }), + }); + + closeChipPick(); await reloadSheet(); }; @@ -573,6 +616,47 @@ export default function App() { )} + {chipPickOpen && ( +
+
e.stopPropagation()}> +
+
+ Wer hat die Karte? +
+ +
+ +
+ Chip auswählen: +
+ +
+ {CHIP_LIST.map((c) => ( + + ))} +
+ +
+ Tipp: Wenn du wieder auf den Notiz-Button klickst, geht’s von s.XX zurück auf . +
+
+
+ )} + +
{sections.map((sec) => (
@@ -1071,4 +1155,23 @@ const styles = { /* kleines Dark-Wash, damit Schwarz/Gold lesbar ist */ filter: "saturate(0.9) contrast(1.05) brightness(0.55)", }, + + chipGrid: { + marginTop: 12, + display: "grid", + gridTemplateColumns: "repeat(5, minmax(0, 1fr))", + gap: 8, + }, + + chipBtn: { + padding: "10px 0", + borderRadius: 12, + border: `1px solid rgba(233,216,166,0.22)`, + background: "rgba(255,255,255,0.06)", + color: stylesTokens.textGold, + fontWeight: 1100, + cursor: "pointer", + boxShadow: "inset 0 1px 0 rgba(255,255,255,0.06)", + }, + };