fix(dashboard): Findings/SBOM filter by targets + accurate target findings_count #155

Merged
sharang merged 1 commits from feat/dashboard-targets-dropdowns into main 2026-07-13 07:59:46 +00:00
3 changed files with 33 additions and 14 deletions
@@ -461,6 +461,25 @@ impl PipelineOrchestrator {
} }, } },
) )
.await?; .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) => { Err(e) => {
tracing::error!(target_id, error = %e, "Unified scan pipeline failed"); tracing::error!(target_id, error = %e, "Unified scan pipeline failed");
+5 -5
View File
@@ -20,7 +20,7 @@ pub fn FindingsPage() -> Element {
let mut selected_ids = use_signal(Vec::<String>::new); let mut selected_ids = use_signal(Vec::<String>::new);
let repos = use_resource(|| async { let repos = use_resource(|| async {
crate::infrastructure::repositories::fetch_repositories(1) crate::infrastructure::onboarding::fetch_targets()
.await .await
.ok() .ok()
}); });
@@ -86,14 +86,14 @@ pub fn FindingsPage() -> Element {
} }
select { select {
onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, onchange: move |e| { repo_filter.set(e.value()); page.set(1); },
option { value: "", "All Repositories" } option { value: "", "All Targets" }
{ {
match &*repos.read() { match &*repos.read() {
Some(Some(resp)) => rsx! { 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 id = t.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string();
let name = repo.name.clone(); let name = t.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string();
rsx! { rsx! {
option { value: "{id}", "{name}" } option { value: "{id}", "{name}" }
} }
+9 -9
View File
@@ -28,9 +28,9 @@ pub fn SbomPage() -> Element {
let mut diff_repo_a = use_signal(String::new); let mut diff_repo_a = use_signal(String::new);
let mut diff_repo_b = use_signal(String::new); let mut diff_repo_b = use_signal(String::new);
// ── Repos for dropdowns ── // ── Targets for dropdowns ──
let repos = use_resource(|| async { let repos = use_resource(|| async {
crate::infrastructure::repositories::fetch_repositories(1) crate::infrastructure::onboarding::fetch_targets()
.await .await
.ok() .ok()
}); });
@@ -114,14 +114,14 @@ pub fn SbomPage() -> Element {
select { select {
class: "sbom-filter-select", class: "sbom-filter-select",
onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, onchange: move |e| { repo_filter.set(e.value()); page.set(1); },
option { value: "", "All Repositories" } option { value: "", "All Targets" }
{ {
match &*repos.read() { match &*repos.read() {
Some(Some(resp)) => rsx! { Some(Some(resp)) => rsx! {
for repo in &resp.data { for repo in &resp.data {
{ {
let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); 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.name.clone(); let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string();
rsx! { option { value: "{id}", "{name}" } } rsx! { option { value: "{id}", "{name}" } }
} }
} }
@@ -476,8 +476,8 @@ pub fn SbomPage() -> Element {
Some(Some(resp)) => rsx! { Some(Some(resp)) => rsx! {
for repo in &resp.data { for repo in &resp.data {
{ {
let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); 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.name.clone(); let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string();
rsx! { option { value: "{id}", "{name}" } } rsx! { option { value: "{id}", "{name}" } }
} }
} }
@@ -498,8 +498,8 @@ pub fn SbomPage() -> Element {
Some(Some(resp)) => rsx! { Some(Some(resp)) => rsx! {
for repo in &resp.data { for repo in &resp.data {
{ {
let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); 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.name.clone(); let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string();
rsx! { option { value: "{id}", "{name}" } } rsx! { option { value: "{id}", "{name}" } }
} }
} }