force-action-on-important-notifications #1176
@@ -26,6 +26,22 @@ impl Notification {
|
|||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn oldest_unread_with_action(db: &SqlitePool, user_id: i64) -> Option<Self> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"SELECT id, user_id, message, read_at, created_at, category, link, action_after_reading
|
||||||
|
FROM notification
|
||||||
|
WHERE user_id = ? AND read_at IS NULL AND action_after_reading IS NOT NULL
|
||||||
|
ORDER BY created_at ASC
|
||||||
|
LIMIT 1",
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
.fetch_optional(db)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_with_tx(
|
pub async fn create_with_tx(
|
||||||
db: &mut Transaction<'_, Sqlite>,
|
db: &mut Transaction<'_, Sqlite>,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
|||||||
@@ -104,9 +104,11 @@ pub struct Stat {
|
|||||||
|
|
||||||
impl Stat {
|
impl Stat {
|
||||||
pub async fn guest(db: &SqlitePool, year: Option<i32>) -> Stat {
|
pub async fn guest(db: &SqlitePool, year: Option<i32>) -> Stat {
|
||||||
let year = match year {
|
let year = year.unwrap_or_else(|| chrono::Local::now().year());
|
||||||
Some(year) => year,
|
let year_filter = if year == 0 {
|
||||||
None => chrono::Local::now().year(),
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("AND l.arrival LIKE '{}-%'", year)
|
||||||
};
|
};
|
||||||
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
||||||
// proper guests
|
// proper guests
|
||||||
@@ -121,7 +123,7 @@ LEFT JOIN (
|
|||||||
FROM rower
|
FROM rower
|
||||||
GROUP BY logbook_id
|
GROUP BY logbook_id
|
||||||
) m ON l.id = m.logbook_id
|
) m ON l.id = m.logbook_id
|
||||||
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND not b.external;
|
WHERE l.distance_in_km IS NOT NULL {year_filter} AND not b.external;
|
||||||
"
|
"
|
||||||
))
|
))
|
||||||
.fetch_one(db)
|
.fetch_one(db)
|
||||||
@@ -145,7 +147,7 @@ WHERE u.id NOT IN (
|
|||||||
WHERE ro.name = 'Donau Linz'
|
WHERE ro.name = 'Donau Linz'
|
||||||
)
|
)
|
||||||
AND l.distance_in_km IS NOT NULL
|
AND l.distance_in_km IS NOT NULL
|
||||||
AND l.arrival LIKE '{year}-%'
|
{year_filter}
|
||||||
AND u.name != 'Externe Steuerperson';
|
AND u.name != 'Externe Steuerperson';
|
||||||
"
|
"
|
||||||
))
|
))
|
||||||
@@ -183,25 +185,27 @@ AND u.name != 'Externe Steuerperson';
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
|
pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
|
||||||
let year = match year {
|
let year = year.unwrap_or_else(|| chrono::Local::now().year());
|
||||||
Some(year) => year,
|
let year_filter = if year == 0 {
|
||||||
None => chrono::Local::now().year(),
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("AND l.arrival LIKE '{}-%'", year)
|
||||||
};
|
};
|
||||||
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
||||||
sqlx::query(&format!(
|
sqlx::query(&format!(
|
||||||
"
|
"
|
||||||
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km, COUNT(*) AS amount_trips
|
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km, COUNT(*) AS amount_trips
|
||||||
FROM (
|
FROM (
|
||||||
SELECT * FROM user
|
SELECT * FROM user
|
||||||
WHERE id IN (
|
WHERE id IN (
|
||||||
SELECT user_id FROM user_role
|
SELECT user_id FROM user_role
|
||||||
JOIN role ON user_role.role_id = role.id
|
JOIN role ON user_role.role_id = role.id
|
||||||
WHERE role.name = 'Donau Linz'
|
WHERE role.name = 'Donau Linz'
|
||||||
)
|
)
|
||||||
) u
|
) u
|
||||||
INNER JOIN rower r ON u.id = r.rower_id
|
INNER JOIN rower r ON u.id = r.rower_id
|
||||||
INNER JOIN logbook l ON r.logbook_id = l.id
|
INNER JOIN logbook l ON r.logbook_id = l.id
|
||||||
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND u.name != 'Externe Steuerperson'
|
WHERE l.distance_in_km IS NOT NULL {year_filter} AND u.name != 'Externe Steuerperson'
|
||||||
GROUP BY u.name
|
GROUP BY u.name
|
||||||
ORDER BY rowed_km DESC, u.name;
|
ORDER BY rowed_km DESC, u.name;
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -88,17 +88,20 @@ pub struct UserWithDetails {
|
|||||||
pub allowed_to_steer: bool,
|
pub allowed_to_steer: bool,
|
||||||
pub on_water: bool,
|
pub on_water: bool,
|
||||||
pub roles: Vec<String>,
|
pub roles: Vec<String>,
|
||||||
|
pub action_notification: Option<Notification>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserWithDetails {
|
impl UserWithDetails {
|
||||||
pub async fn from_user(user: User, db: &SqlitePool) -> Self {
|
pub async fn from_user(user: User, db: &SqlitePool) -> Self {
|
||||||
let allowed_to_steer = user.allowed_to_steer(db).await;
|
let allowed_to_steer = user.allowed_to_steer(db).await;
|
||||||
|
let action_notification = Notification::oldest_unread_with_action(db, user.id).await;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
on_water: user.on_water(db).await,
|
on_water: user.on_water(db).await,
|
||||||
roles: user.roles(db).await,
|
roles: user.roles(db).await,
|
||||||
amount_unread_notifications: user.amount_unread_notifications(db).await,
|
amount_unread_notifications: user.amount_unread_notifications(db).await,
|
||||||
allowed_to_steer,
|
allowed_to_steer,
|
||||||
|
action_notification,
|
||||||
user,
|
user,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,21 @@
|
|||||||
{% include "includes/footer" %}
|
{% include "includes/footer" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include "dynamics/sidebar" %}
|
{% include "dynamics/sidebar" %}
|
||||||
|
{% if loggedin_user and loggedin_user.action_notification %}
|
||||||
|
<dialog id="action-notification-modal" class="max-w-screen-sm dark:bg-primary-600 dark:text-white rounded-md">
|
||||||
|
<div class="p-4">
|
||||||
|
<small class="text-gray-600 dark:text-gray-100">
|
||||||
|
<strong>{{ loggedin_user.action_notification.category }}</strong>
|
||||||
|
</small>
|
||||||
|
<div class="my-4">{{ loggedin_user.action_notification.message }}</div>
|
||||||
|
<a href="/notification/{{ loggedin_user.action_notification.id }}/read" class="btn btn-dark w-full mt-3">
|
||||||
|
✓
|
||||||
|
<span class="sr-only">Notification gelesen</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
<script>document.getElementById('action-notification-modal').showModal();</script>
|
||||||
|
{% endif %}
|
||||||
<script src="/public/main.js"></script>
|
<script src="/public/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -78,11 +78,12 @@
|
|||||||
var queryParams = new URLSearchParams(window.location.search);
|
var queryParams = new URLSearchParams(window.location.search);
|
||||||
return queryParams.get('year');
|
return queryParams.get('year');
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateYears() {
|
function populateYears() {
|
||||||
var select = document.getElementById('yearSelect');
|
var select = document.getElementById('yearSelect');
|
||||||
var currentYear = new Date().getFullYear();
|
var currentYear = new Date().getFullYear();
|
||||||
var selectedYear = getYearFromURL() || currentYear;
|
var selectedYear = getYearFromURL() || currentYear;
|
||||||
|
|
||||||
for (var year = 1977; year <= currentYear; year++) {
|
for (var year = 1977; year <= currentYear; year++) {
|
||||||
var option = document.createElement('option');
|
var option = document.createElement('option');
|
||||||
option.value = option.textContent = year;
|
option.value = option.textContent = year;
|
||||||
@@ -91,13 +92,21 @@
|
|||||||
}
|
}
|
||||||
select.appendChild(option);
|
select.appendChild(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gesamtOption = document.createElement('option');
|
||||||
|
gesamtOption.value = 0;
|
||||||
|
gesamtOption.textContent = 'GESAMT';
|
||||||
|
if (selectedYear == 0) {
|
||||||
|
gesamtOption.selected = true;
|
||||||
|
}
|
||||||
|
select.appendChild(gesamtOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeYear() {
|
function changeYear() {
|
||||||
var selectedYear = document.getElementById('yearSelect').value;
|
var selectedYear = document.getElementById('yearSelect').value;
|
||||||
window.location.href = '?year=' + selectedYear;
|
window.location.href = '?year=' + selectedYear;
|
||||||
}
|
}
|
||||||
|
|
||||||
populateYears();
|
populateYears();
|
||||||
</script>
|
</script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
Reference in New Issue
Block a user