restructure #1050

Merged
philipp merged 6 commits from restructure into staging 2025-05-22 12:41:52 +02:00
9 changed files with 246 additions and 239 deletions

View File

@ -1,10 +1,7 @@
use std::io::Write; use std::io::Write;
use chrono::{Duration, NaiveDate, NaiveTime}; use chrono::NaiveDate;
use ics::{ use ics::ICalendar;
ICalendar,
properties::{DtEnd, DtStart, Summary},
};
use serde::Serialize; use serde::Serialize;
use sqlx::{FromRow, Row, SqlitePool}; use sqlx::{FromRow, Row, SqlitePool};
@ -142,7 +139,7 @@ WHERE planned_event.id like ?
.ok() .ok()
} }
async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> { pub(crate) async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
if let Some(trip_type_id) = self.trip_type_id { if let Some(trip_type_id) = self.trip_type_id {
TripType::find_by_id(db, trip_type_id).await TripType::find_by_id(db, trip_type_id).await
} else { } else {
@ -474,66 +471,6 @@ WHERE trip_details.id=?
String::from_utf8(buf).unwrap() String::from_utf8(buf).unwrap()
} }
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
let mut vevent = ics::Event::new(
format!("event-{}@rudernlinz.at", self.id),
"19900101T180000",
);
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str.clone() // TODO: remove again
};
vevent.push(DtStart::new(format!(
"{}T{}00",
self.day.replace('-', ""),
formatted_time
)));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
.expect("Failed to parse time");
let long_trip = match self.trip_type(db).await {
Some(a) if a.name == "Lange Ausfahrt" => true,
_ => false,
};
let later_time = if long_trip {
original_time + Duration::hours(6)
} else {
original_time + Duration::hours(3)
};
if later_time > original_time {
// Check if no day-overflow
let time_three_hours_later = later_time.format("%H%M").to_string();
vevent.push(DtEnd::new(format!(
"{}T{}00",
self.day.replace('-', ""),
time_three_hours_later
)));
}
let tripdetails = self.trip_details(db).await;
let mut name = String::new();
if self.is_cancelled() {
name.push_str("ABGESAGT");
if let Some(notes) = &tripdetails.notes {
if !notes.is_empty() {
name.push_str(&format!(" (Grund: {notes})"))
}
}
name.push_str("! :-( ");
}
name.push_str(&format!("{} ", self.name));
if let Some(triptype) = tripdetails.triptype(db).await {
name.push_str(&format!("{} ", triptype.name))
}
vevent.push(Summary::new(name));
vevent
}
pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails { pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails {
TripDetails::find_by_id(db, self.trip_details_id) TripDetails::find_by_id(db, self.trip_details_id)
.await .await

View File

@ -422,7 +422,6 @@ ORDER BY departure DESC
min_distance: i32, min_distance: i32,
year: i32, year: i32,
filter: Filter, filter: Filter,
exclude_last_log: bool,
) -> Vec<LogbookWithBoatAndRowers> { ) -> Vec<LogbookWithBoatAndRowers> {
let logs: Vec<Logbook> = sqlx::query_as( let logs: Vec<Logbook> = sqlx::query_as(
&format!(" &format!("
@ -454,9 +453,6 @@ ORDER BY departure DESC
} }
} }
} }
if exclude_last_log {
ret.pop();
}
ret ret
} }

View File

@ -162,7 +162,7 @@ impl Mail {
} }
} }
if user.has_role(db, "schnupperant").await { if user.has_role(db, "schnupperant").await || user.has_role(db, "scheckbuch").await {
continue; continue;
} }
@ -279,7 +279,7 @@ Der Vorstand");
} }
} }
if user.has_role(db, "schnupperant").await { if user.has_role(db, "schnupperant").await || user.has_role(db, "scheckbuch").await {
continue; continue;
} }

View File

