move to /admimn

This commit is contained in:
Philipp Hofer 2025-04-08 20:18:50 +02:00
parent d69f2ee13e
commit bacce1c55a
9 changed files with 150 additions and 139 deletions

View File

@ -9,8 +9,8 @@ station_new: "Neue Station"
station_name: "Stationsname" station_name: "Stationsname"
station_create_succ: "Station %{name} erfolgreich erstellt" station_create_succ: "Station %{name} erfolgreich erstellt"
station_create_err_duplicate_name: "Station %{name} konnte _NICHT_ erstellt werden, da es bereits eine Station mit diesem Namen gibt (%{err})!" station_create_err_duplicate_name: "Station %{name} konnte _NICHT_ erstellt werden, da es bereits eine Station mit diesem Namen gibt (%{err})!"
station_delete_succ: "Station ${name} erfolgreich gelöscht" station_delete_succ: "Station %{name} erfolgreich gelöscht"
station_delete_err_nonexisting: "Station mit ID ${id} konnte nicht gelöscht werden, da sie nicht existiert" station_delete_err_nonexisting: "Station mit ID %{id} konnte nicht gelöscht werden, da sie nicht existiert"
station_delete_err_already_used: "Station ${name} konnte nicht gelöscht werden, da sie bereits verwendet wird (${err})" station_delete_err_already_used: "Station %{name} konnte nicht gelöscht werden, da sie bereits verwendet wird (%{err})"
routes: "Routen" routes: "Routen"
teams: "Teams" teams: "Teams"

44
src/admin/mod.rs Normal file
View File

@ -0,0 +1,44 @@
use crate::page;
use axum::{routing::get, Router};
use maud::{html, Markup};
use sqlx::SqlitePool;
use std::sync::Arc;
use tower_sessions::Session;
pub(crate) mod route;
pub(crate) mod station;
pub(crate) mod team;
async fn index(session: Session) -> Markup {
let content = html! {
h1 { (t!("app_name")) }
nav {
ul {
li {
a role="button" href="/admin/station" {
(t!("stations"))
}
}
li {
a role="button" href="/admin/route" {
(t!("routes"))
}
}
li {
a role="button" href="/admin/team" {
(t!("teams"))
}
}
}
}
};
page(content, session, false).await
}
pub(super) fn routes() -> Router<Arc<SqlitePool>> {
Router::new()
.route("/", get(index))
.nest("/station", station::routes())
.nest("/route", route::routes())
.nest("/team", team::routes())
}

View File

@ -1,4 +1,4 @@
use crate::{station::Station, team::Team}; use crate::admin::{station::Station, team::Team};
use axum::Router; use axum::Router;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Row, SqlitePool}; use sqlx::{FromRow, Row, SqlitePool};

View File

