error component wip
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
|
use super::error::error_provider::ErrorContext;
|
||||||
use crate::services::rest::RestService;
|
use crate::services::rest::RestService;
|
||||||
|
use crate::services::rest::RestServiceError;
|
||||||
use common::CreateMilestone;
|
use common::CreateMilestone;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
@@ -13,6 +15,7 @@ use yew_router::scope_ext::RouterScopeExt;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
Submit,
|
Submit,
|
||||||
|
SubmitResult(Result<(), RestServiceError>),
|
||||||
UpdateInput(String),
|
UpdateInput(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ impl Component for CreateMilestoneComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
|
fn update(&mut self, ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Submit => {
|
Msg::Submit => {
|
||||||
log::info!("Creating achievement");
|
log::info!("Creating achievement");
|
||||||
@@ -49,16 +52,32 @@ impl Component for CreateMilestoneComponent {
|
|||||||
|
|
||||||
let goal = goal as usize;
|
let goal = goal as usize;
|
||||||
let payload = CreateMilestone { goal };
|
let payload = CreateMilestone { goal };
|
||||||
|
let link = ctx.link().clone();
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
match RestService::create_milestone(payload).await {
|
let res = RestService::create_milestone(payload).await;
|
||||||
Ok(_response) => {}
|
link.send_message(Msg::SubmitResult(res));
|
||||||
Err(_err) => {}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self.submitted = true;
|
self.submitted = true;
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
Msg::SubmitResult(result) => {
|
||||||
|
match result {
|
||||||
|
Ok(_response) => {
|
||||||
|
let nav = ctx.link().navigator().unwrap();
|
||||||
|
nav.push(&crate::Route::Root);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let (error_context, _context_listener) = ctx
|
||||||
|
.link()
|
||||||
|
.context(Callback::from(|_: ErrorContext| ()))
|
||||||
|
.expect("No Error Context Provided");
|
||||||
|
log::info!("dispatching error");
|
||||||
|
error_context.dispatch(err.to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
true
|
||||||
|
}
|
||||||
Msg::UpdateInput(value) => {
|
Msg::UpdateInput(value) => {
|
||||||
self.input_value = value;
|
self.input_value = value;
|
||||||
true
|
true
|
||||||
|
|||||||
68
crates/frontend/src/components/error/error_component.rs
Normal file
68
crates/frontend/src/components/error/error_component.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
use super::error_provider::ErrorContext;
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
pub enum Msg {
|
||||||
|
ErrorContextUpdated(ErrorContext),
|
||||||
|
HideError,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ErrorComponent {
|
||||||
|
error: Option<String>,
|
||||||
|
_context_listener: ContextHandle<ErrorContext>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for ErrorComponent {
|
||||||
|
type Message = Msg;
|
||||||
|
type Properties = ();
|
||||||
|
|
||||||
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
|
let (error_context, context_listener) = ctx
|
||||||
|
.link()
|
||||||
|
.context(ctx.link().callback(Msg::ErrorContextUpdated))
|
||||||
|
.expect("No Error Context Provided");
|
||||||
|
Self {
|
||||||
|
error: error_context.value.clone(),
|
||||||
|
_context_listener: context_listener,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::ErrorContextUpdated(error_context) => {
|
||||||
|
self.error = error_context.value.clone();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Msg::HideError => {
|
||||||
|
self.error = None;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
let onclick_hide = ctx.link().callback(|_| Msg::HideError);
|
||||||
|
html! {
|
||||||
|
<div>
|
||||||
|
{ self.view_error(onclick_hide) }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorComponent {
|
||||||
|
fn view_error(&self, onclick_hide: Callback<MouseEvent>) -> Html {
|
||||||
|
match &self.error {
|
||||||
|
Some(error_msg) => {
|
||||||
|
html! {
|
||||||
|
<div class="error-message">
|
||||||
|
{ error_msg }
|
||||||
|
<button onclick={onclick_hide}>
|
||||||
|
{ "Hide" }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => html! {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
crates/frontend/src/components/error/error_provider.rs
Normal file
34
crates/frontend/src/components/error/error_provider.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct Error {
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reducible for Error {
|
||||||
|
type Action = String;
|
||||||
|
|
||||||
|
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
||||||
|
Error { value: Some(action) }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ErrorContext = UseReducerHandle<Error>;
|
||||||
|
|
||||||
|
#[derive(Properties, Debug, PartialEq)]
|
||||||
|
pub struct ErrorProviderProps {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn ErrorProvider(props: &ErrorProviderProps) -> Html {
|
||||||
|
let err = use_reducer(Default::default);
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<ContextProvider<ErrorContext> context={err}>
|
||||||
|
{props.children.clone()}
|
||||||
|
</ContextProvider<ErrorContext>>
|
||||||
|
}
|
||||||
|
}
|
||||||
2
crates/frontend/src/components/error/mod.rs
Normal file
2
crates/frontend/src/components/error/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod error_component;
|
||||||
|
pub mod error_provider;
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
use crate::services::confirm::ConfirmService;
|
||||||
|
use crate::services::rest::RestService;
|
||||||
|
use common::DeleteMilestone;
|
||||||
use common::Milestone;
|
use common::Milestone;
|
||||||
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Properties, Clone, PartialEq)]
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
@@ -9,6 +13,8 @@ pub struct Props {
|
|||||||
|
|
||||||
#[function_component(MilestoneComponent)]
|
#[function_component(MilestoneComponent)]
|
||||||
pub fn milestone_component(props: &Props) -> Html {
|
pub fn milestone_component(props: &Props) -> Html {
|
||||||
|
let confirm_service = use_memo(|_| ConfirmService, ());
|
||||||
|
|
||||||
let unfilled = props.milestone.goal - props.completed_achievements.min(props.milestone.goal);
|
let unfilled = props.milestone.goal - props.completed_achievements.min(props.milestone.goal);
|
||||||
let filled = props.completed_achievements.min(props.milestone.goal);
|
let filled = props.completed_achievements.min(props.milestone.goal);
|
||||||
|
|
||||||
@@ -24,14 +30,32 @@ pub fn milestone_component(props: &Props) -> Html {
|
|||||||
.take(filled)
|
.take(filled)
|
||||||
.collect::<Html>();
|
.collect::<Html>();
|
||||||
|
|
||||||
|
let uuid = props.milestone.uuid;
|
||||||
|
let onclick_delete = Callback::from(move |_| {
|
||||||
|
if !confirm_service.confirm("Are you sure you want to delete?") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log::info!("Delete achievement confirmed.");
|
||||||
|
|
||||||
|
spawn_local(async move {
|
||||||
|
match RestService::delete_milestone(DeleteMilestone { uuid }).await {
|
||||||
|
Ok(_response) => {}
|
||||||
|
Err(_err) => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="row">
|
<div class="row flex">
|
||||||
<p style="text-align: center" class="u-full-width">
|
<p style="text-align: center" class="flex-grow">
|
||||||
{format!("{} / {}", props.completed_achievements, props.milestone.goal)}
|
{format!("{} / {}", props.completed_achievements, props.milestone.goal)}
|
||||||
<br />
|
<br />
|
||||||
{filled_stars}
|
{filled_stars}
|
||||||
{unfilled_stars}
|
{unfilled_stars}
|
||||||
</p>
|
</p>
|
||||||
|
<div class="flex-intrinsic-size">
|
||||||
|
<button onclick={onclick_delete} class="button color-danger"><i class="fas fa-trash"/></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ pub mod achievement;
|
|||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod create_achievement;
|
pub mod create_achievement;
|
||||||
pub mod create_milestone;
|
pub mod create_milestone;
|
||||||
|
pub mod error;
|
||||||
pub mod milestone;
|
pub mod milestone;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::components::error::error_component::ErrorComponent;
|
||||||
|
use crate::components::error::error_provider::ErrorProvider;
|
||||||
use components::admin::Admin;
|
use components::admin::Admin;
|
||||||
use components::create_achievement::CreateAchievementComponent;
|
use components::create_achievement::CreateAchievementComponent;
|
||||||
use components::create_milestone::CreateMilestoneComponent;
|
use components::create_milestone::CreateMilestoneComponent;
|
||||||
@@ -49,11 +51,14 @@ pub fn App() -> Html {
|
|||||||
|
|
||||||
html! {
|
html! {
|
||||||
<ContextProvider<AppState> context={(*ctx).clone()}>
|
<ContextProvider<AppState> context={(*ctx).clone()}>
|
||||||
<BrowserRouter>
|
<ErrorProvider>
|
||||||
<div class="container" style="margin-top: 5%; margin-bottom: 25%">
|
<div class="container" style="margin-top: 5%; margin-bottom: 25%">
|
||||||
|
<ErrorComponent />
|
||||||
|
<BrowserRouter>
|
||||||
<Switch<Route> render={switch}/>
|
<Switch<Route> render={switch}/>
|
||||||
</div>
|
</BrowserRouter>
|
||||||
</BrowserRouter>
|
</div>
|
||||||
|
</ErrorProvider>
|
||||||
</ContextProvider<AppState>>
|
</ContextProvider<AppState>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user