Simplify item status cycling logic

Replaced long-press and multiple buttons with a single click-based cycle for item status changes (unknown → green → red → gray → unknown). Removed redundant code and associated styles for a cleaner and more intuitive user experience.
This commit is contained in:
2026-02-03 09:19:27 +01:00
parent e8be5a5893
commit 46c12557a6

View File

@@ -126,7 +126,7 @@ function AdminPanel() {
</div> </div>
<div style={{ fontSize: 12, opacity: 0.75 }}> <div style={{ fontSize: 12, opacity: 0.75 }}>
Tipp: Tap auf Item = rot (ausschließen), Long-Press = grün (bestätigt), ? = unsicher. Tipp: Klick auf Item: Grün Rot Grau Leer
</div> </div>
</div> </div>
</div> </div>
@@ -136,39 +136,6 @@ function AdminPanel() {
); );
} }
function useLongPress(onLongPress, onClick, { delay = 450 } = {}) {
const [longPressed, setLongPressed] = useState(false);
const timeoutRef = React.useRef(null);
const start = (e) => {
e.preventDefault();
setLongPressed(false);
timeoutRef.current = setTimeout(() => {
setLongPressed(true);
onLongPress();
}, delay);
};
const clear = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
timeoutRef.current = null;
};
const end = () => {
clear();
if (!longPressed) onClick();
};
return {
onMouseDown: start,
onMouseUp: end,
onMouseLeave: clear,
onTouchStart: start,
onTouchEnd: end,
onTouchMove: clear,
};
}
export default function App() { export default function App() {
const [me, setMe] = useState(null); const [me, setMe] = useState(null);
const [loginEmail, setLoginEmail] = useState("admin@local"); const [loginEmail, setLoginEmail] = useState("admin@local");
@@ -236,30 +203,20 @@ export default function App() {
setSheet(sh); setSheet(sh);
}; };
const toggleCross = async (entry) => { // ✅ Cycle: unknown -> green -> red -> gray -> unknown
const next = entry.status === 1 ? 0 : 1; const cycleStatus = async (entry) => {
await api(`/games/${gameId}/sheet/${entry.entry_id}`, { let next = 0;
method: "PATCH",
body: JSON.stringify({ status: next }),
});
await reloadSheet();
};
const toggleConfirmed = async (entry) => { if (entry.status === 0) next = 2; // grün (confirmed)
const next = entry.status === 2 ? 0 : 2; else if (entry.status === 2) next = 1; // rot (crossed)
await api(`/games/${gameId}/sheet/${entry.entry_id}`, { else if (entry.status === 1) next = 3; // grau (maybe)
method: "PATCH", else next = 0; // zurück unknown
body: JSON.stringify({ status: next }),
});
await reloadSheet();
};
const toggleMaybe = async (entry) => {
const next = entry.status === 3 ? 0 : 3;
await api(`/games/${gameId}/sheet/${entry.entry_id}`, { await api(`/games/${gameId}/sheet/${entry.entry_id}`, {
method: "PATCH", method: "PATCH",
body: JSON.stringify({ status: next }), body: JSON.stringify({ status: next }),
}); });
await reloadSheet(); await reloadSheet();
}; };
@@ -316,16 +273,16 @@ export default function App() {
: []; : [];
const getRowBg = (status) => { const getRowBg = (status) => {
if (status === 1) return "rgba(255, 0, 0, 0.06)"; // crossed if (status === 1) return "rgba(255, 0, 0, 0.06)"; // rot
if (status === 2) return "rgba(0, 170, 60, 0.07)"; // confirmed if (status === 2) return "rgba(0, 170, 60, 0.07)"; // grün
if (status === 3) return "rgba(255, 180, 70, 0.12)"; // maybe if (status === 3) return "rgba(120, 120, 120, 0.14)"; // grau
return "rgba(255,255,255,0.22)"; return "rgba(255,255,255,0.22)";
}; };
const getNameColor = (status) => { const getNameColor = (status) => {
if (status === 1) return "#b10000"; if (status === 1) return "#b10000";
if (status === 2) return "#0b6a1e"; if (status === 2) return "#0b6a1e";
if (status === 3) return "#6b4d00"; if (status === 3) return "#444444";
return "#20140c"; return "#20140c";
}; };
@@ -371,59 +328,37 @@ export default function App() {
<div style={styles.sectionHeader}>{sec.title}</div> <div style={styles.sectionHeader}>{sec.title}</div>
<div style={{ display: "grid" }}> <div style={{ display: "grid" }}>
{sec.entries.map((e) => { {sec.entries.map((e) => (
const pressHandlers = useLongPress( <div
() => toggleConfirmed(e), // long press -> confirmed key={e.entry_id}
() => toggleCross(e), // tap -> crossed style={{
{ delay: 450 } ...styles.row,
); background: getRowBg(e.status),
}}
return ( >
{/* ✅ Klick Cycle */}
<div <div
key={e.entry_id} onClick={() => cycleStatus(e)}
style={{ style={{
...styles.row, ...styles.name,
background: getRowBg(e.status), textDecoration: e.status === 1 ? "line-through" : "none",
color: getNameColor(e.status),
opacity: e.status === 1 ? 0.75 : 1,
}} }}
title="Klick: Grün → Rot → Grau → Leer"
> >
{/* Name: Tap = rot, Long-Press = grün */} {e.label}
<div
{...pressHandlers}
style={{
...styles.name,
textDecoration: e.status === 1 ? "line-through" : "none",
color: getNameColor(e.status),
opacity: e.status === 1 ? 0.75 : 1,
}}
title="Tippen = ausschließen | Lange halten = bestätigt"
>
{e.label}
</div>
{/* ? = maybe */}
<button
onClick={() => toggleMaybe(e)}
style={{
...styles.maybeBtn,
background: e.status === 3
? "linear-gradient(180deg, rgba(255,210,120,0.9), rgba(180,120,20,0.25))"
: styles.maybeBtn.background,
}}
title="Unsicher"
>
?
</button>
<div style={styles.cell}>{e.status === 1 ? "X" : ""}</div>
<div style={styles.cell}>{e.status === 1 ? "✓" : ""}</div>
<div style={styles.cell}>{e.status === 2 ? "✓" : ""}</div>
<button onClick={() => toggleTag(e)} style={styles.tagBtn} title="i → m → s → leer">
{e.note_tag || "—"}
</button>
</div> </div>
);
})} <div style={styles.cell}>{e.status === 1 ? "X" : ""}</div>
<div style={styles.cell}>{e.status === 1 ? "✓" : ""}</div>
<div style={styles.cell}>{e.status === 2 ? "✓" : ""}</div>
<button onClick={() => toggleTag(e)} style={styles.tagBtn} title="i → m → s → leer">
{e.note_tag || "—"}
</button>
</div>
))}
</div> </div>
</div> </div>
))} ))}
@@ -486,7 +421,7 @@ const styles = {
}, },
row: { row: {
display: "grid", display: "grid",
gridTemplateColumns: "1fr 42px 40px 40px 40px 56px", // ✅ extra Spalte für ? gridTemplateColumns: "1fr 40px 40px 40px 56px", // ✅ ohne ? Button
gap: 8, gap: 8,
padding: "10px 12px", padding: "10px 12px",
alignItems: "center", alignItems: "center",
@@ -504,15 +439,6 @@ const styles = {
fontWeight: 1000, fontWeight: 1000,
color: "#20140c", color: "#20140c",
}, },
maybeBtn: {
padding: "8px 0",
fontWeight: 1000,
borderRadius: 10,
border: "1px solid rgba(0,0,0,0.25)",
background: "linear-gradient(180deg, rgba(255,255,255,0.55), rgba(0,0,0,0.06))",
color: "#6b4d00",
cursor: "pointer",
},
tagBtn: { tagBtn: {
padding: "8px 0", padding: "8px 0",
fontWeight: 1000, fontWeight: 1000,