Skip to main content

max / goingson

3.1 KB · 96 lines History Blame Raw
1 //! Dashboard statistics commands.
2 //!
3 //! Provides aggregated statistics for the dashboard view including
4 //! overdue tasks, upcoming events, and high-urgency items.
5
6 use serde::Serialize;
7 use std::sync::Arc;
8 use tauri::State;
9 use tracing::instrument;
10
11 use goingson_core::DashboardStats;
12
13 use crate::state::{AppState, DESKTOP_USER_ID};
14 use super::ApiError;
15
16 // ============ Types ============
17
18 #[derive(Debug, Serialize)]
19 #[serde(rename_all = "camelCase")]
20 pub struct DashboardStatsResponse {
21 pub overdue_count: i64,
22 pub due_today_count: i64,
23 pub due_this_week_count: i64,
24 pub unread_emails: i64,
25 pub upcoming_events: i64,
26 pub active_projects: i64,
27 pub high_urgency_tasks: Vec<HighUrgencyTaskResponse>,
28 }
29
30 #[derive(Debug, Serialize)]
31 #[serde(rename_all = "camelCase")]
32 pub struct HighUrgencyTaskResponse {
33 pub id: String,
34 pub description: String,
35 pub urgency: f64,
36 pub status: String,
37 pub due: Option<String>,
38 }
39
40 impl From<DashboardStats> for DashboardStatsResponse {
41 fn from(s: DashboardStats) -> Self {
42 DashboardStatsResponse {
43 overdue_count: s.overdue_count,
44 due_today_count: s.tasks_due_today,
45 due_this_week_count: s.tasks_due_this_week,
46 unread_emails: s.unread_emails,
47 upcoming_events: s.upcoming_events,
48 active_projects: s.active_projects,
49 high_urgency_tasks: s.high_urgency_tasks
50 .into_iter()
51 .map(|t| HighUrgencyTaskResponse {
52 id: t.id,
53 description: t.description,
54 urgency: t.urgency,
55 status: t.status,
56 due: t.due,
57 })
58 .collect(),
59 }
60 }
61 }
62
63 // ============ Commands ============
64
65 /// Retrieves aggregated statistics for the dashboard.
66 ///
67 /// Returns counts for overdue tasks, tasks due today/this week, unread emails,
68 /// upcoming events, active projects, and a list of high-urgency tasks.
69 ///
70 /// # Errors
71 ///
72 /// Returns `DATABASE_ERROR` if the query fails.
73 #[tauri::command]
74 #[instrument(skip_all)]
75 pub async fn get_dashboard_stats(state: State<'_, Arc<AppState>>) -> Result<DashboardStatsResponse, ApiError> {
76 // Compute the "today"/"this week" windows from the user's *local* calendar
77 // day so due-date counts match what the user sees, then hand them to the
78 // repo as UTC instants. "This week" preserves the prior inclusive
79 // today..today+7 span (an 8-day exclusive upper bound).
80 let now = chrono::Utc::now();
81 let tz = crate::tz::system_tz();
82 let local_today = now.with_timezone(&tz).date_naive();
83 let midnight = |d: chrono::NaiveDate| {
84 crate::tz::local_civil_to_utc(d.and_hms_opt(0, 0, 0).expect("valid midnight"))
85 };
86 let today_start = midnight(local_today);
87 let tomorrow_start = midnight(local_today + chrono::Duration::days(1));
88 let week_end = midnight(local_today + chrono::Duration::days(8));
89
90 let stats = state
91 .stats
92 .get_dashboard_stats(DESKTOP_USER_ID, now, today_start, tomorrow_start, week_end)
93 .await?;
94 Ok(DashboardStatsResponse::from(stats))
95 }
96