diff --git a/src/admin/vote/Assign.jsx b/src/admin/vote/Assign.jsx
index 9bc6023..32eb96c 100644
--- a/src/admin/vote/Assign.jsx
+++ b/src/admin/vote/Assign.jsx
@@ -11,6 +11,8 @@ export default function Assign() {
const [loading, setLoading] = React.useState(false);
const [mode, setMode] = React.useState("by-option");
+ const [search, setSearch] = React.useState("");
+
const [rules, setRules] = React.useState([
{
apply: "*",
@@ -26,6 +28,12 @@ export default function Assign() {
newResults[result.id] = result.result;
});
+ // assign other results to first choice
+ choices.forEach((choice) => {
+ if (!newResults[choice.id]) {
+ newResults[choice.id] = choice.selected[0];
+ }
+ });
setResults(newResults);
}
@@ -412,6 +420,61 @@ export default function Assign() {
snackbar({ message: "Ergebnisse gespeichert." });
}
+ const filteredResults = () => {
+ if (mode === "power-search") {
+ return sortedResults.filter(([key]) => {
+ const choice = choices.find((choice) => choice.id === key);
+ const searchTerms = search.split(",");
+ return searchTerms.every((term) => {
+ const orTerms = term.split("|");
+ return orTerms.some((orTerm) => {
+ const isNegative = orTerm.startsWith("!");
+ const [key, value] = isNegative
+ ? orTerm.slice(1).split("=")
+ : orTerm.split("=");
+ if (value) {
+ if (key === "name") {
+ return isNegative
+ ? !choice.name.toLowerCase().includes(value.toLowerCase())
+ : choice.name.toLowerCase().includes(value.toLowerCase());
+ }
+ if (key === "grade") {
+ return isNegative
+ ? choice.grade !== value
+ : choice.grade === value;
+ }
+ if (key === "assignedTo") {
+ return isNegative
+ ? results[choice.id] !== value
+ : results[choice.id] === value;
+ }
+ if (key === "choice") {
+ return isNegative
+ ? results[choice.id] !== choice.selected[parseInt(value) - 1]
+ : results[choice.id] === choice.selected[parseInt(value) - 1];
+ }
+ if (key === "selected") {
+ return isNegative
+ ? !choice.selected.includes(value)
+ : choice.selected.includes(value);
+ }
+ if (key.startsWith("selected[")) {
+ const index = parseInt(key.match(/\d+/)?.[0]) - 1;
+ if (isNaN(index)) return false;
+ if (!choice.selected[index]) return false;
+ return isNegative
+ ? choice.selected[index] !== value
+ : choice.selected[index] === value;
+ }
+ }
+ return false;
+ });
+ });
+ });
+ }
+ return sortedResults;
+ };
+
return (
- Hier sind die optimierten Zuordnungen der Schüler zu den Projekten.
+ {mode !== "power-search" && (
+ <>
+ Insgesamt wurden {Object.keys(results).length} Schüler zugeordnet.
+ Davon haben:{" "}
+ {Object.entries(wahlenCounts).map(([wahl, count]) => (
+ <>
+
{count} Schüler die{" "}
+ {wahl === "1"
+ ? "Erstwahlen, "
+ : `${wahl}. Wahlen${
+ wahl === vote.selectCount.toString() ? "." : ","
+ } `}
+ >
+ ))}
+ >
+ )}
- Insgesamt wurden {Object.keys(results).length} Schüler zugeordnet. Davon
- haben:
-
- {Object.entries(wahlenCounts).map(([wahl, count]) => (
- -
- {count} Schüler die{" "}
- {wahl === "1" ? "Erstwahlen" : `${wahl}. Wahlen`}
-
- ))}
-
+ setMode("power-search")}
+ unchecked-icon="query_stats"
+ >
+ Power-Search
+
setMode("by-option")}>
Nach Projekt
@@ -469,7 +544,208 @@ export default function Assign() {
Nach Name
-
+ {mode === "power-search" && (
+
+
+ Durchsuchen Sie die Ergebnisse mit folgenden Operatoren:
+
+ -
+
name=Johan
: Schüler deren Name Johan enthält
+
+ -
+
grade=12
: Schüler der 12. Klasse
+
+ -
+
assignedTo=abc
: Schüler die zu dem Projekt mit der
+ ID abc zugewiesen sind
+
+ -
+
choice=2
: Schüler die zu ihrer Zweitwahl zugewiesen
+ sind
+
+ -
+
selected=abc
: Schüler die das Projekt mit der ID
+ abc gewählt haben
+
+ -
+
selected[1]=abc
: Schüler die das Projekt mit der ID
+ abc als Erstwahl gewählt haben
+
+
+
+ Kombinieren Sie beliegbig viele Operatoren mit einem Komma:{" "}
+
+ name=Johan,grade=12,assignedTo=xyz,choice=2,selected=abc
+
+
+ Schreiben Sie ein logisches Oder mit einem Pipe:{" "}
+
name=Johan|name=Max,grade=12
+
+ Nutzen Sie ein ! um die Bedingung zu negieren:{" "}
+
!grade=12
+
+
+
+
+
+ Projekte anzeigen
+
+
+
+
+
+
+ Projekt
+ |
+
+ Maximalanzahl
+ |
+
+ Belegte Plätze
+ |
+
+ ID
+ |
+
+
+
+ {options.map((option, i) => (
+
+ {option.title} |
+ {option.max} |
+
+ {
+ sortedResults.filter(
+ ([, value]) => value === option.id
+ ).length
+ }
+ |
+ {option.id} |
+
+ ))}
+
+
+
+
+
+
+
+ {
+ setSearch(e.target.value);
+ }}
+ value={search}
+ >
+
+ {
+ const filtered = filteredResults();
+ const randomIndex = Math.floor(Math.random() * filtered.length);
+
+ const element = document.querySelector(
+ `#power-search tr:nth-child(${
+ randomIndex + 1
+ })`
+ );
+
+ console.log(element);
+ element.scrollIntoView({
+ behavior: "smooth",
+ });
+ element.style.backgroundColor =
+ "rgb(var(--mdui-color-tertiary-container-dark))";
+ setTimeout(() => {
+ element.style.backgroundColor = "";
+ }, 5000);
+ }}
+ icon="casino--outlined"
+ >
+
+
+
+
+
+
+
+ Name
+ |
+
+ Klasse
+ |
+
+ #
+ |
+ {Array.from(
+ { length: vote.selectCount },
+ (_, i) => i + 1
+ ).map((i) => (
+
+ Wahl {i}
+ |
+ ))}
+
+
+
+ {filteredResults().map(([key, value]) => (
+
+ {choices.find((choice) => choice.id === key).name} |
+ {choices.find((choice) => choice.id === key).grade} |
+
+ {choices.find((choice) => choice.id === key).listIndex}
+ |
+ {choices
+ .find((choice) => choice.id === key)
+ .selected.map((selected, i) => (
+ {
+ if (selected === value) return;
+ const newResults = { ...results };
+ newResults[key] = selected;
+ setResults(newResults);
+ const previousResults = { ...results };
+ snackbar({
+ message: "Änderung rückgängig machen",
+ action: "Rückgängig",
+ onActionClick: () => setResults(previousResults),
+ });
+ }}
+ >
+ {selected === value && `✓ `}
+ {`${
+ options.find((option) => option.id === selected)
+ .title
+ } (${
+ sortedResults.filter(
+ ([, value]) => value === selected
+ ).length
+ }/${
+ options.find((option) => option.id === selected).max
+ })`}
+ |
+ ))}
+
+ ))}
+
+
+
+
+ )}
{mode === "by-option" && (