use std::net::ToSocketAddrs; use gloo_net::http::Request; use serde::{Deserialize, Serialize}; use wasm_bindgen_futures::spawn_local; use yew::prelude::*; use yew_router::prelude::use_navigator; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct User { pub id: i16, pub last_name: String, pub first_name: String, pub username: String, pub is_admin: bool, pub pwd: String, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct UserCreateScheme { pub first_name: String, pub last_name: String, pub username: String, pub is_admin: bool, pub pwd: String, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct LoginScheme { pub username: String, pub pwd: String, } #[derive(Deserialize, Serialize, Debug)] pub struct UserUpdateScheme { pub id: i16, pub first_name: String, pub last_name: String, pub username: String, pub make_admin: bool, pub new_pwd: String, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct FilteredUser { pub id: i16, pub first_name: String, pub last_name: String, pub username: String, pub is_admin: bool, } #[derive(Properties, PartialEq)] pub struct UserProps { pub id: i16, } #[derive(Deserialize, Debug)] struct ApiError { message: String, _status: String, } #[component(Register)] pub fn register_component() -> Html { let first_name = use_state(|| "".to_string()); let last_name = use_state(|| "".to_string()); let username = use_state(|| "".to_string()); let is_admin = use_state(|| false); let pwd = use_state(|| "".to_string()); let status = use_state(|| None::); let onsubmit = { let first_name = first_name.clone(); let last_name = last_name.clone(); let username = username.clone(); let is_admin = is_admin.clone(); let pwd = pwd.clone(); let status = status.clone(); Callback::from(move |e: SubmitEvent| { e.prevent_default(); let first_name = (*first_name).clone(); let last_name = (*last_name).clone(); let username = (*username).clone(); let is_admin = *is_admin; let pwd = (*pwd).clone(); let status = status.clone(); spawn_local(async move { let payload = UserCreateScheme { first_name, last_name, username, is_admin, pwd, }; let request = Request::post("/api/register") .json(&payload) .expect("Error building request"); match request.send().await { Ok(response) if response.status() == 200 => status.set(Some("Success".into())), Ok(response) => status.set(Some(format!("Error: {}", response.status()))), Err(err) => status.set(Some(format!("Network error: {}", err))), } }); }) }; let fn_change = { let first_name = first_name.clone(); Callback::from(move |e: InputEvent| { let input: web_sys::HtmlInputElement = e.target_unchecked_into(); first_name.set(input.value()); }) }; let ln_change = { let last_name = last_name.clone(); Callback::from(move |e: InputEvent| { let input: web_sys::HtmlInputElement = e.target_unchecked_into(); last_name.set(input.value()); }) }; let un_change = { let username = username.clone(); Callback::from(move |e: InputEvent| { let input: web_sys::HtmlInputElement = e.target_unchecked_into(); username.set(input.value()); }) }; let admin_change = { let is_admin = is_admin.clone(); Callback::from(move |e: Event| { let input: web_sys::HtmlInputElement = e.target_unchecked_into(); is_admin.set(input.checked()); }) }; let pwd_change = { let pwd = pwd.clone(); Callback::from(move |e: InputEvent| { let input: web_sys::HtmlInputElement = e.target_unchecked_into(); pwd.set(input.value()); }) }; html! {
} } #[component(Login)] pub fn login_component() -> Html { let username = use_state(|| "".to_string()); let pwd = use_state(|| "".to_string()); let loading = use_state(|| false); let error = use_state(|| String::new()); let success = use_state(|| false); let navigator = use_navigator().unwrap(); let onsubmit = { let username = username.clone(); let pwd = pwd.clone(); let loading = loading.clone(); let error = error.clone(); let success = success.clone(); let navigator = navigator.clone(); Callback::from(move |e: SubmitEvent| { e.prevent_default(); let username = (*username).clone(); let pwd = (*pwd).clone(); let loading = loading.clone(); let error = error.clone(); let success = success.clone(); let navigator = navigator.clone(); loading.set(true); error.set(String::new()); success.set(false); spawn_local(async move { let payload = LoginScheme { username, pwd }; let response = Request::post("/api/login") .header("Content-Type", "application/json") .credentials(web_sys::RequestCredentials::Include) .json(&payload) .unwrap() .send() .await; loading.set(false); match response { Ok(r) if r.status() == 200 => { success.set(true); navigator.push(&crate::Route::Home); } Ok(r) => { let text = r.text().await.unwrap_or_else(|_| "unknown".into()); error.set(format!("HTTP {}: {}", r.status(), text)); } Err(err) => error.set(format!("Network error: {}", err)), } }); }) }; html! {
if !error.is_empty() {

{(*error).clone()}

}
} } #[component(AllUsers)] pub fn all_users_component() -> Html { let users = use_state(|| Vec::::new()); let error = use_state(|| None::); let loading = use_state(|| false); { let users = users.clone(); let error = error.clone(); let loading = loading.clone(); use_effect_with((), move |_| { loading.set(true); spawn_local(async move { let url = format!("/api/users"); match Request::get(&url).send().await { Ok(response) if response.status() == 200 => { match response.json::>().await { Ok(u) => users.set(u), Err(err) => error.set(Some(format!("Parse error: {}", err))), } } Ok(response) => { if let Ok(text) = response.text().await { error.set(Some(text)); } else { error.set(Some(format!("status {}", response.status()))); } } Err(err) => error.set(Some(format!("Network error: {}", err))), } loading.set(false); }); || () }); } if *loading { html! {