@ -1,9 +1,14 @@
use std::io::Write; use std::io::Write;
use ics::{ICalendar, components::Property}; use ics::{
components::Property,
properties::{DtEnd, DtStart, Summary},
ICalendar,
};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::{event::Event, trip::Trip, user::User}; use crate::model::{event::Event, trip::Trip, user::User};
use chrono::{Duration, NaiveTime};
pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String { pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String {
let mut calendar = ICalendar::new("2.0", "ics-rs"); let mut calendar = ICalendar::new("2.0", "ics-rs");
@ -25,3 +30,125 @@ pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String {
write!(&mut buf, "{}", calendar).unwrap(); write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap() String::from_utf8(buf).unwrap()
} }
impl Trip {
pub(crate) async fn get_vevent<'a>(self, db: &'a SqlitePool, user: &'a User) -> ics::Event<'a> {
let mut vevent =
ics::Event::new(format!("trip-{}@rudernlinz.at", self.id), "19900101T180000");
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str
};
vevent.push(DtStart::new(format!(
"{}T{}00",
self.day.replace('-', ""),
formatted_time
)));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
.expect("Failed to parse time");
let long_trip = match self.trip_type(db).await {
Some(a) if a.name == "Lange Ausfahrt" => true,
_ => false,
};
let later_time = if long_trip {
original_time + Duration::hours(6)
} else {
original_time + Duration::hours(3)
};
if later_time > original_time {
// Check if no day-overflow
let time_three_hours_later = later_time.format("%H%M").to_string();
vevent.push(DtEnd::new(format!(
"{}T{}00",
self.day.replace('-', ""),
time_three_hours_later
)));
}
let mut name = String::new();
if self.is_cancelled() {
name.push_str("ABGESAGT");
if let Some(notes) = &self.notes {
if !notes.is_empty() {
name.push_str(&format!(" (Grund: {notes})"))
}
}
name.push_str("! :-( ");
}
if self.cox_id == user.id {
name.push_str("Ruderausfahrt (selber ausgeschrieben)");
} else {
name.push_str(&format!("Ruderausfahrt mit {} ", self.cox_name));
}
vevent.push(Summary::new(name));
vevent
}
}
impl Event {
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
let mut vevent = ics::Event::new(
format!("event-{}@rudernlinz.at", self.id),
"19900101T180000",
);
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str.clone() // TODO: remove again
};
vevent.push(DtStart::new(format!(
"{}T{}00",
self.day.replace('-', ""),
formatted_time
)));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
.expect("Failed to parse time");
let long_trip = match self.trip_type(db).await {
Some(a) if a.name == "Lange Ausfahrt" => true,
_ => false,
};
let later_time = if long_trip {
original_time + Duration::hours(6)
} else {
original_time + Duration::hours(3)
};
if later_time > original_time {
// Check if no day-overflow
let time_three_hours_later = later_time.format("%H%M").to_string();
vevent.push(DtEnd::new(format!(
"{}T{}00",
self.day.replace('-', ""),
time_three_hours_later
)));
}
let tripdetails = self.trip_details(db).await;
let mut name = String::new();
if self.is_cancelled() {
name.push_str("ABGESAGT");
if let Some(notes) = &tripdetails.notes {
if !notes.is_empty() {
name.push_str(&format!(" (Grund: {notes})"))
}
}
name.push_str("! :-( ");
}
name.push_str(&format!("{} ", self.name));
if let Some(triptype) = tripdetails.triptype(db).await {
name.push_str(&format!("{} ", triptype.name))
}
vevent.push(Summary::new(name));
vevent
}
}

View File

