423 lines
15 KiB
Rust
423 lines
15 KiB
Rust
// 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;
|
|
|
|
/// 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);
|
|
|
|
// --- 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(60), // Left: Columns Table
|
|
Constraint::Percentage(40), // 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 =
|
|
add_table_state.current_focus == AddTableFocus::ColumnsTable;
|
|
let columns_border_style = if columns_focused {
|
|
Style::default().fg(theme.highlight)
|
|
} else {
|
|
Style::default().fg(theme.secondary)
|
|
};
|
|
let column_rows: Vec<Row<'_>> = add_table_state
|
|
.columns
|
|
.iter()
|
|
.map(|col_def| {
|
|
Row::new(vec![
|
|
Cell::from(col_def.name.clone()),
|
|
Cell::from(col_def.data_type.clone()),
|
|
])
|
|
.style(Style::default().fg(theme.fg))
|
|
})
|
|
.collect();
|
|
// Use different headers/constraints based on layout? For now, keep consistent.
|
|
let header_cells = ["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::Percentage(60), Constraint::Percentage(40)],
|
|
)
|
|
.header(header)
|
|
.block(
|
|
Block::default()
|
|
.title(Span::styled(" Columns ", theme.fg))
|
|
.title_alignment(Alignment::Center)
|
|
.borders(Borders::ALL) // Use ALL borders for consistency
|
|
.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| {
|
|
let is_focused = current_focus == button_focus;
|
|
let base_style = Style::default().fg(if is_focused {
|
|
theme.bg // Reversed text color
|
|
} else {
|
|
theme.secondary // Normal text color
|
|
});
|
|
if is_focused {
|
|
base_style
|
|
.add_modifier(Modifier::BOLD)
|
|
.bg(theme.highlight) // Reversed background
|
|
} else {
|
|
base_style
|
|
}
|
|
};
|
|
let get_button_border_style = |button_focus: AddTableFocus, current_focus| {
|
|
if current_focus == button_focus {
|
|
Style::default().fg(theme.highlight)
|
|
} else {
|
|
Style::default().fg(theme.secondary)
|
|
}
|
|
};
|
|
|
|
// --- Add Button Rendering ---
|
|
let add_button = Paragraph::new(" Add ")
|
|
.style(get_button_style(
|
|
AddTableFocus::AddColumnButton,
|
|
add_table_state.current_focus,
|
|
))
|
|
.alignment(Alignment::Center)
|
|
.block(
|
|
Block::default()
|
|
.borders(Borders::ALL)
|
|
.border_type(BorderType::Rounded)
|
|
.border_style(get_button_border_style(
|
|
AddTableFocus::AddColumnButton,
|
|
add_table_state.current_focus,
|
|
)),
|
|
);
|
|
f.render_widget(add_button, add_button_area); // Render into the calculated area
|
|
|
|
// --- Indexes Table Rendering ---
|
|
let indexes_focused =
|
|
add_table_state.current_focus == AddTableFocus::IndexesTable;
|
|
let indexes_border_style = if indexes_focused {
|
|
Style::default().fg(theme.highlight)
|
|
} else {
|
|
Style::default().fg(theme.secondary)
|
|
};
|
|
let index_rows: Vec<Row<'_>> = 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 = add_table_state.current_focus == AddTableFocus::LinksTable;
|
|
let links_border_style = if links_focused {
|
|
Style::default().fg(theme.highlight)
|
|
} else {
|
|
Style::default().fg(theme.secondary)
|
|
};
|
|
let link_rows: Vec<Row<'_>> = 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", "Req"]
|
|
.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(50), // Save Button
|
|
Constraint::Percentage(50), // 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(
|
|
AddTableFocus::SaveButton,
|
|
add_table_state.current_focus,
|
|
)),
|
|
);
|
|
f.render_widget(save_button, bottom_button_chunks[0]);
|
|
|
|
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(
|
|
AddTableFocus::CancelButton,
|
|
add_table_state.current_focus,
|
|
)),
|
|
);
|
|
f.render_widget(cancel_button, bottom_button_chunks[1]);
|
|
}
|