diff --git a/src/lib.rs b/src/lib.rs
index bfb6c5c..c52eefd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
-use axum::{Router, body::Body, response::Response, routing::get};
-use maud::{Markup, html};
+use axum::{body::Body, response::Response, routing::get, Router};
+use maud::{html, Markup};
use partials::page;
use sqlx::SqlitePool;
use std::sync::Arc;
@@ -79,11 +79,24 @@ async fn serve_marker_png() -> Response
{
async fn index(session: Session) -> Markup {
let content = html! {
h1 { "Stationslauf-App" }
- a role="button" href="/station" {
- "Stationen"
+ nav {
+ ul {
+ li {
+ a role="button" href="/station" {
+ "Stationen"
+ }
+ }
+ li {
+ a role="button" href="/route" {
+ "Routen"
+ }
+ }
+ li {
+ a role="button" href="/group" {
+ "Gruppen"
+ }
+ }
}
- a role="button" href="/route" {
- "Routen"
}
};
page(content, session, false).await
diff --git a/src/route/mod.rs b/src/route/mod.rs
new file mode 100644
index 0000000..4970e4c
--- /dev/null
+++ b/src/route/mod.rs
@@ -0,0 +1,167 @@
+use crate::station::Station;
+use axum::Router;
+use serde::{Deserialize, Serialize};
+use sqlx::{FromRow, SqlitePool};
+use std::sync::Arc;
+
+mod web;
+
+#[derive(FromRow, Debug, Serialize, Deserialize)]
+pub(crate) struct Route {
+ pub(crate) id: i64,
+ name: String,
+}
+
+impl Route {
+ async fn all(db: &SqlitePool) -> Vec {
+ sqlx::query_as::<_, Self>("SELECT id, name FROM route;")
+ .fetch_all(db)
+ .await
+ .unwrap()
+ }
+
+ pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option {
+ sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id)
+ .fetch_one(db)
+ .await
+ .ok()
+ }
+
+ async fn create(db: &SqlitePool, name: &str) -> Result<(), String> {
+ sqlx::query!("INSERT INTO route(name) VALUES (?)", name)
+ .execute(db)
+ .await
+ .map_err(|e| e.to_string())?;
+ Ok(())
+ }
+
+ async fn update_name(&self, db: &SqlitePool, name: &str) {
+ sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id)
+ .execute(db)
+ .await
+ .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) -> Result<(), String> {
+ sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
+ .execute(db)
+ .await
+ .map_err(|e| e.to_string())?;
+ Ok(())
+ }
+
+ async fn stations(&self, db: &SqlitePool) -> Vec {
+ sqlx::query_as::<_, Station>(
+ "
+ 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 {
+ sqlx::query_as::<_, Station>(
+ "
+ 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()
+ }
+}
+
+pub(super) fn routes() -> Router> {
+ web::routes()
+}
diff --git a/src/route.rs b/src/route/web.rs
similarity index 72%
rename from src/route.rs
rename to src/route/web.rs
index 45f912c..9a4f855 100644
--- a/src/route.rs
+++ b/src/route/web.rs
@@ -1,172 +1,17 @@
+use super::Route;
use crate::{err, page, station::Station, succ};
use axum::{
- Form, Router,
extract::State,
response::{IntoResponse, Redirect},
routing::{get, post},
+ Form, Router,
};
-use maud::{Markup, PreEscaped, html};
-use serde::{Deserialize, Serialize};
-use sqlx::{FromRow, SqlitePool};
+use maud::{html, Markup, PreEscaped};
+use serde::Deserialize;
+use sqlx::SqlitePool;
use std::sync::Arc;
use tower_sessions::Session;
-#[derive(FromRow, Debug, Serialize, Deserialize)]
-pub(crate) struct Route {
- pub(crate) id: i64,
- name: String,
-}
-
-impl Route {
- async fn all(db: &SqlitePool) -> Vec {
- sqlx::query_as::<_, Self>("SELECT id, name FROM route;")
- .fetch_all(db)
- .await
- .unwrap()
- }
-
- pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option {
- sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id)
- .fetch_one(db)
- .await
- .ok()
- }
-
- async fn create(db: &SqlitePool, name: &str) -> Result<(), String> {
- sqlx::query!("INSERT INTO route(name) VALUES (?)", name)
- .execute(db)
- .await
- .map_err(|e| e.to_string())?;
- Ok(())
- }
-
- async fn update_name(&self, db: &SqlitePool, name: &str) {
- sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id)
- .execute(db)
- .await
- .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) -> Result<(), String> {
- sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
- .execute(db)
- .await
- .map_err(|e| e.to_string())?;
- Ok(())
- }
-
- async fn stations(&self, db: &SqlitePool) -> Vec {
- sqlx::query_as::<_, Station>(
- "
- 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 {
- sqlx::query_as::<_, Station>(
- "
- 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>, session: Session) -> Markup {
let routes = Route::all(&db).await;
diff --git a/src/station/mod.rs b/src/station/mod.rs
index c70b7e9..9e1ebf5 100644
--- a/src/station/mod.rs
+++ b/src/station/mod.rs
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use std::sync::Arc;
-mod routes;
+mod web;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub(crate) struct Station {
@@ -130,5 +130,5 @@ impl Station {
}
pub(super) fn routes() -> Router> {
- routes::routes()
+ web::routes()
}
diff --git a/src/station/routes.rs b/src/station/web.rs
similarity index 100%
rename from src/station/routes.rs
rename to src/station/web.rs