diff --git a/src/admin/vote/Answers.jsx b/src/admin/vote/Answers.jsx index 82297c1..c785c1a 100644 --- a/src/admin/vote/Answers.jsx +++ b/src/admin/vote/Answers.jsx @@ -5,8 +5,9 @@ import { getDoc, getDocs, onSnapshot, + setDoc, } from "firebase/firestore"; -import { confirm, snackbar } from "mdui"; +import { confirm, prompt, snackbar } from "mdui"; import React from "react"; import { useLoaderData, useNavigate } from "react-router-dom"; import { db } from "../../firebase"; @@ -16,9 +17,14 @@ export default function Answers() { const [loading, setLoading] = React.useState(true); - const search = new URLSearchParams(window.location.search).get("search"); + const searchParams = new URLSearchParams(window.location.search); + const search = searchParams.get("search"); + const grade = searchParams.get("grade"); + const listIndex = searchParams.get("listIndex"); - const [mode, setMode] = React.useState(search ? "by-name" : "by-option"); + const [mode, setMode] = React.useState( + search || grade || listIndex ? "by-name" : "by-option" + ); const [answers, setAnswers] = React.useState([]); const grades = [...new Set(answers.map((answer) => answer.grade))]; @@ -80,7 +86,43 @@ export default function Answers() { searchField.value = search; } } - }, [search, mode, answers, loading]); + if (grade && listIndex && mode === "by-name") { + const elements = document.querySelectorAll("tbody tr"); + elements.forEach((element) => { + const gradeCell = element.querySelector("td:nth-child(2)").textContent; + const listIndexCell = + element.querySelector("td:nth-child(3)").textContent; + if (gradeCell == grade && listIndexCell == listIndex) { + element.style.display = ""; + } else { + element.style.display = "none"; + } + }); + } + }, [search, mode, answers, loading, grade, listIndex]); + + async function updateAnswer({ id, data }) { + try { + await setDoc(doc(db, `/votes/${vote.id}/choices/${id}`), data, { + merge: true, + }); + snackbar({ + message: "Antwort erfolgreich aktualisiert.", + timeout: 5000, + }); + // update the answers + const newAnswers = answers.map((answer) => + answer.id === id ? { ...answer, ...data } : answer + ); + setAnswers(newAnswers); + } catch (error) { + snackbar({ + message: "Fehler beim Aktualisieren der Antwort.", + timeout: 5000, + }); + console.error(error); + } + } if (loading) { return ; @@ -119,6 +161,9 @@ export default function Answers() { setMode("by-name")}> Nach Name + setMode("by-date")}> + Nach Datum +
@@ -255,7 +300,17 @@ export default function Answers() { .sort((a, b) => a.listIndex - b.listIndex) .map((answer, i) => ( - {answer.name} + + { + setMode("by-name"); + navigate(`.?search=${answer.id}`); + }} + > + {answer.name} + + {answer.listIndex} {answer.selected.map((selected, i) => ( @@ -305,6 +360,14 @@ export default function Answers() { navigate(`.?search=${e.target.value}`); }} > + {grade && listIndex && ( + <> +

+ + {`Klasse ${grade}, #${listIndex}`} + + + )}

@@ -329,7 +392,7 @@ export default function Answers() { ))} @@ -357,20 +420,73 @@ export default function Answers() { +
- ID + Antwort-Id
{ + let data = JSON.stringify(answer, null, 2); + prompt({ + placeholder: "JSON-2Daten", + confirmText: "Speichern", + cancelText: "Abbrechen", + headline: "Antwort bearbeiten", + description: + "Beachten Sie: bei fehlerhaften Daten kann diese Antwort unbrauchbar werden. Stellen Sie vor dem Speichern sicher, dass die Daten korrekt sind.", + onConfirm: (value) => { + try { + const data = JSON.parse(value); + console.log(data); + updateAnswer({ id: answer.id, data }); + } catch (error) { + snackbar("Ungültige JSON-Daten."); + } + }, + textFieldOptions: { + value: data, + oninput: (e) => { + data = e.target.value; + }, + autosize: true, + rows: 5, + }, + validator: (value) => { + try { + JSON.parse(value); + return true; + } catch (error) { + return false; + } + }, + }); + }} + > + Bearbeiten + { confirm({ headline: "Löschen", description: "Sind Sie sicher, dass Sie diese Antwort löschen möchten?", - }).then(() => { - // Löschen - deleteDoc( - doc(db, `/votes/${vote.id}/choices/${answer.id}`) - ).then(() => { - window.location.reload(); - }); + confirmText: "Löschen", + cancelText: "Abbrechen", + onConfirm: () => { + deleteDoc( + doc( + db, + `/votes/${vote.id}/choices/${answer.id}` + ) + ).then(() => { + snackbar({ + message: "Antwort erfolgreich gelöscht.", + timeout: 5000, + }); + window.location.reload(); + }); + }, }); }} > @@ -383,6 +499,69 @@ export default function Answers() { )} + + {mode === "by-date" && ( +
+
+ Hinweis: Als Datenschutzmaßnahme wird die Uhrzeit vorerst nicht + angezeigt. Die Antworten sind nach der Uhrzeit sortiert. Erst beim + Klicken auf das Datum wird die Uhrzeit sichtbar. Diese Ansicht kann + Ihnen helfen, sich einen Überblick über die letzten Antworten zu + verschaffen. +
+
+ + + + + + + + + + {answers + .sort((a, b) => b.timestamp.seconds - a.timestamp.seconds) + .map((answer, i) => ( + + + + + + ))} + +
+ Name + + Klasse + + Datum +
+ { + setMode("by-name"); + navigate(`.?search=${answer.id}`); + }} + > + {answer.name} + + {answer.grade} + + + {new Date( + answer.timestamp.seconds * 1000 + ).toLocaleDateString("de-DE")} + + +
+
+
+ )} ); } diff --git a/src/admin/vote/Match.tsx b/src/admin/vote/Match.tsx index 77060c0..791aad4 100644 --- a/src/admin/vote/Match.tsx +++ b/src/admin/vote/Match.tsx @@ -54,14 +54,7 @@ export default function Match() {
{s.listIndex} - choice.listIndex === - s.listIndex.toString() && - choice.grade === c.grade.toString() - )[0]?.id - }`} + to={`../answers?grade=${c.grade}&listIndex=${s.listIndex}`} > { choices.filter( @@ -71,21 +64,21 @@ export default function Match() { choice.grade === c.grade.toString() )[0]?.name } + {choices.filter( + (choice) => + choice.listIndex === s.listIndex.toString() && + choice.grade === c.grade.toString() + ).length > 1 && + " + " + + (choices.filter( + (choice) => + choice.listIndex === + s.listIndex.toString() && + choice.grade === c.grade.toString() + ).length - + 1) + + " weitere"} - {choices.filter( - (choice) => - choice.listIndex === s.listIndex.toString() && - choice.grade === c.grade.toString() - ).length > 1 && - " + " + - (choices.filter( - (choice) => - choice.listIndex === - s.listIndex.toString() && - choice.grade === c.grade.toString() - ).length - - 1) + - " weitere"}{" "} {choices.filter( (choice) => choice.listIndex === s.listIndex.toString() &&