show stations their location; Fixes #17
All checks were successful
CI/CD Pipeline / test (push) Successful in 2m53s
CI/CD Pipeline / deploy (push) Successful in 2m20s

This commit is contained in:
Philipp Hofer 2025-04-11 22:33:10 +02:00
parent e81a994074
commit a601507435
8 changed files with 76 additions and 21 deletions

View File

@ -1,8 +1,8 @@
use super::team::Team; use super::team::Team;
use crate::{ use crate::{
AppState,
admin::route::Route, admin::route::Route,
models::rating::{Rating, TeamsAtStationLocation}, models::rating::{Rating, TeamsAtStationLocation},
AppState,
}; };
use axum::Router; use axum::Router;
use chrono::{DateTime, Local, NaiveDateTime, Utc}; use chrono::{DateTime, Local, NaiveDateTime, Utc};
@ -19,8 +19,8 @@ pub(crate) struct Station {
amount_people: Option<i64>, amount_people: Option<i64>,
last_login: Option<NaiveDateTime>, // TODO use proper timestamp (NaiveDateTime?) last_login: Option<NaiveDateTime>, // TODO use proper timestamp (NaiveDateTime?)
pub(crate) pw: String, pub(crate) pw: String,
lat: Option<f64>, pub(crate) lat: Option<f64>,
lng: Option<f64>, pub(crate) lng: Option<f64>,
} }
impl Station { impl Station {
@ -81,7 +81,10 @@ impl Station {
let teams = TeamsAtStationLocation::for_station(db, self).await; let teams = TeamsAtStationLocation::for_station(db, self).await;
if !teams.not_yet_here.contains(team) { if !teams.not_yet_here.contains(team) {
return Err(format!("Kann Team nicht der Warteschlange hinzufügen, weil das Team {} nicht zu deiner Station kommen soll.", team.name)); return Err(format!(
"Kann Team nicht der Warteschlange hinzufügen, weil das Team {} nicht zu deiner Station kommen soll.",
team.name
));
} }
Rating::create(db, self, team).await?; Rating::create(db, self, team).await?;
@ -132,7 +135,10 @@ 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();
if !waiting_teams.contains(&team) { if !waiting_teams.contains(&team) {
return Err(format!("Kann Team nicht von der Warteschlange gelöscht werden, weil das Team {} nicht in der Warteschlange ist.", team.name)); return Err(format!(
"Kann Team nicht von der Warteschlange gelöscht werden, weil das Team {} nicht in der Warteschlange ist.",
team.name
));
} }
Rating::delete(db, self, team).await?; Rating::delete(db, self, team).await?;

View File

@ -1,17 +1,18 @@
use crate::{ use crate::{
AppState,
admin::station::Station, admin::station::Station,
er, err, er, err,
models::rating::{Rating, TeamsAtStationLocation}, models::rating::{Rating, TeamsAtStationLocation},
partials::page, partials::page,
suc, succ, AppState, suc, succ,
}; };
use axum::{ use axum::{
Form, Router,
extract::State, extract::State,
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
routing::{get, post}, routing::{get, post},
Form, Router,
}; };
use maud::{html, Markup}; use maud::{Markup, html};
use serde::Deserialize; use serde::Deserialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;
@ -506,7 +507,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
} }
tbody { tbody {
@for station in &stations { @for station in &stations {
@let status = TeamsAtStationLocation::for_station(&db, &station).await; @let status = TeamsAtStationLocation::for_station(&db, station).await;
tr { tr {
td { td {
@if station.routes(&db).await.is_empty() { @if station.routes(&db).await.is_empty() {

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
admin::{route::Route, station::Station},
AppState, AppState,
admin::{route::Route, station::Station},
}; };
use axum::Router; use axum::Router;
use chrono::{DateTime, Local, NaiveDateTime, Utc}; use chrono::{DateTime, Local, NaiveDateTime, Utc};

View File

@ -1,17 +1,18 @@
use super::{CreateError, LastContactTeam, Team}; use super::{CreateError, LastContactTeam, Team};
use crate::{ use crate::{
AppState,
admin::{route::Route, station::Station}, admin::{route::Route, station::Station},
err, err,
partials::page, partials::page,
pl, succ, AppState, pl, succ,
}; };
use axum::{ use axum::{
Form, Router,
extract::State, extract::State,
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
routing::{get, post}, routing::{get, post},
Form, Router,
}; };
use maud::{html, Markup, PreEscaped}; use maud::{Markup, PreEscaped, html};
use serde::Deserialize; use serde::Deserialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;

View File

@ -17,7 +17,7 @@ macro_rules! testdb {
i18n!("locales", fallback = "de-AT"); i18n!("locales", fallback = "de-AT");
use admin::station::Station; use admin::station::Station;
use axum::{body::Body, extract::FromRef, response::Response, routing::get, Router}; use axum::{Router, body::Body, extract::FromRef, response::Response, routing::get};
use partials::page; use partials::page;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;

View File

@ -1,4 +1,4 @@
use crate::{admin::team::Team, Station}; use crate::{Station, admin::team::Team};
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};

View File

@ -1,4 +1,4 @@
use maud::{html, Markup, DOCTYPE}; use maud::{DOCTYPE, Markup, html};
use tower_sessions::Session; use tower_sessions::Session;
pub(crate) async fn page(content: Markup, session: Session, leaflet: bool) -> Markup { pub(crate) async fn page(content: Markup, session: Session, leaflet: bool) -> Markup {

View File

@ -37,6 +37,35 @@ async fn view(
let content = html! { let content = html! {
h1 { (format!("Station {}", station.name)) } h1 { (format!("Station {}", station.name)) }
@if let (Some(lat), Some(lng)) = (station.lat, station.lng) {
article {
b { "Hier befindet sich deine Station:" }
div id="map" style="height: 500px" {}
script { (format!("
const map = L.map('map').setView([{lat}, {lng}], 14);
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
attribution: '© OpenStreetMap contributors'
}}).addTo(map);
const myIcon = L.icon({{
iconUrl: '/marker.png',
iconAnchor: [12, 41]
}});
currentMarker = L.marker([{lat}, {lng}], {{icon: myIcon}}).addTo(map);
map.setView([lat, lng], 14);
"))
}
sub {
a href=(format!("https://www.google.com/maps?q={lat},{lng}")) target="_blank" {
"Google Maps..."
}
}
}
}
article { article {
"Insgesamt sollten " "Insgesamt sollten "
(teams.total_teams) (teams.total_teams)
@ -200,7 +229,9 @@ async fn view(
}; };
partials::page(content, session, false).await let use_map = station.lat.is_some() && station.lng.is_some();
partials::page(content, session, use_map).await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -221,7 +252,11 @@ async fn new_waiting(
return Redirect::to("/s/{id}/{code}"); return Redirect::to("/s/{id}/{code}");
}; };
let Some(team) = Team::find_by_id(&db, form.team_id).await else { let Some(team) = Team::find_by_id(&db, form.team_id).await else {
err!(session, "Konnte das Team der Warteschlange nicht hinzufügen, weil ein Team mit ID {} nicht existiert", form.team_id); err!(
session,
"Konnte das Team der Warteschlange nicht hinzufügen, weil ein Team mit ID {} nicht existiert",
form.team_id
);
return Redirect::to("/s/{id}/{code}"); return Redirect::to("/s/{id}/{code}");
}; };
@ -247,7 +282,11 @@ async fn remove_waiting(
}; };
let Some(team) = Team::find_by_id(&db, team_id).await else { let Some(team) = Team::find_by_id(&db, team_id).await else {
err!(session, "Konnte das Team nicht von der Warteschlange entfernen, weil ein Team mit ID {} nicht existiert", team_id); err!(
session,
"Konnte das Team nicht von der Warteschlange entfernen, weil ein Team mit ID {} nicht existiert",
team_id
);
return Redirect::to("/s/{id}/{code}"); return Redirect::to("/s/{id}/{code}");
}; };
@ -303,7 +342,11 @@ async fn remove_doing(
}; };
let Some(team) = Team::find_by_id(&db, team_id).await else { let Some(team) = Team::find_by_id(&db, team_id).await else {
err!(session, "Konnte das Team nicht zur Warteschlange hinzufügen, weil ein Team mit ID {} nicht existiert", team_id); err!(
session,
"Konnte das Team nicht zur Warteschlange hinzufügen, weil ein Team mit ID {} nicht existiert",
team_id
);
return Redirect::to("/s/{id}/{code}"); return Redirect::to("/s/{id}/{code}");
}; };
@ -359,7 +402,11 @@ async fn remove_left(
}; };
let Some(team) = Team::find_by_id(&db, team_id).await else { let Some(team) = Team::find_by_id(&db, team_id).await else {
err!(session, "Konnte das Team nicht zur Arbeits-Position hinzufügen, weil ein Team mit ID {} nicht existiert", team_id); err!(
session,
"Konnte das Team nicht zur Arbeits-Position hinzufügen, weil ein Team mit ID {} nicht existiert",
team_id
);
return Redirect::to("/s/{id}/{code}"); return Redirect::to("/s/{id}/{code}");
}; };