Skip to main content

datui_lib/widgets/
template_modal.rs

1use crate::template::Template;
2use crate::widgets::multiline_text_input::MultiLineTextInput;
3use crate::widgets::text_input::TextInput;
4use ratatui::widgets::TableState;
5
6#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
7pub enum TemplateModalMode {
8    #[default]
9    List,
10    Create,
11    Edit,
12}
13
14#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
15pub enum TemplateFocus {
16    #[default]
17    TemplateList,
18    CreateButton,
19    EditButton,
20    DeleteButton,
21    HelpButton,
22    SaveButton,
23    CancelButton,
24}
25
26#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
27pub enum CreateFocus {
28    #[default]
29    Name,
30    Description,
31    ExactPath,
32    RelativePath,
33    PathPattern,
34    FilenamePattern,
35    SchemaMatch,
36    SaveButton,
37    CancelButton,
38}
39
40#[derive(Default)]
41pub struct TemplateModal {
42    pub active: bool,
43    pub mode: TemplateModalMode,
44    pub focus: TemplateFocus,
45    pub create_focus: CreateFocus,
46    pub table_state: TableState,
47    pub templates: Vec<(Template, f64)>, // Templates with relevance scores
48    // Create/Edit mode fields
49    pub create_name_input: TextInput,
50    pub create_description_input: MultiLineTextInput,
51    pub create_exact_path_input: TextInput,
52    pub create_relative_path_input: TextInput,
53    pub create_path_pattern_input: TextInput,
54    pub create_filename_pattern_input: TextInput,
55    pub create_schema_match_enabled: bool,
56    pub editing_template_id: Option<String>, // ID of template being edited (None for create)
57    pub show_help: bool,                     // Show help modal
58    pub delete_confirm: bool,                // Show delete confirmation
59    pub delete_confirm_focus: bool, // true = Delete button, false = Cancel button (default)
60    pub name_error: Option<String>, // Error message for name validation
61    pub history_limit: usize,
62    pub show_score_details: bool, // Show score details popup
63}
64
65impl TemplateModal {
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    pub fn selected_template(&self) -> Option<&Template> {
71        self.table_state
72            .selected()
73            .and_then(|i| self.templates.get(i))
74            .map(|(template, _)| template)
75    }
76
77    pub fn next_focus(&mut self) {
78        if self.mode == TemplateModalMode::List {
79            // In list mode, focus is always on the template list
80            // No focus cycling needed
81        } else {
82            // In create mode, cycle through create focus areas
83            self.create_focus = match self.create_focus {
84                CreateFocus::Name => CreateFocus::Description,
85                CreateFocus::Description => CreateFocus::ExactPath,
86                CreateFocus::ExactPath => CreateFocus::RelativePath,
87                CreateFocus::RelativePath => CreateFocus::PathPattern,
88                CreateFocus::PathPattern => CreateFocus::FilenamePattern,
89                CreateFocus::FilenamePattern => CreateFocus::SchemaMatch,
90                CreateFocus::SchemaMatch => CreateFocus::SaveButton,
91                CreateFocus::SaveButton => CreateFocus::CancelButton,
92                CreateFocus::CancelButton => CreateFocus::Name,
93            };
94        }
95    }
96
97    pub fn prev_focus(&mut self) {
98        if self.mode == TemplateModalMode::List {
99            // In list mode, focus is always on the template list
100            // No focus cycling needed
101        } else {
102            // Reverse cycle in create mode
103            self.create_focus = match self.create_focus {
104                CreateFocus::Name => CreateFocus::CancelButton,
105                CreateFocus::Description => CreateFocus::Name,
106                CreateFocus::ExactPath => CreateFocus::Description,
107                CreateFocus::RelativePath => CreateFocus::ExactPath,
108                CreateFocus::PathPattern => CreateFocus::RelativePath,
109                CreateFocus::FilenamePattern => CreateFocus::PathPattern,
110                CreateFocus::SchemaMatch => CreateFocus::FilenamePattern,
111                CreateFocus::SaveButton => CreateFocus::SchemaMatch,
112                CreateFocus::CancelButton => CreateFocus::SaveButton,
113            };
114        }
115    }
116
117    pub fn enter_create_mode(&mut self, history_limit: usize, theme: &crate::config::Theme) {
118        self.mode = TemplateModalMode::Create;
119        self.create_focus = CreateFocus::Name;
120        self.editing_template_id = None;
121        self.name_error = None;
122        self.history_limit = history_limit;
123        // Initialize text input widgets
124        self.create_name_input = TextInput::new()
125            .with_history_limit(history_limit)
126            .with_theme(theme);
127        self.create_description_input = MultiLineTextInput::new()
128            .with_history_limit(history_limit)
129            .with_theme(theme);
130        self.create_exact_path_input = TextInput::new()
131            .with_history_limit(history_limit)
132            .with_theme(theme);
133        self.create_relative_path_input = TextInput::new()
134            .with_history_limit(history_limit)
135            .with_theme(theme);
136        self.create_path_pattern_input = TextInput::new()
137            .with_history_limit(history_limit)
138            .with_theme(theme);
139        self.create_filename_pattern_input = TextInput::new()
140            .with_history_limit(history_limit)
141            .with_theme(theme);
142        // Clear all fields
143        self.create_name_input.clear();
144        self.create_description_input.clear();
145        self.create_exact_path_input.clear();
146        self.create_relative_path_input.clear();
147        self.create_path_pattern_input.clear();
148        self.create_filename_pattern_input.clear();
149        self.create_schema_match_enabled = false;
150    }
151
152    pub fn enter_edit_mode(
153        &mut self,
154        template: &Template,
155        history_limit: usize,
156        theme: &crate::config::Theme,
157    ) {
158        self.mode = TemplateModalMode::Edit;
159        self.create_focus = CreateFocus::Name;
160        self.editing_template_id = Some(template.id.clone());
161        self.history_limit = history_limit;
162        // Initialize text input widgets
163        self.create_name_input = TextInput::new()
164            .with_history_limit(history_limit)
165            .with_theme(theme);
166        self.create_description_input = MultiLineTextInput::new()
167            .with_history_limit(history_limit)
168            .with_theme(theme);
169        self.create_exact_path_input = TextInput::new()
170            .with_history_limit(history_limit)
171            .with_theme(theme);
172        self.create_relative_path_input = TextInput::new()
173            .with_history_limit(history_limit)
174            .with_theme(theme);
175        self.create_path_pattern_input = TextInput::new()
176            .with_history_limit(history_limit)
177            .with_theme(theme);
178        self.create_filename_pattern_input = TextInput::new()
179            .with_history_limit(history_limit)
180            .with_theme(theme);
181        // Populate fields from template
182        self.create_name_input.value = template.name.clone();
183        self.create_name_input.cursor = self.create_name_input.value.chars().count();
184        self.create_description_input.value = template.description.clone().unwrap_or_default();
185        self.create_description_input.cursor = self.create_description_input.value.chars().count();
186        self.create_description_input.update_line_col_from_cursor();
187        self.create_exact_path_input.value = template
188            .match_criteria
189            .exact_path
190            .as_ref()
191            .map(|p| p.to_string_lossy().to_string())
192            .unwrap_or_default();
193        self.create_exact_path_input.cursor = self.create_exact_path_input.value.chars().count();
194        self.create_relative_path_input.value = template
195            .match_criteria
196            .relative_path
197            .clone()
198            .unwrap_or_default();
199        self.create_relative_path_input.cursor =
200            self.create_relative_path_input.value.chars().count();
201        self.create_path_pattern_input.value = template
202            .match_criteria
203            .path_pattern
204            .clone()
205            .unwrap_or_default();
206        self.create_path_pattern_input.cursor =
207            self.create_path_pattern_input.value.chars().count();
208        self.create_filename_pattern_input.value = template
209            .match_criteria
210            .filename_pattern
211            .clone()
212            .unwrap_or_default();
213        self.create_filename_pattern_input.cursor =
214            self.create_filename_pattern_input.value.chars().count();
215        self.create_schema_match_enabled = template.match_criteria.schema_columns.is_some();
216    }
217
218    pub fn exit_create_mode(&mut self) {
219        self.mode = TemplateModalMode::List;
220        self.focus = TemplateFocus::TemplateList;
221        self.editing_template_id = None;
222    }
223}