/ web / src / components / api_modal.rs
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  }