add route+route_station functionality; add warnings if stuff is mmissing Fixes #5
This commit is contained in:
parent
3965cc4c09
commit
5eb1df221b
@ -7,7 +7,9 @@
|
|||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
- [x] Station
|
- [x] Station
|
||||||
- [ ] Route
|
- [x] Route
|
||||||
|
- [x] Route_station
|
||||||
|
- [ ] Group
|
||||||
|
|
||||||
## Fancy features
|
## Fancy features
|
||||||
- see when a group starts going to your direction
|
- see when a group starts going to your direction
|
||||||
|
@ -15,9 +15,9 @@ CREATE TABLE route (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE route_station (
|
CREATE TABLE route_station (
|
||||||
route_id INTEGER,
|
route_id INTEGER NOT NULL,
|
||||||
station_id INTEGER,
|
station_id INTEGER NOT NULL,
|
||||||
pos INTEGER,
|
pos INTEGER NOT NULL,
|
||||||
PRIMARY KEY (route_id, station_id),
|
PRIMARY KEY (route_id, station_id),
|
||||||
FOREIGN KEY (route_id) REFERENCES route(id),
|
FOREIGN KEY (route_id) REFERENCES route(id),
|
||||||
FOREIGN KEY (station_id) REFERENCES station(id)
|
FOREIGN KEY (station_id) REFERENCES station(id)
|
||||||
|
@ -7,8 +7,8 @@ use tokio::net::TcpListener;
|
|||||||
use tower_sessions::{MemoryStore, Session, SessionManagerLayer};
|
use tower_sessions::{MemoryStore, Session, SessionManagerLayer};
|
||||||
|
|
||||||
mod partials;
|
mod partials;
|
||||||
mod route;
|
pub(crate) mod route;
|
||||||
mod station;
|
pub(crate) mod station; // TODO: find nicer name for this 'associative table'
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! err {
|
macro_rules! err {
|
||||||
|
320
src/route.rs
320
src/route.rs
@ -1,19 +1,19 @@
|
|||||||
use crate::{err, page, succ};
|
use crate::{err, page, station::Station, succ};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::State,
|
extract::State,
|
||||||
response::{IntoResponse, Redirect},
|
response::{IntoResponse, Redirect},
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Form, Router,
|
Form, Router,
|
||||||
};
|
};
|
||||||
use maud::{html, Markup};
|
use maud::{html, Markup, PreEscaped};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{FromRow, SqlitePool};
|
use sqlx::{FromRow, SqlitePool};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_sessions::Session;
|
use tower_sessions::Session;
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
||||||
struct Route {
|
pub(crate) struct Route {
|
||||||
id: i64,
|
pub(crate) id: i64,
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +47,123 @@ impl Route {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn add_station(&self, db: &SqlitePool, station: &Station) -> Result<(), String> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
INSERT INTO route_station (route_id, station_id, pos)
|
||||||
|
VALUES (?, ?, (
|
||||||
|
SELECT COALESCE(MAX(pos), 0) + 2
|
||||||
|
FROM route_station
|
||||||
|
WHERE route_id = ?
|
||||||
|
))
|
||||||
|
"#,
|
||||||
|
self.id,
|
||||||
|
station.id,
|
||||||
|
self.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_station(&self, db: &SqlitePool, station: &Station) -> bool {
|
||||||
|
let result = sqlx::query!(
|
||||||
|
"DELETE FROM route_station WHERE route_id = ? AND station_id = ?",
|
||||||
|
self.id,
|
||||||
|
station.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
result.rows_affected() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn move_station_higher(&self, db: &SqlitePool, station: &Station) -> bool {
|
||||||
|
let result = sqlx::query!(
|
||||||
|
"UPDATE route_station SET pos = pos-3 WHERE route_id = ? AND station_id = ?",
|
||||||
|
self.id,
|
||||||
|
station.id
|
||||||
|
)
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if result.rows_affected() == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
&format!(
|
||||||
|
"
|
||||||
|
-- Step 1: Create a temporary table with new rank values
|
||||||
|
CREATE TEMP TABLE IF NOT EXISTS temp_pos AS
|
||||||
|
SELECT
|
||||||
|
route_id,
|
||||||
|
station_id,
|
||||||
|
ROW_NUMBER() OVER (ORDER BY pos ASC) AS new_rank
|
||||||
|
FROM
|
||||||
|
route_station WHERE route_id = {};
|
||||||
|
|
||||||
|
-- Step 2: Update the original table
|
||||||
|
UPDATE route_station
|
||||||
|
SET pos = (SELECT 2*new_rank FROM temp_pos WHERE temp_pos.route_id = route_station.route_id AND temp_pos.station_id = route_station.station_id) WHERE route_id = {};
|
||||||
|
|
||||||
|
-- Step 3: Drop the temporary table (no longer needed)
|
||||||
|
DROP TABLE temp_pos;",
|
||||||
|
self.id,
|
||||||
|
self.id,
|
||||||
|
))
|
||||||
|
.execute(db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn delete(&self, db: &SqlitePool) {
|
async fn delete(&self, db: &SqlitePool) {
|
||||||
sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
|
sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn stations(&self, db: &SqlitePool) -> Vec<Station> {
|
||||||
|
sqlx::query_as::<_, Station>(
|
||||||
|
r#"
|
||||||
|
SELECT s.id, s.name, s.notes, s.amount_people, s.last_login, s.pw, s.lat, s.lng
|
||||||
|
FROM station s
|
||||||
|
JOIN route_station rs ON s.id = rs.station_id
|
||||||
|
WHERE rs.route_id = ?
|
||||||
|
ORDER BY rs.pos
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(self.id)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn stations_not_in_route(&self, db: &SqlitePool) -> Vec<Station> {
|
||||||
|
sqlx::query_as::<_, Station>(
|
||||||
|
r#"
|
||||||
|
SELECT id, name, notes, amount_people, last_login, pw, lat, lng
|
||||||
|
FROM station
|
||||||
|
WHERE id NOT IN (
|
||||||
|
SELECT station_id
|
||||||
|
FROM route_station
|
||||||
|
WHERE route_id = ?
|
||||||
|
)
|
||||||
|
ORDER BY name
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(self.id)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
||||||
@ -63,9 +174,20 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
|||||||
a href="/" { "↩️" }
|
a href="/" { "↩️" }
|
||||||
"Routen"
|
"Routen"
|
||||||
}
|
}
|
||||||
|
article {
|
||||||
|
em { "Routen " }
|
||||||
|
"definieren welche "
|
||||||
|
a href="/station" { "Stationen" }
|
||||||
|
" 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 {
|
||||||
@for route in &routes{
|
@for route in &routes{
|
||||||
li {
|
li {
|
||||||
|
@if route.stations(&db).await.is_empty() {
|
||||||
|
em data-tooltip="Keine Stationen zugeteilt" {
|
||||||
|
"⚠️"
|
||||||
|
}
|
||||||
|
}
|
||||||
a href=(format!("/route/{}", route.id)){
|
a href=(format!("/route/{}", route.id)){
|
||||||
(route.name)
|
(route.name)
|
||||||
}
|
}
|
||||||
@ -76,6 +198,11 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@if routes.is_empty() {
|
||||||
|
article class="warning" {
|
||||||
|
"Es gibt noch keine Routen. Das kannst du hier ändern ⤵️"
|
||||||
|
}
|
||||||
|
}
|
||||||
h2 { "Neue Route" }
|
h2 { "Neue Route" }
|
||||||
form action="/route" method="post" {
|
form action="/route" method="post" {
|
||||||
fieldset role="group" {
|
fieldset role="group" {
|
||||||
@ -144,6 +271,9 @@ async fn view(
|
|||||||
return Err(Redirect::to("/route"));
|
return Err(Redirect::to("/route"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let cur_stations = route.stations(&db).await;
|
||||||
|
let stations_not_in_route = route.stations_not_in_route(&db).await;
|
||||||
|
|
||||||
let content = html! {
|
let content = html! {
|
||||||
h1 {
|
h1 {
|
||||||
a href="/route" { "↩️" }
|
a href="/route" { "↩️" }
|
||||||
@ -158,6 +288,52 @@ async fn view(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
h2 { "Stationen" }
|
||||||
|
@if cur_stations.is_empty() {
|
||||||
|
@if stations_not_in_route.is_empty() {
|
||||||
|
article class="error" {
|
||||||
|
(PreEscaped("Bevor du einer Route Stationen zuweisen kannst, musst du die Stationen erstellen → "))
|
||||||
|
a role="button" href="/station" {
|
||||||
|
"Station erstellen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
article class="warning" {
|
||||||
|
"Diese Route hat noch keine Stationen zugewiesen. Das kannst du hier ändern ⤵️"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
ol {
|
||||||
|
@for (idx, station) in cur_stations.into_iter().enumerate() {
|
||||||
|
li {
|
||||||
|
(station.name)
|
||||||
|
@if idx > 0 {
|
||||||
|
a href=(format!("/route/{}/move-station-higher/{}", route.id, station.id)){
|
||||||
|
em data-tooltip=(format!("{} nach vor reihen", station.name)) {
|
||||||
|
"⬆️"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a href=(format!("/route/{}/delete-station/{}", route.id, station.id))
|
||||||
|
onclick="return confirm('Bist du sicher, dass die Station von der Route entfernt werden soll?');" {
|
||||||
|
"🗑️"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@if !stations_not_in_route.is_empty(){
|
||||||
|
form action=(format!("/route/{}/add-station", route.id)) method="post" {
|
||||||
|
select name="station" aria-label="Hinzuzufügende Station auswählen" required {
|
||||||
|
@for station in &stations_not_in_route {
|
||||||
|
option value=(station.id) {
|
||||||
|
(station.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input type="submit" value="Hinzufügen";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(page(content, session, false).await)
|
Ok(page(content, session, false).await)
|
||||||
}
|
}
|
||||||
@ -193,6 +369,133 @@ async fn update_name(
|
|||||||
Redirect::to(&format!("/route/{id}"))
|
Redirect::to(&format!("/route/{id}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct AddStationForm {
|
||||||
|
station: i64,
|
||||||
|
}
|
||||||
|
async fn add_station(
|
||||||
|
State(db): State<Arc<SqlitePool>>,
|
||||||
|
session: Session,
|
||||||
|
axum::extract::Path(id): axum::extract::Path<i64>,
|
||||||
|
Form(form): Form<AddStationForm>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let Some(route) = Route::find_by_id(&db, id).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Route mit ID {id} konnte nicht geöffnet werden, da sie nicht existiert"
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to("/route");
|
||||||
|
};
|
||||||
|
let Some(station) = Station::find_by_id(&db, form.station).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Station mit ID {id} konnte nicht hinzugefügt werden, da sie nicht existiert"
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to(&format!("/route/{id}"));
|
||||||
|
};
|
||||||
|
|
||||||
|
match route.add_station(&db, &station).await {
|
||||||
|
Ok(_) => succ!(
|
||||||
|
session,
|
||||||
|
"Station {} wurde der Route {} hinzugefügt",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
),
|
||||||
|
Err(e) => err!(
|
||||||
|
session,
|
||||||
|
"Station {} kann nur 1x der Route {} hinzugefügt werden. ({e})",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
Redirect::to(&format!("/route/{id}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_station(
|
||||||
|
State(db): State<Arc<SqlitePool>>,
|
||||||
|
session: Session,
|
||||||
|
axum::extract::Path((route_id, station_id)): axum::extract::Path<(i64, i64)>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let Some(route) = Route::find_by_id(&db, route_id).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Konnte keine Station von Route mit ID {route_id} entfernen, da diese Route nicht existiert."
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to("/route");
|
||||||
|
};
|
||||||
|
let Some(station) = Station::find_by_id(&db, station_id).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Konnte Station mit der ID {station_id} nicht von der Route {} entfernen, da die Station nicht existiert.", route.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to(&format!("/route/{route_id}"));
|
||||||
|
};
|
||||||
|
|
||||||
|
if route.delete_station(&db, &station).await {
|
||||||
|
succ!(
|
||||||
|
session,
|
||||||
|
"Station '{}' wurde von der Route '{}' gelöscht",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Station '{}' konnte nicht von der Route '{}' gelöscht werden, da diese nicht auf dieser Route liegt",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Redirect::to(&format!("/route/{route_id}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn move_station_higher(
|
||||||
|
State(db): State<Arc<SqlitePool>>,
|
||||||
|
session: Session,
|
||||||
|
axum::extract::Path((route_id, station_id)): axum::extract::Path<(i64, i64)>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let Some(route) = Route::find_by_id(&db, route_id).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Konnte keine Station von Route mit ID {route_id} verschieben, da diese Route nicht existiert."
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to("/route");
|
||||||
|
};
|
||||||
|
let Some(station) = Station::find_by_id(&db, station_id).await else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Konnte Station mit der ID {station_id} nicht in der Route {} verschieben, da die Station nicht existiert.", route.name
|
||||||
|
);
|
||||||
|
|
||||||
|
return Redirect::to(&format!("/route/{route_id}"));
|
||||||
|
};
|
||||||
|
|
||||||
|
if route.move_station_higher(&db, &station).await {
|
||||||
|
succ!(
|
||||||
|
session,
|
||||||
|
"Station '{}' wurde in der Route '{}' erfolgreich vorgereiht",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
err!(
|
||||||
|
session,
|
||||||
|
"Station '{}' konnte in der Route '{}' nicht vorgereiht werden, da diese nicht auf dieser Route liegt",
|
||||||
|
station.name,
|
||||||
|
route.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Redirect::to(&format!("/route/{route_id}"))
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn routes() -> Router<Arc<SqlitePool>> {
|
pub(super) fn routes() -> Router<Arc<SqlitePool>> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
@ -200,4 +503,13 @@ pub(super) fn routes() -> Router<Arc<SqlitePool>> {
|
|||||||
.route("/{id}/delete", get(delete))
|
.route("/{id}/delete", get(delete))
|
||||||
.route("/{id}", get(view))
|
.route("/{id}", get(view))
|
||||||
.route("/{id}/name", post(update_name))
|
.route("/{id}/name", post(update_name))
|
||||||
|
.route("/{id}/add-station", post(add_station))
|
||||||
|
.route(
|
||||||
|
"/{route_id}/delete-station/{station_id}",
|
||||||
|
get(delete_station),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/{route_id}/move-station-higher/{station_id}",
|
||||||
|
get(move_station_higher),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::route::Route;
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -7,9 +8,9 @@ use std::sync::Arc;
|
|||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
||||||
struct Station {
|
pub(crate) struct Station {
|
||||||
id: i64,
|
pub(crate) id: i64,
|
||||||
name: String,
|
pub(crate) name: String,
|
||||||
notes: Option<String>,
|
notes: Option<String>,
|
||||||
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?)
|
||||||
@ -19,7 +20,7 @@ struct Station {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Station {
|
impl Station {
|
||||||
async fn all(db: &SqlitePool) -> Vec<Self> {
|
pub(crate) async fn all(db: &SqlitePool) -> Vec<Self> {
|
||||||
sqlx::query_as::<_, Self>(
|
sqlx::query_as::<_, Self>(
|
||||||
"SELECT id, name, notes, amount_people, last_login, pw, lat, lng FROM station;",
|
"SELECT id, name, notes, amount_people, last_login, pw, lat, lng FROM station;",
|
||||||
)
|
)
|
||||||
@ -103,11 +104,28 @@ impl Station {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(&self, db: &SqlitePool) {
|
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
|
||||||
sqlx::query!("DELETE FROM station WHERE id = ?", self.id)
|
sqlx::query!("DELETE FROM station WHERE id = ?", self.id)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.map_err(|e| e.to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn routes(&self, db: &SqlitePool) -> Vec<Route> {
|
||||||
|
sqlx::query_as::<_, Route>(
|
||||||
|
r#"
|
||||||
|
SELECT r.id, r.name
|
||||||
|
FROM route r
|
||||||
|
JOIN route_station rs ON r.id = rs.route_id
|
||||||
|
WHERE rs.station_id = ?
|
||||||
|
ORDER BY rs.pos
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(self.id)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +47,14 @@ async fn delete(
|
|||||||
return Redirect::to("/station");
|
return Redirect::to("/station");
|
||||||
};
|
};
|
||||||
|
|
||||||
station.delete(&db).await;
|
match station.delete(&db).await {
|
||||||
|
Ok(_) => succ!(session, "Station '{}' erfolgreich gelöscht!", station.name),
|
||||||
succ!(session, "Station '{}' erfolgreich gelöscht!", station.name);
|
Err(e) => err!(
|
||||||
|
session,
|
||||||
|
"Station '{}' kann nicht gelöscht werden, da sie bereits verwendet wird. ({e})",
|
||||||
|
station.name
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
Redirect::to("/station")
|
Redirect::to("/station")
|
||||||
}
|
}
|
||||||
@ -410,9 +415,18 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
|||||||
a href="/" { "↩️" }
|
a href="/" { "↩️" }
|
||||||
"Stationen"
|
"Stationen"
|
||||||
}
|
}
|
||||||
|
article {
|
||||||
|
em { "Stationen " }
|
||||||
|
"sind festgelegte Orte mit spezifischen Aufgaben."
|
||||||
|
}
|
||||||
ol {
|
ol {
|
||||||
@for station in &stations {
|
@for station in &stations {
|
||||||
li {
|
li {
|
||||||
|
@if station.routes(&db).await.is_empty() {
|
||||||
|
em data-tooltip="Noch keiner Route zugeordnet" {
|
||||||
|
"⚠️"
|
||||||
|
}
|
||||||
|
}
|
||||||
a href=(format!("/station/{}", station.id)){
|
a href=(format!("/station/{}", station.id)){
|
||||||
(station.name)
|
(station.name)
|
||||||
}
|
}
|
||||||
@ -423,6 +437,11 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@if stations.is_empty() {
|
||||||
|
article class="warning" {
|
||||||
|
"Es gibt noch keine Stationen. Das kannst du hier ändern ⤵️"
|
||||||
|
}
|
||||||
|
}
|
||||||
h2 { "Neue Station" }
|
h2 { "Neue Station" }
|
||||||
form action="/station" method="post" {
|
form action="/station" method="post" {
|
||||||
fieldset role="group" {
|
fieldset role="group" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user