Skip to main content

datui_lib/widgets/
template_modal.rs

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