From d4efdf4833ffd99f22194ceb81f58fd28692b797 Mon Sep 17 00:00:00 2001 From: filipriec Date: Thu, 27 Mar 2025 12:27:43 +0100 Subject: [PATCH] login improvements --- client/src/components/auth/login.rs | 145 +++++++++++++++++++++++----- client/src/state/pages/auth.rs | 21 ++++ 2 files changed, 143 insertions(+), 23 deletions(-) diff --git a/client/src/components/auth/login.rs b/client/src/components/auth/login.rs index 1916f17..cc73317 100644 --- a/client/src/components/auth/login.rs +++ b/client/src/components/auth/login.rs @@ -1,58 +1,157 @@ // src/components/auth/login.rs - +use crate::{ + components::handlers::canvas::render_canvas, // Import render_canvas + config::colors::themes::Theme, + state::{pages::auth::AuthState, pages::form::FormState}, // Import FormState +}; use ratatui::{ - layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::Style, + layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, + style::{Color, Style}, widgets::{Block, BorderType, Borders, Paragraph}, Frame, }; -use crate::{ - config::colors::themes::Theme, - state::pages::auth::AuthState -}; -pub fn render_login(f: &mut Frame, area: Rect, theme: &Theme, state: &mut AuthState) { +pub fn render_login( + f: &mut Frame, + area: Rect, + theme: &Theme, + state: &AuthState, // Changed to immutable borrow for rendering +) { + // Main login block (remains the same) let block = Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(Style::default().fg(theme.accent)) - .style(Style::default().bg(theme.bg)) - .title(" Login "); + .title(" Login ") + .style(Style::default().bg(theme.bg)); - let inner_area = block.inner(area); f.render_widget(block, area); + let inner_area = area.inner(Margin { + horizontal: 1, + vertical: 1, + }); + + // Define field names + let fields: &[&str] = &["Username/Email", "Password"]; + + // Prepare inputs for render_canvas (mask password) + // Note: We need an owned String for the masked password to get a reference. + let password_display_owned = "*".repeat(state.password.len()); + let inputs: &[&String] = &[&state.username, &password_display_owned]; + + // Determine if we are in "edit mode" (fields focused vs buttons focused) + let is_edit_mode = !state.return_selected; + + // --- Temporary FormState for render_canvas --- + // HACK: Create a temporary FormState to satisfy the render_canvas signature. + // This avoids modifying render_canvas for now, but ideally, render_canvas + // would be refactored to take cursor_pos and has_unsaved_changes directly. + let field_names_owned: Vec = + fields.iter().map(|&s| s.to_string()).collect(); + let mut temp_form_state = FormState::new(field_names_owned); + temp_form_state.current_cursor_pos = state.current_cursor_pos; + // We don't use the 'unsaved changes' warning border for login + temp_form_state.has_unsaved_changes = false; + // -------------------------------------------- + + // Adjusted layout: Canvas area, Error area, Buttons area let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Percentage(40), - Constraint::Length(3), - Constraint::Percentage(40), + // Height for canvas: fields.len() + 2 for borders + Constraint::Length(fields.len() as u16 + 2), + Constraint::Min(1), // Spacer/error message area + Constraint::Length(3), // Buttons area height ]) .split(inner_area); - let button_style = if state.return_selected { + let canvas_area = chunks[0]; + let error_area = chunks[1]; + let button_area = chunks[2]; // Use the last chunk for buttons + + // --- Render Input Fields using render_canvas --- + render_canvas( + f, + canvas_area, + &temp_form_state, // Pass the temporary state + fields, + &state.current_field, // Pass the current field index from AuthState + inputs, // Pass the prepared username and masked password + theme, + is_edit_mode, // Controls border color (accent when true) + ); + // --- End render_canvas --- + + // Render buttons (logic remains mostly the same, uses button_area) + let button_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(button_area); // Use the dedicated button_area + + // 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) + }; f.render_widget( - Paragraph::new("Return to Intro") - .style(button_style) + Paragraph::new("Login") + .style(login_style) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Plain) - .border_style(if state.return_selected { - Style::default().fg(theme.accent) - } else { - Style::default().fg(theme.border) - }), + .border_style(login_border_style), ), - chunks[1] + 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) + }; + + f.render_widget( + Paragraph::new("Return") + .style(return_style) + .alignment(Alignment::Center) + .block( + Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Plain) + .border_style(return_border_style), + ), + button_chunks[1], + ); + + // Render error message if present (uses error_area) + if let Some(err) = &state.error_message { + let err_block = Paragraph::new(err.as_str()) + .style(Style::default().fg(Color::Red)) // Keep explicit error color + .alignment(Alignment::Center); + f.render_widget(err_block, error_area); // Render in the middle area + } + + // Cursor position is now handled internally by render_canvas + // No need for manual f.set_cursor_position here } + diff --git a/client/src/state/pages/auth.rs b/client/src/state/pages/auth.rs index c59b6bc..296329d 100644 --- a/client/src/state/pages/auth.rs +++ b/client/src/state/pages/auth.rs @@ -6,6 +6,8 @@ pub struct AuthState { pub username: String, pub password: String, pub error_message: Option, + pub current_field: usize, + pub current_cursor_pos: usize, } impl AuthState { @@ -15,6 +17,25 @@ impl AuthState { username: String::new(), password: String::new(), error_message: None, + current_field: 0, + current_cursor_pos: 0, + } + } + + pub fn get_current_input_mut(&mut self) -> &mut String { + match self.current_field { + 0 => &mut self.username, + 1 => &mut self.password, + _ => panic!("Invalid current_field in AuthState"), + } + } + + pub fn get_current_input(&self) -> &str { + match self.current_field { + 0 => &self.username, + 1 => &self.password, + _ => panic!("Invalid current_field in AuthState"), } } } +