From 75c2bc9bbb2bd8a4ece38a37df4810875739dfc9 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Sun, 13 Apr 2025 22:30:19 +0200 Subject: [PATCH] fancy station view; Fixes #33 --- src/admin/station/mod.rs | 19 ++- src/models/rating.rs | 17 ++- src/station.rs | 298 +++++++++++++++++++++------------------ 3 files changed, 192 insertions(+), 142 deletions(-) diff --git a/src/admin/station/mod.rs b/src/admin/station/mod.rs index 7884339..2fb11db 100644 --- a/src/admin/station/mod.rs +++ b/src/admin/station/mod.rs @@ -121,11 +121,18 @@ impl Station { let waiting_teams: Vec<&Team> = teams.waiting.iter().map(|(team, _)| team).collect(); let doing_teams: Vec<&Team> = teams.doing.iter().map(|(team, _)| team).collect(); - let finished_teams: Vec<&Team> = teams.left.iter().map(|(team, _)| team).collect(); + let finished_teams: Vec<&Team> = teams + .left_not_yet_rated + .iter() + .map(|(team, _)| team) + .collect(); + let finished_and_rated_teams: Vec<&Team> = + teams.left_and_rated.iter().map(|(team, _)| team).collect(); if !waiting_teams.contains(&team) && !doing_teams.contains(&team) && !finished_teams.contains(&team) + && !finished_and_rated_teams.contains(&team) { return Err( "Es können nur Teams bewertet werden, die zumindest schon bei der Station sind." @@ -242,9 +249,15 @@ impl Station { ) -> Result<(), String> { let teams = TeamsAtStationLocation::for_station(db, self).await; - let left_teams: Vec<&Team> = teams.left.iter().map(|(team, _)| team).collect(); + let left_and_rated_teams: Vec<&Team> = + teams.left_and_rated.iter().map(|(team, _)| team).collect(); + let left_not_yet_rated_teams: Vec<&Team> = teams + .left_not_yet_rated + .iter() + .map(|(team, _)| team) + .collect(); - if !left_teams.contains(&team) { + if !left_and_rated_teams.contains(&team) && !left_not_yet_rated_teams.contains(&team) { return Err(format!( "Team kann nicht zur Arbeitsposition hinzugefügt werden, weil das Team {} aktuell nicht feritg ist", team.name diff --git a/src/models/rating.rs b/src/models/rating.rs index b322dac..11894ec 100644 --- a/src/models/rating.rs +++ b/src/models/rating.rs @@ -1,4 +1,4 @@ -use crate::{Station, admin::team::Team}; +use crate::{admin::team::Team, Station}; use chrono::{DateTime, Local, NaiveDateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; @@ -123,7 +123,8 @@ pub(crate) struct TeamsAtStationLocation { pub(crate) not_yet_here: Vec, pub(crate) waiting: Vec<(Team, Rating)>, pub(crate) doing: Vec<(Team, Rating)>, - pub(crate) left: Vec<(Team, Rating)>, + pub(crate) left_not_yet_rated: Vec<(Team, Rating)>, + pub(crate) left_and_rated: Vec<(Team, Rating)>, } impl TeamsAtStationLocation { @@ -134,13 +135,18 @@ impl TeamsAtStationLocation { let mut not_yet_here = Vec::new(); let mut waiting = Vec::new(); let mut doing = Vec::new(); - let mut left = Vec::new(); + let mut left_not_yet_rated = Vec::new(); + let mut left_and_rated = Vec::new(); for team in teams { match Rating::find_by_team_and_station(db, &team, station).await { Some(rating) => { if rating.left_at.is_some() { - left.push((team, rating)); + if rating.points.is_some() { + left_and_rated.push((team, rating)); + } else { + left_not_yet_rated.push((team, rating)); + } } else if rating.started_at.is_some() { doing.push((team, rating)); } else { @@ -156,7 +162,8 @@ impl TeamsAtStationLocation { not_yet_here, waiting, doing, - left, + left_not_yet_rated, + left_and_rated, } } } diff --git a/src/station.rs b/src/station.rs index b453c85..15b919e 100644 --- a/src/station.rs +++ b/src/station.rs @@ -8,7 +8,7 @@ use axum::{ routing::{get, post}, Form, Router, }; -use maud::{html, Markup}; +use maud::{html, Markup, PreEscaped}; use serde::Deserialize; use sqlx::SqlitePool; use std::sync::Arc; @@ -112,159 +112,189 @@ async fn view( " Teams zu deiner Station kommen." progress value=(teams.total_teams-teams.not_yet_here.len() as i64) max=(teams.total_teams) {} } - h2 { "Teams aktuell bei dir" } - @if !teams.waiting.is_empty() { - (teams.waiting.len()) - " Teams warten an deiner Station:" - ol { - @for (team, rating) in teams.waiting { - li { - (team.name) - " (seit " - (rating.local_time_arrived_at()) - ")" - details { - summary { "✏️" } - article { - form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { - label { - "Notizen" - @if let Some(notes) = &rating.notes { - input type="text" name="notes" value=(notes); - } @else { - input type="text" name="notes"; - } - } - input type="submit" value="Notizen speichern"; + @if !teams.not_yet_here.is_empty() { + form action=(format!("/s/{id}/{code}/new-waiting")) method="post" { + fieldset role="group" { + select name="team_id" aria-label="Team auswählen" required { + @for team in &teams.not_yet_here { + option value=(team.id) { + (team.name) } + } + } + input type="submit" value="Neues Team da"; + } + } + } + h2 { "Teams bei dir" } + @if !teams.doing.is_empty() { + @for (team, rating) in teams.doing { + article { + details { + summary { + em data-tooltip="Aktiv" { "🎬 " } + (team.name) + small { + " (seit " + (rating.local_time_doing()) + ")" + } + "✏️" + a href=(format!("/s/{id}/{code}/team-finished/{}", team.id)) { + button { "Team fertig" } + } + } + form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { + label { + "Notizen" + @if let Some(notes) = &rating.notes { + input type="text" name="notes" value=(notes); + } @else { + input type="text" name="notes"; + } + } + input type="submit" value="Notizen speichern"; + } + a href=(format!("/s/{id}/{code}/remove-doing/{}", team.id)) + onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir arbeitet? Das Team wird zurück auf die Warte-Position gesetzt');" { + "🗑️" + } + } + } + } + } + @if !teams.waiting.is_empty() { + @for (team, rating) in teams.waiting { + article { + details { + summary { + em data-tooltip="Wartend" { "⏳ " } + (team.name) + small { + " (seit " + (rating.local_time_arrived_at()) + ")" + } + "✏️" + a href=(format!("/s/{id}/{code}/team-starting/{}", team.id)) { + button { "Team startet" } + } + } + form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { + label { + "Notizen" + @if let Some(notes) = &rating.notes { + input type="text" name="notes" value=(notes); + } @else { + input type="text" name="notes"; + } + } + input type="submit" value="Notizen speichern"; + } a href=(format!("/s/{id}/{code}/remove-waiting/{}", team.id)) onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir ist? Das kann _NICHT_ mehr rückgängig gemacht werden.');" { "🗑️" } - } - } - a href=(format!("/s/{id}/{code}/team-starting/{}", team.id)) { - button { "Team startet" } - } } } - } + } } - @if !teams.not_yet_here.is_empty() { - form action=(format!("/s/{id}/{code}/new-waiting")) method="post" { - fieldset role="group" { - select name="team_id" aria-label="Team auswählen" required { - @for team in &teams.not_yet_here { - option value=(team.id) { - (team.name) - } - } - } - input type="submit" value="Neues Team da"; - } + @if !teams.left_not_yet_rated.is_empty() { + h2 { "Noch zu Bewerten" } + article class="warning" { + "Noch keine Punkte für diese Gruppe vergeben ⤵️" } - } - @if !teams.doing.is_empty() { - (teams.doing.len()) - " Teams arbeiten an deiner Station:" - ol { - @for (team, rating) in teams.doing { - li { - (team.name) - " (seit " - (rating.local_time_doing()) - ")" - details { - summary { "✏️" } - article { - form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { - label { - "Notizen" - @if let Some(notes) = &rating.notes { - input type="text" name="notes" value=(notes); - } @else { - input type="text" name="notes"; - } - } - input type="submit" value="Notizen speichern"; - } - a href=(format!("/s/{id}/{code}/remove-doing/{}", team.id)) - onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir arbeitet? Das Team wird zurück auf die Warte-Position gesetzt');" { - "🗑️" - } - } - } - a href=(format!("/s/{id}/{code}/team-finished/{}", team.id)) { - button { "Team fertig" } - } + @for (team, rating) in teams.left_not_yet_rated { + article { + em data-tooltip="Zu bewerten" { "☐ " } + (team.name) + small { + " (um " + (rating.local_time_left()) + " gegangen)" } + form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { + label { + @if let Some(points) = rating.points { + span { (points) " Punkte" } + input type="range" name="points" min="0" max="10" value=(points) + onchange="if(!confirm('Du hast die Gruppe bereits bewertet. Bist du sicher, dass du deine Bewertung nochmal ändern möchtest?')) { this.value = this.defaultValue; this.previousElementSibling.textContent = this.defaultValue + ' Punkte'; }" + oninput="this.previousElementSibling.textContent = this.value + ' Punkte'" {} + } @else { + span { "0 Punkte" } + input type="range" name="points" min="0" max="10" value="0" oninput="this.previousElementSibling.textContent = this.value + ' Punkte'" {} + } + + } + label { + "Notizen" + @if let Some(notes) = &rating.notes { + input type="text" name="notes" value=(notes); + } @else { + input type="text" name="notes"; + } + } + input type="submit" value="Speichern"; + } + a href=(format!("/s/{id}/{code}/remove-left/{}", team.id)) + onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir fertig ist? Das Team wird zurück auf die Arbeits-Position gesetzt');" { + "🗑️" + } } - } + } } - @if !teams.left.is_empty() { - h2 { "Teams die bei dir waren" } - (teams.left.len()) - " Teams waren schon bei dir" - ol { - @for (team, rating) in teams.left { - li { - (team.name) - " (gegangen um " - (rating.local_time_left()) - @if let Some(points) = rating.points { - ", " - (points) + + + h2 { "Was bisher geschah" } + @if !teams.left_and_rated.is_empty() { + @for (team, rating) in teams.left_and_rated { + article { + details { + summary { + em data-tooltip="Schon bewertet" { "✅ " } + (team.name) + (PreEscaped(" → ")) + (rating.points.unwrap()) " Punkte" } - @if let Some(notes) = &rating.notes{ - ", Notizen: " - (notes) + small { + " (um " + (rating.local_time_arrived_at()) + " eingetroffen, um " + (rating.local_time_doing()) + " gestartet und um " + (rating.local_time_left()) + " gegangen)" } - ")" + form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { + label { + @if let Some(points) = rating.points { + span { (points) " Punkte" } + input type="range" name="points" min="0" max="10" value=(points) + onchange="if(!confirm('Du hast die Gruppe bereits bewertet. Bist du sicher, dass du deine Bewertung nochmal ändern möchtest?')) { this.value = this.defaultValue; this.previousElementSibling.textContent = this.defaultValue + ' Punkte'; }" + oninput="this.previousElementSibling.textContent = this.value + ' Punkte'" {} + } - details open[rating.points.is_none()] { - summary { "✏️" } - article { - @if rating.points.is_none() { - article class="warning" { - "Noch keine Punkte für diese Gruppe vergeben. Gib sie hier ein und drücke dann auf " - em { "Speichern" } - } - } - form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { - label { - @if let Some(points) = rating.points { - span { (points) " Punkte" } - input type="range" name="points" min="0" max="10" value=(points) - onchange="if(!confirm('Du hast die Gruppe bereits bewertet. Bist du sicher, dass du deine Bewertung nochmal ändern möchtest?')) { this.value = this.defaultValue; this.previousElementSibling.textContent = this.defaultValue + ' Punkte'; }" - oninput="this.previousElementSibling.textContent = this.value + ' Punkte'" {} - } @else { - span { "0 Punkte" } - input type="range" name="points" min="0" max="10" value="0" oninput="this.previousElementSibling.textContent = this.value + ' Punkte'" {} - } - - } - label { - "Notizen" - @if let Some(notes) = &rating.notes { - input type="text" name="notes" value=(notes); - } @else { - input type="text" name="notes"; - } - } - input type="submit" value="Speichern"; - } - - a href=(format!("/s/{id}/{code}/remove-left/{}", team.id)) - onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir fertig ist? Das Team wird zurück auf die Arbeits-Position gesetzt');" { - "🗑️" - } - } + } + label { + "Notizen" + @if let Some(notes) = &rating.notes { + input type="text" name="notes" value=(notes); + } @else { + input type="text" name="notes"; + } + } + input type="submit" value="Speichern"; + } + a href=(format!("/s/{id}/{code}/remove-left/{}", team.id)) + onclick="return confirm('Bist du sicher, dass das Team noch nicht bei dir fertig ist? Das Team wird zurück auf die Arbeits-Position gesetzt');" { + "🗑️" } } } } - + } @else { + "Du hast bisher noch keine Teams bewertet." } };