{ "Loading" }

} } else if let Some(e) = &*error { html! {

{ format!("Error: {}", e) }

} } else { html! {
    { for users.iter().map(|t| html! {
  • { format!("{} {}- #{}", t.first_name, t.last_name, t.id) }

  • })}
} } } #[component(UserByID)] pub fn user_by_id_component(props: &UserProps) -> Html { let user = use_state(|| None::); let error = use_state(|| None::); let loading = use_state(|| false); let id = props.id; { let user = user.clone(); let error = error.clone(); let loading = loading.clone(); use_effect_with(id, move |id_ref| { loading.set(true); let user = user.clone(); let error = error.clone(); let id = *id_ref; spawn_local(async move { let url = format!("/api/users/{}", id); match Request::get(&url).send().await { Ok(response) => { let status = response.status(); if status == 200 { match response.json::().await { Ok(u) => user.set(Some(u)), Err(err) => error.set(Some(format!("Parse error: {}", err))), } } else { match response.json::().await { Ok(ae) => error.set(Some(ae.message)), Err(_) => { if let Ok(text) = response.text().await { error.set(Some(text)); } else { error.set(Some(format!("Server error: {}", status))); } } } } } Err(err) => error.set(Some(format!("Network error: {}", err))), } loading.set(false); }); || () }); } let first_name = use_state(|| "".to_string()); let last_name = use_state(|| "".to_string()); let username = use_state(|| "".to_string()); let make_admin = use_state(|| false); let new_pwd = use_state(|| String::new()); let saving = use_state(|| false); let save_error = use_state(|| None::); let save_success = use_state(|| false); { let user = user.clone(); let first_name = first_name.clone(); let last_name = last_name.clone(); let username = username.clone(); let make_admin = make_admin.clone(); use_effect_with(user, move |user_ref| { if let Some(u) = &**user_ref { first_name.set(u.first_name.clone()); last_name.set(u.last_name.clone()); username.set(u.username.clone()); make_admin.set(u.is_admin); } || () }); } let onsubmit = { let first_name = first_name.clone(); let last_name = last_name.clone(); let username = username.clone(); let make_admin = make_admin.clone(); let new_pwd = new_pwd.clone(); let saving = saving.clone(); let save_error = save_error.clone(); let save_success = save_success.clone(); let user_state = user.clone(); Callback::from(move |e: SubmitEvent| { e.prevent_default(); let first_name = (*first_name).clone(); let last_name = (*last_name).clone(); let username = (*username).clone(); let make_admin = *make_admin; let new_pwd = (*new_pwd).clone(); saving.set(true); save_error.set(None); save_success.set(false); let saving = saving.clone(); let save_error = save_error.clone(); let save_success = save_success.clone(); let user_state = user_state.clone(); let id = id; spawn_local(async move { let payload = UserUpdateScheme { id: id, first_name, last_name, username, make_admin, new_pwd, }; let url = format!("/api/users/{}", id); let request = Request::patch(&url) .header("Content-Type", "application/json") .credentials(web_sys::RequestCredentials::Include) .json(&payload) .expect("Failed to construct Request"); match request.send().await { Ok(resp) if resp.status() == 200 => { if let Ok(updated) = resp.json::().await { user_state.set(Some(updated)); } save_success.set(true); } Ok(resp) => { let txt = resp.text().await.unwrap_or_else(|_| "Unknown".into()); save_error.set(Some(format!("HTTP {}: {}", resp.status(), txt))); } Err(err) => save_error.set(Some(format!("Network error: {}", err))), } saving.set(false); }); }) }; let deleting = use_state(|| false); let delete_error = use_state(|| None::); let ondelete = { let deleting = deleting.clone(); let delete_error = delete_error.clone(); let user_state = user.clone(); // or ticket let id = id; Callback::from(move |e: MouseEvent| { e.prevent_default(); // confirm if !web_sys::window() .and_then(|w| { w.confirm_with_message("Are you sure you want to delete this item?") .ok() }) .unwrap_or(false) { return; } deleting.set(true); delete_error.set(None); let deleting = deleting.clone(); let delete_error = delete_error.clone(); let user_state = user_state.clone(); spawn_local(async move { let url = format!("/api/users/{}", id); // or /api/tickets/{} let req = Request::delete(&url).credentials(web_sys::RequestCredentials::Include); match req.send().await { Ok(resp) if resp.status() == 200 || resp.status() == 204 => { // remove local state or navigate away user_state.set(None); // clears the shown item } Ok(resp) => { let txt = resp.text().await.unwrap_or_else(|_| "Unknown".into()); delete_error.set(Some(format!("HTTP {}: {}", resp.status(), txt))); } Err(err) => delete_error.set(Some(format!("Network error: {}", err))), } deleting.set(false); }); }) }; if *loading { html! {

{ "Loading" }

} } else if let Some(e) = &*error { html! {

{ format!("Error: {}", e) }

} } else if let Some(u) = &*user { html! {

{ "Vorname: " }{ &u.first_name }

{ "Nachname: " }{ &u.last_name }

{ "Benutzername: " }{ &u.username }

{ "Ist Admin: " }{ u.is_admin }

{ format!("User #{}", u.id) }

if *save_success {

{ "Updated successfully" }

} if let Some(err) = &*save_error {

{ err.clone() }

}
if let Some(err) = &*delete_error {

{ err.clone() }

}
} } else { html! {

{ "No ticket found." }

} } }