Adds code inspector, file tree components, graph visualization JS, graph API handlers, sidebar navigation updates, and misc improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
3.7 KiB
Rust
103 lines
3.7 KiB
Rust
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}" }
|
|
}
|
|
}
|
|
}
|