// src/components/admin/add_table.rs use crate::config::colors::themes::Theme; use crate::state::app::highlight::HighlightState; use crate::state::app::state::AppState; use crate::state::pages::add_table::{AddTableFocus, AddTableState}; use crate::state::pages::canvas_state::CanvasState; // use crate::state::pages::add_table::{ColumnDefinition, LinkDefinition}; // Not directly used here use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Modifier, Style}, text::{Line, Span}, widgets::{Block, BorderType, Borders, Cell, Paragraph, Row, Table}, Frame, }; use crate::components::handlers::canvas::render_canvas; use crate::components::common::dialog; /// Renders the Add New Table page layout, structuring the display of table information, /// input fields, and action buttons. Adapts layout based on terminal width. pub fn render_add_table( f: &mut Frame, area: Rect, theme: &Theme, app_state: &AppState, // Currently unused, might be needed later add_table_state: &mut AddTableState, is_edit_mode: bool, // Determines if canvas inputs are in edit mode highlight_state: &HighlightState, // For text highlighting in canvas ) { // --- Configuration --- // Threshold width to switch between wide and narrow layouts const NARROW_LAYOUT_THRESHOLD: u16 = 120; // Adjust this value as needed // --- State Checks --- let focus_on_canvas_inputs = matches!( add_table_state.current_focus, AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType ); // --- Main Page Block --- let main_block = Block::default() .title(" Add New Table ") .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(Style::default().fg(theme.border)) .style(Style::default().bg(theme.bg)); let inner_area = main_block.inner(area); f.render_widget(main_block, area); // --- Fullscreen Columns Table Check (Narrow Screens Only) --- if area.width < NARROW_LAYOUT_THRESHOLD && add_table_state.current_focus == AddTableFocus::InsideColumnsTable { // Render ONLY the columns table taking the full inner area let columns_border_style = Style::default().fg(theme.highlight); // Always highlighted when fullscreen let column_rows: Vec> = add_table_state .columns .iter() .map(|col_def| { Row::new(vec![ Cell::from(if col_def.selected { "[*]" } else { "[ ]" }), Cell::from(col_def.name.clone()), Cell::from(col_def.data_type.clone()), ]) .style(Style::default().fg(theme.fg)) }) .collect(); let header_cells = ["Sel", "Name", "Type"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let header = Row::new(header_cells).height(1).bottom_margin(1); let columns_table = Table::new(column_rows, [Constraint::Length(5), Constraint::Percentage(50), Constraint::Percentage(50)]) .header(header) .block( Block::default() .title(Span::styled(" Columns (Fullscreen) ", theme.fg)) // Indicate fullscreen .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(columns_border_style), ) .row_highlight_style( Style::default() .add_modifier(Modifier::REVERSED) .fg(theme.highlight), ) .highlight_symbol(" > "); // Use the inside symbol f.render_stateful_widget(columns_table, inner_area, &mut add_table_state.column_table_state); return; // IMPORTANT: Stop rendering here for fullscreen mode } // --- Area Variable Declarations --- let top_info_area: Rect; let columns_area: Rect; let canvas_area: Rect; let add_button_area: Rect; let indexes_area: Rect; let links_area: Rect; let bottom_buttons_area: Rect; // --- Layout Decision --- if area.width >= NARROW_LAYOUT_THRESHOLD { // --- WIDE Layout (Based on first screenshot) --- let main_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(3), // Top Info (Profile/Table Name) - Increased to 3 lines Constraint::Min(10), // Middle Area (Columns | Right Pane) Constraint::Length(3), // Bottom Buttons ]) .split(inner_area); top_info_area = main_chunks[0]; let middle_area = main_chunks[1]; bottom_buttons_area = main_chunks[2]; // Split Middle Horizontally let middle_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(50), // Left: Columns Table Constraint::Percentage(50), // Right: Inputs etc. ]) .split(middle_area); columns_area = middle_chunks[0]; let right_pane_area = middle_chunks[1]; // Split Right Pane Vertically let right_pane_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(5), // Input Canvas Area Constraint::Length(3), // Add Button Area Constraint::Min(5), // Indexes & Links Area ]) .split(right_pane_area); canvas_area = right_pane_chunks[0]; add_button_area = right_pane_chunks[1]; let indexes_links_area = right_pane_chunks[2]; // Split Indexes/Links Horizontally let indexes_links_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(50), // Indexes Table Constraint::Percentage(50), // Links Table ]) .split(indexes_links_area); indexes_area = indexes_links_chunks[0]; links_area = indexes_links_chunks[1]; // --- Top Info Rendering (Wide - 2 lines) --- let profile_text = Paragraph::new(vec![ Line::from(Span::styled( format!("Profile: {}", add_table_state.profile_name), theme.fg, )), Line::from(Span::styled( format!("Table name: {}", add_table_state.table_name), theme.fg, )), ]) .block( Block::default() .borders(Borders::BOTTOM) .border_style(Style::default().fg(theme.secondary)), ); f.render_widget(profile_text, top_info_area); } else { // --- NARROW Layout (Based on second screenshot) --- let main_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(1), // Top: Profile & Table Name (Single Row) Constraint::Length(5), // Column Definition Input Canvas Area Constraint::Length(3), // Add Button Area Constraint::Min(5), // Columns Table Area Constraint::Min(5), // Indexes & Links Area Constraint::Length(3), // Bottom: Save/Cancel Buttons ]) .split(inner_area); top_info_area = main_chunks[0]; canvas_area = main_chunks[1]; add_button_area = main_chunks[2]; columns_area = main_chunks[3]; let indexes_links_area = main_chunks[4]; bottom_buttons_area = main_chunks[5]; // Split Indexes/Links Horizontally let indexes_links_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(50), // Indexes Table Constraint::Percentage(50), // Links Table ]) .split(indexes_links_area); indexes_area = indexes_links_chunks[0]; links_area = indexes_links_chunks[1]; // --- Top Info Rendering (Narrow - 1 line) --- let top_info_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(50), Constraint::Percentage(50), ]) .split(top_info_area); let profile_text = Paragraph::new(Span::styled( format!("Profile: {}", add_table_state.profile_name), theme.fg, )) .alignment(Alignment::Left); f.render_widget(profile_text, top_info_chunks[0]); let table_name_text = Paragraph::new(Span::styled( format!("Table: {}", add_table_state.table_name), theme.fg, )) .alignment(Alignment::Left); f.render_widget(table_name_text, top_info_chunks[1]); } // --- Common Widget Rendering (Uses calculated areas) --- // --- Columns Table Rendering --- let columns_focused = matches!(add_table_state.current_focus, AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable); let columns_border_style = if columns_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let column_rows: Vec> = add_table_state .columns .iter() .map(|col_def| { Row::new(vec![ Cell::from(if col_def.selected { "[*]" } else { "[ ]" }), Cell::from(col_def.name.clone()), Cell::from(col_def.data_type.clone()), ]) .style(Style::default().fg(theme.fg)) }) .collect(); let header_cells = ["Sel", "Name", "Type"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let header = Row::new(header_cells).height(1).bottom_margin(1); let columns_table = Table::new( column_rows, [ // Define constraints for 3 columns: Sel, Name, Type Constraint::Length(5), Constraint::Percentage(60), Constraint::Percentage(35), ], ) .header(header) .block( Block::default() .title(Span::styled(" Columns ", theme.fg)) .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(columns_border_style), ) .row_highlight_style( Style::default() .add_modifier(Modifier::REVERSED) .fg(theme.highlight), ) .highlight_symbol(" > "); f.render_stateful_widget( columns_table, columns_area, &mut add_table_state.column_table_state, ); // --- Canvas Rendering (Column Definition Input) --- let _active_field_rect = render_canvas( f, canvas_area, add_table_state, &add_table_state.fields(), &add_table_state.current_field(), &add_table_state.inputs(), theme, is_edit_mode && focus_on_canvas_inputs, highlight_state, ); // --- Button Style Helpers --- let get_button_style = |button_focus: AddTableFocus, current_focus| { // Only handles text style (FG + Bold) now, no BG let is_focused = current_focus == button_focus; let base_style = Style::default().fg(if is_focused { theme.highlight // Highlighted text color } else { theme.secondary // Normal text color }); if is_focused { base_style.add_modifier(Modifier::BOLD) } else { base_style } }; // Updated signature to accept bool and theme let get_button_border_style = |is_focused: bool, theme: &Theme| { if is_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) } }; // --- Add Button Rendering --- // Determine if the add button is focused let is_add_button_focused = add_table_state.current_focus == AddTableFocus::AddColumnButton; // Create the Add button Paragraph widget let add_button = Paragraph::new(" Add ") .style(get_button_style(AddTableFocus::AddColumnButton, add_table_state.current_focus)) // Use existing closure .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style(is_add_button_focused, theme)), // Pass bool and theme ); // Render the button in its designated area f.render_widget(add_button, add_button_area); // --- Indexes Table Rendering --- let indexes_focused = matches!(add_table_state.current_focus, AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable); let indexes_border_style = if indexes_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let index_rows: Vec> = add_table_state .indexes .iter() .map(|index_name| { Row::new(vec![Cell::from(index_name.clone())]) .style(Style::default().fg(theme.fg)) }) .collect(); let index_header_cells = ["Column Name"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let index_header = Row::new(index_header_cells).height(1).bottom_margin(1); let indexes_table = Table::new(index_rows, [Constraint::Percentage(100)]) .header(index_header) .block( Block::default() .title(Span::styled(" Indexes ", theme.fg)) .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(indexes_border_style), ) .row_highlight_style( Style::default() .add_modifier(Modifier::REVERSED) .fg(theme.highlight), ) .highlight_symbol(" > "); f.render_stateful_widget( indexes_table, indexes_area, &mut add_table_state.index_table_state, ); // --- Links Table Rendering --- let links_focused = matches!(add_table_state.current_focus, AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable); let links_border_style = if links_focused { Style::default().fg(theme.highlight) } else { Style::default().fg(theme.secondary) }; let link_rows: Vec> = add_table_state .links .iter() .map(|link_def| { Row::new(vec![ Cell::from(link_def.linked_table_name.clone()), Cell::from(if link_def.is_required { "[X]" } else { "[ ]" }), ]) .style(Style::default().fg(theme.fg)) }) .collect(); let link_header_cells = ["Linked Table", "Selected"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let link_header = Row::new(link_header_cells).height(1).bottom_margin(1); let links_table = Table::new(link_rows, [Constraint::Percentage(80), Constraint::Min(5)]) .header(link_header) .block( Block::default() .title(Span::styled(" Links ", theme.fg)) .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(links_border_style), ) .row_highlight_style( Style::default() .add_modifier(Modifier::REVERSED) .fg(theme.highlight), ) .highlight_symbol(" > "); f.render_stateful_widget( links_table, links_area, &mut add_table_state.link_table_state, ); // --- Save/Cancel Buttons Rendering --- let bottom_button_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(33), // Save Button Constraint::Percentage(34), // Delete Button Constraint::Percentage(33), // Cancel Button ]) .split(bottom_buttons_area); let save_button = Paragraph::new(" Save table ") .style(get_button_style( AddTableFocus::SaveButton, add_table_state.current_focus, )) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( add_table_state.current_focus == AddTableFocus::SaveButton, // Pass bool theme, )), ); f.render_widget(save_button, bottom_button_chunks[0]); let delete_button = Paragraph::new(" Delete Selected ") .style(get_button_style( AddTableFocus::DeleteSelectedButton, add_table_state.current_focus, )) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( add_table_state.current_focus == AddTableFocus::DeleteSelectedButton, // Pass bool theme, )), ); f.render_widget(delete_button, bottom_button_chunks[1]); let cancel_button = Paragraph::new(" Cancel ") .style(get_button_style( AddTableFocus::CancelButton, add_table_state.current_focus, )) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(get_button_border_style( add_table_state.current_focus == AddTableFocus::CancelButton, // Pass bool theme, )), ); f.render_widget(cancel_button, bottom_button_chunks[2]); // --- DIALOG --- // Render the dialog overlay if it's active if app_state.ui.dialog.dialog_show { // Use the passed-in app_state dialog::render_dialog( f, f.area(), // Render over the whole frame area theme, &app_state.ui.dialog.dialog_title, &app_state.ui.dialog.dialog_message, &app_state.ui.dialog.dialog_buttons, app_state.ui.dialog.dialog_active_button_index, app_state.ui.dialog.is_loading, ); } }