Files
compliance-scanner-agent/compliance-dashboard/src/pages/dast_targets.rs
Sharang Parnerkar c44bf12a4a
All checks were successful
CI / Clippy (push) Successful in 4m6s
CI / Security Audit (push) Has been skipped
CI / Tests (push) Has been skipped
CI / Format (pull_request) Successful in 3s
CI / Format (push) Successful in 3s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Clippy (pull_request) Successful in 4m10s
CI / Security Audit (pull_request) Has been skipped
CI / Tests (pull_request) Has been skipped
CI / Detect Changes (push) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
feat: UI improvements with icons, back navigation, and overview cards
- Move AI Chat and MCP Servers from sidebar to overview page as cards
- Remove Graph from sidebar (accessible from repositories page)
- Add back buttons to all drill-down pages (finding detail, graph, chat, etc.)
- Replace text labels with icons + tooltips on action buttons and status badges
- Improve spacing and margins across tables and cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:19:50 +01:00

157 lines
7.3 KiB
Rust

use dioxus::prelude::*;
use dioxus_free_icons::icons::bs_icons::*;
use dioxus_free_icons::Icon;
use crate::components::page_header::PageHeader;
use crate::components::toast::{ToastType, Toasts};
use crate::infrastructure::dast::{add_dast_target, fetch_dast_targets, trigger_dast_scan};
#[component]
pub fn DastTargetsPage() -> Element {
let mut targets = use_resource(|| async { fetch_dast_targets().await.ok() });
let mut toasts = use_context::<Toasts>();
let mut show_form = use_signal(|| false);
let mut new_name = use_signal(String::new);
let mut new_url = use_signal(String::new);
rsx! {
div { class: "back-nav",
button {
class: "btn btn-ghost btn-back",
onclick: move |_| { navigator().go_back(); },
Icon { icon: BsArrowLeft, width: 16, height: 16 }
"Back"
}
}
PageHeader {
title: "DAST Targets",
description: "Configure target applications for dynamic security testing",
}
div { class: "mb-4",
button {
class: "btn btn-primary",
onclick: move |_| show_form.set(!show_form()),
if show_form() { "Cancel" } else { "Add Target" }
}
}
if show_form() {
div { class: "card mb-4",
h3 { "Add New Target" }
div { class: "form-group",
label { "Name" }
input {
class: "input",
r#type: "text",
placeholder: "My Web App",
value: "{new_name}",
oninput: move |e| new_name.set(e.value()),
}
}
div { class: "form-group",
label { "Base URL" }
input {
class: "input",
r#type: "text",
placeholder: "https://example.com",
value: "{new_url}",
oninput: move |e| new_url.set(e.value()),
}
}
button {
class: "btn btn-primary",
onclick: move |_| {
let name = new_name();
let url = new_url();
spawn(async move {
match add_dast_target(name, url).await {
Ok(_) => {
toasts.push(ToastType::Success, "Target created");
targets.restart();
}
Err(e) => toasts.push(ToastType::Error, e.to_string()),
}
});
show_form.set(false);
new_name.set(String::new());
new_url.set(String::new());
},
"Create Target"
}
}
}
div { class: "card",
h3 { "Configured Targets" }
match &*targets.read() {
Some(Some(data)) => {
let target_list = &data.data;
if target_list.is_empty() {
rsx! { p { "No DAST targets configured. Add one to get started." } }
} else {
rsx! {
table { class: "table",
thead {
tr {
th { "Name" }
th { "URL" }
th { "Type" }
th { "Rate Limit" }
th { "Destructive" }
th { "Actions" }
}
}
tbody {
for target in target_list {
{
let target_id = target.get("_id").and_then(|v| v.get("$oid")).and_then(|v| v.as_str()).unwrap_or("").to_string();
rsx! {
tr {
td { "{target.get(\"name\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" }
td { code { "{target.get(\"base_url\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" } }
td { "{target.get(\"target_type\").and_then(|v| v.as_str()).unwrap_or(\"-\")}" }
td { "{target.get(\"rate_limit\").and_then(|v| v.as_u64()).unwrap_or(0)} req/s" }
td {
if target.get("allow_destructive").and_then(|v| v.as_bool()).unwrap_or(false) {
span { class: "badge badge-danger", "Yes" }
} else {
span { class: "badge badge-success", "No" }
}
}
td {
button {
class: "btn btn-sm",
onclick: {
let tid = target_id.clone();
move |_| {
let tid = tid.clone();
spawn(async move {
match trigger_dast_scan(tid).await {
Ok(_) => toasts.push(ToastType::Success, "DAST scan triggered"),
Err(e) => toasts.push(ToastType::Error, e.to_string()),
}
});
}
},
"Scan"
}
}
}
}
}
}
}
}
}
}
},
Some(None) => rsx! { p { "Failed to load targets." } },
None => rsx! { p { "Loading..." } },
}
}
}
}