use axum::{extract::State, routing::get, Json, Router}; use serde::Serialize; use serde_json::{json, Value}; #[derive(Serialize)] pub struct DashboardMetricsResponse { kpis: Vec, trend_series: Vec, rev_series: Vec, lead_rows: Vec, } pub fn router() -> Router { Router::new().route("/metrics", get(get_metrics)) } async fn get_metrics(State(state): State) -> Json { let total_users: i64 = sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM users") .fetch_one(&state.pool) .await .unwrap_or(0); let active_companies: i64 = sqlx::query_scalar::<_, i64>( "SELECT COUNT(*) FROM company_profiles WHERE status = 'APPROVED'", ) .fetch_one(&state.pool) .await .unwrap_or(0); let open_leads: i64 = sqlx::query_scalar::<_, i64>( "SELECT COUNT(*) FROM leads WHERE status = 'PENDING_APPROVAL' OR status = 'APPROVED'", ) .fetch_one(&state.pool) .await .unwrap_or(0); let pending_approvals: i64 = sqlx::query_scalar::<_, i64>( r#" SELECT COUNT(*) FROM ( SELECT id FROM user_role_profiles WHERE status = 'PENDING_APPROVAL' ) sub "#, ) .fetch_one(&state.pool) .await .unwrap_or(0); let total_revenue: i64 = sqlx::query_scalar::<_, i64>( "SELECT COALESCE(SUM(amount_inr), 0) FROM payments WHERE status = 'SUCCESS'", ) .fetch_one(&state.pool) .await .unwrap_or(0); let kpis = vec![ json!({ "id": "users", "title": "Total Users", "value": format!("{}", total_users), "trend": "-", "trendUp": true }), json!({ "id": "companies", "title": "Active Companies", "value": format!("{}", active_companies), "trend": "-", "trendUp": true }), json!({ "id": "leads", "title": "Open Leads", "value": format!("{}", open_leads), "trend": "-", "trendUp": true }), json!({ "id": "approvals", "title": "Pending Approvals", "value": format!("{}", pending_approvals), "trend": "-", "trendUp": false }), json!({ "id": "revenue", "title": "Total Revenue", "value": format!("₹{:.0}", total_revenue as f64 / 100.0), "trend": "-", "trendUp": true }), ]; // User registrations per day (last 7 days) #[derive(sqlx::FromRow)] struct TrendRow { day_name: Option, count: Option, } let trend_rows = sqlx::query_as::<_, TrendRow>( r#" SELECT TO_CHAR(DATE_TRUNC('day', created_at), 'Dy') AS day_name, COUNT(*) AS count FROM users WHERE created_at >= NOW() - INTERVAL '7 days' GROUP BY DATE_TRUNC('day', created_at), day_name ORDER BY DATE_TRUNC('day', created_at) "#, ) .fetch_all(&state.pool) .await .unwrap_or_default(); let trend_series: Vec = trend_rows .into_iter() .map(|r| json!({ "name": r.day_name.unwrap_or_default(), "Users": r.count.unwrap_or(0) })) .collect(); // Revenue per week (last 4 weeks) #[derive(sqlx::FromRow)] struct RevRow { week_name: Option, total: Option, } let rev_rows = sqlx::query_as::<_, RevRow>( r#" SELECT TO_CHAR(DATE_TRUNC('week', created_at), '"Week" W') AS week_name, COALESCE(SUM(amount_inr), 0) AS total FROM payments WHERE status = 'SUCCESS' AND created_at >= NOW() - INTERVAL '28 days' GROUP BY DATE_TRUNC('week', created_at), week_name ORDER BY DATE_TRUNC('week', created_at) "#, ) .fetch_all(&state.pool) .await .unwrap_or_default(); let rev_series: Vec = rev_rows .into_iter() .map(|r| json!({ "name": r.week_name.unwrap_or_default(), "Revenue": r.total.unwrap_or(0) })) .collect(); // Recent open leads #[derive(sqlx::FromRow)] struct LeadRow { id: uuid::Uuid, title: String, status: String, created_at: chrono::DateTime, requester_name: Option, } let recent_leads = sqlx::query_as::<_, LeadRow>( r#" SELECT r.id, r.title, r.status, r.created_at, CONCAT(u.first_name, ' ', u.last_name) AS requester_name FROM leads r LEFT JOIN users u ON u.id = r.created_by_user_id WHERE r.status IN ('PENDING_APPROVAL', 'APPROVED') ORDER BY r.created_at DESC LIMIT 5 "#, ) .fetch_all(&state.pool) .await .unwrap_or_default(); let lead_rows: Vec = recent_leads .into_iter() .map(|r| { json!({ "id": format!("L-{}", &r.id.to_string()[..8].to_uppercase()), "client": r.requester_name.unwrap_or_else(|| "Unknown".to_string()), "service": r.title, "status": r.status, "date": r.created_at.format("%b %d, %Y").to_string() }) }) .collect(); Json(DashboardMetricsResponse { kpis, trend_series, rev_series, lead_rows, }) }