2026-04-02 13:09:43 +02:00
|
|
|
use axum::{extract::State, routing::get, Json, Router};
|
2026-03-25 23:55:44 +01:00
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 13:09:43 +02:00
|
|
|
async fn get_metrics(State(state): State<crate::AppState>) -> Json<DashboardMetricsResponse> {
|
|
|
|
|
// Return live scalar counts for Users, Companies, and Leads where possible
|
|
|
|
|
let total_users: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM users")
|
|
|
|
|
.fetch_one(&state.pool)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_or(Some(0))
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let active_companies: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM company_profiles WHERE status = 'APPROVED'")
|
|
|
|
|
.fetch_one(&state.pool)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_or(Some(0))
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
let open_leads: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM requirements WHERE status = 'PENDING_APPROVAL' OR status = 'APPROVED'")
|
|
|
|
|
.fetch_one(&state.pool)
|
|
|
|
|
.await
|
|
|
|
|
.unwrap_or(Some(0))
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
2026-03-25 23:55:44 +01:00
|
|
|
let kpis = vec![
|
2026-04-02 13:09:43 +02:00
|
|
|
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 }),
|
2026-03-25 23:55:44 +01:00
|
|
|
json!({ "id": "credits", "title": "Credits Purchased", "value": "$45,200", "trend": "+18%", "trendUp": true }),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let trend_series = vec![
|
|
|
|
|
json!({ "name": "Mon", "Freelancers": 40, "Agencies": 24 }),
|
|
|
|
|
json!({ "name": "Tue", "Freelancers": 30, "Agencies": 13 }),
|
|
|
|
|
json!({ "name": "Wed", "Freelancers": 20, "Agencies": 58 }),
|
|
|
|
|
json!({ "name": "Thu", "Freelancers": 27, "Agencies": 39 }),
|
|
|
|
|
json!({ "name": "Fri", "Freelancers": 18, "Agencies": 48 }),
|
|
|
|
|
json!({ "name": "Sat", "Freelancers": 23, "Agencies": 38 }),
|
|
|
|
|
json!({ "name": "Sun", "Freelancers": 34, "Agencies": 43 }),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let rev_series = vec![
|
|
|
|
|
json!({ "name": "Week 1", "Revenue": 4000, "Profit": 2400 }),
|
|
|
|
|
json!({ "name": "Week 2", "Revenue": 3000, "Profit": 1398 }),
|
|
|
|
|
json!({ "name": "Week 3", "Revenue": 2000, "Profit": 9800 }),
|
|
|
|
|
json!({ "name": "Week 4", "Revenue": 2780, "Profit": 3908 }),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let lead_rows = vec![
|
|
|
|
|
json!({ "id": "L-1001", "client": "Acme Corp", "service": "Photography", "status": "Open", "value": "$1,200", "date": "Oct 24, 2023" }),
|
|
|
|
|
json!({ "id": "L-1002", "client": "Stark Ind", "service": "Web Dev", "status": "In Progress", "value": "$4,500", "date": "Oct 23, 2023" }),
|
|
|
|
|
json!({ "id": "L-1003", "client": "Wayne Ent", "service": "SEO", "status": "Closed", "value": "$800", "date": "Oct 22, 2023" }),
|
|
|
|
|
json!({ "id": "L-1004", "client": "Daily Bugle", "service": "Copywriting", "status": "Open", "value": "$350", "date": "Oct 21, 2023" }),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
Json(DashboardMetricsResponse {
|
|
|
|
|
kpis,
|
|
|
|
|
trend_series: trend_series,
|
|
|
|
|
rev_series: rev_series,
|
|
|
|
|
lead_rows: lead_rows,
|
|
|
|
|
})
|
|
|
|
|
}
|