displaying data properly, fixing hardcoded backend to firma part one

This commit is contained in:
filipriec
2025-06-07 14:05:35 +02:00
parent 08e01d41f2
commit 387e1a0fe0
10 changed files with 101 additions and 119 deletions

View File

@@ -17,28 +17,29 @@ pub fn render_form(
fields: &[&str], fields: &[&str],
current_field_idx: &usize, current_field_idx: &usize,
inputs: &[&String], inputs: &[&String],
table_name: &str, // This parameter receives the correct table name
theme: &Theme, theme: &Theme,
is_edit_mode: bool, is_edit_mode: bool,
highlight_state: &HighlightState, highlight_state: &HighlightState,
total_count: u64, total_count: u64,
current_position: u64, current_position: u64,
) { ) {
// Create Adresar card // 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() let adresar_card = Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.border_style(Style::default().fg(theme.border)) .border_style(Style::default().fg(theme.border))
.title(" Adresar ") .title(card_title) // Use the dynamic title
.style(Style::default().bg(theme.bg).fg(theme.fg)); .style(Style::default().bg(theme.bg).fg(theme.fg));
f.render_widget(adresar_card, area); f.render_widget(adresar_card, area);
// Define inner area
let inner_area = area.inner(Margin { let inner_area = area.inner(Margin {
horizontal: 1, horizontal: 1,
vertical: 1, vertical: 1,
}); });
// Create main layout
let main_layout = Layout::default() let main_layout = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
@@ -47,12 +48,11 @@ pub fn render_form(
]) ])
.split(inner_area); .split(inner_area);
// Render count/position
let count_position_text = if total_count == 0 && current_position == 1 { let count_position_text = if total_count == 0 && current_position == 1 {
"Total: 0 | New Entry".to_string() "Total: 0 | New Entry".to_string()
} else if current_position > total_count && total_count > 0 { } else if current_position > total_count && total_count > 0 {
format!("Total: {} | New Entry ({})", total_count, current_position) format!("Total: {} | New Entry ({})", total_count, current_position)
} else if total_count == 0 && current_position > 1 { // Should not happen if logic is correct } else if total_count == 0 && current_position > 1 {
format!("Total: 0 | New Entry ({})", current_position) format!("Total: 0 | New Entry ({})", current_position)
} }
else { else {
@@ -63,7 +63,6 @@ pub fn render_form(
.alignment(Alignment::Left); .alignment(Alignment::Left);
f.render_widget(count_para, main_layout[0]); f.render_widget(count_para, main_layout[0]);
// Delegate input handling to canvas
render_canvas( render_canvas(
f, f,
main_layout[1], main_layout[1],

View File

@@ -23,8 +23,6 @@ pub async fn handle_read_only_event(
add_table_state: &mut AddTableState, add_table_state: &mut AddTableState,
add_logic_state: &mut AddLogicState, add_logic_state: &mut AddLogicState,
key_sequence_tracker: &mut KeySequenceTracker, key_sequence_tracker: &mut KeySequenceTracker,
current_position: &mut u64,
total_count: u64,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
command_message: &mut String, command_message: &mut String,
edit_mode_cooldown: &mut bool, edit_mode_cooldown: &mut bool,
@@ -74,12 +72,10 @@ pub async fn handle_read_only_event(
action, action,
form_state, form_state,
grpc_client, grpc_client,
current_position,
total_count,
ideal_cursor_column, ideal_cursor_column,
) )
.await? .await?
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { // Handle login context actions } else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
crate::tui::functions::login::handle_action(action).await? crate::tui::functions::login::handle_action(action).await?
} else if app_state.ui.show_add_table { } else if app_state.ui.show_add_table {
add_table_ro::execute_action( add_table_ro::execute_action(
@@ -143,12 +139,10 @@ pub async fn handle_read_only_event(
action, action,
form_state, form_state,
grpc_client, grpc_client,
current_position,
total_count,
ideal_cursor_column, ideal_cursor_column,
) )
.await? .await?
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) { // Handle login context actions } else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
crate::tui::functions::login::handle_action(action).await? crate::tui::functions::login::handle_action(action).await?
} else if app_state.ui.show_add_table { } else if app_state.ui.show_add_table {
add_table_ro::execute_action( add_table_ro::execute_action(
@@ -177,7 +171,7 @@ pub async fn handle_read_only_event(
key_sequence_tracker, key_sequence_tracker,
command_message, command_message,
).await? ).await?
} else if app_state.ui.show_login { // Handle login general actions } else if app_state.ui.show_login {
auth_ro::execute_action( auth_ro::execute_action(
action, action,
app_state, app_state,
@@ -211,8 +205,6 @@ pub async fn handle_read_only_event(
action, action,
form_state, form_state,
grpc_client, grpc_client,
current_position,
total_count,
ideal_cursor_column, ideal_cursor_column,
) )
.await? .await?
@@ -245,7 +237,7 @@ pub async fn handle_read_only_event(
key_sequence_tracker, key_sequence_tracker,
command_message, command_message,
).await? ).await?
} else if app_state.ui.show_login { // Handle login general actions } else if app_state.ui.show_login {
auth_ro::execute_action( auth_ro::execute_action(
action, action,
app_state, app_state,

View File

@@ -338,15 +338,23 @@ impl EventHandler {
} }
} }
let mut current_position = form_state.current_position;
let total_count = form_state.total_count;
let (_should_exit, message) = read_only::handle_read_only_event( let (_should_exit, message) = read_only::handle_read_only_event(
app_state, key_event, config, form_state, login_state, register_state, app_state,
&mut admin_state.add_table_state, &mut admin_state.add_logic_state, key_event,
&mut self.key_sequence_tracker, &mut current_position, total_count, config,
grpc_client, &mut self.command_message, &mut self.edit_mode_cooldown, form_state,
login_state,
register_state,
&mut admin_state.add_table_state,
&mut admin_state.add_logic_state,
&mut self.key_sequence_tracker,
// No more current_position or total_count arguments
grpc_client,
&mut self.command_message,
&mut self.edit_mode_cooldown,
&mut self.ideal_cursor_column, &mut self.ideal_cursor_column,
).await?; )
.await?;
return Ok(EventOutcome::Ok(message)); return Ok(EventOutcome::Ok(message));
} }
@@ -365,15 +373,22 @@ impl EventHandler {
return Ok(EventOutcome::Ok("".to_string())); return Ok(EventOutcome::Ok("".to_string()));
} }
let mut current_position = form_state.current_position;
let total_count = form_state.total_count;
let (_should_exit, message) = read_only::handle_read_only_event( let (_should_exit, message) = read_only::handle_read_only_event(
app_state, key_event, config, form_state, login_state, register_state, app_state,
&mut admin_state.add_table_state, &mut admin_state.add_logic_state, key_event,
&mut self.key_sequence_tracker, &mut current_position, total_count, config,
grpc_client, &mut self.command_message, &mut self.edit_mode_cooldown, form_state,
login_state,
register_state,
&mut admin_state.add_table_state,
&mut admin_state.add_logic_state,
&mut self.key_sequence_tracker,
grpc_client,
&mut self.command_message,
&mut self.edit_mode_cooldown,
&mut self.ideal_cursor_column, &mut self.ideal_cursor_column,
).await?; )
.await?;
return Ok(EventOutcome::Ok(message)); return Ok(EventOutcome::Ok(message));
} }

View File

@@ -44,8 +44,6 @@ pub async fn handle_highlight_event(
&mut admin_state.add_table_state, &mut admin_state.add_table_state,
&mut admin_state.add_logic_state, &mut admin_state.add_logic_state,
key_sequence_tracker, key_sequence_tracker,
current_position,
total_count,
grpc_client, grpc_client,
command_message, // Pass the message buffer command_message, // Pass the message buffer
edit_mode_cooldown, edit_mode_cooldown,

View File

@@ -161,7 +161,7 @@ impl UiService {
form_state.total_count = total_count; form_state.total_count = total_count;
if total_count > 0 { if total_count > 0 {
form_state.current_position = 1; form_state.current_position = total_count;
} else { } else {
form_state.current_position = 1; form_state.current_position = 1;
} }

View File

@@ -51,7 +51,6 @@ impl FormState {
theme: &Theme, theme: &Theme,
is_edit_mode: bool, is_edit_mode: bool,
highlight_state: &HighlightState, highlight_state: &HighlightState,
// total_count and current_position are now part of self
) { ) {
let fields_str_slice: Vec<&str> = let fields_str_slice: Vec<&str> =
self.fields.iter().map(|s| s.as_str()).collect(); self.fields.iter().map(|s| s.as_str()).collect();
@@ -64,6 +63,7 @@ impl FormState {
&fields_str_slice, &fields_str_slice,
&self.current_field, &self.current_field,
&values_str_slice, &values_str_slice,
&self.table_name,
theme, theme,
is_edit_mode, is_edit_mode,
highlight_state, highlight_state,
@@ -107,33 +107,35 @@ impl FormState {
&mut self, &mut self,
response_data: &HashMap<String, String>, response_data: &HashMap<String, String>,
) { ) {
self.values = self.fields.iter() // Create a new vector for the values, ensuring they are in the correct order.
.map(|field| response_data.get(field).cloned().unwrap_or_default()) self.values = self.fields.iter().map(|field_from_schema| {
.collect(); // For each field from our schema, find the corresponding key in the
// response data by doing a case-insensitive comparison.
response_data
.iter()
.find(|(key_from_data, _)| key_from_data.eq_ignore_ascii_case(field_from_schema))
.map(|(_, value)| value.clone()) // If found, clone its value.
.unwrap_or_default() // If not found, use an empty string.
}).collect();
if let Some(id_str) = response_data.get("id") { // Now, do the same case-insensitive lookup for the 'id' field.
match id_str.parse::<i64>() { let id_str_opt = response_data
Ok(parsed_id) => self.id = parsed_id, .iter()
Err(e) => { .find(|(k, _)| k.eq_ignore_ascii_case("id"))
tracing::error!( .map(|(_, v)| v);
"Failed to parse 'id' field '{}' for table {}.{}: {}",
id_str, if let Some(id_str) = id_str_opt {
self.profile_name, if let Ok(parsed_id) = id_str.parse::<i64>() {
self.table_name, self.id = parsed_id;
e } else {
); tracing::error!( "Failed to parse 'id' field '{}' for table {}.{}", id_str, self.profile_name, self.table_name);
self.id = 0; // Default to 0 if parsing fails self.id = 0;
}
} }
} else { } else {
// If no ID is present, it might be a new record structure or an error
// For now, assume it means the record doesn't have an ID from the server yet
self.id = 0; self.id = 0;
} }
self.has_unsaved_changes = false; self.has_unsaved_changes = false;
// current_field and current_cursor_pos might need resetting or adjusting
// depending on the desired behavior after loading data.
// For now, let's reset current_field to 0.
self.current_field = 0; self.current_field = 0;
self.current_cursor_pos = 0; self.current_cursor_pos = 0;
} }

View File

@@ -1,19 +1,16 @@
// src/tui/functions/form.rs // src/tui/functions/form.rs
use crate::state::pages::canvas_state::CanvasState; // Import the trait
use crate::state::pages::form::FormState; use crate::state::pages::form::FormState;
use crate::services::grpc_client::GrpcClient; use crate::services::grpc_client::GrpcClient;
use crate::state::pages::canvas_state::CanvasState;
use crate::services::ui_service::UiService;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
pub async fn handle_action( pub async fn handle_action(
action: &str, action: &str,
form_state: &mut FormState, form_state: &mut FormState,
grpc_client: &mut GrpcClient, _grpc_client: &mut GrpcClient,
current_position: &mut u64,
total_count: u64,
ideal_cursor_column: &mut usize, ideal_cursor_column: &mut usize,
) -> Result<String> { ) -> Result<String> {
// Check for unsaved changes in both cases // FIX: Call has_unsaved_changes() via the CanvasState trait.
if form_state.has_unsaved_changes() { if form_state.has_unsaved_changes() {
return Ok( return Ok(
"Unsaved changes. Save (Ctrl+S) or Revert (Ctrl+R) before navigating." "Unsaved changes. Save (Ctrl+S) or Revert (Ctrl+R) before navigating."
@@ -21,56 +18,31 @@ pub async fn handle_action(
); );
} }
let total_count = form_state.total_count;
match action { match action {
"previous_entry" => { "previous_entry" => {
let new_position = form_state.current_position.saturating_sub(1); if total_count > 0 {
if new_position >= 1 { if form_state.current_position > 1 {
form_state.current_position = new_position; form_state.current_position -= 1;
*current_position = new_position;
if new_position <= form_state.total_count {
let load_message = UiService::load_table_data_by_position(grpc_client, form_state).await?;
let current_input = form_state.get_current_input();
let max_cursor_pos = if !current_input.is_empty() {
current_input.len() - 1
} else { 0 };
form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos);
Ok(load_message)
} else { } else {
Ok(format!("Moved to position {}", new_position)) form_state.current_position = total_count;
} }
} else { *ideal_cursor_column = 0;
Ok("Already at first position".into())
} }
} }
"next_entry" => { "next_entry" => {
if form_state.current_position <= form_state.total_count { if total_count > 0 {
form_state.current_position += 1; if form_state.current_position < total_count {
*current_position = form_state.current_position; form_state.current_position += 1;
if form_state.current_position <= form_state.total_count {
let load_message = UiService::load_table_data_by_position(grpc_client, form_state).await?;
let current_input = form_state.get_current_input();
let max_cursor_pos = if !current_input.is_empty() {
current_input.len() - 1
} else { 0 };
form_state.current_cursor_pos = (*ideal_cursor_column).min(max_cursor_pos);
Ok(load_message)
} else { } else {
form_state.reset_to_empty(); form_state.current_position = 1;
form_state.current_field = 0;
form_state.current_cursor_pos = 0;
*ideal_cursor_column = 0;
Ok("New form entry mode".into())
} }
} else { *ideal_cursor_column = 0;
Ok("Already at last entry".into())
} }
} }
_ => Err(anyhow!("Unknown form action: {}", action)) _ => return Err(anyhow!("Unknown form action: {}", action)),
} }
Ok(String::new())
} }

View File

@@ -137,6 +137,7 @@ pub fn render_ui(
f, app_state, auth_state, admin_state, main_content_area, theme, f, app_state, auth_state, admin_state, main_content_area, theme,
&app_state.profile_tree, &app_state.selected_profile, &app_state.profile_tree, &app_state.selected_profile,
); );
} else if app_state.ui.show_form { } else if app_state.ui.show_form {
let (sidebar_area, form_actual_area) = calculate_sidebar_layout( let (sidebar_area, form_actual_area) = calculate_sidebar_layout(
app_state.ui.show_sidebar, main_content_area app_state.ui.show_sidebar, main_content_area
@@ -158,12 +159,24 @@ pub fn render_ui(
}; };
let fields_vec: Vec<&str> = form_state.fields.iter().map(AsRef::as_ref).collect(); let fields_vec: Vec<&str> = form_state.fields.iter().map(AsRef::as_ref).collect();
let values_vec: Vec<&String> = form_state.values.iter().collect(); let values_vec: Vec<&String> = form_state.values.iter().collect();
// --- START FIX ---
// Add the missing `&form_state.table_name` argument to this function call.
render_form( render_form(
f, form_render_area, form_state, &fields_vec, &form_state.current_field, f,
&values_vec, theme, is_event_handler_edit_mode, highlight_state, form_render_area,
form_state,
&fields_vec,
&form_state.current_field,
&values_vec,
&form_state.table_name, // <-- THIS ARGUMENT WAS MISSING
theme,
is_event_handler_edit_mode,
highlight_state,
form_state.total_count, form_state.total_count,
form_state.current_position, form_state.current_position,
); );
// --- END FIX ---
} }
if let Some(area) = buffer_list_area { if let Some(area) = buffer_list_area {

View File

@@ -56,7 +56,6 @@ pub async fn get_table_data(
let system_columns = vec![ let system_columns = vec![
("id".to_string(), "BIGINT".to_string()), ("id".to_string(), "BIGINT".to_string()),
("deleted".to_string(), "BOOLEAN".to_string()), ("deleted".to_string(), "BOOLEAN".to_string()),
("firma".to_string(), "TEXT".to_string()),
]; ];
let all_columns: Vec<(String, String)> = system_columns let all_columns: Vec<(String, String)> = system_columns
.into_iter() .into_iter()

View File

@@ -19,19 +19,11 @@ pub async fn post_table_data(
let table_name = request.table_name; let table_name = request.table_name;
let mut data = HashMap::new(); let mut data = HashMap::new();
// Process and validate all data values // CORRECTED: Process and trim all incoming data values.
// We remove the hardcoded validation. We will let the database's
// NOT NULL constraints or Steel validation scripts handle required fields.
for (key, value) in request.data { for (key, value) in request.data {
let trimmed = value.trim().to_string(); data.insert(key, value.trim().to_string());
// Handle specially - it cannot be empty
if trimmed.is_empty() {
return Err(Status::invalid_argument("Firma cannot be empty"));
}
// Add trimmed non-empty values to data map
if !trimmed.is_empty() {
data.insert(key, trimmed);
}
} }
// Lookup profile // Lookup profile