From 460f6e9c94f8762695e809c26c50a8bd2c3ca124 Mon Sep 17 00:00:00 2001 From: thatscringebro Date: Fri, 13 Mar 2026 11:41:57 -0400 Subject: [PATCH] modifier tr + ajout/modifier ac --- src/app.rs | 83 ++++++++++++++++++++++++++++++++++--- src/data_layer.rs | 31 ++++++++++---- src/entities/account.rs | 20 +++++++++ src/entities/accountlist.rs | 7 ++++ src/entities/trxtable.rs | 7 ++++ src/enums.rs | 7 ++++ src/main.rs | 24 +++++++++++ src/ui.rs | 49 +++++++++++++++++++++- 8 files changed, 213 insertions(+), 15 deletions(-) diff --git a/src/app.rs b/src/app.rs index 702fdca..0676246 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,6 +11,7 @@ pub struct App { pub new_account: Account, pub new_transaction: Transaction, pub selected_transaction_input: TransactionInput, + pub selected_account_input: AccountInput, pub current_input: String, pub connection: Connection, } @@ -35,6 +36,7 @@ impl App { new_account: Account::new(0, String::new(), 0.0, AccountType::Cash), new_transaction: Transaction::new_empty(), selected_transaction_input: TransactionInput::Type, + selected_account_input: AccountInput::Asset, current_input: String::new(), connection: con, }; @@ -66,15 +68,24 @@ impl App { } pub fn save_new_account(&mut self) { + let new = self.new_account.get_id() == 0; + self.next_ac_input(); let ac = data_layer::upsert_account(&self.connection, self.new_account.clone()); - self.acc_list.add_account(ac); + if new { + self.acc_list.add_account(ac); + } self.new_account = Account::new(0, String::new(), 0.0, AccountType::Cash); + self.current_input = String::new(); + self.selected_account_input = AccountInput::Asset; } pub fn save_new_tr(&mut self) { + let new = self.new_transaction.get_id() == 0; self.next_tr_input(); let tr = data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()); - self.trx_table.add_tr(tr); + if new { + self.trx_table.add_tr(tr); + } self.new_transaction = Transaction::new_empty(); self.current_input = String::new(); self.selected_transaction_input = TransactionInput::Type; @@ -119,7 +130,7 @@ impl App { ); } } - self.set_current_input(); + self.set_current_input_tr(); } pub fn previous_tr_input(&mut self) { match self.selected_transaction_input { @@ -160,9 +171,57 @@ impl App { ); } } - self.set_current_input(); + self.set_current_input_tr(); } - fn set_current_input(&mut self) { + pub fn next_ac_input(&mut self) { + match self.selected_account_input { + AccountInput::Asset => { + self.selected_account_input = AccountInput::Name; + self.new_account.set_asset_price( + self.current_input + .parse::() + .expect("Failed to parse data"), + ); + } + AccountInput::Name => { + self.selected_account_input = AccountInput::Type; + self.new_account.set_name(self.current_input.clone()); + } + AccountInput::Type => { + self.new_account.set_type(AccountType::from_usize( + self.current_input + .parse::() + .expect("Failed to parse data"), + )); + } + } + self.set_current_input_ac(); + } + pub fn previous_ac_input(&mut self) { + match self.selected_account_input { + AccountInput::Asset => { + self.new_account.set_asset_price( + self.current_input + .parse::() + .expect("Failed to parse data"), + ); + } + AccountInput::Name => { + self.new_account.set_name(self.current_input.clone()); + self.selected_account_input = AccountInput::Asset; + } + AccountInput::Type => { + self.new_account.set_type(AccountType::from_usize( + self.current_input + .parse::() + .expect("Failed to parse data"), + )); + self.selected_account_input = AccountInput::Name; + } + } + self.set_current_input_ac(); + } + pub fn set_current_input_tr(&mut self) { match self.selected_transaction_input { TransactionInput::Type => { self.current_input = self.new_transaction.get_type().get_id().to_string() @@ -183,4 +242,18 @@ impl App { } } } + pub fn set_current_input_ac(&mut self) { + match self.selected_account_input { + AccountInput::Asset => { + self.current_input = self.new_account.get_asset_price().to_string(); + } + AccountInput::Name => { + self.current_input = self.new_account.get_name(); + } + AccountInput::Type => { + let ac_type = self.new_account.get_ac_type() as usize; + self.current_input = ac_type.to_string(); + } + } + } } diff --git a/src/data_layer.rs b/src/data_layer.rs index 531d911..f563513 100644 --- a/src/data_layer.rs +++ b/src/data_layer.rs @@ -45,17 +45,20 @@ pub fn upsert_account(con: &Connection, ac: Account) -> Account { let query; let ac_type = ac.get_ac_type() as i64; if ac.get_id() == 0 { - query = "INSERT INTO Accounts (name, type) VALUES (?, ?) RETURNING id;"; + query = "INSERT INTO Accounts (name, type, asset_price) VALUES (:name, :type, :asset_price) RETURNING id;"; } else { - query = "UPDATE Accounts SET name = ?, type = ? WHERE id = ? RETURNING id;"; + query = "UPDATE Accounts SET name = :name, type = :type, asset_price = :asset_price WHERE id = :id RETURNING id;"; } let mut statement = con.prepare(query).unwrap(); - statement.bind((1, &ac.get_name() as &str)).unwrap(); - statement.bind((2, ac_type)).unwrap(); + statement.bind((":name", &ac.get_name() as &str)).unwrap(); + statement.bind((":type", ac_type)).unwrap(); + statement + .bind((":asset_price", ac.get_asset_price())) + .unwrap(); if ac.get_id() != 0 { - statement.bind((3, ac.get_id())).unwrap(); + statement.bind((":id", ac.get_id())).unwrap(); } let id; @@ -95,15 +98,25 @@ pub fn get_account(con: &Connection, id: i64) -> Account { } pub fn get_account_total(id: i64, con: &Connection) -> f64 { - let mut query = "SELECT SUM(amount) as total FROM Transactions".to_owned(); + let mut query = " +SELECT + SUM( + CASE + WHEN a.type = 2 THEN t.asset_amount * a.asset_price + ELSE t.amount + END + ) AS total +FROM Transactions t +JOIN Accounts a ON t.account_id = a.id" + .to_owned(); if id != 0 { - query.push_str(" WHERE account_id = ?") + query.push_str(" WHERE account_id = :id") } let mut statement = con.prepare(query).unwrap(); if id != 0 { - statement.bind((1, id)).unwrap(); + statement.bind((":id", id)).unwrap(); } if let Ok(State::Row) = statement.next() { @@ -200,7 +213,7 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction { RETURNING id;"; } else { query = "UPDATE Transactions - SET account_id = :ac_id, type_id = :type_id, amount = :amnt, date = :date, description = :desc, asset_amount = asset_amnt + SET account_id = :ac_id, type_id = :type_id, amount = :amnt, date = :date, description = :desc, asset_amount = :asset_amnt WHERE id = :id RETURNING id;"; } diff --git a/src/entities/account.rs b/src/entities/account.rs index e7f62e0..31b294a 100644 --- a/src/entities/account.rs +++ b/src/entities/account.rs @@ -27,12 +27,21 @@ impl Account { pub fn get_name(&self) -> String { return self.name.clone(); } + pub fn set_name(&mut self, name: String) { + self.name = name; + } pub fn get_asset_price(&self) -> f64 { return self.asset_price; } + pub fn set_asset_price(&mut self, asset_price: f64) { + self.asset_price = asset_price; + } pub fn get_ac_type(&self) -> AccountType { return AccountType::try_from(self.ac_type as i64).unwrap(); } + pub fn set_type(&mut self, ac_type: AccountType) { + self.ac_type = ac_type; + } pub fn get_total(&self, con: &Connection) -> f64 { return data_layer::get_account_total(self.id, con); } @@ -43,6 +52,17 @@ pub enum AccountType { Cash = 1, Assets, } +impl AccountType { + pub fn from_usize(v: usize) -> Self { + const CASH: usize = AccountType::Cash as usize; + const ASSET: usize = AccountType::Assets as usize; + match v { + CASH => return Self::Cash, + ASSET => return Self::Assets, + _ => return Self::Cash, + } + } +} impl TryFrom for AccountType { type Error = (); diff --git a/src/entities/accountlist.rs b/src/entities/accountlist.rs index 3969aff..cc3ebb5 100644 --- a/src/entities/accountlist.rs +++ b/src/entities/accountlist.rs @@ -32,6 +32,13 @@ impl AccountList { } } + pub fn selected_ac(&self) -> Account { + if let Some(i) = self.state.selected() { + return self.accounts.get(i).unwrap().clone(); + } + return Account::new(0, String::new(), 0.0, AccountType::Cash); + } + pub fn add_account(&mut self, ac: Account) { self.accounts.push(ac); } diff --git a/src/entities/trxtable.rs b/src/entities/trxtable.rs index 638ce87..39b7d43 100644 --- a/src/entities/trxtable.rs +++ b/src/entities/trxtable.rs @@ -30,6 +30,13 @@ impl TrxTable { return self.trx.clone(); } + pub fn selected_tr(&self) -> Transaction { + if let Some(i) = self.state.selected() { + return self.trx.get(i).unwrap().clone(); + } + return Transaction::new_empty(); + } + pub fn add_tr(&mut self, tr: Transaction) { self.trx.insert(0, tr); } diff --git a/src/enums.rs b/src/enums.rs index e23f813..00d3e26 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -17,3 +17,10 @@ pub enum TransactionInput { Description, Asset, } + +#[derive(PartialEq)] +pub enum AccountInput { + Asset, + Name, + Type, +} diff --git a/src/main.rs b/src/main.rs index 849531a..fe8803e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,9 +101,33 @@ where app.new_transaction.set_date(Local::now()); } }, + KeyCode::Char('m') => match app.current_widget { + CurrentWidget::AccountList => { + app.current_screen = CurrentScreen::NewAccount; + app.new_account = app.acc_list.selected_ac(); + app.set_current_input_ac(); + } + CurrentWidget::TrxList => { + app.current_screen = CurrentScreen::NewTransaction; + app.new_transaction = app.trx_table.selected_tr(); + app.set_current_input_tr(); + } + }, _ => {} }, CurrentScreen::NewAccount => match key.code { + KeyCode::Down => { + app.next_ac_input(); + } + KeyCode::Up => { + app.previous_ac_input(); + } + KeyCode::Backspace => { + app.current_input.pop(); + } + KeyCode::Char(value) => { + app.current_input.push(value); + } KeyCode::Enter => { app.save_new_account(); app.current_screen = CurrentScreen::Main; diff --git a/src/ui.rs b/src/ui.rs index 080391b..d2d5eee 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -112,6 +112,35 @@ pub fn ui(frame: &mut Frame, app: &mut App) { let area = centered_rect(50, 40, frame.area()); frame.render_widget(Clear, area); frame.render_widget(popup, area); + + let chunks = Layout::default() + .direction(Direction::Vertical) + .margin(1) + .constraints([ + Constraint::Length(3), + Constraint::Length(3), + Constraint::Length(3), + ]) + .split(area); + + let ac_asset = Paragraph::new(ac_input_str(AccountInput::Asset, app)) + .style(ac_input_style(AccountInput::Asset, app)) + .block( + Block::default() + .borders(Borders::BOTTOM) + .title("Asset Price"), + ); + frame.render_widget(ac_asset, chunks[0]); + + let ac_name = Paragraph::new(ac_input_str(AccountInput::Name, app)) + .style(ac_input_style(AccountInput::Name, app)) + .block(Block::default().borders(Borders::BOTTOM).title("Name")); + frame.render_widget(ac_name, chunks[1]); + + let ac_type = Paragraph::new(ac_input_str(AccountInput::Type, app)) + .style(ac_input_style(AccountInput::Type, app)) + .block(Block::default().borders(Borders::BOTTOM).title("Type")); + frame.render_widget(ac_type, chunks[2]); } if let CurrentScreen::NewTransaction = app.current_screen { @@ -233,7 +262,25 @@ fn tr_input_str(input: TransactionInput, app: &mut App) -> String { TransactionInput::Asset => app.new_transaction.get_asset_amnt().to_string(), } } - +fn ac_input_style(input: AccountInput, app: &mut App) -> Style { + if app.selected_account_input == input { + return SELECTED_STYLE; + } + return Style::default(); +} +fn ac_input_str(input: AccountInput, app: &mut App) -> String { + if app.selected_account_input == input { + return app.current_input.clone(); + } + match input { + AccountInput::Name => app.new_account.get_name(), + AccountInput::Type => { + let ac_type = app.new_account.get_ac_type() as usize; + return ac_type.to_string(); + } + AccountInput::Asset => app.new_account.get_asset_price().to_string(), + } +} impl From<&Account> for ListItem<'_> { fn from(value: &Account) -> Self { let line = Line::styled(