diff --git a/client/src/components/auth/login.rs b/client/src/components/auth/login.rs index b7a47fb..a1bed23 100644 --- a/client/src/components/auth/login.rs +++ b/client/src/components/auth/login.rs @@ -1,14 +1,14 @@ // src/components/auth/login.rs use crate::{ - components::form::form::render_generic_form, config::colors::themes::Theme, state::pages::auth::AuthState, }; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect, Margin}, - style::{Color, Style}, + style::{Color, Style, Modifier}, widgets::{Block, BorderType, Borders, Paragraph}, Frame, + text::{Line, Span}, }; pub fn render_login( @@ -17,10 +17,10 @@ pub fn render_login( theme: &Theme, state: &AuthState, ) { - // Main login block with plain borders (matches main form style) + // Main container let block = Block::default() .borders(Borders::ALL) - .border_type(BorderType::Plain) // Matches main form style + .border_type(BorderType::Plain) .border_style(Style::default().fg(theme.border)) .title(" Login ") .style(Style::default().bg(theme.bg)); @@ -32,49 +32,99 @@ pub fn render_login( vertical: 1, }); - // Define field names - let fields = &["Username/Email", "Password"]; - - // Split layout for form and buttons + // Layout chunks let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Min(3), // Form area - Constraint::Length(1), // Error message area - Constraint::Length(3), // Buttons area + Constraint::Length(4), // Form (2 fields + padding) + Constraint::Length(1), // Error message + Constraint::Length(3), // Buttons ]) .split(inner_area); - // Render form with plaintext display - render_generic_form( - f, - chunks[0], - "Login", - state, - fields, - theme, - !state.return_selected, // is_edit_mode - ); + // --- FORM RENDERING --- + let fields = &["Username/Email", "Password"]; + let inputs = &[&state.username, &state.password]; + let current_field = state.current_field; - // Render buttons + // Create input container (store the inner area before rendering) + let input_block = Block::default() + .borders(Borders::ALL) + .border_style(if !state.return_selected { + Style::default().fg(theme.accent) + } else { + Style::default().fg(theme.border) + }) + .style(Style::default().bg(theme.bg)); + + // Calculate inner area before consuming input_block + let input_area = input_block.inner(chunks[0]); + + // Now render the widget + f.render_widget(input_block, chunks[0]); + + let input_layout = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) + .split(input_area); + + // Render field labels + for (i, field) in fields.iter().enumerate() { + f.render_widget( + Paragraph::new(Line::from(Span::styled( + format!("{}:", field), + Style::default().fg(theme.fg), + ))), + Rect { + x: input_layout[0].x, + y: input_layout[0].y + i as u16, + width: input_layout[0].width, + height: 1, + }, + ); + } + + // Render input fields + let input_rows = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![Constraint::Length(1); fields.len()]) + .split(input_layout[1]); + + for (i, input) in inputs.iter().enumerate() { + let is_active = i == current_field; + let mut style = Style::default().fg(theme.fg); + if is_active { + style = style.fg(theme.highlight); + } + + f.render_widget( + Paragraph::new(input.as_str()).style(style), + input_rows[i], + ); + + // Set cursor position if active + if is_active { + f.set_cursor_position(( + input_rows[i].x + state.current_cursor_pos as u16, + input_rows[i].y, + )); + } + } + + // --- BUTTONS --- let button_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .split(chunks[2]); - // Login button - let login_style = if !state.return_selected { - Style::default() - .fg(theme.highlight) - .add_modifier(ratatui::style::Modifier::BOLD) - } else { - Style::default().fg(theme.fg) - }; - let login_border_style = if !state.return_selected { - Style::default().fg(theme.accent) - } else { - Style::default().fg(theme.border) - }; + // Login Button + let login_active = !state.return_selected; + let mut login_style = Style::default().fg(theme.fg); + let mut login_border = Style::default().fg(theme.border); + if login_active { + login_style = login_style.fg(theme.highlight).add_modifier(Modifier::BOLD); + login_border = login_border.fg(theme.accent); + } f.render_widget( Paragraph::new("Login") @@ -84,24 +134,19 @@ pub fn render_login( Block::default() .borders(Borders::ALL) .border_type(BorderType::Plain) - .border_style(login_border_style), + .border_style(login_border), ), button_chunks[0], ); - // Return button - let return_style = if state.return_selected { - Style::default() - .fg(theme.highlight) - .add_modifier(ratatui::style::Modifier::BOLD) - } else { - Style::default().fg(theme.fg) - }; - let return_border_style = if state.return_selected { - Style::default().fg(theme.accent) - } else { - Style::default().fg(theme.border) - }; + // Return Button + let return_active = state.return_selected; + let mut return_style = Style::default().fg(theme.fg); + let mut return_border = Style::default().fg(theme.border); + if return_active { + return_style = return_style.fg(theme.highlight).add_modifier(Modifier::BOLD); + return_border = return_border.fg(theme.accent); + } f.render_widget( Paragraph::new("Return") @@ -111,16 +156,18 @@ pub fn render_login( Block::default() .borders(Borders::ALL) .border_type(BorderType::Plain) - .border_style(return_border_style), + .border_style(return_border), ), button_chunks[1], ); - // Render error message if present + // Error message if let Some(err) = &state.error_message { - let err_block = Paragraph::new(err.as_str()) - .style(Style::default().fg(Color::Red)) - .alignment(Alignment::Center); - f.render_widget(err_block, chunks[1]); + f.render_widget( + Paragraph::new(err.as_str()) + .style(Style::default().fg(Color::Red)) + .alignment(Alignment::Center), + chunks[1], + ); } } diff --git a/client/src/components/form/form.rs b/client/src/components/form/form.rs index 47d349f..08f9ea2 100644 --- a/client/src/components/form/form.rs +++ b/client/src/components/form/form.rs @@ -65,47 +65,3 @@ pub fn render_form( is_edit_mode, ); } - -// New generic form renderer -pub fn render_generic_form( - f: &mut Frame, - area: Rect, - title: &str, - state: &impl CanvasState, - fields: &[&str], - theme: &Theme, - is_edit_mode: bool, -) { - // Create form card - let form_card = Block::default() - .borders(Borders::ALL) - .border_style(Style::default().fg(theme.border)) - .title(format!(" {} ", title)) - .style(Style::default().bg(theme.bg).fg(theme.fg)); - - f.render_widget(form_card, area); - - // Define inner area - let inner_area = area.inner(Margin { - horizontal: 1, - vertical: 1, - }); - - // Create main layout - let main_layout = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Min(1)]) - .split(inner_area); - - // Delegate to render_canvas - render_canvas( - f, - main_layout[0], - state, - fields, - &state.current_field(), - &state.inputs(), - theme, - is_edit_mode, - ); -} diff --git a/client/src/tui/functions/login.rs b/client/src/tui/functions/login.rs index 23ae6f9..9d636a5 100644 --- a/client/src/tui/functions/login.rs +++ b/client/src/tui/functions/login.rs @@ -10,18 +10,18 @@ pub async fn handle_action( match action { "move_up" => { if auth_state.return_selected { - // Coming from return button to fields + // From Return button to last field (password) auth_state.return_selected = false; - auth_state.current_field = 1; // Focus on password field + auth_state.current_field = 1; } else if auth_state.current_field == 1 { - // Moving from password to username/email + // Password -> Username auth_state.current_field = 0; } else if auth_state.current_field == 0 { - // Wrap around to buttons - auth_state.return_selected = false; // Select Login button + // Username -> Password (wrap around fields only) + auth_state.current_field = 1; } - // Update cursor position when in a field + // Update cursor position if !auth_state.return_selected { let current_input = auth_state.get_current_input(); let max_cursor_pos = current_input.len(); @@ -29,18 +29,18 @@ pub async fn handle_action( } Ok(format!("Navigation 'up' from functions/login")) - }, + } "move_down" => { if auth_state.return_selected { - // Coming from return button to fields + // From Return button to first field (username) auth_state.return_selected = false; - auth_state.current_field = 0; // Focus on username field + auth_state.current_field = 0; } else if auth_state.current_field == 0 { - // Moving from username/email to password + // Username -> Password auth_state.current_field = 1; } else if auth_state.current_field == 1 { - // Moving from password to buttons - auth_state.return_selected = false; // Select Login button + // Password -> Buttons (Login button) + auth_state.return_selected = false; } // Update cursor position when in a field @@ -51,7 +51,7 @@ pub async fn handle_action( } Ok(format!("Navigation 'down' from functions/login")) - }, + } _ => Err("Unknown login action".into()) } }