159 lines
5 KiB
Rust
159 lines
5 KiB
Rust
use axum::{extract::State, routing::get, Json, Router};
|
|
use serde::Serialize;
|
|
use serde_json::{json, Value};
|
|
|
|
#[derive(Serialize)]
|
|
pub struct DashboardMetricsResponse {
|
|
kpis: Vec<Value>,
|
|
trend_series: Vec<Value>,
|
|
rev_series: Vec<Value>,
|
|
lead_rows: Vec<Value>,
|
|
}
|
|
|
|
pub fn router() -> Router<crate::AppState> {
|
|
Router::new().route("/metrics", get(get_metrics))
|
|
}
|
|
|
|
async fn get_metrics(State(state): State<crate::AppState>) -> Json<DashboardMetricsResponse> {
|
|
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<String>,
|
|
count: Option<i64>,
|
|
}
|
|
|
|
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<Value> = 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<String>,
|
|
total: Option<i64>,
|
|
}
|
|
|
|
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<Value> = 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<chrono::Utc>,
|
|
requester_name: Option<String>,
|
|
}
|
|
|
|
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<Value> = 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,
|
|
})
|
|
}
|