243 lines
10 KiB
Rust
243 lines
10 KiB
Rust
// 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<ListItem> = 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);
|
|
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 only on Profiles focus
|
|
.highlight_style(if profile_focus {
|
|
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_pane_has_focus = matches!(admin_state.current_focus, AdminFocus::Tables | AdminFocus::InsideTablesList);
|
|
let table_border_style = if table_pane_has_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<ListItem>, Vec<String>) = 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<ListItem> = 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 whether the pane OR inside has focus
|
|
.highlight_style(if table_pane_has_focus {
|
|
Style::default().add_modifier(ratatui::style::Modifier::REVERSED)
|
|
} else {
|
|
Style::default()
|
|
})
|
|
.highlight_symbol("> ");
|
|
|
|
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]);
|
|
}
|