Compare commits

..

4 Commits

Author SHA1 Message Date
b1ccd21068 Merge branch 'master' into gractwo
All checks were successful
mnemo-build-and-publish / gractwo-mnemo-build (push) Successful in 1m14s
2026-05-06 02:50:44 +02:00
7d284f0777 prioritize special uuids in user page display 2026-05-06 02:49:42 +02:00
84dde9cc4b require permission to delete quotes 2026-05-06 02:24:05 +02:00
e7c0523841 quotelink hover, also make dashboard quotes into links 2026-05-06 02:00:23 +02:00
5 changed files with 30 additions and 8 deletions

View File

@@ -4,7 +4,7 @@ use crate::{quotes::Quote, web::icons};
pub fn quote(quote: &Quote) -> Markup {
html!(
div class="border border-neutral-200/25 bg-neutral-200/5 p-3 pb-1 overflow-clip rounded-md relative flex flex-col" {
div class="border border-neutral-200/25 bg-neutral-200/5 p-3 pb-1 overflow-clip rounded-md relative flex flex-col transition-colors group-hover/a:border-neutral-200/35 group-hover/a:bg-neutral-200/10" {
div class="absolute top-4 right-6 -rotate-12 opacity-[.025] scale-x-[4.5] scale-y-[4]" {
(PreEscaped(icons::QUOTE))
}

View File

@@ -61,7 +61,9 @@ pub async fn page(
"This just in! This quote was added "
(format_time_ago(q.get_creation_timestamp())) " ago."
}
div class="flex-1 [&>div]:h-full" {(quote(q))}
div class="flex-1 [&>div]:h-full" {
a href=(format!("/quotes/{}", q.id)) class="group/a" {(quote(q))}
}
} @else {
p class="text-neutral-500 font-light mb-4" {"No quotes yet."}
}
@@ -76,7 +78,9 @@ pub async fn page(
"This quote was added "
(format_time_ago(q.get_creation_timestamp())) " ago."
}
div class="flex-1 [&>div]:h-full" {(quote(&q))}
div class="flex-1 [&>div]:h-full" {
a href=(format!("/quotes/{}", q.id)) class="group/a" {(quote(&q))}
}
}
}
}

View File

@@ -86,7 +86,7 @@ pub async fn page(
}
div class="flex flex-col gap-4 mb-8" {
@for q in &quotes {
a href=(format!("/quotes/{}", q.id)) {(quote(q))}
a href=(format!("/quotes/{}", q.id)) class="group/a" {(quote(q))}
}
div class="flex justify-between items-center mt-4 text-neutral-400" {

View File

@@ -3,6 +3,7 @@ use axum::{
http::HeaderMap,
response::{IntoResponse, Redirect, Response},
};
use http::StatusCode;
use maud::{PreEscaped, html};
use uuid::Uuid;
@@ -14,6 +15,7 @@ use crate::{
users::{
User,
auth::{UserAuthRequired, UserAuthenticate},
permissions::Permission,
},
web::{
components::{nav::nav, quote::quote},
@@ -33,6 +35,9 @@ pub async fn page(
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
};
let q = Quote::get_by_id(&mut conn, id).await;
let can_delete = u
.has_permission(&mut conn, Permission::DeleteQuotes)
.await?;
Ok(base(
"Add Quote | Mnemosyne",
@@ -53,9 +58,11 @@ pub async fn page(
span class="scale-[.75]" {(PreEscaped(icons::PEN))}
"Edit"
}
a href=(format!("/quotes/{id}/delete")) class="px-2 py-1 cursor-pointer border rounded flex flex-row gap-1 bg-pink-400/10 border-pink-400/25 hover:bg-pink-400/20 hover:border-pink-400/45" {
span class="scale-[.75]" {(PreEscaped(icons::TRASH))}
"Delete"
@if can_delete {
a href=(format!("/quotes/{id}/delete")) class="px-2 py-1 cursor-pointer border rounded flex flex-row gap-1 bg-pink-400/10 border-pink-400/25 hover:bg-pink-400/20 hover:border-pink-400/45" {
span class="scale-[.75]" {(PreEscaped(icons::TRASH))}
"Delete"
}
}
}
} @else {
@@ -126,6 +133,9 @@ pub async fn delete(
) -> Result<Response, CompositeError> {
let mut tx = state.pool.begin().await?;
let u = User::authenticate(&mut *tx, &headers).await?.required()?;
if !u.has_permission(&mut tx, Permission::DeleteQuotes).await? {
return Ok((StatusCode::FORBIDDEN, "No permission.").into_response());
}
let q = Quote::get_by_id(&mut *tx, id).await?;
LogEntry::new(&mut *tx, u, LogAction::DeleteQuote { quote: q.clone() }).await?;

View File

@@ -3,6 +3,7 @@ use axum::{
response::{IntoResponse, Redirect, Response},
};
use maud::{PreEscaped, html};
use uuid::Uuid;
use crate::{
MnemoState,
@@ -27,7 +28,14 @@ pub async fn page(
Some(u) => u,
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
};
let us = User::get_all(&mut *conn).await;
let us = User::get_all(&mut *conn).await.map(|mut v| {
v.sort_by_key(|p| match p.id {
id if id == Uuid::nil() => (0, p.id),
id if id == Uuid::max() => (1, p.id),
_ => (2, p.id),
});
v
});
let can_create_users = u
.has_permission(&mut *conn, Permission::ManuallyCreateUsers)
.await;