more external string, continue #12
All checks were successful
CI/CD Pipeline / test (push) Successful in 14m52s
CI/CD Pipeline / deploy (push) Successful in 6m32s

This commit is contained in:
Philipp Hofer 2025-04-22 16:13:22 +02:00
parent 5c72e88a40
commit 1628bdc252
3 changed files with 156 additions and 175 deletions

View File

@ -34,6 +34,8 @@ info_crewless_station: "Wenn das eine unbemannte Station ist, wähle hier 0 Pers
# # # # ## ## # # # # # # # ## # # # # # # ## ## # # # # # # # ## # #
# ###### ###### # # ###### # # # #### # # #### # ###### ###### # # ###### # # # #### # # ####
# #
rating: "Bewertung"
ratings: "Bewertungen"
invalid_rating_code: "Falscher Quick-Login-Link. Bitte nochmal scannen oder neu eingeben." invalid_rating_code: "Falscher Quick-Login-Link. Bitte nochmal scannen oder neu eingeben."
infos: "Infos" infos: "Infos"
station_has_no_teams_to_take_to_start: "Nachdem es offiziell losgeht kannst du gleich direkt zu deiner Station gehen, du musst keine Teams mitnehmen." station_has_no_teams_to_take_to_start: "Nachdem es offiziell losgeht kannst du gleich direkt zu deiner Station gehen, du musst keine Teams mitnehmen."
@ -114,9 +116,26 @@ confirm_station_cancel_team_finished: "Bist du sicher, dass das Team noch nicht
# #
station: "Station" station: "Station"
stations: "Stationen" stations: "Stationen"
station_url: "Stations-Link"
station_url_info: "Diesen Link nur Betreuern der Station #{station} geben! Mit diesem Link erhält man die Berechtigung, Teams zu bewerten."
login_link: "Login-Link"
station_name_edit: "Routennamen bearbeiten"
go_to_stations: "Zu den Stationen" go_to_stations: "Zu den Stationen"
crewless_station: "Station ohne Stationsbetreuer" crewless_station: "Station ohne Stationsbetreuer"
amount_crew: "Anzahl Stationsbetreuer"
last_access_crew: "Letzter Zugriff eines Stationsbetreuers"
not_loggedin_yet: "Noch nicht eingeloggt :-("
not_sure_about_amount_crew: "Ich weiß noch nicht wv. Personen benötigt werden."
station_create: "Station erstellen" station_create: "Station erstellen"
arrived: "Angekommen"
started: "Begonnen"
left: "Gegangen"
delete_location: "Standort löschen"
save_new_location: "Neuen Standort speichern"
confirm_delete_location: "Bist du sicher, dass du den Standort der Station löschen willst?"
click_on_map_for_new_location: "Um einen neuen Standort zu wählen, auf einen Punkt in der Karte klicken"
click_on_map_for_location: "Um einen Standort zu wählen, auf einen Punkt in der Karte klicken"
enter_ratings_for_station: "Bewertungen für Station %{station} eingeben"
no_stations_yet: "Es gibt noch keine Stationen." no_stations_yet: "Es gibt noch keine Stationen."
stations_expl_without_first_word: "sind festgelegte Orte mit spezifischen Aufgaben." stations_expl_without_first_word: "sind festgelegte Orte mit spezifischen Aufgaben."
station_warning_not_assigned_route: "Noch keiner Route zugeordnet" # should be short -> tooltip station_warning_not_assigned_route: "Noch keiner Route zugeordnet" # should be short -> tooltip
@ -127,11 +146,26 @@ 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_open_err_nonexisting: "Station mit ID %{id} konnte nicht geöffnet werden, da sie nicht existiert"
station_edit_err_nonexisting: "Station mit ID %{id} konnte nicht bearbeitet 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_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})"
station_has_not_rated_team_yet: "Station hat Team noch nicht bewertet" # should be short -> tooltip station_has_not_rated_team_yet: "Station hat Team noch nicht bewertet" # should be short -> tooltip
station_move_up: "%{name} nach vor reihen" # should be short -> tooltip station_move_up: "%{name} nach vor reihen" # should be short -> tooltip
generate_station_pdf: "Stations PDF generieren" generate_station_pdf: "Stations PDF generieren"
station_new_name: "Station %{old} heißt ab sofort %{new}."
station_new_notes: "Notizen für die Station %{station} erfolgreich bearbeitet"
station_new_crew_amount: "Anzahl an Betreuer für die Station %{station} erfolgreich bearbeitet"
already_entered: "Schon eingetragen"
team_currently_at_station: "Team gerade bei der Station"
error_rating_team_already_rated: "Konnte team %{team} nicht bewerten, da das Team bereits von der Station %{station} bewertet wurde."
funny_you_entered_no_rating: "Du hast keine Bewertungen eingegeben... Spaßvogel!"
entered_n_ratings: "Erfolgreich %{amount} Bewertung(en) eingetragen"
location_changed: "Standort für die Station %{station} wurde erfolgreich geändert"
location_deleted: "Standort für die Station %{station} wurde erfolgreich gelöscht"
progress: "Fortschritt"
station_team_progress: "%{arrived}/%{total} (davon %{waiting} wartend + %{active} aktiv)"
station_ready: "Station bereit!"
# #
@ -172,6 +206,7 @@ highscore: "Highscore"
# # # # # # # # # ## # # # # # # # # # ##
# # # ##### # # # # # # # # ##### # # # # #
# #
admin: "Admin"
admins: "Admins" admins: "Admins"
cant_update_pw_if_already_existing: "Kann kein neues Passwort setzen, weil es bereits eins gibt..." cant_update_pw_if_already_existing: "Kann kein neues Passwort setzen, weil es bereits eins gibt..."
cant_update_pw_if_already_existing_for_user: "Kann kein neues Passwort für %{user} setzen, weil es bereits eins gibt..." cant_update_pw_if_already_existing_for_user: "Kann kein neues Passwort für %{user} setzen, weil es bereits eins gibt..."
@ -179,6 +214,18 @@ cant_update_pw_with_wrong_code: "Falscher Code zum Setzen eines neuen Passworts
new_pw_for_user: "Neues Passwort für %{user} setzen" new_pw_for_user: "Neues Passwort für %{user} setzen"
pw_set: "Passwort erfolgreich gesetzt" pw_set: "Passwort erfolgreich gesetzt"
cant_delete_last_admin: "Du kannst den letzten Admin nicht löschen" cant_delete_last_admin: "Du kannst den letzten Admin nicht löschen"
user_created: "User %{name} erfolgreich erstellt"
user_deleted: "User %{name} erfolgreich gelöscht"
user_delete_error_already_in_use: "User %{name} kann nicht gelöscht werden, da er/sie bereits verwendet wird. (%{err})"
user_create_error: "User %{name} konnte nicht erstellt werden: %{err}"
nonexisting_user: "User mit ID %{id} existiert nicht."
edit_username: "Username bearbeiten"
new_admin_link: "Passwort vergessen: Neuen Loginlink generieren"
confirm_new_admin_link: "Bist du sicher, dass du einen neuen Passwort-Link generieren willst? Mit dem alten Passwort kann man sich dann nicht mehr einloggen."
new_user_name: "Admin %{old} heißt ab sofort %{new}"
succ_new_admin_link: "Neuer Loginlink für User %{user} wurde generiert"
new_admin: "Neuer Admin"
confirm_admin_delete: "Bist du sicher, dass der User gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden."
# #

