summaryrefslogtreecommitdiff
path: root/schist_desktop_gui/src/gui/components/new_transaction_form.rs
diff options
context:
space:
mode:
Diffstat (limited to 'schist_desktop_gui/src/gui/components/new_transaction_form.rs')
-rw-r--r--schist_desktop_gui/src/gui/components/new_transaction_form.rs185
1 files changed, 171 insertions, 14 deletions
diff --git a/schist_desktop_gui/src/gui/components/new_transaction_form.rs b/schist_desktop_gui/src/gui/components/new_transaction_form.rs
index 58509b1..7fb9b85 100644
--- a/schist_desktop_gui/src/gui/components/new_transaction_form.rs
+++ b/schist_desktop_gui/src/gui/components/new_transaction_form.rs
@@ -1,9 +1,16 @@
-use iced::{Element, widget::row};
+use iced::{Element, widget::{column, row}};
+use schist_models::{Account, Bucket, DateUtc, Transaction};
+use schist_traits::{Dateable, Nowlike};
-use crate::{gui::components::{Text, button, input::input}, style::SPACING_SM, traits::{Component, Viewable}};
+use crate::{gui::components::{Text, button, input::input}, style::{SPACING_SM, SPACING_V_SM}, traits::{Component, Viewable}};
#[derive(Clone, Debug)]
pub struct NewTransactionForm {
+ active_account: Option<Account>,
+ buckets: Vec<Bucket>,
+ this_year: String,
+ this_month: String,
+ today: String,
day: String,
month: String,
year: String,
@@ -11,6 +18,7 @@ pub struct NewTransactionForm {
payee: String,
quantity: String,
submit_text: Text,
+ validation_err: Option<String>,
}
#[derive(Clone, Debug)]
@@ -22,13 +30,21 @@ pub enum Message {
PayeeInputChanged(String),
QuantityInputChanged(String),
Submit,
+ SetActiveAccount(Account),
+ SetBuckets(Vec<Bucket>),
}
-pub enum Action { None }
+pub enum Action { None, AddTransaction(Transaction) }
impl NewTransactionForm {
- pub fn new() -> Self {
+ pub fn new(active_account: Option<Account>, buckets: Vec<Bucket>) -> Self {
+ let today = DateUtc::now();
Self {
+ active_account,
+ buckets,
+ this_year: today.year().to_string(),
+ this_month: today.month().to_string(),
+ today: today.day().to_string(),
day: String::new(),
month: String::new(),
year: String::new(),
@@ -36,27 +52,168 @@ impl NewTransactionForm {
payee: String::new(),
quantity: String::new(),
submit_text: Text::new("+ Add"),
+ validation_err: None,
}
}
+
+ fn reset(&mut self) {
+ self.day = String::new();
+ self.month = String::new();
+ self.year = String::new();
+ self.bucket = String::new();
+ self.payee = String::new();
+ self.quantity = String::new();
+ self.validation_err = None;
+ }
+
+ fn create_transaction_or_else_validation_error(&self) -> Result<Transaction, String> {
+ Ok(Transaction::new(
+ self.get_valid_account_id_or_else_validation_error()?,
+ self.get_valid_quantity_or_else_validation_error()?,
+ self.get_valid_bucket_id_or_else_validation_error()?,
+ &self.payee,
+ self.get_valid_date_or_else_validation_error()?,
+ ""))
+ }
+
+ fn get_valid_account_id_or_else_validation_error(&self) -> Result<i32, String> {
+ match &self.active_account {
+ Option::Some(account) => Ok(account.id),
+ Option::None => Err(format!("No account selected: select an account to add a transaction.")),
+ }
+ }
+
+ fn get_valid_bucket_id_or_else_validation_error(&self) -> Result<Option<i32>, String> {
+ if self.bucket.trim().len() == 0 {
+ return Ok(None);
+ };
+ match self.buckets.iter().find(|b| String::eq(&b.name, &self.bucket)) {
+ Option::Some(bucket) => Ok(Some(bucket.id)),
+ Option::None => Err(format!(
+ "There's no bucket called '{}': check the buckets view to list existing buckets or to add a new one.",
+ self.bucket)),
+ }
+ }
+
+ fn get_valid_date_or_else_validation_error(&self) -> Result<DateUtc, String> {
+ let day = if self.day.trim().len() == 0 {
+ Ok(DateUtc::now().day())
+ } else {
+ self.day.trim().parse::<u32>()
+ };
+ let month = self.get_valid_month_or_else_validation_error()?;
+ let year = self.get_valid_year_or_else_validation_error()?;
+ let first_day_of_month = DateUtc::from_ymd(year, month, 1)
+ .map_err(|err| format!("Unexpected error: {}", err))?;
+ let days_in_month = first_day_of_month.days_in_month();
+ match day {
+ Err(_) => Err(format!("'{}' is not a valid day. Choose a number between 1 and {}.", self.day, days_in_month)),
+ Ok(day) => if day > 0 && day <= days_in_month {
+ DateUtc::from_ymd(year, month, day)
+ .map_err(|err| format!("Unexpected error: {}", err))
+ } else {
+ Err(format!("'{}' is not a valid day: there are only {} days in {}. Choose a number between 1 and {}.",
+ self.day, days_in_month, first_day_of_month.format("%m %Y"), days_in_month))
+ }
+ }
+ }
+
+ fn get_valid_month_or_else_validation_error(&self) -> Result<u32, String> {
+ if self.month.trim().len() == 0 {
+ return Ok(DateUtc::now().month())
+ };
+ match self.month.trim().parse::<u32>() {
+ Err(_) => Err(format!("'{}' is not a valid month. Choose a number between 1 and 12.", self.month)),
+ Ok(month) => if month > 0 && month < 13 {
+ Ok(month)
+ } else {
+ Err(format!("'{}' is not a valid month. Choose a number between 1 and 12.", self.month))
+ }
+ }
+ }
+
+ fn get_valid_year_or_else_validation_error(&self) -> Result<i32, String> {
+ if self.year.trim().len() == 0 {
+ return Ok(DateUtc::now().year())
+ };
+ self.year.trim().parse::<i32>().map_err(|_| format!("'{}' is not a valid year.", self.year))
+ }
+
+ fn get_valid_quantity_or_else_validation_error(&self) -> Result<i32, String> {
+ if self.quantity.trim().len() == 0 {
+ return Err(String::from("No quantity provided: insert a quantity."));
+ };
+ let pounds = self.quantity.parse::<f32>()
+ .map_err(|_| format!("'{}' is not a valid quantity. Use a number.", self.quantity))?;
+ Ok((pounds * 100.0).floor() as i32)
+ }
}
impl<'a> Viewable<'a, Message> for NewTransactionForm {
fn view(&'a self) -> Element<'a, Message> {
- row![
- input("25").on_input(Message::DayInputChanged).width(32.0),
- input("12").on_input(Message::MonthInputChanged).width(32.0),
- input("2026").on_input(Message::YearInputChanged).width(48.0),
- input("").on_input(Message::BucketInputChanged).width(138.0),
- input("").on_input(Message::PayeeInputChanged).width(266.0),
- input("").on_input(Message::QuantityInputChanged).width(104.0),
- button(&self.submit_text, Message::Submit, false).width(64.0),
- ].spacing(u32::from(SPACING_SM))
+ column![
+ Text::new(&self.validation_err.clone().unwrap_or_else(String::new)).danger().small(),
+ row![
+ input(&self.today, &self.day).on_input(Message::DayInputChanged).width(32.0),
+ input(&self.this_month, &self.month).on_input(Message::MonthInputChanged).width(32.0),
+ input(&self.this_year, &self.year).on_input(Message::YearInputChanged).width(48.0),
+ input("", &self.bucket).on_input(Message::BucketInputChanged).width(138.0),
+ input("", &self.payee).on_input(Message::PayeeInputChanged).width(266.0),
+ input("", &self.quantity).on_input(Message::QuantityInputChanged).width(104.0),
+ button(&self.submit_text, Message::Submit, false).width(64.0),
+ ].spacing(u32::from(SPACING_SM)),
+ ].spacing(u32::from(SPACING_V_SM))
.into()
}
}
impl<'a> Component<'a, Message, Action> for NewTransactionForm {
fn update(&mut self, message: Message) -> Action {
- Action::None
+ match message {
+ Message::DayInputChanged(day) => {
+ self.day = day;
+ Action::None
+ }
+ Message::MonthInputChanged(month) => {
+ self.month = month;
+ Action::None
+ }
+ Message::YearInputChanged(year) => {
+ self.year = year;
+ Action::None
+ }
+ Message::BucketInputChanged(bucket) => {
+ self.bucket = bucket;
+ Action::None
+ }
+ Message::PayeeInputChanged(payee) => {
+ self.payee = payee;
+ Action::None
+ }
+ Message::QuantityInputChanged(quantity) => {
+ self.quantity = quantity;
+ Action::None
+ }
+ Message::SetActiveAccount(account) => {
+ self.active_account = Some(account);
+ Action::None
+ }
+ Message::SetBuckets(buckets) => {
+ self.buckets = buckets;
+ Action::None
+ }
+ Message::Submit => {
+ match self.create_transaction_or_else_validation_error() {
+ Ok(transaction) => {
+ self.reset();
+ Action::AddTransaction(transaction)
+ }
+ Err(validation_err) => {
+ self.validation_err = Some(validation_err);
+ Action::None
+ }
+ }
+ }
+ }
}
}