diff --git a/src/ui/app.rs b/src/ui/app.rs index 1ac7e4d..e1d1300 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -1200,6 +1200,41 @@ impl App { } _ => {} }, + WizardStep::ConfigureDates => match key.code { + KeyCode::Up | KeyCode::Char('k') => wizard.move_cursor(-1), + KeyCode::Down | KeyCode::Char('j') => wizard.move_cursor(1), + KeyCode::Char(' ') => wizard.toggle_date_component(), + KeyCode::Enter => wizard.advance(), + KeyCode::Esc => { + self.mode = AppMode::Normal; + self.wizard = None; + } + _ => {} + }, + WizardStep::DefineFormulas => { + if wizard.formula_editing { + match key.code { + KeyCode::Enter => wizard.confirm_formula(), + KeyCode::Esc => wizard.cancel_formula_edit(), + KeyCode::Backspace => wizard.pop_formula_char(), + KeyCode::Char(c) => wizard.push_formula_char(c), + _ => {} + } + } else { + match key.code { + KeyCode::Char('n') => wizard.start_formula_edit(), + KeyCode::Char('d') => wizard.delete_formula(), + KeyCode::Up | KeyCode::Char('k') => wizard.move_cursor(-1), + KeyCode::Down | KeyCode::Char('j') => wizard.move_cursor(1), + KeyCode::Enter => wizard.advance(), + KeyCode::Esc => { + self.mode = AppMode::Normal; + self.wizard = None; + } + _ => {} + } + } + } WizardStep::NameModel => match key.code { KeyCode::Char(c) => wizard.push_name_char(c), KeyCode::Backspace => wizard.pop_name_char(), diff --git a/src/ui/import_wizard_ui.rs b/src/ui/import_wizard_ui.rs index 9e19eda..edf7ccd 100644 --- a/src/ui/import_wizard_ui.rs +++ b/src/ui/import_wizard_ui.rs @@ -5,7 +5,7 @@ use ratatui::{ widgets::{Block, Borders, Clear, Widget}, }; -use crate::import::analyzer::FieldKind; +use crate::import::analyzer::{DateComponent, FieldKind}; use crate::import::wizard::{ImportWizard, WizardStep}; pub struct ImportWizardWidget<'a> { @@ -29,10 +29,12 @@ impl<'a> Widget for ImportWizardWidget<'a> { Clear.render(popup_area, buf); let title = match self.wizard.step { - WizardStep::Preview => " Import Wizard — Step 1: Preview ", - WizardStep::SelectArrayPath => " Import Wizard — Step 2: Select Array ", - WizardStep::ReviewProposals => " Import Wizard — Step 3: Review Fields ", - WizardStep::NameModel => " Import Wizard — Step 4: Name Model ", + WizardStep::Preview => " Import Wizard — Preview ", + WizardStep::SelectArrayPath => " Import Wizard — Select Array ", + WizardStep::ReviewProposals => " Import Wizard — Review Fields ", + WizardStep::ConfigureDates => " Import Wizard — Date Components ", + WizardStep::DefineFormulas => " Import Wizard — Formulas ", + WizardStep::NameModel => " Import Wizard — Name Model ", WizardStep::Done => " Import Wizard — Done ", }; @@ -158,6 +160,152 @@ impl<'a> Widget for ImportWizardWidget<'a> { Style::default().fg(Color::DarkGray), ); } + WizardStep::ConfigureDates => { + buf.set_string( + x, + y, + "Select date components to extract (Space toggle):", + Style::default().fg(Color::Yellow), + ); + y += 1; + + let tc_proposals = self.wizard.time_category_proposals(); + let mut item_idx = 0; + for proposal in &tc_proposals { + if y >= inner.y + inner.height - 2 { + break; + } + let fmt_str = proposal.date_format.as_deref().unwrap_or("?"); + let header = format!(" {} (format: {})", proposal.field, fmt_str); + buf.set_string( + x, + y, + truncate(&header, w), + Style::default() + .fg(Color::Magenta) + .add_modifier(Modifier::BOLD), + ); + y += 1; + + for component in &[ + DateComponent::Year, + DateComponent::Month, + DateComponent::Quarter, + ] { + if y >= inner.y + inner.height - 2 { + break; + } + let enabled = proposal.date_components.contains(component); + let check = if enabled { "[\u{2713}]" } else { "[ ]" }; + let label = match component { + DateComponent::Year => "Year", + DateComponent::Month => "Month", + DateComponent::Quarter => "Quarter", + }; + let row = format!(" {} {}", check, label); + let is_sel = item_idx == self.wizard.cursor; + let style = if is_sel { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else if enabled { + Style::default().fg(Color::Green) + } else { + Style::default().fg(Color::DarkGray) + }; + buf.set_string(x, y, truncate(&row, w), style); + y += 1; + item_idx += 1; + } + } + let hint_y = inner.y + inner.height - 1; + buf.set_string( + x, + hint_y, + "Space: toggle Enter: next Esc: cancel", + Style::default().fg(Color::DarkGray), + ); + } + WizardStep::DefineFormulas => { + buf.set_string( + x, + y, + "Define formulas (optional):", + Style::default().fg(Color::Yellow), + ); + y += 1; + + // Show existing formulas + if self.wizard.pipeline.formulas.is_empty() && !self.wizard.formula_editing { + buf.set_string( + x, + y, + " (no formulas yet)", + Style::default().fg(Color::DarkGray), + ); + y += 1; + } + for (i, formula) in self.wizard.pipeline.formulas.iter().enumerate() { + if y >= inner.y + inner.height - 5 { + break; + } + let is_sel = i == self.wizard.cursor && !self.wizard.formula_editing; + let style = if is_sel { + Style::default() + .fg(Color::Black) + .bg(Color::Cyan) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(Color::Green) + }; + buf.set_string(x, y, truncate(&format!(" {}", formula), w), style); + y += 1; + } + + // Formula input area + if self.wizard.formula_editing { + y += 1; + buf.set_string( + x, + y, + "Formula (e.g., Profit = Revenue - Cost):", + Style::default().fg(Color::Yellow), + ); + y += 1; + let input = format!("> {}\u{2588}", self.wizard.formula_buffer); + buf.set_string(x, y, truncate(&input, w), Style::default().fg(Color::Green)); + y += 1; + } + + // Sample formulas + let samples = self.wizard.sample_formulas(); + if !samples.is_empty() { + y += 1; + buf.set_string(x, y, "Examples:", Style::default().fg(Color::DarkGray)); + y += 1; + for sample in &samples { + if y >= inner.y + inner.height - 1 { + break; + } + buf.set_string( + x, + y, + truncate(&format!(" {}", sample), w), + Style::default().fg(Color::DarkGray), + ); + y += 1; + } + } + + let hint_y = inner.y + inner.height - 1; + let hint = if self.wizard.formula_editing { + "Enter: add Esc: cancel" + } else { + "n: new formula d: delete Enter: next Esc: cancel" + }; + buf.set_string(x, hint_y, hint, Style::default().fg(Color::DarkGray)); + } WizardStep::NameModel => { buf.set_string(x, y, "Model name:", Style::default().fg(Color::Yellow)); y += 1;