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

View File

@@ -17,7 +17,7 @@ pub async fn save(
app_state: &mut AppState,
grpc_client: &mut GrpcClient,
) -> 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 {
return Ok(SaveOutcome::NoChange);
}
@@ -62,7 +62,7 @@ pub async fn save(
.context("Failed to post new table data")?;
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.total_count += 1;
fs.current_position = fs.total_count;
@@ -84,7 +84,7 @@ pub async fn save(
.context("Failed to put (update) table data")?;
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;
}
SaveOutcome::UpdatedExisting
@@ -103,7 +103,7 @@ pub async fn revert(
app_state: &mut AppState,
grpc_client: &mut GrpcClient,
) -> 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
|| (fs.total_count > 0 && fs.current_position > fs.total_count)
|| (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]);
// --- FORM RENDERING (Using persistent FormEditor) ---
if let Some(editor) = &app_state.form_editor {
let active_field_rect = render_canvas(f, main_layout[1], editor, theme);
// --- SUGGESTIONS DROPDOWN ---
if let Some(active_rect) = active_field_rect {
render_suggestions_dropdown(
f,
main_layout[1],
active_rect,
&DefaultCanvasTheme,
editor,
);
if let Some(AppView::Form(path)) = buffer_state.get_active_view() {
if let Some(editor) = app_state.form_editor.get(path) {
let active_field_rect = render_canvas(f, main_layout[1], editor, theme);
if let Some(active_rect) = active_field_rect {
render_suggestions_dropdown(
f,
main_layout[1],
active_rect,
&DefaultCanvasTheme,
editor,
);
}
}
}
}

View File

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

View File

@@ -60,7 +60,7 @@ pub struct AppState {
// UI preferences
pub ui: UiState,
pub form_editor: Option<FormEditor<FormState>>,
pub form_editor: HashMap<String, FormEditor<FormState>>, // key = "profile/table"
#[cfg(feature = "ui-debug")]
pub debug_state: Option<DebugState>,
@@ -81,7 +81,7 @@ impl AppState {
pending_table_structure_fetch: None,
search_state: None,
ui: UiState::default(),
form_editor: None,
form_editor: HashMap::new(),
#[cfg(feature = "ui-debug")]
debug_state: None,
@@ -99,32 +99,28 @@ impl AppState {
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 {
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 {

View File

@@ -113,13 +113,15 @@ pub async fn run_ui() -> Result<()> {
.collect();
// Replace local form_state with app_state.form_editor
app_state.set_form_state(
FormState::new(initial_profile.clone(), initial_table.clone(), initial_field_defs),
&config,
);
let path = format!("{}/{}", initial_profile, initial_table);
app_state.ensure_form_editor(&path, &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
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)
.await
.context(format!(
@@ -137,7 +139,9 @@ pub async fn run_ui() -> Result<()> {
}
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;
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;
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)
.unwrap_or(1);
let mut event_processed = false;
@@ -216,7 +220,7 @@ pub async fn run_ui() -> Result<()> {
if !overlay_active {
if let Page::Form(_) = &router.current {
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) {
KeyEventOutcome::Consumed(Some(msg)) => {
event_handler.command_message = msg;
@@ -725,11 +729,14 @@ pub async fn run_ui() -> Result<()> {
// Workaround for borrow checker
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
.draw(|f| {
let mut temp_form_state = form_state_clone.clone();
if let Some(form_state_clone) = form_state_clone {
terminal.draw(|f| {
render_ui(
f,
&mut router,
@@ -746,6 +753,7 @@ pub async fn run_ui() -> Result<()> {
})
.context("Terminal draw call failed")?;
needs_redraw = false;
}
}
let now = Instant::now();