diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..acec653
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# Build
+bin/
+obj/
+
+# Visual Studio
+.vs/
+*.user
+*.suo
+
+# Rider / VSCode (opcional)
+.idea/
+.vscode/
+
+# Logs
+*.log
+
+# Arquivos temporários
+*.tmp
+
+# Windows
+Thumbs.db
\ No newline at end of file
diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..db385e4
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..834142d
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace UI
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/Assets/Images/Logo_simples.png b/Assets/Images/Logo_simples.png
new file mode 100644
index 0000000..25058e1
Binary files /dev/null and b/Assets/Images/Logo_simples.png differ
diff --git a/Assets/Images/avatar_user.png b/Assets/Images/avatar_user.png
new file mode 100644
index 0000000..d901aed
Binary files /dev/null and b/Assets/Images/avatar_user.png differ
diff --git a/Assets/Images/background_preview.png b/Assets/Images/background_preview.png
new file mode 100644
index 0000000..4f160d0
Binary files /dev/null and b/Assets/Images/background_preview.png differ
diff --git a/Assets/Images/background_texture.png b/Assets/Images/background_texture.png
new file mode 100644
index 0000000..e096729
Binary files /dev/null and b/Assets/Images/background_texture.png differ
diff --git a/Assets/Images/icon_email.png b/Assets/Images/icon_email.png
new file mode 100644
index 0000000..9f531de
Binary files /dev/null and b/Assets/Images/icon_email.png differ
diff --git a/Assets/Images/icon_eye.png b/Assets/Images/icon_eye.png
new file mode 100644
index 0000000..7273075
Binary files /dev/null and b/Assets/Images/icon_eye.png differ
diff --git a/Assets/Images/icon_password.png b/Assets/Images/icon_password.png
new file mode 100644
index 0000000..6f6913a
Binary files /dev/null and b/Assets/Images/icon_password.png differ
diff --git a/Assets/Images/logo_levelcode.png b/Assets/Images/logo_levelcode.png
new file mode 100644
index 0000000..e90148c
Binary files /dev/null and b/Assets/Images/logo_levelcode.png differ
diff --git a/Assets/Images/logo_levelcode.svg b/Assets/Images/logo_levelcode.svg
new file mode 100644
index 0000000..2ec03ac
--- /dev/null
+++ b/Assets/Images/logo_levelcode.svg
@@ -0,0 +1,43 @@
+
diff --git a/Assets/Images/logo_levelcode2.png b/Assets/Images/logo_levelcode2.png
new file mode 100644
index 0000000..34c8515
Binary files /dev/null and b/Assets/Images/logo_levelcode2.png differ
diff --git a/Cadastro/FrmCadastroClientes.xaml b/Cadastro/FrmCadastroClientes.xaml
new file mode 100644
index 0000000..1c9c77c
--- /dev/null
+++ b/Cadastro/FrmCadastroClientes.xaml
@@ -0,0 +1,786 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Cadastro/FrmCadastroClientes.xaml.cs b/Cadastro/FrmCadastroClientes.xaml.cs
new file mode 100644
index 0000000..652637a
--- /dev/null
+++ b/Cadastro/FrmCadastroClientes.xaml.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace UI.Cadastro
+{
+ ///
+ /// Lógica interna para FrmCadastroClientes.xaml
+ ///
+ public partial class FrmCadastroClientes : Window
+ {
+ public FrmCadastroClientes()
+ {
+ InitializeComponent();
+ }
+
+ private void BtnSair_Click(object sender, RoutedEventArgs e)
+ {
+ this.Close();
+ }
+ }
+}
diff --git a/Configuracao/ConfiguracaoBanco.xaml b/Configuracao/ConfiguracaoBanco.xaml
new file mode 100644
index 0000000..6556a00
--- /dev/null
+++ b/Configuracao/ConfiguracaoBanco.xaml
@@ -0,0 +1,707 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Configuracao/ConfiguracaoBanco.xaml.cs b/Configuracao/ConfiguracaoBanco.xaml.cs
new file mode 100644
index 0000000..dde7127
--- /dev/null
+++ b/Configuracao/ConfiguracaoBanco.xaml.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Data.SqlClient;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using UI.Database;
+
+namespace UI.Configuracao
+{
+ public partial class ConfiguracaoBanco : Window
+ {
+ // ── Estado do mostrar/ocultar senha ──────────────────────────
+ private bool _senhaVisivel = false;
+
+ private const string IconeOlhoAberto =
+ "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 " +
+ "11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 " +
+ "5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 " +
+ "3-1.34 3-3-1.34-3-3-3z";
+
+ private const string IconeOlhoFechado =
+ "M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 " +
+ "2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 " +
+ "2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 " +
+ "10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 " +
+ "22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 " +
+ "1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 " +
+ "0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z";
+
+ public ConfiguracaoBanco()
+ {
+ InitializeComponent();
+ CarregarConfiguracoes();
+ AtualizarCamposAutenticacao();
+ }
+
+ // ── Arrastar janela ─────────────────────────────────────────
+ private void Window_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ DragMove();
+ }
+
+ // ── Fechar janela ────────────────────────────────────────────
+ private void BtnFechar_Click(object sender, RoutedEventArgs e)
+ {
+ this.Close();
+ }
+
+ // ── Mostrar/ocultar senha ────────────────────────────────────
+ private void BtnVerSenha_Click(object sender, RoutedEventArgs e)
+ {
+ _senhaVisivel = !_senhaVisivel;
+
+ if (_senhaVisivel)
+ {
+ TxtSenhaVisivel.Text = PboxSenha.Password;
+ PboxSenha.Visibility = Visibility.Collapsed;
+ TxtSenhaVisivel.Visibility = Visibility.Visible;
+ TxtSenhaVisivel.Focus();
+ TxtSenhaVisivel.CaretIndex = TxtSenhaVisivel.Text.Length;
+
+ IconeOlho.Data = Geometry.Parse(IconeOlhoFechado);
+ IconeOlho.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#3B82F6"));
+ }
+ else
+ {
+ PboxSenha.Password = TxtSenhaVisivel.Text;
+ TxtSenhaVisivel.Visibility = Visibility.Collapsed;
+ PboxSenha.Visibility = Visibility.Visible;
+ PboxSenha.Focus();
+
+ IconeOlho.Data = Geometry.Parse(IconeOlhoAberto);
+ IconeOlho.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A6FA5"));
+ }
+ }
+
+ // ── Carrega configurações salvas ao abrir a janela ───────────
+ private void CarregarConfiguracoes()
+ {
+ DBConfig config = ConfiguracaoService.Carregar();
+
+ TxtServidor.Text = config.Servidor;
+ TxtBancoDados.Text = config.Banco;
+ TxtUsuario.Text = config.Usuario;
+ PboxSenha.Password = config.Senha;
+ ToggleAutenticacao.IsChecked = config.Integrada;
+
+ foreach (ComboBoxItem item in CmbSgbd.Items)
+ {
+ if (item.Content?.ToString() == config.Sgbd)
+ {
+ CmbSgbd.SelectedItem = item;
+ break;
+ }
+ }
+
+ foreach (ComboBoxItem item in CmbProvedor.Items)
+ {
+ if (item.Content?.ToString() == config.Provedor)
+ {
+ CmbProvedor.SelectedItem = item;
+ break;
+ }
+ }
+ }
+
+ // ── Atualiza provedor ao trocar o SGBD ───────────────────────
+ private void CmbSgbd_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (CmbProvedor == null) return;
+
+ string sgbd = (CmbSgbd.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "";
+
+ switch (sgbd)
+ {
+ case "SQL Server":
+ CmbProvedor.SelectedIndex = 0;
+ if (TxtProvedorInfo != null) TxtProvedorInfo.Text = "System.Data.SqlClient";
+ break;
+ case "MySQL":
+ CmbProvedor.SelectedIndex = 2;
+ if (TxtProvedorInfo != null) TxtProvedorInfo.Text = "MySql.Data.MySqlClient";
+ break;
+ case "PostgreSQL":
+ CmbProvedor.SelectedIndex = 3;
+ if (TxtProvedorInfo != null) TxtProvedorInfo.Text = "Npgsql";
+ break;
+ case "SQLite":
+ CmbProvedor.SelectedIndex = 4;
+ if (TxtProvedorInfo != null) TxtProvedorInfo.Text = "System.Data.SQLite";
+ break;
+ default:
+ CmbProvedor.SelectedIndex = 0;
+ break;
+ }
+ }
+
+ // ── Toggle autenticação integrada ────────────────────────────
+ private void ToggleAutenticacao_Changed(object sender, RoutedEventArgs e)
+ {
+ AtualizarCamposAutenticacao();
+ }
+
+ private void AtualizarCamposAutenticacao()
+ {
+ bool integrada = ToggleAutenticacao.IsChecked == true;
+
+ if (TxtUsuario != null)
+ {
+ TxtUsuario.IsEnabled = !integrada;
+ TxtUsuario.Opacity = integrada ? 0.4 : 1.0;
+ }
+
+ if (PboxSenha != null)
+ {
+ PboxSenha.IsEnabled = !integrada;
+ PboxSenha.Opacity = integrada ? 0.4 : 1.0;
+ }
+
+ // Sincroniza o TxtSenhaVisivel junto com o PboxSenha
+ if (TxtSenhaVisivel != null)
+ {
+ TxtSenhaVisivel.IsEnabled = !integrada;
+ TxtSenhaVisivel.Opacity = integrada ? 0.4 : 1.0;
+ }
+ }
+
+ // ── Testar conexão ───────────────────────────────────────────
+ private void BtnTestarConexao_Click(object sender, RoutedEventArgs e)
+ {
+ string servidor = TxtServidor.Text.Trim();
+ string banco = TxtBancoDados.Text.Trim();
+ string usuario = TxtUsuario.Text.Trim();
+ string senha = _senhaVisivel ? TxtSenhaVisivel.Text : PboxSenha.Password;
+ bool integrada = ToggleAutenticacao.IsChecked == true;
+
+ if (string.IsNullOrWhiteSpace(servidor) || string.IsNullOrWhiteSpace(banco))
+ {
+ MostrarResultado(false, "Preencha o servidor e o banco de dados.");
+ return;
+ }
+
+ try
+ {
+ string connStr = integrada
+ ? $"Server={servidor};Database={banco};Integrated Security=True;TrustServerCertificate=True;"
+ : $"Server={servidor};Database={banco};User Id={usuario};Password={senha};TrustServerCertificate=True;";
+
+ using (var conn = new SqlConnection(connStr))
+ {
+ conn.Open();
+ MostrarResultado(true, "Conexão bem-sucedida!");
+ DadosConexao.Servidor = servidor;
+ DadosConexao.Banco = banco;
+ DadosConexao.Usuario = usuario;
+ DadosConexao.Senha = senha;
+
+ }
+ }
+ catch (Exception ex)
+ {
+ MostrarResultado(false, $"Falha: {ex.Message}");
+ }
+ }
+
+ private void MostrarResultado(bool sucesso, string mensagem)
+ {
+ PanelResultado.Visibility = Visibility.Visible;
+ TxtResultado.Text = mensagem;
+
+ var cor = sucesso ? "#22C55E" : "#EF4444";
+ TxtResultado.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(cor));
+ EllipseResultado.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString(cor));
+ }
+
+ // ── Salvar configurações ─────────────────────────────────────
+ private void BtnSalvar_Click(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrWhiteSpace(TxtServidor.Text) ||
+ string.IsNullOrWhiteSpace(TxtBancoDados.Text))
+ {
+ MessageBox.Show("Preencha o servidor e o nome do banco.",
+ "Atenção", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ // Garante a senha correta independente de qual controle está visível
+ string senha = _senhaVisivel ? TxtSenhaVisivel.Text : PboxSenha.Password;
+
+ var config = new DBConfig
+ {
+ Servidor = TxtServidor.Text.Trim(),
+ Banco = TxtBancoDados.Text.Trim(),
+ Usuario = TxtUsuario.Text.Trim(),
+ Senha = senha,
+ Integrada = ToggleAutenticacao.IsChecked == true,
+ Sgbd = (CmbSgbd.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "SQL Server",
+ Provedor = (CmbProvedor.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "System.Data.SqlClient"
+ };
+
+ ConfiguracaoService.Salvar(config);
+
+ MessageBox.Show("Configurações salvas com sucesso!",
+ "Salvo", MessageBoxButton.OK, MessageBoxImage.Information);
+
+ this.Close();
+ }
+ }
+}
diff --git a/Configuracao/ConfiguracaoService.cs b/Configuracao/ConfiguracaoService.cs
new file mode 100644
index 0000000..ecda565
--- /dev/null
+++ b/Configuracao/ConfiguracaoService.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+using System.Text.Json;
+using System.Windows;
+
+namespace UI.Configuracao
+{
+ public class DBConfig
+ {
+ public string Servidor { get; set; } = "localhost";
+ public string Banco { get; set; } = "";
+ public string Usuario { get; set; } = "";
+ public string Senha { get; set; } = "";
+ public bool Integrada { get; set; } = true;
+ public string Sgbd { get; set; } = "SQL Server";
+ public string Provedor { get; set; } = "System.Data.SqlClient";
+ }
+
+ public static class ConfiguracaoService
+ {
+ private static readonly string CaminhoConfig =
+ Path.Combine(@"C:\Levelcode\LevelcodeSGI\Config", "settingsdb.json");
+
+ public static DBConfig Carregar()
+ {
+ try
+ {
+ if (File.Exists(CaminhoConfig))
+ {
+ string json = File.ReadAllText(CaminhoConfig);
+ return JsonSerializer.Deserialize(json) ?? new DBConfig();
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Erro ao carregar configurações:\n{ex.Message}",
+ "Aviso", MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+
+ return new DBConfig();
+ }
+
+ public static void Salvar(DBConfig config)
+ {
+ try
+ {
+ string pasta = Path.GetDirectoryName(CaminhoConfig)!;
+ if (!Directory.Exists(pasta))
+ Directory.CreateDirectory(pasta);
+
+ var options = new JsonSerializerOptions { WriteIndented = true };
+ string json = JsonSerializer.Serialize(config, options);
+ File.WriteAllText(CaminhoConfig, json);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Erro ao salvar configurações:\n{ex.Message}",
+ "Erro", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ public static string GerarConnectionString(DBConfig config)
+ {
+ if (config.Integrada)
+ return $"Server={config.Servidor};Database={config.Banco};Integrated Security=True;TrustServerCertificate=True;";
+ else
+ return $"Server={config.Servidor};Database={config.Banco};User Id={config.Usuario};Password={config.Senha};TrustServerCertificate=True;";
+ }
+ }
+}
diff --git a/Configuracao/DatabaseConfig.cs b/Configuracao/DatabaseConfig.cs
new file mode 100644
index 0000000..53c2b05
--- /dev/null
+++ b/Configuracao/DatabaseConfig.cs
@@ -0,0 +1,25 @@
+using System.IO;
+using System.Text.Json;
+
+namespace UI.Configuracao
+{
+ public class DatabaseConfig
+ {
+ public string Servidor { get; set; }
+ public string Banco { get; set; }
+ public string Usuario { get; set; }
+ public string Senha { get; set; }
+ public bool Integrada { get; set; }
+ public string Sgbd { get; set; }
+ public string Provedor { get; set; }
+
+ public static DatabaseConfig Load()
+ {
+ string path = @"C:\Levelcode\LevelcodeSGI\Config\settingsdb.json";
+
+ string json = File.ReadAllText(path);
+
+ return JsonSerializer.Deserialize(json);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Dashboard/DashboardMain.xaml b/Dashboard/DashboardMain.xaml
new file mode 100644
index 0000000..dbb1f54
--- /dev/null
+++ b/Dashboard/DashboardMain.xaml
@@ -0,0 +1,1497 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Dashboard/DashboardMain.xaml.cs b/Dashboard/DashboardMain.xaml.cs
new file mode 100644
index 0000000..61c1480
--- /dev/null
+++ b/Dashboard/DashboardMain.xaml.cs
@@ -0,0 +1,460 @@
+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();
+ }
+ }
+}
diff --git a/Database/DadosConexao.cs b/Database/DadosConexao.cs
new file mode 100644
index 0000000..42500b8
--- /dev/null
+++ b/Database/DadosConexao.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace UI.Database
+{
+ public static class DadosConexao
+ {
+ public static string Servidor { get; set; }
+ public static string Banco { get; set; }
+ public static string Usuario { get; set; }
+ public static string Senha { get; set; }
+ public static string ObterStringConexao()
+ {
+ return $"Server={Servidor};Database={Banco};User Id={Usuario};Password={Senha};";
+ }
+ }
+}
diff --git a/Database/DatabaseService.cs b/Database/DatabaseService.cs
new file mode 100644
index 0000000..5806b9c
--- /dev/null
+++ b/Database/DatabaseService.cs
@@ -0,0 +1,28 @@
+using System.Data.SqlClient;
+using UI.Configuracao;
+
+namespace UI.Database
+{
+ public class DatabaseService
+ {
+ public static SqlConnection GetConnection()
+ {
+ var cfg = DatabaseConfig.Load();
+
+ string connectionString;
+
+ if (cfg.Integrada)
+ {
+ connectionString =
+ $"Server={cfg.Servidor};Database={cfg.Banco};Trusted_Connection=True;TrustServerCertificate=True;";
+ }
+ else
+ {
+ connectionString =
+ $"Server={cfg.Servidor};Database={cfg.Banco};User Id={cfg.Usuario};Password={cfg.Senha};TrustServerCertificate=True;";
+ }
+
+ return new SqlConnection(connectionString);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..6bf5df7
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,898 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..9317a25
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,96 @@
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using UI.Services;
+
+namespace UI
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ private void Window_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ DragMove();
+ }
+ }//Movimentando o formulario
+
+ private void BtnFechar_Click(object sender, RoutedEventArgs e)
+ {
+ Application.Current.Shutdown();
+ }//fechar aplicativo
+
+ private void BtnEntrar_Click(object sender, RoutedEventArgs e)
+ {
+ string email = TxtEmail.Text;
+ string senha = PboxSenha.Password;
+
+ if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(senha))
+ {
+ MessageBox.Show("Informe email e senha.");
+ return;
+ }
+
+ bool login = AuthService.Login(email, senha);
+
+ if (login)
+ {
+ MessageBox.Show("Login realizado com sucesso!");
+
+ // abrir dashboard futuramente
+ // new Dashboard().Show();
+ // this.Close();
+ }
+ else
+ {
+ MessageBox.Show("Usuário ou senha inválidos.");
+ }
+ }
+
+ private void BtnMostrarSenha_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void BtnConfiguracoes_Click(object sender, RoutedEventArgs e)
+ {
+ PopupConfiguracoes.IsOpen = !PopupConfiguracoes.IsOpen;
+ }
+
+ private void MenuAparencia_Click(object sender, RoutedEventArgs e)
+ {
+ var dh = new UI.Dashboard.DashboardMain();
+ dh.ShowDialog();
+ }
+
+ private void MenuConexao_Click(object sender, RoutedEventArgs e)
+ {
+ var db = new UI.Configuracao.ConfiguracaoBanco();
+ db.ShowDialog();
+ }
+
+ private void MenuIdioma_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void MenuSobre_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Outros/FrmAjuda.xaml b/Outros/FrmAjuda.xaml
new file mode 100644
index 0000000..812bdff
--- /dev/null
+++ b/Outros/FrmAjuda.xaml
@@ -0,0 +1,449 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Outros/FrmAjuda.xaml.cs b/Outros/FrmAjuda.xaml.cs
new file mode 100644
index 0000000..2f9ed72
--- /dev/null
+++ b/Outros/FrmAjuda.xaml.cs
@@ -0,0 +1,24 @@
+using System.Windows;
+using System.Windows.Controls;
+
+// ✅ Namespace deve bater exatamente com x:Class do XAML: UI.Ajuda.PaginaAjuda
+// Se o seu projeto usar outro namespace raiz, ajuste aqui e no x:Class do XAML.
+namespace UI.Outros
+{
+ public partial class FrmAjuda : UserControl
+ {
+ public FrmAjuda()
+ {
+ InitializeComponent();
+ }
+
+ private void BtnFeedback_Click(object sender, RoutedEventArgs e)
+ {
+ MessageBox.Show(
+ "Obrigado pelo seu feedback!\nEm breve nossa equipe entrará em contato.",
+ "Feedback Enviado",
+ MessageBoxButton.OK,
+ MessageBoxImage.Information);
+ }
+ }
+}
diff --git a/Services/AuthService.cs b/Services/AuthService.cs
new file mode 100644
index 0000000..58336a6
--- /dev/null
+++ b/Services/AuthService.cs
@@ -0,0 +1,55 @@
+using System.Data.SqlClient;
+using UI.Database;
+
+namespace UI.Services
+{
+ public class AuthService
+ {
+ public static bool Login(string email, string senha)
+ {
+ using (SqlConnection conn = DatabaseService.GetConnection())
+ {
+ conn.Open();
+
+ string sql =
+ @"SELECT Id
+ FROM Usuarios
+ WHERE Email = @Email
+ AND SenhaHash = @Senha
+ AND Status = 1";
+
+ SqlCommand cmd = new SqlCommand(sql, conn);
+
+ cmd.Parameters.AddWithValue("@Email", email);
+ cmd.Parameters.AddWithValue("@Senha", senha);
+
+ object result = cmd.ExecuteScalar();
+
+ if (result != null)
+ {
+ int userId = (int)result;
+
+ AtualizarUltimoLogin(conn, userId);
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ private static void AtualizarUltimoLogin(SqlConnection conn, int userId)
+ {
+ string update =
+ @"UPDATE Usuarios
+ SET UltimoLogin = GETDATE()
+ WHERE Id = @Id";
+
+ SqlCommand cmd = new SqlCommand(update, conn);
+
+ cmd.Parameters.AddWithValue("@Id", userId);
+
+ cmd.ExecuteNonQuery();
+ }
+ }
+}
\ No newline at end of file
diff --git a/UI.csproj b/UI.csproj
new file mode 100644
index 0000000..ffc10a4
--- /dev/null
+++ b/UI.csproj
@@ -0,0 +1,63 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
diff --git a/UI.slnx b/UI.slnx
new file mode 100644
index 0000000..87e0c5f
--- /dev/null
+++ b/UI.slnx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/background_preview.png b/background_preview.png
new file mode 100644
index 0000000..4f160d0
Binary files /dev/null and b/background_preview.png differ