From 7c71ce59bd79633c217f6bb0ec9226b68ffc6ce6 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 17 Apr 2024 13:51:47 +0200 Subject: [PATCH 01/15] show notification badge in menu --- src/model/user.rs | 17 +++++++++++++++-- src/tera/admin/boat.rs | 4 ++-- src/tera/admin/mail.rs | 4 ++-- src/tera/admin/notification.rs | 4 ++-- src/tera/admin/schnupper.rs | 8 ++++---- src/tera/admin/user.rs | 20 +++++++++++++------- src/tera/board/boathouse.rs | 4 ++-- src/tera/boatdamage.rs | 4 ++-- src/tera/boatreservation.rs | 4 ++-- src/tera/ergo.rs | 9 ++++++--- src/tera/log.rs | 10 ++++++---- src/tera/mod.rs | 12 +++++++++--- src/tera/planned.rs | 7 +++++-- src/tera/stat.rs | 6 +++--- templates/admin/mail.html.tera | 10 +++++----- templates/base.html.tera | 14 +++++++++++--- templates/includes/macros.html.tera | 12 ++++++++++++ 17 files changed, 101 insertions(+), 48 deletions(-) diff --git a/src/model/user.rs b/src/model/user.rs index 86e311a..83db24c 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -47,16 +47,18 @@ pub struct User { } #[derive(Debug, Serialize, Deserialize)] -pub struct UserWithRoles { +pub struct UserWithRolesAndNotificationCount { #[serde(flatten)] pub user: User, + pub amount_unread_notifications: i32, pub roles: Vec, } -impl UserWithRoles { +impl UserWithRolesAndNotificationCount { pub async fn from_user(user: User, db: &SqlitePool) -> Self { Self { roles: user.roles(db).await, + amount_unread_notifications: user.amount_unread_notifications(db).await, user, } } @@ -237,6 +239,17 @@ impl User { .count } + pub async fn amount_unread_notifications(&self, db: &SqlitePool) -> i32 { + sqlx::query!( + "SELECT COUNT(*) as count FROM notification WHERE user_id = ? AND read_at IS NULL", + self.id + ) + .fetch_one(db) + .await + .unwrap() + .count + } + pub async fn has_role(&self, db: &SqlitePool, role: &str) -> bool { if sqlx::query!( "SELECT * FROM user_role WHERE user_id=? AND role_id = (SELECT id FROM role WHERE name = ?)", diff --git a/src/tera/admin/boat.rs b/src/tera/admin/boat.rs index d8776f6..8af2cca 100644 --- a/src/tera/admin/boat.rs +++ b/src/tera/admin/boat.rs @@ -1,7 +1,7 @@ use crate::model::{ boat::{Boat, BoatToAdd, BoatToUpdate}, location::Location, - user::{AdminUser, User, UserWithRoles}, + user::{AdminUser, User, UserWithRolesAndNotificationCount}, }; use rocket::{ form::Form, @@ -32,7 +32,7 @@ async fn index( context.insert("users", &users); context.insert( "loggedin_user", - &UserWithRoles::from_user(admin.user, db).await, + &UserWithRolesAndNotificationCount::from_user(admin.user, db).await, ); Template::render("admin/boat/index", context.into_json()) diff --git a/src/tera/admin/mail.rs b/src/tera/admin/mail.rs index 2afcd3a..1f6f11c 100644 --- a/src/tera/admin/mail.rs +++ b/src/tera/admin/mail.rs @@ -10,7 +10,7 @@ use crate::model::log::Log; use crate::model::mail::Mail; use crate::model::role::Role; use crate::model::user::AdminUser; -use crate::model::user::UserWithRoles; +use crate::model::user::UserWithRolesAndNotificationCount; use crate::tera::Config; #[get("/mail")] @@ -27,7 +27,7 @@ async fn index( context.insert( "loggedin_user", - &UserWithRoles::from_user(admin.user, db).await, + &UserWithRolesAndNotificationCount::from_user(admin.user, db).await, ); context.insert("roles", &roles); diff --git a/src/tera/admin/notification.rs b/src/tera/admin/notification.rs index 2e36c51..0e7ab9a 100644 --- a/src/tera/admin/notification.rs +++ b/src/tera/admin/notification.rs @@ -2,7 +2,7 @@ use crate::model::{ log::Log, notification::Notification, role::Role, - user::{AdminUser, User, UserWithRoles}, + user::{AdminUser, User, UserWithRolesAndNotificationCount}, }; use rocket::{ form::Form, @@ -26,7 +26,7 @@ async fn index( } context.insert( "loggedin_user", - &UserWithRoles::from_user(user.user, db).await, + &UserWithRolesAndNotificationCount::from_user(user.user, db).await, ); context.insert("roles", &Role::all(db).await); diff --git a/src/tera/admin/schnupper.rs b/src/tera/admin/schnupper.rs index 41d536d..f08abb3 100644 --- a/src/tera/admin/schnupper.rs +++ b/src/tera/admin/schnupper.rs @@ -1,6 +1,6 @@ use crate::model::{ role::Role, - user::{SchnupperBetreuerUser, User, UserWithRoles}, + user::{SchnupperBetreuerUser, User, UserWithRolesAndNotificationCount}, }; use futures::future::join_all; use rocket::{ @@ -38,9 +38,9 @@ async fn index( let user_futures: Vec<_> = User::all_with_role(db, &schnupperant) .await .into_iter() - .map(|u| async move { UserWithRoles::from_user(u, db).await }) + .map(|u| async move { UserWithRolesAndNotificationCount::from_user(u, db).await }) .collect(); - let users: Vec = join_all(user_futures).await; + let users: Vec = join_all(user_futures).await; let mut context = Context::new(); if let Some(msg) = flash { @@ -49,7 +49,7 @@ async fn index( context.insert("schnupperanten", &users); context.insert( "loggedin_user", - &UserWithRoles::from_user(user.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, ); Template::render("admin/schnupper/index", context.into_json()) diff --git a/src/tera/admin/user.rs b/src/tera/admin/user.rs index 30c63b7..e5d9781 100644 --- a/src/tera/admin/user.rs +++ b/src/tera/admin/user.rs @@ -6,8 +6,8 @@ use crate::model::{ logbook::Logbook, role::Role, user::{ - AdminUser, User, UserWithMembershipPdf, UserWithRoles, UserWithRolesAndMembershipPdf, - VorstandUser, + AdminUser, User, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, + UserWithRolesAndNotificationCount, VorstandUser, }, }; use futures::future::join_all; @@ -67,7 +67,10 @@ async fn index( context.insert("users", &users); context.insert("roles", &roles); context.insert("families", &families); - context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await); + context.insert( + "loggedin_user", + &UserWithRolesAndNotificationCount::from_user(user, db).await, + ); Template::render("admin/user/index", context.into_json()) } @@ -99,7 +102,10 @@ async fn index_admin( context.insert("users", &users); context.insert("roles", &roles); context.insert("families", &families); - context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await); + context.insert( + "loggedin_user", + &UserWithRolesAndNotificationCount::from_user(user, db).await, + ); Template::render("admin/user/index", context.into_json()) } @@ -127,7 +133,7 @@ async fn fees( } context.insert( "loggedin_user", - &UserWithRoles::from_user(admin.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(admin.into(), db).await, ); Template::render("admin/user/fees", context.into_json()) @@ -147,7 +153,7 @@ async fn scheckbuch( for s in scheckbooks { scheckbooks_with_roles.push(( Logbook::completed_with_user(db, &s).await, - UserWithRoles::from_user(s, db).await, + UserWithRolesAndNotificationCount::from_user(s, db).await, )) } @@ -158,7 +164,7 @@ async fn scheckbuch( } context.insert( "loggedin_user", - &UserWithRoles::from_user(user.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, ); Template::render("admin/user/scheckbuch", context.into_json()) diff --git a/src/tera/board/boathouse.rs b/src/tera/board/boathouse.rs index 266f81a..fbaa16c 100644 --- a/src/tera/board/boathouse.rs +++ b/src/tera/board/boathouse.rs @@ -1,7 +1,7 @@ use crate::model::{ boat::Boat, boathouse::Boathouse, - user::{AdminUser, UserWithRoles, VorstandUser}, + user::{AdminUser, UserWithRolesAndNotificationCount, VorstandUser}, }; use rocket::{ form::Form, @@ -39,7 +39,7 @@ async fn index( context.insert( "loggedin_user", - &UserWithRoles::from_user(admin.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(admin.into(), db).await, ); Template::render("board/boathouse", context.into_json()) diff --git a/src/tera/boatdamage.rs b/src/tera/boatdamage.rs index 39088af..0fca62b 100644 --- a/src/tera/boatdamage.rs +++ b/src/tera/boatdamage.rs @@ -13,7 +13,7 @@ use crate::{ model::{ boat::Boat, boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified}, - user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRoles}, + user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRolesAndNotificationCount}, }, tera::log::KioskCookie, }; @@ -59,7 +59,7 @@ async fn index( context.insert("boats", &boats); context.insert( "loggedin_user", - &UserWithRoles::from_user(user.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, ); Template::render("boatdamages", context.into_json()) diff --git a/src/tera/boatreservation.rs b/src/tera/boatreservation.rs index 0cc5574..06d42b3 100644 --- a/src/tera/boatreservation.rs +++ b/src/tera/boatreservation.rs @@ -14,7 +14,7 @@ use crate::{ model::{ boat::Boat, boatreservation::{BoatReservation, BoatReservationToAdd}, - user::{DonauLinzUser, User, UserWithRoles}, + user::{DonauLinzUser, User, UserWithRolesAndNotificationCount}, }, tera::log::KioskCookie, }; @@ -74,7 +74,7 @@ async fn index( context.insert("user", &User::all(db).await); context.insert( "loggedin_user", - &UserWithRoles::from_user(user.into(), db).await, + &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, ); Template::render("boatreservations", context.into_json()) diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index 8818d6b..c419781 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -18,7 +18,7 @@ use tera::Context; use crate::model::{ log::Log, - user::{AdminUser, User, UserWithRoles}, + user::{AdminUser, User, UserWithRolesAndNotificationCount}, }; #[derive(Serialize)] @@ -51,7 +51,7 @@ async fn send(db: &State, _user: AdminUser) -> Template { Template::render( "ergo.final", - context!(loggedin_user: &UserWithRoles::from_user(_user.user, db).await, thirty, dozen), + context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(_user.user, db).await, thirty, dozen), ) } @@ -120,7 +120,10 @@ async fn index(db: &State, user: User, flash: Option, user: DonauLinzUser) -> Template { Template::render( "log.completed", - context!(logs, loggedin_user: &UserWithRoles::from_user(user.into(), db).await), + context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await), ) } @@ -108,7 +110,7 @@ async fn show_for_year(db: &State, user: AdminUser, year: i32) -> Te Template::render( "log.completed", - context!(logs, loggedin_user: &UserWithRoles::from_user(user.user, db).await), + context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.user, db).await), ) } diff --git a/src/tera/mod.rs b/src/tera/mod.rs index 5f89c2b..fece01b 100644 --- a/src/tera/mod.rs +++ b/src/tera/mod.rs @@ -23,7 +23,7 @@ use tera::Context; use crate::model::{ notification::Notification, role::Role, - user::{User, UserWithRoles}, + user::{User, UserWithRolesAndNotificationCount}, }; pub(crate) mod admin; @@ -53,7 +53,10 @@ async fn index(db: &State, user: User, flash: Option, user: User, flash: Option, user: DonauLinzUser) -> Template { Template::render( "stat.boats", - context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, kiosk), + context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, kiosk), ) } @@ -38,7 +38,7 @@ async fn index(db: &State, user: DonauLinzUser, year: Option) - Template::render( "stat.people", - context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km), + context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km), ) } diff --git a/templates/admin/mail.html.tera b/templates/admin/mail.html.tera index 8f54cd1..6c97d4c 100644 --- a/templates/admin/mail.html.tera +++ b/templates/admin/mail.html.tera @@ -8,16 +8,16 @@
-- 2.45.2 From 42a3addd9a5d0f60263dc4f10a8ebca1dacedb7e Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 17 Apr 2024 13:57:24 +0200 Subject: [PATCH 02/15] make it more clear which action is required if notifications are present --- templates/index.html.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/index.html.tera b/templates/index.html.tera index 8899e66..6392869 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -27,7 +27,7 @@
{% if not notification.read_at %} - -- 2.45.2 From ff27eb5eeddc1d2b395751ae0445aedf67a26c43 Mon Sep 17 00:00:00 2001 From: philipp Date: Thu, 18 Apr 2024 21:17:26 +0200 Subject: [PATCH 03/15] improve error msg if event is locked --- src/tera/planned.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tera/planned.rs b/src/tera/planned.rs index 785b9b3..c189d79 100644 --- a/src/tera/planned.rs +++ b/src/tera/planned.rs @@ -107,7 +107,7 @@ async fn join( ), Err(UserTripError::DetailsLocked) => Flash::error( Redirect::to("/planned"), - "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.", + "Die Boote sind bereits zugeteilt. Bitte wende dich an die zuständige Person oder einen der angemeldeten Steuerleute (siehe Nummern in der Signalgruppe), wenn du nachfragen möchtest, ob doch noch Plätze verfügbar sind.", ), } } -- 2.45.2 From 96c07c0eb39bfda4fd1065d175bdb1d98cfdf78c Mon Sep 17 00:00:00 2001 From: philipp Date: Fri, 19 Apr 2024 08:45:56 +0200 Subject: [PATCH 04/15] improve error msg if event is locked --- src/tera/cox.rs | 4 ++-- src/tera/planned.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tera/cox.rs b/src/tera/cox.rs index 0a04e8e..cc31580 100644 --- a/src/tera/cox.rs +++ b/src/tera/cox.rs @@ -105,7 +105,7 @@ async fn join(db: &State, planned_event_id: i64, cox: CoxUser) -> Fl "Du hast dich bereits als Ruderer angemeldet!", ), Err(CoxHelpError::DetailsLocked) => { - Flash::error(Redirect::to("/planned"), "Boot ist bereits eingeteilt.") + Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du noch steuern möchtest, frag bitte bei einer bereits angemeldeten Steuerperson nach, ob das noch möglich ist.") } } } else { @@ -151,7 +151,7 @@ async fn remove(db: &State, planned_event_id: i64, cox: CoxUser) -> Flash::success(Redirect::to("/planned"), "Erfolgreich abgemeldet!") } Err(TripHelpDeleteError::DetailsLocked) => { - Flash::error(Redirect::to("/planned"), "Boot bereits eingeteilt") + Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht steuern kannst, melde dich bitte unbedingt schnellstmöglich bei einer anderen Steuerperson!") } Err(TripHelpDeleteError::CoxNotHelping) => { Flash::error(Redirect::to("/planned"), "Steuermann hilft nicht aus...") diff --git a/src/tera/planned.rs b/src/tera/planned.rs index c189d79..5ee1242 100644 --- a/src/tera/planned.rs +++ b/src/tera/planned.rs @@ -107,7 +107,7 @@ async fn join( ), Err(UserTripError::DetailsLocked) => Flash::error( Redirect::to("/planned"), - "Die Boote sind bereits zugeteilt. Bitte wende dich an die zuständige Person oder einen der angemeldeten Steuerleute (siehe Nummern in der Signalgruppe), wenn du nachfragen möchtest, ob doch noch Plätze verfügbar sind.", + "Die Bootseinteilung wurde bereits gemacht. Wenn du noch mitrudern möchtest, frag bitte bei einer angemeldeten Steuerperson nach, ob das noch möglich ist.", ), } } @@ -148,7 +148,7 @@ async fn remove_guest( ) .await; - Flash::error(Redirect::to("/planned"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.") + Flash::error(Redirect::to("/planned"), "Die Bootseinteilung wurde bereits gemacht. Wenn du doch nicht mitrudern kannst, melde dich bitte unbedingt schnellstmöglich bei einer angemeldeten Steuerperson!") } Err(UserTripDeleteError::GuestNotParticipating) => { Flash::error(Redirect::to("/planned"), "Gast nicht angemeldet.") -- 2.45.2 From bc2790fd4d477610e909a1f8346c9e4f718c4140 Mon Sep 17 00:00:00 2001 From: philipp Date: Fri, 19 Apr 2024 09:16:55 +0200 Subject: [PATCH 05/15] cleaner logs --- src/model/logbook.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/logbook.rs b/src/model/logbook.rs index 9ccae27..5d83f12 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -591,7 +591,7 @@ ORDER BY departure DESC } pub async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), LogbookDeleteError> { - Log::create(db, format!("{user:?} deleted trip: {self:?}")).await; + Log::create(db, format!("{} deleted trip: {self:?}", user.name)).await; if user.has_role(db, "admin").await || user.has_role(db, "Vorstand").await -- 2.45.2 From 30756ad4aa844ce33923540e16c4251051c20af0 Mon Sep 17 00:00:00 2001 From: Marie Birner Date: Fri, 19 Apr 2024 11:09:31 +0200 Subject: [PATCH 06/15] [TASK] improve styling --- frontend/scss/app.scss | 1 + frontend/scss/components/_notification.scss | 5 ++++ templates/includes/macros.html.tera | 27 ++++++++++++--------- 3 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 frontend/scss/components/_notification.scss diff --git a/frontend/scss/app.scss b/frontend/scss/app.scss index 770dc11..02e94d7 100644 --- a/frontend/scss/app.scss +++ b/frontend/scss/app.scss @@ -13,3 +13,4 @@ @import 'components/search'; @import 'components/important'; @import 'components/searchable-table'; +@import 'components/notification'; diff --git a/frontend/scss/components/_notification.scss b/frontend/scss/components/_notification.scss new file mode 100644 index 0000000..0532299 --- /dev/null +++ b/frontend/scss/components/_notification.scss @@ -0,0 +1,5 @@ +.notification { + right: -.2rem; + top: -.1rem; + font-size: .5rem; +} diff --git a/templates/includes/macros.html.tera b/templates/includes/macros.html.tera index ba3c29c..30283e3 100644 --- a/templates/includes/macros.html.tera +++ b/templates/includes/macros.html.tera @@ -28,20 +28,25 @@
-- 2.45.2 From c528b8831ae92b9c82bb7def666ee83852b985d6 Mon Sep 17 00:00:00 2001 From: Marie Birner Date: Fri, 19 Apr 2024 11:25:25 +0200 Subject: [PATCH 07/15] [TASK] finalize notification styling --- templates/includes/macros.html.tera | 38 ++++++++++++++--------------- templates/index.html.tera | 10 ++++---- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/templates/includes/macros.html.tera b/templates/includes/macros.html.tera index 30283e3..59660f2 100644 --- a/templates/includes/macros.html.tera +++ b/templates/includes/macros.html.tera @@ -28,29 +28,29 @@
-
+
+ {% if loggedin_user.amount_unread_notifications > 0 %} + + + + {{ loggedin_user.amount_unread_notifications }} + + + {% endif %} -

Ruderassistent

+

Ruderassistent

-