From b49dce3334e743fa74e3a7bbcfb4e678370faebb Mon Sep 17 00:00:00 2001 From: filipriec Date: Sun, 15 Jun 2025 12:15:25 +0200 Subject: [PATCH] dropdown is being triggered --- client/config.toml | 3 +- client/src/components/form/form.rs | 57 ++++++++++----- client/src/modes/canvas/edit.rs | 97 +++++++++++++------------ client/src/state/pages/form.rs | 88 +++++++++++++++++----- client/src/tui/functions/common/form.rs | 2 +- client/src/ui/handlers/ui.rs | 63 +++++++++++----- 6 files changed, 202 insertions(+), 108 deletions(-) diff --git a/client/config.toml b/client/config.toml index a02804d..cf791d2 100644 --- a/client/config.toml +++ b/client/config.toml @@ -70,10 +70,11 @@ prev_field = ["shift+enter"] exit = ["esc", "ctrl+e"] delete_char_forward = ["delete"] delete_char_backward = ["backspace"] -move_left = ["left"] +move_left = [""] move_right = ["right"] suggestion_down = ["ctrl+n", "tab"] suggestion_up = ["ctrl+p", "shift+tab"] +trigger_autocomplete = ["left"] [keybindings.command] exit_command_mode = ["ctrl+g", "esc"] diff --git a/client/src/components/form/form.rs b/client/src/components/form/form.rs index 12a3ec7..bafa5d2 100644 --- a/client/src/components/form/form.rs +++ b/client/src/components/form/form.rs @@ -1,36 +1,37 @@ // src/components/form/form.rs +use crate::components::common::autocomplete; // <--- ADD THIS IMPORT +use crate::components::handlers::canvas::render_canvas; +use crate::config::colors::themes::Theme; +use crate::state::app::highlight::HighlightState; +use crate::state::pages::canvas_state::CanvasState; +use crate::state::pages::form::FormState; // <--- CHANGE THIS IMPORT use ratatui::{ - widgets::{Paragraph, Block, Borders}, - layout::{Layout, Constraint, Direction, Rect, Margin, Alignment}, + layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, style::Style, + widgets::{Block, Borders, Paragraph}, Frame, }; -use crate::config::colors::themes::Theme; -use crate::state::pages::canvas_state::CanvasState; -use crate::state::app::highlight::HighlightState; -use crate::components::handlers::canvas::render_canvas; pub fn render_form( f: &mut Frame, area: Rect, - form_state_param: &impl CanvasState, + form_state: &FormState, // <--- CHANGE THIS to the concrete type fields: &[&str], current_field_idx: &usize, inputs: &[&String], - table_name: &str, // This parameter receives the correct table name + table_name: &str, theme: &Theme, is_edit_mode: bool, highlight_state: &HighlightState, total_count: u64, current_position: u64, ) { - // Use the dynamic `table_name` parameter for the title instead of a hardcoded string. let card_title = format!(" {} ", table_name); let adresar_card = Block::default() .borders(Borders::ALL) .border_style(Style::default().fg(theme.border)) - .title(card_title) // Use the dynamic title + .title(card_title) .style(Style::default().bg(theme.bg).fg(theme.fg)); f.render_widget(adresar_card, area); @@ -42,10 +43,7 @@ pub fn render_form( let main_layout = Layout::default() .direction(Direction::Vertical) - .constraints([ - Constraint::Length(1), - Constraint::Min(1), - ]) + .constraints([Constraint::Length(1), Constraint::Min(1)]) .split(inner_area); let count_position_text = if total_count == 0 && current_position == 1 { @@ -54,19 +52,22 @@ pub fn render_form( format!("Total: {} | New Entry ({})", total_count, current_position) } else if total_count == 0 && current_position > 1 { format!("Total: 0 | New Entry ({})", current_position) - } - else { - format!("Total: {} | Position: {}/{}", total_count, current_position, total_count) + } else { + format!( + "Total: {} | Position: {}/{}", + total_count, current_position, total_count + ) }; let count_para = Paragraph::new(count_position_text) .style(Style::default().fg(theme.fg)) .alignment(Alignment::Left); f.render_widget(count_para, main_layout[0]); - render_canvas( + // Get the active field's rect from render_canvas + let active_field_rect = render_canvas( f, main_layout[1], - form_state_param, + form_state, fields, current_field_idx, inputs, @@ -74,4 +75,22 @@ pub fn render_form( is_edit_mode, highlight_state, ); + + // --- NEW: RENDER AUTOCOMPLETE --- + if form_state.autocomplete_active { + if let Some(suggestions) = form_state.get_suggestions() { + if let Some(active_rect) = active_field_rect { + if !suggestions.is_empty() { + autocomplete::render_autocomplete_dropdown( + f, + active_rect, + f.area(), + theme, + suggestions, + form_state.get_selected_suggestion_index(), + ); + } + } + } + } } diff --git a/client/src/modes/canvas/edit.rs b/client/src/modes/canvas/edit.rs index 3aa8978..96afbd8 100644 --- a/client/src/modes/canvas/edit.rs +++ b/client/src/modes/canvas/edit.rs @@ -5,15 +5,13 @@ use crate::state::pages::{ auth::{LoginState, RegisterState}, canvas_state::CanvasState, }; -use crate::state::pages::form::FormState; // <<< ADD THIS LINE -// AddLogicState is already imported -// AddTableState is already imported +use crate::state::pages::form::FormState; use crate::state::pages::admin::AdminState; use crate::modes::handlers::event::EventOutcome; use crate::functions::modes::edit::{add_logic_e, auth_e, form_e, add_table_e}; use crate::state::app::state::AppState; use anyhow::Result; -use crossterm::event::KeyEvent; // Removed KeyCode, KeyModifiers as they were unused +use crossterm::event::KeyEvent; use tracing::debug; #[derive(Debug, Clone, PartialEq, Eq)] @@ -25,7 +23,7 @@ pub enum EditEventOutcome { pub async fn handle_edit_event( key: KeyEvent, config: &Config, - form_state: &mut FormState, // Now FormState is in scope + form_state: &mut FormState, login_state: &mut LoginState, register_state: &mut RegisterState, admin_state: &mut AdminState, @@ -35,20 +33,16 @@ pub async fn handle_edit_event( grpc_client: &mut GrpcClient, app_state: &AppState, ) -> Result { - // --- Global command mode check --- if let Some("enter_command_mode") = config.get_action_for_key_in_mode( - &config.keybindings.global, // Assuming command mode can be entered globally + &config.keybindings.global, key.code, key.modifiers, ) { - // This check might be redundant if EventHandler already prevents entering Edit mode - // when command_mode is true. However, it's a safeguard. return Ok(EditEventOutcome::Message( "Cannot enter command mode from edit mode here.".to_string(), )); } - // --- Common actions (save, revert) --- if let Some(action) = config.get_action_for_key_in_mode( &config.keybindings.common, key.code, @@ -60,12 +54,10 @@ pub async fn handle_edit_event( } else if app_state.ui.show_register { auth_e::execute_common_action(action, register_state, grpc_client, current_position, total_count).await? } else if app_state.ui.show_add_table { - // TODO: Implement common actions for AddTable if needed format!("Action '{}' not implemented for Add Table in edit mode.", action) } else if app_state.ui.show_add_logic { - // TODO: Implement common actions for AddLogic if needed format!("Action '{}' not implemented for Add Logic in edit mode.", action) - } else { // Assuming Form view + } else { let outcome = form_e::execute_common_action(action, form_state, grpc_client).await?; match outcome { EventOutcome::Ok(msg) | EventOutcome::DataSaved(_, msg) => msg, @@ -76,20 +68,47 @@ pub async fn handle_edit_event( } } - // --- Edit-specific actions --- if let Some(action_str) = config.get_edit_action_for_key(key.code, key.modifiers).as_deref() { - // --- Handle "enter_decider" (Enter key) --- + tracing::info!("[Handler] `handle_edit_event` received action: '{}'", action_str); + + // --- MANUAL AUTOCOMPLETE TRIGGER --- + if action_str == "trigger_autocomplete" { + tracing::info!("[Handler] Action is 'trigger_autocomplete'. Checking conditions..."); // <-- ADD THIS + if app_state.ui.show_form { + tracing::info!("[Handler] In form view. Checking field..."); // <-- ADD THIS + if let Some(field_def) = form_state.fields.get(form_state.current_field) { + if field_def.is_link { + tracing::info!("[Handler] Field '{}' is a link. Activating autocomplete.", field_def.display_name); // <-- ADD THIS + form_state.autocomplete_active = true; + form_state.selected_suggestion_index = Some(0); + form_state.autocomplete_suggestions = vec![ + "Hardcoded Supplier A".to_string(), + "Hardcoded Supplier B".to_string(), + "Hardcoded Company C".to_string(), + ]; + return Ok(EditEventOutcome::Message("Autocomplete triggered".to_string())); + } else { + tracing::error!("[Handler] Field '{}' is NOT a link.", field_def.display_name); // <-- ADD THIS + } + } + } else { + tracing::error!("[Handler] Not in form view. Cannot trigger autocomplete."); // <-- ADD THIS + } + return Ok(EditEventOutcome::Message("Not a linkable field".to_string())); + } + // --- END OF NEW LOGIC --- + if action_str == "enter_decider" { let effective_action = if app_state.ui.show_register && register_state.in_suggestion_mode - && register_state.current_field() == 4 { // Role field + && register_state.current_field() == 4 { "select_suggestion" } else if app_state.ui.show_add_logic && admin_state.add_logic_state.in_target_column_suggestion_mode - && admin_state.add_logic_state.current_field() == 1 { // Target Column field + && admin_state.add_logic_state.current_field() == 1 { "select_suggestion" } else { - "next_field" // Default action for Enter + "next_field" }; let msg = if app_state.ui.show_login { @@ -100,13 +119,12 @@ pub async fn handle_edit_event( add_logic_e::execute_edit_action(effective_action, key, &mut admin_state.add_logic_state, ideal_cursor_column).await? } else if app_state.ui.show_register { auth_e::execute_edit_action(effective_action, key, register_state, ideal_cursor_column).await? - } else { // Form view + } else { form_e::execute_edit_action(effective_action, key, form_state, ideal_cursor_column).await? }; return Ok(EditEventOutcome::Message(msg)); } - // --- Handle "exit" (Escape key) --- if action_str == "exit" { if app_state.ui.show_register && register_state.in_suggestion_mode { let msg = auth_e::execute_edit_action("exit_suggestion_mode", key, register_state, ideal_cursor_column).await?; @@ -121,11 +139,9 @@ pub async fn handle_edit_event( } } - // --- Autocomplete for AddLogicState Target Column --- - if app_state.ui.show_add_logic && admin_state.add_logic_state.current_field() == 1 { // Target Column field - if action_str == "suggestion_down" { // "Tab" is mapped to suggestion_down + if app_state.ui.show_add_logic && admin_state.add_logic_state.current_field() == 1 { + if action_str == "suggestion_down" { if !admin_state.add_logic_state.in_target_column_suggestion_mode { - // Attempt to open suggestions if let Some(profile_name) = admin_state.add_logic_state.profile_name.clone().into() { if let Some(table_name) = admin_state.add_logic_state.selected_table_name.clone() { debug!("Fetching table structure for autocomplete: Profile='{}', Table='{}'", profile_name, table_name); @@ -136,7 +152,6 @@ pub async fn handle_edit_event( admin_state.add_logic_state.update_target_column_suggestions(); if !admin_state.add_logic_state.target_column_suggestions.is_empty() { admin_state.add_logic_state.in_target_column_suggestion_mode = true; - // update_target_column_suggestions handles initial selection return Ok(EditEventOutcome::Message("Column suggestions shown".to_string())); } else { return Ok(EditEventOutcome::Message("No column suggestions for current input".to_string())); @@ -144,7 +159,7 @@ pub async fn handle_edit_event( } Err(e) => { debug!("Error fetching table structure: {}", e); - admin_state.add_logic_state.table_columns_for_suggestions.clear(); // Clear old data on error + admin_state.add_logic_state.table_columns_for_suggestions.clear(); admin_state.add_logic_state.update_target_column_suggestions(); return Ok(EditEventOutcome::Message(format!("Error fetching columns: {}", e))); } @@ -152,10 +167,10 @@ pub async fn handle_edit_event( } else { return Ok(EditEventOutcome::Message("No table selected for column suggestions".to_string())); } - } else { // Should not happen if AddLogic is properly initialized + } else { return Ok(EditEventOutcome::Message("Profile name missing for column suggestions".to_string())); } - } else { // Already in suggestion mode, navigate down + } else { let msg = add_logic_e::execute_edit_action(action_str, key, &mut admin_state.add_logic_state, ideal_cursor_column).await?; return Ok(EditEventOutcome::Message(msg)); } @@ -165,17 +180,12 @@ pub async fn handle_edit_event( } } - // --- Autocomplete for RegisterState Role Field --- - if app_state.ui.show_register && register_state.current_field() == 4 { // Role field - if !register_state.in_suggestion_mode && action_str == "suggestion_down" { // Tab + if app_state.ui.show_register && register_state.current_field() == 4 { + if !register_state.in_suggestion_mode && action_str == "suggestion_down" { register_state.update_role_suggestions(); if !register_state.role_suggestions.is_empty() { register_state.in_suggestion_mode = true; - // update_role_suggestions should handle initial selection return Ok(EditEventOutcome::Message("Role suggestions shown".to_string())); - } else { - // If Tab doesn't open suggestions, it might fall through to "next_field" - // or you might want specific behavior. For now, let it fall through. } } if register_state.in_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up") { @@ -184,28 +194,24 @@ pub async fn handle_edit_event( } } - // --- Dispatch other edit actions --- let msg = if app_state.ui.show_login { auth_e::execute_edit_action(action_str, key, login_state, ideal_cursor_column).await? } else if app_state.ui.show_add_table { add_table_e::execute_edit_action(action_str, key, &mut admin_state.add_table_state, ideal_cursor_column).await? } else if app_state.ui.show_add_logic { - // If not a suggestion action handled above for AddLogic if !(admin_state.add_logic_state.in_target_column_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up")) { add_logic_e::execute_edit_action(action_str, key, &mut admin_state.add_logic_state, ideal_cursor_column).await? - } else { String::new() /* Already handled */ } + } else { String::new() } } else if app_state.ui.show_register { if !(register_state.in_suggestion_mode && matches!(action_str, "suggestion_down" | "suggestion_up")) { auth_e::execute_edit_action(action_str, key, register_state, ideal_cursor_column).await? - } else { String::new() /* Already handled */ } - } else { // Form view + } else { String::new() } + } else { form_e::execute_edit_action(action_str, key, form_state, ideal_cursor_column).await? }; return Ok(EditEventOutcome::Message(msg)); } - // --- Character insertion --- - // If character insertion happens while in suggestion mode, exit suggestion mode first. let mut exited_suggestion_mode_for_typing = false; if app_state.ui.show_register && register_state.in_suggestion_mode { register_state.in_suggestion_mode = false; @@ -228,16 +234,12 @@ pub async fn handle_edit_event( add_logic_e::execute_edit_action("insert_char", key, &mut admin_state.add_logic_state, ideal_cursor_column).await? } else if app_state.ui.show_register { auth_e::execute_edit_action("insert_char", key, register_state, ideal_cursor_column).await? - } else { // Form view + } else { form_e::execute_edit_action("insert_char", key, form_state, ideal_cursor_column).await? }; - // After character insertion, update suggestions if applicable if app_state.ui.show_register && register_state.current_field() == 4 { register_state.update_role_suggestions(); - // If we just exited suggestion mode by typing, don't immediately show them again unless Tab is pressed. - // However, update_role_suggestions will set show_role_suggestions if matches are found. - // This is fine, as the render logic checks in_suggestion_mode. } if app_state.ui.show_add_logic && admin_state.add_logic_state.current_field() == 1 { admin_state.add_logic_state.update_target_column_suggestions(); @@ -247,6 +249,5 @@ pub async fn handle_edit_event( char_insert_msg = "Suggestions hidden".to_string(); } - Ok(EditEventOutcome::Message(char_insert_msg)) } diff --git a/client/src/state/pages/form.rs b/client/src/state/pages/form.rs index fbeaa9b..6cd745b 100644 --- a/client/src/state/pages/form.rs +++ b/client/src/state/pages/form.rs @@ -1,30 +1,44 @@ // src/state/pages/form.rs -use std::collections::HashMap; use crate::config::colors::themes::Theme; -use ratatui::layout::Rect; -use ratatui::Frame; use crate::state::app::highlight::HighlightState; use crate::state::pages::canvas_state::CanvasState; +use ratatui::layout::Rect; +use ratatui::Frame; +use std::collections::HashMap; +// A struct to bridge the display name (label) to the data key from the server. +#[derive(Debug, Clone)] +pub struct FieldDefinition { + pub display_name: String, + pub data_key: String, + pub is_link: bool, // --- NEW --- To identify FK fields +} + +#[derive(Clone)] pub struct FormState { pub id: i64, pub profile_name: String, pub table_name: String, pub total_count: u64, pub current_position: u64, - pub fields: Vec, + pub fields: Vec, pub values: Vec, pub current_field: usize, pub has_unsaved_changes: bool, pub current_cursor_pos: usize, + + // --- NEW AUTOCOMPLETE STATE --- + pub autocomplete_active: bool, + pub autocomplete_suggestions: Vec, + pub selected_suggestion_index: Option, } impl FormState { pub fn new( profile_name: String, table_name: String, - fields: Vec, + fields: Vec, ) -> Self { let values = vec![String::new(); fields.len()]; FormState { @@ -38,10 +52,13 @@ impl FormState { current_field: 0, has_unsaved_changes: false, current_cursor_pos: 0, + // --- INITIALIZE NEW STATE --- + autocomplete_active: false, + autocomplete_suggestions: Vec::new(), + selected_suggestion_index: None, } } - // This signature is now correct and only deals with form-related state. pub fn render( &self, f: &mut Frame, @@ -51,13 +68,13 @@ impl FormState { highlight_state: &HighlightState, ) { let fields_str_slice: Vec<&str> = - self.fields.iter().map(|s| s.as_str()).collect(); + self.fields().iter().map(|s| *s).collect(); let values_str_slice: Vec<&String> = self.values.iter().collect(); crate::components::form::form::render_form( f, area, - self, + self, // <--- This now correctly passes the concrete &FormState &fields_str_slice, &self.current_field, &values_str_slice, @@ -70,7 +87,6 @@ impl FormState { ); } - // ... other methods are unchanged ... pub fn reset_to_empty(&mut self) { self.id = 0; self.values.iter_mut().for_each(|v| v.clear()); @@ -82,6 +98,7 @@ impl FormState { } else { self.current_position = 1; } + self.deactivate_autocomplete(); // Deactivate on reset } pub fn get_current_input(&self) -> &str { @@ -102,13 +119,16 @@ impl FormState { response_data: &HashMap, new_position: u64, ) { - self.values = self.fields.iter().map(|field_from_schema| { - response_data - .iter() - .find(|(key_from_data, _)| key_from_data.eq_ignore_ascii_case(field_from_schema)) - .map(|(_, value)| value.clone()) - .unwrap_or_default() - }).collect(); + self.values = self + .fields + .iter() + .map(|field_def| { + response_data + .get(&field_def.data_key) + .cloned() + .unwrap_or_default() + }) + .collect(); let id_str_opt = response_data .iter() @@ -119,7 +139,12 @@ impl FormState { if let Ok(parsed_id) = id_str.parse::() { self.id = parsed_id; } else { - tracing::error!( "Failed to parse 'id' field '{}' for table {}.{}", id_str, self.profile_name, self.table_name); + tracing::error!( + "Failed to parse 'id' field '{}' for table {}.{}", + id_str, + self.profile_name, + self.table_name + ); self.id = 0; } } else { @@ -130,6 +155,15 @@ impl FormState { self.has_unsaved_changes = false; self.current_field = 0; self.current_cursor_pos = 0; + self.deactivate_autocomplete(); // Deactivate on update + } + + // --- NEW HELPER METHOD --- + /// Deactivates autocomplete and clears its state. + pub fn deactivate_autocomplete(&mut self) { + self.autocomplete_active = false; + self.autocomplete_suggestions.clear(); + self.selected_suggestion_index = None; } } @@ -159,13 +193,18 @@ impl CanvasState for FormState { } fn fields(&self) -> Vec<&str> { - self.fields.iter().map(|s| s.as_str()).collect() + self.fields + .iter() + .map(|f| f.display_name.as_str()) + .collect() } fn set_current_field(&mut self, index: usize) { if index < self.fields.len() { self.current_field = index; } + // Deactivate autocomplete when changing fields + self.deactivate_autocomplete(); } fn set_current_cursor_pos(&mut self, pos: usize) { @@ -176,11 +215,20 @@ impl CanvasState for FormState { self.has_unsaved_changes = changed; } + // --- MODIFIED: Implement autocomplete trait methods --- fn get_suggestions(&self) -> Option<&[String]> { - None + if self.autocomplete_active { + Some(&self.autocomplete_suggestions) + } else { + None + } } fn get_selected_suggestion_index(&self) -> Option { - None + if self.autocomplete_active { + self.selected_suggestion_index + } else { + None + } } } diff --git a/client/src/tui/functions/common/form.rs b/client/src/tui/functions/common/form.rs index 8f663c4..952bb91 100644 --- a/client/src/tui/functions/common/form.rs +++ b/client/src/tui/functions/common/form.rs @@ -23,7 +23,7 @@ pub async fn save( let data_map: HashMap = form_state.fields.iter() .zip(form_state.values.iter()) - .map(|(field, value)| (field.clone(), value.clone())) + .map(|(field_def, value)| (field_def.data_key.clone(), value.clone())) .collect(); let outcome: SaveOutcome; diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 213f411..acbd02b 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -9,7 +9,7 @@ use crate::modes::common::commands::CommandHandler; use crate::modes::handlers::event::{EventHandler, EventOutcome}; use crate::modes::handlers::mode_manager::{AppMode, ModeManager}; use crate::state::pages::canvas_state::CanvasState; -use crate::state::pages::form::FormState; +use crate::state::pages::form::{FormState, FieldDefinition}; // Import FieldDefinition use crate::state::pages::auth::AuthState; use crate::state::pages::auth::LoginState; use crate::state::pages::auth::RegisterState; @@ -92,13 +92,19 @@ pub async fn run_ui() -> Result<()> { .await .context("Failed to initialize app state and form")?; - let filtered_columns = filter_user_columns(initial_columns_from_service); + let initial_field_defs: Vec = filter_user_columns(initial_columns_from_service) + .into_iter() + .map(|col_name| FieldDefinition { + display_name: col_name.clone(), + data_key: col_name, + is_link: false, + }) + .collect(); let mut form_state = FormState::new( initial_profile.clone(), initial_table.clone(), - filtered_columns, - vec![], // FIX 1: Provide the missing 4th argument + initial_field_defs, ); UiService::fetch_and_set_table_count(&mut grpc_client, &mut form_state) @@ -334,36 +340,54 @@ pub async fn run_ui() -> Result<()> { .await { Ok(structure_response) => { - let new_columns: Vec = structure_response + // --- START OF MODIFIED LOGIC --- + let all_columns: Vec = structure_response .columns .iter() .map(|c| c.name.clone()) .collect(); - // FIX 2: Look up links in the profile_tree - let new_links: Vec = app_state + // 1. Process regular columns first, filtering out FKs + let mut field_definitions: Vec = + filter_user_columns(all_columns) + .into_iter() + .filter(|col_name| !col_name.ends_with("_id")) // Exclude FKs + .map(|col_name| FieldDefinition { + display_name: col_name.clone(), + data_key: col_name, + is_link: false, // Regular fields are not links + }) + .collect(); + + // 2. Process linked tables to create the correct labels + let linked_tables: Vec = app_state .profile_tree .profiles .iter() .find(|p| p.name == *prof_name) .and_then(|profile| { - profile - .tables - .iter() - .find(|t| t.name == *tbl_name) + profile.tables.iter().find(|t| t.name == *tbl_name) }) - .map_or(vec![], |table| { - table.depends_on.clone() + .map_or(vec![], |table| table.depends_on.clone()); + + for linked_table_name in linked_tables { + let base_name = linked_table_name + .split_once('_') + .map_or(linked_table_name.as_str(), |(_, rest)| rest); + let data_key = format!("{}_id", base_name); + let display_name = linked_table_name; + + field_definitions.push(FieldDefinition { + display_name, + data_key, + is_link: true, // These fields ARE links }); - - let filtered_columns = - filter_user_columns(new_columns); - + } + // --- END OF MODIFIED LOGIC --- form_state = FormState::new( prof_name.clone(), tbl_name.clone(), - filtered_columns, - new_links, + field_definitions, ); if let Err(e) = UiService::fetch_and_set_table_count( @@ -401,6 +425,7 @@ pub async fn run_ui() -> Result<()> { prev_view_table_name = current_view_table; table_just_switched = true; } + Err(e) => { app_state.update_dialog_content( &format!("Error fetching table structure: {}", e),