From 3454fb503db628ff966c5c7f5d222e36ed690703 Mon Sep 17 00:00:00 2001 From: gabatxo1312 Date: Wed, 28 Jan 2026 23:26:37 +0100 Subject: [PATCH] Add paginate --- src/models/book.rs | 42 +++++++++++++++++++++++++++++++++++----- src/routes/book.rs | 46 +++++++++++++++++++++++++++++++++++++------- src/routes/user.rs | 2 +- templates/index.html | 23 +++++++++++++++++++++- 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/models/book.rs b/src/models/book.rs index 75975af..ea84252 100644 --- a/src/models/book.rs +++ b/src/models/book.rs @@ -7,7 +7,7 @@ use snafu::ResultExt; use snafu::prelude::*; use crate::routes::book::BookForm; -use crate::routes::book::BookQuery; +use crate::routes::book::IndexQuery; use crate::state::AppState; use crate::state::error::BookSnafu; @@ -54,6 +54,13 @@ pub struct BookOperator { pub state: AppState, } +#[derive(Debug, Clone)] +pub struct BooksPaginate { + pub books: Vec, + pub current_page: u64, + pub total_page: u64, +} + impl BookOperator { /// Creates a new `BookOperator` with the given application state. pub fn new(state: AppState) -> Self { @@ -63,7 +70,22 @@ impl BookOperator { /// Lists all books matching the optional query filters. /// /// Results are ordered by ID in descending order (newest first). - pub async fn list(&self, query: Option) -> Result, BookError> { + pub async fn list(&self) -> Result, BookError> { + Entity::find() + .order_by_desc(Column::Id) + .all(&self.state.db) + .await + .context(DBSnafu) + } + + pub async fn list_paginate( + &self, + page: u64, + query: Option, + ) -> Result { + let page = if page > 0 { page } else { 1 }; // keep 1-indexed + let page_0indexed = page - 1; // convert for SeaORM (0-based index) + let mut conditions = Condition::all(); if let Some(book_query) = query { if let Some(title) = book_query.title { @@ -83,12 +105,22 @@ impl BookOperator { } } - Entity::find() + let book_pages = Entity::find() .filter(conditions) .order_by_desc(Column::Id) - .all(&self.state.db) + .paginate(&self.state.db, 1); + + let books = book_pages + .fetch_page(page_0indexed) .await - .context(DBSnafu) + .context(DBSnafu)?; + let total_page = book_pages.num_pages().await.context(DBSnafu)?; + + Ok(BooksPaginate { + books, + current_page: page, + total_page, + }) } /// Finds a book by its ID. diff --git a/src/routes/book.rs b/src/routes/book.rs index 2f31528..26545fb 100644 --- a/src/routes/book.rs +++ b/src/routes/book.rs @@ -31,12 +31,15 @@ struct BookWithUser { /// Query for filter search query #[serde_as] -#[derive(Deserialize, Clone)] -pub struct BookQuery { +#[derive(Deserialize, Clone, Debug)] +pub struct IndexQuery { pub title: Option, + pub page: Option, pub authors: Option, + #[serde(default)] #[serde_as(as = "NoneAsEmptyString")] pub owner_id: Option, + #[serde(default)] #[serde_as(as = "NoneAsEmptyString")] pub current_holder_id: Option, } @@ -45,22 +48,31 @@ pub struct BookQuery { #[template(path = "index.html")] struct BookIndexTemplate { books_with_user: Vec, - query: BookQuery, + query: IndexQuery, users: Vec, + current_page: u64, + total_page: u64, + base_query: String, } pub async fn index( State(state): State, - Query(query): Query, + Query(query): Query, ) -> Result { + let page: u64 = query + .page + .map(|p| p.max(1) as u64) // Minimum 1 + .unwrap_or(1); + // Get all Users let users = UserOperator::new(state.clone()) .list() .await .context(UserSnafu)?; + // Get all Book filtered with query - let books = BookOperator::new(state) - .list(Some(query.clone())) + let books_paginate = BookOperator::new(state) + .list_paginate(page, Some(query.clone())) .await .context(BookSnafu)?; @@ -73,7 +85,8 @@ pub async fn index( .collect(); // Build object of Book with his relation Owner (User) and current_holder (User) - let result: Vec = books + let result: Vec = books_paginate + .books .into_iter() .filter_map(|book| { let owner = user_by_id.get(&book.owner_id).cloned()?; @@ -89,10 +102,29 @@ pub async fn index( }) .collect(); + // build original search to be sure to keep + // search when we change page + let mut base_query = String::new(); + if let Some(title) = &query.title { + base_query.push_str(&format!("title={}&", title)); + } + if let Some(authors) = &query.authors { + base_query.push_str(&format!("authors={}&", authors)); + } + if let Some(owner_id) = &query.owner_id { + base_query.push_str(&format!("owner_id={}&", owner_id)); + } + if let Some(current_holder_id) = &query.current_holder_id { + base_query.push_str(&format!("current_holder_id={}&", current_holder_id)); + } + Ok(BookIndexTemplate { books_with_user: result, query, users, + current_page: books_paginate.current_page, + total_page: books_paginate.total_page, + base_query, }) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 5a92c3d..8a49cb4 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(None) + .list() .await .context(BookSnafu)?; diff --git a/templates/index.html b/templates/index.html index 2db8b9e..a1882c9 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,7 +6,6 @@ {% block main %} {{ typography::heading("All books") }} - {% call cards::card() %}
@@ -116,5 +115,27 @@ {% endfor %} + + {% if total_page > 1 %} + + {% endif %} {% endcall %} {% endblock %} -- 2.39.5