diff --git a/compliance-agent/src/pipeline/orchestrator.rs b/compliance-agent/src/pipeline/orchestrator.rs index 4da7a3e..d7eed7e 100644 --- a/compliance-agent/src/pipeline/orchestrator.rs +++ b/compliance-agent/src/pipeline/orchestrator.rs @@ -461,6 +461,25 @@ impl PipelineOrchestrator { } }, ) .await?; + // Refresh the target's cached findings count. The shared pipeline + // (Stage 7) increments `repositories`, which the unified path does + // not use, so set the accurate total on the target itself. + let total = self + .db + .findings() + .count_documents(doc! { "repo_id": target_id }) + .await + .unwrap_or(*count as u64); + self.db + .onboarded_targets() + .update_one( + doc! { "_id": oid }, + doc! { "$set": { + "findings_count": total as i64, + "updated_at": mongodb::bson::DateTime::now(), + } }, + ) + .await?; } Err(e) => { tracing::error!(target_id, error = %e, "Unified scan pipeline failed"); diff --git a/compliance-dashboard/src/pages/findings.rs b/compliance-dashboard/src/pages/findings.rs index b7b546b..fda987f 100644 --- a/compliance-dashboard/src/pages/findings.rs +++ b/compliance-dashboard/src/pages/findings.rs @@ -20,7 +20,7 @@ pub fn FindingsPage() -> Element { let mut selected_ids = use_signal(Vec::::new); let repos = use_resource(|| async { - crate::infrastructure::repositories::fetch_repositories(1) + crate::infrastructure::onboarding::fetch_targets() .await .ok() }); @@ -86,14 +86,14 @@ pub fn FindingsPage() -> Element { } select { onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, - option { value: "", "All Repositories" } + option { value: "", "All Targets" } { match &*repos.read() { Some(Some(resp)) => rsx! { - for repo in &resp.data { + for t in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = t.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = t.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } diff --git a/compliance-dashboard/src/pages/sbom.rs b/compliance-dashboard/src/pages/sbom.rs index 14c1c46..bd1f6f7 100644 --- a/compliance-dashboard/src/pages/sbom.rs +++ b/compliance-dashboard/src/pages/sbom.rs @@ -28,9 +28,9 @@ pub fn SbomPage() -> Element { let mut diff_repo_a = use_signal(String::new); let mut diff_repo_b = use_signal(String::new); - // ── Repos for dropdowns ── + // ── Targets for dropdowns ── let repos = use_resource(|| async { - crate::infrastructure::repositories::fetch_repositories(1) + crate::infrastructure::onboarding::fetch_targets() .await .ok() }); @@ -114,14 +114,14 @@ pub fn SbomPage() -> Element { select { class: "sbom-filter-select", onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, - option { value: "", "All Repositories" } + option { value: "", "All Targets" } { match &*repos.read() { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } } @@ -476,8 +476,8 @@ pub fn SbomPage() -> Element { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } } @@ -498,8 +498,8 @@ pub fn SbomPage() -> Element { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } }