diff --git a/src/admin/mod.rs b/src/admin/mod.rs index 216d826..3578d83 100644 --- a/src/admin/mod.rs +++ b/src/admin/mod.rs @@ -45,6 +45,11 @@ async fn highscore(State(db): State>, session: Session) -> Marku td { a href=(format!("/admin/station/{}", station.id)){ (station.name) + @if station.crewless() { + em data-tooltip="Station ohne Stationsbetreuer" data-placement="bottom" { + small { "🤖" } + } + } } } } diff --git a/src/admin/route/mod.rs b/src/admin/route/mod.rs index 28213e5..c8d470f 100644 --- a/src/admin/route/mod.rs +++ b/src/admin/route/mod.rs @@ -150,6 +150,22 @@ DROP TABLE temp_pos;", .unwrap() } + pub(crate) async fn crewless_stations(&self, db: &SqlitePool) -> Vec { + self.stations(db) + .await + .into_iter() + .filter(|s| s.crewless()) + .collect() + } + + pub(crate) async fn crewful_stations(&self, db: &SqlitePool) -> Vec { + self.stations(db) + .await + .into_iter() + .filter(|s| !s.crewless()) + .collect() + } + async fn stations_not_in_route(&self, db: &SqlitePool) -> Vec { // TODO: switch to macro sqlx::query_as::<_, Station>( @@ -200,6 +216,7 @@ DROP TABLE temp_pos;", JOIN route_station rs ON s.id = rs.station_id LEFT JOIN 'team' g ON s.id = g.first_station_id WHERE rs.route_id = {} + AND (s.amount_people != 0 OR s.amount_people is NULL) GROUP BY s.id ORDER BY team_count ASC, s.id ASC LIMIT 1", diff --git a/src/admin/route/web.rs b/src/admin/route/web.rs index f838401..f0ae9ed 100644 --- a/src/admin/route/web.rs +++ b/src/admin/route/web.rs @@ -1,12 +1,12 @@ use super::Route; -use crate::{AppState, admin::station::Station, err, page, succ}; +use crate::{admin::station::Station, err, page, succ, AppState}; use axum::{ - Form, Router, extract::State, response::{IntoResponse, Redirect}, routing::{get, post}, + Form, Router, }; -use maud::{Markup, PreEscaped, html}; +use maud::{html, Markup, PreEscaped}; use serde::Deserialize; use sqlx::SqlitePool; use std::sync::Arc; @@ -162,6 +162,11 @@ async fn view( @for (idx, station) in cur_stations.into_iter().enumerate() { li { (station.name) + @if station.crewless() { + em data-tooltip="Station ohne Stationsbetreuer" { + small { "🤖" } + } + } @if idx > 0 { a href=(format!("/admin/route/{}/move-station-higher/{}", route.id, station.id)){ em data-tooltip=(format!("{} nach vor reihen", station.name)) { diff --git a/src/admin/station/mod.rs b/src/admin/station/mod.rs index 37f4df5..2ae5e54 100644 --- a/src/admin/station/mod.rs +++ b/src/admin/station/mod.rs @@ -16,7 +16,7 @@ pub(crate) struct Station { pub(crate) id: i64, pub(crate) name: String, notes: Option, - amount_people: Option, + pub(crate) amount_people: Option, last_login: Option, // TODO use proper timestamp (NaiveDateTime?) pub(crate) pw: String, pub(crate) ready: bool, @@ -45,6 +45,13 @@ impl Station { .ok() } + pub fn crewless(&self) -> bool { + if let Some(amount_people) = self.amount_people { + return amount_people == 0; + } + false + } + pub async fn login(db: &SqlitePool, id: i64, code: &str) -> Option { let station = sqlx::query_as!( Self, diff --git a/src/admin/station/web.rs b/src/admin/station/web.rs index c8254c1..c159988 100644 --- a/src/admin/station/web.rs +++ b/src/admin/station/web.rs @@ -126,15 +126,17 @@ async fn view( } } } - tr { - th scope="row" { "Stations-Link" }; - td { - a href=(format!("/s/{}/{}", station.id, station.pw)) { - "Login-Link" + @if !station.crewless() { + tr { + th scope="row" { "Stations-Link" }; + td { + a href=(format!("/s/{}/{}", station.id, station.pw)) { + "Login-Link" - } - article class="warning" { - (format!("Diesen Link nur Betreuern der Station {} geben! Mit diesem Link erhält man die Berechtigung, Teams zu bewerten.", station.name)) + } + article class="warning" { + (format!("Diesen Link nur Betreuern der Station {} geben! Mit diesem Link erhält man die Berechtigung, Teams zu bewerten.", station.name)) + } } } } @@ -161,12 +163,14 @@ async fn view( } } } - tr { - th scope="row" { "Letzter Zugriff eines Stationsbetreuers" }; - td { - @match station.local_last_login() { - Some(last_login) => (last_login), - None => "noch nicht eingeloggt :-(", + @if !station.crewless() { + tr { + th scope="row" { "Letzter Zugriff eines Stationsbetreuers" }; + td { + @match station.local_last_login() { + Some(last_login) => (last_login), + None => "noch nicht eingeloggt :-(", + } } } } @@ -672,6 +676,11 @@ async fn index(State(db): State>, session: Session) -> Markup { a href=(format!("/admin/station/{}", station.id)){ (station.name) } + @if station.crewless() { + em data-tooltip="Station ohne Stationsbetreuer" { + small { "🤖" } + } + } } td { em data-tooltip=(format!("{}/{} Teams (davon {} wartend + {} aktiv)", status.total_teams-status.not_yet_here.len() as i64, status.total_teams, status.waiting.len(), status.doing.len())) { diff --git a/src/admin/team/web.rs b/src/admin/team/web.rs index fc88121..f9d6462 100644 --- a/src/admin/team/web.rs +++ b/src/admin/team/web.rs @@ -95,29 +95,14 @@ async fn delete( Redirect::to("/admin/team") } -async fn quick( - State(db): State>, - session: Session, - axum::extract::Path(id): axum::extract::Path, -) -> Result { - let Some(team) = Team::find_by_id(&db, id).await else { - err!( - session, - "Team mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" - ); - - return Err(Redirect::to("/admin/team")); - }; - - let stations = team.route(&db).await.stations(&db).await; - - // maybe switch to maud-display impl of team - let content = html! { +async fn quick(db: Arc, team: &Team, stations: Vec, redirect: &str) -> Markup { + html! { h1 { a href=(format!("/admin/team/{}", team.id)) { "↩️" } "Bewertungen Team " (team.name) } form action=(format!("/admin/team/{}/quick", team.id)) method="post" { + input type="hidden" name="redirect" value=(redirect); table { thead { tr { @@ -155,12 +140,53 @@ async fn quick( } input type="submit" value="Speichern"; } + } +} +async fn quick_crewless( + State(db): State>, + session: Session, + axum::extract::Path(id): axum::extract::Path, +) -> Result { + let Some(team) = Team::find_by_id(&db, id).await else { + err!( + session, + "Team mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" + ); + + return Err(Redirect::to("/admin/team")); }; + + let stations: Vec = team.route(&db).await.crewless_stations(&db).await; + + let content = quick(db, &team, stations, "/crewless").await; + + Ok(page(content, session, true).await) +} + +async fn quick_all( + State(db): State>, + session: Session, + axum::extract::Path(id): axum::extract::Path, +) -> Result { + let Some(team) = Team::find_by_id(&db, id).await else { + err!( + session, + "Team mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" + ); + + return Err(Redirect::to("/admin/team")); + }; + + let stations = team.route(&db).await.stations(&db).await; + + let content = quick(db, &team, stations, "").await; + Ok(page(content, session, true).await) } #[derive(Deserialize, Debug)] struct QuickUpdate { + redirect: String, #[serde(flatten)] fields: HashMap, } @@ -228,7 +254,7 @@ async fn quick_post( succ!(session, "Erfolgreich {amount_succ} Bewertungen eingetragen"); } - Redirect::to(&format!("/admin/team/{id}/quick")) + Redirect::to(&format!("/admin/team/{id}/quick{}", form.redirect)) } async fn view( @@ -247,7 +273,7 @@ async fn view( let first_station = team.first_station(&db).await; let routes = Route::all(&db).await; - let stations = team.route(&db).await.stations(&db).await; + let stations = team.route(&db).await.crewful_stations(&db).await; // maybe switch to maud-display impl of team let content = html! { @@ -378,9 +404,13 @@ async fn view( } a href=(format!("/admin/team/{}/quick", team.id)){ button { - "Stations-Bewertungen für Team " - (team.name) - " eingeben" + "Bewertungen" + } + } + hr; + a href=(format!("/admin/team/{}/quick/crewless", team.id)){ + button { + "Unbemannte Bewertungen" } } }; @@ -743,7 +773,8 @@ pub(super) fn routes() -> Router { .route("/lost", get(lost)) .route("/{id}", get(view)) .route("/{id}/delete", get(delete)) - .route("/{id}/quick", get(quick)) + .route("/{id}/quick", get(quick_all)) + .route("/{id}/quick/crewless", get(quick_crewless)) .route("/{id}/quick", post(quick_post)) .route("/{id}/name", post(update_name)) .route("/{id}/notes", post(update_notes))