@ -1,5 +1,5 @@
use super::Route; use super::Route;
use crate::{err, page, station::Station, succ}; use crate::{admin::station::Station, err, page, succ};
use axum::{ use axum::{
extract::State, extract::State,
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
@ -17,15 +17,15 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let content = html! { let content = html! {
h1 { h1 {
a href="/" { "↩️" } a href="/admin/" { "↩️" }
"Routen" "Routen"
} }
article { article {
em { "Routen " } em { "Routen " }
"definieren welche " "definieren welche "
a href="/station" { "Stationen" } a href="/admin/station" { "Stationen" }
" von den " " von den "
a href="/team" { "Teams" } a href="/admin/team" { "Teams" }
" in welcher Reihenfolge abgeklappert werden sollen. Wenn es verschiedene Kategorien (zB Kinder- und Erwachsenenwertung) gibt, kannst du auch mehrere Routen mit (teils) überlappenden Stationen erstellen." " in welcher Reihenfolge abgeklappert werden sollen. Wenn es verschiedene Kategorien (zB Kinder- und Erwachsenenwertung) gibt, kannst du auch mehrere Routen mit (teils) überlappenden Stationen erstellen."
} }
ol { ol {
@ -36,10 +36,10 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
"⚠️" "⚠️"
} }
} }
a href=(format!("/route/{}", route.id)){ a href=(format!("/admin/route/{}", route.id)){
(route.name) (route.name)
} }
a href=(format!("/route/{}/delete", route.id)) a href=(format!("/admin/route/{}/delete", route.id))
onclick="return confirm('Bist du sicher, dass die Route gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" { onclick="return confirm('Bist du sicher, dass die Route gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" {
"🗑️" "🗑️"
} }
@ -52,7 +52,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
} }
} }
h2 { "Neue Route" } h2 { "Neue Route" }
form action="/route" method="post" { form action="/admin/route" method="post" {
fieldset role="team" { fieldset role="team" {
input type="text" name="name" placeholder="Routenname" required; input type="text" name="name" placeholder="Routenname" required;
input type="submit" value="Neue Route"; input type="submit" value="Neue Route";
@ -81,7 +81,7 @@ async fn create(
), ),
} }
Redirect::to("/route") Redirect::to("/admin/route")
} }
async fn delete( async fn delete(
@ -95,7 +95,7 @@ async fn delete(
"Route mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert" "Route mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert"
); );
return Redirect::to("/route"); return Redirect::to("/admin/route");
}; };
match route.delete(&db).await { match route.delete(&db).await {
@ -107,7 +107,7 @@ async fn delete(
), ),
} }
Redirect::to("/route") Redirect::to("/admin/route")
} }
async fn view( async fn view(
@ -121,7 +121,7 @@ async fn view(
"Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" "Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
); );
return Err(Redirect::to("/route")); return Err(Redirect::to("/admin/route"));
}; };
let cur_stations = route.stations(&db).await; let cur_stations = route.stations(&db).await;
@ -131,13 +131,13 @@ async fn view(
let content = html! { let content = html! {
h1 { h1 {
a href="/route" { "↩️" } a href="/admin/route" { "↩️" }
"Route " (route.name) "Route " (route.name)
} }
article { article {
details { details {
summary { "Routenname bearbeiten ✏️" } summary { "Routenname bearbeiten ✏️" }
form action=(format!("/route/{}/name", route.id)) method="post" { form action=(format!("/admin/route/{}/name", route.id)) method="post" {
input type="text" name="name" value=(route.name) required; input type="text" name="name" value=(route.name) required;
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -148,7 +148,7 @@ async fn view(
@if stations_not_in_route.is_empty() { @if stations_not_in_route.is_empty() {
article class="error" { article class="error" {
(PreEscaped("Bevor du einer Route Stationen zuweisen kannst, musst du die Stationen erstellen &rarr; ")) (PreEscaped("Bevor du einer Route Stationen zuweisen kannst, musst du die Stationen erstellen &rarr; "))
a role="button" href="/station" { a role="button" href="/admin/station" {
"Station erstellen" "Station erstellen"
} }
} }
@ -163,13 +163,13 @@ async fn view(
li { li {
(station.name) (station.name)
@if idx > 0 { @if idx > 0 {
a href=(format!("/route/{}/move-station-higher/{}", route.id, station.id)){ a href=(format!("/admin/route/{}/move-station-higher/{}", route.id, station.id)){
em data-tooltip=(format!("{} nach vor reihen", station.name)) { em data-tooltip=(format!("{} nach vor reihen", station.name)) {
"⬆️" "⬆️"
} }
} }
} }
a href=(format!("/route/{}/delete-station/{}", route.id, station.id)) a href=(format!("/admin/route/{}/delete-station/{}", route.id, station.id))
onclick="return confirm('Bist du sicher, dass die Station von der Route entfernt werden soll?');" { onclick="return confirm('Bist du sicher, dass die Station von der Route entfernt werden soll?');" {
"🗑️" "🗑️"
} }
@ -178,7 +178,7 @@ async fn view(
} }
} }
@if !stations_not_in_route.is_empty(){ @if !stations_not_in_route.is_empty(){
form action=(format!("/route/{}/add-station", route.id)) method="post" { form action=(format!("/admin/route/{}/add-station", route.id)) method="post" {
select name="station" aria-label="Hinzuzufügende Station auswählen" required { select name="station" aria-label="Hinzuzufügende Station auswählen" required {
@for station in &stations_not_in_route { @for station in &stations_not_in_route {
option value=(station.id) { option value=(station.id) {
@ -193,7 +193,7 @@ async fn view(
@if teams.is_empty() { @if teams.is_empty() {
article { article {
"Noch keine Team ist dieser Route zugeteilt." "Noch keine Team ist dieser Route zugeteilt."
a role="button" href="/team" { a role="button" href="/admin/team" {
"Zu den Teams" "Zu den Teams"
} }
@ -202,7 +202,7 @@ async fn view(
ol { ol {
@for team in &teams { @for team in &teams {
li { li {
a href=(format!("/team/{}", team.id)) { a href=(format!("/admin/team/{}", team.id)) {
(team.name) (team.name)
} }
} }
@ -230,7 +230,7 @@ async fn update_name(
"Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" "Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
); );
return Redirect::to("/route"); return Redirect::to("/admin/route");
}; };
route.update_name(&db, &form.name).await; route.update_name(&db, &form.name).await;
@ -242,7 +242,7 @@ async fn update_name(
form.name form.name
); );
Redirect::to(&format!("/route/{id}")) Redirect::to(&format!("/admin/route/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -261,7 +261,7 @@ async fn add_station(
"Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" "Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
); );
return Redirect::to("/route"); return Redirect::to("/admin/route");
}; };
let Some(station) = Station::find_by_id(&db, form.station).await else { let Some(station) = Station::find_by_id(&db, form.station).await else {
err!( err!(
@ -269,7 +269,7 @@ async fn add_station(
"Station mit ID {id} konnte nicht hinzugefügt werden, da sie nicht existiert" "Station mit ID {id} konnte nicht hinzugefügt werden, da sie nicht existiert"
); );
return Redirect::to(&format!("/route/{id}")); return Redirect::to(&format!("/admin/route/{id}"));
}; };
match route.add_station(&db, &station).await { match route.add_station(&db, &station).await {
@ -287,7 +287,7 @@ async fn add_station(
), ),
} }
Redirect::to(&format!("/route/{id}")) Redirect::to(&format!("/admin/route/{id}"))
} }
async fn delete_station( async fn delete_station(
@ -301,7 +301,7 @@ async fn delete_station(
"Konnte keine Station von Route mit ID {route_id} entfernen, da diese Route nicht existiert." "Konnte keine Station von Route mit ID {route_id} entfernen, da diese Route nicht existiert."
); );
return Redirect::to("/route"); return Redirect::to("/admin/route");
}; };
let Some(station) = Station::find_by_id(&db, station_id).await else { let Some(station) = Station::find_by_id(&db, station_id).await else {
err!( err!(
@ -310,7 +310,7 @@ async fn delete_station(
route.name route.name
); );
return Redirect::to(&format!("/route/{route_id}")); return Redirect::to(&format!("/admin/route/{route_id}"));
}; };
if route.delete_station(&db, &station).await { if route.delete_station(&db, &station).await {
@ -329,7 +329,7 @@ async fn delete_station(
); );
} }
Redirect::to(&format!("/route/{route_id}")) Redirect::to(&format!("/admin/route/{route_id}"))
} }
async fn move_station_higher( async fn move_station_higher(
@ -343,7 +343,7 @@ async fn move_station_higher(
"Konnte keine Station von Route mit ID {route_id} verschieben, da diese Route nicht existiert." "Konnte keine Station von Route mit ID {route_id} verschieben, da diese Route nicht existiert."
); );
return Redirect::to("/route"); return Redirect::to("/admin/route");
}; };
let Some(station) = Station::find_by_id(&db, station_id).await else { let Some(station) = Station::find_by_id(&db, station_id).await else {
err!( err!(
@ -352,7 +352,7 @@ async fn move_station_higher(
route.name route.name
); );
return Redirect::to(&format!("/route/{route_id}")); return Redirect::to(&format!("/admin/route/{route_id}"));
}; };
if route.move_station_higher(&db, &station).await { if route.move_station_higher(&db, &station).await {
@ -371,7 +371,7 @@ async fn move_station_higher(
); );
} }
Redirect::to(&format!("/route/{route_id}")) Redirect::to(&format!("/admin/route/{route_id}"))
} }
pub(super) fn routes() -> Router<Arc<SqlitePool>> { pub(super) fn routes() -> Router<Arc<SqlitePool>> {

View File

@ -1,4 +1,4 @@
use crate::route::Route; use crate::admin::route::Route;
use axum::Router; use axum::Router;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,4 +1,4 @@
use crate::{er, err, partials::page, station::Station, suc, succ}; use crate::{admin::station::Station, er, err, partials::page, suc, succ};
use axum::{ use axum::{
extract::State, extract::State,
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
@ -33,7 +33,7 @@ async fn create(
), ),
} }
Redirect::to("/station") Redirect::to("/admin/station")
} }
async fn delete( async fn delete(
@ -44,7 +44,7 @@ async fn delete(
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
er!(session, t!("station_delete_err_nonexisting", id = id)); er!(session, t!("station_delete_err_nonexisting", id = id));
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
match station.delete(&db).await { match station.delete(&db).await {
@ -59,7 +59,7 @@ async fn delete(
), ),
} }
Redirect::to("/station") Redirect::to("/admin/station")
} }
async fn view( async fn view(
@ -73,19 +73,19 @@ async fn view(
"Station mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
); );
return Err(Redirect::to("/station")); return Err(Redirect::to("/admin/station"));
}; };
// maybe switch to maud-display impl of station // maybe switch to maud-display impl of station
let content = html! { let content = html! {
h1 { h1 {
a href="/station" { "↩️" } a href="/admin/station" { "↩️" }
"Station " (station.name) "Station " (station.name)
} }
article { article {
details { details {
summary { "Stationsname bearbeiten ✏️" } summary { "Stationsname bearbeiten ✏️" }
form action=(format!("/station/{}/name", station.id)) method="post" { form action=(format!("/admin/station/{}/name", station.id)) method="post" {
input type="text" name="name" value=(station.name) required; input type="text" name="name" value=(station.name) required;
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -102,7 +102,7 @@ async fn view(
(notes) (notes)
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/station/{}/notes", station.id)) method="post" { form action=(format!("/admin/station/{}/notes", station.id)) method="post" {
textarea name="notes" required rows="10" { (notes) }; textarea name="notes" required rows="10" { (notes) };
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -110,7 +110,7 @@ async fn view(
}, },
None => details { None => details {
summary { "Neue Notiz hinzufügen" } summary { "Neue Notiz hinzufügen" }
form action=(format!("/station/{}/notes", station.id)) method="post" { form action=(format!("/admin/station/{}/notes", station.id)) method="post" {
textarea name="notes" required rows="10" {}; textarea name="notes" required rows="10" {};
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -136,12 +136,12 @@ async fn view(
} }
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/station/{}/amount-people", station.id)) method="post" { form action=(format!("/admin/station/{}/amount-people", station.id)) method="post" {
input type="number" name="amount_people" min="0" max="10"; input type="number" name="amount_people" min="0" max="10";
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
button class="error" { button class="error" {
a href=(format!("/station/{}/amount-people-reset", station.id)) { a href=(format!("/admin/station/{}/amount-people-reset", station.id)) {
em data-tooltip="Ich weiß noch nicht wv. Personen benötigt werden." { em data-tooltip="Ich weiß noch nicht wv. Personen benötigt werden." {
"?" "?"
} }
@ -173,12 +173,12 @@ async fn view(
} }
} }
@if station.lat.is_some() && station.lng.is_some() { @if station.lat.is_some() && station.lng.is_some() {
a href=(format!("/station/{}/location-clear", station.id)) a href=(format!("/admin/station/{}/location-clear", station.id))
onclick="return confirm('Bist du sicher, dass du den Standort der Station löschen willst?');"{ onclick="return confirm('Bist du sicher, dass du den Standort der Station löschen willst?');"{
"Standort löschen" "Standort löschen"
} }
} }
form action=(format!("/station/{}/location", station.id)) method="post" { form action=(format!("/admin/station/{}/location", station.id)) method="post" {
input type="hidden" name="lat" id="lat"; input type="hidden" name="lat" id="lat";
input type="hidden" name="lng" id="lng"; input type="hidden" name="lng" id="lng";
input type="submit" value="Neuen Standort speichern" style="display: None;" id="location-submit"; input type="submit" value="Neuen Standort speichern" style="display: None;" id="location-submit";
@ -254,7 +254,7 @@ async fn update_name(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_name(&db, &form.name).await; station.update_name(&db, &form.name).await;
@ -266,7 +266,7 @@ async fn update_name(
form.name form.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -285,7 +285,7 @@ async fn update_notes(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_notes(&db, &form.notes).await; station.update_notes(&db, &form.notes).await;
@ -296,7 +296,7 @@ async fn update_notes(
station.name station.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -315,7 +315,7 @@ async fn update_amount_people(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_amount_people(&db, form.amount_people).await; station.update_amount_people(&db, form.amount_people).await;
@ -326,7 +326,7 @@ async fn update_amount_people(
station.name station.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
async fn update_amount_people_reset( async fn update_amount_people_reset(
@ -340,7 +340,7 @@ async fn update_amount_people_reset(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_amount_people_reset(&db).await; station.update_amount_people_reset(&db).await;
@ -351,7 +351,7 @@ async fn update_amount_people_reset(
station.name station.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -371,7 +371,7 @@ async fn update_location(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_location(&db, form.lat, form.lng).await; station.update_location(&db, form.lat, form.lng).await;
@ -382,7 +382,7 @@ async fn update_location(
station.name station.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
async fn update_location_clear( async fn update_location_clear(
@ -396,7 +396,7 @@ async fn update_location_clear(
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/station"); return Redirect::to("/admin/station");
}; };
station.update_location_clear(&db).await; station.update_location_clear(&db).await;
@ -407,7 +407,7 @@ async fn update_location_clear(
station.name station.name
); );
Redirect::to(&format!("/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup { async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
@ -415,7 +415,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let content = html! { let content = html! {
h1 { h1 {
a href="/" { "↩️" } a href="/admin/" { "↩️" }
(t!("stations")) (t!("stations"))
} }
article { article {
@ -433,10 +433,10 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
"⚠️" "⚠️"
} }
} }
a href=(format!("/station/{}", station.id)){ a href=(format!("/admin/station/{}", station.id)){
(station.name) (station.name)
} }
a href=(format!("/station/{}/delete", station.id)) a href=(format!("/admin/station/{}/delete", station.id))
onclick=(format!("return confirm('{}');", t!("station_confirm_deletion"))) { onclick=(format!("return confirm('{}');", t!("station_confirm_deletion"))) {
"🗑️" "🗑️"
} }
@ -449,7 +449,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
} }
} }
h2 { (t!("station_new")) } h2 { (t!("station_new")) }
form action="/station" method="post" { form action="/admin/station" method="post" {
fieldset role="team" { fieldset role="team" {
input type="text" name="name" placeholder=(t!("station_name")) required; input type="text" name="name" placeholder=(t!("station_name")) required;
input type="submit" value=(t!("station_new")); input type="submit" value=(t!("station_new"));

View File

@ -1,4 +1,4 @@
use crate::{route::Route, station::Station}; use crate::admin::{route::Route, station::Station};
use axum::Router; use axum::Router;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, SqlitePool};

View File

@ -1,5 +1,5 @@
use super::{CreateError, Team}; use super::{CreateError, Team};
use crate::{err, partials::page, pl, route::Route, station::Station, succ}; use crate::{admin::route::Route, admin::station::Station, err, partials::page, pl, succ};
use axum::{ use axum::{
extract::State, extract::State,
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
@ -31,7 +31,7 @@ async fn create(
form.route_id form.route_id
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
match Team::create(&db, &form.name, &route).await { match Team::create(&db, &form.name, &route).await {
@ -49,7 +49,7 @@ async fn create(
), ),
} }
Redirect::to("/team") Redirect::to("/admin/team")
} }
async fn delete( async fn delete(
@ -63,7 +63,7 @@ async fn delete(
"Team mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert" "Team mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
match team.delete(&db).await { match team.delete(&db).await {
@ -75,7 +75,7 @@ async fn delete(
), ),
} }
Redirect::to("/team") Redirect::to("/admin/team")
} }
async fn view( async fn view(
@ -89,7 +89,7 @@ async fn view(
"Team mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
); );
return Err(Redirect::to("/team")); return Err(Redirect::to("/admin/team"));
}; };
let first_station = team.first_station(&db).await; let first_station = team.first_station(&db).await;
let routes = Route::all(&db).await; let routes = Route::all(&db).await;
@ -99,13 +99,13 @@ async fn view(
// maybe switch to maud-display impl of team // maybe switch to maud-display impl of team
let content = html! { let content = html! {
h1 { h1 {
a href="/team" { "↩️" } a href="/admin/team" { "↩️" }
"Team " (team.name) "Team " (team.name)
} }
article { article {
details { details {
summary { "Teamnamen bearbeiten ✏️" } summary { "Teamnamen bearbeiten ✏️" }
form action=(format!("/team/{}/name", team.id)) method="post" { form action=(format!("/admin/team/{}/name", team.id)) method="post" {
input type="text" name="name" value=(team.name) required; input type="text" name="name" value=(team.name) required;
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -122,7 +122,7 @@ async fn view(
(notes) (notes)
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/team/{}/notes", team.id)) method="post" { form action=(format!("/admin/team/{}/notes", team.id)) method="post" {
textarea name="notes" required rows="10" { (notes) }; textarea name="notes" required rows="10" { (notes) };
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -130,7 +130,7 @@ async fn view(
}, },
None => details { None => details {
summary { "Neue Notiz hinzufügen" } summary { "Neue Notiz hinzufügen" }
form action=(format!("/team/{}/notes", team.id)) method="post" { form action=(format!("/admin/team/{}/notes", team.id)) method="post" {
textarea name="notes" required rows="10" {}; textarea name="notes" required rows="10" {};
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
@ -147,11 +147,11 @@ async fn view(
} }
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/team/{}/amount-people", team.id)) method="post" { form action=(format!("/admin/team/{}/amount-people", team.id)) method="post" {
input type="number" name="amount_people" min="0" max="10"; input type="number" name="amount_people" min="0" max="10";
input type="submit" value="Speichern"; input type="submit" value="Speichern";
} }
a href=(format!("/team/{}/amount-people-reset", team.id)) { a href=(format!("/admin/team/{}/amount-people-reset", team.id)) {
button class="error" { button class="error" {
em data-tooltip="Ich weiß noch nicht wv. Personen dieses Team beherbergt." { em data-tooltip="Ich weiß noch nicht wv. Personen dieses Team beherbergt." {
"?" "?"
@ -164,13 +164,13 @@ async fn view(
tr { tr {
th scope="row" { "Route" }; th scope="row" { "Route" };
td { td {
a href=(format!("/route/{}", &team.route(&db).await.id)) { a href=(format!("/admin/route/{}", &team.route(&db).await.id)) {
(&team.route(&db).await.name) (&team.route(&db).await.name)
} }
@if routes.len() > 1 { @if routes.len() > 1 {
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/team/{}/update-route", team.id)) method="post" { form action=(format!("/admin/team/{}/update-route", team.id)) method="post" {
select name="route_id" aria-label="Route auswählen" required { select name="route_id" aria-label="Route auswählen" required {
@for route in &routes { @for route in &routes {
@if route.id != team.route(&db).await.id { @if route.id != team.route(&db).await.id {
@ -194,13 +194,13 @@ async fn view(
} }
}; };
td { td {
a href=(format!("/station/{}", first_station.id)) { a href=(format!("/admin/station/{}", first_station.id)) {
(first_station.name) (first_station.name)
} }
@if stations.len() > 1 { @if stations.len() > 1 {
details { details {
summary { "✏️" } summary { "✏️" }
form action=(format!("/team/{}/update-first-station", team.id)) method="post" { form action=(format!("/admin/team/{}/update-first-station", team.id)) method="post" {
select name="first_station_id" aria-label="Station auswählen" required { select name="first_station_id" aria-label="Station auswählen" required {
@for station in &stations { @for station in &stations {
@if station.id != first_station.id { @if station.id != first_station.id {
@ -243,7 +243,7 @@ async fn update_name(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
team.update_name(&db, &form.name).await; team.update_name(&db, &form.name).await;
@ -255,7 +255,7 @@ async fn update_name(
form.name form.name
); );
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -274,7 +274,7 @@ async fn update_notes(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
team.update_notes(&db, &form.notes).await; team.update_notes(&db, &form.notes).await;
@ -285,7 +285,7 @@ async fn update_notes(
team.name team.name
); );
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -304,7 +304,7 @@ async fn update_amount_people(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
team.update_amount_people(&db, form.amount_people).await; team.update_amount_people(&db, form.amount_people).await;
@ -315,7 +315,7 @@ async fn update_amount_people(
team.name team.name
); );
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -335,7 +335,7 @@ async fn update_route(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
let Some(route) = Route::find_by_id(&db, form.route_id).await else { let Some(route) = Route::find_by_id(&db, form.route_id).await else {
@ -345,7 +345,7 @@ async fn update_route(
form.route_id form.route_id
); );
return Redirect::to(&format!("/team/{id}")); return Redirect::to(&format!("/admin/team/{id}"));
}; };
match team.update_route(&db, &route).await { match team.update_route(&db, &route).await {
@ -365,7 +365,7 @@ async fn update_route(
), ),
} }
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -384,7 +384,7 @@ async fn update_first_station(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
let Some(station) = Station::find_by_id(&db, form.first_station_id).await else { let Some(station) = Station::find_by_id(&db, form.first_station_id).await else {
@ -395,7 +395,7 @@ async fn update_first_station(
team.id team.id
); );
return Redirect::to(&format!("/team/{id}")); return Redirect::to(&format!("/admin/team/{id}"));
}; };
if !station.is_in_route(&db, &team.route(&db).await).await { if !station.is_in_route(&db, &team.route(&db).await).await {
@ -408,7 +408,7 @@ async fn update_first_station(
team.name team.name
); );
return Redirect::to(&format!("/team/{id}")); return Redirect::to(&format!("/admin/team/{id}"));
} }
team.update_first_station(&db, &station).await; team.update_first_station(&db, &station).await;
@ -420,7 +420,7 @@ async fn update_first_station(
station.name station.name
); );
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
async fn update_amount_people_reset( async fn update_amount_people_reset(
@ -434,7 +434,7 @@ async fn update_amount_people_reset(
"Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert" "Team mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
); );
return Redirect::to("/team"); return Redirect::to("/admin/team");
}; };
team.update_amount_people_reset(&db).await; team.update_amount_people_reset(&db).await;
@ -445,7 +445,7 @@ async fn update_amount_people_reset(
team.name team.name
); );
Redirect::to(&format!("/team/{id}")) Redirect::to(&format!("/admin/team/{id}"))
} }
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup { async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
@ -454,24 +454,24 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let content = html! { let content = html! {
h1 { h1 {
a href="/" { "↩️" } a href="/admin/" { "↩️" }
"Teams" "Teams"
} }
article { article {
em { "Teams " } em { "Teams " }
"sind eine Menge an Personen, die verschiedene " "sind eine Menge an Personen, die verschiedene "
a href="/station" { "Stationen" } a href="/admin/station" { "Stationen" }
" ablaufen. Welche Stationen, entscheidet sich je nachdem, welcher " " ablaufen. Welche Stationen, entscheidet sich je nachdem, welcher "
a href="/route" { "Route" } a href="/admin/route" { "Route" }
" sie zugewiesen sind." " sie zugewiesen sind."
} }
ol { ol {
@for team in &teams{ @for team in &teams{
li { li {
a href=(format!("/team/{}", team.id)){ a href=(format!("/admin/team/{}", team.id)){
(team.name) (team.name)
} }
a href=(format!("/team/{}/delete", team.id)) a href=(format!("/admin/team/{}/delete", team.id))
onclick="return confirm('Bist du sicher, dass das Team gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" { onclick="return confirm('Bist du sicher, dass das Team gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" {
"🗑️" "🗑️"
} }
@ -490,12 +490,12 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
@if routes.is_empty() { @if routes.is_empty() {
article class="error" { article class="error" {
(PreEscaped("Bevor du ein Team erstellen kannst, musst du zumindest eine Route erstellen, die das Team gehen kann &rarr; ")) (PreEscaped("Bevor du ein Team erstellen kannst, musst du zumindest eine Route erstellen, die das Team gehen kann &rarr; "))
a role="button" href="/route" { a role="button" href="/admin/route" {
"Team erstellen" "Team erstellen"
} }
} }
} @else { } @else {
form action="/team" method="post" { form action="/admin/team" method="post" {
@if routes.len() == 1 { @if routes.len() == 1 {
fieldset role="team" { fieldset role="team" {
input type="text" name="name" placeholder="Teamnamen" required; input type="text" name="name" placeholder="Teamnamen" required;

View File

@ -4,18 +4,14 @@ extern crate rust_i18n;
i18n!("locales", fallback = "de-AT"); i18n!("locales", fallback = "de-AT");
use axum::{body::Body, response::Response, routing::get, Router}; use axum::{body::Body, response::Response, routing::get, Router};
use maud::{html, Markup};
use partials::page; use partials::page;
use rust_i18n::t;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_sessions::{MemoryStore, Session, SessionManagerLayer}; use tower_sessions::{MemoryStore, SessionManagerLayer};
pub(crate) mod admin;
mod partials; mod partials;
pub(crate) mod route;
pub(crate) mod station;
pub(crate) mod team;
pub(crate) fn pl(amount: usize, single: &str, append: &str) -> String { pub(crate) fn pl(amount: usize, single: &str, append: &str) -> String {
if amount == 1 { if amount == 1 {
@ -104,42 +100,13 @@ async fn serve_marker_png() -> Response<Body> {
.unwrap() .unwrap()
} }
async fn index(session: Session) -> Markup {
let content = html! {
h1 { (t!("app_name")) }
nav {
ul {
li {
a role="button" href="/station" {
(t!("stations"))
}
}
li {
a role="button" href="/route" {
(t!("routes"))
}
}
li {
a role="button" href="/team" {
(t!("teams"))
}
}
}
}
};
page(content, session, false).await
}
/// Starts the main application. /// Starts the main application.
pub async fn start(listener: TcpListener, db: SqlitePool) { pub async fn start(listener: TcpListener, db: SqlitePool) {
let session_store = MemoryStore::default(); let session_store = MemoryStore::default();
let session_layer = SessionManagerLayer::new(session_store); let session_layer = SessionManagerLayer::new(session_store);
let app = Router::new() let app = Router::new()
.route("/", get(index)) .nest("/admin", admin::routes())
.nest("/station", station::routes())
.nest("/route", route::routes())
.nest("/team", team::routes())
.route("/pico.css", get(serve_pico_css)) .route("/pico.css", get(serve_pico_css))
.route("/style.css", get(serve_my_css)) .route("/style.css", get(serve_my_css))
.route("/leaflet.css", get(serve_leaflet_css)) .route("/leaflet.css", get(serve_leaflet_css))