new newbie flag #1223

Merged
philipp merged 2 commits from newbie-flag into main 2026-06-04 19:50:27 +02:00
17 changed files with 114 additions and 28 deletions
+1
View File
@@ -14,6 +14,7 @@ INSERT INTO "role" (name) VALUES ('schriftfuehrer');
INSERT INTO "role" (name) VALUES ('no-einschreibgebuehr');
INSERT INTO "role" (name) VALUES ('schnupper-betreuer');
INSERT INTO "role" (name) VALUES ('allow_website_login');
INSERT INTO "role" (name) VALUES ('Vereinsneuling');
INSERT INTO "user" (name, pw) VALUES('admin', '$argon2id$v=19$m=19456,t=2,p=1$dS/X5/sPEKTj4Rzs/CuvzQ$4P4NCw4Ukhv80/eQYTsarHhnw61JuL1KMx/L9dm82YM');
INSERT INTO "user_role" (user_id, role_id) VALUES(1,1);
INSERT INTO "user_role" (user_id, role_id) VALUES(1,2);
+5 -3
View File
@@ -301,8 +301,9 @@ mod test {
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
allow_guests: event.allow_guests,
};
event.update(&pool, &user, &cancel_update).await;
event.update(&pool, &user, &cancel_update).await.unwrap();
// Rower received notification
let notifications = Notification::for_user(&pool, &rower).await;
@@ -331,13 +332,14 @@ mod test {
always_show: event.always_show,
is_locked: event.is_locked,
trip_type_id: None,
allow_guests: event.allow_guests,
};
event.update(&pool, &user, &update).await;
event.update(&pool, &user, &update).await.unwrap();
assert!(Notification::for_user(&pool, &rower).await.is_empty());
assert!(Notification::for_user(&pool, &cox.user).await.is_empty());
// Cancel event again
event.update(&pool, &user, &cancel_update).await;
event.update(&pool, &user, &cancel_update).await.unwrap();
// Rower is removed if notification is accepted
assert!(event.is_rower_registered(&pool, &rower).await);
+21 -6
View File
@@ -48,6 +48,7 @@ pub struct Registration {
pub name: String,
pub registered_at: String,
pub is_guest: bool,
pub is_newbie: bool,
pub is_real_guest: bool,
}
@@ -61,7 +62,8 @@ SELECT
user_note,
user_id,
(SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at,
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'scheckbuch'))) as is_guest
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'scheckbuch'))) as is_guest,
(SELECT EXISTS (SELECT 1 FROM user_role WHERE user_role.user_id = user_trip.user_id AND user_role.role_id = (SELECT id FROM role WHERE name = 'Vereinsneuling'))) as is_newbie
FROM user_trip WHERE trip_details_id = {}
"#,trip_details_id),
)
@@ -74,6 +76,7 @@ FROM user_trip WHERE trip_details_id = {}
name: r.get::<Option<String>, usize>(0).or(r.get::<Option<String>, usize>(1)).unwrap(), //Ok, either name or user_note needs to be set
registered_at: r.get::<String,usize>(3),
is_guest: r.get::<bool, usize>(4),
is_newbie: r.get::<bool, usize>(5),
is_real_guest: r.get::<Option<i64>, usize>(2).is_none(),
})
.collect()
@@ -98,6 +101,7 @@ FROM trip WHERE planned_event_id = ?
name: r.name.unwrap(),
registered_at: r.registered_at.unwrap(),
is_guest: false,
is_newbie: false,
is_real_guest: false,
})
.collect() //Okay, as Event can only be created with proper DB backing
@@ -113,6 +117,7 @@ pub struct EventUpdate<'a> {
pub always_show: bool,
pub is_locked: bool,
pub trip_type_id: Option<i64>,
pub allow_guests: bool,
}
impl EventUpdate<'_> {
@@ -318,7 +323,17 @@ WHERE trip_details.id=?
}
//TODO: create unit test
pub async fn update(&self, db: &SqlitePool, user: &EventUser, update: &EventUpdate<'_>) {
pub async fn update(&self, db: &SqlitePool, user: &EventUser, update: &EventUpdate<'_>) -> Result<(), String> {
let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.cancelled();
if tripdetails.allow_guests && !update.allow_guests {
let rowers = Registration::all_rower(db, self.trip_details_id).await;
if rowers.iter().any(|r| r.is_newbie) {
return Err("Es sind bereits Neulinge angemeldet — 'Neulinge willkommen' kann nicht deaktiviert werden.".into());
}
}
sqlx::query!(
"UPDATE planned_event SET name = ?, planned_amount_cox = ? WHERE id = ?",
update.name,
@@ -329,16 +344,14 @@ WHERE trip_details.id=?
.await
.unwrap(); //Okay, as planned_event can only be created with proper DB backing
let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.cancelled();
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 = ?, allow_guests = ? WHERE id = ?",
update.max_people,
update.notes,
update.always_show,
update.is_locked,
update.trip_type_id,
update.allow_guests,
self.trip_details_id
)
.execute(db)
@@ -426,6 +439,8 @@ WHERE trip_details.id=?
.await;
Notification::delete_by_action(db, &format!("remove_trip_by_event:{}", self.id)).await;
}
Ok(())
}
pub async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
+14 -1
View File
@@ -48,6 +48,7 @@ pub struct TripUpdate<'a> {
pub notes: Option<&'a str>,
pub trip_type: Option<i64>, //TODO: Move to `TripType`
pub is_locked: bool,
pub allow_guests: bool,
}
impl TripUpdate<'_> {
@@ -228,6 +229,13 @@ WHERE day=?
let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
let was_already_cancelled = tripdetails.cancelled();
if tripdetails.allow_guests && !update.allow_guests {
let rowers = Registration::all_rower(db, trip_details_id).await;
if rowers.iter().any(|r| r.is_newbie) {
return Err(TripUpdateError::NeulingAlreadyRegistered);
}
}
let is_locked = if update.cancelled() {
false
} else {
@@ -235,11 +243,12 @@ WHERE day=?
};
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 = ?, allow_guests = ? WHERE id = ?",
update.max_people,
update.notes,
update.trip_type,
is_locked,
update.allow_guests,
trip_details_id
)
.execute(db)
@@ -407,6 +416,7 @@ pub enum TripUpdateError {
NotYourTrip,
TripDetailsDoesNotExist,
TripTypeNotAllowed,
NeulingAlreadyRegistered,
}
#[cfg(test)]
@@ -540,6 +550,7 @@ mod test {
notes: None,
trip_type: None,
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
@@ -568,6 +579,7 @@ mod test {
notes: None,
trip_type: Some(1),
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
@@ -596,6 +608,7 @@ mod test {
notes: None,
trip_type: None,
is_locked: false,
allow_guests: false,
};
assert!(Trip::update_own(&pool, &update).await.is_err());
assert_eq!(trip.max_people, 1);
+11
View File
@@ -528,6 +528,17 @@ impl User {
Ok(())
}
pub(crate) async fn add_vereinsneuling(
&self,
db: &SqlitePool,
updated_by: &ManageUserUser,
) -> Result<(), String> {
if let Some(vereinsneuling) = Role::find_by_name(db, "Vereinsneuling").await {
self.add_role(db, updated_by, &vereinsneuling).await?;
}
Ok(())
}
pub(crate) async fn remove_membership_pdf(&self, db: &SqlitePool, updated_by: &ManageUserUser) {
ActivityBuilder::new(&format!(
"{updated_by} hat die Beitrittserklärung vom Beutzer gelöscht."
+10 -5
View File
@@ -112,6 +112,7 @@ impl UserWithDetails {
self.roles.contains(&"Donau Linz".into())
|| self.roles.contains(&"Förderndes Mitglied".into())
|| self.roles.contains(&"scheckbuch".into())
|| self.roles.contains(&"Vereinsneuling".into())
|| self.user.name == "Externe Steuerperson"
}
}
@@ -598,18 +599,22 @@ ASKÖ Ruderverein Donau Linz", self.name),
pub async fn get_days(&self, db: &SqlitePool) -> Vec<Day> {
let mut days = Vec::new();
for i in 0..self.amount_days_to_show(db).await {
let roles = self.roles(db).await;
let is_beginner = roles.contains(&"scheckbuch".to_string())
|| roles.contains(&"Vereinsneuling".to_string());
let days_to_show = self.amount_days_to_show(db).await;
for i in 0..days_to_show {
let date = (Local::now() + chrono::Duration::days(i)).date_naive();
if self.has_role(db, "scheckbuch").await {
if is_beginner {
days.push(Day::new_guest(db, date, false).await);
} else {
days.push(Day::new(db, date, false).await);
}
}
for date in TripDetails::pinned_days(db, self.amount_days_to_show(db).await - 1).await {
if self.has_role(db, "scheckbuch").await {
for date in TripDetails::pinned_days(db, days_to_show - 1).await {
if is_beginner {
let day = Day::new_guest(db, date, true).await;
if !day.events.is_empty() {
days.push(day);
@@ -868,7 +873,7 @@ special_user!(TechUser, +"tech");
special_user!(ErgoUser, +"ergo");
special_user!(SteeringUser, +"cox", +"Bootsführer");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied", +"Vereinsneuling");
special_user!(DonauLinzUser, +"Donau Linz", +"Förderndes Mitglied", -"Unterstützend"); // TODO:
// remove ->
// RegularUser
+3
View File
@@ -99,6 +99,7 @@ impl NoMembershipUser {
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let regular = RegularUser::new(db, &self.user).await.unwrap();
regular.send_welcome_mail_to_user(db, smtp_pw).await?;
@@ -149,6 +150,7 @@ impl NoMembershipUser {
let unterstuetzend = Role::find_by_name(db, "Unterstützend").await.unwrap();
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
unterstuetzend
@@ -203,6 +205,7 @@ impl NoMembershipUser {
let foerdernd = Role::find_by_name(db, "Förderndes Mitglied").await.unwrap();
self.user.add_role(db, changed_by, &foerdernd).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
+1
View File
@@ -47,6 +47,7 @@ pub trait ClubMember {
let user = User::find_by_name(db, name).await.unwrap();
user.change_financial(db, created_by, financial).await?;
user.add_role(db, created_by, role).await?;
user.add_vereinsneuling(db, created_by).await?;
ActivityBuilder::new(&format!(
"{created_by} hat Mitglied {user} mit der Rolle {role} angelegt."
+3
View File
@@ -68,6 +68,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
// Notify
let regular = RegularUser::new(db, &self.user).await.unwrap();
@@ -123,6 +124,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let unterstuetzend = UnterstuetzendUser::new(db, &self.user).await.unwrap();
unterstuetzend
@@ -179,6 +181,7 @@ impl ScheckbuchUser {
let scheckbook = Role::find_by_name(db, "scheckbuch").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let foerdernd = FoerderndUser::new(db, &self.user).await.unwrap();
foerdernd.send_welcome_mail_to_user(db, smtp_pw).await?;
+3
View File
@@ -75,6 +75,7 @@ impl SchnupperantUser {
let regular = Role::find_by_name(db, "Donau Linz").await.unwrap();
self.user.add_role(db, changed_by, &regular).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
let participated_schnupperkurs = Role::find_by_name(db, "participated_schnupperkurs")
.await
@@ -224,6 +225,7 @@ impl SchnupperantUser {
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
self.add_role(db, changed_by, &no_einschreibgebuehr)
.await
@@ -293,6 +295,7 @@ impl SchnupperantUser {
let scheckbook = Role::find_by_name(db, "schnupperant").await.unwrap();
self.user.remove_role(db, changed_by, &scheckbook).await?;
self.user.add_role(db, changed_by, &unterstuetzend).await?;
self.user.add_vereinsneuling(db, changed_by).await?;
if let Some(no_einschreibgebuehr) = Role::find_by_name(db, "no-einschreibgebuehr").await {
self.add_role(db, changed_by, &no_einschreibgebuehr)
.await
+6 -4
View File
@@ -62,6 +62,7 @@ struct UpdateEventForm<'r> {
always_show: bool,
is_locked: bool,
trip_type: Option<i64>,
allow_guests: bool,
}
#[put("/planned-event", data = "<data>")]
@@ -78,12 +79,13 @@ async fn update(
always_show: data.always_show,
is_locked: data.is_locked,
trip_type_id: data.trip_type,
allow_guests: data.allow_guests,
};
match Event::find_by_id(db, data.id).await {
Some(planned_event) => {
planned_event.update(db, &user, &update).await;
Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet")
}
Some(planned_event) => match planned_event.update(db, &user, &update).await {
Ok(_) => Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet"),
Err(e) => Flash::error(Redirect::to("/planned"), e),
},
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),
}
}
+6
View File
@@ -52,6 +52,7 @@ struct EditTripForm<'r> {
notes: Option<&'r str>,
trip_type: Option<i64>,
is_locked: bool,
allow_guests: bool,
}
#[post("/trip/<trip_id>", data = "<data>")]
@@ -69,6 +70,7 @@ async fn update(
notes: data.notes,
trip_type: data.trip_type,
is_locked: data.is_locked,
allow_guests: data.allow_guests,
};
match Trip::update_own(db, &update).await {
Ok(_) => Flash::success(
@@ -85,6 +87,10 @@ async fn update(
Err(TripUpdateError::TripDetailsDoesNotExist) => {
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
}
Err(TripUpdateError::NeulingAlreadyRegistered) => Flash::error(
Redirect::to("/planned"),
"Es sind bereits Neulinge angemeldet — 'Neulinge willkommen' kann nicht deaktiviert werden.",
),
}
} else {
Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht")
+2
View File
@@ -1,3 +1,5 @@
INSERT INTO "role" (name) VALUES ('Vereinsneuling');
-- test user
INSERT INTO user(name) VALUES('Marie');
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz'));
+1 -1
View File
@@ -9,7 +9,7 @@
{{ macros::input(label='Startzeit', name='tripdetails.planned_starting_time', type='time', required=true) }}
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', required=true, min='0') }}
{{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='tripdetails.max_people', type='number', required=true, min='0') }}
{{ macros::checkbox(label='Scheckbuch-Anmeldungen erlauben', name='tripdetails.allow_guests') }}
{{ macros::checkbox(label='Neulinge willkommen', name='tripdetails.allow_guests') }}
{{ macros::checkbox(label='Immer anzeigen', name='always_show') }}
{{ macros::input(label='Anmerkungen', name='tripdetails.notes', type='input') }}
{{ macros::select(label='Typ', data=trip_types, name='tripdetails.trip_type', default='Reguläre Ausfahrt') }}
+1 -1
View File
@@ -4,7 +4,7 @@
<input class="day-js" type="hidden" name="day" value="" />
{{ macros::input(label='Startzeit (zB "10:00")', name='planned_starting_time', type='time', required=true) }}
{{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }}
{{ macros::checkbox(label='Scheckbuch-Anmeldungen erlauben', name='allow_guests') }}
{{ macros::checkbox(label='Neulinge willkommen', name='allow_guests') }}
{{ macros::input(label='Anmerkungen', name='notes', type='input') }}
{% if loggedin_user.allowed_to_steer %}
{{ macros::select(label='Typ', data=trip_types, name='trip_type', default='Reguläre Ausfahrt') }}
+1 -1
View File
@@ -320,7 +320,7 @@ function setChoiceByLabel(choicesInstance, label) {
{% if participants | length > 0 %}
{% for rower in participants %}
<div class="relative">
{{ rower.name }}
{{ rower.name }}{% if rower.is_newbie %} 🐣{% endif %}
{% if rower.is_guest %}<small class="text-gray-600 dark:text-gray-100">(Scheckbuch)</small>{% endif %}
{% if rower.is_real_guest %}
<small class="text-gray-600 dark:text-gray-100">(Gast)</small>
+22 -3
View File
@@ -55,6 +55,18 @@
</div>
</div>
{% endif %}
{% if "Vereinsneuling" in loggedin_user.roles %}
<div class="grid gap-3 sm:col-span-2 lg:col-span-3">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<h2 class="h2">Willkommen im Verein!</h2>
<div class="text-sm p-3">
Du siehst aktuell alle Ausfahrten, die für (Vereins-)Neulinge ausgeschrieben sind. Du kannst dich also gerne bei jeder angezeigten Ausfahrt anmelden :-)
<br /><br />
Sobald du einige Ausfahrten hinter dir hast und eine Fahrt nach Ottensheim kein Problem mehr ist, werden dir alle Ausfahrten freigeschalten. Du fühlst dich schon bereit dazu? Dann einfach kurz jemanden vom Vorstand ansprechen oder eine kurze Mail an <a href="mailto:info@rudernlinz.at" class="underline">info@rudernlinz.at</a> schreiben.
</div>
</div>
</div>
{% endif %}
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten</h1>
{% include "includes/buttons" %}
{% for day in days %}
@@ -223,6 +235,9 @@
{{ 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) }}
{% endif %}
{# --- END List Rowers --- #}
{% if event.allow_guests %}
<div class="text-primary-900 bg-primary-50 text-center p-1 mb-4">Neulinge willkommen</div>
{% endif %}
{% if "manage_events" in loggedin_user.roles %}
<form action="/planned/join/{{ event.trip_details_id }}" method="get" />
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
@@ -231,9 +246,6 @@
type="submit" />
</form>
{% endif %}
{% if event.allow_guests %}
<div class="text-primary-900 bg-primary-50 text-center p-1 mb-4">Gäste willkommen!</div>
{% endif %}
{% if "manage_events" in loggedin_user.roles %}
{# --- START Edit Form --- #}
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
@@ -250,6 +262,7 @@
{{ 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='Gesperrt', name='is_locked', id=event.id,checked=event.is_locked) }}
{{ macros::checkbox(label='Neulinge willkommen', name='allow_guests', id=event.id,checked=event.allow_guests) }}
{{ macros::select(label='Typ', name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=event.trip_type_id) }}
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=event.notes) }}
<input value="Speichern" class="btn btn-primary" type="submit" />
@@ -281,6 +294,7 @@
{{ 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='trip_type', type='hidden', value=event.trip_type_id) }}
{{ macros::input(label='', name='allow_guests', type='hidden', value=event.allow_guests) }}
<input value="Ausfahrt absagen" class="btn btn-alert" type="submit" />
</form>
</div>
@@ -363,6 +377,9 @@
{% 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) }}
{% if trip.allow_guests %}
<div class="text-primary-900 bg-primary-50 text-center p-1 mb-4">Neulinge willkommen</div>
{% endif %}
{% if trip.cox_id == loggedin_user.id %}
<form action="/planned/join/{{ trip.trip_details_id }}" method="get" />
{{ macros::input(label='Gast', class="input rounded-t", name='user_note', type='text', required=true) }}
@@ -380,6 +397,7 @@
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=trip.max_people, min=trip.rower | length) }}
{{ macros::input(label='Anmerkungen', name='notes', type='input', value=trip.notes) }}
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }}
{{ macros::checkbox(label='Neulinge willkommen', name='allow_guests', id=trip.id,checked=trip.allow_guests) }}
{% if loggedin_user.allowed_to_steer %}
{{ macros::select(label='Typ', name='trip_type', data=trip_types, default='Reguläre Ausfahrt', selected_id=trip.trip_type_id, only_ergo=not loggedin_user.allowed_to_steer) }}
{% else %}
@@ -407,6 +425,7 @@
{{ 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='trip_type', type='hidden', value=trip.trip_type_id) }}
{{ macros::input(label='', name='allow_guests', type='hidden', value=trip.allow_guests) }}
<input value="Ausfahrt absagen" class="btn btn-alert" type="submit" />
</form>
</div>