211 lines
6.2 KiB
Rust
211 lines
6.2 KiB
Rust
// src/state/pages/add_table.rs
|
||
|
||
use canvas::{DataProvider, AppMode};
|
||
use ratatui::widgets::TableState;
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub struct ColumnDefinition {
|
||
pub name: String,
|
||
pub data_type: String,
|
||
pub selected: bool,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||
pub struct IndexDefinition {
|
||
pub name: String,
|
||
pub selected: bool,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||
pub struct LinkDefinition {
|
||
pub linked_table_name: String,
|
||
pub is_required: bool,
|
||
pub selected: bool,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||
pub enum AddTableFocus {
|
||
#[default]
|
||
InputTableName, // Field 0 for CanvasState
|
||
InputColumnName, // Field 1 for CanvasState
|
||
InputColumnType, // Field 2 for CanvasState
|
||
AddColumnButton,
|
||
// Result Tables
|
||
ColumnsTable,
|
||
IndexesTable,
|
||
LinksTable,
|
||
// Inside Tables (Scrolling Focus)
|
||
InsideColumnsTable,
|
||
InsideIndexesTable,
|
||
InsideLinksTable,
|
||
// Buttons
|
||
SaveButton,
|
||
DeleteSelectedButton,
|
||
CancelButton,
|
||
}
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct AddTableState {
|
||
pub profile_name: String,
|
||
pub table_name: String,
|
||
pub table_name_input: String,
|
||
pub column_name_input: String,
|
||
pub column_type_input: String,
|
||
pub columns: Vec<ColumnDefinition>,
|
||
pub indexes: Vec<IndexDefinition>,
|
||
pub links: Vec<LinkDefinition>,
|
||
pub current_focus: AddTableFocus,
|
||
pub last_canvas_field: usize,
|
||
pub column_table_state: TableState,
|
||
pub index_table_state: TableState,
|
||
pub link_table_state: TableState,
|
||
pub table_name_cursor_pos: usize,
|
||
pub column_name_cursor_pos: usize,
|
||
pub column_type_cursor_pos: usize,
|
||
pub has_unsaved_changes: bool,
|
||
pub app_mode: canvas::AppMode,
|
||
}
|
||
|
||
impl Default for AddTableState {
|
||
fn default() -> Self {
|
||
AddTableState {
|
||
profile_name: "default".to_string(),
|
||
table_name: String::new(),
|
||
table_name_input: String::new(),
|
||
column_name_input: String::new(),
|
||
column_type_input: String::new(),
|
||
columns: Vec::new(),
|
||
indexes: Vec::new(),
|
||
links: Vec::new(),
|
||
current_focus: AddTableFocus::InputTableName,
|
||
last_canvas_field: 2,
|
||
column_table_state: TableState::default(),
|
||
index_table_state: TableState::default(),
|
||
link_table_state: TableState::default(),
|
||
table_name_cursor_pos: 0,
|
||
column_name_cursor_pos: 0,
|
||
column_type_cursor_pos: 0,
|
||
has_unsaved_changes: false,
|
||
app_mode: canvas::AppMode::Edit,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl AddTableState {
|
||
pub const INPUT_FIELD_COUNT: usize = 3;
|
||
|
||
/// Helper method to add a column from current inputs
|
||
pub fn add_column_from_inputs(&mut self) -> Option<String> {
|
||
if self.column_name_input.trim().is_empty() || self.column_type_input.trim().is_empty() {
|
||
return Some("Both column name and type are required".to_string());
|
||
}
|
||
|
||
// Check for duplicate column names
|
||
if self.columns.iter().any(|col| col.name == self.column_name_input.trim()) {
|
||
return Some("Column name already exists".to_string());
|
||
}
|
||
|
||
// Add the column
|
||
self.columns.push(ColumnDefinition {
|
||
name: self.column_name_input.trim().to_string(),
|
||
data_type: self.column_type_input.trim().to_string(),
|
||
selected: false,
|
||
});
|
||
|
||
// Clear inputs and reset focus to column name for next entry
|
||
self.column_name_input.clear();
|
||
self.column_type_input.clear();
|
||
self.column_name_cursor_pos = 0;
|
||
self.column_type_cursor_pos = 0;
|
||
self.current_focus = AddTableFocus::InputColumnName;
|
||
self.last_canvas_field = 1;
|
||
self.has_unsaved_changes = true;
|
||
|
||
Some(format!("Column '{}' added successfully", self.columns.last().unwrap().name))
|
||
}
|
||
|
||
/// Helper method to delete selected items
|
||
pub fn delete_selected_items(&mut self) -> Option<String> {
|
||
let mut deleted_items = Vec::new();
|
||
|
||
// Remove selected columns
|
||
let initial_column_count = self.columns.len();
|
||
self.columns.retain(|col| {
|
||
if col.selected {
|
||
deleted_items.push(format!("column '{}'", col.name));
|
||
false
|
||
} else {
|
||
true
|
||
}
|
||
});
|
||
|
||
// Remove selected indexes
|
||
let initial_index_count = self.indexes.len();
|
||
self.indexes.retain(|idx| {
|
||
if idx.selected {
|
||
deleted_items.push(format!("index '{}'", idx.name));
|
||
false
|
||
} else {
|
||
true
|
||
}
|
||
});
|
||
|
||
// Remove selected links
|
||
let initial_link_count = self.links.len();
|
||
self.links.retain(|link| {
|
||
if link.selected {
|
||
deleted_items.push(format!("link to '{}'", link.linked_table_name));
|
||
false
|
||
} else {
|
||
true
|
||
}
|
||
});
|
||
|
||
if deleted_items.is_empty() {
|
||
Some("No items selected for deletion".to_string())
|
||
} else {
|
||
self.has_unsaved_changes = true;
|
||
Some(format!("Deleted: {}", deleted_items.join(", ")))
|
||
}
|
||
}
|
||
}
|
||
|
||
impl DataProvider for AddTableState {
|
||
fn field_count(&self) -> usize {
|
||
3 // Table name, Column name, Column type
|
||
}
|
||
|
||
fn field_name(&self, index: usize) -> &str {
|
||
match index {
|
||
0 => "Table name",
|
||
1 => "Name",
|
||
2 => "Type",
|
||
_ => "",
|
||
}
|
||
}
|
||
|
||
fn field_value(&self, index: usize) -> &str {
|
||
match index {
|
||
0 => &self.table_name_input,
|
||
1 => &self.column_name_input,
|
||
2 => &self.column_type_input,
|
||
_ => "",
|
||
}
|
||
}
|
||
|
||
fn set_field_value(&mut self, index: usize, value: String) {
|
||
match index {
|
||
0 => self.table_name_input = value,
|
||
1 => self.column_name_input = value,
|
||
2 => self.column_type_input = value,
|
||
_ => {}
|
||
}
|
||
self.has_unsaved_changes = true;
|
||
}
|
||
|
||
fn supports_suggestions(&self, _field_index: usize) -> bool {
|
||
false // AddTableState doesn’t use suggestions
|
||
}
|
||
}
|