From 6505e18b0b8f8e696e9facdc3e70299c12c80106 Mon Sep 17 00:00:00 2001 From: filipriec Date: Wed, 16 Apr 2025 21:22:34 +0200 Subject: [PATCH] working admin panel, needs to do buttons for navigation next --- .../src/components/admin/admin_panel_admin.rs | 89 +++++++++++++------ .../functions/modes/navigation/admin_nav.rs | 77 ++++++++++------ client/src/state/pages/admin.rs | 24 ++--- 3 files changed, 118 insertions(+), 72 deletions(-) diff --git a/client/src/components/admin/admin_panel_admin.rs b/client/src/components/admin/admin_panel_admin.rs index 3c6b6b5..911a2fa 100644 --- a/client/src/components/admin/admin_panel_admin.rs +++ b/client/src/components/admin/admin_panel_admin.rs @@ -19,19 +19,27 @@ pub fn render_admin_panel_admin( admin_state: &mut AdminState, theme: &Theme, ) { - // Split the area into three panes: Profiles | Tables | Dependencies - let chunks = Layout::default() + // Split vertically: Top for panes, Bottom for buttons + let main_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Length(1)].as_ref()) // 1 line for buttons + .split(area); + let panes_area = main_chunks[0]; + let buttons_area = main_chunks[1]; + + // Split the top area into three panes: Profiles | Tables | Dependencies + let pane_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(25), // Profiles Constraint::Percentage(40), // Tables Constraint::Percentage(35), // Dependencies ].as_ref()) - .split(area); // Use the whole area directly + .split(panes_area); // Use the whole area directly - let profiles_pane = chunks[0]; - let tables_pane = chunks[1]; - let deps_pane = chunks[2]; + let profiles_pane = pane_chunks[0]; + let tables_pane = pane_chunks[1]; + let deps_pane = pane_chunks[2]; // --- Profiles Pane (Left) --- let profile_focus = admin_state.current_focus == AdminFocus::Profiles; @@ -56,10 +64,11 @@ pub fn render_admin_panel_admin( .profiles .iter() .enumerate() - .map(|(i, profile)| { - // THIS line checks the selection state for the profile list - let is_selected = admin_state.profile_list_state.selected() == Some(i); - let prefix = if is_selected { "[*] " } else { "[ ] " }; // Use [*] for selected + .map(|(idx, profile)| { + // Check persistent selection for prefix, navigation state for style/highlight + let is_selected = admin_state.selected_profile_index == Some(idx); // Use persistent state for [*] + let is_navigated = admin_state.profile_list_state.selected() == Some(idx); // Use nav state for highlight/> + let prefix = if is_selected { "[*] " } else { "[ ] " }; let style = if is_selected { // Style based on selection too Style::default().fg(theme.highlight).add_modifier(ratatui::style::Modifier::BOLD) } else { @@ -74,7 +83,8 @@ pub fn render_admin_panel_admin( // Build and render profile list inside the block's inner area let profile_list = List::new(profile_list_items) - .highlight_style(if profile_focus { + // Highlight style depends on focus AND navigation state + .highlight_style(if profile_focus { // Use focus state Style::default().add_modifier(ratatui::style::Modifier::REVERSED) } else { Style::default() @@ -92,11 +102,11 @@ pub fn render_admin_panel_admin( }; // Get selected profile information - let selected_profile_idx = admin_state.profile_list_state.selected(); + let navigated_profile_idx = admin_state.profile_list_state.selected(); // Use nav state for display let selected_profile_name = app_state .profile_tree .profiles - .get(selected_profile_idx.unwrap_or(usize::MAX)) // Use index, provide default if None + .get(navigated_profile_idx.unwrap_or(usize::MAX)) // Use nav state for title .map_or("None", |p| &p.name); // Block for the tables pane @@ -110,17 +120,18 @@ pub fn render_admin_panel_admin( // Create table list items and get dependencies for the selected table let (table_list_items, selected_table_deps): (Vec, Vec) = if let Some( - profile, - ) = selected_profile_idx.and_then(|idx| app_state.profile_tree.profiles.get(idx)) { + profile, // Get profile based on NAVIGATED profile index + ) = navigated_profile_idx.and_then(|idx| app_state.profile_tree.profiles.get(idx)) { let items: Vec = profile .tables .iter() .enumerate() - .map(|(idx, table)| { - let is_navigated = admin_state.table_list_state.selected() == Some(idx); - let prefix = if admin_state.is_table_selected(idx) { "[*] " } else { "[ ] " }; - // Don't show dependencies inline anymore - let style = if is_navigated { + .map(|(idx, table)| { // Renamed i to idx for clarity + // Check persistent selection for prefix, navigation state for style/highlight + let is_selected = admin_state.selected_table_index == Some(idx); // Use persistent state for [*] + let is_navigated = admin_state.table_list_state.selected() == Some(idx); // Use nav state for highlight/> + let prefix = if is_selected { "[*] " } else { "[ ] " }; + let style = if is_navigated { // Style based on navigation highlight Style::default().fg(theme.highlight).add_modifier(ratatui::style::Modifier::BOLD) } else { Style::default().fg(theme.fg) @@ -132,10 +143,12 @@ pub fn render_admin_panel_admin( }) .collect(); - // Get dependencies only for the currently selected/highlighted table - let deps = admin_state.table_list_state.selected() - .and_then(|idx| profile.tables.get(idx)) - .map_or(vec![], |t| t.depends_on.clone()); + // Get dependencies only for the PERSISTENTLY selected table in the PERSISTENTLY selected profile + let chosen_profile_idx = admin_state.selected_profile_index; // Use persistent profile selection + let deps = chosen_profile_idx // Start with the chosen profile index + .and_then(|p_idx| app_state.profile_tree.profiles.get(p_idx)) // Get the chosen profile + .and_then(|p| admin_state.selected_table_index.and_then(|t_idx| p.tables.get(t_idx))) // Get the chosen table using its index + .map_or(Vec::new(), |t| t.depends_on.clone()); // If found, clone its depends_on, otherwise return empty Vec (items, deps) } else { @@ -145,7 +158,8 @@ pub fn render_admin_panel_admin( // Build and render table list inside the block's inner area let table_list = List::new(table_list_items) - .highlight_style(if table_focus { + // Highlight style depends on focus AND navigation state + .highlight_style(if table_focus { // Use focus state Style::default().add_modifier(ratatui::style::Modifier::REVERSED) } else { Style::default() @@ -155,9 +169,11 @@ pub fn render_admin_panel_admin( f.render_stateful_widget(table_list, tables_inner_area, &mut admin_state.table_list_state); // --- Dependencies Pane (Right) --- - let selected_table_name = selected_profile_idx + // Get name based on PERSISTENT selections + let chosen_profile_idx = admin_state.selected_profile_index; // Use persistent profile selection + let selected_table_name = chosen_profile_idx .and_then(|p_idx| app_state.profile_tree.profiles.get(p_idx)) - .and_then(|p| admin_state.table_list_state.selected().and_then(|t_idx| p.tables.get(t_idx))) + .and_then(|p| admin_state.selected_table_index.and_then(|t_idx| p.tables.get(t_idx))) // Use persistent table selection .map_or("N/A", |t| &t.name); // Get name of the selected table // Block for the dependencies pane @@ -189,5 +205,22 @@ pub fn render_admin_panel_admin( // Build and render dependencies paragraph inside the block's inner area let deps_paragraph = Paragraph::new(deps_content); f.render_widget(deps_paragraph, deps_inner_area); -} + // --- Buttons Row --- + let button_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage(33), + Constraint::Percentage(34), + Constraint::Percentage(33), + ].as_ref()) + .split(buttons_area); + + let btn_style = Style::default().fg(theme.secondary); // Style for button text + let btn1 = Paragraph::new("Add Logic").style(btn_style).alignment(Alignment::Center); + let btn2 = Paragraph::new("Add Table").style(btn_style).alignment(Alignment::Center); + let btn3 = Paragraph::new("Change Table").style(btn_style).alignment(Alignment::Center); + f.render_widget(btn1, button_chunks[0]); + f.render_widget(btn2, button_chunks[1]); + f.render_widget(btn3, button_chunks[2]); +} diff --git a/client/src/functions/modes/navigation/admin_nav.rs b/client/src/functions/modes/navigation/admin_nav.rs index 4944656..bd9f646 100644 --- a/client/src/functions/modes/navigation/admin_nav.rs +++ b/client/src/functions/modes/navigation/admin_nav.rs @@ -26,15 +26,15 @@ pub fn handle_admin_navigation( match current_focus { AdminFocus::Profiles => { if profile_count > 0 { + // Updates navigation state, resets table state admin_state.previous_profile(profile_count); - // Reset table selection when profile changes - admin_state.table_list_state.select(None); *command_message = "Navigated profiles".to_string(); } } AdminFocus::Tables => { - if let Some(selected_profile_index) = admin_state.profile_list_state.selected() { - if let Some(profile) = app_state.profile_tree.profiles.get(selected_profile_index) { + // Updates table navigation state + if let Some(nav_profile_idx) = admin_state.profile_list_state.selected() { + if let Some(profile) = app_state.profile_tree.profiles.get(nav_profile_idx) { let table_count = profile.tables.len(); if table_count > 0 { admin_state.previous_table(table_count); @@ -50,15 +50,14 @@ pub fn handle_admin_navigation( match current_focus { AdminFocus::Profiles => { if profile_count > 0 { + // Updates navigation state, resets table state admin_state.next_profile(profile_count); - // Reset table selection when profile changes - admin_state.table_list_state.select(None); *command_message = "Navigated profiles".to_string(); } } AdminFocus::Tables => { - if let Some(selected_profile_index) = admin_state.profile_list_state.selected() { - if let Some(profile) = app_state.profile_tree.profiles.get(selected_profile_index) { + if let Some(nav_profile_idx) = admin_state.profile_list_state.selected() { + if let Some(profile) = app_state.profile_tree.profiles.get(nav_profile_idx) { let table_count = profile.tables.len(); if table_count > 0 { admin_state.next_table(table_count); @@ -73,8 +72,27 @@ pub fn handle_admin_navigation( // --- Horizontal Navigation (Focus Change) --- Some("next_option") | Some("previous_option") => { + let old_focus = admin_state.current_focus; admin_state.toggle_focus(); - *command_message = format!("Focus set to {:?}", admin_state.current_focus); + let new_focus = admin_state.current_focus; + *command_message = format!("Focus set to {:?}", new_focus); + + // If focus moved TO Tables, select the first item for navigation + if old_focus == AdminFocus::Profiles && new_focus == AdminFocus::Tables { + if let Some(profile_idx) = admin_state.profile_list_state.selected() { // Use nav state + if let Some(profile) = app_state.profile_tree.profiles.get(profile_idx) { + if !profile.tables.is_empty() { + admin_state.table_list_state.select(Some(0)); + } else { + admin_state.table_list_state.select(None); + } + } else { + admin_state.table_list_state.select(None); + } + } else { + admin_state.table_list_state.select(None); + } + } true // Event handled } @@ -82,29 +100,36 @@ pub fn handle_admin_navigation( Some("select") => { match current_focus { AdminFocus::Profiles => { - // If a profile is selected, move focus to tables - if admin_state.profile_list_state.selected().is_some() { - admin_state.toggle_focus(); // Move focus to Tables - // Optionally select the first table if available - if let Some(selected_profile_index) = admin_state.profile_list_state.selected() { - if let Some(profile) = app_state.profile_tree.profiles.get(selected_profile_index) { - if !profile.tables.is_empty() { - admin_state.table_list_state.select(Some(0)); - } - } - } - *command_message = "Selected profile, focus on Tables".to_string(); + // Set the persistent selection to the currently navigated item + if let Some(nav_idx) = admin_state.profile_list_state.selected() { + admin_state.selected_profile_index = Some(nav_idx); // Set persistent selection + + // Move focus to Tables + admin_state.toggle_focus(); // Move focus + + // Select the first table for navigation highlight + admin_state.table_list_state.select(None); // Clear table nav first + admin_state.selected_table_index = None; // Clear persistent table selection + if let Some(profile) = app_state.profile_tree.profiles.get(nav_idx) { + if !profile.tables.is_empty() { + // Set table nav highlight + admin_state.table_list_state.select(Some(0)); + } + } + + *command_message = format!("Selected profile idx {}, focus on Tables", nav_idx); } else { *command_message = "No profile selected".to_string(); } } AdminFocus::Tables => { - if let Some(idx) = admin_state.get_selected_table_index() { - admin_state.toggle_table_selection(idx); - *command_message = format!("Toggled selection for table index {}", idx); - } else { + // Set the persistent selection to the currently navigated item + if let Some(nav_idx) = admin_state.table_list_state.selected() { + admin_state.selected_table_index = Some(nav_idx); // Set persistent selection + *command_message = format!("Selected table index {}", nav_idx); + } else { *command_message = "No table highlighted".to_string(); - } + } // We don't change focus here for now. } } diff --git a/client/src/state/pages/admin.rs b/client/src/state/pages/admin.rs index b718b9d..384fabd 100644 --- a/client/src/state/pages/admin.rs +++ b/client/src/state/pages/admin.rs @@ -1,7 +1,6 @@ // src/state/pages/admin.rs use ratatui::widgets::ListState; -use std::collections::HashSet; // Define the focus states for the admin panel panes #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -13,10 +12,11 @@ pub enum AdminFocus { #[derive(Default, Clone, Debug)] pub struct AdminState { - pub profiles: Vec, - pub profile_list_state: ListState, // State for the profiles list - pub table_list_state: ListState, // State for the tables list navigation/highlight - pub selected_table_indices: HashSet, // Indices of tables marked with [*] + pub profiles: Vec, // Holds profile names (used by non-admin view) + pub profile_list_state: ListState, // Tracks navigation highlight (>) in profiles + pub table_list_state: ListState, // Tracks navigation highlight (>) in tables + pub selected_profile_index: Option, // Index with [*] in profiles (persistent) + pub selected_table_index: Option, // Index with [*] in tables (persistent) pub current_focus: AdminFocus, // Tracks which pane is focused } @@ -86,8 +86,7 @@ impl AdminState { /// Selects a profile by index and resets table selection. pub fn select_profile(&mut self, index: Option) { self.profile_list_state.select(index); - self.table_list_state.select(None); // Reset table selection - self.selected_table_indices.clear(); + self.table_list_state.select(None); } /// Selects a table by index. @@ -179,16 +178,5 @@ impl AdminState { }; } - /// Toggles the selection state of the table at the given index. - pub fn toggle_table_selection(&mut self, index: usize) { - if !self.selected_table_indices.remove(&index) { - // If remove returned false, it wasn't present, so insert it. - self.selected_table_indices.insert(index); - } - } - - pub fn is_table_selected(&self, index: usize) -> bool { - self.selected_table_indices.contains(&index) - } }