use crate::components::error::error_provider::ErrorContext; use crate::services::confirm::ConfirmService; use crate::services::rest::RestService; use common::DeleteAchievement; use common::DeleteMilestone; use common::UpdateAchievementTimeOfReveal; use std::rc::Rc; use wasm_bindgen::JsCast; use wasm_bindgen_futures::spawn_local; use yew::function_component; use yew::functional::*; use yew::html; use yew::use_state; use yew::Callback; use yew::Html; use yew::Properties; use yew_router::prelude::use_navigator; #[function_component] pub fn Admin() -> Html { let nav = use_navigator().expect("cannot get navigator"); let app_state = use_context::().expect("no app state ctx found"); let achievements = app_state .state .achievements .iter() .cloned() .enumerate() .map(|(idx, a)| (idx + 1, a)) .map(|(n, a)| { let uuid = a.uuid.to_string(); html! { } }) .collect::(); let onclick_create_achievement = { let nav = nav.clone(); Callback::from(move |_: web_sys::MouseEvent| { nav.push(&crate::Route::CreateAchievement); }) }; let onclick_create_milestone = Callback::from(move |_: web_sys::MouseEvent| { nav.push(&crate::Route::CreateMilestone); }); let mut milestones = app_state.state.milestones.clone(); milestones.sort_by_key(|m| m.goal); let milestones = milestones .into_iter() .map(|m| { let uuid = m.uuid.to_string(); html! { } }) .collect::(); html! { <>

{"Admin View"}


{"Milestones"}

{milestones}

{"Achievements"}

{achievements} } } #[derive(Properties, PartialEq)] struct AchievementProps { achievement: common::Achievement, number: usize, } #[function_component] fn Achievement(props: &AchievementProps) -> Html { let err_ctx = Rc::new(use_context::()); let achievement = &props.achievement; let uuid = achievement.uuid; let time_of_reveal: Option = achievement .time_of_reveal .map(|naive_time| naive_time.format("%H:%M").to_string()); let timed_reveal_enabled = use_state(|| time_of_reveal.is_some()); let input_time = use_state(|| time_of_reveal.clone().unwrap_or("".to_string())); let awaiting_response = use_state(|| false); let onsubmit_timed_reveal = { let awaiting_response = awaiting_response.clone(); let timed_reveal_enabled = timed_reveal_enabled.clone(); let input_time = input_time.clone(); let err_ctx = Rc::clone(&err_ctx); Callback::from(move |e: web_sys::SubmitEvent| { e.prevent_default(); let new_time_of_reveal = if *timed_reveal_enabled { if let Ok(naive_time) = chrono::NaiveTime::parse_from_str(&input_time, "%H:%M") { Some(naive_time) } else { // TODO: show UI error log::debug!("Could not parse time: {}", *input_time); return; } } else { None }; let payload = UpdateAchievementTimeOfReveal { time_of_reveal: new_time_of_reveal, uuid, }; awaiting_response.set(true); let awaiting_response = awaiting_response.clone(); let err_ctx = Rc::clone(&err_ctx); spawn_local(async move { match RestService::update_time_of_reveal(payload).await { Ok(_response) => {} Err(err) => { if let Some(err_ctx) = &*err_ctx { err_ctx.dispatch(err.to_string()); } } } awaiting_response.set(false); }); }) }; let oninput_time = { let input_time = input_time.clone(); Callback::from(move |e: web_sys::InputEvent| { let Some(input) = e .target() .and_then(|t| t.dyn_into::().ok()) else { unreachable!() }; log::debug!("{:?}", input.value()); input_time.set(input.value()); }) }; let oninput_timed_reveal_checkbox = { let timed_reveal_enabled = timed_reveal_enabled.clone(); Callback::from(move |e: web_sys::InputEvent| { let Some(input) = e .target() .and_then(|t| t.dyn_into::().ok()) else { unreachable!() }; timed_reveal_enabled.set(input.checked()); }) }; let onclick_delete = Callback::from(move |_| { if !ConfirmService::confirm("Are you sure you want to delete?") { return; } log::info!("Delete achievement confirmed."); let err_ctx = Rc::clone(&err_ctx); spawn_local(async move { match RestService::delete_achievement(DeleteAchievement { uuid }).await { Ok(_response) => {} Err(err) => { if let Some(err_ctx) = &*err_ctx { err_ctx.dispatch(err.to_string()); } } } }); }); let new_value: Option<&str> = timed_reveal_enabled.then_some(&**input_time); let show_submit_button: bool = time_of_reveal.as_deref() != new_value; html! {
// Achievement number

{format!("{}.", props.number)}

// Achievement text

{&achievement.goal}

// Delete button
// Timed reveal form
// Timed reveal: Enable checkbox
// Time input { if *timed_reveal_enabled { html! {
}} else { html! {}}} // Timed reveal form submit button { if show_submit_button { html! { }} else { html! {}}}
} } #[function_component] fn Milestone(props: &MilestoneProps) -> Html { let err_ctx = Rc::new(use_context::()); let uuid = props.milestone.uuid; let onclick_delete = Callback::from(move |_| { if !ConfirmService::confirm("Are you sure you want to delete?") { return; } let err_ctx = Rc::clone(&err_ctx); spawn_local(async move { match RestService::delete_milestone(DeleteMilestone { uuid }).await { Ok(_response) => {} Err(err) => { if let Some(err_ctx) = &*err_ctx { err_ctx.dispatch(err.to_string()); } } } }); }); html! {

{format!("Goal: {} achievements", props.milestone.goal)}

} } #[derive(Properties, Clone, PartialEq)] struct MilestoneProps { milestone: common::Milestone, }