diff --git a/src/application/routes/bags.rs b/src/application/routes/bags.rs index b9efda4..c1ef51f 100644 --- a/src/application/routes/bags.rs +++ b/src/application/routes/bags.rs @@ -22,16 +22,19 @@ use crate::presentation::web::views::{BagView, ListNavigator, Paginated, Roaster const BAG_PAGE_PATH: &str = "/bags"; const BAG_FRAGMENT_PATH: &str = "/bags#bag-list"; +struct BagPageData { + open_bags: Vec, + bags: Paginated, + navigator: ListNavigator, +} + #[tracing::instrument(skip(state))] async fn load_bag_page( state: &AppState, request: ListRequest, -) -> Result<(Vec, Paginated, ListNavigator), AppError> { +) -> Result { let open_bags = state.bag_repo.list_open().await.map_err(AppError::from)?; - let open_bags_view = open_bags - .into_iter() - .map(BagView::from_with_roast) - .collect(); + let open_bags_view = open_bags.into_iter().map(BagView::from_domain).collect(); let page = state .bag_repo @@ -42,12 +45,16 @@ async fn load_bag_page( let (bags, navigator) = crate::application::routes::support::build_page_view( page, request, - BagView::from_with_roast, + BagView::from_domain, BAG_PAGE_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))] @@ -75,7 +82,11 @@ pub(crate) async fn bags_page( 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 .map_err(map_app_error)?; @@ -352,7 +363,11 @@ async fn render_bag_list_fragment( request: ListRequest, is_authenticated: bool, ) -> Result { - 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 { is_authenticated, diff --git a/src/application/routes/roasts.rs b/src/application/routes/roasts.rs index 9c4daea..91e1c73 100644 --- a/src/application/routes/roasts.rs +++ b/src/application/routes/roasts.rs @@ -112,7 +112,7 @@ pub(crate) async fn roast_page( let bag_views = bags .into_iter() - .map(crate::presentation::web::views::BagView::from_with_roast) + .map(crate::presentation::web::views::BagView::from_domain) .collect(); let is_authenticated = diff --git a/src/infrastructure/repositories/bags.rs b/src/infrastructure/repositories/bags.rs index 0c9d2d7..ac59546 100644 --- a/src/infrastructure/repositories/bags.rs +++ b/src/infrastructure/repositories/bags.rs @@ -9,6 +9,16 @@ use crate::domain::listing::{ListRequest, Page, SortDirection}; use crate::domain::repositories::BagRepository; 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)] pub struct SqlBagRepository { pool: DatabasePool, @@ -111,22 +121,12 @@ impl BagRepository for SqlBagRepository { 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"; crate::infrastructure::repositories::pagination::paginate( &self.pool, request, - base_query, + BASE_SELECT, count_query, &order_clause, |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, RepositoryError> { - let 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 - WHERE b.roast_id = ? - ORDER BY b.roast_date DESC - "#; + let query = format!( + "{} WHERE b.roast_id = ? ORDER BY b.roast_date DESC", + BASE_SELECT + ); - let records = query_as::<_, BagWithRoastRecord>(query) + let records = query_as::<_, BagWithRoastRecord>(&query) .bind(roast_id.into_inner()) .fetch_all(&self.pool) .await @@ -226,19 +219,12 @@ impl BagRepository for SqlBagRepository { } async fn list_open(&self) -> Result, RepositoryError> { - let 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 - WHERE b.closed = FALSE - ORDER BY b.roast_date DESC - "#; + let query = format!( + "{} WHERE b.closed = FALSE ORDER BY b.roast_date DESC", + BASE_SELECT + ); - let records = query_as::<_, BagWithRoastRecord>(query) + let records = query_as::<_, BagWithRoastRecord>(&query) .fetch_all(&self.pool) .await .map_err(|err| RepositoryError::unexpected(err.to_string()))?; @@ -268,23 +254,14 @@ impl BagRepository for SqlBagRepository { 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 - WHERE b.closed = TRUE - "#; + let base_query = format!("{} WHERE b.closed = TRUE", BASE_SELECT); let count_query = "SELECT COUNT(*) FROM bags WHERE closed = TRUE"; crate::infrastructure::repositories::pagination::paginate( &self.pool, request, - base_query, + &base_query, count_query, &order_clause, |record| Ok(Self::to_domain_with_roast(record)), diff --git a/src/presentation/web/views.rs b/src/presentation/web/views.rs index cfb1c4f..01c9d84 100644 --- a/src/presentation/web/views.rs +++ b/src/presentation/web/views.rs @@ -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::roasters::Roaster; use crate::domain::roasts::{Roast, RoastWithRoaster}; @@ -537,33 +537,7 @@ pub struct BagView { } impl BagView { - pub fn from_domain( - 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 { + pub fn from_domain(bag: BagWithRoast) -> Self { Self { id: bag.bag.id.to_string(), roast_id: bag.bag.roast_id.to_string(),