diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index ef3774c..f947785 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -460,22 +460,6 @@ impl EventHandler { // Let the current page handle decoupled movement first if let Some(ma) = movement_action { match &mut router.current { - // LOGIN: From buttons (general) back into the canvas with 'k' (Up), - // but ONLY from the left-most "Login" button. - Page::AddTable(state) => { - if state.handle_movement(ma) { - // Keep UI focus consistent with inputs vs. outer elements - use crate::pages::admin_panel::add_table::state::AddTableFocus; - let is_canvas_input = matches!( - state.current_focus, - AddTableFocus::InputTableName - | AddTableFocus::InputColumnName - | AddTableFocus::InputColumnType - ); - app_state.ui.focus_outside_canvas = !is_canvas_input; - return Ok(EventOutcome::Ok(String::new())); - } - } Page::Intro(state) => { if state.handle_movement(ma) { return Ok(EventOutcome::Ok(String::new())); @@ -506,8 +490,9 @@ impl EventHandler { if let Page::AddTable(add_table_state) = &mut router.current { let client_clone = self.grpc_client.clone(); let sender_clone = self.save_table_result_sender.clone(); - if add_table::nav::handle_add_table_navigation( + if crate::pages::admin_panel::add_table::event::handle_add_table_event( key_event, + movement_action, // wrapper handles both movement + nav config, app_state, add_table_state, @@ -515,9 +500,7 @@ impl EventHandler { sender_clone, &mut self.command_message, ) { - return Ok(EventOutcome::Ok( - self.command_message.clone(), - )); + return Ok(EventOutcome::Ok(self.command_message.clone())); } } diff --git a/client/src/pages/admin_panel/add_logic/loader.rs b/client/src/pages/admin_panel/add_logic/loader.rs new file mode 100644 index 0000000..c3b48bc --- /dev/null +++ b/client/src/pages/admin_panel/add_logic/loader.rs @@ -0,0 +1,108 @@ +// src/pages/admin_panel/add_logic/loader.rs +use anyhow::{Context, Result}; +use tracing::{error, info, warn}; + +use crate::pages::admin_panel::add_logic::state::AddLogicState; +use crate::pages::routing::{Page, Router}; +use crate::services::grpc_client::GrpcClient; +use crate::services::ui_service::UiService; +use crate::state::app::state::AppState; + +/// Process pending table structure fetch for AddLogic page. +/// Returns true if UI needs a redraw. +pub async fn process_pending_table_structure_fetch( + app_state: &mut AppState, + router: &mut Router, + grpc_client: &mut GrpcClient, + command_message: &mut String, +) -> Result { + let mut needs_redraw = false; + + if let Some((profile_name, table_name)) = app_state.pending_table_structure_fetch.take() { + if let Page::AddLogic(state) = &mut router.current { + if state.profile_name == profile_name + && state.selected_table_name.as_deref() == Some(table_name.as_str()) + { + info!( + "Fetching table structure for {}.{}", + profile_name, table_name + ); + + let fetch_message = UiService::initialize_add_logic_table_data( + grpc_client, + state, + &app_state.profile_tree, + ) + .await + .unwrap_or_else(|e| { + error!( + "Error initializing add_logic_table_data for {}.{}: {}", + profile_name, table_name, e + ); + format!("Error fetching table structure: {}", e) + }); + + if !fetch_message.contains("Error") && !fetch_message.contains("Warning") { + info!("{}", fetch_message); + } else { + *command_message = fetch_message; + } + + needs_redraw = true; + } else { + error!( + "Mismatch in pending_table_structure_fetch: app_state wants {}.{}, \ + but AddLogic state is for {}.{:?}", + profile_name, table_name, state.profile_name, state.selected_table_name + ); + } + } else { + warn!( + "Pending table structure fetch for {}.{} but AddLogic view is not active. Ignored.", + profile_name, table_name + ); + } + } + + Ok(needs_redraw) +} + +/// If the AddLogic page is awaiting columns for a selected table in the script editor, +/// fetch them and update the state. Returns true if UI needs a redraw. +pub async fn maybe_fetch_columns_for_awaiting_table( + grpc_client: &mut GrpcClient, + state: &mut AddLogicState, + command_message: &mut String, +) -> Result { + if let Some(table_name) = state + .script_editor_awaiting_column_autocomplete + .clone() + { + let profile_name = state.profile_name.clone(); + + info!( + "Fetching columns for table selection: {}.{}", + profile_name, table_name + ); + match UiService::fetch_columns_for_table(grpc_client, &profile_name, &table_name).await { + Ok(columns) => { + state.set_columns_for_table_autocomplete(columns.clone()); + info!("Loaded {} columns for table '{}'", columns.len(), table_name); + *command_message = + format!("Columns for '{}' loaded. Select a column.", table_name); + } + Err(e) => { + error!( + "Failed to fetch columns for {}.{}: {}", + profile_name, table_name, e + ); + state.script_editor_awaiting_column_autocomplete = None; + state.deactivate_script_editor_autocomplete(); + *command_message = format!("Error loading columns for '{}': {}", table_name, e); + } + } + return Ok(true); + } + + Ok(false) +} diff --git a/client/src/pages/admin_panel/add_logic/mod.rs b/client/src/pages/admin_panel/add_logic/mod.rs index 60e4320..64fadcc 100644 --- a/client/src/pages/admin_panel/add_logic/mod.rs +++ b/client/src/pages/admin_panel/add_logic/mod.rs @@ -3,3 +3,4 @@ pub mod ui; pub mod nav; pub mod state; +pub mod loader; diff --git a/client/src/pages/admin_panel/add_table/event.rs b/client/src/pages/admin_panel/add_table/event.rs new file mode 100644 index 0000000..b5ba3d6 --- /dev/null +++ b/client/src/pages/admin_panel/add_table/event.rs @@ -0,0 +1,51 @@ +// src/pages/admin_panel/add_table/event.rs + +use crate::config::binds::config::Config; +use crate::movement::MovementAction; +use crate::pages::admin_panel::add_table::nav; +use crate::pages::admin_panel::add_table::nav::SaveTableResultSender; +use crate::pages::admin_panel::add_table::state::{AddTableFocus, AddTableState}; +use crate::services::GrpcClient; +use crate::state::app::state::AppState; +use crossterm::event::KeyEvent; + +/// Handle all AddTable page-specific events in one place. +/// - First try movement actions (Up/Down/Select/Esc) via AddTableState::handle_movement +/// - Then, if not handled, try rich actions via nav::handle_add_table_navigation +/// +/// Returns true if the key was handled (caller should stop propagation). +pub fn handle_add_table_event( + key_event: KeyEvent, + movement_action: Option, + config: &Config, + app_state: &mut AppState, + state: &mut AddTableState, + grpc_client: GrpcClient, + save_result_sender: SaveTableResultSender, + command_message: &mut String, +) -> bool { + // 1) Try movement first (keeps focus cycling consistent) + if let Some(ma) = movement_action { + if state.handle_movement(ma) { + let is_canvas_input = matches!( + state.current_focus, + AddTableFocus::InputTableName + | AddTableFocus::InputColumnName + | AddTableFocus::InputColumnType + ); + app_state.ui.focus_outside_canvas = !is_canvas_input; + return true; + } + } + + // 2) Rich actions/navigation for AddTable + nav::handle_add_table_navigation( + key_event, + config, + app_state, + state, + grpc_client, + save_result_sender, + command_message, + ) +} diff --git a/client/src/pages/admin_panel/add_table/mod.rs b/client/src/pages/admin_panel/add_table/mod.rs index bb9d3fc..d432d8a 100644 --- a/client/src/pages/admin_panel/add_table/mod.rs +++ b/client/src/pages/admin_panel/add_table/mod.rs @@ -4,3 +4,4 @@ pub mod ui; pub mod nav; pub mod state; pub mod logic; +pub mod event; diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index d7dc7ea..2a33b5d 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -12,6 +12,7 @@ use crate::state::pages::auth::AuthState; use crate::state::pages::auth::UserRole; use crate::pages::login::LoginFormState; use crate::pages::register::RegisterFormState; +use crate::pages::admin_panel::add_logic; use crate::pages::admin::AdminState; use crate::pages::admin::AdminFocus; use crate::pages::admin::admin; @@ -485,67 +486,25 @@ pub async fn run_ui() -> Result<()> { } } - // Continue with the rest of the positioning logic... - // Now we can use CanvasState methods like get_current_input(), current_field(), etc. + let needs_redraw_from_fetch = add_logic::loader::process_pending_table_structure_fetch( + &mut app_state, + &mut router, + &mut grpc_client, + &mut event_handler.command_message, + ).await.unwrap_or(false); - if let Some((profile_name, table_name)) = app_state.pending_table_structure_fetch.take() { - if let Page::AddLogic(state) = &mut router.current { - if state.profile_name == profile_name - && state.selected_table_name.as_deref() == Some(table_name.as_str()) - { - info!("Fetching table structure for {}.{}", profile_name, table_name); - let fetch_message = UiService::initialize_add_logic_table_data( - &mut grpc_client, - state, - &app_state.profile_tree, - ) - .await - .unwrap_or_else(|e| { - error!("Error initializing add_logic_table_data: {}", e); - format!("Error fetching table structure: {}", e) - }); - - if !fetch_message.contains("Error") && !fetch_message.contains("Warning") { - info!("{}", fetch_message); - } else { - event_handler.command_message = fetch_message; - } - needs_redraw = true; - } else { - error!( - "Mismatch in pending_table_structure_fetch: app_state wants {}.{}, but AddLogic state is for {}.{:?}", - profile_name, - table_name, - state.profile_name, - state.selected_table_name - ); - } - } else { - warn!( - "Pending table structure fetch for {}.{} but AddLogic view is not active. Fetch ignored.", - profile_name, table_name - ); - } + if needs_redraw_from_fetch { + needs_redraw = true; } if let Page::AddLogic(state) = &mut router.current { - if let Some(table_name) = state.script_editor_awaiting_column_autocomplete.clone() { - let profile_name = state.profile_name.clone(); + let needs_redraw_from_columns = add_logic::loader::maybe_fetch_columns_for_awaiting_table( + &mut grpc_client, + state, + &mut event_handler.command_message, + ).await.unwrap_or(false); - info!("Fetching columns for table selection: {}.{}", profile_name, table_name); - match UiService::fetch_columns_for_table(&mut grpc_client, &profile_name, &table_name).await { - Ok(columns) => { - state.set_columns_for_table_autocomplete(columns.clone()); - info!("Loaded {} columns for table '{}'", columns.len(), table_name); - event_handler.command_message = format!("Columns for '{}' loaded. Select a column.", table_name); - } - Err(e) => { - error!("Failed to fetch columns for {}.{}: {}", profile_name, table_name, e); - state.script_editor_awaiting_column_autocomplete = None; - state.deactivate_script_editor_autocomplete(); - event_handler.command_message = format!("Error loading columns for '{}': {}", table_name, e); - } - } + if needs_redraw_from_columns { needs_redraw = true; } }