diff --git a/client/src/pages/admin_panel/add_table/event.rs b/client/src/pages/admin_panel/add_table/event.rs index b5ba3d6..e09457c 100644 --- a/client/src/pages/admin_panel/add_table/event.rs +++ b/client/src/pages/admin_panel/add_table/event.rs @@ -4,7 +4,7 @@ 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::pages::admin_panel::add_table::state::{AddTableFocus, AddTableFormState}; use crate::services::GrpcClient; use crate::state::app::state::AppState; use crossterm::event::KeyEvent; @@ -19,31 +19,31 @@ pub fn handle_add_table_event( movement_action: Option, config: &Config, app_state: &mut AppState, - state: &mut AddTableState, + page: &mut AddTableFormState, grpc_client: GrpcClient, save_result_sender: SaveTableResultSender, command_message: &mut String, ) -> bool { - // 1) Try movement first (keeps focus cycling consistent) + // 1) Try movement first if let Some(ma) = movement_action { - if state.handle_movement(ma) { + if page.state.handle_movement(ma) { let is_canvas_input = matches!( - state.current_focus, + page.current_focus(), AddTableFocus::InputTableName - | AddTableFocus::InputColumnName - | AddTableFocus::InputColumnType + | AddTableFocus::InputColumnName + | AddTableFocus::InputColumnType ); app_state.ui.focus_outside_canvas = !is_canvas_input; return true; } } - // 2) Rich actions/navigation for AddTable + // 2) Rich actions/navigation nav::handle_add_table_navigation( key_event, config, app_state, - state, + &mut page.state, grpc_client, save_result_sender, command_message, diff --git a/client/src/pages/admin_panel/add_table/state.rs b/client/src/pages/admin_panel/add_table/state.rs index 2887053..df076c6 100644 --- a/client/src/pages/admin_panel/add_table/state.rs +++ b/client/src/pages/admin_panel/add_table/state.rs @@ -1,6 +1,7 @@ // src/pages/admin_panel/add_table/state.rs use canvas::{DataProvider, AppMode}; +use canvas::FormEditor; use ratatui::widgets::TableState; use serde::{Deserialize, Serialize}; use crate::movement::{move_focus, MovementAction}; @@ -381,3 +382,77 @@ impl AddTableState { move_focus(&ORDER, &mut self.current_focus, action) } } + +pub struct AddTableFormState { + pub state: AddTableState, + pub editor: FormEditor, + pub focus_outside_canvas: bool, +} + +impl std::fmt::Debug for AddTableFormState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AddTableFormState") + .field("state", &self.state) + .field("focus_outside_canvas", &self.focus_outside_canvas) + .finish() + } +} + +impl AddTableFormState { + pub fn new(profile_name: String) -> Self { + let mut state = AddTableState::default(); + state.profile_name = profile_name; + let editor = FormEditor::new(state.clone()); + Self { + state, + editor, + focus_outside_canvas: false, + } + } + + pub fn from_state(state: AddTableState) -> Self { + let editor = FormEditor::new(state.clone()); + Self { + state, + editor, + focus_outside_canvas: false, + } + } + + /// Sync state from editor’s snapshot + pub fn sync_from_editor(&mut self) { + self.state = self.editor.data_provider().clone(); + } + + // === Delegates to AddTableState fields === + pub fn current_focus(&self) -> AddTableFocus { + self.state.current_focus + } + pub fn set_current_focus(&mut self, focus: AddTableFocus) { + self.state.current_focus = focus; + } + pub fn profile_name(&self) -> &str { + &self.state.profile_name + } + pub fn table_name(&self) -> &str { + &self.state.table_name + } + pub fn columns(&self) -> &Vec { + &self.state.columns + } + pub fn indexes(&self) -> &Vec { + &self.state.indexes + } + pub fn links(&self) -> &Vec { + &self.state.links + } + pub fn column_table_state(&mut self) -> &mut TableState { + &mut self.state.column_table_state + } + pub fn index_table_state(&mut self) -> &mut TableState { + &mut self.state.index_table_state + } + pub fn link_table_state(&mut self) -> &mut TableState { + &mut self.state.link_table_state + } +} diff --git a/client/src/pages/admin_panel/add_table/ui.rs b/client/src/pages/admin_panel/add_table/ui.rs index 5293da4..d944f5f 100644 --- a/client/src/pages/admin_panel/add_table/ui.rs +++ b/client/src/pages/admin_panel/add_table/ui.rs @@ -1,8 +1,8 @@ // src/pages/admin_panel/add_table/ui.rs use crate::config::colors::themes::Theme; use crate::state::app::state::AppState; -use crate::pages::admin_panel::add_table::state::{AddTableFocus, AddTableState}; -use canvas::{render_canvas, FormEditor}; +use crate::pages::admin_panel::add_table::state::{AddTableFocus, AddTableFormState}; +use canvas::render_canvas; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Modifier, Style}, @@ -19,7 +19,7 @@ pub fn render_add_table( area: Rect, theme: &Theme, app_state: &AppState, - add_table_state: &mut AddTableState, + add_table_state: &mut AddTableFormState, ) { // --- Configuration --- // Threshold width to switch between wide and narrow layouts @@ -27,7 +27,7 @@ pub fn render_add_table( // --- State Checks --- let focus_on_canvas_inputs = matches!( - add_table_state.current_focus, + add_table_state.current_focus(), AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType @@ -45,11 +45,11 @@ pub fn render_add_table( f.render_widget(main_block, area); // --- Fullscreen Columns Table Check (Narrow Screens Only) --- - if area.width < NARROW_LAYOUT_THRESHOLD && add_table_state.current_focus == AddTableFocus::InsideColumnsTable { + if area.width < NARROW_LAYOUT_THRESHOLD && add_table_state.current_focus() == AddTableFocus::InsideColumnsTable { // Render ONLY the columns table taking the full inner area let columns_border_style = Style::default().fg(theme.highlight); // Always highlighted when fullscreen let column_rows: Vec> = add_table_state - .columns + .columns() .iter() .map(|col_def| { Row::new(vec![ @@ -80,16 +80,16 @@ pub fn render_add_table( .fg(theme.highlight), ) .highlight_symbol(" > "); // Use the inside symbol - f.render_stateful_widget(columns_table, inner_area, &mut add_table_state.column_table_state); + f.render_stateful_widget(columns_table, inner_area, add_table_state.column_table_state()); return; // IMPORTANT: Stop rendering here for fullscreen mode } // --- Fullscreen Indexes Table Check --- - if add_table_state.current_focus == AddTableFocus::InsideIndexesTable { // Remove width check + if add_table_state.current_focus() == AddTableFocus::InsideIndexesTable { // Remove width check // Render ONLY the indexes table taking the full inner area let indexes_border_style = Style::default().fg(theme.highlight); // Always highlighted when fullscreen let index_rows: Vec> = add_table_state - .indexes + .indexes() .iter() .map(|index_def| { Row::new(vec![ @@ -115,16 +115,16 @@ pub fn render_add_table( ) .row_highlight_style(Style::default().add_modifier(Modifier::REVERSED).fg(theme.highlight)) .highlight_symbol(" > "); // Use the inside symbol - f.render_stateful_widget(indexes_table, inner_area, &mut add_table_state.index_table_state); + f.render_stateful_widget(indexes_table, inner_area, &mut add_table_state.index_table_state()); return; // IMPORTANT: Stop rendering here for fullscreen mode } // --- Fullscreen Links Table Check --- - if add_table_state.current_focus == AddTableFocus::InsideLinksTable { + if add_table_state.current_focus() == AddTableFocus::InsideLinksTable { // Render ONLY the links table taking the full inner area let links_border_style = Style::default().fg(theme.highlight); // Always highlighted when fullscreen let link_rows: Vec> = add_table_state - .links + .links() .iter() .map(|link_def| { Row::new(vec![ @@ -151,7 +151,7 @@ pub fn render_add_table( ) .row_highlight_style(Style::default().add_modifier(Modifier::REVERSED).fg(theme.highlight)) .highlight_symbol(" > "); // Use the inside symbol - f.render_stateful_widget(links_table, inner_area, &mut add_table_state.link_table_state); + f.render_stateful_widget(links_table, inner_area, &mut add_table_state.link_table_state()); return; // IMPORTANT: Stop rendering here for fullscreen mode } @@ -220,11 +220,11 @@ pub fn render_add_table( // --- Top Info Rendering (Wide - 2 lines) --- let profile_text = Paragraph::new(vec![ Line::from(Span::styled( - format!("Profile: {}", add_table_state.profile_name), + format!("Profile: {}", add_table_state.profile_name()), theme.fg, )), Line::from(Span::styled( - format!("Table name: {}", add_table_state.table_name), + format!("Table name: {}", add_table_state.table_name()), theme.fg, )), ]) @@ -276,14 +276,14 @@ pub fn render_add_table( .split(top_info_area); let profile_text = Paragraph::new(Span::styled( - format!("Profile: {}", add_table_state.profile_name), + format!("Profile: {}", add_table_state.profile_name()), theme.fg, )) .alignment(Alignment::Left); f.render_widget(profile_text, top_info_chunks[0]); let table_name_text = Paragraph::new(Span::styled( - format!("Table: {}", add_table_state.table_name), + format!("Table: {}", add_table_state.table_name()), theme.fg, )) .alignment(Alignment::Left); @@ -293,14 +293,14 @@ pub fn render_add_table( // --- Common Widget Rendering (Uses calculated areas) --- // --- Columns Table Rendering --- - let columns_focused = matches!(add_table_state.current_focus, AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable); + let columns_focused = matches!(add_table_state.current_focus(), AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable); let columns_border_style = if columns_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let column_rows: Vec> = add_table_state - .columns + .columns() .iter() .map(|col_def| { Row::new(vec![ @@ -341,12 +341,11 @@ pub fn render_add_table( f.render_stateful_widget( columns_table, columns_area, - &mut add_table_state.column_table_state, + &mut add_table_state.column_table_state(), ); // --- Canvas Rendering (Column Definition Input) - USING CANVAS LIBRARY --- - let editor = FormEditor::new(add_table_state.clone()); - let _active_field_rect = render_canvas(f, canvas_area, &editor, theme); + let _active_field_rect = render_canvas(f, canvas_area, &add_table_state.editor, theme); // --- Button Style Helpers --- let get_button_style = |button_focus: AddTableFocus, current_focus| { @@ -374,11 +373,11 @@ pub fn render_add_table( // --- Add Button Rendering --- // Determine if the add button is focused - let is_add_button_focused = add_table_state.current_focus == AddTableFocus::AddColumnButton; + let is_add_button_focused = add_table_state.current_focus() == AddTableFocus::AddColumnButton; // Create the Add button Paragraph widget let add_button = Paragraph::new(" Add ") - .style(get_button_style(AddTableFocus::AddColumnButton, add_table_state.current_focus)) // Use existing closure + .style(get_button_style(AddTableFocus::AddColumnButton, add_table_state.current_focus())) // Use existing closure .alignment(Alignment::Center) .block( Block::default() @@ -391,14 +390,14 @@ pub fn render_add_table( f.render_widget(add_button, add_button_area); // --- Indexes Table Rendering --- - let indexes_focused = matches!(add_table_state.current_focus, AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable); + let indexes_focused = matches!(add_table_state.current_focus(), AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable); let indexes_border_style = if indexes_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let index_rows: Vec> = add_table_state - .indexes + .indexes() .iter() .map(|index_def| { // Use index_def now Row::new(vec![ @@ -432,18 +431,18 @@ pub fn render_add_table( f.render_stateful_widget( indexes_table, indexes_area, - &mut add_table_state.index_table_state, + &mut add_table_state.index_table_state(), ); // --- Links Table Rendering --- - let links_focused = matches!(add_table_state.current_focus, AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable); + let links_focused = matches!(add_table_state.current_focus(), AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable); let links_border_style = if links_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let link_rows: Vec> = add_table_state - .links + .links() .iter() .map(|link_def| { Row::new(vec![ @@ -477,7 +476,7 @@ pub fn render_add_table( f.render_stateful_widget( links_table, links_area, - &mut add_table_state.link_table_state, + &mut add_table_state.link_table_state(), ); // --- Save/Cancel Buttons Rendering --- @@ -493,7 +492,7 @@ pub fn render_add_table( let save_button = Paragraph::new(" Save table ") .style(get_button_style( AddTableFocus::SaveButton, - add_table_state.current_focus, + add_table_state.current_focus(), )) .alignment(Alignment::Center) .block( @@ -501,7 +500,7 @@ pub fn render_add_table( .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( - add_table_state.current_focus == AddTableFocus::SaveButton, // Pass bool + add_table_state.current_focus() == AddTableFocus::SaveButton, // Pass bool theme, )), ); @@ -510,7 +509,7 @@ pub fn render_add_table( let delete_button = Paragraph::new(" Delete Selected ") .style(get_button_style( AddTableFocus::DeleteSelectedButton, - add_table_state.current_focus, + add_table_state.current_focus(), )) .alignment(Alignment::Center) .block( @@ -518,7 +517,7 @@ pub fn render_add_table( .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( - add_table_state.current_focus == AddTableFocus::DeleteSelectedButton, // Pass bool + add_table_state.current_focus() == AddTableFocus::DeleteSelectedButton, // Pass bool theme, )), ); @@ -527,7 +526,7 @@ pub fn render_add_table( let cancel_button = Paragraph::new(" Cancel ") .style(get_button_style( AddTableFocus::CancelButton, - add_table_state.current_focus, + add_table_state.current_focus(), )) .alignment(Alignment::Center) .block( @@ -535,7 +534,7 @@ pub fn render_add_table( .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( - add_table_state.current_focus == AddTableFocus::CancelButton, // Pass bool + add_table_state.current_focus() == AddTableFocus::CancelButton, // Pass bool theme, )), ); diff --git a/client/src/pages/routing/router.rs b/client/src/pages/routing/router.rs index 3099554..8542a4b 100644 --- a/client/src/pages/routing/router.rs +++ b/client/src/pages/routing/router.rs @@ -1,7 +1,7 @@ // src/pages/routing/router.rs use crate::state::pages::auth::AuthState; use crate::pages::admin_panel::add_logic::state::AddLogicFormState; -use crate::pages::admin_panel::add_table::state::AddTableState; +use crate::pages::admin_panel::add_table::state::AddTableFormState; use crate::pages::admin::AdminState; use crate::pages::forms::FormState; use crate::pages::login::LoginFormState; @@ -15,7 +15,7 @@ pub enum Page { Register(RegisterFormState), Admin(AdminState), AddLogic(AddLogicFormState), - AddTable(AddTableState), + AddTable(AddTableFormState), Form(String), } diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 78dac5a..9ff930b 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_table; use crate::pages::admin_panel::add_logic; use crate::pages::admin::AdminState; use crate::pages::admin::AdminFocus; @@ -424,7 +425,17 @@ pub async fn run_ui() -> Result<()> { router.navigate(Page::Admin(admin_state.clone())); } - AppView::AddTable => router.navigate(Page::AddTable(admin_state.add_table_state.clone())), + AppView::AddTable => { + router.navigate(Page::AddTable( + add_table::state::AddTableFormState::from_state( + admin_state.add_table_state.clone(), + ), + )); + if let Page::AddTable(page) = &mut router.current { + // Ensure keymap is set once + page.editor.set_keymap(config.build_canvas_keymap()); + } + } AppView::AddLogic => { if let Page::AddLogic(page) = &mut router.current { // Ensure keymap is set once @@ -640,6 +651,9 @@ pub async fn run_ui() -> Result<()> { if let Page::Register(page) = &router.current { let _ = CursorManager::update_for_mode(page.editor.mode()); } + if let Page::AddTable(page) = &router.current { + let _ = CursorManager::update_for_mode(page.editor.mode()); + } if let Page::AddLogic(page) = &router.current { let _ = CursorManager::update_for_mode(page.editor.mode()); }