Files
mnemosyne/src/web/pages/quotes.rs

102 lines
3.8 KiB
Rust

use axum::{
extract::{Query, Request, State},
response::{IntoResponse, Redirect, Response},
};
use maud::{PreEscaped, html};
use serde::Deserialize;
use crate::{
MnemoState,
error::CompositeError,
quotes::Quote,
users::{User, auth::UserAuthenticate},
web::{
components::{nav::nav, quote::quote},
icons,
pages::base,
},
};
pub mod add;
#[derive(Deserialize)]
pub struct PageQuery {
page: Option<i64>,
}
pub async fn page(
State(state): State<MnemoState>,
Query(query): Query<PageQuery>,
req: Request,
) -> Result<Response, CompositeError> {
let mut conn = state.pool.acquire().await?;
let u = match User::authenticate(&mut *conn, req.headers()).await? {
Some(u) => u,
None => return Ok(Redirect::to(&format!("/login?r={}", req.uri().path())).into_response()),
};
let page = query.page.unwrap_or(1).max(1);
let per_page = 10;
let offset = (page - 1) * per_page;
let quotes = Quote::get_chronological_offset(&mut *conn, offset, per_page).await?;
let total_quotes = Quote::total_count(&mut *conn).await?;
let total_pages = (total_quotes as f64 / per_page as f64).ceil() as i64;
Ok(base(
"Quotes | Mnemosyne",
html!(
(nav(Some(&u), req.uri().path()))
div class="max-w-4xl mx-auto px-2" {
div class="my-4 flex justify-between" {
p class="flex items-center gap-2" {
span class="text-neutral-500" {(PreEscaped(icons::SCROLL_TEXT))}
span class="text-2xl font-semibold font-lora" {"Quotes"}
}
a href="/quotes/add" class="group border rounded flex items-center gap-1 px-2 border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-400/5 hover:bg-neutral-400/10" {
span class="text-neutral-300 group-hover:text-neutral-200" {(PreEscaped(icons::PLUS))}
span class="text-neutral-300 group-hover:text-neutral-200" {"Add quote"}
}
}
input class="border w-full border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-950/50 p-2 rounded"
placeholder="Search not yet implemented.";
div class="my-2 w-full" {
p class="ml-auto w-fit text-neutral-500 text-sm flex items-center" {
span class="scale-[.6]" {(PreEscaped(icons::CALENDAR_ARROW_DOWN))}
"Chronological"
}
}
div class="flex flex-col gap-4 mb-8" {
@for q in &quotes {
(quote(q))
}
div class="flex justify-between items-center mt-4 text-neutral-400" {
@if page > 1 {
a href=(format!("/quotes?page={}", (page - 1).max(1))) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" {
"Previous"
}
} @else {
div {}
}
span {
"Page " (page) " of " (total_pages)
}
@if page < total_pages {
a href=(format!("/quotes?page={}", page + 1)) class="px-4 py-2 border border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-200/5 hover:bg-neutral-200/15 rounded" {
"Next"
}
} @else {
div {}
}
}
}
}
),
)
.into_response())
}