From a2a889bced55b993dda58981548f6897ac5eab14 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Mon, 4 May 2026 19:12:06 +0200 Subject: [PATCH] feat: wire activity feed, users list, and profile page handlers --- crates/presentation/src/dtos.rs | 7 ++ crates/presentation/src/handlers.rs | 103 ++++++++++++++++++++++++++++ crates/presentation/src/routes.rs | 4 +- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/crates/presentation/src/dtos.rs b/crates/presentation/src/dtos.rs index 4503799..4fa6e11 100644 --- a/crates/presentation/src/dtos.rs +++ b/crates/presentation/src/dtos.rs @@ -223,6 +223,13 @@ impl From for GetDiaryQuery { } } +#[derive(serde::Deserialize, Default)] +pub struct ProfileQueryParams { + pub view: Option, + pub limit: Option, + pub offset: Option, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/presentation/src/handlers.rs b/crates/presentation/src/handlers.rs index 2fd7ff3..d3b2af7 100644 --- a/crates/presentation/src/handlers.rs +++ b/crates/presentation/src/handlers.rs @@ -235,6 +235,109 @@ pub mod html { } } } + + pub async fn get_activity_feed( + OptionalCookieUser(user_id): OptionalCookieUser, + State(state): State, + Query(params): Query, + ) -> impl IntoResponse { + let ctx = build_page_context(&state, user_id).await; + let query = application::queries::GetActivityFeedQuery { + limit: params.limit, + offset: params.offset, + }; + match application::use_cases::get_activity_feed::execute(&state.app_ctx, query).await { + Ok(entries) => { + let limit = entries.limit; + let offset = entries.offset; + let has_more = (offset + limit) < entries.total_count as u32; + let data = application::ports::ActivityFeedPageData { + ctx, + current_offset: offset, + has_more, + limit, + entries, + }; + match state.html_renderer.render_activity_feed_page(data) { + Ok(html) => Html(html).into_response(), + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(), + } + } + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + } + } + + pub async fn get_users_list( + OptionalCookieUser(user_id): OptionalCookieUser, + State(state): State, + ) -> impl IntoResponse { + let ctx = build_page_context(&state, user_id).await; + match application::use_cases::get_users::execute(&state.app_ctx, application::queries::GetUsersQuery).await { + Ok(users) => { + let data = application::ports::UsersPageData { ctx, users }; + match state.html_renderer.render_users_page(data) { + Ok(html) => Html(html).into_response(), + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(), + } + } + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + } + } + + pub async fn get_user_profile( + OptionalCookieUser(user_id): OptionalCookieUser, + State(state): State, + Path(profile_user_uuid): Path, + Query(params): Query, + ) -> impl IntoResponse { + let ctx = build_page_context(&state, user_id).await; + let view = params.view.clone().unwrap_or_else(|| "recent".to_string()); + + let profile_user = match state.app_ctx.user_repository + .find_by_id(&domain::value_objects::UserId::from_uuid(profile_user_uuid)) + .await + { + Ok(Some(u)) => u, + Ok(None) => return (StatusCode::NOT_FOUND, "User not found").into_response(), + Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + }; + + let query = application::queries::GetUserProfileQuery { + user_id: profile_user_uuid, + view: view.clone(), + limit: params.limit, + offset: params.offset, + }; + + match application::use_cases::get_user_profile::execute(&state.app_ctx, query).await { + Ok(profile) => { + let (offset, has_more, limit) = profile.entries.as_ref() + .map(|e| { + let has_more = (e.offset + e.limit) < e.total_count as u32; + (e.offset, has_more, e.limit) + }) + .unwrap_or((0, false, 20)); + let data = application::ports::ProfilePageData { + ctx, + profile_user_id: profile_user_uuid, + profile_user_email: profile_user.email().value().to_string(), + stats: profile.stats, + view, + entries: profile.entries, + current_offset: offset, + has_more, + limit, + history: profile.history, + trends: profile.trends, + }; + match state.html_renderer.render_profile_page(data) { + Ok(html) => Html(html).into_response(), + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(), + } + } + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + } + } } pub mod posters { diff --git a/crates/presentation/src/routes.rs b/crates/presentation/src/routes.rs index 2b06974..d1c12a8 100644 --- a/crates/presentation/src/routes.rs +++ b/crates/presentation/src/routes.rs @@ -14,7 +14,9 @@ pub fn build_router(state: AppState) -> Router { fn html_routes() -> Router { Router::new() - .route("/", routing::get(handlers::html::get_index)) + .route("/", routing::get(handlers::html::get_activity_feed)) + .route("/users", routing::get(handlers::html::get_users_list)) + .route("/users/{id}", routing::get(handlers::html::get_user_profile)) .route( "/login", routing::get(handlers::html::get_login_page)