diff --git a/src/app.rs b/src/app.rs index bba371c..e3b8416 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,15 +1,15 @@ use std::sync::Arc; -use crossterm::event::{Event, self, KeyCode}; +use crossterm::event::{self, Event, KeyCode}; use tokio; use log::warn; -use crate::db::DB; use crate::db::data_cache::DataCache; +use crate::db::DB; use crate::uis::history::HistoryState; -use crate::uis::new_transaction::NewTransactionTabState; use crate::uis::navigation_frame::NavigationState; +use crate::uis::new_transaction::NewTransactionTabState; pub type AppResult = std::result::Result>; @@ -80,21 +80,19 @@ impl<'a> App<'a> { } } } - return Ok(()) + return Ok(()); } pub fn refresh(&mut self) { - let fut = Arc::clone(&self.db); - - tokio::spawn(async move { + + tokio::spawn(async move { let res = fut.lock().await.get_all_records().await; match res { - Ok(_) => {}, - Err(e) => warn!("{}", e) + Ok(_) => {} + Err(e) => warn!("{}", e), } }); - } } diff --git a/src/db/connection.rs b/src/db/connection.rs index 6888f32..03c18d1 100644 --- a/src/db/connection.rs +++ b/src/db/connection.rs @@ -1,8 +1,7 @@ - use futures::TryStreamExt; +use sqlx::postgres::PgPoolOptions; use sqlx::Error; use sqlx::PgPool; -use sqlx::postgres::PgPoolOptions; use super::data_cache::DataCache; use super::tables::Transaction; @@ -13,12 +12,12 @@ pub struct DB { data_cache: DataCache, } - impl DB { pub async fn new(data_cache: DataCache) -> Result { let connection_pool = PgPoolOptions::new() .max_connections(3) - .connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis").await?; + .connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis") + .await?; Ok(DB { conn_pool: connection_pool, data_cache, @@ -35,7 +34,6 @@ impl DB { temp_transactions.push(row); } - { let mut transactions = self.data_cache.transactions.lock().unwrap(); transactions.clear(); @@ -43,7 +41,5 @@ impl DB { } return Ok(()); - - } } diff --git a/src/db/data_cache.rs b/src/db/data_cache.rs index 59a769f..f482d25 100644 --- a/src/db/data_cache.rs +++ b/src/db/data_cache.rs @@ -1,11 +1,9 @@ use std::sync::{Arc, Mutex}; -use super::tables; - +use super::tables::{self, Account}; #[derive(Clone)] pub struct DataCache { - pub accounts: Arc>>, pub buckets: Arc>>, @@ -13,15 +11,13 @@ pub struct DataCache { pub transaction_catagories: Arc>>, pub transaction_breakdowns: Arc>>, - + pub transactions: Arc>>, - } - impl DataCache { pub fn new() -> DataCache { - DataCache { + DataCache { accounts: Arc::new(Mutex::new(Vec::new())), buckets: Arc::new(Mutex::new(Vec::new())), transaction_catagories: Arc::new(Mutex::new(Vec::new())), @@ -29,4 +25,16 @@ impl DataCache { transactions: Arc::new(Mutex::new(Vec::new())), } } + + pub fn find_account_by_id(&self, id: i32) -> Option { + { + let accounts = self.accounts.lock().unwrap(); + for i in accounts.iter() { + if i.acnt_id == id { + return Some(i.clone()); + } + } + } + return None; + } } diff --git a/src/db/mod.rs b/src/db/mod.rs index 52701cd..4dfa967 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,6 +1,5 @@ - pub mod connection; pub use self::connection::DB; -pub mod tables; pub mod data_cache; +pub mod tables; diff --git a/src/db/tables.rs b/src/db/tables.rs index ba4bab6..c455a1f 100644 --- a/src/db/tables.rs +++ b/src/db/tables.rs @@ -1,9 +1,8 @@ use std::borrow::Cow; -use sqlx::{FromRow, postgres::types::PgMoney}; +use sqlx::{postgres::types::PgMoney, FromRow}; use time::Date; - #[derive(Clone, FromRow)] pub struct Account { pub acnt_id: i32, @@ -11,7 +10,6 @@ pub struct Account { pub acnt_description: String, } - #[derive(Clone, FromRow)] pub struct Buckets { pub bkt_id: i32, @@ -20,7 +18,6 @@ pub struct Buckets { pub bkt_description: String, } - #[derive(Clone, FromRow)] pub struct TransactionCatagories { pub trns_ctgry_id: i32, @@ -29,7 +26,6 @@ pub struct TransactionCatagories { pub trns_ctgry_description: String, } - #[derive(Clone, FromRow)] pub struct TransactionBreakdown { pub trns_brkdwn_id: i32, @@ -39,27 +35,60 @@ pub struct TransactionBreakdown { pub trns_brkdwn_bucket: i32, } +#[derive(Clone)] +pub struct PartialTransactionBreakdown { + pub trns_brkdwn_id: Option, + pub trns_brkdwn_amount: Option, + pub trns_brkdwn_parent_transaction: Option, + pub trns_brkdwn_catagory: Option, + pub trns_brkdwn_bucket: Option, +} + +impl PartialTransactionBreakdown { + pub fn new_empty() -> PartialTransactionBreakdown { + PartialTransactionBreakdown { + trns_brkdwn_id: None, + trns_brkdwn_amount: None, + trns_brkdwn_parent_transaction: None, + trns_brkdwn_catagory: None, + trns_brkdwn_bucket: None, + } + } +} #[derive(Clone, FromRow)] -pub struct Transaction{ +pub struct Transaction { pub trns_id: i32, pub trns_amount: PgMoney, pub trns_description: String, pub trns_bucket: i32, - pub trns_date: Date + pub trns_date: Date, +} + +#[derive(Clone)] +pub struct PartialTransaction { + pub trns_id: Option, + pub trns_amount: Option, + pub trns_description: Option, + pub trns_bucket: Option, + pub trns_date: Option, } impl Transaction { - pub fn get_header() -> String { - return format!(" {:<7} | {:<9} | {:<10}", "Id", "Amount", "Date") + return format!(" {:<7} | {:<9} | {:<10}", "Id", "Amount", "Date"); } pub fn to_string(&self) -> String { - return format!(" T{:0>6} | {:>9} | {:>10}", self.trns_id, self.trns_amount.to_decimal(2).to_string(), self.trns_date.to_string()) + return format!( + " T{:0>6} | {:>9} | {:>10}", + self.trns_id, + self.trns_amount.to_decimal(2).to_string(), + self.trns_date.to_string() + ); } pub fn as_str(&self) -> Cow { - return self.to_string().into() + return self.to_string().into(); } } diff --git a/src/lib.rs b/src/lib.rs index 5f2124d..f0d9329 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,8 @@ - pub mod app; pub mod tui; pub mod ui; -pub mod uis; pub mod db; +pub mod uis; diff --git a/src/main.rs b/src/main.rs index d1ec030..e193538 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,18 @@ -use std::io; use std::fs::File; +use std::io; use std::time::Duration; use simplelog::*; -use ratatui::{ - backend::CrosstermBackend, - Terminal, -}; +use ratatui::{backend::CrosstermBackend, Terminal}; -use recount::db::DB; -use recount::db::data_cache::DataCache; use recount::app::{App, AppResult}; +use recount::db::data_cache::DataCache; +use recount::db::DB; use recount::tui::Tui; #[tokio::main] async fn main() -> AppResult<()> { - let log_file = "testing_log.txt".to_string(); init_logger(log_file); @@ -49,18 +45,17 @@ async fn main() -> AppResult<()> { tui.exit() } - pub fn init_logger(output_file: String) { // TODO: configure the log levels to something appropriate CombinedLogger::init(vec![ // TermLogger::new( - // LevelFilter::Info, - // Config::default(), - // TerminalMode::Mixed, - // ColorChoice::Auto, + // LevelFilter::Info, + // Config::default(), + // TerminalMode::Mixed, + // ColorChoice::Auto, // ), WriteLogger::new( - LevelFilter::Info, + LevelFilter::Debug, Config::default(), File::create(output_file).unwrap(), ), diff --git a/src/tui.rs b/src/tui.rs index e34a81c..f03ca54 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,10 +1,10 @@ -use crate::ui; use crate::app::{App, AppResult}; +use crate::ui; use crossterm::event::{DisableMouseCapture, EnableMouseCapture}; use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen}; -use std::io; use ratatui::backend::Backend; use ratatui::Terminal; +use std::io; /// Representation of a terminal user interface. /// diff --git a/src/ui.rs b/src/ui.rs index 8c81638..61155b0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,32 +1,39 @@ -use crate::{app::{App, ActiveFrame}, uis::render_history_tab}; +use crate::{ + app::{ActiveFrame, App}, + uis::render_history_tab, +}; +use log; use ratatui::{ backend::Backend, layout::{Constraint, Direction, Layout}, + style::Style, widgets::{Block, Borders}, - Frame, style::Style, + Frame, }; -use log; use crate::uis::{render_navigation_frame, render_new_transaction_tab}; - -pub fn render (f: &mut Frame, app: &mut App) { - +pub fn render(f: &mut Frame, app: &mut App) { let size = f.size(); let chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Length(3), Constraint::Min(0), Constraint::Length(3)].as_ref()) + .constraints( + [ + Constraint::Length(3), + Constraint::Min(0), + Constraint::Length(3), + ] + .as_ref(), + ) .split(size); - let bottom_block = Block::default() - .borders(Borders::ALL) - .border_style({ - if let ActiveFrame::Navigation = app.states.active_frame { - Style::default() - } else { - Style::default().fg(ratatui::style::Color::Green) - } - }); + let bottom_block = Block::default().borders(Borders::ALL).border_style({ + if let ActiveFrame::Navigation = app.states.active_frame { + Style::default() + } else { + Style::default().fg(ratatui::style::Color::Green) + } + }); f.render_widget(bottom_block, chunks[1]); render_navigation_frame(f, chunks[0], chunks[2], app); diff --git a/src/uis/history.rs b/src/uis/history.rs index a9e73f9..918f2bd 100644 --- a/src/uis/history.rs +++ b/src/uis/history.rs @@ -1,22 +1,26 @@ -use crossterm::event::{KeyEvent, KeyEventKind, KeyCode}; -use ratatui::{backend::Backend, Frame, layout::{Rect, Layout, Direction, Constraint}, widgets::{Block, Borders, List, ListItem, Paragraph}, text::{Text, Line, Span}, style::{Style, Color}}; use crate::{app::App, db::tables::Transaction}; - +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{ + backend::Backend, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Style}, + text::{Line, Span, Text}, + widgets::{Block, Borders, List, ListItem, Paragraph}, + Frame, +}; pub struct HistoryState { pub selected_index: Option, } impl HistoryState { - pub fn new() -> HistoryState { - HistoryState { + HistoryState { selected_index: None, } } pub fn handle_event(event: KeyEvent, app: &mut App) { - if app.states.history.selected_index.is_none() { app.states.history.selected_index = Some(0); } @@ -26,14 +30,13 @@ impl HistoryState { if event.kind == KeyEventKind::Press { match event.code { KeyCode::Up | KeyCode::Tab => app.states.history.next(max_index), - KeyCode::Down => app.states.history.previous(), + KeyCode::Down => app.states.history.previous(), _ => {} } } } pub fn next(&mut self, max_index: usize) { - if self.selected_index.is_some() { let cur_index = self.selected_index.unwrap(); if cur_index + 1 < max_index { @@ -43,19 +46,16 @@ impl HistoryState { } pub fn previous(&mut self) { - if self.selected_index.is_some() { let cur_index = self.selected_index.unwrap(); if cur_index > 0 { self.selected_index = Some(cur_index - 1); } } - } } -pub fn render_history_tab (f: &mut Frame, body_rect: Rect, app: &mut App) { - +pub fn render_history_tab(f: &mut Frame, body_rect: Rect, app: &mut App) { let split_body = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) @@ -65,45 +65,51 @@ pub fn render_history_tab (f: &mut Frame, body_rect: Rect, app: & { let transacts_list = app.data_cache.transactions.lock().unwrap(); if app.states.history.selected_index.is_some() { - let selected_item: &Transaction = &transacts_list[app.states.history.selected_index.unwrap()]; + let selected_item: &Transaction = + &transacts_list[app.states.history.selected_index.unwrap()]; let ident_style: Style = Style::default().fg(Color::Yellow); let value_style: Style = Style::default().fg(Color::LightBlue); - lines.push( - Line::from(vec![ - Span::styled("Transaction Id: ", ident_style), - Span::styled(format!("T{:0>6}", selected_item.trns_id), value_style), - ]) - ); - lines.push( - Line::from(vec![ - Span::styled("Amount: ", ident_style), - Span::styled(format!("{}", selected_item.trns_amount.to_decimal(2).to_string()), value_style) - ]) - ); - lines.push( - Line::from(vec![ - Span::styled("Transaction Date: ", ident_style), - Span::styled(format!("{}", selected_item.trns_date.to_string()), value_style) - ]) - ); + lines.push(Line::from(vec![ + Span::styled("Transaction Id: ", ident_style), + Span::styled(format!("T{:0>6}", selected_item.trns_id), value_style), + ])); + lines.push(Line::from(vec![ + Span::styled("Amount: ", ident_style), + Span::styled( + format!("{}", selected_item.trns_amount.to_decimal(2).to_string()), + value_style, + ), + ])); + lines.push(Line::from(vec![ + Span::styled("Transaction Date: ", ident_style), + Span::styled( + format!("{}", selected_item.trns_date.to_string()), + value_style, + ), + ])); } - let paragraph = Paragraph::new(lines.clone()) - .block(Block::default().borders(Borders::ALL).title("Details").title_alignment(ratatui::layout::Alignment::Left)); + let paragraph = Paragraph::new(lines.clone()).block( + Block::default() + .borders(Borders::ALL) + .title("Details") + .title_alignment(ratatui::layout::Alignment::Left), + ); f.render_widget(paragraph, split_body[1]); - let list_chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(1), Constraint::Min(3)]) .split(split_body[0]); - let list_header = Paragraph::new( - Text::styled(Transaction::get_header(), Style::default().fg(Color::Black).bg(Color::Cyan)) - ).block(Block::default().borders(Borders::NONE)); + let list_header = Paragraph::new(Text::styled( + Transaction::get_header(), + Style::default().fg(Color::Black).bg(Color::Cyan), + )) + .block(Block::default().borders(Borders::NONE)); f.render_widget(list_header, list_chunks[0]); @@ -117,19 +123,17 @@ pub fn render_history_tab (f: &mut Frame, body_rect: Rect, app: & } else { Style::default().fg(Color::Black).bg(Color::White) } - }, + } None => Style::default().fg(Color::Black).bg(Color::White), }; - items.push(ListItem::new( - Text::from(transacts_list.get(i).unwrap().as_str()) - ).style(style)); + items.push( + ListItem::new(Text::from(transacts_list.get(i).unwrap().as_str())).style(style), + ); } + let history_items = List::new(items).block(Block::default().borders(Borders::NONE)); - let history_items = List::new(items) - .block(Block::default().borders(Borders::NONE)); - - f.render_widget(history_items, list_chunks[1]); + f.render_widget(history_items, list_chunks[1]); } } diff --git a/src/uis/navigation_frame.rs b/src/uis/navigation_frame.rs index 225de46..340571d 100644 --- a/src/uis/navigation_frame.rs +++ b/src/uis/navigation_frame.rs @@ -1,7 +1,14 @@ -use crossterm::event::{KeyEvent, KeyEventKind, KeyCode}; -use ratatui::{widgets::{Borders, Paragraph, Block, Tabs}, backend::Backend, Frame, style::{Style, Color, Modifier}, text::{Text, Line}, layout::{Rect, Layout, Direction, Constraint}}; +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{ + backend::Backend, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style}, + text::{Line, Text}, + widgets::{Block, Borders, Paragraph, Tabs}, + Frame, +}; -use crate::app::{App, ActiveFrame}; +use crate::app::{ActiveFrame, App}; pub struct NavigationState<'a> { pub tabs: Vec<&'a str>, @@ -37,7 +44,7 @@ impl<'a> NavigationState<'a> { match self.tab_index { 0 => Some(ActiveFrame::History), 1 => Some(ActiveFrame::NewTransaction), - _ => todo!() + _ => todo!(), } } @@ -45,34 +52,39 @@ impl<'a> NavigationState<'a> { if event.kind == KeyEventKind::Press { match event.code { KeyCode::Tab => app.states.nav_state.next_tab(), - KeyCode::Enter => app.states.active_frame = app.states.nav_state.get_active_tab_frametype().unwrap(), + KeyCode::Enter => { + app.states.active_frame = + app.states.nav_state.get_active_tab_frametype().unwrap() + } _ => {} } } } } -pub fn render_navigation_frame (f: &mut Frame, status_rect: Rect, navbar_rect: Rect, app: &App) { - +pub fn render_navigation_frame( + f: &mut Frame, + status_rect: Rect, + navbar_rect: Rect, + app: &App, +) { let status_bar_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(75), Constraint::Min(10)]) .split(status_rect); - let left_block = Block::default() - .borders(Borders::ALL) - .border_style({ - if let ActiveFrame::Navigation = app.states.active_frame { - Style::default().fg(ratatui::style::Color::Green) - } else { - Style::default() - } - }); - let right_block = Block::default() - .borders(Borders::ALL); + let left_block = Block::default().borders(Borders::ALL).border_style({ + if let ActiveFrame::Navigation = app.states.active_frame { + Style::default().fg(ratatui::style::Color::Green) + } else { + Style::default() + } + }); + let right_block = Block::default().borders(Borders::ALL); - - let titles = app.states.nav_state + let titles = app + .states + .nav_state .tabs .iter() .cloned() @@ -93,11 +105,9 @@ pub fn render_navigation_frame (f: &mut Frame, status_rect: Rect, f.render_widget(tabs, status_bar_chunks[0]); - let connection_paragraph = Paragraph::new( - Text::styled("Aurora", - Style::default().fg(Color::Green) - ) - ).block(right_block); + let connection_paragraph = + Paragraph::new(Text::styled("Aurora", Style::default().fg(Color::Green))) + .block(right_block); f.render_widget(connection_paragraph, status_bar_chunks[1]); @@ -112,9 +122,7 @@ pub fn render_navigation_frame (f: &mut Frame, status_rect: Rect, app.states.nav_state.message.clone().unwrap() } }; - let bottom_navbar = Paragraph::new( - Text::styled(navbar, Style::default().fg(Color::White)) - ) + let bottom_navbar = Paragraph::new(Text::styled(navbar, Style::default().fg(Color::White))) .block(Block::default().borders(Borders::ALL)); f.render_widget(bottom_navbar, navbar_rect); } diff --git a/src/uis/new_transaction.rs b/src/uis/new_transaction.rs index ae54c06..3d84af4 100644 --- a/src/uis/new_transaction.rs +++ b/src/uis/new_transaction.rs @@ -1,17 +1,72 @@ -use crate::app::App; -use crossterm::event::{KeyEvent, KeyEventKind, KeyCode}; -use ratatui::{backend::Backend, Frame, layout::{Rect, Layout, Direction, Constraint}, widgets::{Paragraph, Borders, Block}, text::Text, style::{Style, Color}}; +use crate::{ + app::App, + db::tables::{Account, Buckets, PartialTransactionBreakdown}, +}; +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{ + backend::Backend, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Style}, + text::Text, + widgets::{Block, Borders, Paragraph}, + Frame, +}; +use rust_decimal::Decimal; +use time::Date; + +use log::debug; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum ManualDataFocus { + Account, + Amount, + Date, + Bucket, + Description, + Breakdowns, +} + +struct ManualData { + pub focus: Option, + + pub account: Option, + pub amount: Option, + pub date: Option, + pub bucket: Option, + pub description: Option, + + pub editing_breakdown: PartialTransactionBreakdown, + pub breakdowns: Vec, +} + +impl ManualData { + pub fn new() -> ManualData { + ManualData { + focus: None, + account: None, + amount: None, + date: None, + bucket: None, + description: None, + editing_breakdown: PartialTransactionBreakdown::new_empty(), + breakdowns: Vec::new(), + } + } +} pub struct NewTransactionTabState<'a> { pub cur_tab_index: usize, pub tabs: Vec<&'a str>, + + manual_data: ManualData, } impl<'a> NewTransactionTabState<'a> { pub fn new() -> NewTransactionTabState<'a> { NewTransactionTabState { cur_tab_index: 0, - tabs: vec!["Quick Entry", "Manual Entry"] + tabs: vec!["Quick Entry", "Manual Entry"], + manual_data: ManualData::new(), } } @@ -29,8 +84,7 @@ impl<'a> NewTransactionTabState<'a> { } } -pub fn render_new_transaction_tab (f: &mut Frame, body_rect: Rect, app: &App) { - +pub fn render_new_transaction_tab(f: &mut Frame, body_rect: Rect, app: &App) { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(3), Constraint::Min(0)]) @@ -39,7 +93,7 @@ pub fn render_new_transaction_tab (f: &mut Frame, body_rect: Rect // Render the custom tab bar let mut constraints: Vec = vec![]; let tab_percent: u16 = (100 / app.states.transactions.tabs.len()) as u16; - for _ in 0..app.states.transactions.tabs.len() { + for _ in 0..app.states.transactions.tabs.len() { constraints.push(Constraint::Percentage(tab_percent)); } @@ -48,41 +102,127 @@ pub fn render_new_transaction_tab (f: &mut Frame, body_rect: Rect .constraints(constraints) .split(chunks[0]); - for i in 0..app.states.transactions.tabs.len() { - - let tab = Paragraph::new( - Text::styled(app.states.transactions.tabs[i], - Style::default().fg(Color::White) - ) - ) - .alignment(ratatui::layout::Alignment::Center) - .block( - Block::default() - .borders(Borders::ALL) - .style({ - if app.states.transactions.cur_tab_index == i { - Style::default().bg(Color::Blue) - } else { - Style::default() - } - }) - ); + for i in 0..app.states.transactions.tabs.len() { + let tab = Paragraph::new(Text::styled( + app.states.transactions.tabs[i], + Style::default().fg(Color::White), + )) + .alignment(ratatui::layout::Alignment::Center) + .block(Block::default().borders(Borders::ALL).style({ + if app.states.transactions.cur_tab_index == i { + Style::default().bg(Color::Blue) + } else { + Style::default() + } + })); f.render_widget(tab, tab_chunks[i]) } match app.states.transactions.tabs[app.states.transactions.cur_tab_index] { "Quick Entry" => render_quick_entry(f, chunks[1], app), "Manual Entry" => render_manual_entry(f, chunks[1], app), - _ => return + _ => return, + }; +} + +pub fn render_manual_entry(f: &mut Frame, area: Rect, app: &App) { + let num_can_show = (area.height / 3) as usize; + let mut constraints: Vec = Vec::new(); + for _ in 0..num_can_show { + constraints.push(Constraint::Length(1)); + } + + debug!("Can show: {}", num_can_show); + debug!("saved {} constraints", constraints.len()); + + let split_body = Layout::default() + .direction(Direction::Vertical) + .constraints(constraints) + .split(area); + + { + let mut first_found = false; + let mut num_rendered = 0; + let manual_state = &app.states.transactions.manual_data; + + if manual_state.focus.is_none() + || matches!(manual_state.focus.unwrap(), ManualDataFocus::Account) + { + let right_text = match manual_state.account.clone() { + Some(val) => val.acnt_dsply_name, + None => "".to_string(), + }; + + render_manual_row( + f, + split_body[num_rendered * 2], + "Account: ", + right_text, + manual_state.focus, + ManualDataFocus::Account + ); + + first_found = true; + num_rendered = 1; + } + + if (first_found && num_rendered < num_can_show) + || matches!(manual_state.focus.unwrap(), ManualDataFocus::Amount) + { + let right_text = match manual_state.amount { + Some(val) => val.to_string(), + None => "".to_string(), + }; + + render_manual_row( + f, + split_body[num_rendered * 2], + "Amount: ", + right_text, + manual_state.focus, + ManualDataFocus::Amount + ); + + first_found = true; + num_rendered += 1; + } + } +} + +pub fn render_manual_row( + f: &mut Frame, + row_area: Rect, + left_text: &str, + right_text: String, + focus: Option, + matching: ManualDataFocus, +) { + let is_active = focus.map(|focus| focus == matching).unwrap_or(false); + + let horizontal_pieces = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(row_area); + + f.render_widget( + Paragraph::new(Text::styled(left_text, Style::default().fg(Color::Yellow))), + horizontal_pieces[0], + ); + + let right_bg_color = if is_active { + Color::Yellow + } else { + Color::White }; + f.render_widget( + Paragraph::new(Text::styled(right_text, Style::default().fg(Color::Black))).block( + Block::default() + .borders(Borders::NONE) + .style(Style::default().bg(right_bg_color)), + ), + horizontal_pieces[1], + ); } -pub fn render_manual_entry (f: &mut Frame, area: Rect, app: &App) { - -} - - -pub fn render_quick_entry (f: &mut Frame, area: Rect, app: &App) { - -} +pub fn render_quick_entry(f: &mut Frame, area: Rect, app: &App) {}