171 lines
6.1 KiB
Rust
171 lines
6.1 KiB
Rust
use crate::event_bus::{EventBus, Request};
|
|
use crate::events::Event;
|
|
use std::str::FromStr;
|
|
use web_sys::{HtmlInputElement, HtmlSelectElement};
|
|
use yew::{classes, html, Component, Context, Html, NodeRef, Properties};
|
|
use yew_agent::{Dispatched, Dispatcher};
|
|
|
|
pub enum Msg {
|
|
AmountChanged,
|
|
Deposit,
|
|
Withdraw,
|
|
Transfer,
|
|
}
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct AccountProps {
|
|
pub account_nrs: Vec<String>,
|
|
pub balance: f64,
|
|
pub nr: String,
|
|
pub owner: String,
|
|
}
|
|
|
|
pub struct Account {
|
|
amount_ref: NodeRef,
|
|
transfer_account_ref: NodeRef,
|
|
amount_valid: bool,
|
|
event_bus: Dispatcher<EventBus>,
|
|
}
|
|
|
|
impl Account {
|
|
fn amount(&self) -> f64 {
|
|
if let Some(amount_el) = self.amount_ref.cast::<HtmlInputElement>() {
|
|
f64::from_str(&amount_el.value()).unwrap_or_default()
|
|
} else {
|
|
0_f64
|
|
}
|
|
}
|
|
|
|
fn is_transfer_account_valid(&self) -> bool {
|
|
self.selected_transfer_account().is_some()
|
|
}
|
|
|
|
fn is_amount_valid(&self) -> bool {
|
|
self.amount() > 0_f64
|
|
}
|
|
|
|
fn selected_transfer_account(&self) -> Option<String> {
|
|
if let Some(transfer_account_el) = self.transfer_account_ref.cast::<HtmlSelectElement>() {
|
|
let value: String = transfer_account_el.value();
|
|
if value.is_empty() || value == "undefined" {
|
|
None
|
|
} else {
|
|
Some(value)
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Component for Account {
|
|
type Message = Msg;
|
|
type Properties = AccountProps;
|
|
|
|
fn create(_: &Context<Self>) -> Self {
|
|
Self {
|
|
amount_ref: NodeRef::default(),
|
|
transfer_account_ref: NodeRef::default(),
|
|
amount_valid: false,
|
|
event_bus: EventBus::dispatcher(),
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
match msg {
|
|
Msg::AmountChanged => {
|
|
let is_amount_valid = self.is_amount_valid();
|
|
let needs_redraw = is_amount_valid != self.amount_valid;
|
|
self.amount_valid = is_amount_valid;
|
|
|
|
needs_redraw
|
|
}
|
|
Msg::Deposit => {
|
|
let amount = self.amount() + ctx.props().balance;
|
|
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(
|
|
amount,
|
|
ctx.props().nr.clone(),
|
|
)));
|
|
|
|
false
|
|
}
|
|
Msg::Withdraw => {
|
|
let amount = ctx.props().balance - self.amount();
|
|
if amount > 0_f64 {
|
|
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(
|
|
amount,
|
|
ctx.props().nr.clone(),
|
|
)));
|
|
} else {
|
|
self.event_bus.send(Request::EventBusMsg(Event::ShowError(
|
|
"Balance can not be overdrawn".into(),
|
|
)));
|
|
}
|
|
|
|
false
|
|
}
|
|
Msg::Transfer => {
|
|
let amount = ctx.props().balance - self.amount();
|
|
|
|
if amount > 0_f64 {
|
|
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(
|
|
amount,
|
|
ctx.props().nr.clone(),
|
|
)));
|
|
} else {
|
|
let transfer_nr = self.selected_transfer_account();
|
|
if let Some(transfer_nr) = transfer_nr {
|
|
self.event_bus.send(Request::EventBusMsg(Event::Transfer(
|
|
amount,
|
|
ctx.props().nr.clone(),
|
|
transfer_nr,
|
|
)));
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
let onchange = ctx.link().callback(|_| Msg::AmountChanged);
|
|
|
|
let on_deposit = ctx.link().callback(|_| Msg::Deposit);
|
|
|
|
let on_withdraw = ctx.link().callback(|_| Msg::Withdraw);
|
|
|
|
let on_transfer = ctx.link().callback(|_| Msg::Transfer);
|
|
|
|
html! {
|
|
<>
|
|
<section class={classes!("account")}>
|
|
<fieldset class={classes!("account__grid")}>
|
|
<legend class={classes!("account__title")}>
|
|
{&ctx.props().owner} {" — "} {&ctx.props().balance}
|
|
</legend>
|
|
|
|
<div class={classes!("account__amount")}>
|
|
<label class={classes!("account__label")} for="amount">{"Amount"}</label>
|
|
<input {onchange} ref={self.amount_ref.clone()} class={classes!("account__input")} id="amount" type="number" step="0.01" min="0" placeholder="0.0" />
|
|
</div>
|
|
|
|
<div class={classes!("account__accounts")}>
|
|
<label class={classes!("account__label")} for="accounts">{"Remote Account"}</label>
|
|
<select ref={self.transfer_account_ref.clone()} class={classes!("account__input")} name="accounts" id="account-select">
|
|
{ for ctx.props().account_nrs.iter().filter(|e| {
|
|
ctx.props().nr != e.to_string()
|
|
}).map(|e| html!{ <option value={e.to_string()}>{e}</option> }) }
|
|
</select>
|
|
</div>
|
|
|
|
<button onclick={on_deposit} class={classes!("account__button", "account__deposit")} disabled={!self.amount_valid}>{"deposit"}</button>
|
|
<button onclick={on_withdraw} class={classes!("account__button", "account__withdraw")} disabled={!self.amount_valid}>{"withdraw"}</button>
|
|
<button onclick={on_transfer} class={classes!("account__button", "account__transfer")} disabled={!self.amount_valid || !self.is_transfer_account_valid()}>{"transfer"}</button>
|
|
</fieldset>
|
|
</section>
|
|
</>
|
|
}
|
|
}
|
|
}
|