use dioxus::prelude::*; use crate::components::page_header::PageHeader; use crate::infrastructure::graph::fetch_impact; #[component] pub fn ImpactAnalysisPage(repo_id: String, finding_id: String) -> Element { let impact_data = use_resource(move || { let rid = repo_id.clone(); let fid = finding_id.clone(); async move { fetch_impact(rid, fid).await.ok() } }); rsx! { PageHeader { title: "Impact Analysis", description: "Blast radius and affected entry points for a security finding", } div { class: "card", match &*impact_data.read() { Some(Some(resp)) => { let impact = resp.data.clone().unwrap_or_default(); rsx! { div { class: "grid grid-cols-2 gap-4 mb-4", div { class: "stat-card", div { class: "stat-value", "{impact.get(\"blast_radius\").and_then(|v| v.as_u64()).unwrap_or(0)}" } div { class: "stat-label", "Blast Radius (nodes affected)" } } div { class: "stat-card", div { class: "stat-value", "{impact.get(\"affected_entry_points\").and_then(|v| v.as_array()).map(|a| a.len()).unwrap_or(0)}" } div { class: "stat-label", "Entry Points Affected" } } } h3 { "Affected Entry Points" } if let Some(entries) = impact.get("affected_entry_points").and_then(|v| v.as_array()) { if entries.is_empty() { p { class: "text-muted", "No entry points affected." } } else { ul { class: "list", for entry in entries { li { "{entry.as_str().unwrap_or(\"-\")}" } } } } } h3 { class: "mt-4", "Call Chains" } if let Some(chains) = impact.get("call_chains").and_then(|v| v.as_array()) { if chains.is_empty() { p { class: "text-muted", "No call chains found." } } else { for (i, chain) in chains.iter().enumerate() { div { class: "card mb-2", strong { "Chain {i + 1}: " } if let Some(steps) = chain.as_array() { for (j, step) in steps.iter().enumerate() { span { "{step.as_str().unwrap_or(\"-\")}" } if j < steps.len() - 1 { span { class: "text-muted", " → " } } } } } } } } h3 { class: "mt-4", "Direct Callers" } if let Some(callers) = impact.get("direct_callers").and_then(|v| v.as_array()) { if callers.is_empty() { p { class: "text-muted", "No direct callers." } } else { ul { class: "list", for caller in callers { li { code { "{caller.as_str().unwrap_or(\"-\")}" } } } } } } } }, Some(None) => rsx! { p { "No impact analysis data available for this finding." } }, None => rsx! { p { "Loading impact analysis..." } }, } } } }