@ -2,7 +2,7 @@ use std::cmp;
use chrono::{Datelike, Local, NaiveDate}; use chrono::{Datelike, Local, NaiveDate};
use serde::Serialize; use serde::Serialize;
use sqlx::{Sqlite, SqlitePool, Transaction}; use sqlx::{Acquire, Sqlite, SqlitePool, Transaction};
use crate::model::{ use crate::model::{
logbook::{Filter, Logbook, LogbookWithBoatAndRowers}, logbook::{Filter, Logbook, LogbookWithBoatAndRowers},
@ -141,11 +141,7 @@ impl Status {
} }
} }
pub(crate) async fn for_user_tx( pub(crate) async fn for_user_tx(db: &mut Transaction<'_, Sqlite>, user: &User) -> Option<Self> {
db: &mut Transaction<'_, Sqlite>,
user: &User,
exclude_last_log: bool,
) -> Option<Self> {
let Ok(agebracket) = AgeBracket::try_from(user) else { let Ok(agebracket) = AgeBracket::try_from(user) else {
return None; return None;
}; };
@ -164,7 +160,6 @@ impl Status {
agebracket.required_dist_single_day_in_km(), agebracket.required_dist_single_day_in_km(),
year, year,
Filter::SingleDayOnly, Filter::SingleDayOnly,
exclude_last_log,
) )
.await; .await;
let multi_day_trips_over_required_distance = let multi_day_trips_over_required_distance =
@ -174,7 +169,6 @@ impl Status {
agebracket.required_dist_multi_day_in_km(), agebracket.required_dist_multi_day_in_km(),
year, year,
Filter::MultiDayOnly, Filter::MultiDayOnly,
exclude_last_log,
) )
.await; .await;
@ -195,7 +189,7 @@ impl Status {
pub(crate) async fn for_user(db: &SqlitePool, user: &User) -> Option<Self> { pub(crate) async fn for_user(db: &SqlitePool, user: &User) -> Option<Self> {
let mut tx = db.begin().await.unwrap(); let mut tx = db.begin().await.unwrap();
let ret = Self::for_user_tx(&mut tx, user, false).await; let ret = Self::for_user_tx(&mut tx, user).await;
tx.commit().await.unwrap(); tx.commit().await.unwrap();
ret ret
} }
@ -204,11 +198,19 @@ impl Status {
db: &mut Transaction<'_, Sqlite>, db: &mut Transaction<'_, Sqlite>,
user: &User, user: &User,
) -> bool { ) -> bool {
if let Some(status) = Self::for_user_tx(db, user, false).await { if let Some(status) = Self::for_user_tx(db, user).await {
// if user has agebracket... // if user has agebracket...
if status.achieved { if status.achieved {
// ... and has achieved the 'Fahrtenabzeichen' // ... and has achieved the 'Fahrtenabzeichen'
let without_last_entry = Self::for_user_tx(db, user, true).await.unwrap(); let mut without_last = db.begin().await.unwrap();
let last = Logbook::completed_with_user_tx(&mut without_last, user).await;
let last = last.last().unwrap();
sqlx::query!("DELETE FROM logbook WHERE id=?", last.logbook.id)
.execute(&mut *without_last)
.await
.unwrap(); //Okay, because we can only create a Logbook of a valid id
let without_last_entry = Self::for_user_tx(&mut without_last, user).await.unwrap();
if !without_last_entry.achieved { if !without_last_entry.achieved {
// ... and this wasn't the case before the last logentry // ... and this wasn't the case before the last logentry
return true; return true;

77
src/model/trip/create.rs Normal file
View File

@ -0,0 +1,77 @@
use super::Trip;
use crate::model::{
notification::Notification,
tripdetails::TripDetails,
triptype::TripType,
user::{ErgoUser, SteeringUser, User},
};
use sqlx::SqlitePool;
impl Trip {
/// Cox decides to create own trip.
pub async fn new_own(db: &SqlitePool, cox: &SteeringUser, trip_details: TripDetails) {
Self::perform_new(db, &cox.user, trip_details).await
}
/// ErgoUser decides to create ergo 'trip'. Returns false, if trip is not a ergo-session (and
/// thus User is not allowed to create such a trip)
pub async fn new_own_ergo(db: &SqlitePool, ergo: &ErgoUser, trip_details: TripDetails) -> bool {
if let Some(typ) = trip_details.triptype(db).await {
let allowed_type = TripType::find_by_id(db, 4).await.unwrap();
if typ == allowed_type {
Self::perform_new(db, &ergo.user, trip_details).await;
return true;
}
}
false
}
async fn perform_new(db: &SqlitePool, user: &User, trip_details: TripDetails) {
let _ = sqlx::query!(
"INSERT INTO trip (cox_id, trip_details_id) VALUES(?, ?)",
user.id,
trip_details.id
)
.execute(db)
.await;
Self::notify_trips_same_datetime(db, trip_details, user).await;
}
async fn notify_trips_same_datetime(db: &SqlitePool, trip_details: TripDetails, user: &User) {
let same_starting_datetime = TripDetails::find_by_startingdatetime(
db,
trip_details.day,
trip_details.planned_starting_time,
)
.await;
for notify in same_starting_datetime {
// don't notify oneself
if notify.id == trip_details.id {
continue;
}
// don't notify people who have cancelled their trip
if notify.cancelled() {
continue;
}
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
let user_earlier_trip = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&user_earlier_trip,
&format!(
"{user} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
trip.day, trip.planned_starting_time
),
"Neue Ausfahrt zur selben Zeit",
None,
None,
)
.await;
}
}
}
}

View File

@ -1,25 +1,26 @@
use chrono::{Duration, Local, NaiveDate, NaiveTime}; use chrono::{Local, NaiveDate};
use ics::properties::{DtEnd, DtStart, Summary};
use serde::Serialize; use serde::Serialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
mod create;
use super::{ use super::{
event::{Event, Registration}, event::{Event, Registration},
log::Log, log::Log,
notification::Notification, notification::Notification,
tripdetails::TripDetails, tripdetails::TripDetails,
triptype::TripType, triptype::TripType,
user::{ErgoUser, SteeringUser, User}, user::{SteeringUser, User},
usertrip::UserTrip, usertrip::UserTrip,
}; };
#[derive(Serialize, Clone, Debug)] #[derive(Serialize, Clone, Debug)]
pub struct Trip { pub struct Trip {
id: i64, pub id: i64,
pub cox_id: i64, pub cox_id: i64,
cox_name: String, pub cox_name: String,
trip_details_id: Option<i64>, trip_details_id: Option<i64>,
planned_starting_time: String, pub planned_starting_time: String,
pub max_people: i64, pub max_people: i64,
pub day: String, pub day: String,
pub notes: Option<String>, pub notes: Option<String>,
@ -69,65 +70,6 @@ impl TripWithDetails {
} }
impl Trip { impl Trip {
/// Cox decides to create own trip.
pub async fn new_own(db: &SqlitePool, cox: &SteeringUser, trip_details: TripDetails) {
Self::perform_new(db, &cox.user, trip_details).await
}
pub async fn new_own_ergo(db: &SqlitePool, ergo: &ErgoUser, trip_details: TripDetails) {
let typ = trip_details.triptype(db).await;
if let Some(typ) = typ {
let allowed_type = TripType::find_by_id(db, 4).await.unwrap();
if typ == allowed_type {
Self::perform_new(db, &ergo.user, trip_details).await;
}
}
}
async fn perform_new(db: &SqlitePool, user: &User, trip_details: TripDetails) {
let _ = sqlx::query!(
"INSERT INTO trip (cox_id, trip_details_id) VALUES(?, ?)",
user.id,
trip_details.id
)
.execute(db)
.await;
let same_starting_datetime = TripDetails::find_by_startingdatetime(
db,
trip_details.day,
trip_details.planned_starting_time,
)
.await;
for notify in same_starting_datetime {
// don't notify oneself
if notify.id == trip_details.id {
continue;
}
// don't notify people who have cancelled their trip
if notify.cancelled() {
continue;
}
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
let user_earlier_trip = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
Notification::create(
db,
&user_earlier_trip,
&format!(
"{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
user.name, trip.day, trip.planned_starting_time
),
"Neue Ausfahrt zur selben Zeit",
None,
None,
)
.await;
}
}
}
pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option<Self> { pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
@ -145,7 +87,7 @@ WHERE trip_details.id=?
.ok() .ok()
} }
async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> { pub(crate) async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
if let Some(trip_type_id) = self.trip_type_id { if let Some(trip_type_id) = self.trip_type_id {
TripType::find_by_id(db, trip_type_id).await TripType::find_by_id(db, trip_type_id).await
} else { } else {
@ -153,64 +95,6 @@ WHERE trip_details.id=?
} }
} }
pub(crate) async fn get_vevent<'a>(self, db: &'a SqlitePool, user: &'a User) -> ics::Event<'a> {
let mut vevent =
ics::Event::new(format!("trip-{}@rudernlinz.at", self.id), "19900101T180000");
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str
};
vevent.push(DtStart::new(format!(
"{}T{}00",
self.day.replace('-', ""),
formatted_time
)));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
.expect("Failed to parse time");
let long_trip = match self.trip_type(db).await {
Some(a) if a.name == "Lange Ausfahrt" => true,
_ => false,
};
let later_time = if long_trip {
original_time + Duration::hours(6)
} else {
original_time + Duration::hours(3)
};
if later_time > original_time {
// Check if no day-overflow
let time_three_hours_later = later_time.format("%H%M").to_string();
vevent.push(DtEnd::new(format!(
"{}T{}00",
self.day.replace('-', ""),
time_three_hours_later
)));
}
let mut name = String::new();
if self.is_cancelled() {
name.push_str("ABGESAGT");
if let Some(notes) = &self.notes {
if !notes.is_empty() {
name.push_str(&format!(" (Grund: {notes})"))
}
}
name.push_str("! :-( ");
}
if self.cox_id == user.id {
name.push_str("Ruderausfahrt (selber ausgeschrieben)");
} else {
name.push_str(&format!("Ruderausfahrt mit {} ", self.cox_name));
}
vevent.push(Summary::new(name));
vevent
}
pub async fn all(db: &SqlitePool) -> Vec<Self> { pub async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
@ -491,7 +375,7 @@ WHERE day=?
trips trips
} }
fn is_cancelled(&self) -> bool { pub(crate) fn is_cancelled(&self) -> bool {
self.max_people == -1 self.max_people == -1
} }
} }
@ -583,11 +467,9 @@ mod test {
let last_notification = &Notification::for_user(&pool, &cox).await[0]; let last_notification = &Notification::for_user(&pool, &cox).await[0];
assert!( assert!(last_notification
last_notification
.message .message
.starts_with("cox2 hat eine Ausfahrt zur selben Zeit") .starts_with("cox2 hat eine Ausfahrt zur selben Zeit"));
);
} }
#[sqlx::test] #[sqlx::test]

View File

@ -1,8 +1,8 @@
use super::User; use super::User;
use crate::{ use crate::{
BOAT_STORAGE, DUAL_MEMBERSHIP, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND, model::family::Family, BOAT_STORAGE, DUAL_MEMBERSHIP, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE,
REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, TRIAL_ROWING, TRIAL_ROWING_REDUCED, FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG, SCHECKBUCH, STUDENT_OR_PUPIL, TRIAL_ROWING,
UNTERSTUETZEND, model::family::Family, TRIAL_ROWING_REDUCED, UNTERSTUETZEND,
}; };
use chrono::{Datelike, Local, NaiveDate}; use chrono::{Datelike, Local, NaiveDate};
use serde::Serialize; use serde::Serialize;
@ -70,6 +70,7 @@ impl User {
&& !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Unterstützend").await
&& !self.has_role(db, "Förderndes Mitglied").await && !self.has_role(db, "Förderndes Mitglied").await
&& !self.has_role(db, "schnupperant").await && !self.has_role(db, "schnupperant").await
&& !self.has_role(db, "scheckbuch").await
{ {
return None; return None;
} }
@ -110,6 +111,7 @@ impl User {
&& !self.has_role(db, "Unterstützend").await && !self.has_role(db, "Unterstützend").await
&& !self.has_role(db, "Förderndes Mitglied").await && !self.has_role(db, "Förderndes Mitglied").await
&& !self.has_role(db, "schnupperant").await && !self.has_role(db, "schnupperant").await
&& !self.has_role(db, "scheckbuch").await
{ {
return fee; return fee;
} }
@ -162,6 +164,8 @@ impl User {
} else { } else {
fee.add("Schnupperkurs".into(), TRIAL_ROWING); fee.add("Schnupperkurs".into(), TRIAL_ROWING);
} }
} else if self.has_role(db, "scheckbuch").await {
fee.add("Scheckbuch".into(), SCHECKBUCH);
} else if self.has_role(db, "Unterstützend").await { } else if self.has_role(db, "Unterstützend").await {
fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND); fee.add("Unterstützendes Mitglied".into(), UNTERSTUETZEND);
} else if self.has_role(db, "Förderndes Mitglied").await { } else if self.has_role(db, "Förderndes Mitglied").await {

View File

@ -1,9 +1,8 @@
use rocket::{ use rocket::{
FromForm, Route, State,
form::Form, form::Form,
get, post, get, post,
response::{Flash, Redirect}, response::{Flash, Redirect},
routes, routes, FromForm, Route, State,
}; };
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -26,18 +25,10 @@ async fn create_ergo(
//created //created
Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix
//Log::create(
// db,
// format!(
// "Cox {} created trip on {} @ {} for {} rower",
// cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
// ),
//)
//.await;
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.") Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
} }
/// SteeringUser created new trip
#[post("/trip", data = "<data>")] #[post("/trip", data = "<data>")]
async fn create( async fn create(
db: &State<SqlitePool>, db: &State<SqlitePool>,
@ -49,15 +40,6 @@ async fn create(
//created //created
Trip::new_own(db, &cox, trip_details).await; //TODO: fix Trip::new_own(db, &cox, trip_details).await; //TODO: fix
//Log::create(
// db,
// format!(
// "Cox {} created trip on {} @ {} for {} rower",
// cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
// ),
//)
//.await;
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.") Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
} }