use dioxus::prelude::*; use crate::components::page_header::PageHeader; use crate::components::stat_card::StatCard; #[cfg(feature = "server")] use crate::infrastructure::stats::fetch_overview_stats; #[component] pub fn OverviewPage() -> Element { let stats = use_resource(move || async move { #[cfg(feature = "server")] { fetch_overview_stats().await.ok() } #[cfg(not(feature = "server"))] { crate::infrastructure::stats::fetch_overview_stats() .await .ok() } }); rsx! { PageHeader { title: "Overview", description: "Security and compliance scanning dashboard", } match &*stats.read() { Some(Some(s)) => rsx! { div { class: "stat-cards", StatCard { label: "Repositories", value: s.total_repositories.to_string() } StatCard { label: "Total Findings", value: s.total_findings.to_string() } StatCard { label: "Critical", value: s.critical_findings.to_string(), color: "var(--danger)", } StatCard { label: "High", value: s.high_findings.to_string(), color: "#f97316", } StatCard { label: "Medium", value: s.medium_findings.to_string(), color: "var(--warning)", } StatCard { label: "Low", value: s.low_findings.to_string(), color: "var(--success)", } StatCard { label: "Dependencies", value: s.total_sbom_entries.to_string() } StatCard { label: "CVE Alerts", value: s.total_cve_alerts.to_string() } StatCard { label: "Tracker Issues", value: s.total_issues.to_string() } } div { class: "card", div { class: "card-header", "Severity Distribution" } div { class: "severity-chart", SeverityBar { label: "Critical", count: s.critical_findings, max: s.total_findings, color: "var(--danger)" } SeverityBar { label: "High", count: s.high_findings, max: s.total_findings, color: "var(--orange)" } SeverityBar { label: "Medium", count: s.medium_findings, max: s.total_findings, color: "var(--warning)" } SeverityBar { label: "Low", count: s.low_findings, max: s.total_findings, color: "var(--success)" } } } }, Some(None) => rsx! { div { class: "card", p { style: "color: var(--text-secondary);", "Unable to load stats. Make sure the agent API is running." } } }, None => rsx! { div { class: "loading", "Loading overview..." } }, } } } #[component] fn SeverityBar(label: String, count: u64, max: u64, color: String) -> Element { let height_pct = if max > 0 { (count as f64 / max as f64) * 100.0 } else { 0.0 }; rsx! { div { class: "severity-bar", div { class: "severity-bar-count", "{count}" } div { class: "severity-bar-fill", style: "height: {height_pct.max(2.0)}%; background: {color};", } div { class: "severity-bar-label", "{label}" } } } }