fancy station view; Fixes #33
Some checks failed
CI/CD Pipeline / test (push) Successful in 4m11s
CI/CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
Philipp Hofer 2025-04-13 22:30:19 +02:00
parent 4cbd5269d6
commit 75c2bc9bbb
3 changed files with 192 additions and 142 deletions

View File

@ -121,11 +121,18 @@ impl Station {
let waiting_teams: Vec<&Team> = teams.waiting.iter().map(|(team, _)| team).collect(); 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 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) if !waiting_teams.contains(&team)
&& !doing_teams.contains(&team) && !doing_teams.contains(&team)
&& !finished_teams.contains(&team) && !finished_teams.contains(&team)
&& !finished_and_rated_teams.contains(&team)
{ {
return Err( return Err(
"Es können nur Teams bewertet werden, die zumindest schon bei der Station sind." "Es können nur Teams bewertet werden, die zumindest schon bei der Station sind."
@ -242,9 +249,15 @@ impl Station {
) -> Result<(), String> { ) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await; 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!( return Err(format!(
"Team kann nicht zur Arbeitsposition hinzugefügt werden, weil das Team {} aktuell nicht feritg ist", "Team kann nicht zur Arbeitsposition hinzugefügt werden, weil das Team {} aktuell nicht feritg ist",
team.name team.name

View File

@ -1,4 +1,4 @@
use crate::{Station, admin::team::Team}; use crate::{admin::team::Team, Station};
use chrono::{DateTime, Local, NaiveDateTime, Utc}; use chrono::{DateTime, Local, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, SqlitePool};
@ -123,7 +123,8 @@ pub(crate) struct TeamsAtStationLocation {
pub(crate) not_yet_here: Vec<Team>, pub(crate) not_yet_here: Vec<Team>,
pub(crate) waiting: Vec<(Team, Rating)>, pub(crate) waiting: Vec<(Team, Rating)>,
pub(crate) doing: 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 { impl TeamsAtStationLocation {
@ -134,13 +135,18 @@ impl TeamsAtStationLocation {
let mut not_yet_here = Vec::new(); let mut not_yet_here = Vec::new();
let mut waiting = Vec::new(); let mut waiting = Vec::new();
let mut doing = 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 { for team in teams {
match Rating::find_by_team_and_station(db, &team, station).await { match Rating::find_by_team_and_station(db, &team, station).await {
Some(rating) => { Some(rating) => {
if rating.left_at.is_some() { 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() { } else if rating.started_at.is_some() {
doing.push((team, rating)); doing.push((team, rating));
} else { } else {
@ -156,7 +162,8 @@ impl TeamsAtStationLocation {
not_yet_here, not_yet_here,
waiting, waiting,
doing, doing,
left, left_not_yet_rated,
left_and_rated,
} }
} }
} }

View File

@ -8,7 +8,7 @@ use axum::{
routing::{get, post}, routing::{get, post},
Form, Router, Form, Router,
}; };
use maud::{html, Markup}; use maud::{html, Markup, PreEscaped};
use serde::Deserialize; use serde::Deserialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;
@ -112,159 +112,189 @@ async fn view(
" Teams zu deiner Station kommen." " Teams zu deiner Station kommen."
progress value=(teams.total_teams-teams.not_yet_here.len() as i64) max=(teams.total_teams) {} progress value=(teams.total_teams-teams.not_yet_here.len() as i64) max=(teams.total_teams) {}
} }
h2 { "Teams aktuell bei dir" } @if !teams.not_yet_here.is_empty() {
@if !teams.waiting.is_empty() { form action=(format!("/s/{id}/{code}/new-waiting")) method="post" {
(teams.waiting.len()) fieldset role="group" {
" Teams warten an deiner Station:" select name="team_id" aria-label="Team auswählen" required {
ol { @for team in &teams.not_yet_here {
@for (team, rating) in teams.waiting { option value=(team.id) {
li { (team.name)
(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";
} }
}
}
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)) 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.');" { 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() { @if !teams.left_not_yet_rated.is_empty() {
form action=(format!("/s/{id}/{code}/new-waiting")) method="post" { h2 { "Noch zu Bewerten" }
fieldset role="group" { article class="warning" {
select name="team_id" aria-label="Team auswählen" required { "Noch keine Punkte für diese Gruppe vergeben ⤵️"
@for team in &teams.not_yet_here {
option value=(team.id) {
(team.name)
}
}
}
input type="submit" value="Neues Team da";
}
} }
} @for (team, rating) in teams.left_not_yet_rated {
@if !teams.doing.is_empty() { article {
(teams.doing.len()) em data-tooltip="Zu bewerten" { "" }
" Teams arbeiten an deiner Station:" (team.name)
ol { small {
@for (team, rating) in teams.doing { " (um "
li { (rating.local_time_left())
(team.name) " gegangen)"
" (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" }
}
} }
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()) h2 { "Was bisher geschah" }
" Teams waren schon bei dir" @if !teams.left_and_rated.is_empty() {
ol { @for (team, rating) in teams.left_and_rated {
@for (team, rating) in teams.left { article {
li { details {
(team.name) summary {
" (gegangen um " em data-tooltip="Schon bewertet" { "" }
(rating.local_time_left()) (team.name)
@if let Some(points) = rating.points { (PreEscaped(" &rarr; "))
", " (rating.points.unwrap())
(points)
" Punkte" " Punkte"
} }
@if let Some(notes) = &rating.notes{ small {
", Notizen: " " (um "
(notes) (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 { "✏️" } label {
article { "Notizen"
@if rating.points.is_none() { @if let Some(notes) = &rating.notes {
article class="warning" { input type="text" name="notes" value=(notes);
"Noch keine Punkte für diese Gruppe vergeben. Gib sie hier ein und drücke dann auf " } @else {
em { "Speichern" } input type="text" name="notes";
} }
} }
form action=(format!("/s/{id}/{code}/team-update/{}", team.id)) method="post" { input type="submit" value="Speichern";
label { }
@if let Some(points) = rating.points { a href=(format!("/s/{id}/{code}/remove-left/{}", team.id))
span { (points) " Punkte" } 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');" {
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');" {
"🗑️"
}
}
} }
} }
} }
} }
} @else {
"Du hast bisher noch keine Teams bewertet."
} }
}; };