From a441d99b5ed7471915963eed5e0ef2ca8b852c1c Mon Sep 17 00:00:00 2001 From: philipp Date: Tue, 3 Sep 2024 17:39:08 +0300 Subject: [PATCH] delete cancelled trips if all rowers have seen the notification --- src/model/notification.rs | 18 ++--- src/model/trip.rs | 10 ++- src/model/usertrip.rs | 146 +++++++++++++++++++++++++++++++----- templates/planned.html.tera | 2 +- 4 files changed, 145 insertions(+), 31 deletions(-) diff --git a/src/model/notification.rs b/src/model/notification.rs index d27e894..ab592c3 100644 --- a/src/model/notification.rs +++ b/src/model/notification.rs @@ -5,7 +5,7 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; -use super::{role::Role, user::User}; +use super::{role::Role, user::User, usertrip::UserTrip}; #[derive(FromRow, Debug, Serialize, Deserialize, Clone)] pub struct Notification { @@ -140,15 +140,13 @@ ORDER BY read_at DESC, created_at DESC; let re = Regex::new(r"^remove_user_trip_with_trip_details_id:(\d+)$").unwrap(); if let Some(caps) = re.captures(action) { if let Some(matched) = caps.get(1) { - if let Ok(number) = matched.as_str().parse::() { - let _ = sqlx::query!( - "DELETE FROM user_trip WHERE user_id = ? AND trip_details_id = ?", - self.user_id, - number - ) - .execute(db) - .await - .unwrap(); + if let Ok(number) = matched.as_str().parse::() { + if let Some(usertrip) = + UserTrip::find_by_userid_and_trip_detail_id(db, self.user_id, number) + .await + { + let _ = usertrip.self_delete(db).await; + } } } } diff --git a/src/model/trip.rs b/src/model/trip.rs index d9765f8..908c9ad 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -215,12 +215,18 @@ WHERE day=? let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); let was_already_cancelled = tripdetails.max_people == 0; + let is_locked = if update.max_people == 0 { + false + } else { + update.is_locked + }; + sqlx::query!( "UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, is_locked = ? WHERE id = ?", update.max_people, update.notes, update.trip_type, - update.is_locked, + is_locked, trip_details_id ) .execute(db) @@ -242,7 +248,7 @@ WHERE day=? db, &user, &format!( - "Die Ausfahrt von {} am {} um {} wurde abgesagt. {}", + "Die Ausfahrt von {} am {} um {} wurde abgesagt. {} Bitte gib Bescheid, dass du die Info erhalten hast indem du auf ✓ klickst.", update.cox.user.name, update.trip.day, update.trip.planned_starting_time, diff --git a/src/model/usertrip.rs b/src/model/usertrip.rs index d0e38b6..f73a19a 100644 --- a/src/model/usertrip.rs +++ b/src/model/usertrip.rs @@ -1,9 +1,21 @@ -use sqlx::SqlitePool; +use serde::{Deserialize, Serialize}; +use sqlx::{FromRow, SqlitePool}; -use super::{notification::Notification, trip::Trip, tripdetails::TripDetails, user::User}; +use super::{ + notification::Notification, + trip::{Trip, TripWithUserAndType}, + tripdetails::TripDetails, + user::{CoxUser, User}, +}; use crate::model::tripdetails::{Action, CoxAtTrip::Yes}; -pub struct UserTrip {} +#[derive(FromRow, Debug, Serialize, Deserialize, Clone)] +pub struct UserTrip { + pub user_id: Option, + pub user_note: Option, + pub trip_details_id: i64, + pub created_at: String, // TODO: switch to NaiveDateTime +} impl UserTrip { pub async fn create( @@ -66,23 +78,27 @@ impl UserTrip { .await .unwrap(); - user_note.unwrap() + user_note.clone().unwrap() }; if let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await { - let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); - Notification::create( - db, - &cox, - &format!( - "{} hat sich für deine Ausfahrt am {} registriert", - name_newly_registered_person, trip.day - ), - "Registrierung bei deiner Ausfahrt", - None, - None, - ) - .await; + if user_note.is_none() { + // Don't show notification if we add guest (as only we are + // allowed to do so) + let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); + Notification::create( + db, + &cox, + &format!( + "{} hat sich für deine Ausfahrt am {} registriert", + name_newly_registered_person, trip.day + ), + "Registrierung bei deiner Ausfahrt", + None, + None, + ) + .await; + } trip_details.check_free_spaces(db).await; } @@ -90,6 +106,34 @@ impl UserTrip { Ok(name_newly_registered_person) } + pub async fn tripdetails(&self, db: &SqlitePool) -> TripDetails { + TripDetails::find_by_id(db, self.trip_details_id) + .await + .unwrap() + } + + pub async fn find_by_userid_and_trip_detail_id( + db: &SqlitePool, + user_id: i64, + trip_detail_id: i64, + ) -> Option { + sqlx::query_as!(Self, "SELECT user_id, user_note, trip_details_id, created_at FROM user_trip WHERE user_id= ? AND trip_details_id = ?", user_id, trip_detail_id) + .fetch_one(db) + .await + .ok() + } + + pub async fn self_delete(&self, db: &SqlitePool) -> Result<(), UserTripDeleteError> { + let trip_details = self.tripdetails(db).await; + if let Some(id) = self.user_id { + let user = User::find_by_id(db, id as i32).await.unwrap(); + return Self::delete(db, &user, &trip_details, self.user_note.clone()).await; + } + + Ok(()) // TODO: fixme + } + + //TODO: cleaner code pub async fn delete( db: &SqlitePool, user: &User, @@ -104,7 +148,24 @@ impl UserTrip { return Err(UserTripDeleteError::NotVisibleToUser); } - if let Some(name) = name { + let mut trip_to_delete = None; + let mut some_trip = None; + if let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await { + some_trip = Some(trip.clone()); + // If trip is cancelled, and lost rower just unregistered, delete the trip + if TripDetails::find_by_id(db, trip_details.id) + .await + .unwrap() + .cancelled() + { + let trip = TripWithUserAndType::from(db, trip.clone()).await; + if trip.rower.len() == 1 { + trip_to_delete = Some(trip.trip); + } + } + } + + if let Some(name) = name.clone() { if !trip_details.user_allowed_to_change(db, user).await { return Err(UserTripDeleteError::NotAllowedToDeleteGuest); } @@ -132,6 +193,55 @@ impl UserTrip { .await .unwrap(); } + + let mut add_info = ""; + if let Some(trip) = &trip_to_delete { + let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); + trip.delete(db, &CoxUser::new(db, cox).await.unwrap()) + .await + .unwrap(); + add_info = " Das war die letzte angemeldete Person. Nachdem nun alle Bescheid wissen, wird die Ausfahrt ab sofort nicht mehr angezeigt."; + } + + if let Some(trip) = some_trip { + let opt_cancelled = if trip_to_delete.is_some() { + "abgesagten " + } else { + "" + }; + if let Some(name) = name { + if !add_info.is_empty() { + let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); + Notification::create( + db, + &cox, + &format!( + "Du hast {} von deiner {}Ausfahrt am {} abgemeldet.{}", + name, opt_cancelled, trip.day, add_info + ), + "Abmeldung von deiner Ausfahrt", + None, + None, + ) + .await; + } + } else { + let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); + Notification::create( + db, + &cox, + &format!( + "{} hat sich von deiner {}Ausfahrt am {} abgemeldet.{}", + user.name, opt_cancelled, trip.day, add_info + ), + "Abmeldung von deiner Ausfahrt", + None, + None, + ) + .await; + } + } + Ok(()) } } diff --git a/templates/planned.html.tera b/templates/planned.html.tera index 67ed223..ac25a81 100644 --- a/templates/planned.html.tera +++ b/templates/planned.html.tera @@ -327,7 +327,7 @@
{% if trip.max_people == 0 %} {# --- border-[#f43f5e] bg-[#f43f5e] --- #} - {{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage') }} + {{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }} {% else %} {% set amount_cur_rower = trip.rower | length %} {{ macros::box(participants=trip.rower, empty_seats=trip.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }}