From da8b164a9f837212c002429944a7ca1eb0699e83 Mon Sep 17 00:00:00 2001 From: JohanGrims Date: Thu, 12 Dec 2024 20:34:41 +0100 Subject: [PATCH 1/3] New Views and functionallity for Answers and Match components --- src/admin/vote/Answers.jsx | 199 ++++++++++++++++++++++++++++++++++--- src/admin/vote/Match.tsx | 37 +++---- 2 files changed, 201 insertions(+), 35 deletions(-) diff --git a/src/admin/vote/Answers.jsx b/src/admin/vote/Answers.jsx index 82297c1..9b4b9fa 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"; @@ -17,8 +18,14 @@ export default function Answers() { const [loading, setLoading] = React.useState(true); const search = new URLSearchParams(window.location.search).get("search"); + const grade = new URLSearchParams(window.location.search).get("grade"); + const listIndex = new URLSearchParams(window.location.search).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 +87,36 @@ 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 }) { + await setDoc(doc(db, `/votes/${vote.id}/choices/${id}`), data, { + merge: true, + }).then(() => { + snackbar({ + message: "Antwort erfolgreich aktualisiert.", + timeout: 5000, + }); + // update the answers + const newAnswers = answers.map((answer) => + answer.id === id ? { id, ...data } : answer + ); + setAnswers(newAnswers); + }); + } if (loading) { return ; @@ -119,6 +155,9 @@ export default function Answers() { setMode("by-name")}> Nach Name + setMode("by-date")}> + Nach Datum +
@@ -255,7 +294,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 +354,14 @@ export default function Answers() { navigate(`.?search=${e.target.value}`); }} > + {grade && listIndex && ( + <> +

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

@@ -329,7 +386,7 @@ export default function Answers() { ))} @@ -357,20 +414,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 +493,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() && From ce40ddd49a38f5088aedec1888ec8e324dd62ce2 Mon Sep 17 00:00:00 2001 From: JohanGrims Date: Thu, 12 Dec 2024 20:38:48 +0100 Subject: [PATCH 2/3] Update src/admin/vote/Answers.jsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/admin/vote/Answers.jsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/admin/vote/Answers.jsx b/src/admin/vote/Answers.jsx index 9b4b9fa..6913d43 100644 --- a/src/admin/vote/Answers.jsx +++ b/src/admin/vote/Answers.jsx @@ -103,19 +103,26 @@ export default function Answers() { }, [search, mode, answers, loading, grade, listIndex]); async function updateAnswer({ id, data }) { - await setDoc(doc(db, `/votes/${vote.id}/choices/${id}`), data, { - merge: true, - }).then(() => { + 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 ? { id, ...data } : 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) { From 4f345ded17bf773a98b0bb52f71c93a11e78cee2 Mon Sep 17 00:00:00 2001 From: JohanGrims Date: Thu, 12 Dec 2024 20:46:31 +0100 Subject: [PATCH 3/3] Refactor URL search parameter retrieval in Answers component for improved readability --- src/admin/vote/Answers.jsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/admin/vote/Answers.jsx b/src/admin/vote/Answers.jsx index 6913d43..c785c1a 100644 --- a/src/admin/vote/Answers.jsx +++ b/src/admin/vote/Answers.jsx @@ -17,11 +17,10 @@ export default function Answers() { const [loading, setLoading] = React.useState(true); - const search = new URLSearchParams(window.location.search).get("search"); - const grade = new URLSearchParams(window.location.search).get("grade"); - const listIndex = new URLSearchParams(window.location.search).get( - "listIndex" - ); + 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 || grade || listIndex ? "by-name" : "by-option"