View File

@ -1,9 +1,9 @@
use crate::{ use crate::{
admin::{station::Station, team::Team}, admin::{station::Station, team::Team},
er, err, er,
models::rating::{Rating, TeamsAtStationLocation}, models::rating::{Rating, TeamsAtStationLocation},
partials::page, partials::page,
suc, succ, AppState, suc, AppState,
}; };
use axum::{ use axum::{
extract::State, extract::State,
@ -74,10 +74,7 @@ async fn view(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> { ) -> Result<Markup, impl IntoResponse> {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_open_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
);
return Err(Redirect::to("/admin/station")); return Err(Redirect::to("/admin/station"));
}; };
@ -88,14 +85,16 @@ async fn view(
let content = html! { let content = html! {
h1 { h1 {
a href="/admin/station" { "↩️" } a href="/admin/station" { "↩️" }
"Station " (station.name) (t!("station"))
" "
(station.name)
} }
article { article {
details { details {
summary { "Stationsname bearbeiten ✏️" } summary { (t!("station_name_edit"))" ✏️" }
form action=(format!("/admin/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=(t!("save"));
} }
} }
@ -103,7 +102,7 @@ async fn view(
table { table {
tbody { tbody {
tr { tr {
th scope="row" { "Notizen" }; th scope="row" { (t!("notes")) };
td { td {
@match station.notes { @match station.notes {
Some(ref notes) => { Some(ref notes) => {
@ -112,7 +111,7 @@ async fn view(
summary { "✏️" } summary { "✏️" }
form action=(format!("/admin/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=(t!("save"));
} }
} }
}, },
@ -120,7 +119,7 @@ async fn view(
summary { "Neue Notiz hinzufügen" } summary { "Neue Notiz hinzufügen" }
form action=(format!("/admin/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=(t!("save"));
} }
} }
} }
@ -129,20 +128,20 @@ async fn view(
@if !station.crewless() { @if !station.crewless() {
tr { tr {
th scope="row" { th scope="row" {
"Stations-Link" (t!("station_link"))
article class="warning" { article class="warning" {
(format!("Diesen Link nur Betreuern der Station {} geben! Mit diesem Link erhält man die Berechtigung, Teams zu bewerten.", station.name)) (t!("station_url_info", station=station.name))
} }
}; };
td { td {
a href=(format!("/s/{}/{}", station.id, station.pw)) { a href=(format!("/s/{}/{}", station.id, station.pw)) {
"Login-Link" (t!("login_link"))
} }
} }
} }
} }
tr { tr {
th scope="row" { "Anzahl Stationsbetreuer" th scope="row" { (t!("amount_crew"))
article class="warning" { article class="warning" {
(t!("info_crewless_station")) (t!("info_crewless_station"))
} }
@ -156,11 +155,11 @@ async fn view(
summary { "✏️" } summary { "✏️" }
form action=(format!("/admin/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=(t!("save"));
} }
button class="error" { button class="error" {
a href=(format!("/admin/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=(t!("not_sure_about_amount_crew")) {
"?" "?"
} }
} }
@ -170,11 +169,11 @@ async fn view(
} }
@if !station.crewless() { @if !station.crewless() {
tr { tr {
th scope="row" { "Letzter Zugriff eines Stationsbetreuers" }; th scope="row" { (t!("last_access_crew")) };
td { td {
@match station.local_last_login() { @match station.local_last_login() {
Some(last_login) => (last_login), Some(last_login) => (last_login),
None => "noch nicht eingeloggt :-(", None => (t!("not_loggedin_yet")),
} }
} }
} }
@ -183,26 +182,26 @@ async fn view(
} }
@if !ratings.is_empty() { @if !ratings.is_empty() {
h2 { "Bewertungen" } h2 { (t!("ratings")) }
div class="overflow-auto" { div class="overflow-auto" {
table { table {
thead { thead {
tr { tr {
th { "Team" } th { (t!("team")) }
th { "Punkte" } th { (t!("points")) }
th { "Notizen" } th { (t!("notes")) }
th { th {
em data-placement="bottom" data-tooltip="Angekommen" { em data-placement="bottom" data-tooltip=(t!("arrived")) {
"👋" "👋"
} }
} }
th { th {
em data-placement="bottom" data-tooltip="Begonnen" { em data-placement="bottom" data-tooltip=(t!("started")) {
"🎬" "🎬"
} }
} }
th { th {
em data-placement="bottom" data-tooltip="Gegangen" { em data-placement="bottom" data-tooltip=(t!("left")) {
"🚶‍♂️" "🚶‍♂️"
} }
} }
@ -244,9 +243,7 @@ async fn view(
} }
a href=(format!("/admin/station/{}/quick", station.id)){ a href=(format!("/admin/station/{}/quick", station.id)){
button { button {
"Bewertungen für Station " (t!("enter_ratings_for_station", station=station.name))
(station.name)
" eingeben"
} }
} }
@ -254,23 +251,23 @@ async fn view(
@if let (Some(_), Some(_)) = (station.lat, station.lng) { @if let (Some(_), Some(_)) = (station.lat, station.lng) {
article { article {
"Um einen neuen Standort zu wählen, auf einen Punkt in der Karte klicken" (t!("click_on_map_for_new_location"))
} }
} @else{ } @else{
article { article {
"Um einen Standort zu wählen, auf einen Punkt in der Karte klicken" (t!("click_on_map_for_location"))
} }
} }
@if station.lat.is_some() && station.lng.is_some() { @if station.lat.is_some() && station.lng.is_some() {
a href=(format!("/admin/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=(format!("return confirm('{}');", t!("confirm_delete_location"))){
"Standort löschen" (t!("delete_location"))
} }
} }
form action=(format!("/admin/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=(t!("save_new_location")) style="display: None;" id="location-submit";
} }
div id="map" style="height: 500px" {} div id="map" style="height: 500px" {}
script { script {
@ -338,21 +335,15 @@ async fn update_name(
Form(form): Form<UpdateNameForm>, Form(form): Form<UpdateNameForm>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_name(&db, &form.name).await; station.update_name(&db, &form.name).await;
succ!( suc!(
session, session,
"Station '{}' heißt ab sofort '{}'.", t!("station_new_name", old = station.name, new = form.name)
station.name,
form.name
); );
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
@ -369,21 +360,13 @@ async fn update_notes(
Form(form): Form<UpdateNotesForm>, Form(form): Form<UpdateNotesForm>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_notes(&db, &form.notes).await; station.update_notes(&db, &form.notes).await;
succ!( suc!(session, t!("station_new_notes", station = station.name));
session,
"Notizen für die Station '{}' wurden erfolgreich bearbeitet!",
station.name
);
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
@ -399,20 +382,15 @@ async fn update_amount_people(
Form(form): Form<UpdateAmountPeopleForm>, Form(form): Form<UpdateAmountPeopleForm>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_amount_people(&db, form.amount_people).await; station.update_amount_people(&db, form.amount_people).await;
succ!( suc!(
session, session,
"Anzahl an Betreuer für die Station '{}' wurden erfolgreich bearbeitet!", t!("station_new_crew_amount", station = station.name)
station.name
); );
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
@ -424,20 +402,15 @@ async fn update_amount_people_reset(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_amount_people_reset(&db).await; station.update_amount_people_reset(&db).await;
succ!( suc!(
session, session,
"Anzahl an Betreuer für die Station '{}' wurden erfolgreich bearbeitet!", t!("station_new_crew_amount", station = station.name)
station.name
); );
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
@ -449,11 +422,7 @@ async fn quick(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> { ) -> Result<Markup, impl IntoResponse> {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
);
return Err(Redirect::to("/admin/station")); return Err(Redirect::to("/admin/station"));
}; };
@ -463,14 +432,16 @@ async fn quick(
let content = html! { let content = html! {
h1 { h1 {
a href=(format!("/admin/station/{}", station.id)) { "↩️" } a href=(format!("/admin/station/{}", station.id)) { "↩️" }
"Bewertungen Station " (station.name) (t!("ratings"))
" "
(station.name)
} }
form action=(format!("/admin/station/{}/quick", station.id)) method="post" { form action=(format!("/admin/station/{}/quick", station.id)) method="post" {
table { table {
thead { thead {
tr { tr {
th { "Team" } th { (t!("team")) }
th { "Punkte" } th { (t!("points")) }
} }
} }
tbody { tbody {
@ -485,11 +456,11 @@ async fn quick(
@if let Some(rating) = Rating::find_by_team_and_station(&db, team, &station).await { @if let Some(rating) = Rating::find_by_team_and_station(&db, team, &station).await {
a href=(format!("/s/{}/{}", station.id, station.pw)){ a href=(format!("/s/{}/{}", station.id, station.pw)){
@if let Some(points) = rating.points { @if let Some(points) = rating.points {
em data-tooltip="Schon eingetragen" { em data-tooltip=(t!("already_entered")) {
(points) (points)
} }
} @else { } @else {
em data-tooltip="Team gerade bei Station" { "?" } em data-tooltip=(t!("team_currently_at_station")) { "?" }
} }
} }
} @else { } @else {
@ -501,7 +472,7 @@ async fn quick(
} }
} }
} }
input type="submit" value="Speichern"; input type="submit" value=(t!("save"));
} }
}; };
Ok(page(content, session, true).await) Ok(page(content, session, true).await)
@ -519,11 +490,7 @@ async fn quick_post(
Form(form): Form<QuickUpdate>, Form(form): Form<QuickUpdate>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
@ -554,10 +521,13 @@ async fn quick_post(
.await .await
.is_some() .is_some()
{ {
ret.push_str(&format!( let msg: String = t!(
"Skipped rating for team {} because this team already has a rating for station {}", "error_rating_team_already_rated",
team.name, station.name team = team.name,
)); station = station.name
)
.into();
ret.push_str(&msg);
continue; continue;
} }
@ -571,12 +541,9 @@ async fn quick_post(
} }
if amount_succ == 0 { if amount_succ == 0 {
succ!( suc!(session, t!("funny_you_entered_no_rating"));
session,
"Du hast keine Bewertungen eingegeben... Spaßvogel!"
);
} else { } else {
succ!(session, "Erfolgreich {amount_succ} Bewertungen eingetragen"); suc!(session, t!("entered_n_ratings", amount = amount_succ));
} }
Redirect::to(&format!("/admin/station/{id}/quick")) Redirect::to(&format!("/admin/station/{id}/quick"))
@ -594,21 +561,13 @@ async fn update_location(
Form(form): Form<UpdateLocationForm>, Form(form): Form<UpdateLocationForm>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_location(&db, form.lat, form.lng).await; station.update_location(&db, form.lat, form.lng).await;
succ!( suc!(session, t!("location_changed", station = station.name));
session,
"Standort für die Station '{}' wurden erfolgreich bearbeitet!",
station.name
);
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
@ -619,21 +578,13 @@ async fn update_location_clear(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else { let Some(station) = Station::find_by_id(&db, id).await else {
err!( er!(session, t!("station_edit_err_nonexisting", id = id));
session,
"Station mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/station"); return Redirect::to("/admin/station");
}; };
station.update_location_clear(&db).await; station.update_location_clear(&db).await;
succ!( suc!(session, t!("location_changed", station = station.name));
session,
"Standort für die Station '{}' wurden erfolgreich gelöscht!",
station.name
);
Redirect::to(&format!("/admin/station/{id}")) Redirect::to(&format!("/admin/station/{id}"))
} }
@ -656,8 +607,8 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
table { table {
thead { thead {
tr { tr {
th { "Station" } th { (t!("station")) }
th { "Fortschritt" } th { (t!("progress")) }
th { "" } th { "" }
} }
} }
@ -667,7 +618,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
tr { tr {
td { td {
@if station.ready { @if station.ready {
em data-tooltip="Station bereit!" { em data-tooltip=(t!("station_ready")) {
small { "🟢 " } small { "🟢 " }
} }
} }
@ -682,13 +633,13 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
(station.name) (station.name)
} }
@if station.crewless() { @if station.crewless() {
em data-tooltip="Station ohne Stationsbetreuer" { em data-tooltip=(t!("crewless_station")) {
small { "🤖" } small { "🤖" }
} }
} }
} }
td { 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())) { em data-tooltip=(t!("station_team_progress", arrived=status.total_teams-status.not_yet_here.len() as i64, total=status.total_teams, waiting= status.waiting.len(), active=status.doing.len() )) {
progress value=(status.total_teams-status.not_yet_here.len() as i64) max=(status.total_teams) {} progress value=(status.total_teams-status.not_yet_here.len() as i64) max=(status.total_teams) {}
} }

View File

@ -1,11 +1,11 @@
use crate::{AppState, auth::User, err, partials::page, succ}; use crate::{auth::User, er, partials::page, suc, AppState};
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::{Markup, html}; use maud::{html, Markup};
use serde::Deserialize; use serde::Deserialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use std::sync::Arc; use std::sync::Arc;
@ -23,15 +23,11 @@ async fn create(
) -> impl IntoResponse { ) -> impl IntoResponse {
let id = match User::create(&db, &form.name).await { let id = match User::create(&db, &form.name).await {
Ok(id) => { Ok(id) => {
succ!(session, "User '{}' erfolgreich erstellt!", form.name); suc!(session, t!("user_created", name = form.name));
id id
} }
Err(e) => { Err(e) => {
err!( er!(session, t!("user_create_err", err = e));
session,
"User '{}' konnte _NICHT_ erstellt werden: {e:?}",
e
);
return Redirect::to("/admin/user"); return Redirect::to("/admin/user");
} }
@ -46,20 +42,20 @@ async fn delete(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(user) = User::find_by_id(&db, id).await else { let Some(user) = User::find_by_id(&db, id).await else {
err!( er!(session, t!("nonexisting_user", id = id));
session,
"User mit ID {id} konnte nicht gelöscht werden, da sie nicht existiert"
);
return Redirect::to("/admin/user"); return Redirect::to("/admin/user");
}; };
match user.delete(&db).await { match user.delete(&db).await {
Ok(()) => succ!(session, "User '{}' erfolgreich gelöscht!", user.name), Ok(()) => suc!(session, t!("user_deleted", user = user.name)),
Err(e) => err!( Err(e) => er!(
session, session,
"User '{}' kann nicht gelöscht werden, da er/sie bereits verwendet wird. ({e})", t!(
user.name "user_delete_error_already_in_use",
user = user.name,
err = e
)
), ),
} }
@ -72,11 +68,7 @@ async fn view(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> { ) -> Result<Markup, impl IntoResponse> {
let Some(user) = User::find_by_id(&db, id).await else { let Some(user) = User::find_by_id(&db, id).await else {
err!( er!(session, t!("nonexisting_user", id = id));
session,
"User mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
);
return Err(Redirect::to("/admin/user")); return Err(Redirect::to("/admin/user"));
}; };
@ -84,14 +76,19 @@ async fn view(
let content = html! { let content = html! {
h1 { h1 {
a href="/admin/user" { "↩️" } a href="/admin/user" { "↩️" }
"User " (user.name) (t!("admin"))
" "
(user.name)
} }
article { article {
details { details {
summary { "Name bearbeiten ✏️" } summary {
(t!("edit_username"))
" ✏️"
}
form action=(format!("/admin/user/{}/name", user.id)) method="post" { form action=(format!("/admin/user/{}/name", user.id)) method="post" {
input type="text" name="name" value=(user.name) required; input type="text" name="name" value=(user.name) required;
input type="submit" value="Speichern"; input type="submit" value=(t!("save"));
} }
} }
@ -99,22 +96,22 @@ async fn view(
table { table {
tbody { tbody {
tr { tr {
th scope="row" { "Name" }; th scope="row" { (t!("name")) };
td { td {
(user.name) (user.name)
} }
} }
tr { tr {
th scope="row" { "Passwort" }; th scope="row" { (t!("pw")) };
td { td {
@if let Some(code) = user.require_new_password_code { @if let Some(code) = user.require_new_password_code {
a href=(format!("/user/{}/set-pw/{}", user.id, code)){ a href=(format!("/user/{}/set-pw/{}", user.id, code)){
"Login-Link" (t!("login_link"))
} }
} @else { } @else {
a href=(format!("/admin/user/{}/new-pw", user.id)) a href=(format!("/admin/user/{}/new-pw", user.id))
onclick="return confirm('Bist du sicher, dass du einen neuen Passwort-Link generieren willst? Mit dem alten Passwort kann man sich dann nicht mehr einloggen.');" { onclick=(format!("return confirm('{}');", t!("confirm_new_admin_link"))) {
"Passwort vergessen: neuen Loginlink generieren" (t!("new_admin_link"))
} }
} }
} }
@ -136,21 +133,15 @@ async fn update_name(
Form(form): Form<UpdateNameForm>, Form(form): Form<UpdateNameForm>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(user) = User::find_by_id(&db, id).await else { let Some(user) = User::find_by_id(&db, id).await else {
err!( er!(session, t!("nonexisting_user", id = id));
session,
"User mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/user"); return Redirect::to("/admin/user");
}; };
user.update_name(&db, &form.name).await; user.update_name(&db, &form.name).await;
succ!( suc!(
session, session,
"User '{}' heißt ab sofort '{}'.", t!("new_user_name", old = user.name, new = form.name)
user.name,
form.name
); );
Redirect::to(&format!("/admin/user/{id}")) Redirect::to(&format!("/admin/user/{id}"))
@ -162,21 +153,13 @@ async fn new_pw(
axum::extract::Path(id): axum::extract::Path<i64>, axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let Some(user) = User::find_by_id(&db, id).await else { let Some(user) = User::find_by_id(&db, id).await else {
err!( er!(session, t!("nonexisting_user", id = id));
session,
"User mit ID {id} konnte nicht bearbeitet werden, da sie nicht existiert"
);
return Redirect::to("/admin/user"); return Redirect::to("/admin/user");
}; };
user.new_pw(&db).await; user.new_pw(&db).await;
succ!( suc!(session, t!("succ_new_admin_link", user = user.name));
session,
"Neuer Loginlink für User '{}' wurde generiert.",
user.name
);
Redirect::to(&format!("/admin/user/{id}")) Redirect::to(&format!("/admin/user/{id}"))
} }
@ -187,7 +170,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let content = html! { let content = html! {
h1 { h1 {
a href="/admin" { "↩️" } a href="/admin" { "↩️" }
"User" (t!("admins"))
} }
article { article {
em { "Admins" } em { "Admins" }
@ -202,11 +185,11 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
"." "."
} }
article { article {
h2 { "Neuer User" } h2 { (t!("new_admin")) }
form action="/admin/user" method="post" { form action="/admin/user" method="post" {
fieldset role="group" { fieldset role="group" {
input type="text" name="name" placeholder="Name" required; input type="text" name="name" placeholder=(t!("name")) required;
input type="submit" value="Neuer User"; input type="submit" value=(t!("new_admin"));
} }
} }
} }
@ -217,7 +200,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
(user.name) (user.name)
} }
a href=(format!("/admin/user/{}/delete", user.id)) a href=(format!("/admin/user/{}/delete", user.id))
onclick="return confirm('Bist du sicher, dass der User gelöscht werden soll? Das kann _NICHT_ mehr rückgängig gemacht werden.');" { onclick=(format!("return confirm('{}');", t!("confirm_admin_delete"))) {
"🗑️" "🗑️"
} }
} }