zero-rower-events; Fixes #913 #927

Merged
philipp merged 2 commits from zero-rower-events into staging 2025-04-18 23:12:46 +02:00
6 changed files with 44 additions and 29 deletions

View File

@ -34,11 +34,13 @@ pub struct Event {
} }
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct EventWithUserAndTriptype { pub struct EventWithDetails {
#[serde(flatten)] #[serde(flatten)]
pub event: Event, pub event: Event,
trip_type: Option<TripType>, trip_type: Option<TripType>,
tripdetails: TripDetails,
cox_needed: bool, cox_needed: bool,
cancelled: bool,
cox: Vec<Registration>, cox: Vec<Registration>,
rower: Vec<Registration>, rower: Vec<Registration>,
} }
@ -116,6 +118,12 @@ pub struct EventUpdate<'a> {
pub trip_type_id: Option<i64>, pub trip_type_id: Option<i64>,
} }
impl EventUpdate<'_> {
fn cancelled(&self) -> bool {
self.max_people == -1
}
}
impl Event { impl Event {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
@ -134,16 +142,13 @@ WHERE planned_event.id like ?
.ok() .ok()
} }
pub async fn get_pinned_for_day( pub async fn get_pinned_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithDetails> {
db: &SqlitePool,
day: NaiveDate,
) -> Vec<EventWithUserAndTriptype> {
let mut events = Self::get_for_day(db, day).await; let mut events = Self::get_for_day(db, day).await;
events.retain(|e| e.event.always_show); events.retain(|e| e.event.always_show);
events events
} }
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithUserAndTriptype> { pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithDetails> {
let day = format!("{day}"); let day = format!("{day}");
let events = sqlx::query_as!( let events = sqlx::query_as!(
Event, Event,
@ -164,10 +169,15 @@ WHERE day=?",
if let Some(trip_type_id) = event.trip_type_id { if let Some(trip_type_id) = event.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await; trip_type = TripType::find_by_id(db, trip_type_id).await;
} }
ret.push(EventWithUserAndTriptype { let tripdetails = TripDetails::find_by_id(db, event.trip_details_id)
.await
.expect("db constraints");
ret.push(EventWithDetails {
cox_needed: event.planned_amount_cox > cox.len() as i64, cox_needed: event.planned_amount_cox > cox.len() as i64,
cox, cox,
rower: Registration::all_rower(db, event.trip_details_id).await, rower: Registration::all_rower(db, event.trip_details_id).await,
cancelled: tripdetails.cancelled(),
tripdetails,
event, event,
trip_type, trip_type,
}); });
@ -315,7 +325,7 @@ WHERE trip_details.id=?
.unwrap(); //Okay, as planned_event can only be created with proper DB backing .unwrap(); //Okay, as planned_event can only be created with proper DB backing
let tripdetails = self.trip_details(db).await; let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.max_people == 0; let was_already_cancelled = tripdetails.cancelled();
sqlx::query!( sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?", "UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?",
@ -340,7 +350,7 @@ WHERE trip_details.id=?
.await; .await;
} }
if update.max_people == 0 && !was_already_cancelled { if update.cancelled() && !was_already_cancelled {
let coxes = Registration::all_cox(db, self.id).await; let coxes = Registration::all_cox(db, self.id).await;
for user in coxes { for user in coxes {
if let Some(user) = User::find_by_name(db, &user.name).await { if let Some(user) = User::find_by_name(db, &user.name).await {
@ -389,7 +399,7 @@ WHERE trip_details.id=?
} }
} }
} }
if update.max_people > 0 && was_already_cancelled { if !update.cancelled() && was_already_cancelled {
Notification::delete_by_action( Notification::delete_by_action(
db, db,
&format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id), &format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id),
@ -427,7 +437,7 @@ WHERE trip_details.id=?
} }
pub fn is_cancelled(&self) -> bool { pub fn is_cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
pub async fn get_ics_feed(db: &SqlitePool) -> String { pub async fn get_ics_feed(db: &SqlitePool) -> String {

View File

@ -6,7 +6,7 @@ use waterlevel::WaterlevelDay;
use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD;
use self::{ use self::{
event::{Event, EventWithUserAndTriptype}, event::{Event, EventWithDetails},
trip::{Trip, TripWithUserAndType}, trip::{Trip, TripWithUserAndType},
waterlevel::Waterlevel, waterlevel::Waterlevel,
weather::Weather, weather::Weather,
@ -44,7 +44,7 @@ pub mod weather;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct Day { pub struct Day {
day: NaiveDate, day: NaiveDate,
events: Vec<EventWithUserAndTriptype>, events: Vec<EventWithDetails>,
trips: Vec<TripWithUserAndType>, trips: Vec<TripWithUserAndType>,
is_pinned: bool, is_pinned: bool,
regular_sees_this_day: bool, regular_sees_this_day: bool,

View File

@ -278,7 +278,7 @@ mod test {
let cancel_update = EventUpdate { let cancel_update = EventUpdate {
name: &event.name, name: &event.name,
planned_amount_cox: event.planned_amount_cox as i32, planned_amount_cox: event.planned_amount_cox as i32,
max_people: 0, max_people: -1,
notes: event.notes.as_deref(), notes: event.notes.as_deref(),
always_show: event.always_show, always_show: event.always_show,
is_locked: event.is_locked, is_locked: event.is_locked,

View File

@ -46,6 +46,12 @@ pub struct TripUpdate<'a> {
pub is_locked: bool, pub is_locked: bool,
} }
impl<'a> TripUpdate<'a> {
fn cancelled(&self) -> bool {
self.max_people == -1
}
}
impl TripWithUserAndType { impl TripWithUserAndType {
pub async fn from(db: &SqlitePool, trip: Trip) -> Self { pub async fn from(db: &SqlitePool, trip: Trip) -> Self {
let mut trip_type = None; let mut trip_type = None;
@ -245,7 +251,7 @@ WHERE trip.id=?
return Err(CoxHelpError::DetailsLocked); return Err(CoxHelpError::DetailsLocked);
} }
if event.max_people == 0 { if event.is_cancelled() {
return Err(CoxHelpError::CanceledEvent); return Err(CoxHelpError::CanceledEvent);
} }
@ -309,9 +315,9 @@ 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.cancelled();
let is_locked = if update.max_people == 0 { let is_locked = if update.cancelled() {
false false
} else { } else {
update.is_locked update.is_locked
@ -329,7 +335,7 @@ WHERE day=?
.await .await
.unwrap(); //Okay, as trip_details can only be created with proper DB backing .unwrap(); //Okay, as trip_details can only be created with proper DB backing
if update.max_people == 0 && !was_already_cancelled { if update.cancelled() && !was_already_cancelled {
let rowers = TripWithUserAndType::from(db, update.trip.clone()) let rowers = TripWithUserAndType::from(db, update.trip.clone())
.await .await
.rower; .rower;
@ -368,7 +374,7 @@ WHERE day=?
.await; .await;
} }
if update.max_people > 0 && was_already_cancelled { if !update.cancelled() && was_already_cancelled {
Notification::delete_by_action( Notification::delete_by_action(
db, db,
&format!("remove_user_trip_with_trip_details_id:{}", trip_details_id), &format!("remove_user_trip_with_trip_details_id:{}", trip_details_id),
@ -463,7 +469,7 @@ WHERE day=?
} }
fn is_cancelled(&self) -> bool { fn is_cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
} }

View File

@ -95,7 +95,7 @@ WHERE day = ? AND planned_starting_time = ?
} }
pub fn cancelled(&self) -> bool { pub fn cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
/// This function is called when a person registers to a trip or when the cox changes the /// This function is called when a person registers to a trip or when the cox changes the

View File

@ -124,7 +124,7 @@
{% if event.always_show and not day.regular_sees_this_day %} {% if event.always_show and not day.regular_sees_this_day %}
<span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span> <span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span>
{% endif -%} {% endif -%}
{%- if event.max_people == 0 %} {%- if event.cancelled %}
<strong class="text-[#f43f5e]">&#9888; Absage <strong class="text-[#f43f5e]">&#9888; Absage
{{ event.planned_starting_time }} {{ event.planned_starting_time }}
Uhr Uhr
@ -202,7 +202,7 @@
<div id="event{{ event.trip_details_id }}"> <div id="event{{ event.trip_details_id }}">
{# --- START List Coxes --- #} {# --- START List Coxes --- #}
{% if event.planned_amount_cox > 0 %} {% if event.planned_amount_cox > 0 %}
{% if event.max_people == 0 %} {% if event.cancelled %}
{{ macros::box(participants=event.cox, empty_seats="", header='Absage', bg='[#f43f5e]') }} {{ macros::box(participants=event.cox, empty_seats="", header='Absage', bg='[#f43f5e]') }}
{% else %} {% else %}
{% if amount_cox_missing > 0 %} {% if amount_cox_missing > 0 %}
@ -215,7 +215,7 @@
{# --- END List Coxes --- #} {# --- END List Coxes --- #}
{# --- START List Rowers --- #} {# --- START List Rowers --- #}
{% set amount_cur_rower = event.rower | length %} {% set amount_cur_rower = event.rower | length %}
{% if event.max_people == 0 %} {% if event.cancelled %}
{{ macros::box(header='Absage', bg='[#f43f5e]', participants=event.rower, trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }} {{ macros::box(header='Absage', bg='[#f43f5e]', participants=event.rower, trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }}
{% else %} {% else %}
{{ macros::box(participants=event.rower, empty_seats=event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }} {{ macros::box(participants=event.rower, empty_seats=event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }}
@ -240,7 +240,7 @@
<input type="hidden" name="_method" value="put" /> <input type="hidden" name="_method" value="put" />
<input type="hidden" name="id" value="{{ event.id }}" /> <input type="hidden" name="id" value="{{ event.id }}" />
{{ macros::input(label='Titel', name='name', type='input', value=event.name) }} {{ macros::input(label='Titel', name='name', type='input', value=event.name) }}
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=event.max_people, min='1') }} {{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=event.max_people, min='0') }}
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=event.planned_amount_cox, required=true, min='0') }} {{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=event.planned_amount_cox, required=true, min='0') }}
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=event.id,checked=event.always_show) }} {{ macros::checkbox(label='Immer anzeigen', name='always_show', id=event.id,checked=event.always_show) }}
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=event.id,checked=event.is_locked) }} {{ macros::checkbox(label='Gesperrt', name='is_locked', id=event.id,checked=event.is_locked) }}
@ -260,7 +260,7 @@
</a> </a>
</div> </div>
{% else %} {% else %}
{% if event.max_people == 0 %} {% if event.cancelled %}
Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen. Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen.
{% else %} {% else %}
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md"> <div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
@ -269,9 +269,8 @@
<input type="hidden" name="_method" value="put" /> <input type="hidden" name="_method" value="put" />
<input type="hidden" name="id" value="{{ event.id }}" /> <input type="hidden" name="id" value="{{ event.id }}" />
{{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }} {{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }}
{{ macros::input(label='', name='max_people', type='hidden', value=0) }} {{ macros::input(label='', name='max_people', type='hidden', value=-1) }}
{{ macros::input(label='', name='name', type='hidden', value=event.name) }} {{ macros::input(label='', name='name', type='hidden', value=event.name) }}
{{ macros::input(label='', name='max_people', type='hidden', value=event.max_people) }}
{{ macros::input(label='', name='planned_amount_cox', type='hidden', value=event.planned_amount_cox) }} {{ macros::input(label='', name='planned_amount_cox', type='hidden', value=event.planned_amount_cox) }}
{{ macros::input(label='', name='always_show', type='hidden', value=event.always_show) }} {{ macros::input(label='', name='always_show', type='hidden', value=event.always_show) }}
{{ macros::input(label='', name='is_locked', type='hidden', value=event.is_locked) }} {{ macros::input(label='', name='is_locked', type='hidden', value=event.is_locked) }}
@ -398,7 +397,7 @@
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md"> <div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt absagen</h3> <h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt absagen</h3>
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid"> <form action="/cox/trip/{{ trip.id }}" method="post" class="grid">
{{ macros::input(label='', name='max_people', type='hidden', value=0) }} {{ macros::input(label='', name='max_people', type='hidden', value=-1) }}
{{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }} {{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }}
{{ macros::input(label='', name='is_locked', type='hidden', value=trip.is_locked) }} {{ macros::input(label='', name='is_locked', type='hidden', value=trip.is_locked) }}
{{ macros::input(label='', name='trip_type', type='hidden', value=trip.trip_type_id) }} {{ macros::input(label='', name='trip_type', type='hidden', value=trip.trip_type_id) }}