// src/components/handlers/sidebar.rs use ratatui::{ widgets::{Block, List, ListItem}, layout::{Rect, Direction, Layout, Constraint}, style::Style, Frame, }; use crate::config::colors::themes::Theme; use common::proto::komp_ac::table_definition::{ProfileTreeResponse}; use ratatui::text::{Span, Line}; use crate::components::utils::text::truncate_string; // Reduced sidebar width const SIDEBAR_WIDTH: u16 = 20; // --- Icons --- const ICON_PROFILE: &str = "📁"; const ICON_TABLE: &str = "📄"; pub fn calculate_sidebar_layout(show_sidebar: bool, main_content_area: Rect) -> (Option, Rect) { if show_sidebar { let chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Length(SIDEBAR_WIDTH), Constraint::Min(0), ]) .split(main_content_area); (Some(chunks[0]), chunks[1]) } else { (None, main_content_area) } } pub fn render_sidebar( f: &mut Frame, area: Rect, theme: &Theme, profile_tree: &ProfileTreeResponse, selected_profile: &Option, ) { let sidebar_block = Block::default().style(Style::default().bg(theme.bg)); let mut items = Vec::new(); let profile_name_available_width = (SIDEBAR_WIDTH as usize).saturating_sub(3); let table_name_available_width = (SIDEBAR_WIDTH as usize).saturating_sub(5); if let Some(profile_name) = selected_profile { // Find the selected profile in the tree if let Some(profile) = profile_tree .profiles .iter() .find(|p| &p.name == profile_name) { // Add profile name as header items.push(ListItem::new(Line::from(vec![ Span::styled(format!("{} ", ICON_PROFILE), Style::default().fg(theme.accent)), Span::styled( truncate_string(&profile.name, profile_name_available_width), Style::default().fg(theme.highlight) ), ]))); // List tables for the selected profile for table in &profile.tables { // Get table name without year prefix to save space let display_name = if table.name.starts_with("2025_") { &table.name[5..] // Skip "2025_" prefix } else { &table.name }; items.push(ListItem::new(Line::from(vec![ Span::raw(" "), // Indentation Span::styled(format!("{} ", ICON_TABLE), Style::default().fg(theme.secondary)), Span::styled( truncate_string(display_name, table_name_available_width), theme.fg ), ]))); } } } else { // Show full profile tree when no profile is selected (compact version) for (profile_idx, profile) in profile_tree.profiles.iter().enumerate() { // Profile header - more compact items.push(ListItem::new(Line::from(vec![ Span::styled(format!("{} ", ICON_PROFILE), Style::default().fg(theme.accent)), Span::styled( &profile.name, Style::default().fg(theme.highlight) ), ]))); // Tables with compact prefixes for (table_idx, table) in profile.tables.iter().enumerate() { let is_last_table = table_idx == profile.tables.len() - 1; let is_last_profile = profile_idx == profile_tree.profiles.len() - 1; // Shorter prefix characters let prefix = match (is_last_profile, is_last_table) { (true, true) => " └", (true, false) => " ├", (false, true) => "│└", (false, false) => "│├", }; // Get table name without year prefix to save space let display_name = if table.name.starts_with("2025_") { &table.name[5..] // Skip "2025_" prefix } else { &table.name }; // Adjust available width if dependency arrow is shown let current_table_available_width = if !table.depends_on.is_empty() { table_name_available_width.saturating_sub(1) } else { table_name_available_width }; let line = vec![ Span::styled(prefix, Style::default().fg(theme.fg)), Span::styled(format!("{} ", ICON_TABLE), Style::default().fg(theme.secondary)), Span::styled(truncate_string(display_name, current_table_available_width), Style::default().fg(theme.fg)), ]; items.push(ListItem::new(Line::from(line))); } // Compact separator between profiles if profile_idx < profile_tree.profiles.len() - 1 { items.push(ListItem::new(Line::from( Span::styled("│", Style::default().fg(theme.secondary)) ))); } } if profile_tree.profiles.is_empty() { items.push(ListItem::new(Span::styled( "No profiles", Style::default().fg(theme.secondary) ))); } } let list = List::new(items) .block(sidebar_block) .highlight_style(Style::default().fg(theme.highlight)) .highlight_symbol(">"); f.render_widget(list, area); }