diff --git a/src/api/networks.rs b/src/api/networks.rs index cdfe5aa..7c90650 100644 --- a/src/api/networks.rs +++ b/src/api/networks.rs @@ -8,7 +8,7 @@ use crate::models::Network; // Network row augmented with pre-computed counts. // Defined here (not in models.rs) because it is a presentation model // specific to the Networks page, not a pure domain entity. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct NetworkWithCounts { pub id: i64, pub cidr: String, diff --git a/src/client/networks.rs b/src/client/networks.rs index 98aff34..4957be3 100644 --- a/src/client/networks.rs +++ b/src/client/networks.rs @@ -16,7 +16,59 @@ use leptos::prelude::*; use leptos::form::ActionForm; -use crate::api::networks::{CreateNetwork, DeleteNetwork, get_networks_with_counts}; +use crate::api::networks::{CreateNetwork, DeleteNetwork, NetworkWithCounts, get_networks_with_counts}; + +// ─── Delete confirmation modal ──────────────────────────────────────────────── + +#[component] +fn DeleteConfirmModal( + network: NetworkWithCounts, + delete_action: ServerAction, + pending_delete: RwSignal>, +) -> impl IntoView { + let id = network.id; + let cidr = network.cidr.clone(); + let host_count = network.host_count; + + view! { + + }.into_any() +} + +// ─── Page ───────────────────────────────────────────────────────────────────── #[component] pub fn NetworksPage() -> impl IntoView { @@ -28,6 +80,16 @@ pub fn NetworksPage() -> impl IntoView { let create_action = ServerAction::::new(); let delete_action = ServerAction::::new(); + // Stores the network pending deletion; Some = modal open, None = closed. + let pending_delete: RwSignal> = RwSignal::new(None); + + // Close the modal automatically after a successful deletion. + Effect::new(move |_| { + if let Some(Ok(_)) = delete_action.value().get() { + pending_delete.set(None); + } + }); + // ── Data resource ───────────────────────────────────────────────────────── // // `Resource::new(source, fetcher)`: @@ -45,6 +107,15 @@ pub fn NetworksPage() -> impl IntoView {

"Networks"

+ // ── Delete confirmation modal ────────────────────────────────────── + {move || pending_delete.get().map(|network| view! { + + })} + // ── Add form ────────────────────────────────────────────────────── // // `` submits the form to the server @@ -123,7 +194,7 @@ pub fn NetworksPage() -> impl IntoView { {list .into_iter() .map(|network| { - let id = network.id; + let network_clone = network.clone(); view! { {network.cidr} @@ -131,8 +202,7 @@ pub fn NetworksPage() -> impl IntoView { {network.application_count} diff --git a/style/rust-ipam.css b/style/rust-ipam.css index 74cc6ea..da67223 100644 --- a/style/rust-ipam.css +++ b/style/rust-ipam.css @@ -924,6 +924,23 @@ td.col-actions { background: var(--bg-hover); } +.modal__body { + margin-bottom: var(--size-lg); +} + +.modal__body p { + margin: 0 0 var(--size-sm); +} + +.warning { + color: var(--color-warning, #b45309); + background: var(--color-warning-bg, #fef3c7); + border: 1px solid var(--color-warning-border, #fcd34d); + border-radius: var(--radius-sm); + padding: var(--size-sm) var(--size-md); + font-size: var(--font-sm); +} + /* Form fields inside modal — single column stack */ .modal .add-form__fields { display: flex;