feat: adiciona novas dependências, implementa reimportação de arquivos e melhorias na interface

This commit is contained in:
FelipeCN
2026-03-04 09:19:47 -03:00
parent 2a21138bf8
commit 12b8501ffb
18 changed files with 148 additions and 197 deletions
Generated
+20
View File
@@ -857,6 +857,7 @@ dependencies = [
"egui", "egui",
"encoding_rs", "encoding_rs",
"genpdf", "genpdf",
"image",
"regex", "regex",
"rfd", "rfd",
"rusqlite", "rusqlite",
@@ -865,6 +866,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.18", "thiserror 2.0.18",
"winres",
] ]
[[package]] [[package]]
@@ -3922,6 +3924,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.5+spec-1.1.0" version = "0.7.5+spec-1.1.0"
@@ -5007,6 +5018,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winres"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
dependencies = [
"toml",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.51.0" version = "0.51.0"
+4
View File
@@ -20,3 +20,7 @@ dirs = "5"
thiserror = "2" thiserror = "2"
regex = "1" regex = "1"
rfd = "0.15" rfd = "0.15"
image = { version = "0.25", default-features = false, features = ["ico"] }
[build-dependencies]
winres = "0.1"
+1 -1
View File
@@ -84,7 +84,7 @@ Verificar se já existe no codigo, pois na tela de configuração do Layout ele
--- ---
### F-05 — Recarregar Arquivo Sem Reconfigurar (Sim bem necessario) ### F-05 — Recarregar Arquivo Sem Reconfigurar (Implementado)
**Problema:** Quando o usuário corrige o arquivo fonte e quer re-verificar, precisa navegar todo o fluxo novamente (selecionar arquivo → configurar colunas → analisar). **Problema:** Quando o usuário corrige o arquivo fonte e quer re-verificar, precisa navegar todo o fluxo novamente (selecionar arquivo → configurar colunas → analisar).
-113
View File
@@ -1,113 +0,0 @@
# Sugestões de Melhoria — Fluxo da Interface
**Data:** 02/03/2026
---
## 1. Indicador de progresso das etapas
O fluxo possui 34 passos bem definidos (`Importando → ConfigurandoColunas → ExibindoResultado`), mas não há nenhum indicador visual de onde o usuário está.
**Sugestão:** Adicionar um breadcrumb simples no topo de todas as telas:
```
① Arquivo ② Colunas ③ Resultado
```
O passo atual ficaria destacado. Isso orienta o usuário sobre o que falta sem exigir nenhuma regra de negócio adicional.
---
## 2. Unificar importação e configuração de colunas em um único painel
Atualmente o caminho é:
1. Selecionar arquivo → clicar **"▶ Configurar Colunas"**
2. Configurar colunas → clicar **"▶ Importar e Analisar"**
São 3 ações separadas para chegar à análise.
**Sugestão:** Mover as configurações CSV/XLSX para a mesma tela de importação como uma seção expansível ("Configurações avançadas"), deixando o botão principal como **"▶ Importar e Analisar"** direto.
---
## 3. Pré-visualização das primeiras linhas do arquivo
Após selecionar o arquivo, o usuário precisa alternar entre a aplicação e a planilha para descobrir quais índices correspondem a cada campo.
**Sugestão:** Exibir as primeiras 35 linhas do arquivo em uma tabela simples logo após a seleção, para que o usuário identifique visualmente o índice de cada coluna sem sair do app.
---
## 4. Confirmação ao clicar em "Nova Análise"
O botão **"< Nova Análise"** em `resultado.rs` executa `app.notas_importadas.clear()` imediatamente, sem nenhum modal de confirmação. Um clique acidental descarta o resultado atual sem aviso.
**Sugestão:** Exibir modal de confirmação com a mensagem:
> "Deseja iniciar uma nova análise? O resultado atual será descartado."
Botões: **Confirmar** | **Cancelar**
---
## 5. Reorganizar seções do resultado
A ordem atual das seções em `resultado.rs` é:
```
Totais → Faltantes → Duplicatas
```
O objetivo principal do software é detectar faltantes e duplicatas; os totais são informação complementar.
**Sugestão:** Inverter para:
```
Faltantes → Duplicatas → Totais
```
Isso coloca a informação mais relevante no topo da tela.
---
## 6. Seletor de layout também na tela de configuração de colunas
O dropdown de layouts está disponível apenas em `import.rs`. O usuário frequentemente percebe que precisa de um layout diferente **depois** de visitar a tela de configuração e ver os campos.
**Sugestão:** Duplicar o seletor de layout no topo de `configuracao_colunas.rs`, evitando que o usuário volte à tela anterior só para trocar o layout.
---
## 7. Botão "Salvar como layout..." na tela de configuração
Para salvar um layout atualmente o usuário precisa navegar para `GerenciandoLayouts`. Esse desvio quebra o fluxo principal.
**Sugestão:** Adicionar um botão **"💾 Salvar como layout..."** diretamente em `configuracao_colunas.rs` que abre um modal simples pedindo apenas o nome do layout. Internamente, chama o mesmo use case `salvar_layout`.
---
## 8. Feedback visual durante a análise
A transição `ConfigurandoColunas → ExibindoResultado` pode demorar com arquivos grandes (até 100k registros, conforme RNF03). Atualmente o app não exibe nenhum sinal enquanto processa, parecendo travado.
**Sugestão:** Adicionar o estado `Analisando` no `EstadoApp` (já previsto no IMPLEMENTACAO.md mas não implementado) e exibir uma mensagem simples tipo:
```
⏳ Analisando... aguarde.
```
Mesmo sem progresso percentual, já elimina a percepção de travamento.
---
## Resumo de Impacto
| # | Sugestão | Impacto UX | Esforço estimado |
|---|----------|------------|-----------------|
| 1 | Breadcrumb de etapas | Médio | Baixo |
| 2 | Unificar importação + configuração | Alto | Médio |
| 3 | Pré-visualização do arquivo | Alto | Médio |
| 4 | Confirmação em "Nova Análise" | Baixo | Baixo |
| 5 | Reordenar seções do resultado | Médio | Baixo |
| 6 | Seletor de layout em configuração | Médio | Baixo |
| 7 | Salvar layout na tela de configuração | Médio | Baixo |
| 8 | Feedback durante análise | Alto | Baixo |
+7
View File
@@ -0,0 +1,7 @@
fn main() {
if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
let mut res = winres::WindowsResource::new();
res.set_icon("icon.ico");
res.compile().unwrap();
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

+8 -2
View File
@@ -6,7 +6,7 @@ use crate::domain::{
}, },
services::{ services::{
detector_duplicidade::duplicidades_por_serie, detector_duplicidade::duplicidades_por_serie,
detector_sequencia::{LIMITE_FALTANTES, calcular_intervalo, detectar_faltantes}, detector_sequencia::{calcular_intervalo, detectar_faltantes, LIMITE_FALTANTES},
}, },
}; };
use rust_decimal::Decimal; use rust_decimal::Decimal;
@@ -107,7 +107,13 @@ mod tests {
} }
fn nota_com_tipo(numero: u64, serie: &str, tipo: &str) -> Nota { fn nota_com_tipo(numero: u64, serie: &str, tipo: &str) -> Nota {
Nota::new(numero, serie.to_string(), Some(tipo.to_string()), None, None) Nota::new(
numero,
serie.to_string(),
Some(tipo.to_string()),
None,
None,
)
} }
#[test] #[test]
+1 -1
View File
@@ -1,5 +1,5 @@
pub mod chave_serie;
pub mod layout; pub mod layout;
pub mod nota; pub mod nota;
pub mod resultado_analise; pub mod resultado_analise;
pub mod serie; pub mod serie;
pub mod chave_serie;
+19 -4
View File
@@ -50,7 +50,13 @@ mod tests {
} }
fn nota_com_tipo(numero: u64, serie: &str, tipo: &str) -> Nota { fn nota_com_tipo(numero: u64, serie: &str, tipo: &str) -> Nota {
Nota::new(numero, serie.to_string(), Some(tipo.to_string()), None, None) Nota::new(
numero,
serie.to_string(),
Some(tipo.to_string()),
None,
None,
)
} }
#[test] #[test]
@@ -84,16 +90,25 @@ mod tests {
#[test] #[test]
fn mesmo_numero_serie_tipos_diferentes_nao_e_duplicata() { fn mesmo_numero_serie_tipos_diferentes_nao_e_duplicata() {
let notas = vec![nota_com_tipo(1, "001", "NFE"), nota_com_tipo(1, "001", "NFCE")]; let notas = vec![
nota_com_tipo(1, "001", "NFE"),
nota_com_tipo(1, "001", "NFCE"),
];
let dup = detectar_duplicidades(&notas); let dup = detectar_duplicidades(&notas);
assert!(dup.is_empty()); assert!(dup.is_empty());
} }
#[test] #[test]
fn mesmo_numero_serie_tipo_igual_e_duplicata() { fn mesmo_numero_serie_tipo_igual_e_duplicata() {
let notas = vec![nota_com_tipo(1, "001", "NFE"), nota_com_tipo(1, "001", "NFE")]; let notas = vec![
nota_com_tipo(1, "001", "NFE"),
nota_com_tipo(1, "001", "NFE"),
];
let dup = detectar_duplicidades(&notas); let dup = detectar_duplicidades(&notas);
assert_eq!(dup.get(&(1, "001".to_string(), Some("NFE".to_string()))), Some(&2)); assert_eq!(
dup.get(&(1, "001".to_string(), Some("NFE".to_string()))),
Some(&2)
);
} }
#[test] #[test]
+6 -10
View File
@@ -26,15 +26,14 @@ pub fn ler_csv(
linha_cabecalho: usize, linha_cabecalho: usize,
) -> Result<ResultadoCsv, ErroArquivo> { ) -> Result<ResultadoCsv, ErroArquivo> {
// Verificar tamanho // Verificar tamanho
let metadata = std::fs::metadata(caminho) let metadata =
.map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?; std::fs::metadata(caminho).map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
if metadata.len() > LIMITE_BYTES { if metadata.len() > LIMITE_BYTES {
return Err(ErroArquivo::TamanhoExcedido(metadata.len())); return Err(ErroArquivo::TamanhoExcedido(metadata.len()));
} }
// Ler conteúdo bruto // Ler conteúdo bruto
let bytes = std::fs::read(caminho) let bytes = std::fs::read(caminho).map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
.map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
// Decodificar encoding // Decodificar encoding
let conteudo = match encoding.to_lowercase().as_str() { let conteudo = match encoding.to_lowercase().as_str() {
@@ -42,10 +41,8 @@ pub fn ler_csv(
let (decoded, _, _) = WINDOWS_1252.decode(&bytes); let (decoded, _, _) = WINDOWS_1252.decode(&bytes);
decoded.into_owned() decoded.into_owned()
} }
_ => { _ => String::from_utf8(bytes)
String::from_utf8(bytes) .map_err(|e| ErroArquivo::ErroLeitura(format!("Encoding inválido: {}", e)))?,
.map_err(|e| ErroArquivo::ErroLeitura(format!("Encoding inválido: {}", e)))?
}
}; };
let mut avisos = ResumoAvisos::default(); let mut avisos = ResumoAvisos::default();
@@ -97,8 +94,7 @@ pub fn preview_csv(
encoding: &str, encoding: &str,
n: usize, n: usize,
) -> Result<Vec<Vec<String>>, ErroArquivo> { ) -> Result<Vec<Vec<String>>, ErroArquivo> {
let bytes = std::fs::read(caminho) let bytes = std::fs::read(caminho).map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
.map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
let conteudo = match encoding.to_lowercase().as_str() { let conteudo = match encoding.to_lowercase().as_str() {
"windows-1252" | "latin-1" | "iso-8859-1" => { "windows-1252" | "latin-1" | "iso-8859-1" => {
+27 -20
View File
@@ -11,10 +11,8 @@ use std::path::Path;
// Fontes embutidas no binário em tempo de compilação. // Fontes embutidas no binário em tempo de compilação.
// Liberation Sans (~402 KB cada) substitui Arial do sistema (~993 KB cada), // Liberation Sans (~402 KB cada) substitui Arial do sistema (~993 KB cada),
// eliminando dependência de fonte externa e reduzindo o tamanho dos PDFs gerados. // eliminando dependência de fonte externa e reduzindo o tamanho dos PDFs gerados.
const FONT_REGULAR: &[u8] = const FONT_REGULAR: &[u8] = include_bytes!("../../assets/fonts/LiberationSans-Regular.ttf");
include_bytes!("../../assets/fonts/LiberationSans-Regular.ttf"); const FONT_BOLD: &[u8] = include_bytes!("../../assets/fonts/LiberationSans-Bold.ttf");
const FONT_BOLD: &[u8] =
include_bytes!("../../assets/fonts/LiberationSans-Bold.ttf");
/// Metadados do relatório. /// Metadados do relatório.
pub struct MetadadosRelatorio { pub struct MetadadosRelatorio {
@@ -56,12 +54,10 @@ impl PdfGenerator for GenpdfGenerator {
doc.set_page_decorator(decorator); doc.set_page_decorator(decorator);
// Título // Título
doc.push( doc.push(Paragraph::new("").styled_string(
Paragraph::new("").styled_string( "Relatório de Análise de Notas Fiscais",
"Relatório de Análise de Notas Fiscais", style::Style::new().bold().with_font_size(16),
style::Style::new().bold().with_font_size(16), ));
),
);
doc.push(Break::new(1)); doc.push(Break::new(1));
// Metadados // Metadados
@@ -81,7 +77,8 @@ impl PdfGenerator for GenpdfGenerator {
// Totais // Totais
doc.push( doc.push(
Paragraph::new("").styled_string("Totais", style::Style::new().bold().with_font_size(14)), Paragraph::new("")
.styled_string("Totais", style::Style::new().bold().with_font_size(14)),
); );
doc.push(Paragraph::new(format!( doc.push(Paragraph::new(format!(
"Total Geral: R$ {}", "Total Geral: R$ {}",
@@ -102,9 +99,10 @@ impl PdfGenerator for GenpdfGenerator {
doc.push(Break::new(1)); doc.push(Break::new(1));
// Notas Faltantes // Notas Faltantes
doc.push( doc.push(Paragraph::new("").styled_string(
Paragraph::new("").styled_string("Notas Faltantes por Série", style::Style::new().bold().with_font_size(14)), "Notas Faltantes por Série",
); style::Style::new().bold().with_font_size(14),
));
// Use faltantes keys for this section (may differ from soma keys if no values) // Use faltantes keys for this section (may differ from soma keys if no values)
let mut chaves_faltantes: Vec<&ChaveSerie> = resultado.faltantes_por_serie.keys().collect(); let mut chaves_faltantes: Vec<&ChaveSerie> = resultado.faltantes_por_serie.keys().collect();
@@ -114,7 +112,10 @@ impl PdfGenerator for GenpdfGenerator {
let faltantes = match resultado.faltantes_por_serie.get(*chave) { let faltantes = match resultado.faltantes_por_serie.get(*chave) {
Some(f) if !f.is_empty() => f, Some(f) if !f.is_empty() => f,
_ => { _ => {
doc.push(Paragraph::new(format!(" Série {}: nenhuma faltante", chave.label()))); doc.push(Paragraph::new(format!(
" Série {}: nenhuma faltante",
chave.label()
)));
continue; continue;
} }
}; };
@@ -131,9 +132,10 @@ impl PdfGenerator for GenpdfGenerator {
doc.push(Break::new(1)); doc.push(Break::new(1));
// Duplicatas // Duplicatas
doc.push( doc.push(Paragraph::new("").styled_string(
Paragraph::new("").styled_string("Duplicatas por Série", style::Style::new().bold().with_font_size(14)), "Duplicatas por Série",
); style::Style::new().bold().with_font_size(14),
));
let mut chaves_dup: Vec<&ChaveSerie> = resultado.duplicadas_por_serie.keys().collect(); let mut chaves_dup: Vec<&ChaveSerie> = resultado.duplicadas_por_serie.keys().collect();
chaves_dup.sort(); chaves_dup.sort();
@@ -142,7 +144,10 @@ impl PdfGenerator for GenpdfGenerator {
let duplicatas = match resultado.duplicadas_por_serie.get(*chave) { let duplicatas = match resultado.duplicadas_por_serie.get(*chave) {
Some(d) if !d.is_empty() => d, Some(d) if !d.is_empty() => d,
_ => { _ => {
doc.push(Paragraph::new(format!(" Série {}: nenhuma duplicata", chave.label()))); doc.push(Paragraph::new(format!(
" Série {}: nenhuma duplicata",
chave.label()
)));
continue; continue;
} }
}; };
@@ -155,7 +160,9 @@ impl PdfGenerator for GenpdfGenerator {
for (numero, count) in duplicatas { for (numero, count) in duplicatas {
doc.push(Paragraph::new(format!( doc.push(Paragraph::new(format!(
" NF {} / Série {}{} ocorrências", " NF {} / Série {}{} ocorrências",
numero, chave.label(), count numero,
chave.label(),
count
))); )));
} }
} }
@@ -125,15 +125,9 @@ pub fn listar(conn: &Connection) -> Result<Vec<Layout>> {
linha_cabecalho: row.get::<_, i64>(5)? as usize, linha_cabecalho: row.get::<_, i64>(5)? as usize,
indice_numero: row.get::<_, i64>(6)? as usize, indice_numero: row.get::<_, i64>(6)? as usize,
indice_serie: row.get::<_, i64>(7)? as usize, indice_serie: row.get::<_, i64>(7)? as usize,
indice_valor: row indice_valor: row.get::<_, Option<i64>>(8)?.map(|v| v as usize),
.get::<_, Option<i64>>(8)? indice_data: row.get::<_, Option<i64>>(9)?.map(|v| v as usize),
.map(|v| v as usize), indice_documento_tipo: row.get::<_, Option<i64>>(15)?.map(|v| v as usize),
indice_data: row
.get::<_, Option<i64>>(9)?
.map(|v| v as usize),
indice_documento_tipo: row
.get::<_, Option<i64>>(15)?
.map(|v| v as usize),
}, },
}) })
} else { } else {
+11 -7
View File
@@ -14,11 +14,9 @@ pub fn aplicar_migrations(conn: &Connection) -> Result<()> {
)?; )?;
let versao_atual: i64 = conn let versao_atual: i64 = conn
.query_row( .query_row("SELECT versao FROM schema_version LIMIT 1;", [], |row| {
"SELECT versao FROM schema_version LIMIT 1;", row.get(0)
[], })
|row| row.get(0),
)
.unwrap_or(0); .unwrap_or(0);
if versao_atual < 1 { if versao_atual < 1 {
@@ -32,9 +30,15 @@ pub fn aplicar_migrations(conn: &Connection) -> Result<()> {
} }
if versao_atual == 0 { if versao_atual == 0 {
conn.execute("INSERT INTO schema_version (versao) VALUES (?1);", [VERSAO_SCHEMA_ATUAL])?; conn.execute(
"INSERT INTO schema_version (versao) VALUES (?1);",
[VERSAO_SCHEMA_ATUAL],
)?;
} else if versao_atual < VERSAO_SCHEMA_ATUAL { } else if versao_atual < VERSAO_SCHEMA_ATUAL {
conn.execute("UPDATE schema_version SET versao = ?1;", [VERSAO_SCHEMA_ATUAL])?; conn.execute(
"UPDATE schema_version SET versao = ?1;",
[VERSAO_SCHEMA_ATUAL],
)?;
} }
Ok(()) Ok(())
+9 -12
View File
@@ -24,8 +24,8 @@ pub struct ResultadoXlsx {
pub fn listar_abas(caminho: &Path) -> Result<Vec<String>, ErroArquivo> { pub fn listar_abas(caminho: &Path) -> Result<Vec<String>, ErroArquivo> {
verificar_tamanho(caminho)?; verificar_tamanho(caminho)?;
let workbook = open_workbook_auto(caminho) let workbook =
.map_err(|e| ErroArquivo::Corrompido(e.to_string()))?; open_workbook_auto(caminho).map_err(|e| ErroArquivo::Corrompido(e.to_string()))?;
Ok(workbook.sheet_names().to_vec()) Ok(workbook.sheet_names().to_vec())
} }
@@ -45,8 +45,8 @@ pub fn ler_xlsx(
) -> Result<ResultadoXlsx, ErroArquivo> { ) -> Result<ResultadoXlsx, ErroArquivo> {
verificar_tamanho(caminho)?; verificar_tamanho(caminho)?;
let mut workbook = open_workbook_auto(caminho) let mut workbook =
.map_err(|e| ErroArquivo::Corrompido(e.to_string()))?; open_workbook_auto(caminho).map_err(|e| ErroArquivo::Corrompido(e.to_string()))?;
let range: calamine::Range<calamine::Data> = workbook let range: calamine::Range<calamine::Data> = workbook
.worksheet_range(nome_aba) .worksheet_range(nome_aba)
@@ -99,14 +99,11 @@ pub fn ler_xlsx(
/// Retorna as primeiras 5 linhas de uma aba XLSX, a partir da linha 1. /// Retorna as primeiras 5 linhas de uma aba XLSX, a partir da linha 1.
/// Usado exclusivamente para pré-visualização na UI. /// Usado exclusivamente para pré-visualização na UI.
pub fn preview_xlsx( pub fn preview_xlsx(caminho: &Path, nome_aba: &str) -> Result<Vec<Vec<String>>, ErroArquivo> {
caminho: &Path,
nome_aba: &str,
) -> Result<Vec<Vec<String>>, ErroArquivo> {
verificar_tamanho(caminho)?; verificar_tamanho(caminho)?;
let mut workbook = open_workbook_auto(caminho) let mut workbook =
.map_err(|e| ErroArquivo::Corrompido(e.to_string()))?; open_workbook_auto(caminho).map_err(|e| ErroArquivo::Corrompido(e.to_string()))?;
let range: calamine::Range<calamine::Data> = workbook let range: calamine::Range<calamine::Data> = workbook
.worksheet_range(nome_aba) .worksheet_range(nome_aba)
@@ -182,8 +179,8 @@ pub fn parsear_letra_linha(s: &str) -> Option<Coordenada> {
} }
fn verificar_tamanho(caminho: &Path) -> Result<(), ErroArquivo> { fn verificar_tamanho(caminho: &Path) -> Result<(), ErroArquivo> {
let metadata = std::fs::metadata(caminho) let metadata =
.map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?; std::fs::metadata(caminho).map_err(|e| ErroArquivo::ErroLeitura(e.to_string()))?;
if metadata.len() > LIMITE_BYTES { if metadata.len() > LIMITE_BYTES {
return Err(ErroArquivo::TamanhoExcedido(metadata.len())); return Err(ErroArquivo::TamanhoExcedido(metadata.len()));
} }
+24 -5
View File
@@ -1,3 +1,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod application; mod application;
mod domain; mod domain;
mod infrastructure; mod infrastructure;
@@ -5,15 +7,32 @@ mod ui;
use ui::app::App; use ui::app::App;
fn load_icon() -> Option<egui::viewport::IconData> {
let bytes = include_bytes!("../icon.ico");
let img = image::load_from_memory(bytes).ok()?.into_rgba8();
let (width, height) = img.dimensions();
Some(egui::viewport::IconData {
rgba: img.into_raw(),
width,
height,
})
}
fn main() -> eframe::Result { fn main() -> eframe::Result {
let mut viewport = egui::ViewportBuilder::default()
.with_title("Comparador de Notas")
.with_inner_size([1024.0, 768.0])
.with_min_inner_size([800.0, 600.0]);
if let Some(icon) = load_icon() {
viewport = viewport.with_icon(std::sync::Arc::new(icon));
}
let native_options = eframe::NativeOptions { let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default() viewport,
.with_title("Comparador de Notas")
.with_inner_size([1024.0, 768.0])
.with_min_inner_size([800.0, 600.0]),
..Default::default() ..Default::default()
}; };
eframe::run_native( eframe::run_native(
"Comparador de Notas", "Comparador de Notas",
native_options, native_options,
+3 -2
View File
@@ -1,7 +1,7 @@
use crate::application::usecases::importar_arquivo::{importar_csv, importar_xlsx};
use crate::application::usecases::executar_analise::{ use crate::application::usecases::executar_analise::{
expandir_analise, pre_analisar, series_com_intervalo_excessivo, expandir_analise, pre_analisar, series_com_intervalo_excessivo,
}; };
use crate::application::usecases::importar_arquivo::{importar_csv, importar_xlsx};
use crate::domain::entities::layout::{Layout, TipoArquivo}; use crate::domain::entities::layout::{Layout, TipoArquivo};
use crate::ui::app::{App, EstadoApp, ResultadoPendente}; use crate::ui::app::{App, EstadoApp, ResultadoPendente};
use egui::{Context, Ui}; use egui::{Context, Ui};
@@ -169,7 +169,8 @@ fn renderizar_csv(ui: &mut Ui, app: &mut App) {
app.layout_csv_atual.delimitador as u8, app.layout_csv_atual.delimitador as u8,
&app.layout_csv_atual.encoding.clone(), &app.layout_csv_atual.encoding.clone(),
5, 5,
).ok(); )
.ok();
} }
} }
}); });
+1 -4
View File
@@ -239,10 +239,7 @@ fn importar_json(app: &mut App, conteudo: &str) {
); );
} }
_ => { _ => {
app.exibir_erro(format!( app.exibir_erro(format!("Conflito de nome: layout '{}' já existe.", nome));
"Conflito de nome: layout '{}' já existe.",
nome
));
} }
} }
} }
+4 -7
View File
@@ -26,12 +26,9 @@ pub fn renderizar_tabela_preview(ui: &mut egui::Ui, linhas: &[Vec<String>]) {
} }
ui.label( ui.label(
egui::RichText::new(format!( egui::RichText::new(format!("Pré-visualização ({} linha(s))", linhas.len()))
"Pré-visualização ({} linha(s))", .small()
linhas.len() .weak(),
))
.small()
.weak(),
); );
ui.add_space(2.0); ui.add_space(2.0);
@@ -69,4 +66,4 @@ pub fn renderizar_tabela_preview(ui: &mut egui::Ui, linhas: &[Vec<String>]) {
} }
}); });
}); });
} }