feat(ui): contribution graph with daily activity heatmap
Add /v1/activity/daily endpoint returning per-day event counts via generate_series + LEFT JOIN. Frontend renders an SVG contribution graph with circles colored by quantile-based thresholds. Clicking a day navigates to /activity/YYYY-MM-DD showing that day's events. New /activity/:timespan route parses single dates (YYYY-MM-DD) and ranges (YYYY-MM-DD..YYYY-MM-DD) from the URL to initialize the activity timeline filter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,8 @@ pub mod hg;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use moments_core::{EventReader, EventWriter, PollerState, PollerStateStore, StoreError};
|
||||
use moments_entities::{Event, EventQuery, ProjectSummary, Source, SourceSummary};
|
||||
use chrono::NaiveDate;
|
||||
use moments_entities::{DailyCount, Event, EventQuery, ProjectSummary, Source, SourceSummary};
|
||||
use sqlx::Row;
|
||||
use sqlx::postgres::{PgPool, PgPoolOptions};
|
||||
use std::str::FromStr;
|
||||
@@ -192,6 +193,35 @@ impl EventReader for PgStore {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn daily_counts(&self, from: NaiveDate, to: NaiveDate) -> Result<Vec<DailyCount>, StoreError> {
|
||||
let rows = sqlx::query(
|
||||
r#"
|
||||
SELECT d::date AS date,
|
||||
COUNT(e.id)::bigint AS count
|
||||
FROM generate_series($1::date, $2::date, '1 day') d
|
||||
LEFT JOIN events e
|
||||
ON e.occurred_at >= d AND e.occurred_at < d + interval '1 day'
|
||||
AND e.public = true
|
||||
GROUP BY d::date
|
||||
ORDER BY d::date
|
||||
"#,
|
||||
)
|
||||
.bind(from)
|
||||
.bind(to)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_err)?;
|
||||
|
||||
rows.into_iter()
|
||||
.map(|r| {
|
||||
Ok(DailyCount {
|
||||
date: r.try_get("date").map_err(map_err)?,
|
||||
count: r.try_get("count").map_err(map_err)?,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user