adding to have multiple forms pages

This commit is contained in:
filipriec
2025-08-29 16:18:42 +02:00
parent 75da9c0f4b
commit 58f109ca91
7 changed files with 62 additions and 58 deletions

View File

@@ -7,7 +7,7 @@ pub fn get_view_layer(view: &AppView) -> u8 {
match view { match view {
AppView::Intro => 1, AppView::Intro => 1,
AppView::Login | AppView::Register | AppView::Admin | AppView::AddTable | AppView::AddLogic => 2, AppView::Login | AppView::Register | AppView::Admin | AppView::AddTable | AppView::AddLogic => 2,
AppView::Form | AppView::Scratch => 3, AppView::Form(_) | AppView::Scratch => 3,
} }
} }

View File

@@ -8,7 +8,7 @@ pub enum AppView {
Admin, Admin,
AddTable, AddTable,
AddLogic, AddLogic,
Form, Form(String),
Scratch, Scratch,
} }
@@ -23,7 +23,7 @@ impl AppView {
AppView::Admin => "Admin_Panel", AppView::Admin => "Admin_Panel",
AppView::AddTable => "Add_Table", AppView::AddTable => "Add_Table",
AppView::AddLogic => "Add_Logic", AppView::AddLogic => "Add_Logic",
AppView::Form => "Form", AppView::Form(_) => "Form",
AppView::Scratch => "*scratch*", AppView::Scratch => "*scratch*",
} }
} }
@@ -31,7 +31,7 @@ impl AppView {
/// Returns the display name with dynamic context (for Form buffers) /// Returns the display name with dynamic context (for Form buffers)
pub fn display_name_with_context(&self, current_table_name: Option<&str>) -> String { pub fn display_name_with_context(&self, current_table_name: Option<&str>) -> String {
match self { match self {
AppView::Form => { AppView::Form(_) => {
current_table_name current_table_name
.unwrap_or("Data Form") .unwrap_or("Data Form")
.to_string() .to_string()

View File

@@ -17,7 +17,7 @@ pub async fn save(
app_state: &mut AppState, app_state: &mut AppState,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
) -> Result<SaveOutcome> { ) -> Result<SaveOutcome> {
if let Some(fs) = app_state.form_state_mut() { if let Some(fs) = app_state.active_form_state_mut(buffer_state) {
if !fs.has_unsaved_changes { if !fs.has_unsaved_changes {
return Ok(SaveOutcome::NoChange); return Ok(SaveOutcome::NoChange);
} }
@@ -62,7 +62,7 @@ pub async fn save(
.context("Failed to post new table data")?; .context("Failed to post new table data")?;
if response.success { if response.success {
if let Some(fs) = app_state.form_state_mut() { if let Some(fs) = app_state.active_form_state_mut(buffer_state) {
fs.id = response.inserted_id; fs.id = response.inserted_id;
fs.total_count += 1; fs.total_count += 1;
fs.current_position = fs.total_count; fs.current_position = fs.total_count;
@@ -84,7 +84,7 @@ pub async fn save(
.context("Failed to put (update) table data")?; .context("Failed to put (update) table data")?;
if response.success { if response.success {
if let Some(fs) = app_state.form_state_mut() { if let Some(fs) = app_state.active_form_state_mut(buffer_state) {
fs.has_unsaved_changes = false; fs.has_unsaved_changes = false;
} }
SaveOutcome::UpdatedExisting SaveOutcome::UpdatedExisting
@@ -103,7 +103,7 @@ pub async fn revert(
app_state: &mut AppState, app_state: &mut AppState,
grpc_client: &mut GrpcClient, grpc_client: &mut GrpcClient,
) -> Result<String> { ) -> Result<String> {
if let Some(fs) = app_state.form_state_mut() { if let Some(fs) = app_state.active_form_state_mut(buffer_state) {
if fs.id == 0 if fs.id == 0
|| (fs.total_count > 0 && fs.current_position > fs.total_count) || (fs.total_count > 0 && fs.current_position > fs.total_count)
|| (fs.total_count == 0 && fs.current_position == 1) || (fs.total_count == 0 && fs.current_position == 1)

View File

@@ -61,18 +61,18 @@ pub fn render_form_page(
f.render_widget(count_para, main_layout[0]); f.render_widget(count_para, main_layout[0]);
// --- FORM RENDERING (Using persistent FormEditor) --- // --- FORM RENDERING (Using persistent FormEditor) ---
if let Some(editor) = &app_state.form_editor { if let Some(AppView::Form(path)) = buffer_state.get_active_view() {
let active_field_rect = render_canvas(f, main_layout[1], editor, theme); if let Some(editor) = app_state.form_editor.get(path) {
let active_field_rect = render_canvas(f, main_layout[1], editor, theme);
// --- SUGGESTIONS DROPDOWN --- if let Some(active_rect) = active_field_rect {
if let Some(active_rect) = active_field_rect { render_suggestions_dropdown(
render_suggestions_dropdown( f,
f, main_layout[1],
main_layout[1], active_rect,
active_rect, &DefaultCanvasTheme,
&DefaultCanvasTheme, editor,
editor, );
); }
} }
} }
} }

View File

@@ -18,7 +18,7 @@ pub enum Page {
Admin(AdminState), Admin(AdminState),
AddLogic(AddLogicState), AddLogic(AddLogicState),
AddTable(AddTableState), AddTable(AddTableState),
Form(FormState), Form(String),
} }
pub struct Router { pub struct Router {

View File

@@ -60,7 +60,7 @@ pub struct AppState {
// UI preferences // UI preferences
pub ui: UiState, pub ui: UiState,
pub form_editor: Option<FormEditor<FormState>>, pub form_editor: HashMap<String, FormEditor<FormState>>, // key = "profile/table"
#[cfg(feature = "ui-debug")] #[cfg(feature = "ui-debug")]
pub debug_state: Option<DebugState>, pub debug_state: Option<DebugState>,
@@ -81,7 +81,7 @@ impl AppState {
pending_table_structure_fetch: None, pending_table_structure_fetch: None,
search_state: None, search_state: None,
ui: UiState::default(), ui: UiState::default(),
form_editor: None, form_editor: HashMap::new(),
#[cfg(feature = "ui-debug")] #[cfg(feature = "ui-debug")]
debug_state: None, debug_state: None,
@@ -99,32 +99,28 @@ impl AppState {
self.current_view_table_name = Some(table_name); self.current_view_table_name = Some(table_name);
} }
pub fn init_form_editor(&mut self, form_state: FormState, config: &Config) {
let mut editor = FormEditor::new(form_state);
editor.set_keymap(config.build_canvas_keymap()); // inject keymap
self.form_editor = Some(editor);
}
/// Replace the current form state and wrap it in a FormEditor with keymap
pub fn set_form_state(&mut self, form_state: FormState, config: &Config) {
let mut editor = FormEditor::new(form_state);
editor.set_keymap(config.build_canvas_keymap());
self.form_editor = Some(editor);
}
/// Immutable access to the underlying FormState
pub fn form_state(&self) -> Option<&FormState> {
self.form_editor.as_ref().map(|e| e.data_provider())
}
/// Mutable access to the underlying FormState
pub fn form_state_mut(&mut self) -> Option<&mut FormState> {
self.form_editor.as_mut().map(|e| e.data_provider_mut())
}
pub fn is_canvas_edit_mode(&self) -> bool { pub fn is_canvas_edit_mode(&self) -> bool {
matches!(self.form_editor.as_ref().map(|e| e.mode()), Some(canvas::AppMode::Edit)) matches!(self.form_editor.as_ref().map(|e| e.mode()), Some(canvas::AppMode::Edit))
} }
pub fn editor_for_path(&mut self, path: &str) -> Option<&mut FormEditor<FormState>> {
self.form_editor.get_mut(path)
}
pub fn form_state_for_path(&mut self, path: &str) -> Option<&mut FormState> {
self.form_editor.get_mut(path).map(|e| e.data_provider_mut())
}
pub fn ensure_form_editor<F>(&mut self, path: &str, config: &Config, loader: F)
where
F: FnOnce() -> FormState,
{
if !self.form_editor.contains_key(path) {
let mut editor = FormEditor::new(loader());
editor.set_keymap(config.build_canvas_keymap());
self.form_editor.insert(path.to_string(), editor);
}
}
} }
impl Default for UiState { impl Default for UiState {

View File

@@ -113,13 +113,15 @@ pub async fn run_ui() -> Result<()> {
.collect(); .collect();
// Replace local form_state with app_state.form_editor // Replace local form_state with app_state.form_editor
app_state.set_form_state( let path = format!("{}/{}", initial_profile, initial_table);
FormState::new(initial_profile.clone(), initial_table.clone(), initial_field_defs), app_state.ensure_form_editor(&path, &config, || {
&config, FormState::new(initial_profile.clone(), initial_table.clone(), initial_field_defs)
); });
buffer_state.update_history(AppView::Form(path.clone()));
router.navigate(Page::Form(path));
// Fetch initial count using app_state accessor // Fetch initial count using app_state accessor
if let Some(form_state) = app_state.form_state_mut() { if let Some(form_state) = app_state.active_form_state_mut(&buffer_state) {
UiService::fetch_and_set_table_count(&mut grpc_client, form_state) UiService::fetch_and_set_table_count(&mut grpc_client, form_state)
.await .await
.context(format!( .context(format!(
@@ -137,7 +139,9 @@ pub async fn run_ui() -> Result<()> {
} }
if auto_logged_in { if auto_logged_in {
buffer_state.history = vec![AppView::Form]; let path = format!("{}/{}", initial_profile, initial_table);
buffer_state.history = vec![AppView::Form(path.clone())];
router.navigate(Page::Form(path));
buffer_state.active_index = 0; buffer_state.active_index = 0;
info!("Initial view set to Form due to auto-login."); info!("Initial view set to Form due to auto-login.");
} }
@@ -150,7 +154,7 @@ pub async fn run_ui() -> Result<()> {
let mut table_just_switched = false; let mut table_just_switched = false;
loop { loop {
let position_before_event = app_state.form_state() let position_before_event = app_state.active_form_state_mut(&buffer_state)
.map(|fs| fs.current_position) .map(|fs| fs.current_position)
.unwrap_or(1); .unwrap_or(1);
let mut event_processed = false; let mut event_processed = false;
@@ -216,7 +220,7 @@ pub async fn run_ui() -> Result<()> {
if !overlay_active { if !overlay_active {
if let Page::Form(_) = &router.current { if let Page::Form(_) = &router.current {
if !app_state.ui.focus_outside_canvas { if !app_state.ui.focus_outside_canvas {
if let Some(editor) = app_state.form_editor.as_mut() { if let Some(editor) = app_state.active_form_editor_mut(&buffer_state) {
match editor.handle_key_event(*key_event) { match editor.handle_key_event(*key_event) {
KeyEventOutcome::Consumed(Some(msg)) => { KeyEventOutcome::Consumed(Some(msg)) => {
event_handler.command_message = msg; event_handler.command_message = msg;
@@ -725,11 +729,14 @@ pub async fn run_ui() -> Result<()> {
// Workaround for borrow checker // Workaround for borrow checker
let current_dir = app_state.current_dir.clone(); let current_dir = app_state.current_dir.clone();
let form_state_clone = app_state.form_state().unwrap().clone(); let form_state_clone = if let Page::Form(path) = &router.current {
app_state.form_state_for_path(path).cloned()
} else {
None
};
terminal if let Some(form_state_clone) = form_state_clone {
.draw(|f| { terminal.draw(|f| {
let mut temp_form_state = form_state_clone.clone();
render_ui( render_ui(
f, f,
&mut router, &mut router,
@@ -746,6 +753,7 @@ pub async fn run_ui() -> Result<()> {
}) })
.context("Terminal draw call failed")?; .context("Terminal draw call failed")?;
needs_redraw = false; needs_redraw = false;
}
} }
let now = Instant::now(); let now = Instant::now();