diff --git a/client/src/lib.rs b/client/src/lib.rs index 41adf36..216a3a2 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -7,6 +7,7 @@ pub mod components; pub mod modes; pub mod functions; pub mod services; +pub mod utils; pub use ui::run_ui; diff --git a/client/src/services/ui_service.rs b/client/src/services/ui_service.rs index f5c45c0..346e5e4 100644 --- a/client/src/services/ui_service.rs +++ b/client/src/services/ui_service.rs @@ -5,6 +5,7 @@ use crate::state::pages::form::FormState; use crate::tui::functions::common::form::SaveOutcome; use crate::state::pages::add_logic::AddLogicState; use crate::state::app::state::AppState; +use crate::utils::columns::filter_user_columns; use anyhow::{Context, Result}; pub struct UiService; @@ -82,7 +83,7 @@ impl UiService { .into_iter() .map(|col| col.name) .collect(); - Ok(column_names) + Ok(filter_user_columns(column_names)) } Err(e) => { tracing::warn!("Failed to fetch columns for {}.{}: {}", profile_name, table_name, e); @@ -90,12 +91,10 @@ impl UiService { } } } - - // MODIFIED: To set initial view table in AppState and return initial column names + pub async fn initialize_app_state_and_form( grpc_client: &mut GrpcClient, app_state: &mut AppState, - // Returns (initial_profile, initial_table, initial_columns) ) -> Result<(String, String, Vec)> { let profile_tree = grpc_client .get_profile_tree() @@ -104,7 +103,6 @@ impl UiService { app_state.profile_tree = profile_tree; // Determine initial table to load (e.g., first table of first profile, or a default) - // For now, let's hardcode a default for simplicity, but this should be more dynamic let initial_profile_name = app_state .profile_tree .profiles @@ -141,10 +139,11 @@ impl UiService { .map(|col| col.name.clone()) .collect(); - Ok((initial_profile_name, initial_table_name, column_names)) + let filtered_columns = filter_user_columns(column_names); + + Ok((initial_profile_name, initial_table_name, filtered_columns)) } - // NEW: Fetches and sets count for the current table in FormState pub async fn fetch_and_set_table_count( grpc_client: &mut GrpcClient, form_state: &mut FormState, @@ -161,35 +160,26 @@ impl UiService { ))?; form_state.total_count = total_count; - // Set initial position: if table has items, point to first, else point to new entry if total_count > 0 { form_state.current_position = 1; } else { - form_state.current_position = 1; // For a new entry in an empty table + form_state.current_position = 1; } Ok(()) } - // MODIFIED: Generic table data loading pub async fn load_table_data_by_position( grpc_client: &mut GrpcClient, - form_state: &mut FormState, // Takes &mut FormState to update it - // position is now read from form_state.current_position + form_state: &mut FormState, ) -> Result { - // Ensure current_position is valid before fetching if form_state.current_position == 0 || (form_state.total_count > 0 && form_state.current_position > form_state.total_count) { - // This indicates a "new entry" state, no data to load from server. - // The caller should handle this by calling form_state.reset_to_empty() - // or ensuring this function isn't called for a new entry position. - // For now, let's assume reset_to_empty was called if needed. - form_state.reset_to_empty(); // Ensure fields are clear for new entry + form_state.reset_to_empty(); return Ok(format!( "New entry mode for table {}.{}", form_state.profile_name, form_state.table_name )); } if form_state.total_count == 0 && form_state.current_position == 1 { - // Table is empty, this is the position for a new entry form_state.reset_to_empty(); return Ok(format!( "New entry mode for empty table {}.{}", @@ -197,7 +187,6 @@ impl UiService { )); } - match grpc_client .get_table_data_by_position( form_state.profile_name.clone(), @@ -208,7 +197,6 @@ impl UiService { { Ok(response) => { form_state.update_from_response(&response.data); - // ID, values, current_field, current_cursor_pos, has_unsaved_changes are set by update_from_response Ok(format!( "Loaded entry {}/{} for table {}.{}", form_state.current_position, @@ -218,9 +206,6 @@ impl UiService { )) } Err(e) => { - // If loading fails (e.g., record deleted, network error), what should happen? - // Maybe reset to a new entry state or show an error and keep current data. - // For now, log error and return error message. tracing::error!( "Error loading entry {} for table {}.{}: {}", form_state.current_position, @@ -228,8 +213,6 @@ impl UiService { form_state.table_name, e ); - // Potentially clear form or revert to a safe state - // form_state.reset_to_empty(); Err(anyhow::anyhow!( "Error loading entry {}: {}", form_state.current_position, @@ -239,27 +222,20 @@ impl UiService { } } - // MODIFIED: To work with FormState's count and position pub async fn handle_save_outcome( save_outcome: SaveOutcome, - _grpc_client: &mut GrpcClient, // May not be needed if count is fetched separately - _app_state: &mut AppState, // May not be needed directly + _grpc_client: &mut GrpcClient, + _app_state: &mut AppState, form_state: &mut FormState, ) -> Result<()> { match save_outcome { SaveOutcome::CreatedNew(new_id) => { - // form_state.total_count and form_state.current_position should have been updated - // by the `save` function itself. - // Ensure form_state.id is set. form_state.id = new_id; - // Potentially, re-fetch count to be absolutely sure, but save should be authoritative. - // UiService::fetch_and_set_table_count(grpc_client, form_state).await?; } SaveOutcome::UpdatedExisting | SaveOutcome::NoChange => { - // No changes to total_count or current_position needed from here. + // No action needed } } Ok(()) } } - diff --git a/client/src/state/pages/form.rs b/client/src/state/pages/form.rs index 869c9d9..f32a6a0 100644 --- a/client/src/state/pages/form.rs +++ b/client/src/state/pages/form.rs @@ -107,11 +107,8 @@ impl FormState { &mut self, response_data: &HashMap, ) { - self.values = self.fields - .iter() - .map(|field_name| { - response_data.get(field_name).cloned().unwrap_or_default() - }) + self.values = self.fields.iter() + .map(|field| response_data.get(field).cloned().unwrap_or_default()) .collect(); if let Some(id_str) = response_data.get("id") { diff --git a/client/src/tui/functions/common/form.rs b/client/src/tui/functions/common/form.rs index f5aed75..e556410 100644 --- a/client/src/tui/functions/common/form.rs +++ b/client/src/tui/functions/common/form.rs @@ -21,9 +21,7 @@ pub async fn save( return Ok(SaveOutcome::NoChange); } - let data_map: HashMap = form_state - .fields - .iter() + let data_map: HashMap = form_state.fields.iter() .zip(form_state.values.iter()) .map(|(field, value)| (field.clone(), value.clone())) .collect(); diff --git a/client/src/ui/handlers/ui.rs b/client/src/ui/handlers/ui.rs index 994b8d3..cbac925 100644 --- a/client/src/ui/handlers/ui.rs +++ b/client/src/ui/handlers/ui.rs @@ -26,6 +26,7 @@ use crate::tui::functions::common::register::RegisterResult; use crate::ui::handlers::context::DialogPurpose; use crate::tui::functions::common::login; use crate::tui::functions::common::register; +use crate::utils::columns::filter_user_columns; use std::time::Instant; use anyhow::{anyhow, Context, Result}; use crossterm::cursor::SetCursorStyle; @@ -87,10 +88,19 @@ pub async fn run_ui() -> Result<()> { .await .context("Failed to initialize app state and form")?; + // Initialize AppState and FormState with table data + let (initial_profile, initial_table, initial_columns_from_service) = // Renamed for clarity + UiService::initialize_app_state_and_form(&mut grpc_client, &mut app_state) + .await + .context("Failed to initialize app state and form")?; + + // Filter the columns obtained from the service + let filtered_columns = filter_user_columns(initial_columns_from_service); // Use the correct variable + let mut form_state = FormState::new( initial_profile.clone(), initial_table.clone(), - initial_columns, + filtered_columns, ); UiService::fetch_and_set_table_count(&mut grpc_client, &mut form_state) diff --git a/client/src/utils/columns.rs b/client/src/utils/columns.rs new file mode 100644 index 0000000..72989a3 --- /dev/null +++ b/client/src/utils/columns.rs @@ -0,0 +1,14 @@ +// src/utils/columns.rs +pub fn is_system_column(column_name: &str) -> bool { + match column_name { + "id" | "deleted" | "created_at" => true, + name if name.ends_with("_id") => true, + _ => false, + } +} + +pub fn filter_user_columns(all_columns: Vec) -> Vec { + all_columns.into_iter() + .filter(|col| !is_system_column(col)) + .collect() +} diff --git a/client/src/utils/mod.rs b/client/src/utils/mod.rs new file mode 100644 index 0000000..fd6b6c7 --- /dev/null +++ b/client/src/utils/mod.rs @@ -0,0 +1,4 @@ +// src/utils/mod.rs + +pub mod columns; +pub use columns::*;