// src/tui/functions/common/login.rs use crate::services::auth::AuthClient; use crate::state::pages::auth::AuthState; use crate::state::pages::auth::LoginState; use crate::state::app::state::AppState; use crate::state::app::buffer::{AppView, BufferState}; use crate::state::pages::canvas_state::CanvasState; use crate::ui::handlers::context::DialogPurpose; use crate::modes::handlers::event::EventOutcome; // Make sure this is imported /// Attempts to log the user in using the provided credentials via gRPC. /// Updates AuthState and AppState on success or failure. /// (This is your existing function - remains unchanged) pub async fn save( auth_state: &mut AuthState, login_state: &mut LoginState, auth_client: &mut AuthClient, app_state: &mut AppState, ) -> Result> { let identifier = login_state.username.clone(); let password = login_state.password.clone(); // --- Client-side validation --- // Prevent login attempt if the identifier field is empty or whitespace. if identifier.trim().is_empty() { let error_message = "Username/Email cannot be empty.".to_string(); app_state.show_dialog( "Login Failed", &error_message, vec!["OK".to_string()], DialogPurpose::LoginFailed, ); login_state.error_message = Some(error_message.clone()); return Ok(error_message); } // Clear previous error/dialog state before attempting login_state.error_message = None; app_state.hide_dialog(); // Hide any previous dialog // Call the gRPC login method match auth_client.login(identifier, password).await { Ok(response) => { // Store authentication details using correct field names auth_state.auth_token = Some(response.access_token.clone()); auth_state.user_id = Some(response.user_id.clone()); auth_state.role = Some(response.role.clone()); auth_state.decoded_username = Some(response.username.clone()); login_state.set_has_unsaved_changes(false); login_state.error_message = None; // Format the success message using response data let success_message = format!( "Login Successful!\n\n\ Username: {}\n\ User ID: {}\n\ Role: {}", response.username, response.user_id, response.role ); app_state.show_dialog( "Login Success", &success_message, vec!["Menu".to_string(), "Exit".to_string()], DialogPurpose::LoginSuccess, ); login_state.password.clear(); login_state.username.clear(); login_state.current_cursor_pos = 0; Ok("Login successful, details shown in dialog.".to_string()) } Err(e) => { let error_message = format!("{}", e); app_state.show_dialog( "Login Failed", &error_message, vec!["OK".to_string()], DialogPurpose::LoginFailed, ); login_state.error_message = Some(error_message.clone()); login_state.set_has_unsaved_changes(true); login_state.username.clear(); login_state.password.clear(); Ok(format!("Login failed: {}", error_message)) } } } // --- Add this new function --- /// Sets the stage for login: shows loading dialog and sets the pending flag. /// Call this from the event handler when login is triggered. pub async fn initiate_login( app_state: &mut AppState, login_state: &mut LoginState, ) -> Result> { // Show the loading dialog immediately app_state.show_loading_dialog("Logging In", "Please wait..."); // Set the flag in LoginState to indicate the actual save should run next loop login_state.login_request_pending = true; // Return immediately to allow redraw Ok(EventOutcome::Ok("Login initiated.".to_string())) } // --- End of new function --- /// Reverts the login form fields to empty and returns to the previous screen (Intro). pub async fn revert( login_state: &mut LoginState, _app_state: &mut AppState, // Keep signature consistent if needed elsewhere ) -> String { // Clear the input fields login_state.username.clear(); login_state.password.clear(); login_state.error_message = None; login_state.set_has_unsaved_changes(false); login_state.login_request_pending = false; // Ensure flag is reset on revert "Login reverted".to_string() } pub async fn back_to_main( login_state: &mut LoginState, app_state: &mut AppState, buffer_state: &mut BufferState, ) -> String { // Clear the input fields login_state.username.clear(); login_state.password.clear(); login_state.error_message = None; login_state.set_has_unsaved_changes(false); login_state.login_request_pending = false; // Ensure flag is reset // Ensure dialog is hidden if revert is called app_state.hide_dialog(); // Navigation logic buffer_state.close_active_buffer(); buffer_state.update_history(AppView::Intro); // Reset focus state app_state.ui.focus_outside_canvas = false; app_state.focused_button_index= 0; "Returned to main menu".to_string() }