use gloo::net::http::Request; use serde::{Deserialize, Serialize}; use wasm_bindgen_futures::spawn_local; use yew::Properties; use yew::prelude::*; #[derive(Serialize, Deserialize, Clone, Debug)] struct GenerationRequest { directory: String, common_name: String, } use yew_router::prelude::*; #[derive(Clone, Routable, PartialEq)] pub enum Route { #[at("/")] Home, #[at("/dir")] Directories, #[at("/dir/:dir_name")] Configs { dir_name: String }, #[at("/dir/:dir_name/:file_name")] Config { dir_name: String, file_name: String }, #[at("/gen/:dir_name")] Generate { dir_name: String }, #[not_found] #[at("/404")] NotFound, } #[derive(Properties, PartialEq)] pub struct NavProps { #[prop_or("".into())] pub currrent_dir: AttrValue, #[prop_or(Route::Home)] pub currrent_route: Route, } // component to show the navbar #[function_component(Navbar)] fn navbar(props: &NavProps) -> Html { let classes_active = "block py-2 px-3 text-white bg-blue-700 rounded-sm md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500"; let classes_inactive = "block py-2 px-3 text-gray-900 rounded-sm hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent"; let has_dir = props.currrent_dir.len() > 0; let is_current_home = props.currrent_route == Route::Home; html! { } } // component to show the directories #[function_component(Directories)] fn directories() -> Html { let dirs = use_state(Vec::new); let message = use_state(|| "".to_string()); let get_dirs = { let dirs = dirs.clone(); let message = message.clone(); Callback::from(move |_| { let url = "http://127.0.0.1:8000/api/v1/get/"; let dirs = dirs.clone(); let message = message.clone(); spawn_local(async move { match Request::get(url).send().await { Ok(response) if response.ok() => match response.json::>().await { Ok(mut json) => { json.sort(); dirs.set(json); } _ => message.set("Failed to fetch directories".into()), }, _ => message.set("Failed to fetch directories".into()), } }) }) }; // Добавляем use_effect с пустым вектором зависимостей { let get_dirs = get_dirs.clone(); use_effect_with((), move |_| { get_dirs.emit(()); () }); } html! {

{"Directories"}

{ "Directory List" }

if !message.is_empty() {

{ &*message }

}
} } #[derive(Properties, PartialEq)] pub struct ConfigsProps { pub dir: AttrValue, } #[function_component(Configs)] fn configs(props: &ConfigsProps) -> Html { let configs = use_state(Vec::new); let message = use_state(|| "".to_string()); let dir_name = props.dir.clone(); let get_configs = { let configs = configs.clone(); let message = message.clone(); Callback::from(move |_| { let url = format!("http://127.0.0.1:8000/api/v1/get/{}", dir_name.clone()); let configs = configs.clone(); let message = message.clone(); spawn_local(async move { match Request::get(&url).send().await { Ok(response) if response.ok() => match response.json::>().await { Ok(mut json) => { json.sort(); configs.set(json); } _ => message.set("Failed to fetch configs".into()), }, _ => message.set("Failed to fetch configs".into()), } }) }) }; { let get_configs_clone = get_configs.clone(); use_effect_with((), move |_| { get_configs_clone.emit(()); () }); } html! {

to={Route::Generate { dir_name: props.dir.clone().to_string() }}> >

{ "Configs" }

{format!("Configs for {}:", props.dir.clone())}

{ for (*configs).iter().map(|file_name| { html! { } }) }
{"Name"}
to={Route::Config { dir_name: props.dir.clone().to_string(), file_name: file_name.clone() }}>{file_name}>
if !message.is_empty() {

{ &*message }

}
} } #[derive(Properties, PartialEq)] pub struct ConfigProps { pub dir: AttrValue, pub file_name: AttrValue, } #[function_component(Config)] fn config(props: &ConfigProps) -> Html { let dir_name = props.dir.clone(); let file_name = props.file_name.clone(); let contents = use_state(|| "".to_string()); let get_contents = { let contents = contents.clone(); Callback::from(move |_| { let url = format!( "http://127.0.0.1:8000/api/v1/get/{}/{}", dir_name.clone(), file_name.clone() ); let contents = contents.clone(); spawn_local(async move { match Request::get(&url).send().await { Ok(response) if response.ok() => { if let Ok(text) = response.text().await { contents.set(text); } } _ => contents.set("Failed to fetch contents".into()), } }) }) }; { let get_contents_clone = get_contents.clone(); use_effect_with((), move |_| { get_contents_clone.emit(()); () }); } html! {

{"Config "}{props.file_name.clone()}{" for "}{props.dir.clone()}{":"}

{"Config "}{props.file_name.clone()}{" for "}{props.dir.clone()}{":"}

{&*contents}
} } #[derive(Properties, PartialEq)] pub struct GenerateProps { pub dir: AttrValue, } #[function_component(Generate)] fn generate(props: &GenerateProps) -> Html { let dir_name = props.dir.clone(); let config_name = use_state(|| "".to_string()); let result = use_state(|| "".to_string()); let error = use_state(|| "".to_string()); let done = use_state(|| false); let generate_config = { let dir_name = dir_name.clone(); let config_name = config_name.clone(); let done = done.clone(); let result = result.clone(); let error = error.clone(); Callback::from(move |_| { let request_body = GenerationRequest { directory: dir_name.clone().to_string(), common_name: config_name.clone().to_string(), }; let json_payload = serde_json::to_string(&request_body).unwrap(); let url = "http://127.0.0.1:8000/api/v1/generate/"; let result = result.clone(); let done = done.clone(); let error = error.clone(); spawn_local(async move { match Request::post(url) .header("Content-Type", "application/json") .body(json_payload) .unwrap() .send() .await { Ok(response) if response.ok() => { if let Ok(text) = response.text().await { result.set(text); done.set(true); error.set("".into()); } } _ => { error.set("Failed to generate config".into()); } } }) }) }; html! {
if !*done.clone() {

{format!("Generate config for {}:", props.dir.clone())}

().unwrap(); config_name.set(input.value()); } })} />
if !error.is_empty() { }
} else { }
} } fn switch(routes: Route) -> Html { match routes.clone() { Route::Home => html! {

{ "Home" }

to={Route::Directories}>{ "Directories" }>
}, Route::Directories => html! {
}, Route::Configs { dir_name } => html! {
}, Route::Generate { dir_name } => html! {
}, Route::Config { dir_name, file_name, } => html! {
}, _ => html! {

{ "todo" }

}, } } #[function_component] fn App() -> Html { html! { render={switch} /> } } fn main() { yew::Renderer::::new().render(); }