From b919bd8f0df993fd08a80afb9462d0f97d2e7bd1 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sun, 18 May 2025 16:00:26 -0500 Subject: [PATCH] receipts/list: Reverse sort and add pagination Now that there are quite a few receipts in the database, scrolling to the end to see the most recent entries is rather cumbersome. Let's show the most recent receipts first, and hide older ones by default by splitting the list into multiple pages. --- ...6af95abd81949be0c369a5d7225272e6c9c58.json | 20 ++++++++ ...7de3b5a3db4415cd55ecbd21c28c108274a6.json} | 9 ++-- sql/receipts/count-receipts.sql | 4 ++ sql/receipts/list-receipts.sql | 6 ++- src/receipts.rs | 11 +++- src/routes/receipts.rs | 50 ++++++++++++++++++- templates/receipt-list.html.tera | 19 +++++++ 7 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 .sqlx/query-34f56cde503100c09bbb378ce656af95abd81949be0c369a5d7225272e6c9c58.json rename .sqlx/{query-71dcdc6a24d99eff2dd7af673a0ebb6fda45b0ebd5244309472921a934e1b829.json => query-ed7bf495d2eefe7b479a79cc2fc77de3b5a3db4415cd55ecbd21c28c108274a6.json} (79%) create mode 100644 sql/receipts/count-receipts.sql diff --git a/.sqlx/query-34f56cde503100c09bbb378ce656af95abd81949be0c369a5d7225272e6c9c58.json b/.sqlx/query-34f56cde503100c09bbb378ce656af95abd81949be0c369a5d7225272e6c9c58.json new file mode 100644 index 0000000..bb8c5dd --- /dev/null +++ b/.sqlx/query-34f56cde503100c09bbb378ce656af95abd81949be0c369a5d7225272e6c9c58.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n count(id) AS \"count!\"\nFROM\n receipts\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "34f56cde503100c09bbb378ce656af95abd81949be0c369a5d7225272e6c9c58" +} diff --git a/.sqlx/query-71dcdc6a24d99eff2dd7af673a0ebb6fda45b0ebd5244309472921a934e1b829.json b/.sqlx/query-ed7bf495d2eefe7b479a79cc2fc77de3b5a3db4415cd55ecbd21c28c108274a6.json similarity index 79% rename from .sqlx/query-71dcdc6a24d99eff2dd7af673a0ebb6fda45b0ebd5244309472921a934e1b829.json rename to .sqlx/query-ed7bf495d2eefe7b479a79cc2fc77de3b5a3db4415cd55ecbd21c28c108274a6.json index f6bfb68..aba2cf8 100644 --- a/.sqlx/query-71dcdc6a24d99eff2dd7af673a0ebb6fda45b0ebd5244309472921a934e1b829.json +++ b/.sqlx/query-ed7bf495d2eefe7b479a79cc2fc77de3b5a3db4415cd55ecbd21c28c108274a6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n id, vendor, date, amount, notes, filename\nFROM\n receipts\nORDER BY date\n", + "query": "SELECT\n id, vendor, date, amount, notes, filename\nFROM\n receipts\nORDER BY\n date DESC,\n id DESC\nLIMIT $1\nOFFSET $2\n", "describe": { "columns": [ { @@ -35,7 +35,10 @@ } ], "parameters": { - "Left": [] + "Left": [ + "Int8", + "Int8" + ] }, "nullable": [ false, @@ -46,5 +49,5 @@ false ] }, - "hash": "71dcdc6a24d99eff2dd7af673a0ebb6fda45b0ebd5244309472921a934e1b829" + "hash": "ed7bf495d2eefe7b479a79cc2fc77de3b5a3db4415cd55ecbd21c28c108274a6" } diff --git a/sql/receipts/count-receipts.sql b/sql/receipts/count-receipts.sql new file mode 100644 index 0000000..58d0c1e --- /dev/null +++ b/sql/receipts/count-receipts.sql @@ -0,0 +1,4 @@ +SELECT + count(id) AS "count!" +FROM + receipts diff --git a/sql/receipts/list-receipts.sql b/sql/receipts/list-receipts.sql index e148802..6d9fa5d 100644 --- a/sql/receipts/list-receipts.sql +++ b/sql/receipts/list-receipts.sql @@ -2,4 +2,8 @@ SELECT id, vendor, date, amount, notes, filename FROM receipts -ORDER BY date +ORDER BY + date DESC, + id DESC +LIMIT $1 +OFFSET $2 diff --git a/src/receipts.rs b/src/receipts.rs index 24b5d3d..5e19dad 100644 --- a/src/receipts.rs +++ b/src/receipts.rs @@ -138,12 +138,21 @@ impl ReceiptsRepository { pub async fn list_receipts( &mut self, + limit: i64, + offset: i64, ) -> Result, sqlx::Error> { - sqlx::query_file_as!(ReceiptJson, "sql/receipts/list-receipts.sql") + sqlx::query_file_as!(ReceiptJson, "sql/receipts/list-receipts.sql", limit, offset) .fetch_all(&mut **self.conn) .await } + pub async fn count_receipts(&mut self) -> Result { + Ok(sqlx::query_file!("sql/receipts/count-receipts.sql") + .fetch_one(&mut **self.conn) + .await? + .count) + } + pub async fn add_receipt( &mut self, data: &ReceiptPostData, diff --git a/src/routes/receipts.rs b/src/routes/receipts.rs index 7923d6d..776f40f 100644 --- a/src/routes/receipts.rs +++ b/src/routes/receipts.rs @@ -1,3 +1,5 @@ +use std::ops::RangeInclusive; + use rocket::form::Form; use rocket::http::{ContentType, Header, MediaType, Status}; use rocket::serde::json::Json; @@ -12,18 +14,62 @@ use crate::imaging; use crate::receipts::*; use crate::{Context, Database}; -#[rocket::get("/")] +fn paginate(total: i64, count: i64, current: i64) -> Vec { + let start = 1; + let end = (total / count).max(1); + let pages = RangeInclusive::new(start, end); + if end < 10 { + pages.map(|p| format!("{}", p)).collect() + } else { + pages + .filter_map(|p| { + if p == start + || (current - 2 <= p && p <= current + 2) + || p == end + { + Some(format!("{}", p)) + } else if p == current - 3 || p == current + 3 { + Some("...".into()) + } else { + None + } + }) + .collect() + } +} + +#[rocket::get("/?&")] pub async fn list_receipts( db: DatabaseConnection, + page: Option, + count: Option, ) -> (Status, Template) { let mut repo = ReceiptsRepository::new(db); - match repo.list_receipts().await { + let count = count.unwrap_or(25); + let page = page.unwrap_or(1); + let total = match repo.count_receipts().await { + Ok(r) => r, + Err(e) => { + return ( + Status::InternalServerError, + Template::render( + "error", + context! { + error: e.to_string(), + }, + ), + ) + }, + }; + match repo.list_receipts(count, (page - 1) * count).await { Ok(r) => ( Status::Ok, Template::render( "receipt-list", context! { receipts: r, + pages: paginate(total, count, page), + count: count, }, ), ), diff --git a/templates/receipt-list.html.tera b/templates/receipt-list.html.tera index 1f61e7f..e4221ca 100644 --- a/templates/receipt-list.html.tera +++ b/templates/receipt-list.html.tera @@ -80,6 +80,18 @@ #confirm-delete dl dd { margin-left: 0; } + + ul.pagination { + margin: 0; + padding: 0; + list-style-type: none; + text-align: center; + } + + ul.pagination li { + display: inline-block; + margin: 1em; + } {% endblock %} {% block main %}

Receipts

@@ -114,6 +126,13 @@ {% endfor %} +
    +{%- for page in pages %} +
  • {% if page == "..." %}...{% else + %}{{ page }}{% + endif %}
  • +{%- endfor %} +

Are you sure you want to delete receipt ?