feat: implement main application structure with UI and layout management

- Added main application logic in `src/ui/app.rs` to handle state and modal dialogs.
- Created module structure for UI components in `src/ui/mod.rs`.
- Implemented column configuration screen in `src/ui/screens/configuracao_colunas.rs`.
- Developed file import screen in `src/ui/screens/import.rs` for CSV and XLSX files.
- Added layout management screen in `src/ui/screens/layouts.rs` for saving and importing layouts.
- Created result display screen in `src/ui/screens/resultado.rs` to show analysis results.
- Introduced modular organization for screens in `src/ui/screens/mod.rs`.
This commit is contained in:
2026-03-02 22:14:02 -03:00
parent 31cd94907a
commit e64988a139
36 changed files with 9166 additions and 2 deletions
+136
View File
@@ -0,0 +1,136 @@
use thiserror::Error;
#[derive(Debug, Error, Clone)]
pub enum ErroNumero {
#[error("Número zero não é válido para uma nota fiscal")]
Zero,
#[error("Valor não numérico: '{0}'")]
NaoNumerico(String),
}
#[derive(Debug, Error, Clone)]
pub enum ErroSerie {
#[error("Série inválida: '{0}' (deve conter de 1 a 3 dígitos numéricos)")]
Invalida(String),
#[error("Série vazia")]
Vazia,
}
#[derive(Debug, Error, Clone)]
pub enum ErroValor {
#[error("Valor negativo não é permitido: '{0}'")]
Negativo(String),
#[error("Valor não numérico: '{0}'")]
NaoNumerico(String),
}
#[derive(Debug, Error, Clone)]
pub enum ErroLayout {
#[error("Campo obrigatório ausente: '{0}'")]
CampoObrigatorioAusente(String),
#[error("JSON malformado: {0}")]
JsonMalformado(String),
#[error("Conflito de nome: layout '{0}' já existe")]
NomeConflitante(String),
}
#[derive(Debug, Error, Clone)]
pub enum ErroArquivo {
#[error("Arquivo muito grande ({0} bytes). Limite: 50 MB")]
TamanhoExcedido(u64),
#[error("Arquivo corrompido ou ilegível: {0}")]
Corrompido(String),
#[error("Formato não suportado: {0}")]
FormatoNaoSuportado(String),
#[error("Erro de leitura: {0}")]
ErroLeitura(String),
}
#[derive(Debug, Error)]
pub enum ErroAnalise {
#[error("Intervalo de faltantes muito grande para a série '{serie}': {intervalo} registros")]
IntervaloMuitoGrande { serie: String, intervalo: u64 },
#[error("Campo obrigatório não mapeado: {0}")]
CampoObrigatorioNaoMapeado(String),
#[error("Índice de coluna inválido: campo '{campo}', índice {indice}")]
IndiceInvalido { campo: String, indice: usize },
#[error("Dois campos mapeados para o mesmo índice: {0}")]
IndicesDuplicados(String),
}
/// Aviso coletado durante a importação, para ser exibido de forma consolidada ao usuário.
#[derive(Debug, Clone)]
pub struct AvisoImportacao {
pub categoria: CategoriaAviso,
pub mensagem: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CategoriaAviso {
LinhaMalformada,
NumeroInvalido,
SerieInvalida,
ValorInvalido,
RegistroDescartado,
}
impl std::fmt::Display for CategoriaAviso {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CategoriaAviso::LinhaMalformada => write!(f, "Linhas malformadas"),
CategoriaAviso::NumeroInvalido => write!(f, "Valores de Numero inválidos"),
CategoriaAviso::SerieInvalida => write!(f, "Registros com Série inválida"),
CategoriaAviso::ValorInvalido => write!(f, "Valores monetários inválidos"),
CategoriaAviso::RegistroDescartado => write!(f, "Registros descartados"),
}
}
}
/// Resumo consolidado de avisos para exibição em um único modal.
#[derive(Debug, Default, Clone)]
pub struct ResumoAvisos {
pub linhas_malformadas: usize,
pub numeros_invalidos: usize,
pub series_invalidas: usize,
pub valores_invalidos: usize,
pub detalhes: Vec<String>,
}
impl ResumoAvisos {
pub fn tem_avisos(&self) -> bool {
self.linhas_malformadas > 0
|| self.numeros_invalidos > 0
|| self.series_invalidas > 0
|| self.valores_invalidos > 0
}
pub fn linhas_para_exibir(&self) -> Vec<String> {
let mut linhas = Vec::new();
if self.linhas_malformadas > 0 {
linhas.push(format!(
"{} linhas descartadas por malformação",
self.linhas_malformadas
));
}
if self.numeros_invalidos > 0 {
linhas.push(format!(
"{} valores de Numero inválidos convertidos ou descartados",
self.numeros_invalidos
));
}
if self.series_invalidas > 0 {
linhas.push(format!(
"{} registros com Série inválida descartados",
self.series_invalidas
));
}
if self.valores_invalidos > 0 {
linhas.push(format!(
"{} valores monetários inválidos descartados",
self.valores_invalidos
));
}
linhas.extend(self.detalhes.clone());
linhas
}
}