diff --git a/src/adresar/handlers/get_table_structure.rs b/src/adresar/handlers/get_table_structure.rs index 716c9d7..e1b6cb1 100644 --- a/src/adresar/handlers/get_table_structure.rs +++ b/src/adresar/handlers/get_table_structure.rs @@ -8,12 +8,6 @@ pub async fn get_table_structure( _request: Empty, ) -> Result { let columns = vec![ - TableColumn { - name: "id".to_string(), - data_type: "BIGSERIAL".to_string(), - is_nullable: false, - is_primary_key: true, - }, TableColumn { name: "firma".to_string(), data_type: "TEXT".to_string(), diff --git a/src/client/terminal.rs b/src/client/terminal.rs index 51ee50e..89fd015 100644 --- a/src/client/terminal.rs +++ b/src/client/terminal.rs @@ -123,4 +123,16 @@ impl AppTerminal { let response = self.grpc_client.put_adresar(request).await?; Ok(response) } + + /// Fetch the table structure once at startup. + /// + /// TODO: In the future, refactor to subscribe to changes rather than one-time fetch. + pub async fn get_table_structure( + &mut self, + ) -> Result> { + // Note: Adjust the request according to your proto definitions. + let request = tonic::Request::new(crate::proto::multieko2::Empty::default()); + let response = self.grpc_client.get_table_structure(request).await?; + Ok(response.into_inner()) + } } diff --git a/src/client/ui/handlers/event.rs b/src/client/ui/handlers/event.rs index 5939a46..d58dca9 100644 --- a/src/client/ui/handlers/event.rs +++ b/src/client/ui/handlers/event.rs @@ -1,4 +1,5 @@ // src/client/ui/handlers/event.rs + use crossterm::event::{Event, KeyCode, KeyModifiers}; use crossterm::cursor::{SetCursorStyle}; use crate::client::terminal::AppTerminal; @@ -66,23 +67,24 @@ impl EventHandler { *current_position = new_position; match app_terminal.get_adresar_by_position(*current_position).await { Ok(response) => { - // Update all form fields - form_state.id = response.id; - form_state.firma = response.firma; - form_state.kz = response.kz; - form_state.drc = response.drc; - form_state.ulica = response.ulica; - form_state.psc = response.psc; - form_state.mesto = response.mesto; - form_state.stat = response.stat; - form_state.banka = response.banka; - form_state.ucet = response.ucet; - form_state.skladm = response.skladm; - form_state.ico = response.ico; - form_state.kontakt = response.kontakt; - form_state.telefon = response.telefon; - form_state.skladu = response.skladu; - form_state.fax = response.fax; + // Update all form fields dynamically + form_state.values = vec![ + response.firma, + response.kz, + response.drc, + response.ulica, + response.psc, + response.mesto, + response.stat, + response.banka, + response.ucet, + response.skladm, + response.ico, + response.kontakt, + response.telefon, + response.skladu, + response.fax, + ]; let current_input = form_state.get_current_input(); form_state.current_cursor_pos = self.ideal_cursor_column.min(current_input.len()); @@ -102,23 +104,24 @@ impl EventHandler { if *current_position <= total_count { match app_terminal.get_adresar_by_position(*current_position).await { Ok(response) => { - // Update all form fields - form_state.id = response.id; - form_state.firma = response.firma; - form_state.kz = response.kz; - form_state.drc = response.drc; - form_state.ulica = response.ulica; - form_state.psc = response.psc; - form_state.mesto = response.mesto; - form_state.stat = response.stat; - form_state.banka = response.banka; - form_state.ucet = response.ucet; - form_state.skladm = response.skladm; - form_state.ico = response.ico; - form_state.kontakt = response.kontakt; - form_state.telefon = response.telefon; - form_state.skladu = response.skladu; - form_state.fax = response.fax; + // Update all form fields dynamically + form_state.values = vec![ + response.firma, + response.kz, + response.drc, + response.ulica, + response.psc, + response.mesto, + response.stat, + response.banka, + response.ucet, + response.skladm, + response.ico, + response.kontakt, + response.telefon, + response.skladu, + response.fax, + ]; let current_input = form_state.get_current_input(); form_state.current_cursor_pos = self.ideal_cursor_column.min(current_input.len()); @@ -204,21 +207,21 @@ impl EventHandler { match key.code { KeyCode::Enter => { let form_data = PostAdresarRequest { - firma: form_state.firma.clone(), - kz: form_state.kz.clone(), - drc: form_state.drc.clone(), - ulica: form_state.ulica.clone(), - psc: form_state.psc.clone(), - mesto: form_state.mesto.clone(), - stat: form_state.stat.clone(), - banka: form_state.banka.clone(), - ucet: form_state.ucet.clone(), - skladm: form_state.skladm.clone(), - ico: form_state.ico.clone(), - kontakt: form_state.kontakt.clone(), - telefon: form_state.telefon.clone(), - skladu: form_state.skladu.clone(), - fax: form_state.fax.clone(), + firma: form_state.values[0].clone(), + kz: form_state.values[1].clone(), + drc: form_state.values[2].clone(), + ulica: form_state.values[3].clone(), + psc: form_state.values[4].clone(), + mesto: form_state.values[5].clone(), + stat: form_state.values[6].clone(), + banka: form_state.values[7].clone(), + ucet: form_state.values[8].clone(), + skladm: form_state.values[9].clone(), + ico: form_state.values[10].clone(), + kontakt: form_state.values[11].clone(), + telefon: form_state.values[12].clone(), + skladu: form_state.values[13].clone(), + fax: form_state.values[14].clone(), }; let command = self.command_input.trim(); @@ -236,21 +239,21 @@ impl EventHandler { let message = if is_new { // POST new entry let post_request = PostAdresarRequest { - firma: form_state.firma.clone(), - kz: form_state.kz.clone(), - drc: form_state.drc.clone(), - ulica: form_state.ulica.clone(), - psc: form_state.psc.clone(), - mesto: form_state.mesto.clone(), - stat: form_state.stat.clone(), - banka: form_state.banka.clone(), - ucet: form_state.ucet.clone(), - skladm: form_state.skladm.clone(), - ico: form_state.ico.clone(), - kontakt: form_state.kontakt.clone(), - telefon: form_state.telefon.clone(), - skladu: form_state.skladu.clone(), - fax: form_state.fax.clone(), + firma: form_state.values[0].clone(), + kz: form_state.values[1].clone(), + drc: form_state.values[2].clone(), + ulica: form_state.values[3].clone(), + psc: form_state.values[4].clone(), + mesto: form_state.values[5].clone(), + stat: form_state.values[6].clone(), + banka: form_state.values[7].clone(), + ucet: form_state.values[8].clone(), + skladm: form_state.values[9].clone(), + ico: form_state.values[10].clone(), + kontakt: form_state.values[11].clone(), + telefon: form_state.values[12].clone(), + skladu: form_state.values[13].clone(), + fax: form_state.values[14].clone(), }; let response = app_terminal.post_adresar(post_request).await?; // Update state @@ -262,21 +265,21 @@ impl EventHandler { // PUT existing entry let put_request = PutAdresarRequest { id: form_state.id, - firma: form_state.firma.clone(), - kz: form_state.kz.clone(), - drc: form_state.drc.clone(), - ulica: form_state.ulica.clone(), - psc: form_state.psc.clone(), - mesto: form_state.mesto.clone(), - stat: form_state.stat.clone(), - banka: form_state.banka.clone(), - ucet: form_state.ucet.clone(), - skladm: form_state.skladm.clone(), - ico: form_state.ico.clone(), - kontakt: form_state.kontakt.clone(), - telefon: form_state.telefon.clone(), - skladu: form_state.skladu.clone(), - fax: form_state.fax.clone(), + firma: form_state.values[0].clone(), + kz: form_state.values[1].clone(), + drc: form_state.values[2].clone(), + ulica: form_state.values[3].clone(), + psc: form_state.values[4].clone(), + mesto: form_state.values[5].clone(), + stat: form_state.values[6].clone(), + banka: form_state.values[7].clone(), + ucet: form_state.values[8].clone(), + skladm: form_state.values[9].clone(), + ico: form_state.values[10].clone(), + kontakt: form_state.values[11].clone(), + telefon: form_state.values[12].clone(), + skladu: form_state.values[13].clone(), + fax: form_state.values[14].clone(), }; let response = app_terminal.put_adresar(put_request).await?; "Entry updated".to_string() diff --git a/src/client/ui/handlers/form.rs b/src/client/ui/handlers/form.rs index e36ba60..ff0267b 100644 --- a/src/client/ui/handlers/form.rs +++ b/src/client/ui/handlers/form.rs @@ -1,58 +1,33 @@ // src/client/ui/handlers/form.rs + use crate::client::components1::render_form; use crate::client::colors::Theme; use ratatui::layout::Rect; use ratatui::Frame; + pub struct FormState { pub id: i64, - pub firma: String, - pub kz: String, - pub drc: String, - pub ulica: String, - pub psc: String, - pub mesto: String, - pub stat: String, - pub banka: String, - pub ucet: String, - pub skladm: String, - pub ico: String, - pub kontakt: String, - pub telefon: String, - pub skladu: String, - pub fax: String, + pub fields: Vec, // Use Vec for dynamic field names + pub values: Vec, // Store field values dynamically pub current_field: usize, - pub fields: Vec<&'static str>, pub has_unsaved_changes: bool, pub current_cursor_pos: usize, } + impl FormState { - pub fn new() -> Self { + /// Create a new FormState with dynamic fields. + pub fn new(fields: Vec) -> Self { + let values = vec![String::new(); fields.len()]; // Initialize values for each field FormState { id: 0, - firma: String::new(), - kz: String::new(), - drc: String::new(), - ulica: String::new(), - psc: String::new(), - mesto: String::new(), - stat: String::new(), - banka: String::new(), - ucet: String::new(), - skladm: String::new(), - ico: String::new(), - kontakt: String::new(), - telefon: String::new(), - skladu: String::new(), - fax: String::new(), + fields, + values, current_field: 0, has_unsaved_changes: false, current_cursor_pos: 0, - fields: vec![ - "Firma", "KZ", "DRC", "Ulica", "PSC", "Mesto", "Stat", "Banka", - "Ucet", "Skladm", "ICO", "Kontakt", "Telefon", "Skladu", "Fax", - ], } } + pub fn render( &self, f: &mut Frame, @@ -62,80 +37,40 @@ impl FormState { total_count: u64, current_position: u64, ) { + // Convert Vec to Vec<&str> for fields + let fields: Vec<&str> = self.fields.iter().map(|s| s.as_str()).collect(); + // Convert Vec to Vec<&String> for values + let values: Vec<&String> = self.values.iter().collect(); + render_form( f, area, self, - &self.fields, + &fields, &self.current_field, - &[ - &self.firma, &self.kz, &self.drc, &self.ulica, &self.psc, &self.mesto, &self.stat, &self.banka, - &self.ucet, &self.skladm, &self.ico, &self.kontakt, &self.telefon, &self.skladu, &self.fax, - ], + &values, &theme, is_edit_mode, total_count, current_position, ); } + pub fn reset_to_empty(&mut self) { - self.firma.clear(); - self.kz.clear(); - self.drc.clear(); - self.ulica.clear(); - self.psc.clear(); - self.mesto.clear(); - self.stat.clear(); - self.banka.clear(); - self.ucet.clear(); - self.skladm.clear(); - self.ico.clear(); - self.kontakt.clear(); - self.telefon.clear(); - self.skladu.clear(); - self.fax.clear(); + self.values.iter_mut().for_each(|v| v.clear()); // Clear all values self.has_unsaved_changes = false; } pub fn get_current_input(&self) -> &str { - match self.current_field { - 0 => &self.firma, - 1 => &self.kz, - 2 => &self.drc, - 3 => &self.ulica, - 4 => &self.psc, - 5 => &self.mesto, - 6 => &self.stat, - 7 => &self.banka, - 8 => &self.ucet, - 9 => &self.skladm, - 10 => &self.ico, - 11 => &self.kontakt, - 12 => &self.telefon, - 13 => &self.skladu, - 14 => &self.fax, - _ => "", - } + self.values + .get(self.current_field) + .map(|s| s.as_str()) + .unwrap_or("") } pub fn get_current_input_mut(&mut self) -> &mut String { - match self.current_field { - 0 => &mut self.firma, - 1 => &mut self.kz, - 2 => &mut self.drc, - 3 => &mut self.ulica, - 4 => &mut self.psc, - 5 => &mut self.mesto, - 6 => &mut self.stat, - 7 => &mut self.banka, - 8 => &mut self.ucet, - 9 => &mut self.skladm, - 10 => &mut self.ico, - 11 => &mut self.kontakt, - 12 => &mut self.telefon, - 13 => &mut self.skladu, - 14 => &mut self.fax, - _ => &mut self.firma, // Default to first field - } + self.values + .get_mut(self.current_field) + .expect("Invalid current_field index") } } diff --git a/src/client/ui/handlers/render.rs b/src/client/ui/handlers/render.rs index 1d63806..9b7df8b 100644 --- a/src/client/ui/handlers/render.rs +++ b/src/client/ui/handlers/render.rs @@ -37,12 +37,11 @@ pub fn render_ui( form_state.render(f, main_chunks[0], theme, is_edit_mode, total_count, current_position); // Right panel - Preview Card + let preview_values: Vec<&String> = form_state.values.iter().collect(); render_preview_card( f, main_chunks[1], - &[ - &form_state.firma, &form_state.ulica, &form_state.mesto, &form_state.psc, &form_state.ico, &form_state.kontakt, &form_state.telefon, - ], + &preview_values, // Pass dynamic values as &[&String] &theme, ); diff --git a/src/client/ui/handlers/ui.rs b/src/client/ui/handlers/ui.rs index e8e0962..bb3ddff 100644 --- a/src/client/ui/handlers/ui.rs +++ b/src/client/ui/handlers/ui.rs @@ -3,26 +3,35 @@ use crate::client::terminal::AppTerminal; use crate::client::colors::Theme; use crate::client::config::Config; -use crate::client::ui::handlers::{ - event::EventHandler, - form::FormState, - state::AppState, - render::render_ui -}; +use crate::client::ui::handlers::{event::EventHandler, form::FormState, state::AppState, render::render_ui}; pub async fn run_ui() -> Result<(), Box> { let config = Config::load()?; let mut app_terminal = AppTerminal::new().await?; let theme = Theme::dark(); - let mut form_state = FormState::new(); + // Fetch table structure at startup (one-time) + // TODO: Later, consider implementing a live update for table structure changes. + let table_structure = app_terminal.get_table_structure().await?; + + // Extract the column names from the response + let column_names: Vec = table_structure + .columns + .iter() + .map(|col| col.name.clone()) + .collect(); + + // Initialize FormState with dynamic fields + let mut form_state = FormState::new(column_names); + + // The rest of your UI initialization remains the same let mut event_handler = EventHandler::new(); let mut app_state = AppState::new()?; // Fetch the total count of Adresar entries let total_count = app_terminal.get_adresar_count().await?; app_state.update_total_count(total_count); - app_state.update_current_position(total_count.saturating_add(1)); // Start in new entry mode + app_state.update_current_position(total_count.saturating_add(1)); // Start in new entry mode form_state.reset_to_empty(); loop { @@ -72,11 +81,32 @@ pub async fn run_ui() -> Result<(), Box> { .await { Ok(response) => { - update_form_state_from_response(&mut form_state, response); + // Update form values dynamically + form_state.values = vec![ + response.firma, + response.kz, + response.drc, + response.ulica, + response.psc, + response.mesto, + response.stat, + response.banka, + response.ucet, + response.skladm, + response.ico, + response.kontakt, + response.telefon, + response.skladu, + response.fax, + ]; + + let current_input = form_state.get_current_input(); + form_state.current_cursor_pos = event_handler.ideal_cursor_column.min(current_input.len()); + form_state.has_unsaved_changes = false; + event_handler.command_message = format!("Loaded entry {}", app_state.current_position); } Err(e) => { - event_handler.command_message = - format!("Error loading entry: {}", e); + event_handler.command_message = format!("Error loading entry: {}", e); } } } else { @@ -91,25 +121,3 @@ pub async fn run_ui() -> Result<(), Box> { } } } - -// Helper function to update form state from gRPC response -fn update_form_state_from_response(form_state: &mut FormState, response: crate::proto::multieko2::AdresarResponse) { - form_state.firma = response.firma; - form_state.kz = response.kz; - form_state.drc = response.drc; - form_state.ulica = response.ulica; - form_state.psc = response.psc; - form_state.mesto = response.mesto; - form_state.stat = response.stat; - form_state.banka = response.banka; - form_state.ucet = response.ucet; - form_state.skladm = response.skladm; - form_state.ico = response.ico; - form_state.kontakt = response.kontakt; - form_state.telefon = response.telefon; - form_state.skladu = response.skladu; - form_state.fax = response.fax; - - form_state.current_field = 0; - form_state.has_unsaved_changes = false; -}