chore: slight simplifications to bags

This commit is contained in:
Jon Seager 2025-11-27 13:24:45 +00:00
parent ec8dbea7ea
commit 7eab263071
No known key found for this signature in database
4 changed files with 50 additions and 84 deletions

View file

@ -22,16 +22,19 @@ use crate::presentation::web::views::{BagView, ListNavigator, Paginated, Roaster
const BAG_PAGE_PATH: &str = "/bags"; const BAG_PAGE_PATH: &str = "/bags";
const BAG_FRAGMENT_PATH: &str = "/bags#bag-list"; const BAG_FRAGMENT_PATH: &str = "/bags#bag-list";
struct BagPageData {
open_bags: Vec<BagView>,
bags: Paginated<BagView>,
navigator: ListNavigator<BagSortKey>,
}
#[tracing::instrument(skip(state))] #[tracing::instrument(skip(state))]
async fn load_bag_page( async fn load_bag_page(
state: &AppState, state: &AppState,
request: ListRequest<BagSortKey>, request: ListRequest<BagSortKey>,
) -> Result<(Vec<BagView>, Paginated<BagView>, ListNavigator<BagSortKey>), AppError> { ) -> Result<BagPageData, AppError> {
let open_bags = state.bag_repo.list_open().await.map_err(AppError::from)?; let open_bags = state.bag_repo.list_open().await.map_err(AppError::from)?;
let open_bags_view = open_bags let open_bags_view = open_bags.into_iter().map(BagView::from_domain).collect();
.into_iter()
.map(BagView::from_with_roast)
.collect();
let page = state let page = state
.bag_repo .bag_repo
@ -42,12 +45,16 @@ async fn load_bag_page(
let (bags, navigator) = crate::application::routes::support::build_page_view( let (bags, navigator) = crate::application::routes::support::build_page_view(
page, page,
request, request,
BagView::from_with_roast, BagView::from_domain,
BAG_PAGE_PATH, BAG_PAGE_PATH,
BAG_FRAGMENT_PATH, BAG_FRAGMENT_PATH,
); );
Ok((open_bags_view, bags, navigator)) Ok(BagPageData {
open_bags: open_bags_view,
bags,
navigator,
})
} }
#[tracing::instrument(skip(state, cookies, headers, query))] #[tracing::instrument(skip(state, cookies, headers, query))]
@ -75,7 +82,11 @@ pub(crate) async fn bags_page(
let roaster_options = roasters.into_iter().map(RoasterOptionView::from).collect(); let roaster_options = roasters.into_iter().map(RoasterOptionView::from).collect();
let (open_bags, bags, navigator) = load_bag_page(&state, request) let BagPageData {
open_bags,
bags,
navigator,
} = load_bag_page(&state, request)
.await .await
.map_err(map_app_error)?; .map_err(map_app_error)?;
@ -352,7 +363,11 @@ async fn render_bag_list_fragment(
request: ListRequest<BagSortKey>, request: ListRequest<BagSortKey>,
is_authenticated: bool, is_authenticated: bool,
) -> Result<Response, AppError> { ) -> Result<Response, AppError> {
let (open_bags, bags, navigator) = load_bag_page(&state, request).await?; let BagPageData {
open_bags,
bags,
navigator,
} = load_bag_page(&state, request).await?;
let template = BagListTemplate { let template = BagListTemplate {
is_authenticated, is_authenticated,

View file

@ -112,7 +112,7 @@ pub(crate) async fn roast_page(
let bag_views = bags let bag_views = bags
.into_iter() .into_iter()
.map(crate::presentation::web::views::BagView::from_with_roast) .map(crate::presentation::web::views::BagView::from_domain)
.collect(); .collect();
let is_authenticated = let is_authenticated =

View file

@ -9,6 +9,16 @@ use crate::domain::listing::{ListRequest, Page, SortDirection};
use crate::domain::repositories::BagRepository; use crate::domain::repositories::BagRepository;
use crate::infrastructure::database::DatabasePool; use crate::infrastructure::database::DatabasePool;
const BASE_SELECT: &str = r#"
SELECT
b.id, b.roast_id, b.roast_date, b.amount, b.remaining, b.closed, b.finished_at, b.created_at, b.updated_at,
r.name as roast_name, r.slug as roast_slug,
rr.name as roaster_name, rr.slug as roaster_slug
FROM bags b
JOIN roasts r ON b.roast_id = r.id
JOIN roasters rr ON r.roaster_id = rr.id
"#;
#[derive(Clone)] #[derive(Clone)]
pub struct SqlBagRepository { pub struct SqlBagRepository {
pool: DatabasePool, pool: DatabasePool,
@ -111,22 +121,12 @@ impl BagRepository for SqlBagRepository {
let order_clause = format!("{} {}", sort_column, direction); let order_clause = format!("{} {}", sort_column, direction);
let base_query = r#"
SELECT
b.id, b.roast_id, b.roast_date, b.amount, b.remaining, b.closed, b.finished_at, b.created_at, b.updated_at,
r.name as roast_name, r.slug as roast_slug,
rr.name as roaster_name, rr.slug as roaster_slug
FROM bags b
JOIN roasts r ON b.roast_id = r.id
JOIN roasters rr ON r.roaster_id = rr.id
"#;
let count_query = "SELECT COUNT(*) FROM bags"; let count_query = "SELECT COUNT(*) FROM bags";
crate::infrastructure::repositories::pagination::paginate( crate::infrastructure::repositories::pagination::paginate(
&self.pool, &self.pool,
request, request,
base_query, BASE_SELECT,
count_query, count_query,
&order_clause, &order_clause,
|record| Ok(Self::to_domain_with_roast(record)), |record| Ok(Self::to_domain_with_roast(record)),
@ -135,19 +135,12 @@ impl BagRepository for SqlBagRepository {
} }
async fn list_by_roast(&self, roast_id: RoastId) -> Result<Vec<BagWithRoast>, RepositoryError> { async fn list_by_roast(&self, roast_id: RoastId) -> Result<Vec<BagWithRoast>, RepositoryError> {
let query = r#" let query = format!(
SELECT "{} WHERE b.roast_id = ? ORDER BY b.roast_date DESC",
b.id, b.roast_id, b.roast_date, b.amount, b.remaining, b.closed, b.finished_at, b.created_at, b.updated_at, BASE_SELECT
r.name as roast_name, r.slug as roast_slug, );
rr.name as roaster_name, rr.slug as roaster_slug
FROM bags b
JOIN roasts r ON b.roast_id = r.id
JOIN roasters rr ON r.roaster_id = rr.id
WHERE b.roast_id = ?
ORDER BY b.roast_date DESC
"#;
let records = query_as::<_, BagWithRoastRecord>(query) let records = query_as::<_, BagWithRoastRecord>(&query)
.bind(roast_id.into_inner()) .bind(roast_id.into_inner())
.fetch_all(&self.pool) .fetch_all(&self.pool)
.await .await
@ -226,19 +219,12 @@ impl BagRepository for SqlBagRepository {
} }
async fn list_open(&self) -> Result<Vec<BagWithRoast>, RepositoryError> { async fn list_open(&self) -> Result<Vec<BagWithRoast>, RepositoryError> {
let query = r#" let query = format!(
SELECT "{} WHERE b.closed = FALSE ORDER BY b.roast_date DESC",
b.id, b.roast_id, b.roast_date, b.amount, b.remaining, b.closed, b.finished_at, b.created_at, b.updated_at, BASE_SELECT
r.name as roast_name, r.slug as roast_slug, );
rr.name as roaster_name, rr.slug as roaster_slug
FROM bags b
JOIN roasts r ON b.roast_id = r.id
JOIN roasters rr ON r.roaster_id = rr.id
WHERE b.closed = FALSE
ORDER BY b.roast_date DESC
"#;
let records = query_as::<_, BagWithRoastRecord>(query) let records = query_as::<_, BagWithRoastRecord>(&query)
.fetch_all(&self.pool) .fetch_all(&self.pool)
.await .await
.map_err(|err| RepositoryError::unexpected(err.to_string()))?; .map_err(|err| RepositoryError::unexpected(err.to_string()))?;
@ -268,23 +254,14 @@ impl BagRepository for SqlBagRepository {
let order_clause = format!("{} {}", sort_column, direction); let order_clause = format!("{} {}", sort_column, direction);
let base_query = r#" let base_query = format!("{} WHERE b.closed = TRUE", BASE_SELECT);
SELECT
b.id, b.roast_id, b.roast_date, b.amount, b.remaining, b.closed, b.finished_at, b.created_at, b.updated_at,
r.name as roast_name, r.slug as roast_slug,
rr.name as roaster_name, rr.slug as roaster_slug
FROM bags b
JOIN roasts r ON b.roast_id = r.id
JOIN roasters rr ON r.roaster_id = rr.id
WHERE b.closed = TRUE
"#;
let count_query = "SELECT COUNT(*) FROM bags WHERE closed = TRUE"; let count_query = "SELECT COUNT(*) FROM bags WHERE closed = TRUE";
crate::infrastructure::repositories::pagination::paginate( crate::infrastructure::repositories::pagination::paginate(
&self.pool, &self.pool,
request, request,
base_query, &base_query,
count_query, count_query,
&order_clause, &order_clause,
|record| Ok(Self::to_domain_with_roast(record)), |record| Ok(Self::to_domain_with_roast(record)),

View file

@ -1,4 +1,4 @@
use crate::domain::bags::{Bag, BagWithRoast}; use crate::domain::bags::BagWithRoast;
use crate::domain::listing::{DEFAULT_PAGE_SIZE, ListRequest, Page, PageSize, SortKey}; use crate::domain::listing::{DEFAULT_PAGE_SIZE, ListRequest, Page, PageSize, SortKey};
use crate::domain::roasters::Roaster; use crate::domain::roasters::Roaster;
use crate::domain::roasts::{Roast, RoastWithRoaster}; use crate::domain::roasts::{Roast, RoastWithRoaster};
@ -537,33 +537,7 @@ pub struct BagView {
} }
impl BagView { impl BagView {
pub fn from_domain( pub fn from_domain(bag: BagWithRoast) -> Self {
bag: Bag,
roast_name: &str,
roaster_name: &str,
roast_slug: &str,
roaster_slug: &str,
) -> Self {
Self {
id: bag.id.to_string(),
roast_id: bag.roast_id.to_string(),
roast_date: bag.roast_date.map(|d| d.to_string()),
amount: format!("{:.1}", bag.amount),
remaining: format!("{:.1}", bag.remaining),
closed: bag.closed,
finished_at: bag
.finished_at
.map(|d| d.to_string())
.unwrap_or_else(|| "".to_string()),
created_at: bag.created_at.format("%Y-%m-%d").to_string(),
roast_name: roast_name.to_string(),
roaster_name: roaster_name.to_string(),
roast_slug: roast_slug.to_string(),
roaster_slug: roaster_slug.to_string(),
}
}
pub fn from_with_roast(bag: BagWithRoast) -> Self {
Self { Self {
id: bag.bag.id.to_string(), id: bag.bag.id.to_string(),
roast_id: bag.bag.roast_id.to_string(), roast_id: bag.bag.roast_id.to_string(),