api_modal.rs
1 use dioxus::prelude::*; 2 use dioxus::hooks::UseResourceState; 3 use dioxus_primitives::dialog::{DialogContent, DialogRoot, DialogTitle}; 4 use ui::components::button::{Button, ButtonSize, ButtonVariant}; 5 6 #[component] 7 pub fn ApiModal( 8 open: Signal<bool>, 9 device_url: Signal<String>, 10 ) -> Element { 11 let mut api_data = use_resource(move || { 12 let is_open = *open.read(); 13 let url = device_url.read().clone(); 14 async move { 15 if !is_open { return String::new(); } 16 match reqwest::get(format!("{url}/api/cloudevents")).await { 17 Ok(response) => match response.text().await { 18 Ok(text) => match serde_json::from_str::<serde_json::Value>(&text) { 19 Ok(json) => serde_json::to_string_pretty(&json).unwrap_or(text), 20 Err(_) => text, 21 }, 22 Err(err) => format!("Error: {err}"), 23 }, 24 Err(err) => format!("Error: {err}"), 25 } 26 } 27 }); 28 29 let content = api_data.value().read().clone().unwrap_or_default(); 30 let is_loading = matches!(*api_data.state().read(), UseResourceState::Pending); 31 32 rsx! { 33 DialogRoot { 34 open: open(), 35 on_open_change: move |v: bool| open.set(v), 36 class: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4", 37 38 DialogContent { 39 class: "w-full max-w-3xl bg-card border border-border rounded-lg shadow-2xl flex flex-col max-h-[80vh]", 40 41 div { class: "flex items-center justify-between px-5 py-4 border-b border-border", 42 div { 43 DialogTitle { "CloudEvents API" } 44 p { class: "text-sm text-muted-foreground", "Response from /api/cloudevents" } 45 } 46 Button { 47 variant: ButtonVariant::Ghost, 48 size: ButtonSize::Small, 49 is_icon_button: true, 50 aria_label: "Close".to_string(), 51 on_click: move |_| open.set(false), 52 lucide_dioxus::X { class: "w-5 h-5" } 53 } 54 } 55 56 div { class: "flex-1 overflow-auto p-4", 57 if is_loading { 58 div { class: "flex items-center justify-center py-12", 59 lucide_dioxus::LoaderCircle { class: "w-6 h-6 animate-spin text-muted-foreground" } 60 } 61 } else { 62 pre { class: "text-xs font-mono text-foreground whitespace-pre-wrap break-all leading-relaxed", 63 "{content}" 64 } 65 } 66 } 67 68 div { class: "flex items-center gap-2 px-5 py-3 border-t border-border", 69 Button { 70 variant: ButtonVariant::Outline, 71 on_click: move |_| { 72 let text = api_data.value().read().clone().unwrap_or_default(); 73 #[cfg(target_arch = "wasm32")] 74 if let Some(window) = web_sys::window() { 75 let _ = window.navigator().clipboard().write_text(&text); 76 } 77 }, 78 "Copy" 79 } 80 Button { 81 variant: ButtonVariant::Outline, 82 on_click: move |_| api_data.restart(), 83 "Refresh" 84 } 85 div { class: "flex-1" } 86 Button { 87 variant: ButtonVariant::Outline, 88 on_click: move |_| open.set(false), 89 "Close" 90 } 91 } 92 } 93 } 94 } 95 }