diff --git a/client/src/components/admin/add_table.rs b/client/src/components/admin/add_table.rs index 0e4487b..2002aff 100644 --- a/client/src/components/admin/add_table.rs +++ b/client/src/components/admin/add_table.rs @@ -3,8 +3,7 @@ use crate::config::colors::themes::Theme; use crate::state::app::highlight::HighlightState; use crate::state::app::state::AppState; use crate::state::pages::add_table::{AddTableFocus, AddTableState}; -use crate::state::pages::canvas_state::CanvasState; -// use crate::state::pages::add_table::{ColumnDefinition, LinkDefinition}; // Not directly used here +use canvas::canvas::{render_canvas, CanvasState, HighlightState as CanvasHighlightState}; // Use canvas library use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Modifier, Style}, @@ -12,16 +11,24 @@ use ratatui::{ widgets::{Block, BorderType, Borders, Cell, Paragraph, Row, Table}, Frame, }; -use crate::components::handlers::canvas::render_canvas; use crate::components::common::dialog; +// Helper function to convert between HighlightState types +fn convert_highlight_state(local: &HighlightState) -> CanvasHighlightState { + match local { + HighlightState::Off => CanvasHighlightState::Off, + HighlightState::Characterwise { anchor } => CanvasHighlightState::Characterwise { anchor: *anchor }, + HighlightState::Linewise { anchor_line } => CanvasHighlightState::Linewise { anchor_line: *anchor_line }, + } +} + /// Renders the Add New Table page layout, structuring the display of table information, /// input fields, and action buttons. Adapts layout based on terminal width. pub fn render_add_table( f: &mut Frame, area: Rect, theme: &Theme, - app_state: &AppState, // Currently unused, might be needed later + app_state: &AppState, add_table_state: &mut AddTableState, is_edit_mode: bool, // Determines if canvas inputs are in edit mode highlight_state: &HighlightState, // For text highlighting in canvas @@ -349,17 +356,15 @@ pub fn render_add_table( &mut add_table_state.column_table_state, ); - // --- Canvas Rendering (Column Definition Input) --- + // --- Canvas Rendering (Column Definition Input) - USING CANVAS LIBRARY --- + let canvas_highlight_state = convert_highlight_state(highlight_state); let _active_field_rect = render_canvas( f, canvas_area, - add_table_state, - &add_table_state.fields(), - &add_table_state.current_field(), - &add_table_state.inputs(), - theme, + add_table_state, // AddTableState implements CanvasState + theme, // Theme implements CanvasTheme is_edit_mode && focus_on_canvas_inputs, - highlight_state, + &canvas_highlight_state, ); // --- Button Style Helpers --- @@ -557,7 +562,7 @@ pub fn render_add_table( // --- DIALOG --- // Render the dialog overlay if it's active - if app_state.ui.dialog.dialog_show { // Use the passed-in app_state + if app_state.ui.dialog.dialog_show { dialog::render_dialog( f, f.area(), // Render over the whole frame area diff --git a/client/src/functions/modes/edit/add_table_e.rs b/client/src/functions/modes/edit/add_table_e.rs index 17aa3dc..7da0c70 100644 --- a/client/src/functions/modes/edit/add_table_e.rs +++ b/client/src/functions/modes/edit/add_table_e.rs @@ -1,7 +1,7 @@ // src/functions/modes/edit/add_table_e.rs use crate::state::pages::add_table::AddTableState; -use crate::state::pages::canvas_state::CanvasState; // Use trait use crossterm::event::{KeyCode, KeyEvent}; +use canvas::canvas::CanvasState; use anyhow::Result; #[derive(PartialEq)] diff --git a/client/src/functions/modes/read_only/add_table_ro.rs b/client/src/functions/modes/read_only/add_table_ro.rs index cb4f621..3318cdb 100644 --- a/client/src/functions/modes/read_only/add_table_ro.rs +++ b/client/src/functions/modes/read_only/add_table_ro.rs @@ -1,8 +1,8 @@ // src/functions/modes/read_only/add_table_ro.rs use crate::config::binds::key_sequences::KeySequenceTracker; use crate::state::pages::add_table::AddTableState; -use crate::state::pages::canvas_state::CanvasState; use crate::state::app::state::AppState; +use canvas::canvas::CanvasState; use anyhow::Result; // Re-use word navigation helpers if they are public or move them to a common module diff --git a/client/src/modes/canvas/edit.rs b/client/src/modes/canvas/edit.rs index 2a01283..4358e03 100644 --- a/client/src/modes/canvas/edit.rs +++ b/client/src/modes/canvas/edit.rs @@ -348,10 +348,10 @@ pub async fn handle_edit_event( ) .await? } else if app_state.ui.show_add_table { - // FIX: Pass &mut event_handler.ideal_cursor_column - add_table_e::execute_edit_action( - action_str, + // NEW: Use unified canvas handler instead of add_table_e::execute_edit_action + handle_canvas_state_edit( key, + config, &mut admin_state.add_table_state, &mut event_handler.ideal_cursor_column, ) @@ -399,10 +399,10 @@ pub async fn handle_edit_event( ) .await? } else if app_state.ui.show_add_table { - // FIX: Pass &mut event_handler.ideal_cursor_column - add_table_e::execute_edit_action( - "insert_char", + // NEW: Use unified canvas handler instead of add_table_e::execute_edit_action + handle_canvas_state_edit( key, + config, &mut admin_state.add_table_state, &mut event_handler.ideal_cursor_column, ) diff --git a/client/src/state/pages/add_table.rs b/client/src/state/pages/add_table.rs index 9a7ebdc..94c325e 100644 --- a/client/src/state/pages/add_table.rs +++ b/client/src/state/pages/add_table.rs @@ -1,5 +1,5 @@ // src/state/pages/add_table.rs -use crate::state::pages::canvas_state::CanvasState; +use canvas::canvas::{CanvasState, ActionContext, CanvasAction}; // External library use ratatui::widgets::TableState; use serde::{Deserialize, Serialize}; @@ -67,7 +67,6 @@ pub struct AddTableState { impl Default for AddTableState { fn default() -> Self { - // Initialize with some dummy data for demonstration AddTableState { profile_name: "default".to_string(), table_name: String::new(), @@ -92,16 +91,91 @@ impl Default for AddTableState { impl AddTableState { pub const INPUT_FIELD_COUNT: usize = 3; + + /// Helper method to add a column from current inputs + pub fn add_column_from_inputs(&mut self) -> Option { + if self.column_name_input.trim().is_empty() || self.column_type_input.trim().is_empty() { + return Some("Both column name and type are required".to_string()); + } + + // Check for duplicate column names + if self.columns.iter().any(|col| col.name == self.column_name_input.trim()) { + return Some("Column name already exists".to_string()); + } + + // Add the column + self.columns.push(ColumnDefinition { + name: self.column_name_input.trim().to_string(), + data_type: self.column_type_input.trim().to_string(), + selected: false, + }); + + // Clear inputs and reset focus to column name for next entry + self.column_name_input.clear(); + self.column_type_input.clear(); + self.column_name_cursor_pos = 0; + self.column_type_cursor_pos = 0; + self.current_focus = AddTableFocus::InputColumnName; + self.last_canvas_field = 1; + self.has_unsaved_changes = true; + + Some(format!("Column '{}' added successfully", self.columns.last().unwrap().name)) + } + + /// Helper method to delete selected items + pub fn delete_selected_items(&mut self) -> Option { + let mut deleted_items = Vec::new(); + + // Remove selected columns + let initial_column_count = self.columns.len(); + self.columns.retain(|col| { + if col.selected { + deleted_items.push(format!("column '{}'", col.name)); + false + } else { + true + } + }); + + // Remove selected indexes + let initial_index_count = self.indexes.len(); + self.indexes.retain(|idx| { + if idx.selected { + deleted_items.push(format!("index '{}'", idx.name)); + false + } else { + true + } + }); + + // Remove selected links + let initial_link_count = self.links.len(); + self.links.retain(|link| { + if link.selected { + deleted_items.push(format!("link to '{}'", link.linked_table_name)); + false + } else { + true + } + }); + + if deleted_items.is_empty() { + Some("No items selected for deletion".to_string()) + } else { + self.has_unsaved_changes = true; + Some(format!("Deleted: {}", deleted_items.join(", "))) + } + } } -// Implement CanvasState for the input fields +// Implement external library's CanvasState for AddTableState impl CanvasState for AddTableState { fn current_field(&self) -> usize { match self.current_focus { AddTableFocus::InputTableName => 0, AddTableFocus::InputColumnName => 1, AddTableFocus::InputColumnType => 2, - // If focus is elsewhere, default to the first field for canvas rendering logic + // If focus is elsewhere, return the last canvas field used _ => self.last_canvas_field, } } @@ -115,37 +189,6 @@ impl CanvasState for AddTableState { } } - fn has_unsaved_changes(&self) -> bool { - self.has_unsaved_changes - } - - fn inputs(&self) -> Vec<&String> { - vec![&self.table_name_input, &self.column_name_input, &self.column_type_input] - } - - fn get_current_input(&self) -> &str { - match self.current_focus { - AddTableFocus::InputTableName => &self.table_name_input, - AddTableFocus::InputColumnName => &self.column_name_input, - AddTableFocus::InputColumnType => &self.column_type_input, - _ => "", // Should not happen if called correctly - } - } - - fn get_current_input_mut(&mut self) -> &mut String { - match self.current_focus { - AddTableFocus::InputTableName => &mut self.table_name_input, - AddTableFocus::InputColumnName => &mut self.column_name_input, - AddTableFocus::InputColumnType => &mut self.column_type_input, - _ => &mut self.table_name_input, - } - } - - fn fields(&self) -> Vec<&str> { - // These must match the order used in render_add_table - vec!["Table name", "Name", "Type"] - } - fn set_current_field(&mut self, index: usize) { // Update both current focus and last canvas field self.current_focus = match index { @@ -174,17 +217,84 @@ impl CanvasState for AddTableState { } } + fn get_current_input(&self) -> &str { + match self.current_focus { + AddTableFocus::InputTableName => &self.table_name_input, + AddTableFocus::InputColumnName => &self.column_name_input, + AddTableFocus::InputColumnType => &self.column_type_input, + _ => "", // Should not happen if called correctly + } + } + + fn get_current_input_mut(&mut self) -> &mut String { + match self.current_focus { + AddTableFocus::InputTableName => &mut self.table_name_input, + AddTableFocus::InputColumnName => &mut self.column_name_input, + AddTableFocus::InputColumnType => &mut self.column_type_input, + _ => &mut self.table_name_input, // Fallback + } + } + + fn inputs(&self) -> Vec<&String> { + vec![&self.table_name_input, &self.column_name_input, &self.column_type_input] + } + + fn fields(&self) -> Vec<&str> { + // These must match the order used in render_add_table + vec!["Table name", "Name", "Type"] + } + + fn has_unsaved_changes(&self) -> bool { + self.has_unsaved_changes + } + fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_unsaved_changes = changed; } - // --- Autocomplete Support (Not needed for this form yet) --- - fn get_suggestions(&self) -> Option<&[String]> { - None - } + fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option { + match action { + // Handle adding column when user presses Enter on the Add button or uses specific action + CanvasAction::Custom(action_str) if action_str == "add_column" => { + self.add_column_from_inputs() + } - fn get_selected_suggestion_index(&self) -> Option { - None + // Handle table saving + CanvasAction::Custom(action_str) if action_str == "save_table" => { + if self.table_name_input.trim().is_empty() { + Some("Table name is required".to_string()) + } else if self.columns.is_empty() { + Some("At least one column is required".to_string()) + } else { + Some(format!("Saving table: {}", self.table_name_input)) + } + } + + // Handle deleting selected items + CanvasAction::Custom(action_str) if action_str == "delete_selected" => { + self.delete_selected_items() + } + + // Handle canceling (clear form) + CanvasAction::Custom(action_str) if action_str == "cancel" => { + // Reset to defaults but keep profile_name + let profile = self.profile_name.clone(); + *self = Self::default(); + self.profile_name = profile; + Some("Form cleared".to_string()) + } + + // Custom validation when moving between fields + CanvasAction::NextField => { + // When leaving table name field, update the table_name for display + if self.current_field() == 0 && !self.table_name_input.trim().is_empty() { + self.table_name = self.table_name_input.trim().to_string(); + } + None // Let canvas library handle the normal field movement + } + + // Let canvas library handle everything else + _ => None, + } } } -