using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Security.Cryptography.Xml; using System.Windows; using System.Windows.Input; using UI.Database; namespace UI.Dashboard { // ───────────────────────────────────────── // MODEL // ───────────────────────────────────────── public class PessoaViewModel { public int Id { get; set; } public string NomeCompleto { get; set; } = ""; public string Email { get; set; } = ""; public string Telefone { get; set; } = ""; public string Cidade { get; set; } = ""; public string Cargo { get; set; } = ""; public DateTime? DataNascimento { get; set; } public string DataNascFormatada => DataNascimento.HasValue ? DataNascimento.Value.ToString("dd/MM/yyyy") : "-"; public string Iniciais { get { var p = NomeCompleto.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (p.Length >= 2) return $"{p[0][0]}{p[^1][0]}".ToUpper(); return NomeCompleto.Length >= 2 ? NomeCompleto[..2].ToUpper() : NomeCompleto.ToUpper(); } } private static readonly (string Bg, string Fg)[] Paleta = { ("#1A3A6E","#60A5FA"), ("#3A1A3A","#C084FC"), ("#1A3A20","#4ADE80"), ("#3A2A10","#FCD34D"), ("#1A2A3A","#38BDF8"), ("#3A1A20","#FB7185"), ("#1A3A35","#34D399"), }; public string AvatarBackground => Paleta[Math.Abs(Id) % Paleta.Length].Bg; public string AvatarForeground => Paleta[Math.Abs(Id) % Paleta.Length].Fg; } // ───────────────────────────────────────── // MODEL DA PAGINACAO // ───────────────────────────────────────── public class PaginaItem { public string Label { get; set; } = ""; public bool IsActive { get; set; } public string FontWeight => IsActive ? "Bold" : "Normal"; } // ───────────────────────────────────────── // REPOSITORIO // ───────────────────────────────────────── public class PessoaRepository { private readonly string _connectionString; public PessoaRepository(string connectionString) { _connectionString = connectionString; } public (List Itens, int TotalRegistros) Buscar( string filtro, int pagina, int itensPorPagina) { using var conn = new SqlConnection(_connectionString); conn.Open(); var filtroParam = string.IsNullOrWhiteSpace(filtro) ? "%" : $"%{filtro}%"; var offset = (pagina - 1) * itensPorPagina; int total; using (var cmdCount = new SqlCommand(@" SELECT COUNT(*) FROM Pessoas WHERE @filtro = '%' OR NomeCompleto LIKE @filtro OR Email LIKE @filtro OR Telefone LIKE @filtro", conn)) { cmdCount.Parameters.AddWithValue("@filtro", filtroParam); total = (int)cmdCount.ExecuteScalar(); } var itens = new List(); using (var cmd = new SqlCommand(@" SELECT Id, NomeCompleto, Email, Telefone, Cidade, Cargo, DataNascimento FROM Pessoas WHERE @filtro = '%' OR NomeCompleto LIKE @filtro OR Email LIKE @filtro OR Cidade LIKE @filtro OR Cargo LIKE @filtro ORDER BY NomeCompleto OFFSET @offset ROWS FETCH NEXT @qtd ROWS ONLY", conn)) { cmd.Parameters.AddWithValue("@filtro", filtroParam); cmd.Parameters.AddWithValue("@offset", offset); cmd.Parameters.AddWithValue("@qtd", itensPorPagina); using var reader = cmd.ExecuteReader(); while (reader.Read()) { itens.Add(new PessoaViewModel { Id = reader.GetInt32(0), NomeCompleto = reader.IsDBNull(1) ? "" : reader.GetString(1), Email = reader.IsDBNull(2) ? "" : reader.GetString(2), Telefone = reader.IsDBNull(3) ? "" : reader.GetString(3), Cidade = reader.IsDBNull(4) ? "" : reader.GetString(4), Cargo = reader.IsDBNull(5) ? "" : reader.GetString(5), DataNascimento = reader.IsDBNull(6) ? null : reader.GetDateTime(6) }); } } return (itens, total); } } // ───────────────────────────────────────── // CODE-BEHIND // ───────────────────────────────────────── public partial class DashboardMain : Window { private PessoaRepository? _repo; private int _paginaAtual = 1; private int _itensPorPagina = 10; private int _totalRegistros = 0; private string _filtroAtual = ""; // Controles da tabela — resolvidos via FindName no OnLoaded private System.Windows.Controls.Border? _loadingPanel; private System.Windows.Controls.Border? _emptyPanel; private System.Windows.Controls.ItemsControl? _listaPessoas; private System.Windows.Controls.ItemsControl? _paginacaoPanel; private System.Windows.Controls.Button? _btnAnterior; private System.Windows.Controls.Button? _btnProximo; private System.Windows.Controls.TextBlock? _runPaginaInfo; // ✅ Barras do gráfico de Distribuição por Sexo private System.Windows.Controls.Border? _barMasc; private System.Windows.Controls.Border? _barFem; private System.Windows.Controls.Border? _barOutros; // ───────────────────────────────────────── // CONSTRUTOR // ───────────────────────────────────────── public DashboardMain() { InitializeComponent(); Loaded += OnLoaded; this.lblCadHoje.Text = ObterCadastrosHoje().ToString(); this.CarregarResumo(); } // ───────────────────────────────────────── // QUERIES — inalteradas em relação ao original // ───────────────────────────────────────── public int ObterCadastrosHoje() { using var conn = new SqlConnection(DadosConexao.ObterStringConexao()); conn.Open(); using var cmd = new SqlCommand( "SELECT TotalHoje FROM vw_CadastrosHoje", conn); var resultado = cmd.ExecuteScalar(); return resultado != null ? Convert.ToInt32(resultado) : 0; } public (string total, string crescimento) ObterResumoCadastros() { using var conn = new SqlConnection(DadosConexao.ObterStringConexao()); conn.Open(); using var cmd = new SqlCommand( "SELECT TotalCadastros, CrescimentoPercentual FROM vw_ResumoCadastros", conn); using var reader = cmd.ExecuteReader(); if (reader.Read()) { int total = reader.GetInt32(0); decimal crescimento = reader.GetDecimal(1); return (total.ToString("N0"), $"↑ {Math.Round(crescimento)}%"); } return ("0", "0%"); } public (string ativos, string inativos) ObterStatusPessoas() { using var conn = new SqlConnection(DadosConexao.ObterStringConexao()); conn.Open(); using var cmd = new SqlCommand( "SELECT Ativos, Inativos FROM vw_StatusPessoas", conn); using var reader = cmd.ExecuteReader(); if (reader.Read()) { int ativos = reader.GetInt32(0); int inativos = reader.GetInt32(1); return (ativos.ToString("N0"), inativos.ToString("N0")); } return ("0", "0"); } // ✅ Query original preservada — apenas 3 colunas, sem alterar a view public (string masc, string fem, string outros) ObterDistribuicaoSexo() { using var conn = new SqlConnection(DadosConexao.ObterStringConexao()); conn.Open(); using var cmd = new SqlCommand( "SELECT PercentMasculino, PercentFeminino, PercentOutros FROM vw_DistribuicaoSexo", conn); using var reader = cmd.ExecuteReader(); if (reader.Read()) { decimal masc = reader.GetDecimal(0); decimal fem = reader.GetDecimal(1); decimal outros = reader.GetDecimal(2); return ( $"{Math.Round(masc)}%", $"{Math.Round(fem)}%", $"{Math.Round(outros)}%" ); } return ("0%", "0%", "0%"); } // ───────────────────────────────────────── // CARREGA RESUMO (cards + gráfico) // ───────────────────────────────────────── private void CarregarResumo() { var resumo = ObterResumoCadastros(); var status = ObterStatusPessoas(); var sexo = ObterDistribuicaoSexo(); TxtTotalCadastros.Text = resumo.total; TxtCrescimento.Text = resumo.crescimento; TxtAtivos.Text = status.ativos; TxtInativos.Text = status.inativos; // Percentuais nos TextBlocks do gráfico TxtMasc.Text = sexo.masc; TxtFem.Text = sexo.fem; TxtOutros.Text = sexo.outros; // Tenta atualizar as barras — se Loaded ainda não rodou, // os campos serão null e a chamada é no-op. // O OnLoaded chama novamente para garantir. AtualizarBarrasGrafico(sexo.masc, sexo.fem, sexo.outros); } // ───────────────────────────────────────── // GRÁFICO DE BARRAS — DISTRIBUIÇÃO POR SEXO // ───────────────────────────────────────── /// /// Recebe as strings de percentual ("52%", "43%", "5%") /// e ajusta a largura de cada barra proporcionalmente. /// private void AtualizarBarrasGrafico(string mascPct, string femPct, string outrosPct) { const double BarMaxWidth = 250; // px — ajuste se o card mudar de tamanho const double BarMinWidth = 4; // evita barra invisível quando valor é 0 double m = ParsePct(mascPct); double f = ParsePct(femPct); double o = ParsePct(outrosPct); // Normaliza caso a soma não seja exatamente 100 double soma = m + f + o; if (soma > 0) { m = m / soma * 100; f = f / soma * 100; o = o / soma * 100; } if (_barMasc != null) _barMasc.Width = Math.Max(BarMinWidth, m / 100 * BarMaxWidth); if (_barFem != null) _barFem.Width = Math.Max(BarMinWidth, f / 100 * BarMaxWidth); if (_barOutros != null) _barOutros.Width = Math.Max(BarMinWidth, o / 100 * BarMaxWidth); } /// Converte "52%" → 52.0 com segurança. private static double ParsePct(string pct) { var limpo = pct.Replace("%", "").Replace(",", ".").Trim(); return double.TryParse(limpo, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double v) ? v : 0; } // ───────────────────────────────────────── // LOADED // ───────────────────────────────────────── private void OnLoaded(object sender, RoutedEventArgs e) { // Controles da tabela _loadingPanel = FindName("LoadingPanel") as System.Windows.Controls.Border; _emptyPanel = FindName("EmptyPanel") as System.Windows.Controls.Border; _listaPessoas = FindName("ListaPessoas") as System.Windows.Controls.ItemsControl; _paginacaoPanel = FindName("PaginacaoPanel") as System.Windows.Controls.ItemsControl; _btnAnterior = FindName("BtnAnterior") as System.Windows.Controls.Button; _btnProximo = FindName("BtnProximo") as System.Windows.Controls.Button; _runPaginaInfo = FindName("RunPaginaInfo") as System.Windows.Controls.TextBlock; // ✅ Barras do gráfico _barMasc = FindName("BarMasc") as System.Windows.Controls.Border; _barFem = FindName("BarFem") as System.Windows.Controls.Border; _barOutros = FindName("BarOutros") as System.Windows.Controls.Border; // Aplica as barras agora que os controles estão resolvidos AtualizarBarrasGrafico(TxtMasc.Text, TxtFem.Text, TxtOutros.Text); // Inicializa repositório e carrega a tabela try { string conexao = DadosConexao.ObterStringConexao(); if (string.IsNullOrWhiteSpace(conexao)) { MessageBox.Show( "String de conexão não configurada.\nVerifique DadosConexao.ObterStringConexao().", "Configuração", MessageBoxButton.OK, MessageBoxImage.Warning); return; } _repo = new PessoaRepository(conexao); CarregarPessoas(); } catch (Exception ex) { MessageBox.Show($"Erro ao inicializar conexão:\n{ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error); } } // ───────────────────────────────────────── // JANELA // ───────────────────────────────────────── private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) DragMove(); } private void BtnFechar_Click(object sender, RoutedEventArgs e) { Application.Current.Shutdown(); } // ───────────────────────────────────────── // TABELA / PAGINAÇÃO // ───────────────────────────────────────── private void CarregarPessoas() { if (_repo == null) return; if (_loadingPanel != null) _loadingPanel.Visibility = Visibility.Visible; if (_emptyPanel != null) _emptyPanel.Visibility = Visibility.Collapsed; if (_listaPessoas != null) _listaPessoas.ItemsSource = null; try { var (itens, total) = _repo.Buscar(_filtroAtual, _paginaAtual, _itensPorPagina); _totalRegistros = total; if (itens.Count == 0) { if (_emptyPanel != null) _emptyPanel.Visibility = Visibility.Visible; } else { if (_listaPessoas != null) _listaPessoas.ItemsSource = itens; } AtualizarPaginacao(); AtualizarInfoRegistros(); } catch (Exception ex) { MessageBox.Show($"Erro ao carregar pessoas:\n{ex.Message}", "Erro", MessageBoxButton.OK, MessageBoxImage.Error); } finally { if (_loadingPanel != null) _loadingPanel.Visibility = Visibility.Collapsed; } } private void AtualizarPaginacao() { int totalPaginas = (int)Math.Ceiling(_totalRegistros / (double)_itensPorPagina); if (totalPaginas < 1) totalPaginas = 1; int inicio = Math.Max(1, _paginaAtual - 2); int fim = Math.Min(totalPaginas, inicio + 4); inicio = Math.Max(1, fim - 4); var paginas = new List(); if (inicio > 1) paginas.Add(new PaginaItem { Label = "..." }); for (int i = inicio; i <= fim; i++) paginas.Add(new PaginaItem { Label = i.ToString(), IsActive = i == _paginaAtual }); if (fim < totalPaginas) paginas.Add(new PaginaItem { Label = "..." }); if (_paginacaoPanel != null) _paginacaoPanel.ItemsSource = paginas; if (_btnAnterior != null) _btnAnterior.IsEnabled = _paginaAtual > 1; if (_btnProximo != null) _btnProximo.IsEnabled = _paginaAtual < totalPaginas; } private void AtualizarInfoRegistros() { if (_runPaginaInfo == null) return; int ini = (_paginaAtual - 1) * _itensPorPagina + 1; int fim = Math.Min(_paginaAtual * _itensPorPagina, _totalRegistros); _runPaginaInfo.Text = _totalRegistros == 0 ? "0 registros" : $"{ini}-{fim} de {_totalRegistros:N0}"; } private void BtnAnterior_Click(object sender, RoutedEventArgs e) { if (_paginaAtual > 1) { _paginaAtual--; CarregarPessoas(); } } private void BtnProximo_Click(object sender, RoutedEventArgs e) { int totalPaginas = (int)Math.Ceiling(_totalRegistros / (double)_itensPorPagina); if (_paginaAtual < totalPaginas) { _paginaAtual++; CarregarPessoas(); } } private void CmbItensPorPagina_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { if (CmbItensPorPagina?.SelectedItem is System.Windows.Controls.ComboBoxItem item && int.TryParse(item.Content?.ToString(), out int qtd)) { _itensPorPagina = qtd; _paginaAtual = 1; CarregarPessoas(); } } private void BtnNovoCadastro_Click(object sender, RoutedEventArgs e) { var cp = new UI.Cadastro.FrmCadastroClientes(); cp.ShowDialog(); } } }