10 Commits

Author SHA1 Message Date
Marie Birner
8500ba826f [TASK] try to include boat and shipmaster in update popup
Some checks failed
CI/CD Pipeline / test (push) Failing after 22m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-05 21:41:52 +02:00
Marie Birner
af2e7cb557 Merge commit '17513bbc386e849962157517d9a4496870b1a064' into single-user-edit-page
Some checks failed
CI/CD Pipeline / test (push) Failing after 22m54s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2025-05-05 21:14:41 +02:00
Marie Birner
81b99ef414 [TASK] edit form on logbook fixes #635 2025-05-05 21:14:23 +02:00
17513bbc38 give frontend stuff to be able to update logbook entriese
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 21:11:41 +02:00
c1cecf3b20 don't panic on 'external cox'
Some checks failed
CI/CD Pipeline / test (push) Successful in 16m19s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 20:46:41 +02:00
8e40e563c6 even less clutter!
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 20:44:00 +02:00
Marie Birner
bb78441cc4 Merge branch 'single-user-edit-page' of https://git.hofer.link/Ruderverein-Donau-Linz/rowt into single-user-edit-page
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 20:37:45 +02:00
Marie Birner
abcf46281b [TASK] style detail view user 2025-05-05 20:37:29 +02:00
c460494be8 Merge branch 'single-user-edit-page' of ssh://git.hofer.link:2222/Ruderverein-Donau-Linz/rowt into single-user-edit-page
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2025-05-05 20:37:20 +02:00
e5560ba536 don't clutter acitvities too much 2025-05-05 20:37:12 +02:00
7 changed files with 117 additions and 73 deletions

View File

@@ -11,6 +11,10 @@
@apply text-white hover:text-primary-100 underline;
}
&-black {
@apply text-black hover:text-primary-950 dark:text-white hover:dark:text-primary-300 underline;
}
&-no-underline {
@apply no-underline;
}

View File

