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

View File

@@ -23,8 +23,6 @@ pub async fn handle_read_only_event(
add_table_state: &mut AddTableState,
add_logic_state: &mut AddLogicState,
key_sequence_tracker: &mut KeySequenceTracker,
current_position: &mut u64,
total_count: u64,
grpc_client: &mut GrpcClient,
command_message: &mut String,
edit_mode_cooldown: &mut bool,
@@ -74,12 +72,10 @@ pub async fn handle_read_only_event(
action,
form_state,
grpc_client,
current_position,
total_count,
ideal_cursor_column,
)
.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?
} else if app_state.ui.show_add_table {
add_table_ro::execute_action(
@@ -143,12 +139,10 @@ pub async fn handle_read_only_event(
action,
form_state,
grpc_client,
current_position,
total_count,
ideal_cursor_column,
)
.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?
} else if app_state.ui.show_add_table {
add_table_ro::execute_action(
@@ -177,7 +171,7 @@ pub async fn handle_read_only_event(
key_sequence_tracker,
command_message,
).await?
} else if app_state.ui.show_login { // Handle login general actions
} else if app_state.ui.show_login {
auth_ro::execute_action(
action,
app_state,
@@ -211,8 +205,6 @@ pub async fn handle_read_only_event(
action,
form_state,
grpc_client,
current_position,
total_count,
ideal_cursor_column,
)
.await?
@@ -245,7 +237,7 @@ pub async fn handle_read_only_event(
key_sequence_tracker,
command_message,
).await?
} else if app_state.ui.show_login { // Handle login general actions
} else if app_state.ui.show_login {
auth_ro::execute_action(
action,
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(
app_state, key_event, config, form_state, login_state, register_state,
&mut admin_state.add_table_state, &mut admin_state.add_logic_state,
&mut self.key_sequence_tracker, &mut current_position, total_count,
grpc_client, &mut self.command_message, &mut self.edit_mode_cooldown,
app_state,
key_event,
config,
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,
).await?;
)
.await?;
return Ok(EventOutcome::Ok(message));
}
@@ -365,15 +373,22 @@ impl EventHandler {
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(
app_state, key_event, config, form_state, login_state, register_state,
&mut admin_state.add_table_state, &mut admin_state.add_logic_state,
&mut self.key_sequence_tracker, &mut current_position, total_count,
grpc_client, &mut self.command_message, &mut self.edit_mode_cooldown,
app_state,
key_event,
config,
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,
).await?;
)
.await?;
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_logic_state,
key_sequence_tracker,
current_position,
total_count,
grpc_client,
command_message, // Pass the message buffer
edit_mode_cooldown,

View File

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

View File

@@ -51,7 +51,6 @@ impl FormState {
theme: &Theme,
is_edit_mode: bool,
highlight_state: &HighlightState,
// total_count and current_position are now part of self
) {
let fields_str_slice: Vec<&str> =
self.fields.iter().map(|s| s.as_str()).collect();
@@ -64,6 +63,7 @@ impl FormState {
&fields_str_slice,
&self.current_field,
&values_str_slice,
&self.table_name,
theme,
is_edit_mode,
highlight_state,
@@ -107,33 +107,35 @@ impl FormState {
&mut self,
response_data: &HashMap<String, String>,
) {
self.values = self.fields.iter()
.map(|field| response_data.get(field).cloned().unwrap_or_default())
.collect();
// Create a new vector for the values, ensuring they are in the correct order.
self.values = self.fields.iter().map(|field_from_schema| {
// 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") {
match id_str.parse::<i64>() {
Ok(parsed_id) => self.id = parsed_id,
Err(e) => {
tracing::error!(
"Failed to parse 'id' field '{}' for table {}.{}: {}",
id_str,
self.profile_name,
self.table_name,
e
);
self.id = 0; // Default to 0 if parsing fails
}
}
// Now, do the same case-insensitive lookup for the 'id' field.
let id_str_opt = response_data
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case("id"))
.map(|(_, v)| v);
if let Some(id_str) = id_str_opt {
if let Ok(parsed_id) = id_str.parse::<i64>() {
self.id = parsed_id;
} 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
tracing::error!( "Failed to parse 'id' field '{}' for table {}.{}", id_str, self.profile_name, self.table_name);
self.id = 0;
}
} else {
self.id = 0;
}
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_cursor_pos = 0;
}

View File

@@ -1,19 +1,16 @@
// src/tui/functions/form.rs
use crate::state::pages::canvas_state::CanvasState; // Import the trait
use crate::state::pages::form::FormState;
use crate::services::grpc_client::GrpcClient;
use crate::state::pages::canvas_state::CanvasState;
use crate::services::ui_service::UiService;
use anyhow::{anyhow, Result};
pub async fn handle_action(
action: &str,
form_state: &mut FormState,
grpc_client: &mut GrpcClient,
current_position: &mut u64,
total_count: u64,
_grpc_client: &mut GrpcClient,
ideal_cursor_column: &mut usize,
) -> Result<String> {
// Check for unsaved changes in both cases
// FIX: Call has_unsaved_changes() via the CanvasState trait.
if form_state.has_unsaved_changes() {
return Ok(
"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 {
"previous_entry" => {
let new_position = form_state.current_position.saturating_sub(1);
if new_position >= 1 {
form_state.current_position = new_position;
*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)
if total_count > 0 {
if form_state.current_position > 1 {
form_state.current_position -= 1;
} else {
Ok(format!("Moved to position {}", new_position))
form_state.current_position = total_count;
}
} else {
Ok("Already at first position".into())
*ideal_cursor_column = 0;
}
}
"next_entry" => {
if form_state.current_position <= form_state.total_count {
if total_count > 0 {
if form_state.current_position < total_count {
form_state.current_position += 1;
*current_position = form_state.current_position;
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 {
form_state.reset_to_empty();
form_state.current_field = 0;
form_state.current_cursor_pos = 0;
form_state.current_position = 1;
}
*ideal_cursor_column = 0;
Ok("New form entry mode".into())
}
} else {
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,
&app_state.profile_tree, &app_state.selected_profile,
);
} else if app_state.ui.show_form {
let (sidebar_area, form_actual_area) = calculate_sidebar_layout(
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 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(
f, form_render_area, form_state, &fields_vec, &form_state.current_field,
&values_vec, theme, is_event_handler_edit_mode, highlight_state,
f,
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.current_position,
);
// --- END FIX ---
}
if let Some(area) = buffer_list_area {

View File

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

View File

@@ -19,19 +19,11 @@ pub async fn post_table_data(
let table_name = request.table_name;
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 {
let trimmed = 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);
}
data.insert(key, value.trim().to_string());
}
// Lookup profile