diff --git a/src/client/hosts.rs b/src/client/hosts.rs index fd5fb83..b0e3b64 100644 --- a/src/client/hosts.rs +++ b/src/client/hosts.rs @@ -1,15 +1,14 @@ // client/hosts.rs — Hosts list page // // Displays all hosts across every network with: -// - Add form : create a host inside a chosen network -// - Filter bar : name (substring), network, open port, application -// - Table : name, IP, network, port count, application count, delete -// - Pagination : configurable page size (15 / 25 / 50 / 100 / All) +// - Add button : opens a modal form to create a host inside a chosen network +// - Filter bar : name (substring), network, open port, application +// - Table : name, IP, network, port count, application count, delete +// - Pagination : configurable page size (15 / 25 / 50 / 100 / All) // -// Each sub-component calls `.into_any()` on its view to return `AnyView` -// (a type-erased wrapper). This prevents Rust from composing all the nested -// generic types into a single enormous type in the parent's monomorphization, -// which would otherwise overflow the compiler's query depth limit. +// Sub-components call `.into_any()` on their views to erase the concrete +// Leptos type, preventing the parent from accumulating a deeply-nested +// generic type that overflows the compiler's query depth limit. use leptos::prelude::*; use leptos::form::ActionForm; @@ -28,46 +27,76 @@ const PER_PAGE_OPTIONS: &[(i64, &str)] = &[ (0, "All"), ]; -// ─── Add host form ──────────────────────────────────────────────────────────── +// ─── Add host modal ─────────────────────────────────────────────────────────── #[component] -fn AddHostForm( +fn AddHostModal( create_action: ServerAction, networks_res: Resource, ServerFnError>>, + show_modal: RwSignal, ) -> impl IntoView { + // Close the modal automatically after a successful creation. + Effect::new(move |_| { + if let Some(Ok(_)) = create_action.value().get() { + show_modal.set(false); + } + }); + view! { -
-

"Add a host"

- -
- - - - + // Backdrop — click outside the card to close +
+ + +
+ + + +
+ + +
+ + {move || create_action.value().get() + .and_then(|r| r.err()) + .map(|e| view! {

{e.to_string()}

}) + } + + }.into_any() } @@ -194,7 +223,7 @@ fn PaginationBar( {move || format!("Page {} of {}", page.get(), total_pages.get().max(1))} + - + // Modal — only rendered when show_modal is true + {move || show_modal.get().then(|| view! { + + })}