From 5d97e63f937e582811ba3269b12439d1a6288199 Mon Sep 17 00:00:00 2001 From: Priec Date: Fri, 22 Aug 2025 22:52:20 +0200 Subject: [PATCH] router2, needs bug fixes --- .../modes/navigation/add_logic_nav.rs | 6 +- client/src/modes/canvas/common_mode.rs | 103 +++--- client/src/modes/common/commands.rs | 25 +- client/src/modes/handlers/event.rs | 312 ++++++++---------- client/src/modes/handlers/mode_manager.rs | 63 ++-- client/src/ui/handlers/render.rs | 15 +- client/src/ui/handlers/ui.rs | 148 +++++---- 7 files changed, 337 insertions(+), 335 deletions(-) diff --git a/client/src/functions/modes/navigation/add_logic_nav.rs b/client/src/functions/modes/navigation/add_logic_nav.rs index 2e4fd68..adc105f 100644 --- a/client/src/functions/modes/navigation/add_logic_nav.rs +++ b/client/src/functions/modes/navigation/add_logic_nav.rs @@ -11,7 +11,8 @@ use tokio::sync::mpsc; use anyhow::Result; use crate::components::common::text_editor::TextEditor; use crate::services::ui_service::UiService; -use tui_textarea::CursorMove; // Ensure this import is present +use tui_textarea::CursorMove; +use crate::pages::routing::{Router, Page}; pub type SaveLogicResultSender = mpsc::Sender>; @@ -25,6 +26,7 @@ pub fn handle_add_logic_navigation( grpc_client: GrpcClient, _save_logic_sender: SaveLogicResultSender, // Marked as unused command_message: &mut String, + router: &mut Router, ) -> bool { // === FULLSCREEN SCRIPT EDITING - COMPLETE ISOLATION === if add_logic_state.current_focus == AddLogicFocus::InsideScriptContent { @@ -380,7 +382,7 @@ pub fn handle_add_logic_navigation( } AddLogicFocus::CancelButton => { buffer_state.update_history(AppView::Admin); - app_state.ui.show_add_logic = false; + router.navigate(Page::Admin(app_state.admin_state().clone())); *command_message = "Cancelled Add Logic".to_string(); *is_edit_mode = false; } diff --git a/client/src/modes/canvas/common_mode.rs b/client/src/modes/canvas/common_mode.rs index cbdc846..388bec7 100644 --- a/client/src/modes/canvas/common_mode.rs +++ b/client/src/modes/canvas/common_mode.rs @@ -1,7 +1,7 @@ // src/modes/canvas/common_mode.rs use crate::tui::terminal::core::TerminalCore; -use crate::state::pages::{form::FormState, auth::LoginState, auth::RegisterState, auth::AuthState}; +use crate::state::pages::auth::AuthState; use crate::state::app::state::AppState; use crate::services::grpc_client::GrpcClient; use crate::services::auth::AuthClient; @@ -13,74 +13,83 @@ use crate::tui::functions::common::{ login::{save as login_save, revert as login_revert}, register::{revert as register_revert}, }; +use crate::pages::routing::{Router, Page}; pub async fn handle_core_action( action: &str, - form_state: &mut FormState, auth_state: &mut AuthState, - login_state: &mut LoginState, - register_state: &mut RegisterState, grpc_client: &mut GrpcClient, auth_client: &mut AuthClient, terminal: &mut TerminalCore, app_state: &mut AppState, + router: &mut Router, ) -> Result { match action { "save" => { - if app_state.ui.show_login { - let message = login_save(auth_state, login_state, auth_client, app_state).await.context("Login save action failed")?; - Ok(EventOutcome::Ok(message)) - } else { - let save_outcome = form_save( - app_state, - form_state, - grpc_client, - ).await.context("Register save action failed")?; - let message = match save_outcome { - SaveOutcome::NoChange => "No changes to save.".to_string(), - SaveOutcome::UpdatedExisting => "Entry updated.".to_string(), - SaveOutcome::CreatedNew(_) => "New entry created.".to_string(), - }; - Ok(EventOutcome::DataSaved(save_outcome, message)) + match &mut router.current { + Page::Login(state) => { + let message = login_save(auth_state, state, auth_client, app_state) + .await + .context("Login save action failed")?; + Ok(EventOutcome::Ok(message)) + } + Page::Form(form_state) => { + let save_outcome = form_save(app_state, form_state, grpc_client) + .await + .context("Form save action failed")?; + let message = match save_outcome { + SaveOutcome::NoChange => "No changes to save.".to_string(), + SaveOutcome::UpdatedExisting => "Entry updated.".to_string(), + SaveOutcome::CreatedNew(_) => "New entry created.".to_string(), + }; + Ok(EventOutcome::DataSaved(save_outcome, message)) + } + _ => Ok(EventOutcome::Ok("Save not applicable".into())), } - }, + } "force_quit" => { terminal.cleanup()?; Ok(EventOutcome::Exit("Force exiting without saving.".to_string())) - }, + } "save_and_quit" => { - let message = if app_state.ui.show_login { - login_save(auth_state, login_state, auth_client, app_state).await.context("Login save n quit action failed")? - } else { - let save_outcome = form_save( - app_state, - form_state, - grpc_client, - ).await?; - match save_outcome { - SaveOutcome::NoChange => "No changes to save.".to_string(), - SaveOutcome::UpdatedExisting => "Entry updated.".to_string(), - SaveOutcome::CreatedNew(_) => "New entry created.".to_string(), + let message = match &mut router.current { + Page::Login(state) => { + login_save(auth_state, state, auth_client, app_state) + .await + .context("Login save and quit action failed")? } + Page::Form(form_state) => { + let save_outcome = form_save(app_state, form_state, grpc_client).await?; + match save_outcome { + SaveOutcome::NoChange => "No changes to save.".to_string(), + SaveOutcome::UpdatedExisting => "Entry updated.".to_string(), + SaveOutcome::CreatedNew(_) => "New entry created.".to_string(), + } + } + _ => "Save not applicable".to_string(), }; terminal.cleanup()?; Ok(EventOutcome::Exit(format!("{}. Exiting application.", message))) - }, + } "revert" => { - if app_state.ui.show_login { - let message = login_revert(login_state, app_state).await; - Ok(EventOutcome::Ok(message)) - } else if app_state.ui.show_register { - let message = register_revert(register_state, app_state).await; - Ok(EventOutcome::Ok(message)) - } else { - let message = form_revert( - form_state, - grpc_client, - ).await.context("Form revert x action failed")?; - Ok(EventOutcome::Ok(message)) + match &mut router.current { + Page::Login(state) => { + let message = login_revert(state, app_state).await; + Ok(EventOutcome::Ok(message)) + } + Page::Register(state) => { + let message = register_revert(state, app_state).await; + Ok(EventOutcome::Ok(message)) + } + Page::Form(form_state) => { + let message = form_revert(form_state, grpc_client) + .await + .context("Form revert action failed")?; + Ok(EventOutcome::Ok(message)) + } + _ => Ok(EventOutcome::Ok("Revert not applicable".into())), } - }, + } _ => Ok(EventOutcome::Ok(format!("Core action not handled: {}", action))), } } diff --git a/client/src/modes/common/commands.rs b/client/src/modes/common/commands.rs index 1b2a154..5f7845d 100644 --- a/client/src/modes/common/commands.rs +++ b/client/src/modes/common/commands.rs @@ -1,7 +1,7 @@ // src/modes/common/commands.rs use crate::tui::terminal::core::TerminalCore; use crate::state::app::state::AppState; -use crate::state::pages::{auth::LoginState, auth::RegisterState}; +use crate::pages::routing::{Router, Page}; use anyhow::Result; pub struct CommandHandler; @@ -16,11 +16,10 @@ impl CommandHandler { action: &str, terminal: &mut TerminalCore, app_state: &mut AppState, - login_state: &LoginState, - register_state: &RegisterState, + router: &Router, ) -> Result<(bool, String)> { match action { - "quit" => self.handle_quit(terminal, app_state, login_state, register_state).await, + "quit" => self.handle_quit(terminal, app_state, router).await, "force_quit" => self.handle_force_quit(terminal).await, "save_and_quit" => self.handle_save_quit(terminal).await, _ => Ok((false, format!("Unknown command: {}", action))), @@ -31,18 +30,14 @@ impl CommandHandler { &self, terminal: &mut TerminalCore, app_state: &mut AppState, - login_state: &LoginState, - register_state: &RegisterState, + router: &Router, ) -> Result<(bool, String)> { - // Use actual unsaved changes state instead of is_saved flag - let has_unsaved = if app_state.ui.show_login { - login_state.has_unsaved_changes() - } else if app_state.ui.show_register { - register_state.has_unsaved_changes() - } else if let Some(fs) = app_state.form_state_mut() { - fs.has_unsaved_changes - } else { - false + // Use router to check unsaved changes + let has_unsaved = match &router.current { + Page::Login(state) => state.has_unsaved_changes(), + Page::Register(state) => state.has_unsaved_changes(), + Page::Form(fs) => fs.has_unsaved_changes, + _ => false, }; if !has_unsaved { diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index e2ad54a..86e106d 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -30,6 +30,7 @@ use crate::state::{ intro::IntroState, }, }; +use crate::pages::routing::{Router, Page}; use crate::search::state::SearchState; use crate::tui::functions::common::login::LoginResult; use crate::tui::functions::common::register::RegisterResult; @@ -127,90 +128,69 @@ impl EventHandler { // Helper functions - replace the removed event_helper functions fn get_current_field_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, + router: &Router, ) -> usize { - if app_state.ui.show_login { - login_state.current_field() - } else if app_state.ui.show_register { - register_state.current_field() - } else { - form_state.current_field() + match &router.current { + Page::Login(state) => state.current_field(), + Page::Register(state) => state.current_field(), + Page::Form(state) => state.current_field(), + _ => 0, } } fn get_current_cursor_pos_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, + router: &Router, ) -> usize { - if app_state.ui.show_login { - login_state.current_cursor_pos() - } else if app_state.ui.show_register { - register_state.current_cursor_pos() - } else { - form_state.current_cursor_pos() + match &router.current { + Page::Login(state) => state.current_cursor_pos(), + Page::Register(state) => state.current_cursor_pos(), + Page::Form(state) => state.current_cursor_pos(), + _ => 0, } } fn get_has_unsaved_changes_for_state( - app_state: &AppState, - login_state: &LoginState, - register_state: &RegisterState, - form_state: &FormState, + router: &Router, ) -> bool { - if app_state.ui.show_login { - login_state.has_unsaved_changes() - } else if app_state.ui.show_register { - register_state.has_unsaved_changes() - } else { - form_state.has_unsaved_changes() + match &router.current { + Page::Login(state) => state.has_unsaved_changes(), + Page::Register(state) => state.has_unsaved_changes(), + Page::Form(state) => state.has_unsaved_changes(), + _ => false, } } fn get_current_input_for_state<'a>( - app_state: &AppState, - login_state: &'a LoginState, - register_state: &'a RegisterState, - form_state: &'a FormState, + router: &'a Router, ) -> &'a str { - if app_state.ui.show_login { - login_state.get_current_input() - } else if app_state.ui.show_register { - register_state.get_current_input() - } else { - form_state.get_current_input() + match &router.current { + Page::Login(state) => state.get_current_input(), + Page::Register(state) => state.get_current_input(), + Page::Form(state) => state.get_current_input(), + _ => "", } } fn set_current_cursor_pos_for_state( - app_state: &AppState, - login_state: &mut LoginState, - register_state: &mut RegisterState, - form_state: &mut FormState, + router: &mut Router, pos: usize, ) { - if app_state.ui.show_login { - login_state.set_current_cursor_pos(pos); - } else if app_state.ui.show_register { - register_state.set_current_cursor_pos(pos); - } else { - form_state.set_current_cursor_pos(pos); + match &mut router.current { + Page::Login(state) => state.set_current_cursor_pos(pos), + Page::Register(state) => state.set_current_cursor_pos(pos), + Page::Form(state) => state.set_current_cursor_pos(pos), + _ => {}, } } fn get_cursor_pos_for_mixed_state( - app_state: &AppState, - login_state: &LoginState, - form_state: &FormState, + router: &Router, ) -> usize { - if app_state.ui.show_login || app_state.ui.show_register { - login_state.current_cursor_pos() - } else { - form_state.current_cursor_pos() + match &router.current { + Page::Login(state) => state.current_cursor_pos(), + Page::Register(state) => state.current_cursor_pos(), + Page::Form(state) => state.current_cursor_pos(), + _ => 0, } } @@ -222,12 +202,9 @@ impl EventHandler { terminal: &mut TerminalCore, command_handler: &mut CommandHandler, auth_state: &mut AuthState, - login_state: &mut LoginState, - register_state: &mut RegisterState, - intro_state: &mut IntroState, - admin_state: &mut AdminState, buffer_state: &mut BufferState, app_state: &mut AppState, + router: &mut Router, ) -> Result { if app_state.ui.show_search_palette { if let Event::Key(key_event) = event { @@ -244,7 +221,7 @@ impl EventHandler { } let mut current_mode = - ModeManager::derive_mode(app_state, self, admin_state); + ModeManager::derive_mode(app_state, self, router); if current_mode == AppMode::General && self.navigation_state.active { if let Event::Key(key_event) = event { @@ -258,7 +235,7 @@ impl EventHandler { if !self.navigation_state.active { self.command_message = outcome.get_message_if_ok(); current_mode = - ModeManager::derive_mode(app_state, self, admin_state); + ModeManager::derive_mode(app_state, self, router); } app_state.update_mode(current_mode); return Ok(outcome); @@ -269,25 +246,14 @@ impl EventHandler { app_state.update_mode(current_mode); - let current_view = { - let ui = &app_state.ui; - if ui.show_intro { - AppView::Intro - } else if ui.show_login { - AppView::Login - } else if ui.show_register { - AppView::Register - } else if ui.show_admin { - AppView::Admin - } else if ui.show_add_logic { - AppView::AddLogic - } else if ui.show_add_table { - AppView::AddTable - } else if ui.show_form { - AppView::Form - } else { - AppView::Scratch - } + let current_view = match &router.current { + Page::Intro(_) => AppView::Intro, + Page::Login(_) => AppView::Login, + Page::Register(_) => AppView::Register, + Page::Admin(_) => AppView::Admin, + Page::AddLogic(_) => AppView::AddLogic, + Page::AddTable(_) => AppView::AddTable, + Page::Form(_) => AppView::Form, }; buffer_state.update_history(current_view); @@ -297,10 +263,8 @@ impl EventHandler { &Event::Key(key_event), config, app_state, - login_state, - register_state, buffer_state, - admin_state, + router, ) .await { @@ -386,7 +350,7 @@ impl EventHandler { config.get_general_action(key_code, modifiers) { if action == "open_search" { - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { if let Some(table_name) = app_state.current_view_table_name.clone() { @@ -405,31 +369,31 @@ impl EventHandler { match current_mode { AppMode::General => { - if app_state.ui.show_admin - && auth_state.role.as_deref() == Some("admin") - { - if admin_nav::handle_admin_navigation( - key_event, - config, - app_state, - admin_state, - buffer_state, - &mut self.command_message, - ) { - return Ok(EventOutcome::Ok( - self.command_message.clone(), - )); + if let Page::Admin(admin_state) = &router.current { + if auth_state.role.as_deref() == Some("admin") { + if admin_nav::handle_admin_navigation( + key_event, + config, + app_state, + admin_state, + buffer_state, + &mut self.command_message, + ) { + return Ok(EventOutcome::Ok( + self.command_message.clone(), + )); + } } } - if app_state.ui.show_add_logic { + if let Page::AddLogic(add_logic_state) = &mut router.current { let client_clone = self.grpc_client.clone(); let sender_clone = self.save_logic_result_sender.clone(); if add_logic_nav::handle_add_logic_navigation( key_event, config, app_state, - &mut admin_state.add_logic_state, + add_logic_state, &mut self.is_edit_mode, buffer_state, client_clone, @@ -442,14 +406,14 @@ impl EventHandler { } } - if app_state.ui.show_add_table { + 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( key_event, config, app_state, - &mut admin_state.add_table_state, + add_table_state, client_clone, sender_clone, &mut self.command_message, @@ -464,10 +428,7 @@ impl EventHandler { key_event, config, app_state, - login_state, - register_state, - intro_state, - admin_state, + router, &mut self.command_mode, &mut self.command_input, &mut self.command_message, @@ -482,53 +443,68 @@ impl EventHandler { buffer_state, index, ); - if app_state.ui.show_admin - && !app_state + if let Page::Admin(admin_state) = &router.current { + if !app_state .profile_tree .profiles .is_empty() - { - admin_state - .profile_list_state - .select(Some(0)); + { + admin_state + .profile_list_state + .select(Some(0)); + } } format!("Intro Option {} selected", index) } - UiContext::Login => match index { - 0 => login::initiate_login( - login_state, - app_state, - self.auth_client.clone(), - self.login_result_sender.clone(), - ), - 1 => login::back_to_main( - login_state, - app_state, - buffer_state, - ) - .await, - _ => "Invalid Login Option".to_string(), - }, - UiContext::Register => match index { - 0 => register::initiate_registration( - register_state, - app_state, - self.auth_client.clone(), - self.register_result_sender.clone(), - ), - 1 => register::back_to_login( - register_state, - app_state, - buffer_state, - ) - .await, - _ => "Invalid Login Option".to_string(), - }, + UiContext::Login => { + if let Page::Login(login_state) = &router.current { + match index { + 0 => login::initiate_login( + login_state, + app_state, + self.auth_client.clone(), + self.login_result_sender.clone(), + ), + 1 => login::back_to_main( + login_state, + app_state, + buffer_state, + ) + .await, + _ => "Invalid Login Option".to_string(), + } + } else { + "Invalid state".to_string() + } + } + UiContext::Register => { + if let Page::Register(register_state) = &router.current { + match index { + 0 => register::initiate_registration( + register_state, + app_state, + self.auth_client.clone(), + self.register_result_sender.clone(), + ), + 1 => register::back_to_login( + register_state, + app_state, + buffer_state, + ) + .await, + _ => "Invalid Login Option".to_string(), + } + } else { + "Invalid state".to_string() + } + } UiContext::Admin => { - admin::handle_admin_selection( - app_state, - admin_state, - ); + if let Page::Admin(admin_state) = &router.current { + admin::handle_admin_selection( + app_state, + admin_state, + ); + } format!("Admin Option {} selected", index) } UiContext::Dialog => "Internal error: Unexpected dialog state" @@ -542,7 +518,7 @@ impl EventHandler { AppMode::ReadOnly => { // First let the canvas editor try to handle the key - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { if let Some(editor) = &mut app_state.form_editor { let outcome = editor.handle_key_event(key_event); let new_mode = AppMode::from(editor.mode()); @@ -588,10 +564,9 @@ impl EventHandler { .handle_core_action( action, auth_state, - login_state, - register_state, terminal, app_state, + router, ) .await; } @@ -603,7 +578,7 @@ impl EventHandler { } AppMode::Highlight => { - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { if let Some(editor) = &mut app_state.form_editor { let outcome = editor.handle_key_event(key_event); let new_mode = AppMode::from(editor.mode()); @@ -640,10 +615,9 @@ impl EventHandler { .handle_core_action( action, auth_state, - login_state, - register_state, terminal, app_state, + router, ) .await; } @@ -652,7 +626,7 @@ impl EventHandler { } // Let the canvas editor handle edit-mode keys - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { if let Some(editor) = &mut app_state.form_editor { let outcome = editor.handle_key_event(key_event); let new_mode = AppMode::from(editor.mode()); @@ -696,7 +670,7 @@ impl EventHandler { } if config.is_command_execute(key_code, modifiers) { - let (mut current_position, total_count) = if let Some(fs) = app_state.form_state() { + let (mut current_position, total_count) = if let Page::Form(fs) = &router.current { (fs.current_position, fs.total_count) } else { (1, 0) @@ -706,8 +680,7 @@ impl EventHandler { key_event, config, app_state, - login_state, - register_state, + router, &mut self.command_input, &mut self.command_message, &mut self.grpc_client, @@ -716,7 +689,7 @@ impl EventHandler { &mut current_position, total_count, ).await?; - if let Some(fs) = app_state.form_state_mut() { + if let Page::Form(fs) = &mut router.current { fs.current_position = current_position; } self.command_mode = false; @@ -724,7 +697,7 @@ impl EventHandler { let new_mode = ModeManager::derive_mode( app_state, self, - admin_state, + router, ); app_state.update_mode(new_mode); return Ok(outcome); @@ -746,9 +719,7 @@ impl EventHandler { &sequence, ) == Some("find_file_palette_toggle") { - if app_state.ui.show_form - || app_state.ui.show_intro - { + if matches!(&router.current, Page::Form(_) | Page::Intro(_)) { let mut all_table_paths: Vec = app_state .profile_tree @@ -830,14 +801,13 @@ impl EventHandler { &mut self, action: &str, auth_state: &mut AuthState, - login_state: &mut LoginState, - register_state: &mut RegisterState, terminal: &mut TerminalCore, app_state: &mut AppState, + router: &mut Router, ) -> Result { match action { "save" => { - if app_state.ui.show_login { + if let Page::Login(login_state) = &router.current { let message = crate::tui::functions::common::login::save( auth_state, login_state, @@ -847,7 +817,7 @@ impl EventHandler { .await?; Ok(EventOutcome::Ok(message)) } else { - let save_outcome = if let Some(fs) = app_state.form_state_mut() { + let save_outcome = if let Page::Form(_) = &router.current { crate::tui::functions::common::form::save( app_state, &mut self.grpc_client, @@ -874,7 +844,7 @@ impl EventHandler { )) } "save_and_quit" => { - let message = if app_state.ui.show_login { + let message = if let Page::Login(login_state) = &router.current { crate::tui::functions::common::login::save( auth_state, login_state, @@ -903,17 +873,17 @@ impl EventHandler { ))) } "revert" => { - let message = if app_state.ui.show_login { + let message = if let Page::Login(login_state) = &router.current { crate::tui::functions::common::login::revert(login_state, app_state) .await - } else if app_state.ui.show_register { + } else if let Page::Register(register_state) = &router.current { crate::tui::functions::common::register::revert( register_state, app_state, ) .await } else { - if let Some(fs) = app_state.form_state_mut() { + if let Page::Form(_) = &router.current { crate::tui::functions::common::form::revert( app_state, &mut self.grpc_client, diff --git a/client/src/modes/handlers/mode_manager.rs b/client/src/modes/handlers/mode_manager.rs index 5b32721..fb9bf62 100644 --- a/client/src/modes/handlers/mode_manager.rs +++ b/client/src/modes/handlers/mode_manager.rs @@ -2,7 +2,7 @@ use crate::state::app::state::AppState; use crate::modes::handlers::event::EventHandler; use crate::state::pages::add_logic::AddLogicFocus; -use crate::state::pages::admin::AdminState; +use crate::pages::routing::{Router, Page}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AppMode { @@ -28,11 +28,11 @@ impl From for AppMode { pub struct ModeManager; impl ModeManager { - /// Determine current mode based on app state + /// Determine current mode based on app state + router pub fn derive_mode( app_state: &AppState, event_handler: &EventHandler, - admin_state: &AdminState, + router: &Router, ) -> AppMode { // Navigation palette always forces General if event_handler.navigation_state.active { @@ -44,16 +44,17 @@ impl ModeManager { return AppMode::Command; } - // Always trust the FormEditor when a form is active - if app_state.ui.show_form && !app_state.ui.focus_outside_canvas { - if let Some(editor) = &app_state.form_editor { - return AppMode::from(editor.mode()); + match &router.current { + // --- Form view --- + Page::Form(_) if !app_state.ui.focus_outside_canvas => { + if let Some(editor) = &app_state.form_editor { + return AppMode::from(editor.mode()); + } + AppMode::General } - } - // --- Non-form views (add_logic, add_table, etc.) --- - if app_state.ui.show_add_logic { - match admin_state.add_logic_state.current_focus { + // --- AddLogic view --- + Page::AddLogic(state) => match state.current_focus { AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription => { @@ -64,26 +65,30 @@ impl ModeManager { } } _ => AppMode::General, + }, + + // --- AddTable view --- + Page::AddTable(_) => { + if app_state.ui.focus_outside_canvas { + AppMode::General + } else if event_handler.is_edit_mode { + AppMode::Edit + } else { + AppMode::ReadOnly + } } - } else if app_state.ui.show_add_table { - if app_state.ui.focus_outside_canvas { - AppMode::General - } else if event_handler.is_edit_mode { - AppMode::Edit - } else { - AppMode::ReadOnly + + // --- Login/Register views --- + Page::Login(_) | Page::Register(_) => { + if event_handler.is_edit_mode { + AppMode::Edit + } else { + AppMode::ReadOnly + } } - } else if app_state.ui.show_login - || app_state.ui.show_register - { - // login/register still use the old flag - if event_handler.is_edit_mode { - AppMode::Edit - } else { - AppMode::ReadOnly - } - } else { - AppMode::General + + // --- Everything else (Intro, Admin, etc.) --- + _ => AppMode::General, } } diff --git a/client/src/ui/handlers/render.rs b/client/src/ui/handlers/render.rs index 675b96b..4cfdbc5 100644 --- a/client/src/ui/handlers/render.rs +++ b/client/src/ui/handlers/render.rs @@ -8,7 +8,11 @@ use crate::components::{ intro::intro::render_intro, render_background, }; -use crate::bottom_panel::{command_line::render_command_line, status_line::render_status_line, find_file_palette}; +use crate::bottom_panel::{ + command_line::render_command_line, + status_line::render_status_line, + find_file_palette, +}; use crate::sidebar::{calculate_sidebar_layout, render_sidebar}; use crate::buffer::render_buffer_list; use crate::search::render_search_palette; @@ -16,12 +20,7 @@ use crate::config::colors::themes::Theme; use crate::modes::general::command_navigation::NavigationState; use crate::buffer::state::BufferState; use crate::state::app::state::AppState; -use crate::state::pages::admin::AdminState; use crate::state::pages::auth::AuthState; -use crate::state::pages::auth::LoginState; -use crate::state::pages::auth::RegisterState; -use crate::state::pages::form::FormState; -use crate::state::pages::intro::IntroState; use crate::bottom_panel::layout::{bottom_panel_constraints, render_bottom_panel}; use crate::components::render_form; use ratatui::{ @@ -46,6 +45,7 @@ pub fn render_ui( ) { render_background(f, f.area(), theme); + // Layout: optional buffer list + main content + bottom panel let mut main_layout_constraints = vec![Constraint::Min(1)]; if app_state.ui.show_buffer_list { main_layout_constraints.insert(0, Constraint::Length(1)); @@ -73,7 +73,7 @@ pub fn render_ui( let main_content_area = root_chunks[chunk_idx]; chunk_idx += 1; - // ✅ Instead of checking app_state.ui.show_*, just match router.current + // Page rendering is now fully router-driven match &mut router.current { Page::Intro(state) => render_intro(f, state, main_content_area, theme), Page::Login(state) => render_login( @@ -160,6 +160,7 @@ pub fn render_ui( } } + // Global overlays (not tied to a page) if let Some(area) = buffer_list_area { render_buffer_list(f, area, theme, buffer_state, app_state); } diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 72c9b7e..8eb3ade 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -199,7 +199,7 @@ pub async fn run_ui() -> Result<()> { event_processed = true; if let crossterm_event::Event::Key(key_event) = &event { - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { if let Some(editor) = app_state.form_editor.as_mut() { match editor.handle_key_event(*key_event) { KeyEventOutcome::Consumed(Some(msg)) => { @@ -391,7 +391,7 @@ pub async fn run_ui() -> Result<()> { } } - if app_state.ui.show_form { + if let Page::Form(_) = &router.current { let current_view_profile = app_state.current_view_profile_name.clone(); let current_view_table = app_state.current_view_table_name.clone(); @@ -477,18 +477,21 @@ pub async fn run_ui() -> Result<()> { // Now we can use CanvasState methods like get_current_input(), current_field(), etc. if let Some((profile_name, table_name)) = app_state.pending_table_structure_fetch.take() { - if app_state.ui.show_add_logic { - if admin_state.add_logic_state.profile_name == profile_name && - admin_state.add_logic_state.selected_table_name.as_deref() == Some(table_name.as_str()) { + 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, - &mut admin_state.add_logic_state, + state, &app_state.profile_tree, - ).await.unwrap_or_else(|e| { - error!("Error initializing add_logic_table_data: {}", e); - format!("Error fetching table structure: {}", e) - }); + ) + .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); @@ -498,10 +501,11 @@ pub async fn run_ui() -> Result<()> { needs_redraw = true; } else { error!( - "Mismatch in pending_table_structure_fetch: app_state wants {}.{}, but add_logic_state is for {}.{:?}", - profile_name, table_name, - admin_state.add_logic_state.profile_name, - admin_state.add_logic_state.selected_table_name + "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 { @@ -512,21 +516,21 @@ pub async fn run_ui() -> Result<()> { } } - if let Some(table_name) = admin_state.add_logic_state.script_editor_awaiting_column_autocomplete.clone() { - if app_state.ui.show_add_logic { - let profile_name = admin_state.add_logic_state.profile_name.clone(); + 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(); 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) => { - admin_state.add_logic_state.set_columns_for_table_autocomplete(columns.clone()); + 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); - admin_state.add_logic_state.script_editor_awaiting_column_autocomplete = None; - admin_state.add_logic_state.deactivate_script_editor_autocomplete(); + state.script_editor_awaiting_column_autocomplete = None; + state.deactivate_script_editor_autocomplete(); event_handler.command_message = format!("Error loading columns for '{}': {}", table_name, e); } } @@ -540,59 +544,75 @@ pub async fn run_ui() -> Result<()> { let position_changed = current_position != position_before_event; let mut position_logic_needs_redraw = false; - if app_state.ui.show_form && !table_just_switched { - if position_changed && !event_handler.is_edit_mode { - position_logic_needs_redraw = true; + if let Page::Form(form_state) = &mut router.current { + if !table_just_switched { + if position_changed && !event_handler.is_edit_mode { + position_logic_needs_redraw = true; - if let Some(form_state) = app_state.form_state_mut() { - if form_state.current_position > form_state.total_count { - form_state.reset_to_empty(); - event_handler.command_message = format!("New entry for {}.{}", form_state.profile_name, form_state.table_name); - } else { - match UiService::load_table_data_by_position(&mut grpc_client, form_state).await { - Ok(load_message) => { - if event_handler.command_message.is_empty() || !load_message.starts_with("Error") { - event_handler.command_message = load_message; + if let Some(form_state) = app_state.form_state_mut() { + if form_state.current_position > form_state.total_count { + form_state.reset_to_empty(); + event_handler.command_message = format!( + "New entry for {}.{}", + form_state.profile_name, + form_state.table_name + ); + } else { + match UiService::load_table_data_by_position(&mut grpc_client, form_state).await { + Ok(load_message) => { + if event_handler.command_message.is_empty() + || !load_message.starts_with("Error") + { + event_handler.command_message = load_message; + } + } + Err(e) => { + event_handler.command_message = + format!("Error loading data: {}", e); } } - Err(e) => { - event_handler.command_message = format!("Error loading data: {}", e); - } } - } - let current_input_after_load_str = form_state.get_current_input(); - let current_input_len_after_load = current_input_after_load_str.chars().count(); - let max_cursor_pos = if current_input_len_after_load > 0 { - current_input_len_after_load.saturating_sub(1) - } else { - 0 - }; - form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos); - } - } else if !position_changed && !event_handler.is_edit_mode { - if let Some(form_state) = app_state.form_state_mut() { - let current_input_str = form_state.get_current_input(); - let current_input_len = current_input_str.chars().count(); - let max_cursor_pos = if current_input_len > 0 { - current_input_len.saturating_sub(1) - } else { - 0 - }; - form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos); + let current_input_after_load_str = form_state.get_current_input(); + let current_input_len_after_load = + current_input_after_load_str.chars().count(); + let max_cursor_pos = if current_input_len_after_load > 0 { + current_input_len_after_load.saturating_sub(1) + } else { + 0 + }; + form_state.current_cursor_pos = + event_handler.ideal_cursor_column.min(max_cursor_pos); + } + } else if !position_changed && !event_handler.is_edit_mode { + if let Some(form_state) = app_state.form_state_mut() { + let current_input_str = form_state.get_current_input(); + let current_input_len = current_input_str.chars().count(); + let max_cursor_pos = if current_input_len > 0 { + current_input_len.saturating_sub(1) + } else { + 0 + }; + form_state.current_cursor_pos = + event_handler.ideal_cursor_column.min(max_cursor_pos); + } } } - } else if app_state.ui.show_register { + } else if let Page::Register(state) = &mut router.current { if !event_handler.is_edit_mode { - let current_input = register_state.get_current_input(); - let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; - register_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos); + let current_input = state.get_current_input(); + let max_cursor_pos = + if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; + state.current_cursor_pos = + event_handler.ideal_cursor_column.min(max_cursor_pos); } - } else if app_state.ui.show_login { + } else if let Page::Login(state) = &mut router.current { if !event_handler.is_edit_mode { - let current_input = login_state.get_current_input(); - let max_cursor_pos = if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; - login_state.current_cursor_pos = event_handler.ideal_cursor_column.min(max_cursor_pos); + let current_input = state.get_current_input(); + let max_cursor_pos = + if !current_input.is_empty() { current_input.len() - 1 } else { 0 }; + state.current_cursor_pos = + event_handler.ideal_cursor_column.min(max_cursor_pos); } } @@ -623,7 +643,7 @@ pub async fn run_ui() -> Result<()> { } if event_processed || needs_redraw || position_changed { - let current_mode = ModeManager::derive_mode(&app_state, &event_handler, &admin_state); + let current_mode = ModeManager::derive_mode(&app_state, &event_handler, &router); match current_mode { AppMode::Edit => { terminal.show_cursor()?; } AppMode::Highlight => { terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?; terminal.show_cursor()?; }