use crate::application::usecases::layouts::{ exportar_layout_json, importar_layout_json, salvar_layout, }; use crate::domain::entities::layout::{Layout, LayoutJson, TipoArquivo}; use crate::domain::errors::ErroLayout; use crate::ui::app::{AcaoModal, App, EstadoApp}; use egui::{Context, Ui}; /// Renderiza a tela de gerenciamento de layouts. pub fn renderizar(ui: &mut Ui, ctx: &Context, app: &mut App) { ui.heading("Gerenciar Layouts"); ui.add_space(8.0); ui.horizontal(|ui| { if ui.button("< Voltar").clicked() { app.estado = EstadoApp::Importando; } }); ui.add_space(12.0); ui.separator(); // Salvar layout atual ui.add_space(8.0); ui.group(|ui| { ui.label("Salvar Layout Atual"); ui.horizontal(|ui| { ui.label("Nome:"); ui.text_edit_singleline(&mut app.nome_layout_atual); if ui.button("💾 Salvar").clicked() { salvar_layout_atual(app); } }); }); ui.add_space(12.0); ui.separator(); ui.add_space(8.0); // Layouts CSV let layouts_csv: Vec<_> = app .layouts_salvos .iter() .filter(|l| l.tipo() == TipoArquivo::Csv) .cloned() .collect(); let layouts_xlsx: Vec<_> = app .layouts_salvos .iter() .filter(|l| l.tipo() == TipoArquivo::Xlsx) .cloned() .collect(); egui::ScrollArea::vertical().show(ui, |ui| { renderizar_secao_layouts(ui, ctx, app, "Layouts CSV", &layouts_csv); ui.add_space(12.0); renderizar_secao_layouts(ui, ctx, app, "Layouts XLSX", &layouts_xlsx); ui.add_space(16.0); ui.separator(); ui.add_space(8.0); // Importar de JSON ui.horizontal(|ui| { ui.label("Importar layout de arquivo JSON:"); if ui.button("📥 Importar JSON").clicked() { if let Some(caminho) = rfd::FileDialog::new() .add_filter("JSON", &["json"]) .pick_file() { match std::fs::read_to_string(&caminho) { Ok(conteudo) => importar_json(app, &conteudo), Err(e) => { app.exibir_erro(format!("Erro ao ler arquivo JSON: {}", e)); } } } } }); }); } fn renderizar_secao_layouts( ui: &mut Ui, _ctx: &Context, app: &mut App, titulo: &str, layouts: &[Layout], ) { ui.label(egui::RichText::new(titulo).strong()); ui.add_space(4.0); if layouts.is_empty() { ui.label("(nenhum layout salvo)"); return; } for layout in layouts { ui.horizontal(|ui| { ui.label(layout.nome()); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { // Excluir if let Some(id) = layout.id() { if ui.button("🗑 Excluir").clicked() { app.exibir_confirmacao( "Confirmar exclusão", format!("Deseja excluir o layout '{}'?", layout.nome()), AcaoModal::ConfirmarExclusaoLayout(id), ); } // Exportar if ui.button("📤 Exportar JSON").clicked() { match exportar_layout_json(layout) { Ok((conteudo, nome_sugerido)) => { if let Some(caminho) = rfd::FileDialog::new() .set_file_name(&nome_sugerido) .add_filter("JSON", &["json"]) .save_file() { if let Err(e) = std::fs::write(&caminho, &conteudo) { app.exibir_erro(format!("Erro ao salvar JSON: {}", e)); } } } Err(e) => { app.exibir_erro(format!("Erro ao exportar layout: {}", e)); } } } // Carregar if ui.button("📂 Carregar").clicked() { aplicar_layout(app, layout); app.nome_layout_atual = layout.nome().to_string(); app.estado = EstadoApp::ConfigurandoColunas; } } }); }); } } fn salvar_layout_atual(app: &mut App) { if app.nome_layout_atual.trim().is_empty() { app.exibir_aviso("Nome inválido", "Informe um nome para o layout."); return; } let layout = match app.tipo_arquivo_atual.clone() { TipoArquivo::Csv => Layout::Csv { id: None, nome: app.nome_layout_atual.trim().to_string(), config: app.layout_csv_atual.clone(), }, TipoArquivo::Xlsx => Layout::Xlsx { id: None, nome: app.nome_layout_atual.trim().to_string(), config: app.layout_xlsx_atual.clone(), }, }; if let Some(conn) = &app.conn { match salvar_layout(conn, &layout) { Ok(_) => { app.recarregar_layouts(); app.exibir_aviso("Sucesso", "Layout salvo com sucesso."); } Err(ErroLayout::NomeConflitante(nome)) => { let id_existente = app .layouts_salvos .iter() .find(|l| l.nome() == nome) .and_then(|l| l.id()); if let Some(id) = id_existente { let layout_com_id = match app.tipo_arquivo_atual.clone() { TipoArquivo::Csv => Layout::Csv { id: Some(id), nome: nome.clone(), config: app.layout_csv_atual.clone(), }, TipoArquivo::Xlsx => Layout::Xlsx { id: Some(id), nome: nome.clone(), config: app.layout_xlsx_atual.clone(), }, }; app.exibir_confirmacao( "Conflito de nome", format!( "Já existe um layout com o nome '{}'. Deseja sobrescrever?", nome ), AcaoModal::SobrescreverLayout(layout_com_id), ); } } Err(e) => { app.exibir_erro(format!("Erro ao salvar layout: {}", e)); } } } } fn importar_json(app: &mut App, conteudo: &str) { if let Some(conn) = &app.conn { match importar_layout_json(conn, conteudo, false, None) { Ok(_) => { app.recarregar_layouts(); app.exibir_aviso("Sucesso", "Layout importado com sucesso."); } Err(ErroLayout::NomeConflitante(nome)) => { // Recriar o layout parseado para passá-lo no modal de confirmação. // O JSON já foi validado pela chamada acima, então o parse aqui não falha. let parsed = serde_json::from_str::(conteudo) .ok() .and_then(|json_repr| Layout::try_from(json_repr).ok()); let id_existente = app .layouts_salvos .iter() .find(|l| l.nome() == nome) .and_then(|l| l.id()); match (parsed, id_existente) { (Some(mut layout), Some(id)) => { match &mut layout { Layout::Csv { id: i, .. } => *i = Some(id), Layout::Xlsx { id: i, .. } => *i = Some(id), } app.exibir_confirmacao( "Conflito de nome", format!( "Já existe um layout com o nome '{}'. Deseja sobrescrever?", nome ), AcaoModal::SobrescreverLayout(layout), ); } _ => { app.exibir_erro(format!( "Conflito de nome: layout '{}' já existe.", nome )); } } } Err(e) => { app.exibir_erro(format!("Erro ao importar layout: {}", e)); } } } } fn aplicar_layout(app: &mut App, layout: &Layout) { match layout { Layout::Csv { config, .. } => { app.layout_csv_atual = config.clone(); app.tipo_arquivo_atual = TipoArquivo::Csv; } Layout::Xlsx { config, .. } => { app.layout_xlsx_atual = config.clone(); app.tipo_arquivo_atual = TipoArquivo::Xlsx; } } }