use egui; use crate::state::{BrowserState, ConfirmAction, ImportMode}; use super::super::{theme, widgets}; /// Draw the post-import error review screen. pub fn draw_review_errors(ui: &mut egui::Ui, state: &mut BrowserState) { if !matches!(state.import_mode, ImportMode::ReviewErrors) { return; } egui::CentralPanel::default().show_inside(ui, |ui| { ui.heading("Import Summary"); ui.add_space(theme::space::LG); let analysis_count = state.analysis_errors.len(); let import_count = state.import_file_errors.len(); // Analysis errors: files in the store that couldn't be analyzed. // M-13: explanatory copy distinguishes this category from the // import-error category below (recoverable here, informational there). if analysis_count > 0 { ui.label( egui::RichText::new(format!( "{analysis_count} file{} failed analysis", if analysis_count == 1 { "" } else { "s" }, )) .strong() .color(theme::accent_red()), ); ui.label( egui::RichText::new( "These files are in the library but couldn't be analysed. \ You can remove them, ignore them, or re-analyse later.", ) .small() .color(theme::text_muted()), ); ui.add_space(theme::space::SM); let mut remove_request: Option<(usize, String)> = None; egui::ScrollArea::vertical() .id_salt("analysis_errors") .max_height(200.0) .show(ui, |ui| { for (i, err) in state.analysis_errors.iter().enumerate() { ui.horizontal(|ui| { // Row labels rendered in primary text. The section // heading carries the red emphasis (M-13); per-row // red on top reads as a wall of failure rather // than a list of files to triage. ui.label(&err.name); ui.label( egui::RichText::new(&err.error) .small() .color(theme::text_secondary()), ); if widgets::danger_small_button(ui, "Remove").clicked() { remove_request = Some((i, err.name.clone())); } }); } }); // Route per-row Remove through the confirm dialog rather than // deleting on click (C-2). Detail line names the specific file. if let Some((idx, name)) = remove_request { state.pending_confirm = Some(ConfirmAction::RemoveFailedSamples { single_index: Some(idx), count: 1, name: Some(name), }); } ui.add_space(theme::space::MD); } // Import errors: files that failed before entering the store // (informational only — nothing to remediate from this screen). M-13. if import_count > 0 { ui.label( egui::RichText::new(format!( "{import_count} file{} failed to import", if import_count == 1 { "" } else { "s" }, )) .strong() .color(theme::accent_red()), ); ui.label( egui::RichText::new( "These files weren't imported. Re-running the import \ is the only way to retry \u{2014} duplicates will be skipped.", ) .small() .color(theme::text_muted()), ); ui.add_space(theme::space::SM); egui::ScrollArea::vertical() .id_salt("import_errors") .max_height(200.0) .show(ui, |ui| { for err in &state.import_file_errors { ui.horizontal(|ui| { ui.label(&err.path); ui.label( egui::RichText::new(&err.error) .small() .color(theme::text_secondary()), ); }); } }); ui.add_space(theme::space::MD); } ui.add_space(theme::space::MD); ui.horizontal(|ui| { if widgets::primary_button(ui, "Keep All").clicked() { state.dismiss_import_errors(); } // Remove All Failed routes through the confirm dialog (C-2). The // detail line surfaces the count so the user can see the blast // radius before committing to permanent deletion. if analysis_count > 0 && widgets::danger_button(ui, "Remove All Failed").clicked() { state.pending_confirm = Some(ConfirmAction::RemoveFailedSamples { single_index: None, count: analysis_count, name: None, }); } }); }); }