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.
This commit is contained in:
2026-02-03 15:00:31 +01:00
parent 3546500d9e
commit 6a5ff44135

View File

@@ -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() {
</div>
)}
{chipPickOpen && (
<div style={styles.modalOverlay} onMouseDown={closeChipPick}>
<div style={styles.modalCard} onMouseDown={(e) => e.stopPropagation()}>
<div style={styles.modalHeader}>
<div style={{ fontWeight: 1000, color: stylesTokens.textGold }}>
Wer hat die Karte?
</div>
<button
onClick={closeChipPick}
style={styles.modalCloseBtn}
aria-label="Schließen"
>
</button>
</div>
<div style={{ marginTop: 12, color: stylesTokens.textMain, opacity: 0.9 }}>
Chip auswählen:
</div>
<div style={styles.chipGrid}>
{CHIP_LIST.map((c) => (
<button
key={c}
onClick={() => chooseChip(c)}
style={styles.chipBtn}
title={`Setze s.${c}`}
>
{c}
</button>
))}
</div>
<div style={{ marginTop: 10, fontSize: 12, color: stylesTokens.textDim }}>
Tipp: Wenn du wieder auf den Notiz-Button klickst, gehts von <b>s.XX</b> zurück auf <b></b>.
</div>
</div>
</div>
)}
<div style={{ marginTop: 14, display: "grid", gap: 14 }}>
{sections.map((sec) => (
<div key={sec.key} style={styles.card}>
@@ -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)",
},
};