diff --git a/client/src/pages/admin/main/logic.rs b/client/src/pages/admin/main/logic.rs new file mode 100644 index 0000000..e88fe90 --- /dev/null +++ b/client/src/pages/admin/main/logic.rs @@ -0,0 +1,351 @@ +// src/pages/admin/main/logic.rs +use crate::pages::admin::{AdminFocus, AdminState}; +use crate::state::app::state::AppState; +use crate::config::binds::config::Config; +use crate::buffer::state::{BufferState, AppView}; +use crate::state::pages::add_table::{AddTableState, LinkDefinition}; +use ratatui::widgets::ListState; +use crate::state::pages::add_logic::{AddLogicState, AddLogicFocus}; // Added AddLogicFocus import + +// Helper functions list_select_next and list_select_previous remain the same +fn list_select_next(list_state: &mut ListState, item_count: usize) { + if item_count == 0 { + list_state.select(None); + return; + } + let i = match list_state.selected() { + Some(i) => if i >= item_count - 1 { 0 } else { i + 1 }, + None => 0, + }; + list_state.select(Some(i)); +} + +fn list_select_previous(list_state: &mut ListState, item_count: usize) { + if item_count == 0 { + list_state.select(None); + return; + } + let i = match list_state.selected() { + Some(i) => if i == 0 { item_count - 1 } else { i - 1 }, + None => if item_count > 0 { item_count - 1 } else { 0 }, + }; + list_state.select(Some(i)); +} + +pub fn handle_admin_navigation( + key: crossterm::event::KeyEvent, + config: &Config, + app_state: &mut AppState, + admin_state: &mut AdminState, + buffer_state: &mut BufferState, + command_message: &mut String, +) -> bool { + let action = config.get_general_action(key.code, key.modifiers).map(String::from); + let current_focus = admin_state.current_focus; + let profile_count = app_state.profile_tree.profiles.len(); + let mut handled = false; + + match current_focus { + AdminFocus::ProfilesPane => { + match action.as_deref() { + Some("select") => { + admin_state.current_focus = AdminFocus::InsideProfilesList; + if !app_state.profile_tree.profiles.is_empty() { + if admin_state.profile_list_state.selected().is_none() { + admin_state.profile_list_state.select(Some(0)); + } + } + *command_message = "Navigating profiles. Use Up/Down. Esc to exit.".to_string(); + handled = true; + } + Some("next_option") | Some("move_down") => { + admin_state.current_focus = AdminFocus::Tables; + *command_message = "Focus: Tables Pane".to_string(); + handled = true; + } + Some("previous_option") | Some("move_up") => { + // No wrap-around: Stay on ProfilesPane if trying to go "before" it + *command_message = "At first focusable pane.".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::InsideProfilesList => { + match action.as_deref() { + Some("move_up") => { + if profile_count > 0 { + list_select_previous(&mut admin_state.profile_list_state, profile_count); + *command_message = "".to_string(); + handled = true; + } + } + Some("move_down") => { + if profile_count > 0 { + list_select_next(&mut admin_state.profile_list_state, profile_count); + *command_message = "".to_string(); + handled = true; + } + } + Some("select") => { + admin_state.selected_profile_index = admin_state.profile_list_state.selected(); + admin_state.selected_table_index = None; // Deselect table when profile changes + if let Some(profile_idx) = admin_state.selected_profile_index { + 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)); // Auto-select first table for nav + } else { + admin_state.table_list_state.select(None); + } + } + } else { + admin_state.table_list_state.select(None); + } + *command_message = format!( + "Profile '{}' set as active.", + admin_state.get_selected_profile_name().unwrap_or(&"N/A".to_string()) + ); + handled = true; + } + Some("exit_table_scroll") => { + admin_state.current_focus = AdminFocus::ProfilesPane; + *command_message = "Focus: Profiles Pane".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::Tables => { + match action.as_deref() { + Some("select") => { + admin_state.current_focus = AdminFocus::InsideTablesList; + let current_profile_idx = admin_state.selected_profile_index + .or_else(|| admin_state.profile_list_state.selected()); + if let Some(profile_idx) = current_profile_idx { + if let Some(profile) = app_state.profile_tree.profiles.get(profile_idx) { + if !profile.tables.is_empty() { + if admin_state.table_list_state.selected().is_none() { + 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); + *command_message = "Select a profile first to view its tables.".to_string(); + } + if admin_state.current_focus == AdminFocus::InsideTablesList && !admin_state.table_list_state.selected().is_none() { + *command_message = "Navigating tables. Use Up/Down. Esc to exit.".to_string(); + } else if admin_state.table_list_state.selected().is_none() { + if current_profile_idx.is_none() { + *command_message = "No profile selected to view tables.".to_string(); + } else { + *command_message = "No tables in selected profile.".to_string(); + } + admin_state.current_focus = AdminFocus::Tables; // Stay in Tables pane if no tables to enter + } + handled = true; + } + Some("previous_option") | Some("move_up") => { + admin_state.current_focus = AdminFocus::ProfilesPane; + *command_message = "Focus: Profiles Pane".to_string(); + handled = true; + } + Some("next_option") | Some("move_down") => { + admin_state.current_focus = AdminFocus::Button1; + *command_message = "Focus: Add Logic Button".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::InsideTablesList => { + match action.as_deref() { + Some("move_up") => { + let current_profile_idx = admin_state.selected_profile_index + .or_else(|| admin_state.profile_list_state.selected()); + if let Some(p_idx) = current_profile_idx { + if let Some(profile) = app_state.profile_tree.profiles.get(p_idx) { + if !profile.tables.is_empty() { + list_select_previous(&mut admin_state.table_list_state, profile.tables.len()); + *command_message = "".to_string(); + handled = true; + } else { + *command_message = "No tables to navigate.".to_string(); + handled = true; + } + } + } else { + *command_message = "No active profile for tables.".to_string(); + handled = true; + } + } + Some("move_down") => { + let current_profile_idx = admin_state.selected_profile_index + .or_else(|| admin_state.profile_list_state.selected()); + if let Some(p_idx) = current_profile_idx { + if let Some(profile) = app_state.profile_tree.profiles.get(p_idx) { + if !profile.tables.is_empty() { + list_select_next(&mut admin_state.table_list_state, profile.tables.len()); + *command_message = "".to_string(); + handled = true; + } else { + *command_message = "No tables to navigate.".to_string(); + handled = true; + } + } + } else { + *command_message = "No active profile for tables.".to_string(); + handled = true; + } + } + Some("select") => { // This is for persistently selecting a table with [*] + admin_state.selected_table_index = admin_state.table_list_state.selected(); + let table_name = admin_state.selected_profile_index + .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))) + .map_or("N/A", |t| t.name.as_str()); + *command_message = format!("Table '{}' set as active.", table_name); + handled = true; + } + Some("exit_table_scroll") => { + admin_state.current_focus = AdminFocus::Tables; + *command_message = "Focus: Tables Pane".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::Button1 => { // Add Logic Button + match action.as_deref() { + Some("select") => { // Typically "Enter" key + if let Some(p_idx) = admin_state.selected_profile_index { + if let Some(profile) = app_state.profile_tree.profiles.get(p_idx) { + if let Some(t_idx) = admin_state.selected_table_index { + if let Some(table) = profile.tables.get(t_idx) { + // Both profile and table are selected, proceed + admin_state.add_logic_state = AddLogicState { + profile_name: profile.name.clone(), + selected_table_name: Some(table.name.clone()), + selected_table_id: Some(table.id), // If you have table IDs + editor_keybinding_mode: config.editor.keybinding_mode.clone(), + current_focus: AddLogicFocus::default(), + ..AddLogicState::default() + }; + + // Store table info for later fetching + app_state.pending_table_structure_fetch = Some(( + profile.name.clone(), + table.name.clone() + )); + + buffer_state.update_history(AppView::AddLogic); + app_state.ui.focus_outside_canvas = false; + *command_message = format!( + "Opening Add Logic for table '{}' in profile '{}'...", + table.name, profile.name + ); + } else { + *command_message = "Error: Selected table data not found.".to_string(); + } + } else { + *command_message = "Select a table first!".to_string(); + } + } else { + *command_message = "Error: Selected profile data not found.".to_string(); + } + } else { + *command_message = "Select a profile first!".to_string(); + } + handled = true; + } + Some("previous_option") | Some("move_up") => { + admin_state.current_focus = AdminFocus::Tables; + *command_message = "Focus: Tables Pane".to_string(); + handled = true; + } + Some("next_option") | Some("move_down") => { + admin_state.current_focus = AdminFocus::Button2; + *command_message = "Focus: Add Table Button".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::Button2 => { // Add Table Button + match action.as_deref() { + Some("select") => { + if let Some(p_idx) = admin_state.selected_profile_index { + if let Some(profile) = app_state.profile_tree.profiles.get(p_idx) { + let selected_profile_name = profile.name.clone(); + // Prepare links from the selected profile's existing tables + let available_links: Vec = profile.tables.iter() + .map(|table| LinkDefinition { + linked_table_name: table.name.clone(), + is_required: false, // Default, can be changed in AddTable screen + selected: false, + }).collect(); + + admin_state.add_table_state = AddTableState { + profile_name: selected_profile_name, + links: available_links, + ..AddTableState::default() // Reset other fields + }; + buffer_state.update_history(AppView::AddTable); + app_state.ui.focus_outside_canvas = false; + *command_message = format!("Opening Add Table for profile '{}'...", admin_state.add_table_state.profile_name); + handled = true; + } else { + *command_message = "Error: Selected profile index out of bounds.".to_string(); + handled = true; + } + } else { + *command_message = "Please select a profile ([*]) first to add a table.".to_string(); + handled = true; + } + } + Some("previous_option") | Some("move_up") => { + admin_state.current_focus = AdminFocus::Button1; + *command_message = "Focus: Add Logic Button".to_string(); + handled = true; + } + Some("next_option") | Some("move_down") => { + admin_state.current_focus = AdminFocus::Button3; + *command_message = "Focus: Change Table Button".to_string(); + handled = true; + } + _ => handled = false, + } + } + + AdminFocus::Button3 => { // Change Table Button + match action.as_deref() { + Some("select") => { + // Future: Logic to load selected table into AddTableState for editing + *command_message = "Action: Change Table (Not Implemented)".to_string(); + handled = true; + } + Some("previous_option") | Some("move_up") => { + admin_state.current_focus = AdminFocus::Button2; + *command_message = "Focus: Add Table Button".to_string(); + handled = true; + } + Some("next_option") | Some("move_down") => { + // No wrap-around: Stay on Button3 if trying to go "after" it + *command_message = "At last focusable button.".to_string(); + handled = true; + } + _ => handled = false, + } + } + } + handled +} diff --git a/client/src/pages/admin/main/mod.rs b/client/src/pages/admin/main/mod.rs index 21e8062..e651e73 100644 --- a/client/src/pages/admin/main/mod.rs +++ b/client/src/pages/admin/main/mod.rs @@ -1,2 +1,7 @@ // src/pages/admin/main/mod.rs + pub mod state; +pub mod ui; +pub mod ui_admin; +pub mod logic; +pub mod tui; diff --git a/client/src/pages/admin/main/tui.rs b/client/src/pages/admin/main/tui.rs new file mode 100644 index 0000000..459c6be --- /dev/null +++ b/client/src/pages/admin/main/tui.rs @@ -0,0 +1,12 @@ +// src/pages/admin/main/tui.rs +use crate::state::app::state::AppState; +use crate::pages::admin::AdminState; + +pub fn handle_admin_selection(app_state: &mut AppState, admin_state: &AdminState) { + let profiles = &app_state.profile_tree.profiles; + if let Some(selected_index) = admin_state.get_selected_index() { + if let Some(profile) = profiles.get(selected_index) { + app_state.selected_profile = Some(profile.name.clone()); + } + } +} diff --git a/client/src/pages/admin/main/ui.rs b/client/src/pages/admin/main/ui.rs new file mode 100644 index 0000000..f166d26 --- /dev/null +++ b/client/src/pages/admin/main/ui.rs @@ -0,0 +1,131 @@ +// src/pages/admin/main/ui.rs + +use crate::config::colors::themes::Theme; +use crate::state::pages::auth::AuthState; +use crate::state::app::state::AppState; +use crate::pages::admin::AdminState; +use common::proto::komp_ac::table_definition::ProfileTreeResponse; +use ratatui::{ + layout::{Constraint, Direction, Layout, Rect}, + style::Style, + text::{Line, Span, Text}, + widgets::{Block, BorderType, Borders, List, ListItem, Paragraph, Wrap}, + Frame, +}; +use crate::pages::admin::main::ui_admin::render_admin_panel_admin; + +pub fn render_admin_panel( + f: &mut Frame, + app_state: &AppState, + auth_state: &AuthState, + admin_state: &mut AdminState, + area: Rect, + theme: &Theme, + profile_tree: &ProfileTreeResponse, + selected_profile: &Option, +) { + let block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::default().fg(theme.accent)) + .style(Style::default().bg(theme.bg)); + + let inner_area = block.inner(area); + f.render_widget(block, area); + + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(3), Constraint::Min(1)]) + .split(inner_area); + + // Content + let content_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) + .split(chunks[1]); + + if auth_state.role.as_deref() != Some("admin") { + render_admin_panel_non_admin( + f, + admin_state, + &content_chunks, + theme, + profile_tree, + selected_profile, + ); + } else { + render_admin_panel_admin( + f, + chunks[1], + app_state, + admin_state, + theme, + ); + } +} + +/// Renders the view for non-admin users (profile list and details). +fn render_admin_panel_non_admin( + f: &mut Frame, + admin_state: &AdminState, + content_chunks: &[Rect], + theme: &Theme, + profile_tree: &ProfileTreeResponse, + selected_profile: &Option, +) { + // Profile list - Use data from admin_state + let items: Vec = admin_state + .profiles + .iter() + .map(|p| { + ListItem::new(Line::from(vec![ + Span::styled( + if Some(p) == selected_profile.as_ref() { "✓ " } else { " " }, + Style::default().fg(theme.accent), + ), + Span::styled(p, Style::default().fg(theme.fg)), + ])) + }) + .collect(); + + let list = List::new(items) + .block(Block::default().title("Profiles")) + .highlight_style(Style::default().bg(theme.highlight).fg(theme.bg)); + + let mut profile_list_state_clone = admin_state.profile_list_state.clone(); + f.render_stateful_widget(list, content_chunks[0], &mut profile_list_state_clone); + + // Profile details - Use selection info from admin_state + if let Some(profile) = admin_state + .get_selected_index() + .and_then(|i| profile_tree.profiles.get(i)) + { + let mut text = Text::default(); + text.lines.push(Line::from(vec![ + Span::styled("Profile: ", Style::default().fg(theme.accent)), + Span::styled(&profile.name, Style::default().fg(theme.highlight)), + ])); + + text.lines.push(Line::from("")); + text.lines.push(Line::from(Span::styled( + "Tables:", + Style::default().fg(theme.accent), + ))); + + for table in &profile.tables { + let mut line = vec![Span::styled(format!("├─ {}", table.name), theme.fg)]; + if !table.depends_on.is_empty() { + line.push(Span::styled( + format!(" → {}", table.depends_on.join(", ")), + Style::default().fg(theme.secondary), + )); + } + text.lines.push(Line::from(line)); + } + + let details_widget = Paragraph::new(text) + .block(Block::default().title("Details")) + .wrap(Wrap { trim: true }); + f.render_widget(details_widget, content_chunks[1]); + } +} diff --git a/client/src/pages/admin/main/ui_admin.rs b/client/src/pages/admin/main/ui_admin.rs new file mode 100644 index 0000000..efe5790 --- /dev/null +++ b/client/src/pages/admin/main/ui_admin.rs @@ -0,0 +1,180 @@ +// src/pages/admin/main/ui_admin.rs + +use crate::config::colors::themes::Theme; +use crate::pages::admin::{AdminFocus, AdminState}; +use crate::state::app::state::AppState; +use ratatui::{ + layout::{Alignment, Constraint, Direction, Layout, Rect}, + style::Style, + text::{Line, Span, Text}, + widgets::{Block, BorderType, Borders, List, ListItem, Paragraph}, + Frame, +}; + +pub fn render_admin_panel_admin( + f: &mut Frame, + area: Rect, + app_state: &AppState, + admin_state: &mut AdminState, + theme: &Theme, +) { + let main_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Length(1)].as_ref()) + .split(area); + let panes_area = main_chunks[0]; + let buttons_area = main_chunks[1]; + + 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); + + let profiles_pane = pane_chunks[0]; + let tables_pane = pane_chunks[1]; + let deps_pane = pane_chunks[2]; + + // --- Profiles Pane (Left) --- + let profile_pane_has_focus = matches!(admin_state.current_focus, AdminFocus::ProfilesPane | AdminFocus::InsideProfilesList); + let profile_border_style = if profile_pane_has_focus { + Style::default().fg(theme.highlight) + } else { + Style::default().fg(theme.border) + }; + 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); + f.render_widget(profiles_block, profiles_pane); + let profile_list_items: Vec = app_state.profile_tree.profiles.iter().enumerate().map(|(idx, profile)| { + let is_persistently_selected = admin_state.selected_profile_index == Some(idx); + let is_nav_highlighted = admin_state.profile_list_state.selected() == Some(idx) && admin_state.current_focus == AdminFocus::InsideProfilesList; + let prefix = if is_persistently_selected { "[*] " } else { "[ ] " }; + let item_style = if is_nav_highlighted { Style::default().fg(theme.highlight).add_modifier(ratatui::style::Modifier::BOLD) } + else if is_persistently_selected { Style::default().fg(theme.accent) } + else { Style::default().fg(theme.fg) }; + ListItem::new(Line::from(vec![Span::styled(prefix, item_style), Span::styled(&profile.name, item_style)])) + }).collect(); + let profile_list = List::new(profile_list_items) + .highlight_style(if admin_state.current_focus == AdminFocus::InsideProfilesList { Style::default().add_modifier(ratatui::style::Modifier::REVERSED) } else { Style::default() }) + .highlight_symbol(if admin_state.current_focus == AdminFocus::InsideProfilesList { "> " } 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) }; + + let profile_to_display_tables_for_idx: Option; + if admin_state.current_focus == AdminFocus::InsideProfilesList { + profile_to_display_tables_for_idx = admin_state.profile_list_state.selected(); + } else { + profile_to_display_tables_for_idx = admin_state.selected_profile_index + .or_else(|| admin_state.profile_list_state.selected()); + } + let tables_pane_title_profile_name = profile_to_display_tables_for_idx + .and_then(|idx| app_state.profile_tree.profiles.get(idx)) + .map_or("None Selected", |p| p.name.as_str()); + let tables_block = Block::default().title(format!(" Tables (Profile: {}) ", tables_pane_title_profile_name)).borders(Borders::ALL).border_type(BorderType::Rounded).border_style(table_border_style); + let tables_inner_area = tables_block.inner(tables_pane); + f.render_widget(tables_block, tables_pane); + + let table_list_items_for_display: Vec = + if let Some(profile_data_for_tables) = profile_to_display_tables_for_idx + .and_then(|idx| app_state.profile_tree.profiles.get(idx)) { + profile_data_for_tables.tables.iter().enumerate().map(|(idx, table)| { + let is_table_persistently_selected = admin_state.selected_table_index == Some(idx) && + profile_to_display_tables_for_idx == admin_state.selected_profile_index; + let is_table_nav_highlighted = admin_state.table_list_state.selected() == Some(idx) && + admin_state.current_focus == AdminFocus::InsideTablesList; + let prefix = if is_table_persistently_selected { "[*] " } else { "[ ] " }; + let style = if is_table_nav_highlighted { Style::default().fg(theme.highlight).add_modifier(ratatui::style::Modifier::BOLD) } + else if is_table_persistently_selected { Style::default().fg(theme.accent) } + else { Style::default().fg(theme.fg) }; + ListItem::new(Line::from(vec![Span::styled(prefix, style), Span::styled(&table.name, style)])) + }).collect() + } else { + vec![ListItem::new("Select a profile to see tables")] + }; + let table_list = List::new(table_list_items_for_display) + .highlight_style(if admin_state.current_focus == AdminFocus::InsideTablesList { Style::default().add_modifier(ratatui::style::Modifier::REVERSED) } else { Style::default() }) + .highlight_symbol(if admin_state.current_focus == AdminFocus::InsideTablesList { "> " } else { " " }); + f.render_stateful_widget(table_list, tables_inner_area, &mut admin_state.table_list_state); + + + // --- Dependencies Pane (Right) --- + let mut deps_pane_title_table_name = "N/A".to_string(); + let dependencies_to_display: Vec; + + if admin_state.current_focus == AdminFocus::InsideTablesList { + // If navigating tables, show dependencies for the '>' highlighted table. + // The profile context is `profile_to_display_tables_for_idx` (from Tables pane logic). + if let Some(p_idx_for_current_tables) = profile_to_display_tables_for_idx { + if let Some(current_profile_showing_tables) = app_state.profile_tree.profiles.get(p_idx_for_current_tables) { + if let Some(table_nav_idx) = admin_state.table_list_state.selected() { // The '>' highlighted table + if let Some(navigated_table) = current_profile_showing_tables.tables.get(table_nav_idx) { + deps_pane_title_table_name = navigated_table.name.clone(); + dependencies_to_display = navigated_table.depends_on.clone(); + } else { + dependencies_to_display = Vec::new(); // Navigated table index out of bounds + } + } else { + dependencies_to_display = Vec::new(); // No table navigated with '>' + } + } else { + dependencies_to_display = Vec::new(); // Profile for tables out of bounds + } + } else { + dependencies_to_display = Vec::new(); // No profile active for table display + } + } else { + // Otherwise, show dependencies for the '[*]' persistently selected table & profile. + if let Some(p_idx) = admin_state.selected_profile_index { // Must be a persistently selected profile + if let Some(selected_profile) = app_state.profile_tree.profiles.get(p_idx) { + if let Some(t_idx) = admin_state.selected_table_index { // Must be a persistently selected table + if let Some(selected_table) = selected_profile.tables.get(t_idx) { + deps_pane_title_table_name = selected_table.name.clone(); + dependencies_to_display = selected_table.depends_on.clone(); + } else { dependencies_to_display = Vec::new(); } + } else { dependencies_to_display = Vec::new(); } + } else { dependencies_to_display = Vec::new(); } + } else { dependencies_to_display = Vec::new(); } + } + + let deps_block = Block::default() + .title(format!(" Dependencies (Table: {}) ", deps_pane_title_table_name)) + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(Style::default().fg(theme.border)); + let deps_inner_area = deps_block.inner(deps_pane); + f.render_widget(deps_block, deps_pane); + + let mut deps_content = Text::default(); + deps_content.lines.push(Line::from(Span::styled( + "Depends On:", + Style::default().fg(theme.accent), + ))); + + if !dependencies_to_display.is_empty() { + for dep in dependencies_to_display { + deps_content.lines.push(Line::from(Span::styled(format!("- {}", dep), theme.fg))); + } + } else { + deps_content.lines.push(Line::from(Span::styled(" None", theme.secondary))); + } + 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); + let get_btn_style = |button_focus: AdminFocus| { if admin_state.current_focus == button_focus { btn_base_style.add_modifier(ratatui::style::Modifier::REVERSED) } else { btn_base_style } }; + 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]); +}