Compare commits

...

2 Commits

Author SHA1 Message Date
96dcf2c4ae Merge pull request 'delete cancelled trips if all rowers have seen the notification' (#720) from delete-cancelled-events into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m26s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 8m19s
Reviewed-on: #720
2024-09-03 16:51:39 +02:00
a441d99b5e delete cancelled trips if all rowers have seen the notification
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m21s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-09-03 17:39:08 +03:00
4 changed files with 145 additions and 31 deletions

View File

@ -5,7 +5,7 @@ use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; 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)] #[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
pub struct Notification { 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(); let re = Regex::new(r"^remove_user_trip_with_trip_details_id:(\d+)$").unwrap();
if let Some(caps) = re.captures(action) { if let Some(caps) = re.captures(action) {
if let Some(matched) = caps.get(1) { if let Some(matched) = caps.get(1) {
if let Ok(number) = matched.as_str().parse::<i32>() { if let Ok(number) = matched.as_str().parse::<i64>() {
let _ = sqlx::query!( if let Some(usertrip) =
"DELETE FROM user_trip WHERE user_id = ? AND trip_details_id = ?", UserTrip::find_by_userid_and_trip_detail_id(db, self.user_id, number)
self.user_id, .await
number {
) let _ = usertrip.self_delete(db).await;
.execute(db) }
.await
.unwrap();
} }
} }
} }

View File

@ -215,12 +215,18 @@ WHERE day=?
let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
let was_already_cancelled = tripdetails.max_people == 0; let was_already_cancelled = tripdetails.max_people == 0;
let is_locked = if update.max_people == 0 {
false
} else {
update.is_locked
};
sqlx::query!( sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, is_locked = ? WHERE id = ?", "UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, is_locked = ? WHERE id = ?",
update.max_people, update.max_people,
update.notes, update.notes,
update.trip_type, update.trip_type,
update.is_locked, is_locked,
trip_details_id trip_details_id
) )
.execute(db) .execute(db)
@ -242,7 +248,7 @@ WHERE day=?
db, db,
&user, &user,
&format!( &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.cox.user.name,
update.trip.day, update.trip.day,
update.trip.planned_starting_time, update.trip.planned_starting_time,

View File

@ -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}; use crate::model::tripdetails::{Action, CoxAtTrip::Yes};
pub struct UserTrip {} #[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
pub struct UserTrip {
pub user_id: Option<i64>,
pub user_note: Option<String>,
pub trip_details_id: i64,
pub created_at: String, // TODO: switch to NaiveDateTime
}
impl UserTrip { impl UserTrip {
pub async fn create( pub async fn create(
@ -66,23 +78,27 @@ impl UserTrip {
.await .await
.unwrap(); .unwrap();
user_note.unwrap() user_note.clone().unwrap()
}; };
if let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await { 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(); if user_note.is_none() {
Notification::create( // Don't show notification if we add guest (as only we are
db, // allowed to do so)
&cox, let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
&format!( Notification::create(
"{} hat sich für deine Ausfahrt am {} registriert", db,
name_newly_registered_person, trip.day &cox,
), &format!(
"Registrierung bei deiner Ausfahrt", "{} hat sich für deine Ausfahrt am {} registriert",
None, name_newly_registered_person, trip.day
None, ),
) "Registrierung bei deiner Ausfahrt",
.await; None,
None,
)
.await;
}
trip_details.check_free_spaces(db).await; trip_details.check_free_spaces(db).await;
} }
@ -90,6 +106,34 @@ impl UserTrip {
Ok(name_newly_registered_person) 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<Self> {
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( pub async fn delete(
db: &SqlitePool, db: &SqlitePool,
user: &User, user: &User,
@ -104,7 +148,24 @@ impl UserTrip {
return Err(UserTripDeleteError::NotVisibleToUser); 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 { if !trip_details.user_allowed_to_change(db, user).await {
return Err(UserTripDeleteError::NotAllowedToDeleteGuest); return Err(UserTripDeleteError::NotAllowedToDeleteGuest);
} }
@ -132,6 +193,55 @@ impl UserTrip {
.await .await
.unwrap(); .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(()) Ok(())
} }
} }

View File

@ -327,7 +327,7 @@
<div id="trip{{ trip.trip_details_id }}"> <div id="trip{{ trip.trip_details_id }}">
{% if trip.max_people == 0 %} {% if trip.max_people == 0 %}
{# --- border-[#f43f5e] bg-[#f43f5e] --- #} {# --- 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 %} {% else %}
{% set amount_cur_rower = trip.rower | length %} {% 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) }} {{ 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) }}