// src/components/admin/admin_panel_admin.rs use crate::config::colors::themes::Theme; use crate::state::pages::admin::{AdminFocus, AdminState}; use crate::state::app::state::AppState; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::Style, text::{Line, Span, Text}, // Added Text widgets::{Block, BorderType, Borders, List, ListItem, Paragraph}, Frame, }; /// Renders the view specific to admin users with a three-pane layout. pub fn render_admin_panel_admin( f: &mut Frame, area: Rect, app_state: &AppState, admin_state: &mut AdminState, theme: &Theme, ) { // 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(panes_area); // Use the whole area directly 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; let profile_border_style = if profile_focus { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.border) }; // Block for the profiles pane let profiles_block = Block::default() .title(" Profiles ") .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(profile_border_style); let profiles_inner_area = profiles_block.inner(profiles_pane); // Get inner area for list f.render_widget(profiles_block, profiles_pane); // Render the block itself // Create profile list items let profile_list_items: Vec = app_state .profile_tree .profiles .iter() .enumerate() .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 { Style::default().fg(theme.fg) }; ListItem::new(Line::from(vec![ Span::styled(prefix, style), Span::styled(&profile.name, style) ])) }) .collect(); // Build and render profile list inside the block's inner area let profile_list = List::new(profile_list_items) // 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() }) .highlight_symbol(if profile_focus { "> " } else { " " }); f.render_stateful_widget(profile_list, profiles_inner_area, &mut admin_state.profile_list_state); // --- Tables Pane (Middle) --- let table_focus = admin_state.current_focus == AdminFocus::Tables; let table_border_style = if table_focus { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.border) }; // Get selected profile information 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(navigated_profile_idx.unwrap_or(usize::MAX)) // Use nav state for title .map_or("None", |p| &p.name); // Block for the tables pane let tables_block = Block::default() .title(format!(" Tables (Profile: {}) ", selected_profile_name)) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(table_border_style); let tables_inner_area = tables_block.inner(tables_pane); // Get inner area for list f.render_widget(tables_block, tables_pane); // Render the block itself // Create table list items and get dependencies for the selected table let (table_list_items, selected_table_deps): (Vec, Vec) = if let Some( 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)| { // 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) }; ListItem::new(Line::from(vec![ Span::styled(prefix, style), Span::styled(&table.name, style), ])) }) .collect(); // 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 { // Default when no profile is selected (vec![ListItem::new("Select a profile to see tables")], vec![]) }; // Build and render table list inside the block's inner area let table_list = List::new(table_list_items) // 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() }) .highlight_symbol(if table_focus { "> " } else { " " }); // Focus indicator f.render_stateful_widget(table_list, tables_inner_area, &mut admin_state.table_list_state); // --- Dependencies Pane (Right) --- // 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.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 let deps_block = Block::default() .title(format!(" Dependencies (Table: {}) ", selected_table_name)) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(Style::default().fg(theme.border)); // No focus highlight for deps pane let deps_inner_area = deps_block.inner(deps_pane); // Get inner area for content f.render_widget(deps_block, deps_pane); // Render the block itself // Prepare content for the dependencies paragraph let mut deps_content = Text::default(); deps_content.lines.push(Line::from(Span::styled( "Depends On:", Style::default().fg(theme.accent), // Use accent color for the label ))); if !selected_table_deps.is_empty() { for dep in selected_table_deps { // List each dependency deps_content.lines.push(Line::from(Span::styled(format!("- {}", dep), theme.fg))); } } else { // Indicate if there are no dependencies deps_content.lines.push(Line::from(Span::styled(" None", theme.secondary))); } // 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_base_style = Style::default().fg(theme.secondary); // Define the helper closure to get style based on focus let get_btn_style = |button_focus: AdminFocus| { if admin_state.current_focus == button_focus { // Apply highlight style if this button is focused btn_base_style.add_modifier(ratatui::style::Modifier::REVERSED) } else { btn_base_style // Use base style otherwise } }; let btn1 = Paragraph::new("Add Logic") .style(get_btn_style(AdminFocus::Button1)) .alignment(Alignment::Center); let btn2 = Paragraph::new("Add Table") .style(get_btn_style(AdminFocus::Button2)) .alignment(Alignment::Center); let btn3 = Paragraph::new("Change Table") .style(get_btn_style(AdminFocus::Button3)) .alignment(Alignment::Center); f.render_widget(btn1, button_chunks[0]); f.render_widget(btn2, button_chunks[1]); f.render_widget(btn3, button_chunks[2]); }