diff --git a/src/app.rs b/src/app.rs index 9863147..71506cb 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,19 +1,49 @@ +use ratatui::widgets::ListState; use sqlite::Connection; +use crate::{data_layer, entities::Account}; + pub enum CurrentScreen { Main, Exiting, } +pub enum CurrentWidget { + AccountList, + TrxInfo, + TrxList, +} + +pub struct AccountList { + accounts: Vec, + pub state: ListState, +} +impl AccountList { + fn new() -> AccountList { + return AccountList { + accounts: Vec::new(), + state: ListState::default(), + }; + } + + fn get_accounts(&self, con: &Connection) -> Vec { + if self.accounts.iter().count() == 0 { + return data_layer::get_accounts(con); + } + return self.accounts.clone(); + } +} pub struct App { pub current_screen: CurrentScreen, - connection: Connection, + pub current_widget: CurrentWidget, + pub acc_list: AccountList, + pub connection: Connection, exit: bool, } impl App { pub fn new() -> App { - let connection = match Connection::open("ft_rs.db") { + let con = match Connection::open("ft_rs.db") { Ok(con) => con, Err(e) => { eprintln!("Error opening database: {}", e); @@ -22,8 +52,14 @@ impl App { }; return App { current_screen: CurrentScreen::Main, - connection: connection, + current_widget: CurrentWidget::AccountList, + acc_list: AccountList::new(), + connection: con, exit: false, }; } + + pub fn get_list_accounts(&self) -> Vec { + return self.acc_list.get_accounts(&self.connection); + } } diff --git a/src/main.rs b/src/main.rs index 4c215de..ea5ab7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use crate::{ app::{App, CurrentScreen}, ui::ui, }; +use app::CurrentWidget; use ratatui::{ Terminal, crossterm::{ @@ -55,6 +56,12 @@ fn run_app(terminal: &mut Terminal, app: &mut App) -> io::Result< KeyCode::Char('q') => { app.current_screen = CurrentScreen::Exiting; } + KeyCode::Right => { + app.current_widget = CurrentWidget::TrxList; + } + KeyCode::Left => { + app.current_widget = CurrentWidget::AccountList; + } _ => {} }, CurrentScreen::Exiting => match key.code { @@ -100,7 +107,7 @@ fn run_app(terminal: &mut Terminal, app: &mut App) -> io::Result< // match choice { // "1" => { -// let accounts = data_layer::get_accounts(&connection); +// let accounts = data_layer::get_accounts)(&connection); // let mut total = 0.0; // for ac in accounts.iter() { // let ac_total = ac.get_total(&connection); diff --git a/src/ui.rs b/src/ui.rs index 07e0c0e..39bfce8 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,16 +1,84 @@ -use crate::app::{App, CurrentScreen}; +use crate::{ + app::{App, CurrentScreen, CurrentWidget}, + entities::Account, +}; use ratatui::{ Frame, layout::{Constraint, Direction, Layout, Rect}, - style::{Color, Style}, - text::Text, - widgets::{Block, Borders, Clear, Paragraph, Wrap}, + style::{ + Color, Modifier, Style, Stylize, + palette::tailwind::{BLUE, GREEN, SLATE}, + }, + text::{Line, Text}, + widgets::{Block, Borders, Clear, HighlightSpacing, List, ListItem, Paragraph, Widget, Wrap}, }; -pub fn ui(frame: &mut Frame, app: &App) { - if let CurrentScreen::Exiting = app.current_screen { - frame.render_widget(Clear, frame.area()); +const NORMAL_ROW_BG: Color = SLATE.c950; +const ALT_ROW_BG_COLOR: Color = SLATE.c900; +const TEXT_FG_COLOR: Color = SLATE.c200; +const SELECTED_STYLE: Style = Style::new().bg(SLATE.c800).add_modifier(Modifier::BOLD); +pub fn ui(frame: &mut Frame, app: &App) { + let layout = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(20), Constraint::Percentage(80)]) + .split(frame.area()); + let right_layout = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![Constraint::Percentage(10), Constraint::Percentage(90)]) + .split(layout[1]); + + let mut ac_block = Block::default() + .title("Accounts") + .borders(Borders::ALL) + .border_style(Style::new().fg(Color::DarkGray)); + let mut info_block = Block::default() + .title("Account info") + .borders(Borders::ALL) + .border_style(Style::new().fg(Color::DarkGray)); + let mut trx_block = Block::default() + .borders(Borders::ALL) + .border_style(Style::new().fg(Color::DarkGray)); + + let active_style = Style::default(); + match app.current_widget { + CurrentWidget::AccountList => ac_block = ac_block.border_style(active_style), + CurrentWidget::TrxInfo => info_block = info_block.border_style(active_style), + CurrentWidget::TrxList => trx_block = trx_block.border_style(active_style), + }; + + let items: Vec = app + .get_list_accounts() + .iter() + .enumerate() + .map(|(i, acc)| { + let color = alternate_colors(i); + ListItem::from(acc).bg(color) + }) + .collect(); + + let list = List::new(items) + .block(ac_block) + .highlight_style(SELECTED_STYLE) + .highlight_symbol(">") + .highlight_spacing(HighlightSpacing::Always); + + frame.render_widget(list, layout[0]); + + let info = if let Some(i) = app.acc_list.state.selected() { + format!( + "Total: {}", + app.get_list_accounts()[i] + .get_total(&app.connection) + .to_string() + ) + } else { + "No account selected...".to_string() + }; + frame.render_widget(Paragraph::new(info).block(info_block), right_layout[0]); + frame.render_widget(Paragraph::new("inner 1").block(trx_block), right_layout[1]); + + if let CurrentScreen::Exiting = app.current_screen { let popup = Block::default() .title("Exiting program") .borders(Borders::all()) @@ -25,7 +93,7 @@ pub fn ui(frame: &mut Frame, app: &App) { .block(popup) .wrap(Wrap { trim: false }); - let area = centered_rect(60, 20, frame.area()); + let area = centered_rect(50, 20, frame.area()); frame.render_widget(exit_paragraph, area); } } @@ -49,3 +117,17 @@ fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { ]) .split(popup_layout[1])[1]; } +const fn alternate_colors(i: usize) -> Color { + if i % 2 == 0 { + NORMAL_ROW_BG + } else { + ALT_ROW_BG_COLOR + } +} + +impl From<&Account> for ListItem<'_> { + fn from(value: &Account) -> Self { + let line = Line::styled(value.get_name(), TEXT_FG_COLOR); + ListItem::new(line) + } +}