Merge pull request 'notification-badge' (#400) from notification-badge into staging
Reviewed-on: #400
This commit is contained in:
commit
aecfb27d6e
@ -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<String>,
|
||||
}
|
||||
|
||||
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 = ?)",
|
||||
|
@ -2,7 +2,7 @@ use crate::model::{
|
||||
boat::{Boat, BoatToAdd, BoatToUpdate},
|
||||
location::Location,
|
||||
log::Log,
|
||||
user::{AdminUser, User, UserWithRoles},
|
||||
user::{AdminUser, User, UserWithRolesAndNotificationCount},
|
||||
};
|
||||
use rocket::{
|
||||
form::Form,
|
||||
@ -33,7 +33,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())
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<UserWithRoles> = join_all(user_futures).await;
|
||||
let users: Vec<UserWithRolesAndNotificationCount> = 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())
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
|
@ -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<SqlitePool>, _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<SqlitePool>, user: User, flash: Option<FlashMessage<'_
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRolesAndNotificationCount::from_user(user, db).await,
|
||||
);
|
||||
context.insert("users", &users);
|
||||
context.insert("thirty", &thirty);
|
||||
context.insert("dozen", &dozen);
|
||||
|
@ -24,7 +24,9 @@ use crate::model::{
|
||||
LogbookUpdateError,
|
||||
},
|
||||
logtype::LogType,
|
||||
user::{AdminUser, DonauLinzUser, User, UserWithRoles, UserWithWaterStatus},
|
||||
user::{
|
||||
AdminUser, DonauLinzUser, User, UserWithRolesAndNotificationCount, UserWithWaterStatus,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct KioskCookie(String);
|
||||
@ -84,7 +86,7 @@ async fn index(
|
||||
context.insert("logtypes", &logtypes);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(user.into(), db).await,
|
||||
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
|
||||
);
|
||||
context.insert("on_water", &on_water);
|
||||
context.insert("distances", &distances);
|
||||
@ -98,7 +100,7 @@ async fn show(db: &State<SqlitePool>, 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<SqlitePool>, 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),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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<SqlitePool>, user: User, flash: Option<FlashMessage<'_
|
||||
}
|
||||
|
||||
context.insert("notifications", &Notification::for_user(db, &user).await);
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRolesAndNotificationCount::from_user(user, db).await,
|
||||
);
|
||||
Template::render("index", context.into_json())
|
||||
}
|
||||
|
||||
@ -75,7 +78,10 @@ async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage
|
||||
context.insert("coxes", &coxes);
|
||||
context.insert("bootskundige", &bootskundige);
|
||||
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRolesAndNotificationCount::from_user(user, db).await,
|
||||
);
|
||||
Template::render("steering", context.into_json())
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ use crate::model::{
|
||||
logbook::Logbook,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{AllowedForPlannedTripsUser, User, UserWithRoles},
|
||||
user::{AllowedForPlannedTripsUser, User, UserWithRolesAndNotificationCount},
|
||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||
};
|
||||
|
||||
@ -44,7 +44,10 @@ async fn index(
|
||||
}
|
||||
|
||||
context.insert("fee", &user.fee(db).await);
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRolesAndNotificationCount::from_user(user, db).await,
|
||||
);
|
||||
context.insert("days", &days);
|
||||
Template::render("planned", context.into_json())
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{
|
||||
stat::{self, BoatStat, Stat},
|
||||
user::{DonauLinzUser, UserWithRoles},
|
||||
user::{DonauLinzUser, UserWithRolesAndNotificationCount},
|
||||
};
|
||||
|
||||
use super::log::KioskCookie;
|
||||
@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, 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<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -
|
||||
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,18 @@
|
||||
<a href="/">
|
||||
Hü
|
||||
{{ loggedin_user.name }}
|
||||
{% if loggedin_user.amount_unread_notifications > 0 %}
|
||||
<span class="bg-red-100 text-red-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-red-900 dark:text-red-300 inline-flex items-center">
|
||||
<svg class="w-3 h-3 me-1.5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20">
|
||||
<path d="M1.5 8.67v8.58a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V8.67l-8.928 5.493a3 3 0 0 1-3.144 0L1.5 8.67Z" />
|
||||
<path d="M22.5 6.908V6.75a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3v.158l9.714 5.978a1.5 1.5 0 0 0 1.572 0L22.5 6.908Z" />
|
||||
</svg>
|
||||
{{ loggedin_user.amount_unread_notifications }}</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<div>
|
||||
{% if not notification.read_at %}
|
||||
<a href="/notification/{{ notification.id }}/read" class="inline-block">
|
||||
<button class="btn btn-primary" type="button">
|
||||
<button class="btn btn-alert bg-red-100 dark:bg-red-900" type="button">
|
||||
✓
|
||||
<span class="sr-only">Notification gelesen</span>
|
||||
</button>
|
||||
|
Loading…
Reference in New Issue
Block a user