Compare commits
3 Commits
0.5.5
...
9ed558562b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ed558562b | ||
|
|
43f5c1a764 | ||
|
|
46149c09db |
@@ -82,8 +82,6 @@ impl TableDependencyGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ... (NavigationState struct and its new(), activate_*, deactivate(), add_char(), remove_char(), move_*, autocomplete_selected(), get_display_input() methods are unchanged) ...
|
|
||||||
pub struct NavigationState {
|
pub struct NavigationState {
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub input: String,
|
pub input: String,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::pages::admin_panel::add_logic;
|
|||||||
use crate::pages::admin_panel::add_table;
|
use crate::pages::admin_panel::add_table;
|
||||||
use crate::pages::register::suggestions::RoleSuggestionsProvider;
|
use crate::pages::register::suggestions::RoleSuggestionsProvider;
|
||||||
use crate::pages::admin::main::logic::handle_admin_navigation;
|
use crate::pages::admin::main::logic::handle_admin_navigation;
|
||||||
use crate::pages::admin::admin::tui::handle_admin_selection;
|
use crate::pages::admin::admin;
|
||||||
use crate::modes::general::command_navigation::{
|
use crate::modes::general::command_navigation::{
|
||||||
handle_command_navigation_event, NavigationState,
|
handle_command_navigation_event, NavigationState,
|
||||||
};
|
};
|
||||||
@@ -36,6 +36,7 @@ use crate::pages::register::RegisterResult;
|
|||||||
use crate::pages::routing::{Router, Page};
|
use crate::pages::routing::{Router, Page};
|
||||||
use crate::movement::MovementAction;
|
use crate::movement::MovementAction;
|
||||||
use crate::dialog;
|
use crate::dialog;
|
||||||
|
use crate::pages::forms;
|
||||||
use crate::pages::forms::FormState;
|
use crate::pages::forms::FormState;
|
||||||
use crate::pages::forms::logic::{save, revert, SaveOutcome};
|
use crate::pages::forms::logic::{save, revert, SaveOutcome};
|
||||||
use crate::search::state::SearchState;
|
use crate::search::state::SearchState;
|
||||||
@@ -304,83 +305,45 @@ impl EventHandler {
|
|||||||
|
|
||||||
if !overlay_active {
|
if !overlay_active {
|
||||||
if let Page::Login(login_page) = &mut router.current {
|
if let Page::Login(login_page) = &mut router.current {
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
let outcome =
|
||||||
|
login::event::handle_login_event(event, app_state, login_page)?;
|
||||||
// Inside canvas: at the last field, 'j' or Down moves focus to buttons
|
// Only return if the login page actually consumed the key
|
||||||
if !app_state.ui.focus_outside_canvas {
|
if !outcome.get_message_if_ok().is_empty() {
|
||||||
let last_idx = login_page
|
return Ok(outcome);
|
||||||
.editor
|
|
||||||
.data_provider()
|
|
||||||
.field_count()
|
|
||||||
.saturating_sub(1);
|
|
||||||
let at_last = login_page.editor.current_field() >= last_idx;
|
|
||||||
if at_last
|
|
||||||
&& matches!(
|
|
||||||
(key_code, modifiers),
|
|
||||||
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
app_state.ui.focus_outside_canvas = true;
|
|
||||||
app_state.focused_button_index = 0; // focus "Login" button
|
|
||||||
// Ensure canvas mode is ReadOnly when leaving
|
|
||||||
login_page.editor.set_mode(CanvasMode::ReadOnly);
|
|
||||||
return Ok(EventOutcome::Ok("Focus moved to buttons".to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only forward to the canvas while focus is inside it
|
|
||||||
if !app_state.ui.focus_outside_canvas {
|
|
||||||
match login_page.handle_key_event(key_event) {
|
|
||||||
KeyEventOutcome::Consumed(Some(msg)) => {
|
|
||||||
self.command_message = msg;
|
|
||||||
return Ok(EventOutcome::Ok("Login input updated".to_string()));
|
|
||||||
}
|
|
||||||
KeyEventOutcome::Consumed(None) => {
|
|
||||||
return Ok(EventOutcome::Ok("Login input updated".to_string()));
|
|
||||||
}
|
|
||||||
KeyEventOutcome::Pending => {
|
|
||||||
return Ok(EventOutcome::Ok("Waiting for next key...".to_string()));
|
|
||||||
}
|
|
||||||
KeyEventOutcome::NotMatched => {
|
|
||||||
// fall through to other handlers (buttons, etc.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if let Page::Register(register_page) = &mut router.current {
|
} else if let Page::Register(register_page) = &mut router.current {
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
let outcome = crate::pages::register::event::handle_register_event(
|
||||||
|
event,
|
||||||
// Inside canvas: at the last field, 'j' or Down moves focus to buttons
|
app_state,
|
||||||
if !app_state.ui.focus_outside_canvas {
|
register_page,
|
||||||
let last_idx = register_page.editor.data_provider().field_count().saturating_sub(1);
|
)?;
|
||||||
let at_last = register_page.editor.current_field() >= last_idx;
|
// Only stop if page actually consumed the key; else fall through to global handling
|
||||||
if at_last
|
if !outcome.get_message_if_ok().is_empty() {
|
||||||
&& matches!(
|
return Ok(outcome);
|
||||||
(key_code, modifiers),
|
|
||||||
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
app_state.ui.focus_outside_canvas = true;
|
|
||||||
app_state.focused_button_index = 0; // focus "Register" button
|
|
||||||
register_page.editor.set_mode(CanvasMode::ReadOnly);
|
|
||||||
return Ok(EventOutcome::Ok("Focus moved to buttons".to_string()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if let Page::Form(path) = &router.current {
|
||||||
// Only forward to the canvas while focus is inside it
|
let outcome = forms::event::handle_form_event(
|
||||||
if !app_state.ui.focus_outside_canvas {
|
event,
|
||||||
match register_page.handle_key_event(key_event) {
|
app_state,
|
||||||
KeyEventOutcome::Consumed(Some(msg)) => {
|
path,
|
||||||
self.command_message = msg;
|
&mut self.ideal_cursor_column,
|
||||||
return Ok(EventOutcome::Ok("Register input updated".to_string()));
|
)?;
|
||||||
}
|
// Only return if the form page actually consumed the key
|
||||||
KeyEventOutcome::Consumed(None) => {
|
if !outcome.get_message_if_ok().is_empty() {
|
||||||
return Ok(EventOutcome::Ok("Register input updated".to_string()));
|
return Ok(outcome);
|
||||||
}
|
}
|
||||||
KeyEventOutcome::Pending => {
|
} else if let Page::Admin(admin_state) = &mut router.current {
|
||||||
return Ok(EventOutcome::Ok("Waiting for next key...".to_string()));
|
if matches!(auth_state.role, Some(UserRole::Admin)) {
|
||||||
}
|
if let Event::Key(key_event) = event {
|
||||||
KeyEventOutcome::NotMatched => {
|
if admin::event::handle_admin_event(
|
||||||
// fall through
|
key_event,
|
||||||
|
config,
|
||||||
|
app_state,
|
||||||
|
admin_state,
|
||||||
|
buffer_state,
|
||||||
|
&mut self.command_message,
|
||||||
|
)? {
|
||||||
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,19 +462,6 @@ impl EventHandler {
|
|||||||
match &mut router.current {
|
match &mut router.current {
|
||||||
// LOGIN: From buttons (general) back into the canvas with 'k' (Up),
|
// LOGIN: From buttons (general) back into the canvas with 'k' (Up),
|
||||||
// but ONLY from the left-most "Login" button.
|
// but ONLY from the left-most "Login" button.
|
||||||
Page::Login(page) => {
|
|
||||||
if app_state.ui.focus_outside_canvas {
|
|
||||||
if app_state.focused_button_index == 0
|
|
||||||
&& matches!(ma, crate::movement::MovementAction::Up)
|
|
||||||
{
|
|
||||||
app_state.ui.focus_outside_canvas = false;
|
|
||||||
// Enter canvas in ReadOnly mode (never jump straight to Edit)
|
|
||||||
page.editor.set_mode(CanvasMode::ReadOnly);
|
|
||||||
// Optional: keep current field (usually 0 initially)
|
|
||||||
return Ok(EventOutcome::Ok(String::new()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Page::AddTable(state) => {
|
Page::AddTable(state) => {
|
||||||
if state.handle_movement(ma) {
|
if state.handle_movement(ma) {
|
||||||
// Keep UI focus consistent with inputs vs. outer elements
|
// Keep UI focus consistent with inputs vs. outer elements
|
||||||
@@ -526,13 +476,6 @@ impl EventHandler {
|
|||||||
return Ok(EventOutcome::Ok(String::new()));
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Page::Admin(state) => {
|
|
||||||
if matches!(auth_state.role, Some(UserRole::Admin)) {
|
|
||||||
if state.handle_movement(app_state, ma) {
|
|
||||||
return Ok(EventOutcome::Ok(String::new()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Page::Intro(state) => {
|
Page::Intro(state) => {
|
||||||
if state.handle_movement(ma) {
|
if state.handle_movement(ma) {
|
||||||
return Ok(EventOutcome::Ok(String::new()));
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
@@ -543,22 +486,6 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Optional page-specific handlers (non-movement or rich actions)
|
// Optional page-specific handlers (non-movement or rich actions)
|
||||||
if let Page::Admin(admin_state) = &mut router.current {
|
|
||||||
if matches!(auth_state.role, Some(UserRole::Admin)) {
|
|
||||||
// Full admin navigation
|
|
||||||
if handle_admin_navigation(
|
|
||||||
key_event,
|
|
||||||
config,
|
|
||||||
app_state,
|
|
||||||
admin_state,
|
|
||||||
buffer_state,
|
|
||||||
&mut self.command_message,
|
|
||||||
) {
|
|
||||||
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let client_clone = self.grpc_client.clone();
|
let client_clone = self.grpc_client.clone();
|
||||||
let sender_clone = self.save_logic_result_sender.clone();
|
let sender_clone = self.save_logic_result_sender.clone();
|
||||||
if add_logic::nav::handle_add_logic_navigation(
|
if add_logic::nav::handle_add_logic_navigation(
|
||||||
@@ -671,7 +598,7 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
UiContext::Admin => {
|
UiContext::Admin => {
|
||||||
if let Page::Admin(admin_state) = &router.current {
|
if let Page::Admin(admin_state) = &router.current {
|
||||||
handle_admin_selection(
|
admin::tui::handle_admin_selection(
|
||||||
app_state,
|
app_state,
|
||||||
admin_state,
|
admin_state,
|
||||||
);
|
);
|
||||||
@@ -858,25 +785,14 @@ impl EventHandler {
|
|||||||
&mut self.auth_client,
|
&mut self.auth_client,
|
||||||
app_state,
|
app_state,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(EventOutcome::Ok(message))
|
Ok(EventOutcome::Ok(message))
|
||||||
} else {
|
} else {
|
||||||
let save_outcome = if let Page::Form(path) = &router.current {
|
if let Page::Form(path) = &router.current {
|
||||||
save(
|
forms::event::save_form(app_state, path, &mut self.grpc_client).await
|
||||||
app_state,
|
|
||||||
path,
|
|
||||||
&mut self.grpc_client,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
} else {
|
} else {
|
||||||
SaveOutcome::NoChange
|
Ok(EventOutcome::Ok("Nothing to save".to_string()))
|
||||||
};
|
}
|
||||||
let message = match save_outcome {
|
|
||||||
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
|
||||||
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
|
||||||
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
|
||||||
};
|
|
||||||
Ok(EventOutcome::DataSaved(save_outcome, message))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"force_quit" => {
|
"force_quit" => {
|
||||||
@@ -898,19 +814,18 @@ impl EventHandler {
|
|||||||
&mut self.auth_client,
|
&mut self.auth_client,
|
||||||
app_state,
|
app_state,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else if let Page::Form(path) = &router.current {
|
||||||
let save_outcome = if let Page::Form(path) = &router.current {
|
let save_result = forms::event::save_form(app_state, path, &mut self.grpc_client).await?;
|
||||||
save(app_state, path, &mut self.grpc_client).await?
|
match save_result {
|
||||||
} else {
|
EventOutcome::DataSaved(_, msg) => msg,
|
||||||
SaveOutcome::NoChange
|
EventOutcome::Ok(msg) => msg,
|
||||||
};
|
_ => "Saved".to_string(),
|
||||||
match save_outcome {
|
|
||||||
SaveOutcome::NoChange => "No changes to save.".to_string(),
|
|
||||||
SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
|
||||||
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
"No changes to save.".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Page::Form(path) = &router.current {
|
if let Page::Form(path) = &router.current {
|
||||||
if let Some(editor) = app_state.editor_for_path(path) {
|
if let Some(editor) = app_state.editor_for_path(path) {
|
||||||
editor.cleanup_cursor()?;
|
editor.cleanup_cursor()?;
|
||||||
@@ -918,8 +833,8 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
terminal.cleanup()?;
|
terminal.cleanup()?;
|
||||||
Ok(EventOutcome::Exit(format!(
|
Ok(EventOutcome::Exit(format!(
|
||||||
"{}. Exiting application.",
|
"{}. Exiting application.",
|
||||||
message
|
message
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
"revert" => {
|
"revert" => {
|
||||||
@@ -933,7 +848,7 @@ impl EventHandler {
|
|||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
if let Page::Form(path) = &router.current {
|
if let Page::Form(path) = &router.current {
|
||||||
revert(app_state, path, &mut self.grpc_client).await?
|
return forms::event::revert_form(app_state, path, &mut self.grpc_client).await;
|
||||||
} else {
|
} else {
|
||||||
"Nothing to revert".to_string()
|
"Nothing to revert".to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
60
client/src/pages/admin/admin/event.rs
Normal file
60
client/src/pages/admin/admin/event.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// src/pages/admin/admin/event.rs
|
||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::KeyEvent;
|
||||||
|
|
||||||
|
use crate::buffer::state::BufferState;
|
||||||
|
use crate::config::binds::config::Config;
|
||||||
|
use crate::pages::admin::AdminState;
|
||||||
|
use crate::pages::admin::main::logic::handle_admin_navigation;
|
||||||
|
use crate::state::app::state::AppState;
|
||||||
|
|
||||||
|
/// Handle all Admin page-specific key events (movement + actions).
|
||||||
|
/// Returns true if the key was handled (so the caller should stop propagation).
|
||||||
|
pub fn handle_admin_event(
|
||||||
|
key_event: KeyEvent,
|
||||||
|
config: &Config,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
admin_state: &mut AdminState,
|
||||||
|
buffer_state: &mut BufferState,
|
||||||
|
command_message: &mut String,
|
||||||
|
) -> Result<bool> {
|
||||||
|
// 1) Map general action to MovementAction (same mapping used in event.rs)
|
||||||
|
let movement_action = if let Some(act) =
|
||||||
|
config.get_general_action(key_event.code, key_event.modifiers)
|
||||||
|
{
|
||||||
|
use crate::movement::MovementAction;
|
||||||
|
match act {
|
||||||
|
"up" => Some(MovementAction::Up),
|
||||||
|
"down" => Some(MovementAction::Down),
|
||||||
|
"left" => Some(MovementAction::Left),
|
||||||
|
"right" => Some(MovementAction::Right),
|
||||||
|
"next" => Some(MovementAction::Next),
|
||||||
|
"previous" => Some(MovementAction::Previous),
|
||||||
|
"select" => Some(MovementAction::Select),
|
||||||
|
"esc" => Some(MovementAction::Esc),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ma) = movement_action {
|
||||||
|
if admin_state.handle_movement(app_state, ma) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Rich Admin navigation (buttons, selections, etc.)
|
||||||
|
if handle_admin_navigation(
|
||||||
|
key_event,
|
||||||
|
config,
|
||||||
|
app_state,
|
||||||
|
admin_state,
|
||||||
|
buffer_state,
|
||||||
|
command_message,
|
||||||
|
) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
54
client/src/pages/admin/admin/loader.rs
Normal file
54
client/src/pages/admin/admin/loader.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// src/pages/admin/admin/loader.rs
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use crate::pages::admin::{AdminFocus, AdminState};
|
||||||
|
use crate::services::grpc_client::GrpcClient;
|
||||||
|
use crate::state::app::state::AppState;
|
||||||
|
|
||||||
|
/// Refresh admin data and ensure focus and selections are valid.
|
||||||
|
pub async fn refresh_admin_state(
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
admin_state: &mut AdminState,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Fetch latest profile tree
|
||||||
|
let refreshed_tree = grpc_client
|
||||||
|
.get_profile_tree()
|
||||||
|
.await
|
||||||
|
.context("Failed to refresh profile tree for Admin panel")?;
|
||||||
|
app_state.profile_tree = refreshed_tree;
|
||||||
|
|
||||||
|
// Populate profile names for AdminState's list
|
||||||
|
let profile_names = app_state
|
||||||
|
.profile_tree
|
||||||
|
.profiles
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.name.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
admin_state.set_profiles(profile_names);
|
||||||
|
|
||||||
|
// Ensure a sane focus
|
||||||
|
if admin_state.current_focus == AdminFocus::default()
|
||||||
|
|| !matches!(
|
||||||
|
admin_state.current_focus,
|
||||||
|
AdminFocus::InsideProfilesList
|
||||||
|
| AdminFocus::Tables
|
||||||
|
| AdminFocus::InsideTablesList
|
||||||
|
| AdminFocus::Button1
|
||||||
|
| AdminFocus::Button2
|
||||||
|
| AdminFocus::Button3
|
||||||
|
| AdminFocus::ProfilesPane
|
||||||
|
)
|
||||||
|
{
|
||||||
|
admin_state.current_focus = AdminFocus::ProfilesPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure a selection exists when profiles are present
|
||||||
|
if admin_state.profile_list_state.selected().is_none()
|
||||||
|
&& !app_state.profile_tree.profiles.is_empty()
|
||||||
|
{
|
||||||
|
admin_state.profile_list_state.select(Some(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -3,5 +3,7 @@
|
|||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod tui;
|
pub mod tui;
|
||||||
|
pub mod event;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
pub use state::{AdminState, AdminFocus};
|
pub use state::{AdminState, AdminFocus};
|
||||||
|
|||||||
62
client/src/pages/forms/event.rs
Normal file
62
client/src/pages/forms/event.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// src/pages/forms/event.rs
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::Event;
|
||||||
|
use canvas::keymap::KeyEventOutcome;
|
||||||
|
use crate::{
|
||||||
|
state::app::state::AppState,
|
||||||
|
pages::forms::{FormState, logic},
|
||||||
|
modes::handlers::event::EventOutcome,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn handle_form_event(
|
||||||
|
event: Event,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
path: &str,
|
||||||
|
ideal_cursor_column: &mut usize,
|
||||||
|
) -> Result<EventOutcome> {
|
||||||
|
if let Event::Key(key_event) = event {
|
||||||
|
if let Some(editor) = app_state.editor_for_path(path) {
|
||||||
|
match editor.handle_key_event(key_event) {
|
||||||
|
KeyEventOutcome::Consumed(Some(msg)) => {
|
||||||
|
return Ok(EventOutcome::Ok(msg));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Consumed(None) => {
|
||||||
|
return Ok(EventOutcome::Ok("Form input updated".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Pending => {
|
||||||
|
return Ok(EventOutcome::Ok("Waiting for next key...".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::NotMatched => {
|
||||||
|
// fall through to navigation / save / revert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EventOutcome::Ok(String::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save wrapper
|
||||||
|
pub async fn save_form(
|
||||||
|
app_state: &mut AppState,
|
||||||
|
path: &str,
|
||||||
|
grpc_client: &mut crate::services::grpc_client::GrpcClient,
|
||||||
|
) -> Result<EventOutcome> {
|
||||||
|
let outcome = logic::save(app_state, path, grpc_client).await?;
|
||||||
|
let message = match outcome {
|
||||||
|
logic::SaveOutcome::NoChange => "No changes to save.".to_string(),
|
||||||
|
logic::SaveOutcome::UpdatedExisting => "Entry updated.".to_string(),
|
||||||
|
logic::SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
|
||||||
|
};
|
||||||
|
Ok(EventOutcome::DataSaved(outcome, message))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn revert_form(
|
||||||
|
app_state: &mut AppState,
|
||||||
|
path: &str,
|
||||||
|
grpc_client: &mut crate::services::grpc_client::GrpcClient,
|
||||||
|
) -> Result<EventOutcome> {
|
||||||
|
let message = logic::revert(app_state, path, grpc_client).await?;
|
||||||
|
Ok(EventOutcome::Ok(message))
|
||||||
|
}
|
||||||
39
client/src/pages/forms/loader.rs
Normal file
39
client/src/pages/forms/loader.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// src/pages/forms/loader.rs
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use crate::{
|
||||||
|
state::app::state::AppState,
|
||||||
|
services::grpc_client::GrpcClient,
|
||||||
|
services::ui_service::UiService, // ✅ import UiService
|
||||||
|
config::binds::Config,
|
||||||
|
pages::forms::FormState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn ensure_form_loaded_and_count(
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
config: &Config,
|
||||||
|
profile: &str,
|
||||||
|
table: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let path = format!("{}/{}", profile, table);
|
||||||
|
|
||||||
|
app_state.ensure_form_editor(&path, config, || {
|
||||||
|
FormState::new(profile.to_string(), table.to_string(), vec![])
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(form_state) = app_state.form_state_for_path(&path) {
|
||||||
|
UiService::fetch_and_set_table_count(grpc_client, form_state)
|
||||||
|
.await
|
||||||
|
.context("Failed to fetch table count")?;
|
||||||
|
|
||||||
|
if form_state.total_count > 0 {
|
||||||
|
UiService::load_table_data_by_position(grpc_client, form_state)
|
||||||
|
.await
|
||||||
|
.context("Failed to load table data")?;
|
||||||
|
} else {
|
||||||
|
form_state.reset_to_empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -3,7 +3,11 @@
|
|||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
|
pub mod event;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
pub use logic::*;
|
pub use logic::*;
|
||||||
|
pub use event::*;
|
||||||
|
pub use loader::*;
|
||||||
|
|||||||
73
client/src/pages/login/event.rs
Normal file
73
client/src/pages/login/event.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// src/pages/login/event.rs
|
||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::{Event, KeyCode, KeyModifiers};
|
||||||
|
use canvas::{keymap::KeyEventOutcome, AppMode as CanvasMode};
|
||||||
|
use crate::{
|
||||||
|
state::app::state::AppState,
|
||||||
|
pages::login::LoginFormState,
|
||||||
|
modes::handlers::event::EventOutcome,
|
||||||
|
};
|
||||||
|
use canvas::DataProvider;
|
||||||
|
|
||||||
|
/// Handles all Login page-specific events
|
||||||
|
pub fn handle_login_event(
|
||||||
|
event: Event,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
login_page: &mut LoginFormState,
|
||||||
|
) -> Result<EventOutcome> {
|
||||||
|
if let Event::Key(key_event) = event {
|
||||||
|
let key_code = key_event.code;
|
||||||
|
let modifiers = key_event.modifiers;
|
||||||
|
|
||||||
|
// From buttons (outside) back into the canvas (ReadOnly) with Up/k from the left-most button
|
||||||
|
if login_page.focus_outside_canvas
|
||||||
|
&& login_page.focused_button_index == 0
|
||||||
|
&& matches!(key_code, KeyCode::Up | KeyCode::Char('k'))
|
||||||
|
&& modifiers.is_empty()
|
||||||
|
{
|
||||||
|
login_page.focus_outside_canvas = false;
|
||||||
|
app_state.ui.focus_outside_canvas = false; // 🔑 keep global in sync
|
||||||
|
login_page.editor.set_mode(CanvasMode::ReadOnly);
|
||||||
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus handoff: inside canvas → buttons
|
||||||
|
if !login_page.focus_outside_canvas {
|
||||||
|
let last_idx = login_page.editor.data_provider().field_count().saturating_sub(1);
|
||||||
|
let at_last = login_page.editor.current_field() >= last_idx;
|
||||||
|
if at_last
|
||||||
|
&& matches!(
|
||||||
|
(key_code, modifiers),
|
||||||
|
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
login_page.focus_outside_canvas = true;
|
||||||
|
login_page.focused_button_index = 0; // focus "Login" button
|
||||||
|
app_state.ui.focus_outside_canvas = true;
|
||||||
|
app_state.focused_button_index = 0;
|
||||||
|
login_page.editor.set_mode(CanvasMode::ReadOnly);
|
||||||
|
return Ok(EventOutcome::Ok("Focus moved to buttons".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward to canvas if focus is inside
|
||||||
|
if !login_page.focus_outside_canvas {
|
||||||
|
match login_page.handle_key_event(key_event) {
|
||||||
|
KeyEventOutcome::Consumed(Some(msg)) => {
|
||||||
|
return Ok(EventOutcome::Ok(msg));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Consumed(None) => {
|
||||||
|
return Ok(EventOutcome::Ok("Login input updated".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Pending => {
|
||||||
|
return Ok(EventOutcome::Ok("Waiting for next key...".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::NotMatched => {
|
||||||
|
// fall through to button handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(EventOutcome::Ok(String::new()))
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
|
pub mod event;
|
||||||
|
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
pub use ui::render_login;
|
pub use ui::render_login;
|
||||||
pub use logic::*;
|
pub use logic::*;
|
||||||
|
pub use event::*;
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ impl DataProvider for LoginState {
|
|||||||
pub struct LoginFormState {
|
pub struct LoginFormState {
|
||||||
pub state: LoginState,
|
pub state: LoginState,
|
||||||
pub editor: FormEditor<LoginState>,
|
pub editor: FormEditor<LoginState>,
|
||||||
|
pub focus_outside_canvas: bool,
|
||||||
|
pub focused_button_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// manual debug because FormEditor doesnt implement debug
|
// manual debug because FormEditor doesnt implement debug
|
||||||
@@ -150,7 +152,12 @@ impl LoginFormState {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let state = LoginState::default();
|
let state = LoginState::default();
|
||||||
let editor = FormEditor::new(state.clone());
|
let editor = FormEditor::new(state.clone());
|
||||||
Self { state, editor }
|
Self {
|
||||||
|
state,
|
||||||
|
editor,
|
||||||
|
focus_outside_canvas: false,
|
||||||
|
focused_button_index: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Delegates to LoginState fields ===
|
// === Delegates to LoginState fields ===
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub fn render_login(
|
|||||||
|
|
||||||
// Login Button
|
// Login Button
|
||||||
let login_button_index = 0;
|
let login_button_index = 0;
|
||||||
let login_active = if app_state.ui.focus_outside_canvas {
|
let login_active = if login_page.focus_outside_canvas {
|
||||||
app_state.focused_button_index == login_button_index
|
app_state.focused_button_index == login_button_index
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|||||||
76
client/src/pages/register/event.rs
Normal file
76
client/src/pages/register/event.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// src/pages/register/event.rs
|
||||||
|
use anyhow::Result;
|
||||||
|
use crossterm::event::{Event, KeyCode, KeyModifiers};
|
||||||
|
use canvas::{keymap::KeyEventOutcome, AppMode as CanvasMode};
|
||||||
|
use canvas::DataProvider;
|
||||||
|
use crate::{
|
||||||
|
state::app::state::AppState,
|
||||||
|
pages::register::RegisterFormState,
|
||||||
|
modes::handlers::event::EventOutcome,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles all Register 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_register_event(
|
||||||
|
event: Event,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
register_page: &mut RegisterFormState,
|
||||||
|
)-> Result<EventOutcome> {
|
||||||
|
if let Event::Key(key_event) = event {
|
||||||
|
let key_code = key_event.code;
|
||||||
|
let modifiers = key_event.modifiers;
|
||||||
|
|
||||||
|
// From buttons (outside) back into the canvas (ReadOnly) with Up/k from the left-most button
|
||||||
|
if register_page.focus_outside_canvas
|
||||||
|
&& register_page.focused_button_index == 0
|
||||||
|
&& matches!(key_code, KeyCode::Up | KeyCode::Char('k'))
|
||||||
|
&& modifiers.is_empty()
|
||||||
|
{
|
||||||
|
register_page.focus_outside_canvas = false;
|
||||||
|
// Keep global in sync for now (cursor styling elsewhere still reads it)
|
||||||
|
app_state.ui.focus_outside_canvas = false;
|
||||||
|
register_page.editor.set_mode(CanvasMode::ReadOnly);
|
||||||
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus handoff: inside canvas → buttons
|
||||||
|
if !register_page.focus_outside_canvas {
|
||||||
|
let last_idx = register_page.editor.data_provider().field_count().saturating_sub(1);
|
||||||
|
let at_last = register_page.editor.current_field() >= last_idx;
|
||||||
|
if at_last
|
||||||
|
&& matches!(
|
||||||
|
(key_code, modifiers),
|
||||||
|
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
register_page.focus_outside_canvas = true;
|
||||||
|
register_page.focused_button_index = 0; // focus "Register" button
|
||||||
|
// Keep global in sync for now
|
||||||
|
app_state.ui.focus_outside_canvas = true;
|
||||||
|
app_state.focused_button_index = 0;
|
||||||
|
register_page.editor.set_mode(CanvasMode::ReadOnly);
|
||||||
|
return Ok(EventOutcome::Ok("Focus moved to buttons".into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward to canvas if focus is inside
|
||||||
|
if !register_page.focus_outside_canvas {
|
||||||
|
match register_page.handle_key_event(key_event) {
|
||||||
|
KeyEventOutcome::Consumed(Some(msg)) => {
|
||||||
|
return Ok(EventOutcome::Ok(msg));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Consumed(None) => {
|
||||||
|
return Ok(EventOutcome::Ok("Register input updated".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::Pending => {
|
||||||
|
return Ok(EventOutcome::Ok("Waiting for next key...".into()));
|
||||||
|
}
|
||||||
|
KeyEventOutcome::NotMatched => {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(EventOutcome::Ok(String::new()))
|
||||||
|
}
|
||||||
@@ -54,6 +54,8 @@ pub async fn back_to_login(
|
|||||||
buffer_state.update_history(AppView::Login);
|
buffer_state.update_history(AppView::Login);
|
||||||
|
|
||||||
// Reset focus state
|
// Reset focus state
|
||||||
|
register_state.focus_outside_canvas = false;
|
||||||
|
register_state.focused_button_index = 0;
|
||||||
app_state.ui.focus_outside_canvas = false;
|
app_state.ui.focus_outside_canvas = false;
|
||||||
app_state.focused_button_index = 0;
|
app_state.focused_button_index = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ pub mod ui;
|
|||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
pub mod suggestions;
|
pub mod suggestions;
|
||||||
|
pub mod event;
|
||||||
|
|
||||||
// pub use state::*;
|
// pub use state::*;
|
||||||
pub use ui::render_register;
|
pub use ui::render_register;
|
||||||
pub use logic::*;
|
pub use logic::*;
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
|
pub use event::*;
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ impl DataProvider for RegisterState {
|
|||||||
pub struct RegisterFormState {
|
pub struct RegisterFormState {
|
||||||
pub state: RegisterState,
|
pub state: RegisterState,
|
||||||
pub editor: FormEditor<RegisterState>,
|
pub editor: FormEditor<RegisterState>,
|
||||||
|
pub focus_outside_canvas: bool,
|
||||||
|
pub focused_button_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RegisterFormState {
|
impl Default for RegisterFormState {
|
||||||
@@ -174,7 +176,12 @@ impl RegisterFormState {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let state = RegisterState::default();
|
let state = RegisterState::default();
|
||||||
let editor = FormEditor::new(state.clone());
|
let editor = FormEditor::new(state.clone());
|
||||||
Self { state, editor }
|
Self {
|
||||||
|
state,
|
||||||
|
editor,
|
||||||
|
focus_outside_canvas: false,
|
||||||
|
focused_button_index: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Delegates to RegisterState ===
|
// === Delegates to RegisterState ===
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ pub fn render_register(
|
|||||||
|
|
||||||
// Register Button
|
// Register Button
|
||||||
let register_button_index = 0;
|
let register_button_index = 0;
|
||||||
let register_active = app_state.ui.focus_outside_canvas
|
let register_active =
|
||||||
&& app_state.focused_button_index == register_button_index;
|
register_page.focus_outside_canvas
|
||||||
|
&& register_page.focused_button_index == register_button_index;
|
||||||
let mut register_style = Style::default().fg(theme.fg);
|
let mut register_style = Style::default().fg(theme.fg);
|
||||||
let mut register_border = Style::default().fg(theme.border);
|
let mut register_border = Style::default().fg(theme.border);
|
||||||
if register_active {
|
if register_active {
|
||||||
@@ -104,8 +105,9 @@ pub fn render_register(
|
|||||||
|
|
||||||
// Return Button
|
// Return Button
|
||||||
let return_button_index = 1;
|
let return_button_index = 1;
|
||||||
let return_active = app_state.ui.focus_outside_canvas
|
let return_active =
|
||||||
&& app_state.focused_button_index == return_button_index;
|
register_page.focus_outside_canvas
|
||||||
|
&& register_page.focused_button_index == return_button_index;
|
||||||
let mut return_style = Style::default().fg(theme.fg);
|
let mut return_style = Style::default().fg(theme.fg);
|
||||||
let mut return_border = Style::default().fg(theme.border);
|
let mut return_border = Style::default().fg(theme.border);
|
||||||
if return_active {
|
if return_active {
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ use crate::pages::login::LoginFormState;
|
|||||||
use crate::pages::register::RegisterFormState;
|
use crate::pages::register::RegisterFormState;
|
||||||
use crate::pages::admin::AdminState;
|
use crate::pages::admin::AdminState;
|
||||||
use crate::pages::admin::AdminFocus;
|
use crate::pages::admin::AdminFocus;
|
||||||
|
use crate::pages::admin::admin;
|
||||||
use crate::pages::intro::IntroState;
|
use crate::pages::intro::IntroState;
|
||||||
use crate::pages::forms::{FormState, FieldDefinition};
|
use crate::pages::forms::{FormState, FieldDefinition};
|
||||||
|
use crate::pages::forms;
|
||||||
use crate::pages::routing::{Router, Page};
|
use crate::pages::routing::{Router, Page};
|
||||||
use crate::buffer::state::BufferState;
|
use crate::buffer::state::BufferState;
|
||||||
use crate::buffer::state::AppView;
|
use crate::buffer::state::AppView;
|
||||||
@@ -412,33 +414,11 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
admin_state = current.clone();
|
admin_state = current.clone();
|
||||||
}
|
}
|
||||||
info!("Auth role at render: {:?}", auth_state.role);
|
info!("Auth role at render: {:?}", auth_state.role);
|
||||||
match grpc_client.get_profile_tree().await {
|
|
||||||
Ok(refreshed_tree) => {
|
|
||||||
app_state.profile_tree = refreshed_tree;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to refresh profile tree for Admin panel: {}", e);
|
|
||||||
event_handler.command_message =
|
|
||||||
format!("Error refreshing admin data: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let profile_names = app_state.profile_tree.profiles.iter()
|
|
||||||
.map(|p| p.name.clone())
|
|
||||||
.collect();
|
|
||||||
admin_state.set_profiles(profile_names);
|
|
||||||
|
|
||||||
if admin_state.current_focus == AdminFocus::default()
|
// Use the admin loader instead of inline logic
|
||||||
|| !matches!(admin_state.current_focus,
|
if let Err(e) = admin::loader::refresh_admin_state(&mut grpc_client, &mut app_state, &mut admin_state).await {
|
||||||
AdminFocus::InsideProfilesList |
|
error!("Failed to refresh admin state: {}", e);
|
||||||
AdminFocus::Tables | AdminFocus::InsideTablesList |
|
event_handler.command_message = format!("Error refreshing admin data: {}", e);
|
||||||
AdminFocus::Button1 | AdminFocus::Button2 | AdminFocus::Button3)
|
|
||||||
{
|
|
||||||
admin_state.current_focus = AdminFocus::ProfilesPane;
|
|
||||||
}
|
|
||||||
if admin_state.profile_list_state.selected().is_none()
|
|
||||||
&& !app_state.profile_tree.profiles.is_empty()
|
|
||||||
{
|
|
||||||
admin_state.profile_list_state.select(Some(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
router.navigate(Page::Admin(admin_state.clone()));
|
router.navigate(Page::Admin(admin_state.clone()));
|
||||||
@@ -459,7 +439,7 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Page::Form(current_path) = &router.current {
|
if let Page::Form(_current_path) = &router.current {
|
||||||
let current_view_profile = app_state.current_view_profile_name.clone();
|
let current_view_profile = app_state.current_view_profile_name.clone();
|
||||||
let current_view_table = app_state.current_view_table_name.clone();
|
let current_view_table = app_state.current_view_table_name.clone();
|
||||||
|
|
||||||
@@ -475,52 +455,16 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
);
|
);
|
||||||
needs_redraw = true;
|
needs_redraw = true;
|
||||||
|
|
||||||
match UiService::load_table_view(
|
// DELEGATE to the forms loader
|
||||||
|
match forms::loader::ensure_form_loaded_and_count(
|
||||||
&mut grpc_client,
|
&mut grpc_client,
|
||||||
&mut app_state,
|
&mut app_state,
|
||||||
|
&config,
|
||||||
prof_name,
|
prof_name,
|
||||||
tbl_name,
|
tbl_name,
|
||||||
)
|
).await {
|
||||||
.await
|
Ok(()) => {
|
||||||
{
|
app_state.hide_dialog();
|
||||||
Ok(new_form_state) => {
|
|
||||||
// Set the new form state and fetch count
|
|
||||||
let path = format!("{}/{}", prof_name, tbl_name);
|
|
||||||
app_state.ensure_form_editor(&path, &config, || new_form_state);
|
|
||||||
|
|
||||||
if let Some(form_state) = app_state.form_state_for_path(&path) {
|
|
||||||
if let Err(e) = UiService::fetch_and_set_table_count(
|
|
||||||
&mut grpc_client,
|
|
||||||
form_state,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
app_state.update_dialog_content(
|
|
||||||
&format!("Error fetching count: {}", e),
|
|
||||||
vec!["OK".to_string()],
|
|
||||||
DialogPurpose::LoginFailed,
|
|
||||||
);
|
|
||||||
} else if form_state.total_count > 0 {
|
|
||||||
if let Err(e) = UiService::load_table_data_by_position(
|
|
||||||
&mut grpc_client,
|
|
||||||
form_state,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
app_state.update_dialog_content(
|
|
||||||
&format!("Error loading data: {}", e),
|
|
||||||
vec!["OK".to_string()],
|
|
||||||
DialogPurpose::LoginFailed,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
app_state.hide_dialog();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
form_state.reset_to_empty();
|
|
||||||
app_state.hide_dialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_view_profile_name = current_view_profile;
|
prev_view_profile_name = current_view_profile;
|
||||||
prev_view_table_name = current_view_table;
|
prev_view_table_name = current_view_table;
|
||||||
table_just_switched = true;
|
table_just_switched = true;
|
||||||
@@ -531,10 +475,9 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
vec!["OK".to_string()],
|
vec!["OK".to_string()],
|
||||||
DialogPurpose::LoginFailed,
|
DialogPurpose::LoginFailed,
|
||||||
);
|
);
|
||||||
app_state.current_view_profile_name =
|
// Reset to previous state on error
|
||||||
prev_view_profile_name.clone();
|
app_state.current_view_profile_name = prev_view_profile_name.clone();
|
||||||
app_state.current_view_table_name =
|
app_state.current_view_table_name = prev_view_table_name.clone();
|
||||||
prev_view_table_name.clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user