From 673fd2c58a353bff81b7c1ba959cf1581d08ea56 Mon Sep 17 00:00:00 2001 From: gabatxo1312 Date: Thu, 29 Jan 2026 00:36:23 +0100 Subject: [PATCH] Add filter on book index --- src/models/book.rs | 24 +++++++++++- src/routes/book.rs | 71 ++++++++++++++++++++++------------ src/routes/user.rs | 2 +- templates/index.html | 90 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 153 insertions(+), 34 deletions(-) diff --git a/src/models/book.rs b/src/models/book.rs index 1493798..8b52f78 100644 --- a/src/models/book.rs +++ b/src/models/book.rs @@ -1,4 +1,5 @@ use sea_orm::ActiveValue::Set; +use sea_orm::Condition; use sea_orm::DeleteResult; use sea_orm::QueryOrder; use sea_orm::entity::prelude::*; @@ -6,6 +7,7 @@ use snafu::ResultExt; use snafu::prelude::*; use crate::routes::book::BookForm; +use crate::routes::book::BookQuery; use crate::state::AppState; use crate::state::error::BookSnafu; @@ -56,8 +58,28 @@ impl BookOperator { Self { state } } - pub async fn list(&self) -> Result, BookError> { + pub async fn list(&self, query: Option) -> Result, BookError> { + let mut conditions = Condition::all(); + if let Some(book_query) = query { + if let Some(title) = book_query.title { + conditions = conditions.add(Column::Title.contains(&title)); + } + + if let Some(authors) = book_query.authors { + conditions = conditions.add(Column::Authors.contains(&authors)); + } + + if let Some(owner_id) = book_query.owner_id { + conditions = conditions.add(Column::OwnerId.eq(owner_id)); + } + + if let Some(current_holder_id) = book_query.current_holder_id { + conditions = conditions.add(Column::CurrentHolderId.eq(current_holder_id)); + } + } + Entity::find() + .filter(conditions) .order_by_desc(Column::Id) .all(&self.state.db) .await diff --git a/src/routes/book.rs b/src/routes/book.rs index b23b541..b9f2c81 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -4,7 +4,7 @@ use askama::Template; use askama_web::WebTemplate; use axum::{ Form, - extract::{Path, State}, + extract::{Path, Query, State}, response::{IntoResponse, Redirect}, }; use serde::Deserialize; @@ -22,12 +22,6 @@ use crate::{ }, }; -#[derive(Template, WebTemplate)] -#[template(path = "index.html")] -struct BookIndexTemplate { - books_with_user: Vec, -} - // Book list with the owner and the current holder inside struct BookWithUser { pub book: BookModel, @@ -35,37 +29,64 @@ struct BookWithUser { pub current_holder: Option, } +#[serde_as] +#[derive(Deserialize, Clone)] +pub struct BookQuery { + pub title: Option, + pub authors: Option, + #[serde_as(as = "NoneAsEmptyString")] + pub owner_id: Option, + #[serde_as(as = "NoneAsEmptyString")] + pub current_holder_id: Option, +} + +#[derive(Template, WebTemplate)] +#[template(path = "index.html")] +struct BookIndexTemplate { + books_with_user: Vec, + query: BookQuery, + users: Vec, +} + pub async fn index( State(state): State, + Query(query): Query, ) -> Result { let users = UserOperator::new(state.clone()) .list() .await .context(UserSnafu)?; - let books = BookOperator::new(state).list().await.context(BookSnafu)?; + let books = BookOperator::new(state) + .list(Some(query.clone())) + .await + .context(BookSnafu)?; - let user_by_id: HashMap = - users.into_iter().map(|user| (user.id, user)).collect(); + let user_by_id: HashMap = users + .clone() + .into_iter() + .map(|user| (user.id, user)) + .collect(); - let mut result: Vec = Vec::with_capacity(books.len()); + let result: Vec = books + .into_iter() + .filter_map(|book| { + let owner = user_by_id.get(&book.owner_id).cloned()?; + let current_holder = book + .current_holder_id + .and_then(|id| user_by_id.get(&id).cloned()); - for book in books { - let owner = user_by_id.get(&book.owner_id).cloned().unwrap(); - let current_holder = if let Some(current_holder_id) = book.current_holder_id { - user_by_id.get(¤t_holder_id).cloned() - } else { - None - }; - - result.push(BookWithUser { - book, - owner, - current_holder, - }); - } + Some(BookWithUser { + book, + owner, + current_holder, + }) + }) + .collect(); Ok(BookIndexTemplate { books_with_user: result, + query, + users, }) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 8a49cb4..5a92c3d 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -36,7 +36,7 @@ pub async fn index( .context(UserSnafu)?; let books = BookOperator::new(state.clone()) - .list() + .list(None) .await .context(BookSnafu)?; diff --git a/templates/index.html b/templates/index.html index 1b18927..2db8b9e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,6 +6,82 @@ {% block main %} {{ typography::heading("All books") }} + + {% call cards::card() %} +
+
+
+ + {% match query.title %} + {% when Some with (value) %} + + {% when None %} + + {% endmatch %} +
+ +
+ + + {% match query.authors %} + {% when Some with (value) %} + + {% when None %} + + {% endmatch %} +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
+
+
+ {% endcall %} + {% call cards::card() %} @@ -19,14 +95,14 @@ - {% for book_with_user in books_with_user %} + {% for book_user in books_with_user %} - - - - + + + + {% endfor %}
{{ book_with_user.book.id }}{{ book_with_user.book.title }}{{ book_with_user.book.authors }}{{ book_with_user.owner.name }}{{ book_user.book.id }}{{ book_user.book.title }}{{ book_user.book.authors }}{{ book_user.owner.name }} - {% match book_with_user.current_holder %} + {% match book_user.current_holder %} {% when Some with (current_holder) %} {{ current_holder.name }} {% when None %} @@ -34,7 +110,7 @@ {% endmatch %} - {{ dropdown::book_dropdown_button(book_with_user.book) }} + {{ dropdown::book_dropdown_button(book_user.book) }}