Files
komp_ac/client/src/pages/admin_panel/add_table/event.rs

286 lines
13 KiB
Rust

// src/pages/admin_panel/add_table/event.rs
use anyhow::Result;
use crate::config::binds::config::Config;
use crate::movement::{move_focus, MovementAction};
use crate::pages::admin_panel::add_table::logic::{
handle_add_column_action, handle_delete_selected_columns, handle_save_table_action,
};
use crate::pages::admin_panel::add_table::nav::SaveTableResultSender;
use crate::pages::admin_panel::add_table::state::{AddTableFocus, AddTableFormState};
use crate::services::grpc_client::GrpcClient;
use crate::state::app::state::AppState;
use crate::modes::handlers::event::EventOutcome;
use canvas::{AppMode as CanvasMode, DataProvider};
use crossterm::event::KeyEvent;
/// Focus traversal order for AddTable (outside canvas)
const ADD_TABLE_FOCUS_ORDER: [AddTableFocus; 10] = [
AddTableFocus::InputTableName,
AddTableFocus::InputColumnName,
AddTableFocus::InputColumnType,
AddTableFocus::AddColumnButton,
AddTableFocus::ColumnsTable,
AddTableFocus::IndexesTable,
AddTableFocus::LinksTable,
AddTableFocus::SaveButton,
AddTableFocus::DeleteSelectedButton,
AddTableFocus::CancelButton,
];
/// Handles all AddTable page-specific events.
/// Return a non-empty Ok(message) only when the page actually consumed the key,
/// otherwise return Ok("") to let global handling proceed.
pub fn handle_add_table_event(
key_event: KeyEvent,
movement: Option<MovementAction>,
config: &Config,
app_state: &mut AppState,
page: &mut AddTableFormState,
mut grpc_client: GrpcClient,
save_result_sender: SaveTableResultSender,
) -> Result<EventOutcome> {
// 1) Inside canvas (FormEditor)
let inside_canvas_inputs = matches!(
page.current_focus(),
AddTableFocus::InputTableName
| AddTableFocus::InputColumnName
| AddTableFocus::InputColumnType
);
if inside_canvas_inputs {
// Disable global shortcuts while typing
app_state.ui.focus_outside_canvas = false;
// Only allow leaving the canvas with Down/Next when in ReadOnly mode
let in_edit_mode = page.editor.mode() == CanvasMode::Edit;
if !in_edit_mode {
if let Some(ma) = movement {
let last_idx = page.editor.data_provider().field_count().saturating_sub(1);
let at_last = page.editor.current_field() >= last_idx;
if at_last && matches!(ma, MovementAction::Down | MovementAction::Next) {
page.state.last_canvas_field = last_idx;
page.set_current_focus(AddTableFocus::AddColumnButton);
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok("Moved to Add button".to_string()));
}
}
}
// Let the FormEditor handle typing
match page.editor.handle_key_event(key_event) {
canvas::keymap::KeyEventOutcome::Consumed(Some(msg)) => {
page.sync_from_editor();
return Ok(EventOutcome::Ok(msg));
}
canvas::keymap::KeyEventOutcome::Consumed(None) => {
page.sync_from_editor();
return Ok(EventOutcome::Ok("Input updated".into()));
}
canvas::keymap::KeyEventOutcome::Pending => {
return Ok(EventOutcome::Ok(String::new()));
}
canvas::keymap::KeyEventOutcome::NotMatched => {
// fall through
}
}
}
// 2) Outside canvas
if let Some(ma) = movement {
// Block outer moves when "inside" any table and handle locally
match page.current_focus() {
AddTableFocus::InsideColumnsTable => {
match ma {
MovementAction::Up => {
if let Some(i) = page.state.column_table_state.selected() {
let next = i.saturating_sub(1);
page.state.column_table_state.select(Some(next));
} else if !page.state.columns.is_empty() {
page.state.column_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Down => {
if let Some(i) = page.state.column_table_state.selected() {
let last = page.state.columns.len().saturating_sub(1);
let next = if i < last { i + 1 } else { i };
page.state.column_table_state.select(Some(next));
} else if !page.state.columns.is_empty() {
page.state.column_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Select => {
if let Some(i) = page.state.column_table_state.selected() {
if let Some(col) = page.state.columns.get_mut(i) {
col.selected = !col.selected;
page.state.has_unsaved_changes = true;
}
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Esc => {
page.state.column_table_state.select(None);
page.set_current_focus(AddTableFocus::ColumnsTable);
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Next | MovementAction::Previous => {
// Block outer movement while inside
return Ok(EventOutcome::Ok(String::new()));
}
_ => {}
}
}
AddTableFocus::InsideIndexesTable => {
match ma {
MovementAction::Up => {
if let Some(i) = page.state.index_table_state.selected() {
let next = i.saturating_sub(1);
page.state.index_table_state.select(Some(next));
} else if !page.state.indexes.is_empty() {
page.state.index_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Down => {
if let Some(i) = page.state.index_table_state.selected() {
let last = page.state.indexes.len().saturating_sub(1);
let next = if i < last { i + 1 } else { i };
page.state.index_table_state.select(Some(next));
} else if !page.state.indexes.is_empty() {
page.state.index_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Select => {
if let Some(i) = page.state.index_table_state.selected() {
if let Some(ix) = page.state.indexes.get_mut(i) {
ix.selected = !ix.selected;
page.state.has_unsaved_changes = true;
}
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Esc => {
page.state.index_table_state.select(None);
page.set_current_focus(AddTableFocus::IndexesTable);
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Next | MovementAction::Previous => {
return Ok(EventOutcome::Ok(String::new()));
}
_ => {}
}
}
AddTableFocus::InsideLinksTable => {
match ma {
MovementAction::Up => {
if let Some(i) = page.state.link_table_state.selected() {
let next = i.saturating_sub(1);
page.state.link_table_state.select(Some(next));
} else if !page.state.links.is_empty() {
page.state.link_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Down => {
if let Some(i) = page.state.link_table_state.selected() {
let last = page.state.links.len().saturating_sub(1);
let next = if i < last { i + 1 } else { i };
page.state.link_table_state.select(Some(next));
} else if !page.state.links.is_empty() {
page.state.link_table_state.select(Some(0));
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Select => {
if let Some(i) = page.state.link_table_state.selected() {
if let Some(link) = page.state.links.get_mut(i) {
link.selected = !link.selected;
page.state.has_unsaved_changes = true;
}
}
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Esc => {
page.state.link_table_state.select(None);
page.set_current_focus(AddTableFocus::LinksTable);
app_state.ui.focus_outside_canvas = true;
return Ok(EventOutcome::Ok(String::new()));
}
MovementAction::Next | MovementAction::Previous => {
return Ok(EventOutcome::Ok(String::new()));
}
_ => {}
}
}
_ => {}
}
let mut current = page.current_focus();
if move_focus(&ADD_TABLE_FOCUS_ORDER, &mut current, ma) {
page.set_current_focus(current);
app_state.ui.focus_outside_canvas = !matches!(
page.current_focus(),
AddTableFocus::InputTableName
| AddTableFocus::InputColumnName
| AddTableFocus::InputColumnType
);
return Ok(EventOutcome::Ok(String::new()));
}
// 3) Rich actions
match ma {
MovementAction::Select => match page.current_focus() {
AddTableFocus::AddColumnButton => {
if let Some(focus_after_add) =
handle_add_column_action(&mut page.state, &mut String::new())
{
page.set_current_focus(focus_after_add);
return Ok(EventOutcome::Ok("Column added".into()));
}
}
AddTableFocus::SaveButton => {
if page.state.table_name.is_empty() {
return Ok(EventOutcome::Ok("Cannot save: Table name is empty".into()));
}
if page.state.columns.is_empty() {
return Ok(EventOutcome::Ok("Cannot save: No columns defined".into()));
}
app_state.show_loading_dialog("Saving", "Please wait...");
let state_clone = page.state.clone();
let sender_clone = save_result_sender.clone();
tokio::spawn(async move {
let result = handle_save_table_action(&mut grpc_client, &state_clone).await;
let _ = sender_clone.send(result).await;
});
return Ok(EventOutcome::Ok("Saving table...".into()));
}
AddTableFocus::DeleteSelectedButton => {
let msg = handle_delete_selected_columns(&mut page.state);
return Ok(EventOutcome::Ok(msg));
}
AddTableFocus::CancelButton => {
return Ok(EventOutcome::Ok("Cancelled Add Table".to_string()));
}
_ => {}
},
_ => {}
}
}
Ok(EventOutcome::Ok(String::new()))
}