@@ -414,12 +414,14 @@ impl User {
.await
.unwrap();
ActivityBuilder::new(&format!(
"{updated_by} hat die Rolle {role} von {self} entfernt."
))
.relevant_for_user(self)
.save(db)
.await;
if !role.hide_in_lists && role.cluster.is_none() {
ActivityBuilder::new(&format!(
"{updated_by} hat die Rolle {role} von {self} entfernt."
))
.relevant_for_user(self)
.save(db)
.await;
}
Ok(())
}
@@ -499,7 +501,7 @@ impl User {
)
})?;
if !role.hide_in_lists {
if !role.hide_in_lists && role.cluster.is_none() {
ActivityBuilder::new(&format!(
"{updated_by} hat die Rolle '{role}' dem Benutzer {self} hinzugefügt."
))

View File

@@ -1,21 +1,20 @@
use std::{fmt::Display, ops::DerefMut};
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
use chrono::{Datelike, Local, NaiveDate};
use log::info;
use rocket::async_trait;
use rocket::{
Request,
http::{Cookie, Status},
request::{FromRequest, Outcome},
time::{Duration, OffsetDateTime},
Request,
};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::activity::ActivityBuilder;
use super::{
Day,
log::Log,
logbook::Logbook,
mail::Mail,
@@ -24,6 +23,7 @@ use super::{
role::Role,
stat::Stat,
tripdetails::TripDetails,
Day,
};
use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD;
use scheckbuch::ScheckbuchUser;
@@ -512,7 +512,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
.save(db)
.await;
return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has
//been deleted
//been deleted
}
if let Some(user_pw) = user.pw.as_ref() {
@@ -622,9 +622,9 @@ ASKÖ Ruderverein Donau Linz", self.name),
pub(crate) async fn amount_days_to_show(&self, db: &SqlitePool) -> i64 {
if self.allowed_to_steer(db).await {
let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap(); //Ok,
//december
//has 31
//days
//december
//has 31
//days
let days_left_in_year = end_of_year
.signed_duration_since(Local::now().date_naive())
.num_days()
@@ -633,9 +633,9 @@ ASKÖ Ruderverein Donau Linz", self.name),
if days_left_in_year <= 31 {
let end_of_next_year =
NaiveDate::from_ymd_opt(Local::now().year() + 1, 12, 31).unwrap(); //Ok,
//december
//has 31
//days
//december
//has 31
//days
end_of_next_year
.signed_duration_since(Local::now().date_naive())
.num_days()
@@ -867,8 +867,8 @@ special_user!(SteeringUser, +"cox", +"Bootsführer");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO:
// remove ->
// RegularUser
// remove ->
// RegularUser
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
special_user!(VorstandUser, +"admin", +"Vorstand");
special_user!(EventUser, +"manage_events");
@@ -982,21 +982,17 @@ mod test {
#[sqlx::test]
fn wrong_pw() {
let pool = testdb!();
assert!(
User::login(&pool, "admin".into(), "admi".into())
.await
.is_err()
);
assert!(User::login(&pool, "admin".into(), "admi".into())
.await
.is_err());
}
#[sqlx::test]
fn wrong_username() {
let pool = testdb!();
assert!(
User::login(&pool, "admi".into(), "admin".into())
.await
.is_err()
);
assert!(User::login(&pool, "admi".into(), "admin".into())
.await
.is_err());
}
#[sqlx::test]
@@ -1015,11 +1011,9 @@ mod test {
let pool = testdb!();
let user = User::find_by_id(&pool, 1).await.unwrap();
assert!(
User::login(&pool, "admin".into(), "abc".into())
.await
.is_err()
);
assert!(User::login(&pool, "admin".into(), "abc".into())
.await
.is_err());
user.update_pw(&pool, "abc".into()).await;

View File

@@ -7,11 +7,11 @@ use crate::{
mail::valid_mails,
role::Role,
user::{
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member,
regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser,
schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser,
AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails,
UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser,
},
},
tera::Config,
@@ -19,7 +19,6 @@ use crate::{
use chrono::NaiveDate;
use futures::future::join_all;
use rocket::{
FromForm, Request, Route, State,
form::Form,
fs::TempFile,
get,
@@ -27,9 +26,9 @@ use rocket::{
post,
request::{FlashMessage, FromRequest, Outcome},
response::{Flash, Redirect},
routes,
routes, FromForm, Request, Route, State,
};
use rocket_dyn_templates::{Template, tera::Context};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
// Custom request guard to extract the Referer header
@@ -133,6 +132,12 @@ async fn view(
format!("User mit ID {} gibts ned", user),
));
};
if user.name == "Externe Steuerperson" {
return Err(Flash::error(
Redirect::to("/admin/user"),
"Diese besondere Person kannst du dir leider nicht anschauen, mein lieber neugieriger Ruderant!"
));
}
let member = Member::from(db, user.clone()).await;
let fee = user.fee(db).await;

View File

@@ -1,7 +1,6 @@
use std::net::IpAddr;
use rocket::{
Request, Route, State,
form::Form,
get,
http::{Cookie, CookieJar},
@@ -10,8 +9,9 @@ use rocket::{
response::{Flash, Redirect},
routes,
time::{Duration, OffsetDateTime},
Request, Route, State,
};
use rocket_dyn_templates::{Template, context};
use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool;
use tera::Context;
@@ -110,10 +110,13 @@ async fn index(
#[get("/show", rank = 3)]
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
let logs = Logbook::completed(db).await;
let boats = Boat::all(db).await;
let users = User::all(db).await;
let logtypes = LogType::all(db).await;
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
context!(logs, boats, users, logtypes, loggedin_user: &UserWithDetails::from_user(user.into_inner(), db).await),
)
}
@@ -582,7 +585,7 @@ mod test {
use sqlx::SqlitePool;
use crate::model::logbook::Logbook;
use crate::tera::{User, log::Boat};
use crate::tera::{log::Boat, User};
use crate::testdb;
#[sqlx::test]

View File

@@ -7,8 +7,8 @@
<a href="/admin/user" class="link link-primary link-no-underline">&larr; Userverwaltung</a>
{% endif %}
<h1 class="h1">{{ user.name }}</h1>
<div class="grid sm:grid-cols-2 gap-3">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="grid sm:grid-cols-2 gap-8 my-8">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">
Grunddaten
<br />
@@ -53,7 +53,7 @@
</div>
</div>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">
Mitgliedschaft
<br />
@@ -285,7 +285,7 @@
</div>
</div>
{% if is_clubmember %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">Rollen</h2>
<div>
<ul class="divide-y divide-gray-200 dark:divide-primary-60 w-full">
@@ -363,7 +363,7 @@
</div>
{% endif %}
{% if supposed_to_pay %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">💸-Beitrag</h2>
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
<div class="py-3">
@@ -400,7 +400,7 @@
</div>
</div>
{% endif %}
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">Aktivitäten</h2>
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
<div class="py-3">
@@ -414,13 +414,13 @@
</div>
</div>
</div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5">
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow">
<h2 class="h2">Ergo-Challenge</h2>
<div class="mx-3 divide-y divide-gray-200 dark:divide-primary-600">
<div class="py-3">
{{ macros::input(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
{{ macros::input(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
{{ macros::input(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
<div class="mx-3">
<div class="grid gap-3 pb-3 mt-3">
{{ macros::inputgroup(label='DOB', name='dob', type="text", value=user.dob, readonly=allowed_to_edit == false) }}
{{ macros::inputgroup(label='Weight (kg)', name='weight', type="text", value=user.weight, readonly=allowed_to_edit == false) }}
{{ macros::inputgroup(label='Sex', name='sex', type="text", value=user.sex, readonly=allowed_to_edit == false) }}
</div>
</div>
</div>

View File

@@ -183,8 +183,6 @@
<div class="border-t bg-white dark:bg-primary-900 py-3 px-4 relative"
data-filterable="true"
data-filter="{{ log.boat.name }} {% for rower in log.rowers %}{{ rower.name }}{% endfor %}">
<details>
<summary style="list-style: none;">
{% if log.logtype and not hide_type %}
<div class="absolute top-0 right-0 bg-primary-100 rounded-bl-md text-primary-950 text-xs w-32 px-2 py-1 text-center font-bold">
{% if log.logtype == 1 %}
@@ -199,7 +197,15 @@
</div>
{% endif %}
<div {% if log.logtype %}class="mt-4 sm:mt-0"{% endif %}>
<strong class="text-black dark:text-white">{{ log.boat.name }}</strong>
{% if allowed_to_edit %}
<a href="#"
onclick="document.getElementById('change-{{ log.id }}').showModal()"
class="link link-black font-bold">{{ log.boat.name }}</a>
{% else %}
<strong class="text-black dark:text-white">
{{ log.boat.name }}
</strong>
{% endif %}
<small class="text-gray-600 dark:text-gray-100">({{ log.shipmaster_user.name -}}
{% if log.shipmaster_only_steering %}
- handgesteuert
@@ -252,35 +258,65 @@
{% endif %}
{% endif %}
</div>
</summary>
{% if allowed_to_edit %}
<form action="/log/update" method="post">
{% if allowed_to_edit %}
<dialog id="change-{{ log.id }}"
class="max-w-screen-sm w-full dark:bg-primary-900 dark:text-white rounded-md"
onclick="document.getElementById('change-{{ log.id }}').close()">
<div onclick="event.stopPropagation();" class="p-3">
<button type="button"
onclick="document.getElementById('change-{{ log.id }}').close()"
title="Schließen"
class="sidebar-close border-0 bg-primary-100 focus:bg-primary-50 text-black flex items-center justify-center transform rotate-45 absolute right-0 mr-3">
<svg class="inline h-5 w-5"
width="16"
height="16"
fill="currentColor"
viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"></path>
</svg>
</button>
<div class="mt-8">
<h2 class="h3">Eintrag '{{ log.boat.name }}' ändern </h2>
<p class="text-center mb-3">ID: {{ log.id }}</p>
<form action="/log/update" method="post" class="grid gap-3">
<input type="hidden" name="id" value="{{ log.id }}" />
<input type="hidden" name="boat_id" value="{{ log.boat_id }}" />
<input type="hidden" name="shipmaster" value="{{ log.shipmaster }}" />
<input type="hidden"
name="steering_person"
value="{{ log.steering_person }}" />
Handgesteuert:
<input type="checkbox"
name="shipmaster_only_steering"
{% if log.shipmaster_only_steering %}checked="checked"{% endif %} />
<input type="datetime-local" name="departure" value="{{ log.departure }}" />
<input type="datetime-local" name="arrival" value="{{ log.arrival }}" />
{{ macros::select(label="Boot", data=boats, name="boat_id", id="boat_id{{ log.id }}", selected_id=log.boat.id ,display=["name", " (","amount_seats", " x)"]) }}
{{ macros::select(label="Schiffsführer", data=log.rowers, name="shipmaster", id="shipmaster{{ log.id }}", selected_id=log.shipmaster_user.id) }}
{{ macros::checkbox(label='Handgesteuert', name='shipmaster_only_steering', id=log.shipmaster_only_steering,checked=log.shipmaster_only_steering) }}
<div>
<label for="departure" class=" text-sm text-gray-600 dark:text-white ">
Abfahrt
</label>
<input type="datetime-local" class="input rounded-md" name="departure" value="{{ log.departure }}" />
</div>
<div>
<label for="arrival" class=" text-sm text-gray-600 dark:text-white ">
Ankunft
</label>
<input type="datetime-local" class="input rounded-md" name="arrival" value="{{ log.arrival }}" />
</div>
<input type="hidden" name="destination" value="{{ log.destination }}" />
<input type="hidden" name="distance_in_km" value="{{ log.distance_in_km }}" />
<input type="hidden" name="comments" value="{{ log.comments }}" />
<input type="hidden" name="logtype" value="{{ log.logtype }}" />
<input type="submit" value="Updaten" />
<input type="submit" class="btn btn-primary" value="Updaten" />
</form>
<a href="/log/{{ log.id }}/delete"
class="w-28 btn btn-alert"
class="w-28 btn btn-alert mt-3"
onclick="return confirm('Willst du diesen Logbucheintrag wirklich löschen?');">
{% include "includes/delete-icon" %}
Löschen
</a>
{% endif %}
</details>
</div>
</div>
</dialog>
{% endif %}
</div>
{% endmacro show_old %}
{% macro home(log) %}