diff --git a/.gitignore b/.gitignore
index 0735242..c5277d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.vs/
+<<<<<<< HEAD
**/.vs/
bin/
obj/
@@ -7,4 +8,11 @@ obj/
*.pdb
*.cache
*.user
-*.suo
\ No newline at end of file
+*.suo
+=======
+bin/
+obj/
+*.user
+*.suo
+*.cache
+>>>>>>> 577e97245b8945b1bfd71ca7bb3971042d27d2a9
diff --git a/ArquivosAuxiliares/DadosAuxiliaresTabelaPermissoes.sql b/ArquivosAuxiliares/DadosAuxiliaresTabelaPermissoes.sql
new file mode 100644
index 0000000..715188e
--- /dev/null
+++ b/ArquivosAuxiliares/DadosAuxiliaresTabelaPermissoes.sql
@@ -0,0 +1,108 @@
+INSERT INTO Permissoes (Nome, Descricao)
+VALUES
+
+-- ========================
+-- EMPRESA
+-- ========================
+('EMPRESA_VER', 'Visualizar dados da empresa'),
+('EMPRESA_EDITAR', 'Editar dados da empresa'),
+
+-- ========================
+-- CLIENTES
+-- ========================
+('CLIENTE_VER', 'Visualizar clientes'),
+('CLIENTE_CADASTRAR', 'Cadastrar clientes'),
+('CLIENTE_EDITAR', 'Editar clientes'),
+('CLIENTE_EXCLUIR', 'Excluir clientes'),
+
+-- ========================
+-- FORNECEDORES
+-- ========================
+('FORNECEDOR_VER', 'Visualizar fornecedores'),
+('FORNECEDOR_CADASTRAR', 'Cadastrar fornecedores'),
+('FORNECEDOR_EDITAR', 'Editar fornecedores'),
+('FORNECEDOR_EXCLUIR', 'Excluir fornecedores'),
+
+-- ========================
+-- FUNCIONARIOS
+-- ========================
+('FUNCIONARIO_VER', 'Visualizar funcionrios'),
+('FUNCIONARIO_CADASTRAR', 'Cadastrar funcionrios'),
+('FUNCIONARIO_EDITAR', 'Editar funcionrios'),
+('FUNCIONARIO_EXCLUIR', 'Excluir funcionrios'),
+
+-- ========================
+-- USUARIOS / SEGURANA
+-- ========================
+('USUARIO_VER', 'Visualizar usurios'),
+('USUARIO_CADASTRAR', 'Cadastrar usurios'),
+('USUARIO_EDITAR', 'Editar usurios'),
+('USUARIO_EXCLUIR', 'Excluir usurios'),
+('PERMISSAO_GERENCIAR', 'Gerenciar permisses'),
+
+-- ========================
+-- SERVIOS
+-- ========================
+('SERVICO_VER', 'Visualizar servios'),
+('SERVICO_CADASTRAR', 'Cadastrar servios'),
+('SERVICO_EDITAR', 'Editar servios'),
+('SERVICO_EXCLUIR', 'Excluir servios'),
+
+-- ========================
+-- CONTRATOS
+-- ========================
+('CONTRATO_VER', 'Visualizar contratos'),
+('CONTRATO_CADASTRAR', 'Cadastrar contratos'),
+('CONTRATO_EDITAR', 'Editar contratos'),
+('CONTRATO_EXCLUIR', 'Excluir contratos'),
+
+-- ========================
+-- FINANCEIRO - RECEBER
+-- ========================
+('RECEBER_VER', 'Visualizar contas a receber'),
+('RECEBER_CADASTRAR', 'Cadastrar contas a receber'),
+('RECEBER_EDITAR', 'Editar contas a receber'),
+('RECEBER_BAIXAR', 'Dar baixa em contas a receber'),
+
+-- ========================
+-- FINANCEIRO - PAGAR
+-- ========================
+('PAGAR_VER', 'Visualizar contas a pagar'),
+('PAGAR_CADASTRAR', 'Cadastrar contas a pagar'),
+('PAGAR_EDITAR', 'Editar contas a pagar'),
+('PAGAR_BAIXAR', 'Dar baixa em contas a pagar'),
+
+-- ========================
+-- PLANO DE CONTAS
+-- ========================
+('PLANO_CONTAS_VER', 'Visualizar plano de contas'),
+('PLANO_CONTAS_CADASTRAR', 'Cadastrar plano de contas'),
+('PLANO_CONTAS_EDITAR', 'Editar plano de contas'),
+('PLANO_CONTAS_EXCLUIR', 'Excluir plano de contas'),
+
+-- ========================
+-- TRANSPORTADORAS
+-- ========================
+('TRANSPORTADORA_VER', 'Visualizar transportadoras'),
+('TRANSPORTADORA_CADASTRAR', 'Cadastrar transportadoras'),
+('TRANSPORTADORA_EDITAR', 'Editar transportadoras'),
+('TRANSPORTADORA_EXCLUIR', 'Excluir transportadoras'),
+
+-- ========================
+-- RELATORIOS
+-- ========================
+('RELATORIO_FINANCEIRO', 'Acessar relatrios financeiros'),
+('RELATORIO_CLIENTES', 'Acessar relatrios de clientes'),
+('RELATORIO_GERAL', 'Acessar relatrios gerais'),
+
+-- ========================
+-- SISTEMA
+-- ========================
+('SISTEMA_CONFIG', 'Alterar configuraes do sistema'),
+('SISTEMA_LOGS', 'Visualizar logs do sistema');
+
+INSERT INTO Permissoes (Nome, Descricao)
+SELECT 'CLIENTE_VER', 'Visualizar clientes'
+WHERE NOT EXISTS (
+ SELECT 1 FROM Permissoes WHERE Nome = 'CLIENTE_VER'
+);
\ No newline at end of file
diff --git a/ArquivosAuxiliares/DadosSistema.sql b/ArquivosAuxiliares/DadosSistema.sql
new file mode 100644
index 0000000..eeab4ec
--- /dev/null
+++ b/ArquivosAuxiliares/DadosSistema.sql
@@ -0,0 +1,74 @@
+-- =========================
+-- PEGAR EMPRESA
+-- =========================
+DECLARE @EmpresaId INT;
+SELECT TOP 1 @EmpresaId = Id FROM Empresa;
+
+-- =========================
+-- CRIAR USUARIO ADMIN
+-- =========================
+IF NOT EXISTS (SELECT 1 FROM Usuarios WHERE Usuario = 'admin' AND EmpresaId = @EmpresaId)
+BEGIN
+ INSERT INTO Usuarios (
+ EmpresaId, Nome, Email, Usuario, SenhaHash, Ativo, CriadoEm
+ )
+ VALUES (
+ @EmpresaId,
+ 'Administrador do Sistema',
+ 'admin@levelcode.com.br',
+ 'admin',
+ CONVERT(VARCHAR(255), HASHBYTES('SHA2_256', 'Nad310311*##'), 2),
+ 1,
+ GETDATE()
+ );
+END
+
+-- =========================
+-- CRIAR PERFIL ADMIN
+-- =========================
+IF NOT EXISTS (SELECT 1 FROM Perfis WHERE Nome = 'Administrador' AND EmpresaId = @EmpresaId)
+BEGIN
+ INSERT INTO Perfis (
+ EmpresaId, Nome, Descricao, Ativo
+ )
+ VALUES (
+ @EmpresaId,
+ 'Administrador',
+ 'Acesso total ao sistema',
+ 1
+ );
+END
+
+-- =========================
+-- PEGAR IDS
+-- =========================
+DECLARE @UsuarioId INT;
+DECLARE @PerfilId INT;
+
+SELECT @UsuarioId = Id FROM Usuarios WHERE Usuario = 'admin' AND EmpresaId = @EmpresaId;
+SELECT @PerfilId = Id FROM Perfis WHERE Nome = 'Administrador' AND EmpresaId = @EmpresaId;
+
+-- =========================
+-- VINCULAR USUARIO AO PERFIL
+-- =========================
+IF NOT EXISTS (
+ SELECT 1 FROM UsuarioPerfis
+ WHERE UsuarioId = @UsuarioId AND PerfilId = @PerfilId
+)
+BEGIN
+ INSERT INTO UsuarioPerfis (UsuarioId, PerfilId)
+ VALUES (@UsuarioId, @PerfilId);
+END
+
+-- =========================
+-- DAR TODAS PERMISSES
+-- =========================
+INSERT INTO PerfilPermissoes (PerfilId, PermissaoId)
+SELECT
+ @PerfilId,
+ Id
+FROM Permissoes
+WHERE NOT EXISTS (
+ SELECT 1 FROM PerfilPermissoes
+ WHERE PerfilId = @PerfilId AND PermissaoId = Permissoes.Id
+);
diff --git a/ArquivosAuxiliares/Database.sql b/ArquivosAuxiliares/Database.sql
new file mode 100644
index 0000000..27e3c15
--- /dev/null
+++ b/ArquivosAuxiliares/Database.sql
@@ -0,0 +1,674 @@
+-- ================================
+-- CRIAR DATABASE
+-- ================================
+IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'LevelOS')
+BEGIN
+ CREATE DATABASE LevelOS;
+END
+GO
+
+USE LevelOS;
+GO
+
+-- ================================
+-- TABELA EMPRESA
+-- ================================
+CREATE TABLE Empresa (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+
+ -- Dados bsicos
+ Nome VARCHAR(255) NOT NULL,
+ CNPJ VARCHAR(20) NOT NULL UNIQUE,
+ TipoEmpresa VARCHAR(20) NOT NULL,
+ RegimeTributario VARCHAR(30) NOT NULL,
+ CNAE VARCHAR(20) NOT NULL,
+
+ -- Endereo
+ Cep VARCHAR(15) NOT NULL,
+ Endereco VARCHAR(255) NOT NULL,
+ Numero INT NOT NULL,
+ Complemento VARCHAR(255),
+ Bairro VARCHAR(255) NOT NULL,
+ Cidade VARCHAR(255) NOT NULL,
+ UF CHAR(2) NOT NULL,
+ Pais VARCHAR(50),
+
+ -- Contatos
+ Telefone1 VARCHAR(20),
+ Telefone2 VARCHAR(20),
+ Celular VARCHAR(20),
+ Whatsapp VARCHAR(20),
+ Email VARCHAR(150) NOT NULL,
+ Site VARCHAR(255),
+
+ -- Fiscal
+ InscricaoEstadual VARCHAR(20) UNIQUE,
+ InscricaoMunicipal VARCHAR(20) UNIQUE,
+
+ -- Contador
+ CNPJCPF_Contador VARCHAR(20),
+ Nome_Contador VARCHAR(255),
+
+ -- Sistema
+ TextoParaRecibo VARCHAR(MAX),
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE()
+);
+GO
+
+-- ================================
+-- CONFIGURAES
+-- ================================
+CREATE TABLE Empresa_config (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Aparncia
+ NomeSistema VARCHAR(255),
+ CorPrimaria VARCHAR(20),
+ CorSecundaria VARCHAR(20),
+ Logo VARCHAR(255),
+ Favicon VARCHAR(255),
+
+ -- OS
+ ExibirCPFCliente BIT DEFAULT 1,
+ ExibirTelefoneCliente BIT DEFAULT 1,
+ ExibirGarantia BIT DEFAULT 1,
+ DiasGarantiaPadrao INT DEFAULT 90,
+
+ -- Financeiro
+ GerarReciboAutomatico BIT DEFAULT 1,
+ ExibirValoresOS BIT DEFAULT 1,
+
+ -- Comunicao
+ EnviarEmailAutomatico BIT DEFAULT 0,
+ EnviarWhatsappAutomatico BIT DEFAULT 0,
+
+ -- Sistema
+ ModoEscuro BIT DEFAULT 0,
+ PermitirEdicaoOSFinalizada BIT DEFAULT 0,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ CONSTRAINT FK_ConfigEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT UQ_ConfigEmpresa UNIQUE (EmpresaId)
+);
+GO
+
+-- ================================
+-- FUNCIONRIOS
+-- ================================
+CREATE TABLE Funcionarios (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Dados pessoais
+ Nome VARCHAR(255) NOT NULL,
+ CPF VARCHAR(14),
+ DataNascimento DATE,
+
+ -- Contato
+ Telefone VARCHAR(20),
+ Celular VARCHAR(20),
+ Email VARCHAR(150) NOT NULL,
+
+ -- Login
+ Usuario VARCHAR(50) NOT NULL,
+ SenhaHash VARCHAR(255) NOT NULL,
+
+ -- Permisso
+ TipoConta VARCHAR(20) NOT NULL DEFAULT 'Atendente',
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+ UltimoLogin DATETIME,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Constraints
+ CONSTRAINT FK_FuncionarioEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT UQ_LoginEmpresa UNIQUE (EmpresaId, Usuario)
+);
+GO
+-- ================================
+-- FORNECEDORES
+-- ================================
+
+CREATE TABLE Fornecedores (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Dados bsicos
+ Nome VARCHAR(255) NOT NULL,
+ TipoPessoa VARCHAR(2) NOT NULL, -- PF ou PJ
+ Documento VARCHAR(20) NOT NULL, -- CPF ou CNPJ
+
+ -- Contato
+ Telefone VARCHAR(20),
+ Celular VARCHAR(20),
+ Whatsapp VARCHAR(20),
+ Email VARCHAR(150),
+
+ -- Endereo
+ Cep VARCHAR(15),
+ Endereco VARCHAR(255),
+ Numero INT,
+ Complemento VARCHAR(255),
+ Bairro VARCHAR(255),
+ Cidade VARCHAR(255),
+ UF CHAR(2),
+
+ -- Dados adicionais
+ NomeFantasia VARCHAR(255),
+ InscricaoEstadual VARCHAR(20),
+ Site VARCHAR(255),
+ Observacoes VARCHAR(MAX),
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_FornecedorEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ -- Evita duplicidade por empresa
+ CONSTRAINT UQ_Fornecedor_Doc UNIQUE (EmpresaId, Documento)
+);
+GO
+
+-- ================================
+-- TRANSPORTADORAS
+-- ================================
+CREATE TABLE Transportadoras (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Dados bsicos
+ RazaoSocial VARCHAR(255) NOT NULL,
+ NomeFantasia VARCHAR(255),
+ CNPJ VARCHAR(20) NOT NULL,
+
+ -- Contato
+ Telefone VARCHAR(20),
+ Celular VARCHAR(20),
+ Whatsapp VARCHAR(20),
+ Email VARCHAR(150),
+
+ -- Endereo
+ Cep VARCHAR(15),
+ Endereco VARCHAR(255),
+ Numero INT,
+ Complemento VARCHAR(255),
+ Bairro VARCHAR(255),
+ Cidade VARCHAR(255),
+ UF CHAR(2),
+
+ -- Dados de transporte
+ TipoFrete VARCHAR(20), -- CIF / FOB / etc
+ PrazoEntrega INT, -- em dias
+ ValorFretePadrao DECIMAL(10,2),
+
+ -- Sistema
+ Observacoes VARCHAR(MAX),
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_TransportadoraEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ -- Evita duplicidade
+ CONSTRAINT UQ_Transportadora_CNPJ UNIQUE (EmpresaId, CNPJ)
+);
+GO
+-- ================================
+-- CLIENTES
+-- ================================
+CREATE TABLE Clientes (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Dados principais
+ Nome VARCHAR(255) NOT NULL,
+ NomeFantasia VARCHAR(255),
+ TipoPessoa VARCHAR(2) NOT NULL, -- PF / PJ
+ Documento VARCHAR(20) NOT NULL, -- CPF/CNPJ
+ RG VARCHAR(20),
+ InscricaoMunicipal VARCHAR(20),
+ DataNascimento DATE,
+
+ -- Contato
+ Contato VARCHAR(255),
+ Telefone1 VARCHAR(20),
+ Telefone2 VARCHAR(20),
+ Celular VARCHAR(20),
+ Whatsapp VARCHAR(20),
+ Email VARCHAR(150),
+ EmailNFe VARCHAR(150),
+ Site VARCHAR(255),
+
+ -- Classificao
+ Grupo VARCHAR(100),
+
+ -- Endereo
+ Cep VARCHAR(15),
+ Endereco VARCHAR(255),
+ Numero INT,
+ Complemento VARCHAR(255),
+ Bairro VARCHAR(255),
+ Cidade VARCHAR(255),
+ UF CHAR(2),
+ Pais VARCHAR(50),
+
+ -- Financeiro
+ LimiteCredito DECIMAL(10,2) DEFAULT 0,
+ Bloqueado BIT DEFAULT 0,
+ ObservacoesCobranca VARCHAR(MAX),
+
+ -- Vendas / OS
+ VendedorPadraoId INT NULL,
+ TipoConsumidor VARCHAR(50), -- Consumidor final, revenda etc
+
+ -- Sistema
+ Observacoes VARCHAR(MAX),
+
+ -- Extras (campos custom)
+ CampoExtra1 VARCHAR(255),
+ CampoExtra2 VARCHAR(255),
+ CampoExtra3 VARCHAR(255),
+
+ -- Carteiras virtuais
+ Bitcoin VARCHAR(255),
+ Ethereum VARCHAR(255),
+ Litecoin VARCHAR(255),
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+ UltimaCompra DATETIME,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_ClienteEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ -- Evita duplicidade
+ CONSTRAINT UQ_Cliente_Doc UNIQUE (EmpresaId, Documento)
+);
+GO
+
+-- ================================
+-- SERVIOS
+-- ================================
+CREATE TABLE Servicos (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Dados bsicos
+ Nome VARCHAR(255) NOT NULL,
+ Descricao VARCHAR(MAX),
+
+ -- Valores
+ ValorPadrao DECIMAL(10,2) NOT NULL,
+ Custo DECIMAL(10,2),
+
+ -- Comisso
+ TipoComissao VARCHAR(10) NOT NULL DEFAULT 'Percentual', -- Percentual ou Fixo
+ ValorComissao DECIMAL(10,2) NOT NULL DEFAULT 0,
+
+ -- Tempo
+ TempoEstimado INT, -- em minutos
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_ServicoEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE
+);
+GO
+-- ================================
+-- Situao OS
+-- ================================
+CREATE TABLE SituacoesOS (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Identificao
+ Descricao VARCHAR(255) NOT NULL,
+
+ -- Classificao (os 3 grupos da tela)
+ Tipo VARCHAR(20) NOT NULL,
+ -- Entrada | Oficina | Saida
+
+ -- Regras da OS
+ ConsideraAberta BIT NOT NULL DEFAULT 1,
+ ConsideraFechada BIT NOT NULL DEFAULT 0,
+ MarcaComoPronto BIT NOT NULL DEFAULT 0,
+
+ -- Aparncia (igual na tela)
+ CorFundo VARCHAR(20),
+ CorFonte VARCHAR(20),
+
+ -- Financeiro (opcional)
+ PlanoContasId INT NULL,
+
+ -- Controle
+ Ordem INT DEFAULT 0, -- ordem de exibio
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_SituacaoEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE
+);
+GO
+-- ================================
+-- CONTRATOS
+-- ================================
+CREATE TABLE Contratos (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+ ClienteId INT NOT NULL,
+
+ -- Dados principais
+ Descricao VARCHAR(255),
+ Observacoes VARCHAR(MAX),
+
+ Valor DECIMAL(10,2) NOT NULL,
+ DataInicio DATE,
+ DataValidade DATE,
+
+ -- Controle de franquia (ex: horas inclusas)
+ FranquiaTempo INT, -- minutos ou horas (voc define padro)
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamentos
+ CONSTRAINT FK_ContratoEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT FK_ContratoCliente
+ FOREIGN KEY (ClienteId) REFERENCES Clientes(Id)
+);
+GO
+-- ================================
+-- CONTRATOS - EQUIPAMENTOS
+-- ================================
+CREATE TABLE ContratoEquipamentos (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ ContratoId INT NOT NULL,
+
+ -- Dados do equipamento
+ Modelo VARCHAR(255),
+ Marca VARCHAR(255),
+ Operadora VARCHAR(100),
+ Serial VARCHAR(100),
+ NumeroPatrimonio VARCHAR(100),
+
+ Observacoes VARCHAR(MAX),
+
+ -- Relacionamento
+ CONSTRAINT FK_ContratoEquipamento
+ FOREIGN KEY (ContratoId) REFERENCES Contratos(Id) ON DELETE CASCADE
+);
+GO
+-- ================================
+-- PLANO DE CONTAS
+-- ================================
+CREATE TABLE PlanoDeContas (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ -- Hierarquia
+ ContaPaiId INT NULL,
+
+ -- Identificao
+ Nome VARCHAR(255) NOT NULL,
+ Codigo VARCHAR(50), -- Ex: 1.01.02
+ Tipo VARCHAR(20) NOT NULL, -- Receita / Despesa
+
+ -- Controle
+ AceitaLancamento BIT NOT NULL DEFAULT 1, -- se pode lanar direto
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamentos
+ CONSTRAINT FK_PlanoContaEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT FK_PlanoContaPai
+ FOREIGN KEY (ContaPaiId) REFERENCES PlanoDeContas(Id)
+);
+GO
+-- ================================
+-- CONTAS A PAGAR
+-- ================================
+CREATE TABLE ContasReceber (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+ ClienteId INT NOT NULL,
+
+ -- Classificao
+ PlanoContaId INT NOT NULL,
+
+ -- Origem (contrato, venda futura, etc)
+ ContratoId INT NULL,
+
+ -- Dados financeiros
+ Descricao VARCHAR(255),
+ Valor DECIMAL(10,2) NOT NULL,
+ DataEmissao DATE,
+ DataVencimento DATE NOT NULL,
+ DataPagamento DATE,
+
+ -- Controle
+ Status VARCHAR(20) NOT NULL DEFAULT 'Pendente',
+ -- Pendente / Pago / Cancelado / Atrasado
+
+ ValorPago DECIMAL(10,2),
+ Juros DECIMAL(10,2) DEFAULT 0,
+ Multa DECIMAL(10,2) DEFAULT 0,
+ Desconto DECIMAL(10,2) DEFAULT 0,
+
+ -- Observaes
+ Observacoes VARCHAR(MAX),
+
+ -- Controle sistema
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamentos
+ CONSTRAINT FK_ReceberEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT FK_ReceberCliente
+ FOREIGN KEY (ClienteId) REFERENCES Clientes(Id),
+
+ CONSTRAINT FK_ReceberPlano
+ FOREIGN KEY (PlanoContaId) REFERENCES PlanoDeContas(Id),
+
+ CONSTRAINT FK_ReceberContrato
+ FOREIGN KEY (ContratoId) REFERENCES Contratos(Id)
+);
+GO
+-- ================================
+-- CONTAS A PAGAR
+-- ================================
+CREATE TABLE ContasPagar (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+ FornecedorId INT NOT NULL,
+
+ -- Classificao
+ PlanoContaId INT NOT NULL,
+
+ -- Dados financeiros
+ Descricao VARCHAR(255),
+ Valor DECIMAL(10,2) NOT NULL,
+ DataEmissao DATE,
+ DataVencimento DATE NOT NULL,
+ DataPagamento DATE,
+
+ -- Controle
+ Status VARCHAR(20) NOT NULL DEFAULT 'Pendente',
+
+ ValorPago DECIMAL(10,2),
+ Juros DECIMAL(10,2) DEFAULT 0,
+ Multa DECIMAL(10,2) DEFAULT 0,
+ Desconto DECIMAL(10,2) DEFAULT 0,
+
+ -- Observaes
+ Observacoes VARCHAR(MAX),
+
+ -- Controle sistema
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamentos
+ CONSTRAINT FK_PagarEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ CONSTRAINT FK_PagarFornecedor
+ FOREIGN KEY (FornecedorId) REFERENCES Fornecedores(Id),
+
+ CONSTRAINT FK_PagarPlano
+ FOREIGN KEY (PlanoContaId) REFERENCES PlanoDeContas(Id)
+);
+GO
+
+-- ================================
+-- USURIOS DO SISTEMA
+-- ================================
+CREATE TABLE Usuarios (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ Nome VARCHAR(255) NOT NULL,
+ Email VARCHAR(150) NOT NULL,
+ Usuario VARCHAR(50) NOT NULL,
+ SenhaHash VARCHAR(255) NOT NULL,
+
+ -- Controle
+ Ativo BIT NOT NULL DEFAULT 1,
+ UltimoLogin DATETIME,
+
+ -- Auditoria
+ CriadoEm DATETIME DEFAULT GETDATE(),
+ AtualizadoEm DATETIME DEFAULT GETDATE(),
+
+ -- Relacionamento
+ CONSTRAINT FK_UsuarioEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE,
+
+ -- Evita login duplicado por empresa
+ CONSTRAINT UQ_Usuario_Login UNIQUE (EmpresaId, Usuario)
+);
+GO
+-- ================================
+-- PERFIS DE USURIO
+-- ================================
+CREATE TABLE Perfis (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ EmpresaId INT NOT NULL,
+
+ Nome VARCHAR(100) NOT NULL, -- Admin, Tcnico, etc
+ Descricao VARCHAR(255),
+
+ Ativo BIT NOT NULL DEFAULT 1,
+
+ CONSTRAINT FK_PerfilEmpresa
+ FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE
+);
+GO
+-- ================================
+-- PERMISSES DE USURIO
+-- ================================
+CREATE TABLE Permissoes (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+
+ Nome VARCHAR(100) NOT NULL, -- Ex: CLIENTE_CADASTRAR
+ Descricao VARCHAR(255)
+);
+GO
+-- ================================
+-- PERFIL - PERMISSES
+-- ================================
+CREATE TABLE PerfilPermissoes (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ PerfilId INT NOT NULL,
+ PermissaoId INT NOT NULL,
+
+ CONSTRAINT FK_PerfilPermissao_Perfil
+ FOREIGN KEY (PerfilId) REFERENCES Perfis(Id) ON DELETE CASCADE,
+
+ CONSTRAINT FK_PerfilPermissao_Permissao
+ FOREIGN KEY (PermissaoId) REFERENCES Permissoes(Id) ON DELETE CASCADE,
+
+ CONSTRAINT UQ_PerfilPermissao UNIQUE (PerfilId, PermissaoId)
+);
+GO
+-- ================================
+-- USURIO - PERFIL
+-- ================================
+CREATE TABLE UsuarioPerfis (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ UsuarioId INT NOT NULL,
+ PerfilId INT NOT NULL,
+
+ CONSTRAINT FK_UsuarioPerfil_Usuario
+ FOREIGN KEY (UsuarioId)
+ REFERENCES Usuarios(Id)
+ ON DELETE CASCADE,
+
+ CONSTRAINT FK_UsuarioPerfil_Perfil
+ FOREIGN KEY (PerfilId)
+ REFERENCES Perfis(Id)
+ ON DELETE NO ACTION,
+
+ CONSTRAINT UQ_UsuarioPerfil UNIQUE (UsuarioId, PerfilId)
+);
+GO
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ExemploUSO.txt b/ArquivosAuxiliares/ExemploUSO.txt
new file mode 100644
index 0000000..4adeae9
--- /dev/null
+++ b/ArquivosAuxiliares/ExemploUSO.txt
@@ -0,0 +1,95 @@
+using DALL;
+using System.Data;
+
+// =============================================
+// Configuração da connection string
+// =============================================
+var connectionString = "Server=206.42.13.180;Database=Levelcode-LevelOS;User Id=sa;Password=suasenha;TrustServerCertificate=True;";
+
+var backupService = new DALLBackupService(connectionString);
+
+// =============================================
+// Backup FULL
+// =============================================
+var resultadoFull = backupService.ExecutarBackupFull();
+
+if (resultadoFull.Sucesso)
+ Console.WriteLine($"✅ Backup FULL concluído em {resultadoFull.Duracao.TotalSeconds:F1}s");
+else
+ Console.WriteLine($"❌ Erro no Backup FULL: {resultadoFull.Erro}");
+
+// =============================================
+// Backup DIFERENCIAL
+// =============================================
+var resultadoDiff = backupService.ExecutarBackupDiferencial();
+
+if (resultadoDiff.Sucesso)
+ Console.WriteLine($"✅ Backup DIFERENCIAL concluído em {resultadoDiff.Duracao.TotalSeconds:F1}s");
+else
+ Console.WriteLine($"❌ Erro no Backup DIFERENCIAL: {resultadoDiff.Erro}");
+
+// =============================================
+// Limpeza de backups antigos
+// =============================================
+var resultadoLimpeza = backupService.ExecutarLimpeza();
+
+if (resultadoLimpeza.Sucesso)
+ Console.WriteLine($"✅ Limpeza concluída em {resultadoLimpeza.Duracao.TotalSeconds:F1}s");
+else
+ Console.WriteLine($"❌ Erro na Limpeza: {resultadoLimpeza.Erro}");
+
+// =============================================
+// Listar histórico de backups
+// =============================================
+var historico = backupService.ListarHistoricoBackups();
+
+Console.WriteLine("\n📋 Histórico de Backups:");
+Console.WriteLine($"{"Tipo",-15} {"TamanhoMB",-12} {"Inicio",-22} {"Arquivo"}");
+Console.WriteLine(new string('-', 90));
+
+foreach (DataRow row in historico.Rows)
+{
+ Console.WriteLine($"{row["Tipo"],-15} {row["TamanhoMB"],-12} {row["Inicio"],-22} {row["Arquivo"]}");
+}
+
+// =============================================
+// Restauração FULL + DIFERENCIAL
+// =============================================
+
+// Pega o arquivo mais recente do histórico automaticamente
+string arquivoFull = "";
+string arquivoDiff = "";
+
+foreach (DataRow row in historico.Rows)
+{
+ if (row["Tipo"].ToString() == "FULL" && string.IsNullOrEmpty(arquivoFull))
+ arquivoFull = row["Arquivo"].ToString()!;
+
+ if (row["Tipo"].ToString() == "DIFERENCIAL" && string.IsNullOrEmpty(arquivoDiff))
+ arquivoDiff = row["Arquivo"].ToString()!;
+
+ if (!string.IsNullOrEmpty(arquivoFull) && !string.IsNullOrEmpty(arquivoDiff))
+ break;
+}
+
+// Restaura FULL
+Console.WriteLine($"\n🔁 Restaurando FULL: {arquivoFull}");
+var r1 = backupService.RestaurarBackup(arquivoFull, TipoRestauracao.Full);
+
+if (r1.Sucesso)
+{
+ Console.WriteLine($"✅ FULL restaurado em {r1.Duracao.TotalSeconds:F1}s");
+
+ // Aplica DIFERENCIAL após o FULL
+ Console.WriteLine($"🔁 Aplicando DIFERENCIAL: {arquivoDiff}");
+ var r2 = backupService.RestaurarBackup(arquivoDiff, TipoRestauracao.Diferencial);
+
+ if (r2.Sucesso)
+ Console.WriteLine($"✅ DIFERENCIAL aplicado em {r2.Duracao.TotalSeconds:F1}s — Banco online!");
+ else
+ Console.WriteLine($"❌ Erro no DIFERENCIAL: {r2.Erro}");
+}
+else
+{
+ Console.WriteLine($"❌ Erro no FULL: {r1.Erro}");
+}
\ No newline at end of file
diff --git a/ArquivosAuxiliares/InsertsLotes.sql b/ArquivosAuxiliares/InsertsLotes.sql
new file mode 100644
index 0000000..3dac05e
--- /dev/null
+++ b/ArquivosAuxiliares/InsertsLotes.sql
@@ -0,0 +1,157 @@
+INSERT INTO Bancos (NUMERO, NOME) VALUES
+(1,'Banco do Brasil S/A'),
+(2,'Banco Central do Brasil'),
+(3,'Banco da Amazonia S/A'),
+(4,'Banco do Nordeste do Brasil S/A'),
+(8,'Banco Santander Meridional S/A'),
+(21,'BANESTES S/A - Banco Est.Esp.Santo'),
+(22,'CREDIREAL'),
+(24,'Banco de Pernambuco S/A - BANDEPE'),
+(25,'Banco Alfa S/A'),
+(27,'Banco Estado Santa Catarina S/A'),
+(28,'BANEB'),
+(29,'Banco BANERJ S/A'),
+(30,'PARAIBAN - Banco da Paraiba S/A'),
+(31,'Banco BEG S/A'),
+(33,'SANTANDER'),
+(34,'Banco BEA S/A'),
+(35,'Banco do Estado do Ceara S/A - BEC'),
+(36,'Banco do Estado do Maranhao S/A'),
+(37,'Banco do Estado do Para S/A'),
+(38,'Banco BANESTADO S/A'),
+(39,'Banco do Estado do Piaui S/A'),
+(40,'Banco Cargill S/A'),
+(41,'Banco Est. Rio Grande do Sul S/A'),
+(44,'Banco BVA S/A'),
+(45,'Banco OPPORTUNITY S/A'),
+(47,'Banco Est. de Sergipe S/A'),
+(48,'Banco BENGE S/A'),
+(63,'IBIBANK S/A - Banco Multiplo'),
+(65,'LEMON BANK Banco Multiplo S/A'),
+(66,'Banco MORGAN S. D. Witter S/A'),
+(67,'Banco BANEB S/A'),
+(68,'Banco BEA S/A'),
+(70,'BRB-Banco de Brasilia S/A'),
+(89,'CREDISAN'),
+(104,'Caixa Economica Federal'),
+(106,'Banco Itabanco S/A'),
+(107,'Banco BBM S/A'),
+(109,'CREDIBANCO S/A'),
+(116,'Banco BNL do Brasil S/A'),
+(148,'Bank Of America Brasil S/A'),
+(151,'Banco Nossa Caixa S/A'),
+(175,'Banco Finasa S/A'),
+(184,'Banco BBA Creditanstalt S/A'),
+(204,'BCO Inter American Express S/A'),
+(208,'Banco Pactual S/A'),
+(210,'DRESDNER Bank Lateinamerika A.'),
+(212,'Banco Matone S/A'),
+(213,'Banco ARBI S/A'),
+(214,'Banco DIBENS S/A'),
+(215,'Banco Com e Invest Sudameris'),
+(216,'Banco Regional MALCON S/A'),
+(217,'Banco JOHN DEERE S/A'),
+(218,'Banco Bonsucesso S/A'),
+(219,'Banco ZOGBI S/A'),
+(222,'BCO Credit Lyonnais Brasil S/A'),
+(224,'Banco Fibra S/A'),
+(225,'Banco Brascan S/A'),
+(229,'Banco Cruzeiro do Sul S/A'),
+(230,'Banco Bandeirantes S/A'),
+(231,'Banco Boavista interatlantico S/A'),
+(233,'Banco GE Capital S/A'),
+(237,'Banco Bradesco S/A'),
+(240,'Banco de Credito Real de M.G. S/A'),
+(241,'Banco Classico S/A'),
+(243,'Banco STOCK Maxima S/A'),
+(244,'Banco Cidade S/A'),
+(246,'Banco ABC-Brasil S/A'),
+(247,'UBS WARBURG S/A'),
+(249,'Banco Investcred UNIBANCO S/A'),
+(250,'Banco SCHAHIN S/A'),
+(252,'Banco FININVEST S/A'),
+(254,'PARANA Banco S/A'),
+(263,'Banco CACIQUE S/A'),
+(265,'Banco Fator S/A'),
+(266,'Banco Cedula S/A'),
+(275,'Banco ABN AMRO Real S/A'),
+(291,'Banco de Cred. Nacional S/A'),
+(294,'BCR'),
+(300,'Banco de LA Nacion Argentina'),
+(318,'Banco BMG S/A'),
+(320,'Banco Ind. e Com. S/A'),
+(341,'Banco Itau S/A'),
+(346,'Banco BFB'),
+(347,'Banco Sudameris Brasil S/A'),
+(351,'Banco Bozano Simonsen S/A'),
+(353,'Banco Santander S/A'),
+(356,'Banco ABN AMRO S/A'),
+(366,'Banco Societe Generale Bras. S/A'),
+(370,'Banco Westlb do Brasil S/A'),
+(376,'Banco CHASE Manhattan S/A'),
+(389,'Banco Mercantil do Brasil S/A'),
+(392,'Banco Mercantil de Sao Paulo S/A'),
+(394,'Banco BMC S/A'),
+(399,'HSBC Bank Brasil S/A'),
+(409,'Unibanco Uniao de Bancos Bras. S/A'),
+(412,'Banco Capital S/A'),
+(422,'Banco Safra S/A'),
+(424,'Banco Santander Nordeste S/A'),
+(453,'Banco Rural S/A'),
+(456,'Banco de Tokio Mitsubishi BR S/A'),
+(464,'Banco Sumitomo Mitsui Bras. S/A'),
+(472,'LLOYDS Bank PLC'),
+(473,'Banco Financial Portugues S/A'),
+(477,'Banco Citibank N/A'),
+(479,'Bankboston Banco Multiplo S/A'),
+(487,'Deutsche Bank S/A'),
+(488,'Morgan G. Trust Company of NY'),
+(492,'ING Bank N.V.'),
+(493,'Banco Union - Brasil S/A'),
+(494,'Banco de La Rep. Or.Del Uruguai'),
+(495,'Banco de La Provinc.Buenos Aires'),
+(496,'Banco Uno-E Brasil S/A'),
+(505,'Banco Credit S. F. Boston S/A'),
+(600,'Banco Luso Brasileiro S/A'),
+(604,'Banco Industrial Brasileiro S/A'),
+(610,'Banco VR S/A'),
+(611,'Banco Paulista S/A'),
+(612,'Banco Guanabara S/A'),
+(613,'Banco Pecunia S/A'),
+(623,'Banco Panamericano S/A'),
+(626,'Banco FICSA S/A'),
+(630,'Banco Intercap S/A'),
+(633,'Banco Rendimento S/A'),
+(634,'Banco Triangulo S/A'),
+(637,'Banco SOFISA S/A'),
+(638,'Banco Prosper S/A'),
+(641,'Banco Bilbao Vizcaya Argentaria Brasil S/A'),
+(643,'Banco Pine S/A'),
+(650,'Banco PEBB S/A'),
+(652,'Banco Frances e Brasileiro S/A'),
+(653,'Banco Indusval S/A'),
+(654,'Banco A. J. RENNER S/A'),
+(655,'Banco Votorantim S/A'),
+(702,'Banco Santos S/A'),
+(707,'Banco Daycoval S/A'),
+(719,'Banco Banif Primus S/A'),
+(721,'Banco Credibel S/A'),
+(733,'Banco das Nacoes S/A'),
+(734,'Banco Gerdau S/A'),
+(735,'Banco Pottencial S/A'),
+(738,'Banco Morada S/A'),
+(739,'Banco BGN S/A'),
+(740,'Banco BARCLAYS S/A'),
+(741,'Banco Ribeirao Preto S/A'),
+(743,'Banco Emblema S/A'),
+(744,'Bankboston N.A.'),
+(745,'Banco Citibank S/A'),
+(746,'Banco Modal S/A'),
+(747,'Banco Rabobank INT Brasil S/A'),
+(748,'Banco Coop. Sic. S/A BANSICREDI'),
+(749,'BR Banco Mercantil S/A'),
+(751,'DRESDNER Bank Brasil S/A'),
+(752,'Banco BNP Paribas Brasil S/A'),
+(753,'Banco Comercial Uruguai S/A'),
+(756,'Banco Cooperativo do Brasil S/A'),
+(757,'Banco KEB do Brasil S/A');
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ProcedureBKP-DIFF_AUTO.sql b/ArquivosAuxiliares/ProcedureBKP-DIFF_AUTO.sql
new file mode 100644
index 0000000..ad63822
--- /dev/null
+++ b/ArquivosAuxiliares/ProcedureBKP-DIFF_AUTO.sql
@@ -0,0 +1,44 @@
+USE [msdb]
+GO
+
+-- =============================================
+-- Job: Backup DIFERENCIAL - a cada 6 horas
+-- =============================================
+EXEC sp_add_job @job_name = N'Backup DIFERENCIAL - Levelcode-LevelOS';
+
+EXEC sp_add_jobstep
+ @job_name = N'Backup DIFERENCIAL - Levelcode-LevelOS',
+ @step_name = N'Executar Backup DIFERENCIAL',
+ @command = N'EXEC [Levelcode-LevelOS].[dbo].[sp_BackupDiferencial]',
+ @database_name = N'Levelcode-LevelOS';
+
+EXEC sp_add_schedule
+ @schedule_name = N'A cada 6 horas',
+ @freq_type = 4, -- diário
+ @freq_interval = 1,
+ @freq_subday_type = 8, -- a cada X horas
+ @freq_subday_interval = 6, -- 6 horas
+ @active_start_time = 080000; -- começa às 08:00
+
+EXEC sp_attach_schedule
+ @job_name = N'Backup DIFERENCIAL - Levelcode-LevelOS',
+ @schedule_name = N'A cada 6 horas';
+
+EXEC sp_add_jobserver
+ @job_name = N'Backup DIFERENCIAL - Levelcode-LevelOS';
+GO
+
+-- Confirma os jobs criados
+SELECT
+ j.name AS Job,
+ s.name AS Schedule,
+ s.active_start_time,
+ CASE s.freq_subday_type
+ WHEN 8 THEN CAST(s.freq_subday_interval AS VARCHAR) + 'h em ' + CAST(s.freq_subday_interval AS VARCHAR) + 'h'
+ ELSE 'Diário fixo'
+ END AS Frequencia
+FROM msdb.dbo.sysjobs j
+JOIN msdb.dbo.sysjobschedules js ON j.job_id = js.job_id
+JOIN msdb.dbo.sysschedules s ON js.schedule_id = s.schedule_id
+WHERE j.name LIKE '%Levelcode%'
+ORDER BY j.name;
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ProcedureBKP-FULL_AUTO.sql b/ArquivosAuxiliares/ProcedureBKP-FULL_AUTO.sql
new file mode 100644
index 0000000..01251e3
--- /dev/null
+++ b/ArquivosAuxiliares/ProcedureBKP-FULL_AUTO.sql
@@ -0,0 +1,25 @@
+USE [msdb]
+GO
+
+-- Job: Backup FULL - toda madrugada às 02:00
+EXEC sp_add_job @job_name = N'Backup FULL - Levelcode-LevelOS';
+
+EXEC sp_add_jobstep
+ @job_name = N'Backup FULL - Levelcode-LevelOS',
+ @step_name = N'Executar Backup FULL',
+ @command = N'EXEC [Levelcode-LevelOS].[dbo].[sp_BackupFull]',
+ @database_name = N'Levelcode-LevelOS';
+
+EXEC sp_add_schedule
+ @schedule_name = N'Diario 02:00',
+ @freq_type = 4, -- diário
+ @freq_interval = 1,
+ @active_start_time = 020000; -- 02:00:00
+
+EXEC sp_attach_schedule
+ @job_name = N'Backup FULL - Levelcode-LevelOS',
+ @schedule_name = N'Diario 02:00';
+
+EXEC sp_add_jobserver
+ @job_name = N'Backup FULL - Levelcode-LevelOS';
+GO
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ProcedureBackups.sql b/ArquivosAuxiliares/ProcedureBackups.sql
new file mode 100644
index 0000000..5214bd6
--- /dev/null
+++ b/ArquivosAuxiliares/ProcedureBackups.sql
@@ -0,0 +1,61 @@
+USE [Levelcode-LevelOS]
+GO
+
+DROP PROCEDURE IF EXISTS [dbo].[sp_BackupFull]
+GO
+
+CREATE PROCEDURE [dbo].[sp_BackupFull]
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DECLARE @Caminho NVARCHAR(500);
+
+ SET @Caminho = '/var/opt/mssql/backups/Levelcode-LevelOS_FULL_'
+ + FORMAT(GETDATE(), 'yyyyMMdd_HHmmss') + '.bak';
+
+ BACKUP DATABASE [Levelcode-LevelOS]
+ TO DISK = @Caminho
+ WITH FORMAT,
+ INIT,
+ NAME = 'Backup FULL - Levelcode-LevelOS',
+ STATS = 10;
+END
+GO
+
+-- Confirma criação
+SELECT name, create_date, modify_date
+FROM sys.procedures
+WHERE name = 'sp_BackupFull';
+
+
+USE [Levelcode-LevelOS]
+GO
+
+DROP PROCEDURE IF EXISTS [dbo].[sp_BackupDiferencial]
+GO
+
+CREATE PROCEDURE [dbo].[sp_BackupDiferencial]
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DECLARE @Caminho NVARCHAR(500);
+
+ SET @Caminho = '/var/opt/mssql/backups/Levelcode-LevelOS_DIFF_'
+ + FORMAT(GETDATE(), 'yyyyMMdd_HHmmss') + '.bak';
+
+ BACKUP DATABASE [Levelcode-LevelOS]
+ TO DISK = @Caminho
+ WITH DIFFERENTIAL,
+ INIT,
+ NAME = 'Backup DIFERENCIAL - Levelcode-LevelOS',
+ STATS = 10;
+END
+GO
+
+-- Confirma ambas criadas
+SELECT name, create_date, modify_date
+FROM sys.procedures
+WHERE name IN ('sp_BackupFull', 'sp_BackupDiferencial')
+ORDER BY name;
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ProcedureRestaurarBanco.sql b/ArquivosAuxiliares/ProcedureRestaurarBanco.sql
new file mode 100644
index 0000000..cbd51ec
--- /dev/null
+++ b/ArquivosAuxiliares/ProcedureRestaurarBanco.sql
@@ -0,0 +1,128 @@
+USE [master]
+GO
+
+DROP PROCEDURE IF EXISTS [dbo].[sp_RestaurarBackup]
+GO
+
+CREATE PROCEDURE [dbo].[sp_RestaurarBackup]
+ @Arquivo NVARCHAR(500) = NULL,
+ @Tipo CHAR(1) = 'F' -- 'F' = FULL | 'D' = DIFERENCIAL
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ -- =============================================
+ -- Se não informar arquivo, lista os disponíveis
+ -- =============================================
+ IF @Arquivo IS NULL
+ BEGIN
+ PRINT '>>> Backups disponíveis no histórico:';
+
+ SELECT
+ ROW_NUMBER() OVER (ORDER BY bs.backup_finish_date DESC) AS N,
+ CASE bs.type
+ WHEN 'D' THEN 'FULL'
+ WHEN 'I' THEN 'DIFERENCIAL'
+ END AS Tipo,
+ bmf.physical_device_name AS Arquivo,
+ bs.backup_start_date AS Inicio,
+ bs.backup_finish_date AS Fim,
+ CAST(bs.backup_size / 1024.0 / 1024.0 AS DECIMAL(10,2)) AS TamanhoMB
+ FROM msdb.dbo.backupset bs
+ JOIN msdb.dbo.backupmediafamily bmf
+ ON bs.media_set_id = bmf.media_set_id
+ WHERE bs.database_name = 'Levelcode-LevelOS'
+ ORDER BY bs.backup_finish_date DESC;
+
+ PRINT '>>> Copie o caminho do arquivo desejado e execute:';
+ PRINT '>>> EXEC sp_RestaurarBackup @Arquivo = ''caminho_aqui'', @Tipo = ''F''';
+ RETURN;
+ END
+
+ -- =============================================
+ -- Verifica o arquivo com RESTORE HEADERONLY
+ -- =============================================
+ BEGIN TRY
+ RESTORE HEADERONLY FROM DISK = @Arquivo;
+ END TRY
+ BEGIN CATCH
+ RAISERROR('Arquivo não encontrado ou inválido: %s', 16, 1, @Arquivo);
+ RETURN;
+ END CATCH
+
+ -- =============================================
+ -- Coloca o banco em SINGLE_USER
+ -- =============================================
+ PRINT '>>> Preparando banco para restauração...';
+
+ BEGIN TRY
+ ALTER DATABASE [Levelcode-LevelOS]
+ SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
+ END TRY
+ BEGIN CATCH
+ PRINT '>>> Aviso: ' + ERROR_MESSAGE();
+ END CATCH
+
+ -- =============================================
+ -- Restauração FULL
+ -- =============================================
+ IF @Tipo = 'F'
+ BEGIN
+ PRINT '>>> Restaurando FULL: ' + @Arquivo;
+
+ RESTORE DATABASE [Levelcode-LevelOS]
+ FROM DISK = @Arquivo
+ WITH REPLACE,
+ NORECOVERY,
+ STATS = 10;
+
+ ALTER DATABASE [Levelcode-LevelOS] SET MULTI_USER;
+
+ PRINT '>>> FULL restaurado com sucesso!';
+ PRINT '>>> Se tiver diferencial execute: EXEC sp_RestaurarBackup @Arquivo = ''caminho_diff'', @Tipo = ''D''';
+ PRINT '>>> Se não tiver diferencial execute: RESTORE DATABASE [Levelcode-LevelOS] WITH RECOVERY;';
+ END
+
+ -- =============================================
+ -- Restauração DIFERENCIAL
+ -- =============================================
+ IF @Tipo = 'D'
+ BEGIN
+ PRINT '>>> Aplicando DIFERENCIAL: ' + @Arquivo;
+
+ RESTORE DATABASE [Levelcode-LevelOS]
+ FROM DISK = @Arquivo
+ WITH RECOVERY,
+ STATS = 10;
+
+ ALTER DATABASE [Levelcode-LevelOS] SET MULTI_USER;
+
+ PRINT '>>> Banco restaurado e online!';
+ END
+END
+GO
+
+--Como usar --
+-- 1. Lista os backups disponíveis
+EXEC [master].[dbo].[sp_RestaurarBackup];
+
+-- 2. Restaura apenas o FULL (sem diferencial)
+EXEC [master].[dbo].[sp_RestaurarBackup]
+ @Arquivo = '/var/opt/mssql/backups/Levelcode-LevelOS_FULL_20260413_020000.bak',
+ @Tipo = 'F';
+
+-- Finaliza o banco após o FULL sem diferencial
+RESTORE DATABASE [Levelcode-LevelOS] WITH RECOVERY;
+
+-- 3. Restaura FULL + DIFERENCIAL (sequência completa)
+EXEC [master].[dbo].[sp_RestaurarBackup]
+ @Arquivo = '/var/opt/mssql/backups/Levelcode-LevelOS_FULL_20260413_020000.bak',
+ @Tipo = 'F';
+
+EXEC [master].[dbo].[sp_RestaurarBackup]
+ @Arquivo = '/var/opt/mssql/backups/Levelcode-LevelOS_DIFF_20260413_080000.bak',
+ @Tipo = 'D';
+
+EXEC [master].[dbo].[sp_RestaurarBackup]
+ @Arquivo = '/var/opt/mssql/backups/Levelcode-LevelOS_FULL_20260413_072013.bak',
+ @Tipo = 'F';
\ No newline at end of file
diff --git a/ArquivosAuxiliares/Procedure_LIMPEZA15DIAS.sql b/ArquivosAuxiliares/Procedure_LIMPEZA15DIAS.sql
new file mode 100644
index 0000000..437ff4d
--- /dev/null
+++ b/ArquivosAuxiliares/Procedure_LIMPEZA15DIAS.sql
@@ -0,0 +1,77 @@
+USE [Levelcode-LevelOS]
+GO
+
+-- =============================================
+-- Procedure: Limpeza de backups antigos (+15 dias)
+-- =============================================
+DROP PROCEDURE IF EXISTS [dbo].[sp_LimpezaBackups]
+GO
+
+CREATE PROCEDURE [dbo].[sp_LimpezaBackups]
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DECLARE @DataCorte DATETIME = DATEADD(DAY, -15, GETDATE());
+ DECLARE @Comando NVARCHAR(500);
+ DECLARE @Arquivo NVARCHAR(500);
+
+ -- Cursor nos backups com mais de 15 dias
+ DECLARE cur CURSOR FOR
+ SELECT DISTINCT bmf.physical_device_name
+ FROM msdb.dbo.backupset bs
+ JOIN msdb.dbo.backupmediafamily bmf
+ ON bs.media_set_id = bmf.media_set_id
+ WHERE bs.database_name = 'Levelcode-LevelOS'
+ AND bs.backup_finish_date < @DataCorte
+ AND bmf.physical_device_name LIKE '/var/opt/mssql/backups/%';
+
+ OPEN cur;
+ FETCH NEXT FROM cur INTO @Arquivo;
+
+ WHILE @@FETCH_STATUS = 0
+ BEGIN
+ -- Deleta o arquivo físico no Linux
+ SET @Comando = 'rm -f ' + @Arquivo;
+ EXEC xp_cmdshell @Comando;
+
+ -- Remove o histórico do msdb
+ PRINT 'Removido: ' + @Arquivo;
+
+ FETCH NEXT FROM cur INTO @Arquivo;
+ END
+
+ CLOSE cur;
+ DEALLOCATE cur;
+
+ -- Limpa o histórico do msdb também
+ EXEC msdb.dbo.sp_delete_backuphistory @oldest_date = @DataCorte;
+
+ PRINT 'Limpeza concluída. Backups anteriores a '
+ + CONVERT(NVARCHAR(20), @DataCorte, 120) + ' removidos.';
+END
+GO
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+USE [Levelcode-LevelOS]
+GO
+
+-- =============================================
+-- Procedure: Limpeza do HISTÓRICO (msdb) +15 dias
+-- =============================================
+DROP PROCEDURE IF EXISTS [dbo].[sp_LimpezaBackups]
+GO
+
+CREATE PROCEDURE [dbo].[sp_LimpezaBackups]
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ DECLARE @DataCorte DATETIME = DATEADD(DAY, -15, GETDATE());
+
+ -- Limpa apenas o histórico do msdb (não precisa de xp_cmdshell)
+ EXEC msdb.dbo.sp_delete_backuphistory @oldest_date = @DataCorte;
+
+ PRINT 'Histórico de backups anteriores a '
+ + CONVERT(NVARCHAR(20), @DataCorte, 120) + ' removido do msdb.';
+END
+GO
\ No newline at end of file
diff --git a/ArquivosAuxiliares/ScriptLimpezaServidor.sh b/ArquivosAuxiliares/ScriptLimpezaServidor.sh
new file mode 100644
index 0000000..1af4c44
--- /dev/null
+++ b/ArquivosAuxiliares/ScriptLimpezaServidor.sh
@@ -0,0 +1,32 @@
+# Cria o script de limpeza
+cat > /var/opt/mssql/backups/limpar_backups.sh << 'EOF'
+#!/bin/bash
+# Deleta arquivos .bak com mais de 15 dias
+find /var/opt/mssql/backups/ -name "*.bak" -mtime +15 -delete
+echo "[$(date)] Limpeza concluída." >> /var/opt/mssql/backups/limpeza.log
+EOF
+
+# Dá permissão de execução
+chmod +x /var/opt/mssql/backups/limpar_backups.sh
+
+# Agenda no cron todo domingo às 03:00
+(crontab -l 2>/dev/null; echo "0 3 * * 0 /var/opt/mssql/backups/limpar_backups.sh") | crontab -
+
+# Confirma o cron
+crontab -l
+
+#-------------------------------------------------------------------------------------------------
+cat > /var/opt/mssql/backups/limpar_backups.sh << 'EOF'
+#!/bin/bash
+find /var/opt/mssql/backups/ -name "*.bak" -mtime +15 -delete
+echo "[$(date)] Limpeza concluída." >> /var/opt/mssql/backups/limpeza.log
+EOF
+
+#Dar permissão
+chmod +x /var/opt/mssql/backups/limpar_backups.sh
+# Executar script
+/var/opt/mssql/backups/limpar_backups.sh && cat /var/opt/mssql/backups/limpeza.log
+# Agendar JOB
+(crontab -l 2>/dev/null; echo "0 3 * * 0 /var/opt/mssql/backups/limpar_backups.sh") | crontab -
+# confirmar agendamento
+crontab -l
\ No newline at end of file
diff --git a/ArquivosAuxiliares/TriggerFuncionarios.sql b/ArquivosAuxiliares/TriggerFuncionarios.sql
new file mode 100644
index 0000000..bb68135
--- /dev/null
+++ b/ArquivosAuxiliares/TriggerFuncionarios.sql
@@ -0,0 +1,14 @@
+--Gatilhos para a tabela funcionarios--
+CREATE OR ALTER TRIGGER TR_Funcionarios_GerarCodigo
+ON Funcionarios
+AFTER INSERT
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ UPDATE F
+ SET CODIGO = CAST(100 + ((F.ID_FUNCIONARIO - 1) * 100) AS VARCHAR)
+ FROM Funcionarios F
+ INNER JOIN inserted I ON F.ID_FUNCIONARIO = I.ID_FUNCIONARIO;
+END
+GO
\ No newline at end of file
diff --git a/ArquivosAuxiliares/Trigger_AgendaCodigoAuto.sql b/ArquivosAuxiliares/Trigger_AgendaCodigoAuto.sql
new file mode 100644
index 0000000..1778b25
--- /dev/null
+++ b/ArquivosAuxiliares/Trigger_AgendaCodigoAuto.sql
@@ -0,0 +1,13 @@
+CREATE OR ALTER TRIGGER TR_Agenda_GerarCodigo
+ON Agenda
+AFTER INSERT
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ UPDATE A
+ SET CODIGO = 'AG' + RIGHT('0000' + CAST(A.ID_AGENDA AS VARCHAR), 4)
+ FROM Agenda A
+ INNER JOIN inserted I ON A.ID_AGENDA = I.ID_AGENDA;
+END
+GO
\ No newline at end of file
diff --git a/ControlesCustom.cs b/ControlesCustom.cs
new file mode 100644
index 0000000..de894d4
--- /dev/null
+++ b/ControlesCustom.cs
@@ -0,0 +1,151 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class RoundTextBox : UserControl
+ {
+ private TextBox _textBox = null!;
+ public int Radius { get; set; } = 4;
+ public Color BorderColor { get; set; } = Color.LightGray;
+ public Color FocusColor { get; set; } = Color.Blue;
+ private bool _focused;
+
+ public bool ReadOnly
+ {
+ get => _textBox.ReadOnly;
+ set => _textBox.ReadOnly = value;
+ }
+ public char PasswordChar
+ {
+ get => _textBox.PasswordChar;
+ set => _textBox.PasswordChar = value;
+ }
+
+ // ── Adiciona isso aqui ──
+ public int SelectionStart
+ {
+ get => _textBox.SelectionStart;
+ set => _textBox.SelectionStart = value;
+ }
+ public new event KeyEventHandler? KeyDown
+ {
+ add => _textBox.KeyDown += value;
+ remove => _textBox.KeyDown -= value;
+ }
+
+ public bool Multiline
+ {
+ get => _textBox.Multiline;
+ set
+ {
+ _textBox.Multiline = value;
+ _textBox.ScrollBars = value ? ScrollBars.Vertical : ScrollBars.None;
+ AjustarTextBox();
+ }
+ }
+
+ public override string Text
+ {
+ get => _textBox.Text;
+ set => _textBox.Text = value;
+ }
+
+ public new Color BackColor
+ {
+ get => base.BackColor;
+ set { base.BackColor = value; if (_textBox != null) _textBox.BackColor = value; }
+ }
+
+ //public RoundTextBox()
+ //{
+ // DoubleBuffered = true;
+ // base.BackColor = Color.White;
+ // _textBox = new TextBox
+ // {
+ // BorderStyle = BorderStyle.None,
+ // Font = new Font("Segoe UI", 9f),
+ // Location = new Point(6, 6),
+ // Width = Width - 12,
+ // Height = Height - 12,
+ // BackColor = Color.White
+ // };
+ // _textBox.GotFocus += (s, e) => { _focused = true; Invalidate(); };
+ // _textBox.LostFocus += (s, e) => { _focused = false; Invalidate(); };
+ // Controls.Add(_textBox);
+ // SizeChanged += (s, e) => AjustarTextBox();
+ //}
+ public RoundTextBox()
+ {
+ DoubleBuffered = true;
+ base.BackColor = Color.White;
+ _textBox = new TextBox
+ {
+ BorderStyle = BorderStyle.None,
+ Font = new Font("Segoe UI", 9f),
+ Location = new Point(6, 6),
+ Width = Width - 12,
+ Height = Height - 12,
+ BackColor = Color.White
+ };
+
+ _textBox.GotFocus += (s, e) => { _focused = true; Invalidate(); };
+ _textBox.LostFocus += (s, e) => { _focused = false; Invalidate(); };
+ _textBox.TextChanged += (s, e) => OnTextChanged(e); // ← adiciona essa
+ _textBox.Leave += (s, e) => OnLeave(e); // ← e essa
+
+ Controls.Add(_textBox);
+ SizeChanged += (s, e) => AjustarTextBox();
+ }
+
+ // Ajusta largura e altura do TextBox interno conforme Multiline
+ private void AjustarTextBox()
+ {
+ _textBox.Width = Width - 12;
+ _textBox.Height = _textBox.Multiline ? Height - 12 : _textBox.PreferredHeight;
+ // Recentraliza verticalmente quando não é multiline
+ if (!_textBox.Multiline)
+ _textBox.Location = new Point(6, (Height - _textBox.PreferredHeight) / 2);
+ else
+ _textBox.Location = new Point(6, 6);
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ using var path = GetPath(new Rectangle(0, 0, Width - 1, Height - 1), Radius);
+ using var brush = new SolidBrush(BackColor);
+ e.Graphics.FillPath(brush, path);
+ using var pen = new Pen(_focused ? FocusColor : BorderColor, _focused ? 1.5f : 1f);
+ e.Graphics.DrawPath(pen, path);
+ }
+
+ private static GraphicsPath GetPath(Rectangle r, int rad)
+ {
+ var path = new GraphicsPath();
+ int d = rad * 2;
+ path.AddArc(r.X, r.Y, d, d, 180, 90);
+ path.AddArc(r.Right - d, r.Y, d, d, 270, 90);
+ path.AddArc(r.Right - d, r.Bottom - d, d, d, 0, 90);
+ path.AddArc(r.X, r.Bottom - d, d, d, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+ }
+
+ public class RoundButton : Button
+ {
+ [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
+ private static extern IntPtr CreateRoundRectRgn(int nL, int nT, int nR, int nB, int nW, int nH);
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ base.OnPaint(e);
+ Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 8, 8));
+ FlatStyle = FlatStyle.Flat;
+ FlatAppearance.BorderSize = 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ControlesCustom.resx b/ControlesCustom.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/ControlesCustom.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Cadastros/AgendaCadastroPanel.cs b/Dashboards/Cadastros/AgendaCadastroPanel.cs
new file mode 100644
index 0000000..d884975
--- /dev/null
+++ b/Dashboards/Cadastros/AgendaCadastroPanel.cs
@@ -0,0 +1,887 @@
+using BLL;
+using CustomMessageBox;
+using DAL;
+using MLL;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class AgendaCadastroPanel : UserControl
+ {
+ string _cx = DadosDaConexao.ObterConexao();
+ // ── CORES ─────────────────────────────────────────────────────────────
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+ private readonly Color GreenColor = Color.FromArgb(34, 197, 94);
+ private readonly Color AmberColor = Color.FromArgb(245, 158, 11);
+ private readonly Color RedColor = Color.FromArgb(239, 68, 68);
+ private readonly Color MutedGray = Color.FromArgb(148, 163, 184);
+ private readonly Color SurfaceColor = Color.FromArgb(248, 250, 252);
+ private readonly Color DisabledBack = Color.FromArgb(241, 245, 249);
+
+ // ── LAYOUT ────────────────────────────────────────────────────────────
+ private Panel pnlToolbar = null!;
+ private Panel pnlLeft = null!;
+ private Panel pnlRight = null!;
+ private Panel pnlEventList = null!;
+ private Panel pnlSplit = null!;
+ private Panel pnlDias = null!;
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────────
+ private RoundButton btnNovo = null!;
+ private RoundButton btnAlterar = null!;
+ private RoundButton btnExcluir = null!;
+ private RoundButton btnLocalizar = null!;
+ private RoundButton btnSalvar = null!;
+ private RoundButton btnCancelar = null!;
+
+ // ── CAMPOS DO FORMULÁRIO ──────────────────────────────────────────────
+ private RoundTextBox txtId = null!;
+ private RoundTextBox txtCodigo = null!;
+ private RoundTextBox txtCompromisso = null!;
+ private RoundTextBox txtData = null!;
+ private RoundTextBox txtDia = null!;
+ private RoundTextBox txtHora = null!;
+ private RoundTextBox txtFunc = null!;
+ private RoundTextBox txtAvisar = null!;
+ private RoundTextBox txtOsVinc = null!;
+ private CheckBox chkRealizado = null!;
+
+ // ── INFO (readonly) ───────────────────────────────────────────────────
+ private RoundTextBox txtCriadoEm = null!;
+ private RoundTextBox txtAtualizadoEm = null!;
+
+ // ── CALENDÁRIO ────────────────────────────────────────────────────────
+ private Label lblMesAno = null!;
+ private Button btnPrev = null!;
+ private Button btnNext = null!;
+
+ // ── ESTADO ────────────────────────────────────────────────────────────
+ private DateTime _currentMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
+ private DateTime? _selectedDate = null;
+ private List _eventos = new();
+ private int _nextId = 1;
+
+ // ── CONSTRUTOR ────────────────────────────────────────────────────────
+ public AgendaCadastroPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+
+ InitializeLayout();
+ SetCampos(false);
+ //CarregarDadosFake();
+ CarregarDadosDoBanco();
+ RenderCalendario();
+ RenderListaEventos();
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // LAYOUT
+ // ══════════════════════════════════════════════════════════════════════
+ private void InitializeLayout()
+ {
+ Controls.Clear();
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = SurfaceColor
+ };
+
+ var flow = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0),
+ BackColor = Color.Transparent
+ };
+
+ btnNovo = CreateToolbarButton("Novo", GreenColor);
+ btnAlterar = CreateToolbarButton("Alterar", AmberColor);
+ btnExcluir = CreateToolbarButton("Excluir", RedColor);
+ btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
+ btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
+ btnCancelar = CreateToolbarButton("Cancelar", MutedGray);
+
+ btnNovo.Click += (_, _) => BtnNovo_Click();
+ btnAlterar.Click += (_, _) => BtnAlterar_Click();
+ btnExcluir.Click += (_, _) => BtnExcluir_Click();
+ btnLocalizar.Click += (_, _) => BtnLocalizar_Click();
+ btnSalvar.Click += (_, _) => BtnSalvar_Click();
+ btnCancelar.Click += (_, _) => BtnCancelar_Click();
+
+ flow.Controls.AddRange(new Control[]
+ { btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar, btnCancelar });
+
+ pnlToolbar.Controls.Add(flow);
+ Controls.Add(pnlToolbar);
+
+ // ── SPLIT ─────────────────────────────────────────────────────────
+ pnlSplit = new Panel { Dock = DockStyle.Fill, BackColor = Color.White };
+ Controls.Add(pnlSplit);
+ pnlSplit.BringToFront();
+
+ // RIGHT ─ calendário + lista
+ pnlRight = new Panel
+ {
+ Dock = DockStyle.Right,
+ Width = 340,
+ BackColor = Color.White,
+ Padding = new Padding(10, 10, 10, 10)
+ };
+ pnlSplit.Controls.Add(pnlRight);
+ BuildCalendario(); // preenche pnlRight
+
+ // Divider
+ var divider = new Panel
+ {
+ Dock = DockStyle.Right,
+ Width = 1,
+ BackColor = BorderColor
+ };
+ pnlSplit.Controls.Add(divider);
+
+ // LEFT ─ formulário
+ pnlLeft = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ BackColor = Color.White
+ };
+ pnlSplit.Controls.Add(pnlLeft);
+ BuildFormulario();
+ }
+
+ // ── FORMULÁRIO ────────────────────────────────────────────────────────
+ private void BuildFormulario()
+ {
+ var content = new Panel { Width = 800, BackColor = Color.White };
+ pnlLeft.Controls.Add(content);
+
+ const int rowH = 52;
+ const int secH = 28;
+ const int secGap = 10;
+ const int inputH = 28;
+ int y = 10;
+
+ // IDENTIFICAÇÃO
+ content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO", y));
+ y += secH + 4;
+
+ txtId = AddInput(content, "ID", 20, y, 60, inputH, readOnly: true);
+ txtCodigo = AddInput(content, "Código", 90, y, 120, inputH);
+ txtCompromisso = AddInput(content, "Compromisso", 220, y, 520, inputH);
+ y += rowH;
+
+ // DATA E HORA
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("DATA E HORA", y));
+ y += secH + 4;
+
+ txtData = AddInput(content, "Data", 20, y, 130, inputH, readOnly: true);
+ txtDia = AddInput(content, "Dia", 160, y, 150, inputH, readOnly: true);
+ txtHora = AddInput(content, "Hora", 320, y, 100, inputH);
+ y += rowH;
+
+ // RESPONSÁVEL E AVISO
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("RESPONSÁVEL E AVISO", y));
+ y += secH + 4;
+
+ txtFunc = AddInput(content, "Funcionário", 20, y, 280, inputH);
+ txtAvisar = AddInput(content, "Avisar", 310, y, 180, inputH);
+ txtOsVinc = AddInput(content, "OS Vinculada", 500, y, 140, inputH);
+ y += rowH;
+
+ // SITUAÇÃO
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("SITUAÇÃO", y));
+ y += secH + 4;
+
+ chkRealizado = new CheckBox
+ {
+ Text = "Realizado",
+ Location = new Point(20, y + 4),
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+ content.Controls.Add(chkRealizado);
+ y += rowH;
+
+ // INFORMAÇÕES DO REGISTRO
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("INFORMAÇÕES DO REGISTRO", y));
+ y += secH + 4;
+
+ txtCriadoEm = AddInput(content, "Criado Em", 20, y, 175, inputH, readOnly: true);
+ txtAtualizadoEm = AddInput(content, "Atualizado Em", 205, y, 175, inputH, readOnly: true);
+ y += rowH;
+
+ content.Height = y + 10;
+ }
+
+ // ── CALENDÁRIO ────────────────────────────────────────────────────────
+ // ATENÇÃO: No WinForms, DockStyle.Fill deve ser adicionado ANTES dos
+ // DockStyle.Top — a ordem de inserção em Controls é invertida visualmente.
+ private void BuildCalendario()
+ {
+ // 1º — Lista de eventos (Fill) → adicionada PRIMEIRO
+ pnlEventList = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ BackColor = Color.White,
+ Padding = new Padding(0, 4, 0, 0)
+ };
+ pnlRight.Controls.Add(pnlEventList); // ← PRIMEIRO
+
+ // 2º — Grid dos dias (Top) → adicionado DEPOIS do Fill
+ pnlDias = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 215,
+ BackColor = Color.White
+ };
+ pnlRight.Controls.Add(pnlDias); // ← SEGUNDO
+
+ // 3º — Cabeçalho mês/ano (Top) → adicionado POR ÚLTIMO (aparece no topo)
+ var pnlCalHeader = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 38,
+ BackColor = Color.White
+ };
+
+ btnPrev = new Button
+ {
+ Text = "‹",
+ Size = new Size(28, 26),
+ Location = new Point(0, 6),
+ FlatStyle = FlatStyle.Flat,
+ Font = new Font("Segoe UI", 13f),
+ ForeColor = AccentBlue,
+ BackColor = Color.White,
+ Cursor = Cursors.Hand
+ };
+ btnPrev.FlatAppearance.BorderColor = BorderColor;
+ btnPrev.Click += (_, _) =>
+ {
+ _currentMonth = _currentMonth.AddMonths(-1);
+ RenderCalendario();
+ RenderListaEventos();
+ };
+
+ btnNext = new Button
+ {
+ Text = "›",
+ Size = new Size(28, 26),
+ Location = new Point(290, 6),
+ FlatStyle = FlatStyle.Flat,
+ Font = new Font("Segoe UI", 13f),
+ ForeColor = AccentBlue,
+ BackColor = Color.White,
+ Cursor = Cursors.Hand
+ };
+ btnNext.FlatAppearance.BorderColor = BorderColor;
+ btnNext.Click += (_, _) =>
+ {
+ _currentMonth = _currentMonth.AddMonths(1);
+ RenderCalendario();
+ RenderListaEventos();
+ };
+
+ lblMesAno = new Label
+ {
+ AutoSize = false,
+ TextAlign = ContentAlignment.MiddleCenter,
+ Font = new Font("Segoe UI", 10f, FontStyle.Bold),
+ ForeColor = TextDark,
+ Size = new Size(256, 26),
+ Location = new Point(32, 6)
+ };
+
+ pnlCalHeader.Controls.AddRange(new Control[] { btnPrev, lblMesAno, btnNext });
+ pnlRight.Controls.Add(pnlCalHeader); // ← ÚLTIMO (fica no topo visualmente)
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // RENDER CALENDÁRIO
+ // ══════════════════════════════════════════════════════════════════════
+ private void RenderCalendario()
+ {
+ pnlDias.Controls.Clear();
+
+ lblMesAno.Text = _currentMonth.ToString("MMMM yyyy",
+ new System.Globalization.CultureInfo("pt-BR")).ToUpper();
+
+ string[] dowLabels = { "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb" };
+ int cellW = 44;
+ int cellH = 26;
+ int startX = 2;
+
+ // Cabeçalho dias da semana
+ for (int i = 0; i < 7; i++)
+ {
+ pnlDias.Controls.Add(new Label
+ {
+ Text = dowLabels[i],
+ Size = new Size(cellW, 18),
+ Location = new Point(startX + i * cellW, 0),
+ TextAlign = ContentAlignment.MiddleCenter,
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = MutedGray
+ });
+ }
+
+ int firstDow = (int)new DateTime(_currentMonth.Year, _currentMonth.Month, 1).DayOfWeek;
+ int daysInMonth = DateTime.DaysInMonth(_currentMonth.Year, _currentMonth.Month);
+ var datesWithEvts = GetDatesWithEvents();
+ DateTime today = DateTime.Today;
+ int col = firstDow, row = 0;
+
+ for (int d = 1; d <= daysInMonth; d++)
+ {
+ var date = new DateTime(_currentMonth.Year, _currentMonth.Month, d);
+ bool isToday = date == today;
+ bool isSelected = _selectedDate.HasValue && date == _selectedDate.Value;
+ bool hasEvent = datesWithEvts.Contains(date.Date);
+
+ var btn = new Button
+ {
+ Text = hasEvent ? $"{d} •" : d.ToString(),
+ Size = new Size(cellW - 2, cellH),
+ Location = new Point(startX + col * cellW, 22 + row * (cellH + 2)),
+ FlatStyle = FlatStyle.Flat,
+ Font = new Font("Segoe UI", 8f,
+ isToday || isSelected ? FontStyle.Bold : FontStyle.Regular),
+ Cursor = Cursors.Hand,
+ Tag = date
+ };
+
+ if (isSelected)
+ {
+ btn.BackColor = AccentBlue;
+ btn.ForeColor = Color.White;
+ btn.FlatAppearance.BorderColor = AccentBlue;
+ }
+ else if (isToday)
+ {
+ btn.BackColor = Color.White;
+ btn.ForeColor = AccentBlue;
+ btn.FlatAppearance.BorderColor = AccentBlue;
+ }
+ else
+ {
+ btn.BackColor = Color.White;
+ btn.ForeColor = TextDark;
+ btn.FlatAppearance.BorderColor = BorderColor;
+ }
+
+ btn.Click += (s, _) =>
+ {
+ if (s is Button b && b.Tag is DateTime dt)
+ {
+ _selectedDate = dt;
+ if (txtData.Enabled)
+ {
+ txtData.Text = dt.ToString("dd/MM/yyyy");
+ txtDia.Text = new System.Globalization.CultureInfo("pt-BR")
+ .DateTimeFormat.GetDayName(dt.DayOfWeek);
+ }
+ RenderCalendario();
+ RenderListaEventos();
+ }
+ };
+
+ pnlDias.Controls.Add(btn);
+
+ col++;
+ if (col == 7) { col = 0; row++; }
+ }
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // RENDER LISTA DE EVENTOS
+ // ══════════════════════════════════════════════════════════════════════
+ private void RenderListaEventos()
+ {
+ pnlEventList.Controls.Clear();
+
+ var filtrados = _selectedDate.HasValue
+ ? _eventos.Where(e => ParseData(e.dDATA).Date == _selectedDate.Value.Date).ToList()
+ : _eventos.Where(e => ParseData(e.dDATA).Month == _currentMonth.Month
+ && ParseData(e.dDATA).Year == _currentMonth.Year).ToList();
+
+ filtrados = filtrados.OrderBy(e => e.HORA).ToList();
+
+ // Largura real do painel (com fallback para evitar 0)
+ int listW = pnlEventList.ClientSize.Width > 0
+ ? pnlEventList.ClientSize.Width
+ : pnlRight.Width - pnlRight.Padding.Horizontal - 20;
+
+ int y = 0;
+
+ // ── Título ────────────────────────────────────────────────────────
+ string titulo = _selectedDate.HasValue
+ ? $"COMPROMISSOS — {_selectedDate.Value:dd/MM/yyyy}"
+ : $"COMPROMISSOS — {_currentMonth.ToString("MMMM/yyyy", new System.Globalization.CultureInfo("pt-BR")).ToUpper()}";
+
+ pnlEventList.Controls.Add(new Label
+ {
+ Text = titulo,
+ AutoSize = false,
+ Width = listW,
+ Height = 18,
+ Location = new Point(0, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = MutedGray
+ });
+ y += 6;
+
+ // Linha separadora
+ pnlEventList.Controls.Add(new Panel
+ {
+ Location = new Point(0, y + 14),
+ Size = new Size(listW, 1),
+ BackColor = BorderColor
+ });
+ y += 22;
+
+ // ── Vazio ─────────────────────────────────────────────────────────
+ if (!filtrados.Any())
+ {
+ pnlEventList.Controls.Add(new Label
+ {
+ Text = "Nenhum compromisso encontrado.",
+ Location = new Point(0, y + 8),
+ AutoSize = true,
+ Font = new Font("Segoe UI", 8.5f),
+ ForeColor = MutedGray
+ });
+ return;
+ }
+
+ // ── Cards ─────────────────────────────────────────────────────────
+ foreach (var ev in filtrados)
+ {
+ bool realizado = ev.REALIZADO?.ToUpper() == "S";
+
+ var card = new Panel
+ {
+ Location = new Point(0, y),
+ Width = listW,
+ Height = 72,
+ BackColor = Color.White,
+ BorderStyle = BorderStyle.None,
+ Cursor = Cursors.Hand,
+ Tag = ev
+ };
+
+ // Borda esquerda colorida
+ card.Controls.Add(new Panel
+ {
+ Location = new Point(0, 0),
+ Size = new Size(3, 72),
+ BackColor = realizado ? GreenColor : AmberColor
+ });
+
+ card.Controls.Add(new Label
+ {
+ Text = $"{ev.HORA} — {ev.dDATA}",
+ Location = new Point(10, 5),
+ AutoSize = true,
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = AccentBlue
+ });
+ card.Controls.Add(new Label
+ {
+ Text = ev.COMPROMISSO,
+ Location = new Point(10, 21),
+ AutoSize = true,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = TextDark
+ });
+ card.Controls.Add(new Label
+ {
+ Text = string.IsNullOrWhiteSpace(ev.FUNC) ? "—" : ev.FUNC,
+ Location = new Point(10, 39),
+ AutoSize = true,
+ Font = new Font("Segoe UI", 8f),
+ ForeColor = MutedGray
+ });
+ card.Controls.Add(new Label
+ {
+ Text = realizado ? "✔ Realizado" : "⏳ Pendente",
+ Location = new Point(10, 55),
+ AutoSize = true,
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = realizado
+ ? Color.FromArgb(22, 101, 52)
+ : Color.FromArgb(146, 64, 14)
+ });
+
+ // Borda do card via Paint
+ card.Paint += (_, pe) =>
+ {
+ using var pen = new Pen(BorderColor);
+ pe.Graphics.DrawRectangle(pen, 0, 0, card.Width - 1, card.Height - 1);
+ };
+
+ // Clique em qualquer parte do card (inclusive labels filhos)
+ var evLocal = ev;
+ EventHandler clickHandler = (_, _) => CarregarEvento(evLocal);
+ card.Click += clickHandler;
+ foreach (Control child in card.Controls)
+ child.Click += clickHandler;
+
+ pnlEventList.Controls.Add(card);
+ y += 78;
+ }
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // EVENTOS DOS BOTÕES
+ // ══════════════════════════════════════════════════════════════════════
+ private void BtnNovo_Click()
+ {
+
+ LimparCampos();
+ SetCampos(true);
+ txtCriadoEm.Text = DateTime.Now.ToString("dd/MM/yyyy");
+
+
+ }
+
+ private void BtnAlterar_Click()
+ {
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ MessageBox.Show("Nenhum registro selecionado para alterar.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+ SetCampos(true);
+ }
+
+ private void BtnExcluir_Click()
+ {
+ BLLAgenda _agendaBLL = new BLLAgenda(this._cx);
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ NT_MessageBox.Show("Nenhum registro selecionado para excluir.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ var confirm = NT_MessageBox.Show("Confirma a exclusão deste registro?",
+ "Confirmar Exclusão", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
+
+ if (confirm == DialogResult.Yes)
+ {
+ int id = int.Parse(txtId.Text);
+
+ bool sucesso = _agendaBLL.Excluir(id);
+
+ if (!sucesso)
+ {
+ NT_MessageBox.Show("Erro ao excluir o registro.",
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ // 🔹 Atualiza lista do banco
+ _eventos.Clear();
+ _eventos.AddRange(_agendaBLL.Listar());
+
+ LimparCampos();
+ SetCampos(false);
+ RenderCalendario();
+ RenderListaEventos();
+
+ NT_MessageBox.Show("Registro excluído com sucesso!",
+ "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ }//Excluir registro selecionado
+
+ private void BtnLocalizar_Click()
+ {
+ FormHelper.Show("Agenda de Compromissos");
+ }
+
+ private void BtnSalvar_Click()
+ {
+ //if (string.IsNullOrWhiteSpace(txtData.Text) ||
+ // string.IsNullOrWhiteSpace(txtCompromisso.Text))
+ //{
+ // MessageBox.Show("Preencha ao menos Data e Compromisso.",
+ // "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ // return;
+ //}
+
+ //bool isNew = string.IsNullOrWhiteSpace(txtId.Text);
+ //int id = isNew ? _nextId++ : int.Parse(txtId.Text);
+
+ //var agenda = new MLL.ModeloAgenda(
+ // iD_AGENDA: id,
+ // cODIGO: txtCodigo.Text,
+ // cOMPROMISSO: txtCompromisso.Text,
+ // dDATA: txtData.Text,
+ // aVISAR: txtAvisar.Text,
+ // fUNC: txtFunc.Text,
+ // dIA: txtDia.Text,
+ // hORA: txtHora.Text,
+ // rEALIZADO: chkRealizado.Checked ? "S" : "N",
+ // oS_VINC: txtOsVinc.Text
+ //);
+
+ //if (isNew)
+ //{
+ // _eventos.Add(agenda);
+ // txtId.Text = id.ToString();
+ //}
+ //else
+ //{
+ // int idx = _eventos.FindIndex(e => e.ID_AGENDA == id);
+ // if (idx >= 0) _eventos[idx] = agenda;
+ //}
+
+ //txtAtualizadoEm.Text = DateTime.Now.ToString("dd/MM/yyyy");
+ //SetCampos(false);
+ //RenderCalendario();
+ //RenderListaEventos();
+
+ //MessageBox.Show("Registro salvo com sucesso!", "Sucesso",
+ // MessageBoxButtons.OK, MessageBoxIcon.Information);
+ BLLAgenda _agendaBLL = new BLLAgenda(_cx);
+ if (string.IsNullOrWhiteSpace(txtData.Text) ||
+ string.IsNullOrWhiteSpace(txtCompromisso.Text))
+ {
+ MessageBox.Show("Preencha ao menos Data e Compromisso.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ bool isNew = string.IsNullOrWhiteSpace(txtId.Text);
+
+ var agenda = new MLL.ModeloAgenda(
+ iD_AGENDA: isNew ? 0 : int.Parse(txtId.Text),
+ cODIGO: txtCodigo.Text,
+ cOMPROMISSO: txtCompromisso.Text,
+ dDATA: txtData.Text,
+ aVISAR: txtAvisar.Text,
+ fUNC: txtFunc.Text,
+ dIA: txtDia.Text,
+ hORA: txtHora.Text,
+ rEALIZADO: chkRealizado.Checked ? "S" : "N",
+ oS_VINC: txtOsVinc.Text
+ );
+
+ bool sucesso;
+
+ if (isNew)
+ {
+ sucesso = _agendaBLL.Inserir(agenda);
+ }
+ else
+ {
+ sucesso = _agendaBLL.Alterar(agenda);
+ }
+
+ if (!sucesso)
+ {
+ MessageBox.Show("Erro ao salvar no banco de dados.",
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ // 🔹 Recarrega dados do banco
+ _eventos = _agendaBLL.Listar();
+
+ // 🔹 Atualiza UI
+ txtAtualizadoEm.Text = DateTime.Now.ToString("dd/MM/yyyy");
+ SetCampos(false);
+ RenderCalendario();
+ RenderListaEventos();
+
+ MessageBox.Show("Registro salvo com sucesso!", "Sucesso",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ private void BtnCancelar_Click()
+ {
+ LimparCampos();
+ SetCampos(false);
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // HELPERS
+ // ══════════════════════════════════════════════════════════════════════
+ private void SetCampos(bool enabled)
+ {
+ var campos = new[] { txtCodigo, txtCompromisso, txtHora, txtFunc, txtAvisar, txtOsVinc };
+ foreach (var c in campos)
+ {
+ c.Enabled = enabled;
+ c.BackColor = enabled ? Color.White : DisabledBack;
+ }
+ chkRealizado.Enabled = enabled;
+ txtData.BackColor = enabled ? Color.FromArgb(239, 246, 255) : DisabledBack;
+ }
+
+ private void LimparCampos()
+ {
+ txtId.Text = txtCodigo.Text = txtCompromisso.Text = string.Empty;
+ txtData.Text = txtDia.Text = txtHora.Text = string.Empty;
+ txtFunc.Text = txtAvisar.Text = txtOsVinc.Text = string.Empty;
+ txtCriadoEm.Text = txtAtualizadoEm.Text = string.Empty;
+ chkRealizado.Checked = false;
+ _selectedDate = null;
+ }
+
+ private void CarregarEvento(MLL.ModeloAgenda ev)
+ {
+ txtId.Text = ev.ID_AGENDA.ToString();
+ txtCodigo.Text = ev.CODIGO;
+ txtCompromisso.Text = ev.COMPROMISSO;
+ txtData.Text = ev.dDATA;
+ txtDia.Text = ev.DIA;
+ txtHora.Text = ev.HORA;
+ txtFunc.Text = ev.FUNC;
+ txtAvisar.Text = ev.AVISAR;
+ txtOsVinc.Text = ev.OS_VINC;
+ txtCriadoEm.Text = ev.dDATA;
+ txtAtualizadoEm.Text = DateTime.Now.ToString("dd/MM/yyyy");
+ chkRealizado.Checked = ev.REALIZADO?.ToUpper() == "S";
+
+ var dt = ParseData(ev.dDATA);
+ if (dt != DateTime.MinValue)
+ {
+ _selectedDate = dt;
+ _currentMonth = new DateTime(dt.Year, dt.Month, 1);
+ }
+
+ SetCampos(false);
+ RenderCalendario();
+ RenderListaEventos();
+ }
+
+ private HashSet GetDatesWithEvents()
+ {
+ var set = new HashSet();
+ foreach (var ev in _eventos)
+ {
+ var dt = ParseData(ev.dDATA);
+ if (dt != DateTime.MinValue) set.Add(dt.Date);
+ }
+ return set;
+ }
+
+ private static DateTime ParseData(string? value)
+ {
+ if (string.IsNullOrWhiteSpace(value)) return DateTime.MinValue;
+ if (DateTime.TryParseExact(value, "dd/MM/yyyy",
+ System.Globalization.CultureInfo.InvariantCulture,
+ System.Globalization.DateTimeStyles.None, out var dt))
+ return dt;
+ if (DateTime.TryParse(value, out dt)) return dt;
+ return DateTime.MinValue;
+ }
+
+ private void CarregarDadosFake()
+ {
+ var hoje = DateTime.Today;
+ _eventos.AddRange(new[]
+ {
+ new MLL.ModeloAgenda(1, "AG001", "Reunião com cliente",
+ hoje.ToString("dd/MM/yyyy"), "30 minutos antes",
+ "Carlos Silva", DiaSemana(hoje), "09:00", "S", "OS-1042"),
+
+ new MLL.ModeloAgenda(2, "AG002", "Visita técnica",
+ hoje.ToString("dd/MM/yyyy"), "1 hora antes",
+ "Ana Souza", DiaSemana(hoje), "14:00", "N", "OS-1055"),
+
+ new MLL.ModeloAgenda(3, "AG003", "Entrega de equipamento",
+ hoje.AddDays(4).ToString("dd/MM/yyyy"), "1 dia antes",
+ "Pedro Lima", DiaSemana(hoje.AddDays(4)), "10:30", "N", ""),
+ });
+ _nextId = 4;
+ }//Carregar dados fake (Não usa mais)
+ private void CarregarDadosDoBanco()
+ {
+ BLLAgenda _agendaBLL = new BLLAgenda(_cx);
+ _eventos.Clear();
+ _eventos.AddRange(_agendaBLL.Listar());
+ }// Método auxiliar para obter os dados da agenda no banco de dados e preencher a lista de eventos.
+
+ private static string DiaSemana(DateTime d) =>
+ new System.Globalization.CultureInfo("pt-BR").DateTimeFormat.GetDayName(d.DayOfWeek);
+
+ // ── UI HELPERS ────────────────────────────────────────────────────────
+ private Panel CreateSectionHeader(string title, int y)
+ {
+ var pnl = new Panel { Location = new Point(20, y), Width = 760, Height = 26 };
+ pnl.Controls.Add(new Label
+ {
+ Text = title,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = AccentBlue,
+ AutoSize = true,
+ Location = new Point(0, 0)
+ });
+ pnl.Controls.Add(new Panel
+ {
+ BackColor = BorderColor,
+ Height = 1,
+ Width = 740,
+ Location = new Point(0, 20)
+ });
+ return pnl;
+ }
+
+ private RoundTextBox AddInput(Control parent, string label,
+ int x, int y, int width, int height,
+ bool readOnly = false)
+ {
+ parent.Controls.Add(new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = readOnly ? Color.Gray : TextDark,
+ AutoSize = true
+ });
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, height),
+ Radius = 4,
+ BorderColor = readOnly ? Color.FromArgb(203, 213, 225) : BorderColor,
+ FocusColor = AccentBlue,
+ ReadOnly = readOnly,
+ BackColor = readOnly ? DisabledBack : Color.White
+ };
+ parent.Controls.Add(txt);
+ return txt;
+ }
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Cadastros/AgendaCadastroPanel.resx b/Dashboards/Cadastros/AgendaCadastroPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Cadastros/AgendaCadastroPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Cadastros/ClienteCadastroPanel.cs b/Dashboards/Cadastros/ClienteCadastroPanel.cs
new file mode 100644
index 0000000..baab19b
--- /dev/null
+++ b/Dashboards/Cadastros/ClienteCadastroPanel.cs
@@ -0,0 +1,530 @@
+using BLL;
+using CustomMessageBox;
+using DAL;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class ClienteCadastroPanel : UserControl
+ {
+ private string _connectionString = DadosDaConexao.ObterConexao();
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+
+ private Panel pnlToolbar = null!;
+ private Panel mainScroll = null!;
+ private Panel content = null!;
+
+ // Identificação
+ private RoundTextBox txtId = null!, txtEmpresaId = null!, txtNome = null!, txtNomeFantasia = null!;
+ private RoundTextBox txtTipoPessoa = null!, txtDocumento = null!, txtRG = null!;
+ private RoundTextBox txtInscricaoMunicipal = null!, txtDataNascimento = null!;
+ private RoundTextBox txtGrupo = null!, txtTipoConsumidor = null!;
+
+ // Contatos
+ private RoundTextBox txtContato = null!, txtTelefone1 = null!, txtTelefone2 = null!;
+ private RoundTextBox txtCelular = null!, txtWhatsapp = null!, txtEmail = null!, txtEmailNFe = null!, txtSite = null!;
+
+ // Endereço
+ private RoundTextBox txtCep = null!, txtEndereco = null!, txtNumero = null!, txtComplemento = null!;
+ private RoundTextBox txtBairro = null!, txtCidade = null!, txtUF = null!, txtPais = null!;
+
+ // Financeiro
+ private RoundTextBox txtLimiteCredito = null!, txtVendedorPadraoId = null!, txtObservacoesCobranca = null!;
+ private CheckBox chkBloqueado = null!, chkAtivo = null!;
+
+ // Carteiras
+ private RoundTextBox txtBitcoin = null!, txtEthereum = null!, txtLitecoin = null!;
+
+ // Extras
+ private RoundTextBox txtObservacoes = null!;
+ private RoundTextBox txtCampoExtra1 = null!, txtCampoExtra2 = null!, txtCampoExtra3 = null!;
+
+ // Info (readonly)
+ private RoundTextBox txtUltimaCompra = null!, txtCriadoEm = null!, txtAtualizadoEm = null!;
+
+ //Funções Auxiliares
+ private void CarregarConfiguracoesSistema()
+ {
+ BLLEmpresaConfig empresaConfig = new BLLEmpresaConfig(_connectionString);
+ int codEmpresa = empresaConfig.ObterEmpresaAtivaId();
+
+ this.txtEmpresaId.Text = codEmpresa.ToString();
+ }
+ private DateTime? ConverterData(string texto)
+ {
+ if (string.IsNullOrWhiteSpace(texto))
+ return null;
+
+ if (DateTime.TryParse(texto, out DateTime data))
+ return data;
+
+ return null;
+ }
+ private RoundTextBox[] TodosOsCampos() => new[]
+ {
+ txtId, txtEmpresaId, txtNome, txtNomeFantasia,
+ txtTipoPessoa, txtDocumento, txtRG,
+ txtInscricaoMunicipal, txtDataNascimento,
+ txtGrupo, txtTipoConsumidor,
+ txtContato, txtTelefone1, txtTelefone2,
+ txtCelular, txtWhatsapp, txtEmail, txtEmailNFe, txtSite,
+ txtCep, txtEndereco, txtNumero, txtComplemento,
+ txtBairro, txtCidade, txtUF, txtPais,
+ txtLimiteCredito, txtVendedorPadraoId, txtObservacoesCobranca,
+ txtBitcoin, txtEthereum, txtLitecoin,
+ txtObservacoes, txtCampoExtra1, txtCampoExtra2, txtCampoExtra3
+ };
+
+ private void SetCampos(bool enabled)
+ {
+ foreach (var campo in TodosOsCampos())
+ {
+ campo.Enabled = enabled;
+ campo.BackColor = enabled ? Color.White : Color.FromArgb(241, 245, 249);
+ }
+ chkBloqueado.Enabled = enabled;
+ chkAtivo.Enabled = enabled;
+ }
+
+ public ClienteCadastroPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+ InitializeLayout();
+ SetCampos(false);
+ }
+
+ private void InitializeLayout()
+ {
+ this.Controls.Clear();
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = Color.FromArgb(248, 250, 252),
+ BorderStyle = BorderStyle.None
+ };
+
+ var flowButtons = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0),
+ BackColor = Color.Transparent
+ };
+
+ var btnNovo = CreateToolbarButton("Novo", Color.FromArgb(34, 197, 94));
+ var btnAlterar = CreateToolbarButton("Alterar", Color.FromArgb(245, 158, 11));
+ var btnExcluir = CreateToolbarButton("Excluir", Color.FromArgb(239, 68, 68));
+ var btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
+ var btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
+ var btnCancelar = CreateToolbarButton("Cancelar", Color.FromArgb(148, 163, 184));
+
+ btnNovo.Click += (s, e) => BtnNovo_Click();
+ btnAlterar.Click += (s, e) => BtnAlterar_Click();
+ btnExcluir.Click += (s, e) => BtnExcluir_Click();
+ btnLocalizar.Click += (s, e) => BtnLocalizar_Click();
+ btnSalvar.Click += (s, e) => BtnSalvar_Click();
+ btnCancelar.Click += (s, e) => BtnCancelar_Click();
+
+ flowButtons.Controls.AddRange(new Control[]
+ { btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar, btnCancelar });
+ pnlToolbar.Controls.Add(flowButtons);
+ this.Controls.Add(pnlToolbar);
+
+ // ── SCROLL + CONTENT ──────────────────────────────────────────────
+ mainScroll = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ BackColor = Color.White
+ };
+ this.Controls.Add(mainScroll);
+ mainScroll.BringToFront();
+
+ content = new Panel
+ {
+ Width = 1100,
+ Height = 900,
+ Location = new Point(0, 0),
+ BackColor = Color.White
+ };
+ mainScroll.Controls.Add(content);
+
+ const int rowH = 52;
+ const int secGap = 10;
+ const int secH = 28;
+ const int inputH = 28;
+
+ int y = 10;
+
+ // ── 1. IDENTIFICAÇÃO DO CLIENTE ───────────────────────────────────
+ content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DO CLIENTE", y));
+ y += secH + 4;
+
+ txtId = AddInput(content, "ID", 20, y, 60, inputH);
+ txtEmpresaId = AddInput(content, "Empresa ID", 90, y, 80, inputH);
+ txtNome = AddInput(content, "Nome / Razão Social", 180, y, 390, inputH);
+ txtNomeFantasia = AddInput(content, "Nome Fantasia", 580, y, 360, inputH);
+ y += rowH;
+
+ txtDocumento = AddInput(content, "CPF/CNPJ", 20, y, 170, inputH);
+ DocumentoHelper.Registrar(txtDocumento);
+ txtRG = AddInput(content, "RG / Insc. Estadual", 200, y, 170, inputH);
+ txtInscricaoMunicipal = AddInput(content, "Insc. Municipal", 380, y, 130, inputH);
+ txtDataNascimento = AddInput(content, "Dt. Nascimento", 520, y, 130, inputH);
+ txtTipoPessoa = AddInput(content, "Tipo Pessoa", 660, y, 110, inputH);
+ txtGrupo = AddInput(content, "Grupo", 780, y, 110, inputH);
+ txtTipoConsumidor = AddInput(content, "Tipo Consumidor", 900, y, 140, inputH);
+ y += rowH;
+
+ // ── 2. CONTATOS E COMUNICAÇÃO ─────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("CONTATOS E COMUNICAÇÃO", y));
+ y += secH + 4;
+
+ txtContato = AddInput(content, "Contato", 20, y, 180, inputH);
+ txtEmail = AddInput(content, "E-mail", 210, y, 230, inputH);
+ txtEmailNFe = AddInput(content, "E-mail XML/NFe", 450, y, 230, inputH);
+ txtSite = AddInput(content, "Site", 690, y, 250, inputH);
+ y += rowH;
+
+ txtTelefone1 = AddInput(content, "Telefone 1", 20, y, 155, inputH);
+ txtTelefone2 = AddInput(content, "Telefone 2", 185, y, 155, inputH);
+ txtCelular = AddInput(content, "Celular", 350, y, 155, inputH);
+ txtWhatsapp = AddInput(content, "WhatsApp", 515, y, 155, inputH);
+ y += rowH;
+
+ // ── 3. ENDEREÇO COMPLETO ──────────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("ENDEREÇO COMPLETO", y));
+ y += secH + 4;
+
+ txtCep = AddInput(content, "CEP", 20, y, 95, inputH);
+ txtCep.Leave += (s, e) =>
+ {
+ string cep = txtCep.Text.Trim().Replace("-", "");
+
+ if (cep.Length != 8) return;
+
+ if (TLL.VerifyCep.verificaCEP(cep))
+ {
+ txtEndereco.Text = TLL.VerifyCep.endereco;
+ txtBairro.Text = TLL.VerifyCep.bairro;
+ txtCidade.Text = TLL.VerifyCep.cidade;
+ txtUF.Text = TLL.VerifyCep.estado;
+
+ // Formata o CEP com hífen
+ txtCep.Text = $"{cep[..5]}-{cep[5..]}";
+ }
+ else
+ {
+ NT_MessageBox.Show("CEP não encontrado.", "CEP",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ };//cep
+ txtEndereco = AddInput(content, "Logradouro", 125, y, 370, inputH);
+ txtNumero = AddInput(content, "Nº", 505, y, 60, inputH);
+ txtComplemento = AddInput(content, "Complemento", 575, y, 175, inputH);
+ txtBairro = AddInput(content, "Bairro", 760, y, 180, inputH);
+ y += rowH;
+
+ txtCidade = AddInput(content, "Cidade", 20, y, 280, inputH);
+ txtUF = AddInput(content, "UF", 310, y, 55, inputH);
+ txtPais = AddInput(content, "País", 375, y, 150, inputH);
+ y += rowH;
+
+ // ── 4. FINANCEIRO ─────────────────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("FINANCEIRO", y));
+ y += secH + 4;
+
+ txtLimiteCredito = AddInput(content, "Limite de Crédito", 20, y, 150, inputH);
+ txtVendedorPadraoId = AddInput(content, "Vendedor Padrão ID", 180, y, 120, inputH);
+ txtObservacoesCobranca = AddInput(content, "Obs. de Cobrança", 310, y, 380, inputH);
+
+ chkBloqueado = CreateCheckBox("Bloqueado", 705, y + 18);
+ chkAtivo = CreateCheckBox("Ativo", 805, y + 18);
+ content.Controls.Add(chkBloqueado);
+ content.Controls.Add(chkAtivo);
+ y += rowH;
+
+ // ── 5. CARTEIRAS DIGITAIS ─────────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("CARTEIRAS DIGITAIS", y));
+ y += secH + 4;
+
+ txtBitcoin = AddInput(content, "Bitcoin (BTC)", 20, y, 290, inputH);
+ txtEthereum = AddInput(content, "Ethereum (ETH)", 320, y, 290, inputH);
+ txtLitecoin = AddInput(content, "Litecoin (LTC)", 620, y, 290, inputH);
+ y += rowH;
+
+ // ── 6. CAMPOS EXTRAS ──────────────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("CAMPOS EXTRAS", y));
+ y += secH + 4;
+
+ txtCampoExtra1 = AddInput(content, "Campo Extra 1", 20, y, 290, inputH);
+ txtCampoExtra2 = AddInput(content, "Campo Extra 2", 320, y, 290, inputH);
+ txtCampoExtra3 = AddInput(content, "Campo Extra 3", 620, y, 290, inputH);
+ y += rowH;
+
+ // ── 7. OBSERVAÇÕES ────────────────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("OBSERVAÇÕES", y));
+ y += secH + 4;
+
+ txtObservacoes = AddInput(content, "Observações Internas", 20, y, 920, inputH);
+ y += rowH;
+
+ // ── 8. INFORMAÇÕES DO REGISTRO ────────────────────────────────────
+ y += secGap;
+ content.Controls.Add(CreateSectionHeader("INFORMAÇÕES DO REGISTRO", y));
+ y += secH + 4;
+
+ txtUltimaCompra = AddInput(content, "Última Compra", 20, y, 175, inputH, readOnly: true);
+ txtCriadoEm = AddInput(content, "Criado Em", 205, y, 175, inputH, readOnly: true);
+ txtAtualizadoEm = AddInput(content, "Atualizado Em", 390, y, 175, inputH, readOnly: true);
+ y += rowH;
+
+ content.Height = y + 10;
+ }
+
+ // ── EVENTOS ───────────────────────────────────────────────────────────
+
+ private void BtnNovo_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ chkBloqueado.Checked = false;
+ chkAtivo.Checked = true;
+
+ SetCampos(true);
+ txtId.Enabled = false;
+ txtId.BackColor = Color.FromArgb(241, 245, 249);
+ this.CarregarConfiguracoesSistema();
+ txtEmpresaId.Enabled = false;
+ txtEmpresaId.BackColor = Color.FromArgb(241, 245, 249);
+ }
+
+ private void BtnAlterar_Click()
+ {
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ MessageBox.Show(
+ "Nenhum registro selecionado para alterar.",
+ "Atenção",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
+ return;
+ }
+
+ SetCampos(true);
+ txtId.Enabled = false;
+ txtId.BackColor = Color.FromArgb(241, 245, 249);
+ txtEmpresaId.Enabled = false;
+ txtEmpresaId.BackColor = Color.FromArgb(241, 245, 249);
+ }
+
+ private void BtnExcluir_Click()
+ {
+ // Solicita confirmação e exclui o registro atual
+ }
+
+ private void BtnLocalizar_Click()
+ {
+ // Abre tela/diálogo de busca e preenche os campos
+ }
+
+ private void BtnSalvar_Click()
+ {
+ var bll = new BLLClientes(_connectionString);
+
+ // 🔒 validação básica front
+ if (string.IsNullOrWhiteSpace(txtNome.Text) ||
+ string.IsNullOrWhiteSpace(txtDocumento.Text))
+ {
+ NT_MessageBox.Show("Preencha ao menos Nome e Documento.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ bool isNew = string.IsNullOrWhiteSpace(txtId.Text);
+
+ var cliente = new ModeloCliente
+ {
+ Id = isNew ? 0 : int.Parse(txtId.Text),
+ EmpresaId = int.TryParse(txtEmpresaId.Text, out var emp) ? emp : 0,
+
+ Nome = txtNome.Text,
+ NomeFantasia = txtNomeFantasia.Text,
+ TipoPessoa = txtTipoPessoa.Text,
+ Documento = txtDocumento.Text,
+
+ RG = txtRG.Text,
+ InscricaoMunicipal = txtInscricaoMunicipal.Text,
+ DataNascimento = ConverterData(txtDataNascimento.Text),
+
+ Contato = txtContato.Text,
+ Telefone1 = txtTelefone1.Text,
+ Telefone2 = txtTelefone2.Text,
+ Celular = txtCelular.Text,
+ Whatsapp = txtWhatsapp.Text,
+ Email = txtEmail.Text,
+ EmailNFe = txtEmailNFe.Text,
+ Site = txtSite.Text,
+
+ Grupo = txtGrupo.Text,
+
+ Cep = txtCep.Text,
+ Endereco = txtEndereco.Text,
+ Numero = int.TryParse(txtNumero.Text, out var num) ? num : null,
+ Complemento = txtComplemento.Text,
+ Bairro = txtBairro.Text,
+ Cidade = txtCidade.Text,
+ UF = txtUF.Text,
+ Pais = txtPais.Text,
+
+ LimiteCredito = decimal.TryParse(txtLimiteCredito.Text, out var lim) ? lim : 0,
+ Bloqueado = chkBloqueado.Checked,
+ ObservacoesCobranca = txtObservacoesCobranca.Text,
+
+ VendedorPadraoId = int.TryParse(txtVendedorPadraoId.Text, out var vend) ? vend : null,
+ TipoConsumidor = txtTipoConsumidor.Text,
+
+ Observacoes = txtObservacoes.Text,
+
+ CampoExtra1 = txtCampoExtra1.Text,
+ CampoExtra2 = txtCampoExtra2.Text,
+ CampoExtra3 = txtCampoExtra3.Text,
+
+ Bitcoin = txtBitcoin.Text,
+ Ethereum = txtEthereum.Text,
+ Litecoin = txtLitecoin.Text,
+
+ Ativo = chkAtivo.Checked,
+ UltimaCompra = ConverterData(txtUltimaCompra.Text),
+
+ // 🔥 esses o banco já gera, mas mantém compatível
+ CriadoEm = DateTime.Now,
+ AtualizadoEm = DateTime.Now
+ };
+
+ bool sucesso = false;
+
+ try
+ {
+ sucesso = isNew
+ ? bll.Inserir(cliente)
+ : bll.Alterar(cliente);
+ }
+ catch (Exception ex)
+ {
+ NT_MessageBox.Show(ex.Message,
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ if (!sucesso)
+ {
+ NT_MessageBox.Show("Erro ao salvar cliente.",
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ SetCampos(false);
+
+ NT_MessageBox.Show("Cliente salvo com sucesso!",
+ "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ private void BtnCancelar_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ chkBloqueado.Checked = false;
+ chkAtivo.Checked = false;
+
+ SetCampos(false);
+ }
+
+ // ── HELPERS ───────────────────────────────────────────────────────────
+
+ private Panel CreateSectionHeader(string title, int y)
+ {
+ var pnl = new Panel { Location = new Point(20, y), Width = 1000, Height = 26 };
+ var lbl = new Label
+ {
+ Text = title,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = AccentBlue,
+ AutoSize = true,
+ Location = new Point(0, 0)
+ };
+ var line = new Panel
+ {
+ BackColor = BorderColor,
+ Height = 1,
+ Width = 980,
+ Location = new Point(0, 20)
+ };
+ pnl.Controls.Add(lbl);
+ pnl.Controls.Add(line);
+ return pnl;
+ }
+
+ private RoundTextBox AddInput(Control parent, string label,
+ int x, int y, int width, int height,
+ bool readOnly = false)
+ {
+ var lbl = new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = readOnly ? Color.Gray : TextDark,
+ AutoSize = true
+ };
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, height),
+ Radius = 4,
+ BorderColor = readOnly ? Color.FromArgb(203, 213, 225) : BorderColor,
+ FocusColor = AccentBlue,
+ ReadOnly = readOnly,
+ BackColor = readOnly ? Color.FromArgb(241, 245, 249) : Color.White
+ };
+ parent.Controls.Add(lbl);
+ parent.Controls.Add(txt);
+ return txt;
+ }
+
+ private CheckBox CreateCheckBox(string text, int x, int y) => new CheckBox
+ {
+ Text = text,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Cadastros/ClienteCadastroPanel.resx b/Dashboards/Cadastros/ClienteCadastroPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Cadastros/ClienteCadastroPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Cadastros/EmpresaCadastroPanel.cs b/Dashboards/Cadastros/EmpresaCadastroPanel.cs
new file mode 100644
index 0000000..06d01ad
--- /dev/null
+++ b/Dashboards/Cadastros/EmpresaCadastroPanel.cs
@@ -0,0 +1,526 @@
+using BLL;
+using CustomMessageBox;
+using DAL;
+using MLL;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class EmpresaCadastroPanel : UserControl
+ {
+ private string _cx = DadosDaConexao.ObterConexao();
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+
+ private Panel pnlToolbar = null!;
+ private Panel mainScroll = null!;
+ private Panel content = null!;
+
+ // Identificação
+ private RoundTextBox txtId = null!, txtNome = null!, txtCNPJ = null!;
+ private RoundTextBox txtTipoEmpresa = null!, txtRegimeTributario = null!, txtCNAE = null!;
+
+ // Endereço
+ private RoundTextBox txtCep = null!, txtEndereco = null!, txtNumero = null!, txtComplemento = null!;
+ private RoundTextBox txtBairro = null!, txtCidade = null!, txtUF = null!, txtPais = null!;
+
+ // Contatos
+ private RoundTextBox txtTelefone1 = null!, txtTelefone2 = null!, txtCelular = null!, txtWhatsapp = null!;
+ private RoundTextBox txtEmail = null!, txtSite = null!;
+
+ // Fiscal
+ private RoundTextBox txtInscricaoEstadual = null!, txtInscricaoMunicipal = null!;
+
+ // Contador
+ private RoundTextBox txtCNPJCPF_Contador = null!, txtNome_Contador = null!;
+
+ // Outros
+ private RoundTextBox txtTextoParaRecibo = null!;
+ private CheckBox chkAtivo = null!;
+
+ // Info (readonly)
+ private RoundTextBox txtCriadoEm = null!, txtAtualizadoEm = null!;
+
+ //Carregar dados da empresa atual, se houver no banco de dados
+ private void CarregarEmpresaAtiva()
+ {
+ var bll = new BLLEmpresa(_cx);
+ var empresa = bll.CarregarEmpresaAtiva();
+
+ if (empresa == null)
+ {
+ MessageBox.Show("Nenhuma empresa ativa encontrada.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ // Preenche todos os campos do formulário
+ txtId.Text = empresa.Id.ToString();
+ txtNome.Text = empresa.Nome;
+ txtCNPJ.Text = empresa.CNPJ;
+ txtTipoEmpresa.Text = empresa.TipoEmpresa;
+ txtRegimeTributario.Text = empresa.RegimeTributario;
+ txtCNAE.Text = empresa.CNAE;
+ txtInscricaoEstadual.Text = empresa.InscricaoEstadual;
+ txtInscricaoMunicipal.Text = empresa.InscricaoMunicipal;
+ txtCep.Text = empresa.Cep;
+ txtEndereco.Text = empresa.Endereco;
+ txtNumero.Text = empresa.Numero.ToString();
+ txtComplemento.Text = empresa.Complemento;
+ txtBairro.Text = empresa.Bairro;
+ txtCidade.Text = empresa.Cidade;
+ txtUF.Text = empresa.UF;
+ txtPais.Text = empresa.Pais;
+ txtTelefone1.Text = empresa.Telefone1;
+ txtTelefone2.Text = empresa.Telefone2;
+ txtCelular.Text = empresa.Celular;
+ txtWhatsapp.Text = empresa.Whatsapp;
+ txtEmail.Text = empresa.Email;
+ txtSite.Text = empresa.Site;
+ txtCNPJCPF_Contador.Text = empresa.CNPJCPF_Contador;
+ txtNome_Contador.Text = empresa.Nome_Contador;
+ txtTextoParaRecibo.Text = empresa.TextoParaRecibo;
+ chkAtivo.Checked = empresa.Ativo;
+ txtCriadoEm.Text = empresa.CriadoEm.ToString("dd/MM/yyyy");
+ txtAtualizadoEm.Text = empresa.AtualizadoEm.ToString("dd/MM/yyyy");
+ }
+ public EmpresaCadastroPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+ InitializeLayout();
+ // Carregar dados da empresa atual, se houver
+ CarregarEmpresaAtiva();
+ }
+
+ private void InitializeLayout()
+ {
+ this.Controls.Clear();
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = Color.FromArgb(248, 250, 252),
+ BorderStyle = BorderStyle.None
+ };
+
+ var flowButtons = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0),
+ BackColor = Color.Transparent
+ };
+
+ var btnNovo = CreateToolbarButton("Novo", Color.FromArgb(34, 197, 94));
+ var btnAlterar = CreateToolbarButton("Alterar", Color.FromArgb(245, 158, 11));
+ var btnExcluir = CreateToolbarButton("Excluir", Color.FromArgb(239, 68, 68));
+ var btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
+ var btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
+ var btnCancelar = CreateToolbarButton("Cancelar", Color.FromArgb(148, 163, 184));
+ var btnCriarCert = CreateToolbarButton("Certificado", Color.FromArgb(148, 160, 184));
+ var btnGerarFichaPDF = CreateToolbarButton("Ficha Tecnica", Color.FromArgb(255, 0, 0));
+
+ btnNovo.Click += (s, e) => BtnNovo_Click();
+ btnAlterar.Click += (s, e) => BtnAlterar_Click();
+ btnExcluir.Click += (s, e) => BtnExcluir_Click();
+ btnLocalizar.Click += (s, e) => BtnLocalizar_Click();
+ btnSalvar.Click += (s, e) => BtnSalvar_Click();
+ btnCancelar.Click += (s, e) => BtnCancelar_Click();
+ btnCriarCert.Click += (s, e) => BtnGerarCertificado_Click();
+ btnGerarFichaPDF.Click += (s, e) => BtnFicha_Click();
+
+ flowButtons.Controls.AddRange(new Control[]
+ { btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar, btnCancelar,btnCriarCert,btnGerarFichaPDF });
+ pnlToolbar.Controls.Add(flowButtons);
+ this.Controls.Add(pnlToolbar);
+
+ // ── SCROLL + CONTENT ──────────────────────────────────────────────
+ mainScroll = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ BackColor = Color.White
+ };
+ this.Controls.Add(mainScroll);
+ mainScroll.BringToFront();
+
+ content = new Panel
+ {
+ Width = 1100,
+ Height = 900,
+ Location = new Point(0, 0),
+ BackColor = Color.White
+ };
+ mainScroll.Controls.Add(content);
+
+ const int rowH = 52;
+ const int secGap = 10;
+ const int secH = 28;
+ const int inputH = 28;
+
+ int y = 10;
+
+ // ── 1. IDENTIFICAÇÃO DA EMPRESA ───────────────────────────────────
+ content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DA EMPRESA", y));
+ y += secH + 4;
+
+ txtId = AddInput(content, "ID", 20, y, 60, inputH, readOnly: true);
+ txtNome = AddInput(content, "Nome / Razão Social", 90, y, 450, inputH);
+ txtCNPJ = AddInput(content, "CNPJ", 550, y, 180, inputH);
+ DocumentoHelper.Registrar(txtCNPJ);
+ txtTipoEmpresa = AddInput(content, "Tipo Empresa", 740, y, 200, inputH);
+ y += rowH;
+
+ txtRegimeTributario = AddInput(content, "Regime Tributário", 20, y, 200, inputH);
+ txtCNAE = AddInput(content, "CNAE", 230, y, 150, inputH);
+ txtInscricaoEstadual = AddInput(content, "Inscrição Estadual", 390, y, 200, inputH);
+ txtInscricaoMunicipal = AddInput(content, "Inscrição Municipal", 600, y, 200, inputH);
+
+ // ── 2. ENDEREÇO COMPLETO ──────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("ENDEREÇO COMPLETO", y));
+ y += secH + 4;
+
+ txtCep = AddInput(content, "CEP", 20, y, 95, inputH);
+ // No InitializeLayout(), após criar o txtCep
+ txtCep.Leave += (s, e) =>
+ {
+ string cep = txtCep.Text.Trim().Replace("-", "");
+
+ if (cep.Length != 8) return;
+
+ if (TLL.VerifyCep.verificaCEP(cep))
+ {
+ txtEndereco.Text = TLL.VerifyCep.endereco;
+ txtBairro.Text = TLL.VerifyCep.bairro;
+ txtCidade.Text = TLL.VerifyCep.cidade;
+ txtUF.Text = TLL.VerifyCep.estado;
+ txtPais.Text = "Brasil";
+
+ // Formata o CEP com hífen
+ txtCep.Text = $"{cep[..5]}-{cep[5..]}";
+ }
+ else
+ {
+ NT_MessageBox.Show("CEP não encontrado.", "CEP",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ };
+ txtEndereco = AddInput(content, "Logradouro", 125, y, 370, inputH);
+ txtNumero = AddInput(content, "Nº", 505, y, 60, inputH);
+ txtComplemento = AddInput(content, "Complemento", 575, y, 175, inputH);
+ txtBairro = AddInput(content, "Bairro", 760, y, 180, inputH);
+ y += rowH;
+
+ txtCidade = AddInput(content, "Cidade", 20, y, 280, inputH);
+ txtUF = AddInput(content, "UF", 310, y, 55, inputH);
+ txtPais = AddInput(content, "País", 375, y, 150, inputH);
+
+ // ── 3. CONTATOS E COMUNICAÇÃO ─────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("CONTATOS E COMUNICAÇÃO", y));
+ y += secH + 4;
+
+ txtTelefone1 = AddInput(content, "Telefone 1", 20, y, 155, inputH);
+ txtTelefone2 = AddInput(content, "Telefone 2", 185, y, 155, inputH);
+ txtCelular = AddInput(content, "Celular", 350, y, 155, inputH);
+ txtWhatsapp = AddInput(content, "WhatsApp", 515, y, 155, inputH);
+ y += rowH;
+
+ txtEmail = AddInput(content, "E-mail", 20, y, 350, inputH);
+ txtSite = AddInput(content, "Site", 380, y, 290, inputH);
+
+ // ── 4. DADOS DO CONTADOR ──────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("DADOS DO CONTADOR", y));
+ y += secH + 4;
+
+ txtCNPJCPF_Contador = AddInput(content, "CPF/CNPJ do Contador", 20, y, 200, inputH);
+ DocumentoHelper.Registrar(txtCNPJCPF_Contador);
+ txtNome_Contador = AddInput(content, "Nome do Contador", 230, y, 400, inputH);
+
+ // ── 5. OUTROS ─────────────────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("OUTROS", y));
+ y += secH + 4;
+
+ txtTextoParaRecibo = AddInput(content, "Texto para Recibo", 20, y, 920, inputH);
+ y += rowH;
+ txtTextoParaRecibo.Multiline = true;
+
+
+ chkAtivo = CreateCheckBox("Ativo", 20, y);
+ content.Controls.Add(chkAtivo);
+
+ // ── 6. INFORMAÇÕES DO REGISTRO ────────────────────────────────────
+ y += 35 + secGap;
+ content.Controls.Add(CreateSectionHeader("INFORMAÇÕES DO REGISTRO", y));
+ y += secH + 4;
+
+ txtCriadoEm = AddInput(content, "Criado Em", 20, y, 175, inputH, readOnly: true);
+ txtAtualizadoEm = AddInput(content, "Atualizado Em", 205, y, 175, inputH, readOnly: true);
+ y += rowH;
+
+ content.Height = y + 10;
+
+ SetCampos(false);
+ }
+
+ // ── CAMPOS ────────────────────────────────────────────────────────────
+
+ private RoundTextBox[] TodosOsCampos() => new[]
+ {
+ txtNome, txtCNPJ, txtTipoEmpresa, txtRegimeTributario, txtCNAE,
+ txtInscricaoEstadual, txtInscricaoMunicipal,
+ txtCep, txtEndereco, txtNumero, txtComplemento,
+ txtBairro, txtCidade, txtUF, txtPais,
+ txtTelefone1, txtTelefone2, txtCelular, txtWhatsapp,
+ txtEmail, txtSite,
+ txtCNPJCPF_Contador, txtNome_Contador,
+ txtTextoParaRecibo
+ };
+
+ private void SetCampos(bool enabled)
+ {
+ foreach (var campo in TodosOsCampos())
+ {
+ campo.Enabled = enabled;
+ campo.BackColor = enabled ? Color.White : Color.FromArgb(241, 245, 249);
+ }
+ chkAtivo.Enabled = enabled;
+ }
+
+ // ── EVENTOS ───────────────────────────────────────────────────────────
+
+ private void BtnNovo_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ chkAtivo.Checked = true;
+ SetCampos(true);
+ }
+
+ private void BtnAlterar_Click()
+ {
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ MessageBox.Show(
+ "Nenhum registro selecionado para alterar.",
+ "Atenção",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
+ return;
+ }
+
+ SetCampos(true);
+ txtId.Enabled = false;
+ txtId.BackColor = Color.FromArgb(241, 245, 249);
+ }
+ // Evento
+ private void BtnFicha_Click()
+ {
+ var bll = new BLLEmpresa(_cx);
+ var empresa = bll.CarregarEmpresaAtiva();
+
+ if (empresa == null)
+ {
+ MessageBox.Show("Nenhuma empresa ativa encontrada.");
+ return;
+ }
+
+ var resultado = TLL.FichaEmpresaPDF.GerarEAbrir(empresa);
+
+ if (!resultado.Sucesso)
+ MessageBox.Show(resultado.Mensagem, "Erro",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ private void BtnExcluir_Click()
+ {
+ // Solicita confirmação e exclui o registro atual
+ }
+
+ private void BtnLocalizar_Click()
+ {
+ // Abre tela/diálogo de busca e preenche os campos
+ }
+ private void BtnGerarCertificado_Click()
+ {
+ ModeloEmpresa empresa = new ModeloEmpresa();
+ // Após salvar a empresa com sucesso...
+ var resultado = TLL.CertificadoHelper.Gerar(empresa, "Nike12122020*##");
+
+ if (resultado.Sucesso)
+ NT_MessageBox.Show(
+ $"Certificado gerado!\nVálido até: {resultado.ValidoAte:dd/MM/yyyy}\n" +
+ $"Thumbprint: {resultado.Thumbprint}",
+ "Certificado", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ else
+ NT_MessageBox.Show(resultado.Mensagem, "Erro",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ private void BtnVerificarCert_Click()
+ {
+ ModeloEmpresa empresa = new ModeloEmpresa();
+ var status = TLL.CertificadoHelper.VerificarStatus(empresa.CNPJ, "Nike12122020*##");
+
+ if (!status.Valido)
+ NT_MessageBox.Show("Certificado expirado! Gere um novo.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+
+ private void BtnSalvar_Click()
+ {
+ BLLEmpresa _empresaBLL = new BLLEmpresa(_cx);
+
+ if (string.IsNullOrWhiteSpace(txtNome.Text) ||
+ string.IsNullOrWhiteSpace(txtCNPJ.Text) ||
+ string.IsNullOrWhiteSpace(txtEmail.Text))
+ {
+ NT_MessageBox.Show("Preencha ao menos Nome, CNPJ e Email.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ bool isNew = string.IsNullOrWhiteSpace(txtId.Text);
+
+ var empresa = new MLL.ModeloEmpresa(
+ id: isNew ? 0 : int.Parse(txtId.Text),
+ nome: txtNome.Text,
+ cNPJ: txtCNPJ.Text,
+ tipoEmpresa: txtTipoEmpresa.Text,
+ regimeTributario: txtRegimeTributario.Text,
+ cNAE: txtCNAE.Text,
+ cep: txtCep.Text,
+ endereco: txtEndereco.Text,
+ numero: string.IsNullOrWhiteSpace(txtNumero.Text) ? 0 : int.Parse(txtNumero.Text),
+ complemento: txtComplemento.Text,
+ bairro: txtBairro.Text,
+ cidade: txtCidade.Text,
+ uF: txtUF.Text,
+ pais: txtPais.Text,
+ telefone1: txtTelefone1.Text,
+ telefone2: txtTelefone2.Text,
+ celular: txtCelular.Text,
+ whatsapp: txtWhatsapp.Text,
+ email: txtEmail.Text,
+ site: txtSite.Text,
+ inscricaoEstadual: txtInscricaoEstadual.Text,
+ inscricaoMunicipal: txtInscricaoMunicipal.Text,
+ cNPJCPF_Contador: txtCNPJCPF_Contador.Text,
+ nome_Contador: txtNome_Contador.Text,
+ textoParaRecibo: txtTextoParaRecibo.Text,
+ ativo: chkAtivo.Checked,
+ criadoEm: DateTime.Now,
+ atualizadoEm: DateTime.Now
+ );
+
+ bool sucesso;
+
+ if (isNew)
+ sucesso = _empresaBLL.Inserir(empresa);
+ else
+ sucesso = _empresaBLL.Alterar(empresa);
+
+ if (!sucesso)
+ {
+ NT_MessageBox.Show("Erro ao salvar empresa.",
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ // 🔹 Atualiza UI
+ txtAtualizadoEm.Text = DateTime.Now.ToString("dd/MM/yyyy");
+ SetCampos(false);
+
+ NT_MessageBox.Show("Empresa salva com sucesso!", "Sucesso",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ private void BtnCancelar_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ chkAtivo.Checked = false;
+ SetCampos(false);
+ }
+
+ // ── HELPERS ───────────────────────────────────────────────────────────
+
+ private Panel CreateSectionHeader(string title, int y)
+ {
+ var pnl = new Panel { Location = new Point(20, y), Width = 1000, Height = 26 };
+ var lbl = new Label
+ {
+ Text = title,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = AccentBlue,
+ AutoSize = true,
+ Location = new Point(0, 0)
+ };
+ var line = new Panel
+ {
+ BackColor = BorderColor,
+ Height = 1,
+ Width = 980,
+ Location = new Point(0, 20)
+ };
+ pnl.Controls.Add(lbl);
+ pnl.Controls.Add(line);
+ return pnl;
+ }
+
+ private RoundTextBox AddInput(Control parent, string label,
+ int x, int y, int width, int height,
+ bool readOnly = false)
+ {
+ var lbl = new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = readOnly ? Color.Gray : TextDark,
+ AutoSize = true
+ };
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, height),
+ Radius = 4,
+ BorderColor = readOnly ? Color.FromArgb(203, 213, 225) : BorderColor,
+ FocusColor = AccentBlue,
+ ReadOnly = readOnly,
+ BackColor = readOnly ? Color.FromArgb(241, 245, 249) : Color.White
+ };
+ parent.Controls.Add(lbl);
+ parent.Controls.Add(txt);
+ return txt;
+ }
+
+ private CheckBox CreateCheckBox(string text, int x, int y) => new CheckBox
+ {
+ Text = text,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Cadastros/EmpresaCadastroPanel.resx b/Dashboards/Cadastros/EmpresaCadastroPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Cadastros/EmpresaCadastroPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Cadastros/EmpresaConfiguracoesPanel.cs b/Dashboards/Cadastros/EmpresaConfiguracoesPanel.cs
new file mode 100644
index 0000000..16f4a1e
--- /dev/null
+++ b/Dashboards/Cadastros/EmpresaConfiguracoesPanel.cs
@@ -0,0 +1,591 @@
+using BLL;
+using CCH;
+using CustomMessageBox;
+using DAL;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class EmpresaConfiguracoesPanel : UserControl
+ {
+ //Variaveis de ambiente
+ private int _idEmpresa;
+ private int _idConfig;
+ private string _connectionString = DadosDaConexao.ObterConexao();
+
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+
+ private Panel pnlToolbar = null!;
+ private Panel mainScroll = null!;
+ private Panel content = null!;
+
+ private RoundTextBox txtId = null!, txtEmpresaId = null!, txtNomeSistema = null!;
+ private RoundTextBox txtCorPrimaria = null!, txtCorSecundaria = null!;
+ private RoundTextBox txtLogo = null!, txtFavicon = null!;
+ private RoundTextBox txtDiasGarantiaPadrao = null!;
+
+ private CheckBox chkExibirCPF = null!, chkExibirTelefone = null!, chkExibirGarantia = null!;
+ private CheckBox chkGerarRecibo = null!, chkExibirValoresOS = null!;
+ private CheckBox chkEmailAuto = null!, chkWhatsAuto = null!, chkModoEscuro = null!;
+ private CheckBox chkPermitirEdicaoOS = null!;
+
+ private RoundTextBox txtCriadoEm = null!, txtAtualizadoEm = null!;
+
+ public EmpresaConfiguracoesPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+ InitializeLayout();
+ this.CarregarConfig();
+
+ }
+
+ private void InitializeLayout()
+ {
+ this.Controls.Clear();
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = Color.FromArgb(248, 250, 252)
+ };
+ var flowButtons = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0)
+ };
+
+ var btnNovo = CreateToolbarButton("Novo", Color.FromArgb(34, 197, 94));
+ var btnAlterar = CreateToolbarButton("Alterar", Color.FromArgb(245, 158, 11));
+ var btnExcluir = CreateToolbarButton("Excluir", Color.FromArgb(239, 68, 68));
+ var btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
+ var btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
+ var btnCancelar = CreateToolbarButton("Cancelar", Color.FromArgb(148, 163, 184));
+ var btnConfigEmpresa = CreateToolbarButton("Gerar Config", Color.FromArgb(148, 163, 184));
+
+ btnNovo.Click += (s, e) => BtnNovo_Click();
+ btnAlterar.Click += (s, e) => BtnAlterar_Click();
+ btnExcluir.Click += (s, e) => BtnExcluir_Click();
+ btnLocalizar.Click += (s, e) => BtnLocalizar_Click();
+ btnSalvar.Click += (s, e) => BtnSalvar_Click();
+ btnCancelar.Click += (s, e) => BtnCancelar_Click();
+ btnConfigEmpresa.Click += (s, e) => BtnGerarConfig_Click();
+
+ flowButtons.Controls.AddRange(new Control[]
+ { btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar,btnConfigEmpresa,btnCancelar });
+ pnlToolbar.Controls.Add(flowButtons);
+ this.Controls.Add(pnlToolbar);
+
+ // ── SCROLL + CONTENT ──────────────────────────────────────────────
+ mainScroll = new Panel { Dock = DockStyle.Fill, AutoScroll = true };
+ this.Controls.Add(mainScroll);
+ mainScroll.BringToFront();
+
+ content = new Panel { Width = 1100, Height = 850, Location = new Point(0, 0) };
+ mainScroll.Controls.Add(content);
+
+ int y = 10;
+ const int rowH = 52;
+
+ // ── 1. IDENTIDADE DO SISTEMA ──────────────────────────────────────
+ content.Controls.Add(CreateSectionHeader("IDENTIDADE DO SISTEMA", y));
+ y += 32;
+
+ txtId = AddInput(content, "ID", 20, y, 60, 28, readOnly: true);
+ txtEmpresaId = AddInput(content, "Empresa ID", 90, y, 80, 28);
+ txtNomeSistema = AddInput(content, "Nome Personalizado do Sistema", 180, y, 350, 28);
+ y += rowH;
+
+ txtCorPrimaria = AddInput(content, "Cor Primária (Hex)", 20, y, 150, 28);
+ txtCorSecundaria = AddInput(content, "Cor Secundária (Hex)", 180, y, 150, 28);
+
+ // Logo com botão lupa
+ txtLogo = AddInput(content, "Caminho da Logo", 340, y, 260, 28);
+ var btnLogo = CreateIconButton("🔍", 608, y + 16);
+ btnLogo.Click += (s, e) => SelecionarImagem(txtLogo, "Logo");
+ content.Controls.Add(btnLogo);
+
+ // Favicon com botão lupa
+ txtFavicon = AddInput(content, "Caminho do Favicon", 650, y, 240, 28);
+ var btnFavicon = CreateIconButton("🔍", 898, y + 16);
+ btnFavicon.Click += (s, e) => SelecionarImagem(txtFavicon, "Favicon");
+ content.Controls.Add(btnFavicon);
+
+ // ── 2. REGRAS DE NEGÓCIO E EXIBIÇÃO ──────────────────────────────
+ y += rowH + 15;
+ content.Controls.Add(CreateSectionHeader("REGRAS DE NEGÓCIO E EXIBIÇÃO", y));
+ y += 32;
+
+ chkExibirCPF = CreateCheckBox("Exibir CPF Cliente na OS", 20, y + 15);
+ chkExibirTelefone = CreateCheckBox("Exibir Telefone na OS", 230, y + 15);
+ chkExibirValoresOS = CreateCheckBox("Exibir Valores na OS", 460, y + 15);
+ content.Controls.AddRange(new Control[] { chkExibirCPF, chkExibirTelefone, chkExibirValoresOS });
+ y += rowH;
+
+ chkExibirGarantia = CreateCheckBox("Utilizar Garantia", 20, y + 15);
+ txtDiasGarantiaPadrao = AddInput(content, "Dias Garantia (Padrão)", 180, y, 120, 28);
+ content.Controls.Add(chkExibirGarantia);
+
+ // ── 3. AUTOMAÇÕES E COMUNICAÇÃO ───────────────────────────────────
+ y += rowH + 15;
+ content.Controls.Add(CreateSectionHeader("AUTOMAÇÕES E COMUNICAÇÃO", y));
+ y += 32;
+
+ chkGerarRecibo = CreateCheckBox("Gerar Recibo Automático", 20, y + 15);
+ chkEmailAuto = CreateCheckBox("Enviar E-mail Automático", 230, y + 15);
+ chkWhatsAuto = CreateCheckBox("Enviar WhatsApp Automático", 460, y + 15);
+ content.Controls.AddRange(new Control[] { chkGerarRecibo, chkEmailAuto, chkWhatsAuto });
+
+ // ── 4. PREFERÊNCIAS TÉCNICAS ──────────────────────────────────────
+ y += rowH + 15;
+ content.Controls.Add(CreateSectionHeader("PREFERÊNCIAS TÉCNICAS", y));
+ y += 32;
+
+ chkModoEscuro = CreateCheckBox("Habilitar Modo Escuro", 20, y + 15);
+ chkPermitirEdicaoOS = CreateCheckBox("Permitir Edição de OS Finalizada", 230, y + 15);
+ content.Controls.AddRange(new Control[] { chkModoEscuro, chkPermitirEdicaoOS });
+
+ // ── 5. INFORMAÇÕES DO REGISTRO ────────────────────────────────────
+ y += rowH + 15;
+ content.Controls.Add(CreateSectionHeader("INFORMAÇÕES DO REGISTRO", y));
+ y += 32;
+
+ txtCriadoEm = AddInput(content, "Criado Em", 20, y, 180, 28, readOnly: true);
+ txtAtualizadoEm = AddInput(content, "Atualizado Em", 210, y, 180, 28, readOnly: true);
+ y += rowH;
+
+ content.Height = y + 20;
+
+ SetCamposStatus(false);
+ }
+
+ // ── SELEÇÃO DE IMAGEM ─────────────────────────────────────────────────
+
+ //private void SelecionarImagem(RoundTextBox destino, string tipo)
+ //{
+ // using var dlg = new OpenFileDialog
+ // {
+ // Title = $"Selecionar {tipo}",
+ // Filter = "Imagens|*.png;*.jpg;*.jpeg;*.bmp;*.ico;*.gif|Todos os arquivos|*.*"
+ // };
+
+ // if (dlg.ShowDialog() == DialogResult.OK)
+ // {
+ // var arquivo = dlg.FileName;
+ // destino.Text = arquivo;
+
+ // // Valida tamanho máximo (2MB)
+ // var info = new FileInfo(arquivo);
+ // if (info.Length > 2 * 1024 * 1024)
+ // {
+ // MessageBox.Show(
+ // $"O arquivo selecionado para {tipo} é maior que 2MB.\nRecomendamos imagens menores para melhor desempenho.",
+ // "Atenção",
+ // MessageBoxButtons.OK,
+ // MessageBoxIcon.Warning);
+ // }
+ // }
+ //}
+ private void SelecionarImagem(RoundTextBox destino, string tipo)
+ {
+ using var dlg = new OpenFileDialog
+ {
+ Title = $"Selecionar {tipo}",
+ Filter = "Imagens|*.png;*.jpg;*.jpeg;*.bmp;*.ico;*.gif|Todos os arquivos|*.*"
+ };
+
+ if (dlg.ShowDialog() == DialogResult.OK)
+ {
+ try
+ {
+ var arquivoOriginal = dlg.FileName;
+
+ // 📁 Pasta destino fixa
+ string pastaDestino = @"C:\Levelcode\LevelOS\Uploads\Images";
+
+ // Cria a pasta se não existir
+ if (!Directory.Exists(pastaDestino))
+ Directory.CreateDirectory(pastaDestino);
+
+ // 🔥 Gera nome único (evita sobrescrever)
+ string extensao = Path.GetExtension(arquivoOriginal);
+ string nomeNovo = $"{tipo}_{Guid.NewGuid()}{extensao}";
+ string caminhoFinal = Path.Combine(pastaDestino, nomeNovo);
+
+ // Copia o arquivo
+ File.Copy(arquivoOriginal, caminhoFinal, false);
+
+ // Atualiza o campo
+ destino.Text = caminhoFinal;
+
+ // ⚠️ Valida tamanho (2MB)
+ var info = new FileInfo(caminhoFinal);
+ if (info.Length > 2 * 1024 * 1024)
+ {
+ MessageBox.Show(
+ $"O arquivo selecionado para {tipo} é maior que 2MB.\nRecomendamos imagens menores para melhor desempenho.",
+ "Atenção",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(
+ "Erro ao copiar imagem: " + ex.Message,
+ "Erro",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
+ }
+ }
+ }//Copiar com mais precisão
+
+ // ── EVENTOS DOS BOTÕES ────────────────────────────────────────────────
+
+ private void BtnNovo_Click()
+ {
+ LimparFormulario();
+ SetCamposStatus(true);
+ txtNomeSistema.Focus();
+ BLLEmpresaConfig empresaConfig = new BLLEmpresaConfig(_connectionString);
+ this.txtEmpresaId.Text = empresaConfig.ObterEmpresaAtivaId().ToString();
+ }
+
+ private void BtnAlterar_Click()
+ {
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ MessageBox.Show(
+ "Nenhum registro selecionado para alterar.",
+ "Atenção",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
+ return;
+ }
+ SetCamposStatus(true);
+ }
+
+ private void BtnExcluir_Click()
+ {
+ // Solicita confirmação e exclui o registro
+ }
+
+ private void BtnLocalizar_Click()
+ {
+ // Abre tela de busca e preenche os campos
+ }
+ private void BtnGerarConfig_Click()
+ {
+ var config = new ModeloEmpresaConfig
+ {
+ IdEmpresaConfig = Convert.ToInt32(txtId.Text), // você precisa ter isso carregado
+ IdEmpresa = Convert.ToInt32(txtEmpresaId.Text), // idem (empresa logada)
+
+ NomeSistema = txtNomeSistema.Text,
+ CorPrimaria = txtCorPrimaria.Text,
+ CorSecundaria = txtCorSecundaria.Text,
+ Logo = txtLogo.Text,
+ Favicon = txtFavicon.Text,
+
+ ExibirCPFCliente = chkExibirCPF.Checked,
+ ExibirTelefoneCliente = chkExibirTelefone.Checked,
+ ExibirGarantia = chkExibirGarantia.Checked,
+ DiasGarantiaPadrao = Convert.ToInt32(txtDiasGarantiaPadrao.Text),
+
+ GerarReciboAutomatico = chkGerarRecibo.Checked,
+ ExibirValoresOS = chkExibirValoresOS.Checked,
+
+ EnviarEmailAutomatico = chkEmailAuto.Checked,
+ EnviarWhatsappAutomatico = chkWhatsAuto.Checked,
+
+ ModoEscuro = chkModoEscuro.Checked,
+ PermitirEdicaoOSFinalizada = chkPermitirEdicaoOS.Checked
+ };
+ bool result = JsonHelper.SalvarBoolean(config, AppFileSystem.AppFileConfigEmpresa);
+ if (result)
+ {
+ NT_MessageBox.Show("Arquivo de configuração gerado com sucesso.","Arquivo gerado",MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ else
+ {
+ NT_MessageBox.Show("Não foi possivel gerar o arquivo de configuração", "falha ao gerar arquivo", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void BtnSalvar_Click()
+ {
+ try
+ {
+ // Valida campos obrigatórios
+ if (string.IsNullOrWhiteSpace(txtNomeSistema.Text))
+ {
+ MessageBox.Show(
+ "O campo 'Nome do Sistema' é obrigatório.",
+ "Atenção",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Warning);
+ txtNomeSistema.Focus();
+ return;
+ }
+
+ // Valida cores HEX
+ if (!string.IsNullOrWhiteSpace(txtCorPrimaria.Text) && !ValidarHex(txtCorPrimaria.Text))
+ {
+ MessageBox.Show("Cor Primária inválida. Ex: #3B82F6", "Atenção");
+ txtCorPrimaria.Focus();
+ return;
+ }
+
+ if (!string.IsNullOrWhiteSpace(txtCorSecundaria.Text) && !ValidarHex(txtCorSecundaria.Text))
+ {
+ MessageBox.Show("Cor Secundária inválida. Ex: #64748B", "Atenção");
+ txtCorSecundaria.Focus();
+ return;
+ }
+
+ // Valida arquivos
+ if (!string.IsNullOrWhiteSpace(txtLogo.Text) && !File.Exists(txtLogo.Text))
+ {
+ MessageBox.Show("Logo não encontrada.", "Atenção");
+ return;
+ }
+
+ if (!string.IsNullOrWhiteSpace(txtFavicon.Text) && !File.Exists(txtFavicon.Text))
+ {
+ MessageBox.Show("Favicon não encontrado.", "Atenção");
+ return;
+ }
+
+ // 🔥 Monta o modelo
+ var config = new ModeloEmpresaConfig
+ {
+ IdEmpresaConfig = Convert.ToInt32(txtId.Text), // você precisa ter isso carregado
+ IdEmpresa = Convert.ToInt32(txtEmpresaId.Text), // idem (empresa logada)
+
+ NomeSistema = txtNomeSistema.Text,
+ CorPrimaria = txtCorPrimaria.Text,
+ CorSecundaria = txtCorSecundaria.Text,
+ Logo = txtLogo.Text,
+ Favicon = txtFavicon.Text,
+
+ ExibirCPFCliente = chkExibirCPF.Checked,
+ ExibirTelefoneCliente = chkExibirTelefone.Checked,
+ ExibirGarantia = chkExibirGarantia.Checked,
+ DiasGarantiaPadrao =Convert.ToInt32(txtDiasGarantiaPadrao.Text),
+
+ GerarReciboAutomatico = chkGerarRecibo.Checked,
+ ExibirValoresOS = chkExibirValoresOS.Checked,
+
+ EnviarEmailAutomatico = chkEmailAuto.Checked,
+ EnviarWhatsappAutomatico = chkWhatsAuto.Checked,
+
+ ModoEscuro = chkModoEscuro.Checked,
+ PermitirEdicaoOSFinalizada = chkPermitirEdicaoOS.Checked
+ };
+
+ var bll = new BLLEmpresaConfig(_connectionString);
+
+ // 🔥 Decide se cria ou atualiza
+ _idConfig = Convert.ToInt32(txtId.Text);
+ if (_idConfig == 0 || _idConfig == null)
+ {
+ bll.Inserir(config);
+ JsonHelper.Salvar(config, AppFileSystem.AppFileConfigEmpresa);
+ }
+ else
+ {
+
+ bll.Alterar(config);
+ JsonHelper.Salvar(config, AppFileSystem.AppFileConfigEmpresa);
+ }
+
+ MessageBox.Show(
+ "Configurações salvas com sucesso!",
+ "LevelOS",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+
+ SetCamposStatus(false);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(
+ ex.Message,
+ "Erro",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
+ }
+ }
+
+ private void BtnCancelar_Click()
+ {
+ LimparFormulario();
+ SetCamposStatus(false);
+ }
+
+ // ── HELPERS DE ESTADO ─────────────────────────────────────────────────
+
+ private void SetCamposStatus(bool active)
+ {
+ var campos = new Control[]
+ {
+ txtEmpresaId, txtNomeSistema, txtCorPrimaria, txtCorSecundaria,
+ txtLogo, txtFavicon, txtDiasGarantiaPadrao,
+ chkExibirCPF, chkExibirTelefone, chkExibirGarantia, chkGerarRecibo,
+ chkExibirValoresOS, chkEmailAuto, chkWhatsAuto, chkModoEscuro, chkPermitirEdicaoOS
+ };
+
+ foreach (var c in campos)
+ {
+ c.Enabled = active;
+ if (c is RoundTextBox r)
+ r.BackColor = active ? Color.White : Color.FromArgb(241, 245, 249);
+ }
+
+ // ID e datas nunca editáveis
+ txtId.Enabled = false;
+ txtId.BackColor = Color.FromArgb(241, 245, 249);
+ txtCriadoEm.Enabled = false;
+ txtAtualizadoEm.Enabled = false;
+ }
+
+ private void LimparFormulario()
+ {
+ var todos = new RoundTextBox[]
+ {
+ txtId, txtEmpresaId, txtNomeSistema, txtCorPrimaria, txtCorSecundaria,
+ txtLogo, txtFavicon, txtDiasGarantiaPadrao, txtCriadoEm, txtAtualizadoEm
+ };
+ foreach (var t in todos) t.Text = string.Empty;
+
+ foreach (Control c in content.Controls)
+ if (c is CheckBox chk) chk.Checked = false;
+ }
+
+ private static bool ValidarHex(string valor)
+ {
+ var v = valor.TrimStart('#');
+ return v.Length == 6 && System.Text.RegularExpressions.Regex.IsMatch(v, @"^[0-9A-Fa-f]{6}$");
+ }
+
+ // ── HELPERS DE UI ─────────────────────────────────────────────────────
+
+ private Panel CreateSectionHeader(string title, int y)
+ {
+ var pnl = new Panel { Location = new Point(20, y), Width = 1000, Height = 26 };
+ pnl.Controls.Add(new Label
+ {
+ Text = title,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = AccentBlue,
+ AutoSize = true
+ });
+ pnl.Controls.Add(new Panel
+ {
+ BackColor = BorderColor,
+ Height = 1,
+ Width = 980,
+ Location = new Point(0, 20)
+ });
+ return pnl;
+ }
+
+ private RoundTextBox AddInput(Control parent, string label,
+ int x, int y, int w, int h,
+ bool readOnly = false)
+ {
+ parent.Controls.Add(new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = readOnly ? Color.Gray : TextDark,
+ AutoSize = true
+ });
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(w, h),
+ Radius = 4,
+ BorderColor = readOnly ? Color.FromArgb(203, 213, 225) : BorderColor,
+ FocusColor = AccentBlue,
+ ReadOnly = readOnly,
+ BackColor = readOnly ? Color.FromArgb(241, 245, 249) : Color.White
+ };
+ parent.Controls.Add(txt);
+ return txt;
+ }
+
+ private Button CreateIconButton(string icon, int x, int y) => new Button
+ {
+ Text = icon,
+ Location = new Point(x, y),
+ Size = new Size(28, 28),
+ FlatStyle = FlatStyle.Flat,
+ Font = new Font("Segoe UI", 10f),
+ Cursor = Cursors.Hand,
+ FlatAppearance = { BorderColor = BorderColor, BorderSize = 1 },
+ BackColor = Color.FromArgb(241, 245, 249)
+ };
+
+ private CheckBox CreateCheckBox(string text, int x, int y) => new CheckBox
+ {
+ Text = text,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 8.25f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+
+ private void CarregarConfig()
+ {
+ BLLEmpresaConfig empresaConfig = new BLLEmpresaConfig(_connectionString);
+ int codEmpresa = empresaConfig.ObterEmpresaAtivaId();
+ if (codEmpresa > 0)
+ {
+ ModeloEmpresaConfig modelo = empresaConfig.ObterPorEmpresa(codEmpresa);
+ this.txtId.Text = modelo.IdEmpresaConfig.ToString();
+ this.txtEmpresaId.Text = modelo.IdEmpresa.ToString();
+ this.txtNomeSistema.Text = modelo.NomeSistema;
+ this.txtCorPrimaria.Text = modelo.CorPrimaria;
+ this.txtCorSecundaria.Text = modelo.CorSecundaria;
+ this.txtLogo.Text = modelo.Logo;
+ this.txtFavicon.Text = modelo.Favicon;
+ this.txtDiasGarantiaPadrao.Text = modelo.DiasGarantiaPadrao.ToString();
+ this.chkExibirCPF.Checked = modelo.ExibirCPFCliente;
+ this.chkExibirTelefone.Checked = modelo.ExibirTelefoneCliente;
+ this.chkExibirGarantia.Checked = modelo.ExibirGarantia;
+ this.chkGerarRecibo.Checked = modelo.GerarReciboAutomatico;
+ this.chkExibirValoresOS.Checked = modelo.ExibirValoresOS;
+ this.chkEmailAuto.Checked = modelo.EnviarEmailAutomatico;
+ this.chkWhatsAuto.Checked = modelo.EnviarWhatsappAutomatico;
+ this.chkModoEscuro.Checked = modelo.ModoEscuro;
+ this.chkPermitirEdicaoOS.Checked = modelo.PermitirEdicaoOSFinalizada;
+ this.txtCriadoEm.Text = modelo.CriadoEm.ToString();
+ this.txtAtualizadoEm.Text = modelo.AtualizadoEm.ToString();
+ }
+ else
+ {
+ NT_MessageBox.Show("Erro ao recuperar configuração da empresa, por favor contacte o administrador do sistema","Erro",MessageBoxButtons.OK,MessageBoxIcon.Error);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Cadastros/EmpresaConfiguracoesPanel.resx b/Dashboards/Cadastros/EmpresaConfiguracoesPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Cadastros/EmpresaConfiguracoesPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Cadastros/FuncionariosCadastroPanel.cs b/Dashboards/Cadastros/FuncionariosCadastroPanel.cs
new file mode 100644
index 0000000..8a5958a
--- /dev/null
+++ b/Dashboards/Cadastros/FuncionariosCadastroPanel.cs
@@ -0,0 +1,947 @@
+using BLL;
+using CustomMessageBox;
+using DAL;
+using MLL;
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Windows.Forms;
+using AForge.Video;
+using AForge.Video.DirectShow;
+
+namespace UI
+{
+ public class FuncionariosCadastroPanel : UserControl
+ {
+ private int idBanco = 0;
+ private string _cx = DadosDaConexao.ObterConexao();
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+
+ private Panel pnlToolbar = null!;
+ private Panel mainScroll = null!;
+ private Panel content = null!;
+
+ // Identificação
+ private RoundTextBox txtId = null!, txtCodigo = null!, txtNome = null!;
+ private RoundTextBox txtCPF = null!, txtCI = null!, txtCTPS = null!;
+ private RoundTextBox txtCNH = null!, txtCatCNH = null!;
+ private RoundTextBox txtECivil = null!, txtRegime = null!;
+
+ // Filiação
+ private RoundTextBox txtPai = null!, txtMae = null!;
+
+ // Endereço
+ private RoundTextBox txtcep=null!, txtEndereco = null!,txtEndNumero=null!, txtBairro = null!, txtCidade = null!, txtUF = null!;
+
+ // Dados Bancários
+ private RoundTextBox txtBanco = null!, txtAgencia = null!, txtConta = null!;
+ private RoundComboBox cbBanco = null!;
+
+ // Contato
+ private RoundTextBox txtTelefone = null!;
+
+ // Acesso
+ private RoundTextBox txtPass = null!;
+ private CheckBox chkMasterUser = null!, chkVendedor = null!, chkTecnico = null!;
+ private CheckBox chkDemitido = null!, chkContador = null!, chkOutros = null!;
+
+ // Outros
+ private RoundTextBox txtFPath = null!, txtObserv = null!;
+
+ // Datas
+ private RoundTextBox txtDataAdm = null!, txtDataDem = null!, txtAniver = null!;
+
+ // ── CARREGAR FUNCIONÁRIO ──────────────────────────────────────────────
+
+ private void CarregarFuncionario(int id)
+ {
+ //var bll = new BLLFuncionarios(_cx);
+ //var f = bll.CarregarPorId(id);
+
+ //if (f == null)
+ //{
+ // NT_MessageBox.Show("Funcionário não encontrado.",
+ // "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ // return;
+ //}
+
+ //PreencherCampos(f);
+ }
+
+ private void PreencherCampos(ModeloFuncionarios f)
+ {
+ txtId.Text = f.ID_FUNCIONARIO.ToString();
+ txtCodigo.Text = f.CODIGO;
+ txtNome.Text = f.NOME;
+ txtPai.Text = f.PAI;
+ txtMae.Text = f.MAE;
+ txtCPF.Text = f.CPF;
+ txtCI.Text = f.CI;
+ txtCTPS.Text = f.CTPS;
+ txtCNH.Text = f.CNH;
+ txtCatCNH.Text = f.CAT_CNH;
+ txtECivil.Text = f.ECIVIL;
+ txtRegime.Text = f.REGIME;
+ txtEndereco.Text = f.ENDERECO;
+ txtBairro.Text = f.BAIRRO;
+ txtCidade.Text = f.CIDADE;
+ txtUF.Text = f.UF;
+ txtBanco.Text = f.BANCO;
+ txtAgencia.Text = f.AGENCIA;
+ txtConta.Text = f.CONTA;
+ txtTelefone.Text = f.TELEFONE;
+ txtPass.Text = f.PASS;
+ txtFPath.Text = f.FPATH;
+ txtObserv.Text = f.OBSERV;
+ //txtDataAdm.Text = f.DATA_ADM;
+ //txtDataDem.Text = f.DATA_DEM;
+ //txtAniver.Text = f.ANIVER;
+
+ //chkMasterUser.Checked = f.MASTERUSER == "S";
+ //chkVendedor.Checked = f.VENDEDOR == "S";
+ //chkTecnico.Checked = f.TECNICO == "S";
+ //chkDemitido.Checked = f.DEMITIDO == "S";
+ //chkContador.Checked = f.FTECNICO == "S";
+ //chkOutros.Checked = f.FVENDEDOR == "S";
+ }
+ private Control[] TodosOsControles() => new Control[]{
+ txtCodigo, txtNome,
+ txtCPF, txtCI, txtCTPS, txtCNH, txtCatCNH,
+ txtECivil, txtRegime,
+ txtPai, txtMae,
+ txtcep, txtEndereco, txtEndNumero, txtBairro, txtCidade, txtUF,
+
+ // 👇 ComboBox entra aqui
+ cbBanco,
+
+ txtAgencia, txtConta,
+ txtTelefone,
+ txtPass,
+ txtFPath, txtObserv,
+ txtDataAdm, txtDataDem, txtAniver
+};//todos os controles
+ private void SetCamposAtulizado(bool enabled)
+ {
+ foreach (var ctrl in TodosOsControles())
+ {
+ ctrl.Enabled = enabled;
+
+ // 🎨 Se for TextBox
+ if (ctrl is RoundTextBox txt)
+ {
+ txt.BackColor = enabled
+ ? Color.White
+ : Color.FromArgb(241, 245, 249);
+ }
+
+ // 🎨 Se for ComboBox
+ else if (ctrl is RoundComboBox cmb)
+ {
+ cmb.BackColor = enabled
+ ? Color.White
+ : Color.FromArgb(241, 245, 249);
+ }
+ }
+
+ foreach (var chk in TodosOsChecks())
+ chk.Enabled = enabled;
+ }//SetCampos
+ private void CarregarComboBoxAtualizada()
+ {
+ BLLBancos bLLBancos = new BLLBancos(_cx);
+
+ var tabela = bLLBancos.LocalizarTodos();
+
+ // 🔥 cria coluna personalizada
+ tabela.Columns.Add("EXIBICAO", typeof(string), "NUMERO + ' - ' + NOME");
+
+ cbBanco.DataSource = tabela;
+ cbBanco.DisplayMember = "EXIBICAO"; // 👈 aqui
+ cbBanco.ValueMember = "ID_BANCOS";
+
+ if (cbBanco.SelectedValue != null)
+ this.idBanco = Convert.ToInt32(cbBanco.SelectedValue);
+ }//CarregarComboBoxAtualizada
+ private DateTime? ConverterData(string texto)
+ {
+ if (string.IsNullOrWhiteSpace(texto))
+ return null;
+
+ if (DateTime.TryParse(texto, out DateTime data))
+ return data;
+
+ return null;
+ }//ConverterData
+
+ public FuncionariosCadastroPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+ InitializeLayout();
+ this.CarregarComboBoxAtualizada();
+ }
+
+ private void InitializeLayout()
+ {
+ this.Controls.Clear();
+
+ // ── TOOLBAR ───────────────────────────────────────────────────────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = Color.FromArgb(248, 250, 252),
+ BorderStyle = BorderStyle.None
+ };
+
+ var flowButtons = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0),
+ BackColor = Color.Transparent
+ };
+
+ var btnNovo = CreateToolbarButton("Novo", Color.FromArgb(34, 197, 94));
+ var btnAlterar = CreateToolbarButton("Alterar", Color.FromArgb(245, 158, 11));
+ var btnExcluir = CreateToolbarButton("Excluir", Color.FromArgb(239, 68, 68));
+ var btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
+ var btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
+ var btnGerarFicha = CreateToolbarButton("Gerar Ficha", Color.FromArgb(255,0,0));
+ var btnCancelar = CreateToolbarButton("Cancelar", Color.FromArgb(148, 163, 184));
+
+ btnNovo.Click += (s, e) => BtnNovo_Click();
+ btnAlterar.Click += (s, e) => BtnAlterar_Click();
+ btnExcluir.Click += (s, e) => BtnExcluir_Click();
+ btnLocalizar.Click += (s, e) => BtnLocalizar_Click();
+ btnSalvar.Click += (s, e) => BtnSalvar_Click();
+ btnCancelar.Click += (s, e) => BtnCancelar_Click();
+ btnGerarFicha.Click += (s, e) => BtnFichaFuncionario_Click();
+ flowButtons.Controls.AddRange(new Control[]
+ { btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar,btnGerarFicha,btnCancelar });
+ pnlToolbar.Controls.Add(flowButtons);
+ this.Controls.Add(pnlToolbar);
+
+ // ── SCROLL + CONTENT ──────────────────────────────────────────────
+ mainScroll = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ BackColor = Color.White
+ };
+ this.Controls.Add(mainScroll);
+ mainScroll.BringToFront();
+
+ content = new Panel
+ {
+ Width = 1100,
+ Height = 900,
+ Location = new Point(0, 0),
+ BackColor = Color.White
+ };
+ mainScroll.Controls.Add(content);
+
+ const int rowH = 52;
+ const int secGap = 10;
+ const int secH = 28;
+ const int inputH = 28;
+
+ int y = 10;
+
+ // ── 1. IDENTIFICAÇÃO DO FUNCIONÁRIO ───────────────────────────────
+ content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DO FUNCIONÁRIO", y));
+ y += secH + 4;
+
+ txtId = AddInput(content, "ID", 20, y, 60, inputH, readOnly: true);
+ txtCodigo = AddInput(content, "Código", 90, y, 100, inputH, readOnly: true);
+ txtNome = AddInput(content, "Nome", 200, y, 450, inputH);
+ y += rowH;
+
+ txtCPF = AddInput(content, "CPF", 20, y, 160, inputH);
+ DocumentoHelper.Registrar(txtCPF);
+ txtCI = AddInput(content, "RG / CI", 190, y, 160, inputH);
+ txtCTPS = AddInput(content, "CTPS", 360, y, 140, inputH);
+ txtCNH = AddInput(content, "CNH", 510, y, 160, inputH);
+ txtCatCNH = AddInput(content, "Cat. CNH", 680, y, 90, inputH);
+ y += rowH;
+
+ txtECivil = AddInput(content, "Estado Civil", 20, y, 160, inputH);
+ txtRegime = AddInput(content, "Regime", 190, y, 160, inputH);
+
+ // ── 2. FILIAÇÃO ───────────────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("FILIAÇÃO", y));
+ y += secH + 4;
+
+ txtPai = AddInput(content, "Nome do Pai", 20, y, 400, inputH);
+ txtMae = AddInput(content, "Nome da Mãe", 430, y, 400, inputH);
+
+
+ // ── 3. ENDEREÇO ─────────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("ENDEREÇO", y));
+ y += secH + 4;
+
+ // LINHA ÚNICA
+ txtcep = AddInput(content, "CEP", 20, y, 80, inputH);
+ txtcep.Leave += (s, e) =>
+ {
+ string cep = txtcep.Text.Trim().Replace("-", "");
+
+ if (cep.Length != 8) return;
+
+ if (TLL.VerifyCep.verificaCEP(cep))
+ {
+ txtEndereco.Text = TLL.VerifyCep.endereco;
+ txtBairro.Text = TLL.VerifyCep.bairro;
+ txtCidade.Text = TLL.VerifyCep.cidade;
+ txtUF.Text = TLL.VerifyCep.estado;
+
+ // Formata o CEP com hífen
+ txtcep.Text = $"{cep[..5]}-{cep[5..]}";
+ }
+ else
+ {
+ NT_MessageBox.Show("CEP não encontrado.", "CEP",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ };//cep
+ txtEndereco = AddInput(content, "Logradouro", 110, y, 260, inputH);
+ txtEndNumero = AddInput(content, "Nº", 380, y, 70, inputH);
+ txtBairro = AddInput(content, "Bairro", 460, y, 150, inputH);
+ txtCidade = AddInput(content, "Cidade", 620, y, 150, inputH);
+ txtUF = AddInput(content, "UF", 780, y, 60, inputH);
+
+ // ── 4. DADOS BANCÁRIOS ────────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("DADOS BANCÁRIOS", y));
+ y += secH + 4;
+
+ //txtBanco = AddInput(content, "Banco", 20, y, 280, inputH);
+ cbBanco = AddComboBox(content, "Banco", 20, y, 200, inputH);
+ txtAgencia = AddInput(content, "Agência", 310, y, 160, inputH);
+ txtConta = AddInput(content, "Conta", 480, y, 160, inputH);
+
+ // ── 5. CONTATO ────────────────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("CONTATO", y));
+ y += secH + 4;
+
+ txtTelefone = AddInput(content, "Telefone", 20, y, 200, inputH);
+
+ // ── 6. ACESSO AO SISTEMA ──────────────────────────────────────────
+ y += rowH + secGap;
+ content.Controls.Add(CreateSectionHeader("ACESSO AO SISTEMA", y));
+ y += secH + 4;
+
+ txtPass = AddInput(content, "Senha", 20, y, 200, inputH);
+ txtPass.PasswordChar = '*';
+ y += rowH;
+
+ chkMasterUser = CreateCheckBox("Master User", 20, y);
+ chkVendedor = CreateCheckBox("Vendedor", 160, y);
+ chkTecnico = CreateCheckBox("Técnico", 290, y);
+ chkOutros = CreateCheckBox("Outros", 420, y);
+ chkContador = CreateCheckBox("Contador", 560, y);
+ chkDemitido = CreateCheckBox("Demitido", 690, y);
+
+ content.Controls.AddRange(new Control[]
+ { chkMasterUser, chkVendedor, chkTecnico, chkOutros, chkContador, chkDemitido });
+
+ // ── 7. OUTROS ─────────────────────────────────────────────────────
+ y += 35 + secGap;
+ content.Controls.Add(CreateSectionHeader("OUTROS", y));
+ y += secH + 4;
+
+ // Label da foto
+ var lblFoto = new Label
+ {
+ Text = "Caminho da Foto (FPATH)",
+ Location = new Point(20, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+ content.Controls.Add(lblFoto);
+
+ // Campo de texto da foto (menor para dar espaço ao botão lupa)
+ txtFPath = new RoundTextBox
+ {
+ Location = new Point(20, y + 16),
+ Size = new Size(560, inputH),
+ Radius = 4,
+ BorderColor = BorderColor,
+ FocusColor = AccentBlue,
+ BackColor = Color.White
+ };
+ content.Controls.Add(txtFPath);
+
+ // Botão lupa 🔍
+ var btnFoto = new Button
+ {
+ Text = "🔍",
+ Location = new Point(588, y + 16),
+ Size = new Size(36, inputH),
+ FlatStyle = FlatStyle.Flat,
+ BackColor = Color.FromArgb(37, 99, 235),
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI", 11f),
+ Cursor = Cursors.Hand,
+ TabStop = false
+ };
+ btnFoto.FlatAppearance.BorderSize = 0;
+ btnFoto.Click += (s, e) => BtnFoto_Click();
+ content.Controls.Add(btnFoto);
+
+ y += rowH;
+
+ txtObserv = AddInput(content, "Observações", 20, y, 920, inputH);
+ txtObserv.Multiline = true;
+ txtObserv.Height = 60;
+
+ // ── 8. DATAS ──────────────────────────────────────────────────────
+ y += 70 + secGap;
+ content.Controls.Add(CreateSectionHeader("DATAS", y));
+ y += secH + 4;
+
+ txtDataAdm = AddInput(content, "Data de Admissão", 20, y, 175, inputH);
+ txtDataDem = AddInput(content, "Data de Demissão", 205, y, 175, inputH);
+ txtAniver = AddInput(content, "Aniversário", 390, y, 175, inputH);
+ y += rowH;
+
+ content.Height = y + 10;
+
+ //SetCampos(false);
+ SetCamposAtulizado(false);
+ }
+
+ // ── FOTO: LUPA ────────────────────────────────────────────────────────
+
+ private void BtnFoto_Click()
+ {
+ using var dlg = new FotoOrigemDialog();
+ if (dlg.ShowDialog() != DialogResult.OK) return;
+
+ string? caminhoSalvo = dlg.UsarWebcam
+ ? CapturarDaWebcam()
+ : SelecionarDoComputador();
+
+ if (!string.IsNullOrEmpty(caminhoSalvo))
+ txtFPath.Text = caminhoSalvo;
+ }
+
+ private string? SelecionarDoComputador()
+ {
+ using var ofd = new OpenFileDialog
+ {
+ Title = "Selecionar Foto do Funcionário",
+ Filter = "Imagens|*.jpg;*.jpeg;*.png;*.bmp|Todos os arquivos|*.*"
+ };
+
+ if (ofd.ShowDialog() != DialogResult.OK) return null;
+
+ return SalvarFoto(ofd.FileName);
+ }
+
+ private string? CapturarDaWebcam()
+ {
+ using var webcamForm = new WebcamCaptureForm();
+ if (webcamForm.ShowDialog() != DialogResult.OK) return null;
+ if (webcamForm.ImagemCapturada == null) return null;
+
+ string temp = Path.Combine(Path.GetTempPath(), $"webcam_{Guid.NewGuid()}.jpg");
+ webcamForm.ImagemCapturada.Save(temp, ImageFormat.Jpeg);
+ return SalvarFoto(temp);
+ }
+
+ private string SalvarFoto(string origem)
+ {
+ const string pasta = @"C:\Levelcode\LevelOS\Uploads\Images\Funcionarios";
+ Directory.CreateDirectory(pasta);
+
+ // Nome: NomeFuncionario_yyyyMMdd_HHmmss.jpg
+ string nomeBase = string.IsNullOrWhiteSpace(txtNome.Text)
+ ? "Funcionario"
+ : txtNome.Text.Trim().Replace(" ", "_");
+
+ string nomeArq = $"{nomeBase}_{DateTime.Now:yyyyMMdd_HHmmss}.jpg";
+ string destino = Path.Combine(pasta, nomeArq);
+
+ File.Copy(origem, destino, overwrite: true);
+ return destino;
+ }
+
+ // ── CAMPOS ────────────────────────────────────────────────────────────
+
+ private RoundTextBox[] TodosOsCampos() => new[]
+ {
+ txtCodigo, txtNome,
+ txtCPF, txtCI, txtCTPS, txtCNH, txtCatCNH,
+ txtECivil, txtRegime,
+ txtPai, txtMae,
+ txtcep,txtEndereco,txtEndNumero, txtBairro, txtCidade, txtUF,
+ /*txtBanco,*/ txtAgencia, txtConta,
+ txtTelefone,
+ txtPass,
+ txtFPath, txtObserv,
+ txtDataAdm, txtDataDem, txtAniver
+ };
+
+ private CheckBox[] TodosOsChecks() => new[]
+ {
+ chkMasterUser, chkVendedor, chkTecnico,
+ chkDemitido, chkContador, chkOutros
+ };
+
+ private void SetCampos(bool enabled)
+ {
+ foreach (var campo in TodosOsCampos())
+ {
+ campo.Enabled = enabled;
+ campo.BackColor = enabled ? Color.White : Color.FromArgb(241, 245, 249);
+ }
+ foreach (var chk in TodosOsChecks())
+ chk.Enabled = enabled;
+ }
+
+ // ── EVENTOS ───────────────────────────────────────────────────────────
+
+ private void BtnNovo_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ foreach (var chk in TodosOsChecks())
+ chk.Checked = false;
+
+ //SetCampos(true);
+ SetCamposAtulizado(true);
+ }
+
+ private void BtnAlterar_Click()
+ {
+ if (string.IsNullOrWhiteSpace(txtId.Text))
+ {
+ NT_MessageBox.Show(
+ "Nenhum registro selecionado para alterar.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ //SetCampos(true);
+ SetCamposAtulizado(true);
+ txtId.Enabled = false;
+ txtId.BackColor = Color.FromArgb(241, 245, 249);
+ }
+
+ private void BtnExcluir_Click()
+ {
+
+ }
+
+ private void BtnLocalizar_Click()
+ {
+ // Abre tela/diálogo de busca e preenche os campos
+ }
+
+ private void BtnSalvar_Click()
+ {
+ var bll = new BLLFuncionarios(_cx);
+
+ if (string.IsNullOrWhiteSpace(txtNome.Text) ||
+ string.IsNullOrWhiteSpace(txtCPF.Text))
+ {
+ NT_MessageBox.Show("Preencha ao menos Nome e CPF.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ bool isNew = string.IsNullOrWhiteSpace(txtId.Text);
+
+ var funcionario = new ModeloFuncionarios
+ {
+ ID_FUNCIONARIO = isNew ? 0 : int.Parse(txtId.Text),
+
+ CODIGO = txtCodigo.Text,
+ NOME = txtNome.Text,
+ PAI = txtPai.Text,
+ MAE = txtMae.Text,
+ CPF = txtCPF.Text,
+ CI = txtCI.Text,
+ CTPS = txtCTPS.Text,
+ CNH = txtCNH.Text,
+ CAT_CNH = txtCatCNH.Text,
+ ECIVIL = txtECivil.Text,
+ REGIME = txtRegime.Text,
+
+ CEP = txtcep.Text,
+ ENDERECO = txtEndereco.Text,
+ ENDNUMERO = txtEndNumero.Text,
+ BAIRRO = txtBairro.Text,
+ CIDADE = txtCidade.Text,
+ UF = txtUF.Text,
+
+ // 🔥 BANCO vindo do ComboBox
+ BANCO = cbBanco.SelectedValue?.ToString(),
+
+ AGENCIA = txtAgencia.Text,
+ CONTA = txtConta.Text,
+ TELEFONE = txtTelefone.Text,
+ PASS = txtPass.Text,
+
+ // 🔥 BOOL correto
+ MASTERUSER = chkMasterUser.Checked,
+ VENDEDOR = chkVendedor.Checked,
+ TECNICO = chkTecnico.Checked,
+ DEMITIDO = chkDemitido.Checked,
+
+ FTECNICO = chkContador.Checked ? "1" : null,
+ FVENDEDOR = chkOutros.Checked ? "1" : null,
+
+ FPATH = txtFPath.Text,
+ OBSERV = txtObserv.Text,
+
+ // 🔥 DATAS
+ DATA_ADM = ConverterData(txtDataAdm.Text),
+ DATA_DEM = ConverterData(txtDataDem.Text),
+ ANIVER = ConverterData(txtAniver.Text)
+ };
+
+ bool sucesso = isNew
+ ? bll.Inserir(funcionario)
+ : bll.Alterar(funcionario);
+
+ if (!sucesso)
+ {
+ NT_MessageBox.Show("Erro ao salvar funcionário.",
+ "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ return;
+ }
+
+ SetCampos(false);
+
+ NT_MessageBox.Show("Funcionário salvo com sucesso!", "Sucesso",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ private void BtnFichaFuncionario_Click()
+ {
+ var bll = new BLLFuncionarios(_cx);
+
+ var funcionario = bll.CarregarPorCodigo("100");
+
+ if (funcionario == null)
+ {
+ MessageBox.Show("Funcionário não encontrado.");
+ return;
+ }
+
+ var resultado = TLL.FichaFuncionarioPDF.GerarEAbrir(funcionario);
+
+ if (!resultado.Sucesso)
+ MessageBox.Show(resultado.Mensagem, "Erro",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ private void BtnCancelar_Click()
+ {
+ foreach (var campo in TodosOsCampos())
+ campo.Text = string.Empty;
+
+ foreach (var chk in TodosOsChecks())
+ chk.Checked = false;
+
+ //SetCampos(false);
+ SetCamposAtulizado(false);
+ //this.cbBanco.Enabled = false;
+
+ }
+
+ // ── HELPERS ───────────────────────────────────────────────────────────
+
+ private Panel CreateSectionHeader(string title, int y)
+ {
+ var pnl = new Panel { Location = new Point(20, y), Width = 1000, Height = 26 };
+ var lbl = new Label
+ {
+ Text = title,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = AccentBlue,
+ AutoSize = true,
+ Location = new Point(0, 0)
+ };
+ var line = new Panel
+ {
+ BackColor = BorderColor,
+ Height = 1,
+ Width = 980,
+ Location = new Point(0, 20)
+ };
+ pnl.Controls.Add(lbl);
+ pnl.Controls.Add(line);
+ return pnl;
+ }
+
+ private RoundTextBox AddInput(Control parent, string label,
+ int x, int y, int width, int height,
+ bool readOnly = false)
+ {
+ var lbl = new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = readOnly ? Color.Gray : TextDark,
+ AutoSize = true
+ };
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, height),
+ Radius = 4,
+ BorderColor = readOnly ? Color.FromArgb(203, 213, 225) : BorderColor,
+ FocusColor = AccentBlue,
+ ReadOnly = readOnly,
+ BackColor = readOnly ? Color.FromArgb(241, 245, 249) : Color.White
+ };
+ parent.Controls.Add(lbl);
+ parent.Controls.Add(txt);
+ return txt;
+ }
+ private RoundComboBox AddComboBox(Control parent, string label,
+ int x, int y, int width, int height)
+ {
+ var lbl = new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+ var cb = new RoundComboBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, height),
+ Radius = 4,
+ BorderColor = BorderColor,
+ FocusColor = AccentBlue,
+ BackColor = Color.White
+ };
+ parent.Controls.Add(lbl);
+ parent.Controls.Add(cb);
+ return cb;
+ }
+
+ private CheckBox CreateCheckBox(string text, int x, int y) => new CheckBox
+ {
+ Text = text,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ };
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+ }
+
+ // ══════════════════════════════════════════════════════════════════════════
+ // DIALOG: Escolher origem da foto (Computador ou Webcam)
+ // ══════════════════════════════════════════════════════════════════════════
+
+ public class FotoOrigemDialog : Form
+ {
+ public bool UsarWebcam { get; private set; }
+
+ public FotoOrigemDialog()
+ {
+ Text = "Selecionar Foto";
+ Size = new Size(340, 160);
+ StartPosition = FormStartPosition.CenterParent;
+ FormBorderStyle = FormBorderStyle.FixedDialog;
+ MaximizeBox = false;
+ MinimizeBox = false;
+ BackColor = Color.White;
+
+ var lbl = new Label
+ {
+ Text = "Como deseja adicionar a foto?",
+ Font = new Font("Segoe UI", 9.5f),
+ Location = new Point(20, 18),
+ AutoSize = true
+ };
+
+ var btnComputador = new Button
+ {
+ Text = "📁 Do Computador",
+ Location = new Point(20, 55),
+ Size = new Size(140, 38),
+ FlatStyle = FlatStyle.Flat,
+ BackColor = Color.FromArgb(37, 99, 235),
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI", 9f),
+ Cursor = Cursors.Hand
+ };
+ btnComputador.FlatAppearance.BorderSize = 0;
+ btnComputador.Click += (s, e) =>
+ {
+ UsarWebcam = false;
+ DialogResult = DialogResult.OK;
+ Close();
+ };
+
+ var btnWebcam = new Button
+ {
+ Text = "📷 Webcam",
+ Location = new Point(172, 55),
+ Size = new Size(140, 38),
+ FlatStyle = FlatStyle.Flat,
+ BackColor = Color.FromArgb(16, 185, 129),
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI", 9f),
+ Cursor = Cursors.Hand
+ };
+ btnWebcam.FlatAppearance.BorderSize = 0;
+ btnWebcam.Click += (s, e) =>
+ {
+ UsarWebcam = true;
+ DialogResult = DialogResult.OK;
+ Close();
+ };
+
+ Controls.AddRange(new Control[] { lbl, btnComputador, btnWebcam });
+ }
+ }
+
+ // ══════════════════════════════════════════════════════════════════════════
+ // FORM: Captura pela Webcam usando AForge
+ // ══════════════════════════════════════════════════════════════════════════
+
+ public class WebcamCaptureForm : Form
+ {
+ public Bitmap? ImagemCapturada { get; private set; }
+
+ private PictureBox picPreview = null!;
+ private VideoCaptureDevice? _camera;
+ private Bitmap? _frameAtual;
+
+ public WebcamCaptureForm()
+ {
+ Text = "Capturar Foto pela Webcam";
+ Size = new Size(680, 560);
+ StartPosition = FormStartPosition.CenterParent;
+ FormBorderStyle = FormBorderStyle.FixedDialog;
+ MaximizeBox = false;
+ BackColor = Color.FromArgb(15, 23, 42);
+
+ picPreview = new PictureBox
+ {
+ Location = new Point(20, 20),
+ Size = new Size(628, 440),
+ SizeMode = PictureBoxSizeMode.Zoom,
+ BackColor = Color.Black
+ };
+ Controls.Add(picPreview);
+
+ var btnCapturar = new Button
+ {
+ Text = "📷 Capturar",
+ Location = new Point(20, 475),
+ Size = new Size(200, 38),
+ FlatStyle = FlatStyle.Flat,
+ BackColor = Color.FromArgb(37, 99, 235),
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI", 9.5f),
+ Cursor = Cursors.Hand
+ };
+ btnCapturar.FlatAppearance.BorderSize = 0;
+ btnCapturar.Click += BtnCapturar_Click;
+ Controls.Add(btnCapturar);
+
+ var btnCancelar = new Button
+ {
+ Text = "Cancelar",
+ Location = new Point(234, 475),
+ Size = new Size(120, 38),
+ FlatStyle = FlatStyle.Flat,
+ BackColor = Color.FromArgb(100, 116, 139),
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI", 9.5f),
+ Cursor = Cursors.Hand
+ };
+ btnCancelar.FlatAppearance.BorderSize = 0;
+ btnCancelar.Click += (s, e) => { PararCamera(); DialogResult = DialogResult.Cancel; Close(); };
+ Controls.Add(btnCancelar);
+
+ FormClosing += (s, e) => PararCamera();
+
+ IniciarCamera();
+ }
+
+ private void IniciarCamera()
+ {
+ var cameras = new FilterInfoCollection(FilterCategory.VideoInputDevice);
+
+ if (cameras.Count == 0)
+ {
+ MessageBox.Show("Nenhuma webcam encontrada.", "Webcam",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ DialogResult = DialogResult.Cancel;
+ Close();
+ return;
+ }
+
+ _camera = new VideoCaptureDevice(cameras[0].MonikerString);
+ _camera.NewFrame += Camera_NewFrame;
+ _camera.Start();
+ }
+
+ private void Camera_NewFrame(object sender, NewFrameEventArgs e)
+ {
+ _frameAtual?.Dispose();
+ _frameAtual = (Bitmap)e.Frame.Clone();
+
+ picPreview.Invoke((MethodInvoker)(() =>
+ {
+ picPreview.Image?.Dispose();
+ picPreview.Image = (Bitmap)_frameAtual.Clone();
+ }));
+ }
+
+ private void BtnCapturar_Click(object? sender, EventArgs e)
+ {
+ if (_frameAtual == null)
+ {
+ MessageBox.Show("Aguarde a câmera inicializar.", "Atenção",
+ MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ ImagemCapturada = (Bitmap)_frameAtual.Clone();
+ PararCamera();
+ DialogResult = DialogResult.OK;
+ Close();
+ }
+
+ private void PararCamera()
+ {
+ if (_camera != null && _camera.IsRunning)
+ {
+ _camera.SignalToStop();
+ _camera.WaitForStop();
+ }
+ }
+ }
+}
diff --git a/Dashboards/Cadastros/FuncionariosCadastroPanel.resx b/Dashboards/Cadastros/FuncionariosCadastroPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Cadastros/FuncionariosCadastroPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Configurações/Database/FrmConfigBanco.cs b/Dashboards/Configurações/Database/FrmConfigBanco.cs
new file mode 100644
index 0000000..6914ab6
--- /dev/null
+++ b/Dashboards/Configurações/Database/FrmConfigBanco.cs
@@ -0,0 +1,338 @@
+using CCH;
+using CustomMessageBox;
+using DAL;
+using Microsoft.Data.SqlClient; // Certifique-se de ter este pacote NuGet instalado
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+using TLL;
+
+namespace UI
+{
+ public class FrmConfigBanco : Form
+ {
+ // ── Cores LevelOS ──────────────────────────────────────────────────
+ private static readonly Color Surface = Color.White;
+ private static readonly Color Surface2 = Color.FromArgb(241, 245, 249);
+ private static readonly Color BorderCol = Color.FromArgb(226, 232, 240);
+ private static readonly Color TextPri = Color.FromArgb(15, 23, 42);
+ private static readonly Color TextSec = Color.FromArgb(71, 85, 105);
+ private static readonly Color TextMuted = Color.FromArgb(148, 163, 184);
+ private static readonly Color Blue = Color.FromArgb(37, 99, 235);
+ private static readonly Color Green = Color.FromArgb(22, 163, 74);
+ private static readonly Color Red = Color.FromArgb(220, 38, 38);
+ private static readonly Color NavyDark = Color.FromArgb(15, 30, 60);
+
+ // ── Controles ──────────────────────────────────────────────────────
+ private readonly TextBox txtHost = new();
+ private readonly TextBox txtPort = new();
+ private readonly TextBox txtBanco = new();
+ private readonly TextBox txtUsuario = new();
+ private readonly TextBox txtSenha = new();
+ private readonly TextBox txtTimeout = new();
+ private readonly CheckBox chkEncrypt = new();
+ private readonly CheckBox chkTrust = new();
+ private readonly Button btnSalvar = new();
+ private readonly Button btnTestar = new();
+ private readonly Button btnCancelar = new();
+ private readonly Label lblStatus = new();
+
+ public FrmConfigBanco()
+ {
+ Text = "LevelOS — Configuração do Banco de Dados";
+ Size = new Size(660, 680);
+ StartPosition = FormStartPosition.CenterScreen;
+ BackColor = Surface2;
+ FormBorderStyle = FormBorderStyle.FixedDialog;
+ MaximizeBox = false;
+
+ BuildLayout();
+ CarregarValores();
+
+ // ── Eventos ──────────────────────────────────────────────────
+ btnSalvar.Click += BtnSalvar_Click;
+ btnTestar.Click += BtnTestar_Click;
+ btnCancelar.Click += (s, e) => Close();
+
+ //Carregar dados
+ if (!File.Exists(AppFileSystem.AppFileDBSystem))
+ {
+ NT_MessageBox.Show("Arquivo de configuração do banco de dados ausente, por favor contacte o administrador do sistema para solucionar o problema", "Erro de SQL:101",
+ MessageBoxButtons.OK,MessageBoxIcon.Error);
+ }
+ else
+ {
+ DatabaseHelper.Carregar(AppFileSystem.AppFileDBSystem, AppInfoSystem.AppKeyMasterCrip);
+ this.txtHost.Text = DadosDaConexao.Host;
+ this.txtPort.Text = DadosDaConexao.Port.ToString();
+ this.txtBanco.Text = DadosDaConexao.Banco;
+ this.txtUsuario.Text = DadosDaConexao.Usuario;
+ this.txtSenha.Text = DadosDaConexao.Senha;
+ this.chkEncrypt.Checked = DadosDaConexao.Encrypt;
+ this.txtTimeout.Text = DadosDaConexao.ConnectTimeout.ToString();
+ this.chkTrust.Checked = DadosDaConexao.TrustServerCertificate;
+ }
+ }
+
+ private void BuildLayout()
+ {
+ // 1. TOPBAR
+ var topbar = new Panel { Dock = DockStyle.Top, Height = 75, BackColor = NavyDark };
+ topbar.Paint += DrawTopbar;
+ Controls.Add(topbar);
+
+ // 2. RODAPÉ
+ var footer = new Panel { Dock = DockStyle.Bottom, Height = 65, BackColor = Surface };
+ footer.Paint += (s, e) => e.Graphics.DrawLine(new Pen(BorderCol), 0, 0, footer.Width, 0);
+ Controls.Add(footer);
+
+ StyleButton(btnCancelar, "Cancelar", Surface, TextSec, BorderCol);
+ btnCancelar.SetBounds(20, 15, 100, 35);
+ btnCancelar.Anchor = AnchorStyles.Left;
+ footer.Controls.Add(btnCancelar);
+
+ StyleButton(btnSalvar, "✔ Salvar", Blue, Color.White);
+ btnSalvar.SetBounds(footer.Width - 120, 15, 100, 35);
+ btnSalvar.Anchor = AnchorStyles.Right;
+ footer.Controls.Add(btnSalvar);
+
+ StyleButton(btnTestar, "Testar conexão", Surface, TextPri, BorderCol);
+ btnTestar.SetBounds(btnSalvar.Left - 145, 15, 135, 35);
+ btnTestar.Anchor = AnchorStyles.Right;
+ footer.Controls.Add(btnTestar);
+
+ lblStatus.SetBounds(130, 15, 250, 35);
+ lblStatus.TextAlign = ContentAlignment.MiddleLeft;
+ lblStatus.Font = new Font("Segoe UI", 9f);
+ lblStatus.Anchor = AnchorStyles.Left | AnchorStyles.Right;
+ footer.Controls.Add(lblStatus);
+
+ // 3. CONTAINER DE CONTEÚDO
+ var pnlContainer = new Panel { Dock = DockStyle.Fill, AutoScroll = true, Padding = new Padding(0, 10, 0, 20) };
+ Controls.Add(pnlContainer);
+ pnlContainer.BringToFront();
+
+ // 4. CARD BRANCO
+ var card = new Panel { BackColor = Surface, Width = 560, Left = 40, Top = 20 };
+ pnlContainer.Controls.Add(card);
+
+ int cy = 30;
+
+ cy = AddSection(card, "SERVIDOR", cy);
+ AddLabel(card, "Host / IP", 30, cy);
+ AddLabel(card, "Porta", 430, cy);
+ cy += 20;
+ StyleInput(txtHost, "ex: localhost ou 192.168.0.1");
+ txtHost.SetBounds(30, cy, 385, 30); card.Controls.Add(txtHost);
+ StyleInput(txtPort, "1433");
+ txtPort.SetBounds(430, cy, 100, 30); card.Controls.Add(txtPort);
+ cy += 45;
+
+ cy = AddSection(card, "BANCO DE DADOS", cy);
+ AddLabel(card, "Nome do Banco", 30, cy);
+ cy += 20;
+ StyleInput(txtBanco, "ex: LevelOS_DB");
+ txtBanco.SetBounds(30, cy, 500, 30); card.Controls.Add(txtBanco);
+ cy += 45;
+
+ cy = AddSection(card, "AUTENTICAÇÃO", cy);
+ AddLabel(card, "Usuário", 30, cy);
+ AddLabel(card, "Senha", 285, cy);
+ cy += 20;
+ StyleInput(txtUsuario, "sa");
+ txtUsuario.SetBounds(30, cy, 245, 30); card.Controls.Add(txtUsuario);
+ StyleInput(txtSenha, "••••••••");
+ txtSenha.SetBounds(285, cy, 245, 30); txtSenha.PasswordChar = '●'; card.Controls.Add(txtSenha);
+ cy += 45;
+
+ cy = AddSection(card, "AVANÇADO", cy);
+ AddLabel(card, "Timeout de conexão (segundos)", 30, cy);
+ cy += 20;
+ StyleInput(txtTimeout, "3");
+ txtTimeout.SetBounds(30, cy, 100, 30); card.Controls.Add(txtTimeout);
+ cy += 40;
+
+ StyleCheckbox(chkEncrypt, "Encrypt Connection");
+ chkEncrypt.SetBounds(30, cy, 250, 25); card.Controls.Add(chkEncrypt);
+ cy += 30;
+ StyleCheckbox(chkTrust, "Trust Server Certificate");
+ chkTrust.SetBounds(30, cy, 250, 25); card.Controls.Add(chkTrust);
+ cy += 40;
+
+ card.Height = cy;
+ }
+
+ // ── Lógica de Negócio ──────────────────────────────────────────────
+
+ private void CarregarValores()
+ {
+ txtHost.Text = DbConfig.Host;
+ txtPort.Text = DbConfig.Port.ToString();
+ txtBanco.Text = DbConfig.Banco;
+ txtUsuario.Text = DbConfig.Usuario;
+ txtSenha.Text = DbConfig.Senha;
+ txtTimeout.Text = DbConfig.ConnectTimeout.ToString();
+ chkEncrypt.Checked = DbConfig.Encrypt;
+ chkTrust.Checked = DbConfig.TrustServerCertificate;
+ }
+
+ private void BtnSalvar_Click(object? sender, EventArgs e)
+ {
+ if (!Validar()) return;
+
+ DbConfig.Host = txtHost.Text.Trim();
+ DbConfig.Port = int.TryParse(txtPort.Text, out int p) ? p : 1433;
+ DbConfig.Banco = txtBanco.Text.Trim();
+ DbConfig.Usuario = txtUsuario.Text.Trim();
+ DbConfig.Senha = txtSenha.Text;
+ DbConfig.ConnectTimeout = int.TryParse(txtTimeout.Text, out int t) ? t : 3;
+ DbConfig.Encrypt = chkEncrypt.Checked;
+ DbConfig.TrustServerCertificate = chkTrust.Checked;
+
+ //Preenchendo dados da conexao
+ DadosDaConexao.Host = DbConfig.Host;
+ DadosDaConexao.Port = DbConfig.Port;
+ DadosDaConexao.Banco =DbConfig.Banco;
+ DadosDaConexao.Usuario = DbConfig.Usuario;
+ DadosDaConexao.Senha = DbConfig.Senha;
+ DadosDaConexao.ConnectTimeout = DbConfig.ConnectTimeout;
+ DadosDaConexao.Encrypt = chkEncrypt.Checked;
+ DadosDaConexao.TrustServerCertificate = chkTrust.Checked;
+ //salvar em arquivo criptografado
+ DatabaseHelper.Salvar(AppFileSystem.AppFileDBSystem, AppInfoSystem.AppKeyMasterCrip);
+
+
+ SetStatus("✔ Configurações salvas!", Green);
+ }
+
+ private void BtnTestar_Click(object? sender, EventArgs e)
+ {
+ if (!Validar()) return;
+
+ SetStatus("⏳ Testando...", TextMuted);
+ btnTestar.Enabled = false;
+ Application.DoEvents(); // Força a interface a atualizar o status
+
+ try
+ {
+ using var conn = new SqlConnection(BuildConnStr());
+ conn.Open();
+ SetStatus($"✔ Conectado! SQL {conn.ServerVersion}", Green);
+ }
+ catch (Exception ex)
+ {
+ SetStatus($"✖ Erro: {ex.Message}", Red);
+ }
+ finally
+ {
+ btnTestar.Enabled = true;
+ }
+ }
+
+ private string BuildConnStr()
+ {
+ SqlConnectionStringBuilder builder = new()
+ {
+ DataSource = $"{txtHost.Text.Trim()},{txtPort.Text.Trim()}",
+ InitialCatalog = txtBanco.Text.Trim(),
+ UserID = txtUsuario.Text.Trim(),
+ Password = txtSenha.Text,
+ ConnectTimeout = int.TryParse(txtTimeout.Text, out int t) ? t : 3,
+ Encrypt = chkEncrypt.Checked,
+ TrustServerCertificate = chkTrust.Checked
+ };
+ return builder.ConnectionString;
+ }
+
+ private bool Validar()
+ {
+ if (string.IsNullOrWhiteSpace(txtHost.Text)) { SetStatus("✖ Informe o Host.", Red); txtHost.Focus(); return false; }
+ if (string.IsNullOrWhiteSpace(txtBanco.Text)) { SetStatus("✖ Informe o Banco.", Red); txtBanco.Focus(); return false; }
+ if (string.IsNullOrWhiteSpace(txtUsuario.Text)) { SetStatus("✖ Informe o Usuário.", Red); txtUsuario.Focus(); return false; }
+ return true;
+ }
+
+ private void SetStatus(string msg, Color color)
+ {
+ lblStatus.Text = msg;
+ lblStatus.ForeColor = color;
+ }
+
+ // ── Helpers de UI ──────────────────────────────────────────────────
+
+ private void DrawTopbar(object sender, PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ var rect = new Rectangle(25, 20, 34, 34);
+ using var path = RoundRectPath(rect, 8);
+ g.FillPath(new SolidBrush(Blue), path);
+ g.DrawString("S", new Font("Segoe UI", 14, FontStyle.Bold), Brushes.White, rect, new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
+ g.DrawString("Configuração do Banco", new Font("Segoe UI Semibold", 12.5f), Brushes.White, 70, 18);
+ g.DrawString("Defina os parâmetros de conexão com o SQL Server", new Font("Segoe UI", 9), new SolidBrush(Color.FromArgb(148, 163, 184)), 70, 40);
+ }
+
+ private int AddSection(Panel p, string title, int y)
+ {
+ var line = new Panel { BackColor = BorderCol, Height = 1, Width = p.Width - 60, Left = 30, Top = y };
+ p.Controls.Add(line);
+ var lbl = new Label { Text = title, Font = new Font("Segoe UI", 7, FontStyle.Bold), ForeColor = TextMuted, Top = y - 7, Left = 35, AutoSize = true, BackColor = Surface };
+ p.Controls.Add(lbl);
+ lbl.BringToFront();
+ return y + 20;
+ }
+
+ private void AddLabel(Panel p, string txt, int x, int y)
+ {
+ p.Controls.Add(new Label { Text = txt, Font = new Font("Segoe UI Semibold", 9), ForeColor = TextSec, Left = x, Top = y, AutoSize = true });
+ }
+
+ private void StyleInput(TextBox t, string placeholder)
+ {
+ t.BorderStyle = BorderStyle.FixedSingle;
+ t.Font = new Font("Segoe UI", 10);
+ t.PlaceholderText = placeholder;
+ t.BackColor = Color.FromArgb(250, 251, 253);
+ }
+
+ private void StyleCheckbox(CheckBox chk, string text)
+ {
+ chk.Text = text; chk.Font = new Font("Segoe UI", 9.5f);
+ chk.ForeColor = TextSec; chk.Cursor = Cursors.Hand;
+ }
+
+ private void StyleButton(Button b, string txt, Color bg, Color fg, Color? borda = null)
+ {
+ b.Text = txt; b.FlatStyle = FlatStyle.Flat; b.BackColor = bg; b.ForeColor = fg; b.Cursor = Cursors.Hand;
+ b.Font = new Font("Segoe UI Semibold", 9.5f);
+ b.FlatAppearance.BorderSize = borda.HasValue ? 1 : 0;
+ if (borda.HasValue) b.FlatAppearance.BorderColor = borda.Value;
+ }
+
+ private static GraphicsPath RoundRectPath(Rectangle r, int radius)
+ {
+ var path = new GraphicsPath();
+ int d = radius * 2;
+ path.AddArc(r.X, r.Y, d, d, 180, 90);
+ path.AddArc(r.Right - d, r.Y, d, d, 270, 90);
+ path.AddArc(r.Right - d, r.Bottom - d, d, d, 0, 90);
+ path.AddArc(r.X, r.Bottom - d, d, d, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+ }
+
+ // ── Dados (DbConfig) ──────────────────────────────────────────────────
+ public static class DbConfig
+ {
+ public static string Host { get; set; } = "localhost";
+ public static int Port { get; set; } = 1433;
+ public static string Banco { get; set; } = "";
+ public static string Usuario { get; set; } = "sa";
+ public static string Senha { get; set; } = "";
+ public static int ConnectTimeout { get; set; } = 3;
+ public static bool Encrypt { get; set; } = true;
+ public static bool TrustServerCertificate { get; set; } = true;
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Configurações/Database/FrmConfigBanco.resx b/Dashboards/Configurações/Database/FrmConfigBanco.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Configurações/Database/FrmConfigBanco.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Consultas/AgendaConsultaPanel.cs b/Dashboards/Consultas/AgendaConsultaPanel.cs
new file mode 100644
index 0000000..b1d23fb
--- /dev/null
+++ b/Dashboards/Consultas/AgendaConsultaPanel.cs
@@ -0,0 +1,431 @@
+using BLL;
+using DAL;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class AgendaConsultaPanel : UserControl
+ {
+ // ── CORES ─────────────────────────────────────────────────────────────
+ private readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private readonly Color TextDark = Color.FromArgb(30, 41, 59);
+ private readonly Color BorderColor = Color.FromArgb(226, 232, 240);
+ private readonly Color GreenColor = Color.FromArgb(34, 197, 94);
+ private readonly Color MutedGray = Color.FromArgb(148, 163, 184);
+ private readonly Color SurfaceColor = Color.FromArgb(248, 250, 252);
+
+ // ── CONTROLES ─────────────────────────────────────────────────────────
+ private Panel pnlToolbar = null!;
+ private Panel pnlFiltros = null!;
+ private Panel pnlRodape = null!;
+ private DataGridView dgvAgenda = null!;
+ private Label lblTotal = null!;
+
+ // ── FILTROS ───────────────────────────────────────────────────────────
+ private RoundTextBox txtFiltroCompromisso = null!;
+ private RoundTextBox txtFiltroFunc = null!;
+ private RoundTextBox txtFiltroOsVinc = null!;
+ private RoundTextBox txtDataInicio = null!;
+ private RoundTextBox txtDataFim = null!;
+ private ComboBox cmbSituacao = null!;
+
+ // ── DADOS ─────────────────────────────────────────────────────────────
+ private List _todos = new();
+ private List _filtrados = new();
+
+ // ── EVENTO: abre cadastro com o registro selecionado ──────────────────
+ public event Action? OnAbrirCadastro;
+
+ //Carrega a string conexao com o banco de dados, para ser usada no repositório
+ private string _conexao = DadosDaConexao.ObterConexao();
+
+ // ── CONSTRUTOR ────────────────────────────────────────────────────────
+ public AgendaConsultaPanel()
+ {
+ Dock = DockStyle.Fill;
+ BackColor = Color.White;
+ DoubleBuffered = true;
+
+ InitializeLayout();
+ //CarregarDadosFake();
+ CarregarDadosDoBanco();
+ AplicarFiltros();
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // LAYOUT
+ //
+ // REGRA WINFORMS — ordem de adição ao Controls é INVERSA à exibição:
+ // 1º adicionar → Fill (ocupa o centro)
+ // 2º adicionar → Bottom
+ // 3º adicionar → Top (empurra o Fill para baixo; último = mais alto)
+ //
+ // ══════════════════════════════════════════════════════════════════════
+ private void InitializeLayout()
+ {
+ Controls.Clear();
+
+ // ── 1º: GRID (Fill) ───────────────────────────────────────────────
+ BuildGrid();
+ Controls.Add(dgvAgenda);
+
+ // ── 2º: RODAPÉ (Bottom) ───────────────────────────────────────────
+ pnlRodape = new Panel
+ {
+ Dock = DockStyle.Bottom,
+ Height = 30,
+ BackColor = SurfaceColor
+ };
+ lblTotal = new Label
+ {
+ AutoSize = true,
+ Location = new Point(16, 7),
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ ForeColor = MutedGray,
+ Text = "0 registros encontrados"
+ };
+ pnlRodape.Controls.Add(lblTotal);
+ Controls.Add(pnlRodape);
+
+ // ── 3º: FILTROS (Top) ─────────────────────────────────────────────
+ pnlFiltros = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 95,
+ BackColor = SurfaceColor,
+ Padding = new Padding(16, 8, 16, 8)
+ };
+ BuildFiltros();
+ Controls.Add(pnlFiltros);
+
+ // ── 4º: TOOLBAR (Top) — por último = fica acima dos filtros ───────
+ pnlToolbar = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = 55,
+ BackColor = SurfaceColor
+ };
+
+ var flow = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(12, 10, 0, 0),
+ BackColor = Color.Transparent
+ };
+
+ var btnPesquisar = CreateToolbarButton("Pesquisar", AccentBlue);
+ var btnLimpar = CreateToolbarButton("Limpar", MutedGray);
+ var btnAbrir = CreateToolbarButton("Abrir", GreenColor);
+ var btnExportar = CreateToolbarButton("Exportar", Color.FromArgb(99, 102, 241));
+
+ btnPesquisar.Click += (_, _) => AplicarFiltros();
+ btnLimpar.Click += (_, _) => LimparFiltros();
+ btnAbrir.Click += (_, _) => AbrirRegistroSelecionado();
+ btnExportar.Click += (_, _) => ExportarCSV();
+
+ flow.Controls.AddRange(new Control[] { btnPesquisar, btnLimpar, btnAbrir, btnExportar });
+ pnlToolbar.Controls.Add(flow);
+ Controls.Add(pnlToolbar); // ← último Top = aparece no topo
+ }
+
+ // ── FILTROS ───────────────────────────────────────────────────────────
+ private void BuildFiltros()
+ {
+ // Linha 1
+ txtFiltroCompromisso = AddFiltroInput(pnlFiltros, "Compromisso", 0, 8, 260);
+ txtFiltroFunc = AddFiltroInput(pnlFiltros, "Funcionário", 270, 8, 200);
+ txtFiltroOsVinc = AddFiltroInput(pnlFiltros, "OS Vinculada", 480, 8, 130);
+
+ // Linha 2
+ txtDataInicio = AddFiltroInput(pnlFiltros, "Data Início", 0, 50, 130);
+ txtDataFim = AddFiltroInput(pnlFiltros, "Data Fim", 140, 50, 130);
+
+ pnlFiltros.Controls.Add(new Label
+ {
+ Text = "Situação",
+ Location = new Point(280, 50),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ });
+
+ cmbSituacao = new ComboBox
+ {
+ Location = new Point(280, 66),
+ Size = new Size(160, 26),
+ DropDownStyle = ComboBoxStyle.DropDownList,
+ Font = new Font("Segoe UI", 8.5f),
+ FlatStyle = FlatStyle.Flat
+ };
+ cmbSituacao.Items.AddRange(new object[] { "Todos", "Pendente", "Realizado" });
+ cmbSituacao.SelectedIndex = 0;
+ pnlFiltros.Controls.Add(cmbSituacao);
+
+ foreach (var txt in new[] { txtFiltroCompromisso, txtFiltroFunc, txtFiltroOsVinc, txtDataInicio, txtDataFim })
+ txt.KeyDown += (_, e) => { if (e.KeyCode == Keys.Enter) AplicarFiltros(); };
+ }
+
+ private RoundTextBox AddFiltroInput(Control parent, string label, int x, int y, int width)
+ {
+ parent.Controls.Add(new Label
+ {
+ Text = label,
+ Location = new Point(x, y),
+ Font = new Font("Segoe UI", 7.5f, FontStyle.Bold),
+ ForeColor = TextDark,
+ AutoSize = true
+ });
+ var txt = new RoundTextBox
+ {
+ Location = new Point(x, y + 16),
+ Size = new Size(width, 26),
+ Radius = 4,
+ BorderColor = BorderColor,
+ FocusColor = AccentBlue,
+ BackColor = Color.White
+ };
+ parent.Controls.Add(txt);
+ return txt;
+ }
+
+ // ── GRID ──────────────────────────────────────────────────────────────
+ private void BuildGrid()
+ {
+ dgvAgenda = new DataGridView
+ {
+ Dock = DockStyle.Fill,
+ BackgroundColor = Color.White,
+ BorderStyle = BorderStyle.None,
+ CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal,
+ ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single,
+ GridColor = BorderColor,
+ RowHeadersVisible = false,
+ AllowUserToAddRows = false,
+ AllowUserToDeleteRows = false,
+ ReadOnly = true,
+ SelectionMode = DataGridViewSelectionMode.FullRowSelect,
+ MultiSelect = false,
+ Font = new Font("Segoe UI", 8.5f),
+ AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
+ ColumnHeadersHeight = 36,
+ ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing,
+ RowTemplate = { Height = 32 }
+ };
+
+ dgvAgenda.ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle
+ {
+ BackColor = SurfaceColor,
+ ForeColor = TextDark,
+ Font = new Font("Segoe UI", 8.5f, FontStyle.Bold),
+ Alignment = DataGridViewContentAlignment.MiddleLeft,
+ Padding = new Padding(8, 0, 0, 0),
+ SelectionBackColor = SurfaceColor,
+ SelectionForeColor = TextDark
+ };
+
+ dgvAgenda.DefaultCellStyle = new DataGridViewCellStyle
+ {
+ ForeColor = TextDark,
+ BackColor = Color.White,
+ SelectionBackColor = Color.FromArgb(239, 246, 255),
+ SelectionForeColor = AccentBlue,
+ Padding = new Padding(8, 0, 0, 0)
+ };
+
+ dgvAgenda.AlternatingRowsDefaultCellStyle = new DataGridViewCellStyle
+ {
+ BackColor = SurfaceColor,
+ SelectionBackColor = Color.FromArgb(239, 246, 255),
+ SelectionForeColor = AccentBlue
+ };
+
+ dgvAgenda.Columns.AddRange(new DataGridViewColumn[]
+ {
+ new DataGridViewTextBoxColumn { Name = "colId", HeaderText = "ID", FillWeight = 5 },
+ new DataGridViewTextBoxColumn { Name = "colCodigo", HeaderText = "Código", FillWeight = 8 },
+ new DataGridViewTextBoxColumn { Name = "colCompromisso", HeaderText = "Compromisso", FillWeight = 26 },
+ new DataGridViewTextBoxColumn { Name = "colData", HeaderText = "Data", FillWeight = 10 },
+ new DataGridViewTextBoxColumn { Name = "colHora", HeaderText = "Hora", FillWeight = 7 },
+ new DataGridViewTextBoxColumn { Name = "colDia", HeaderText = "Dia", FillWeight = 10 },
+ new DataGridViewTextBoxColumn { Name = "colFunc", HeaderText = "Funcionário", FillWeight = 16 },
+ new DataGridViewTextBoxColumn { Name = "colAvisar", HeaderText = "Avisar", FillWeight = 12 },
+ new DataGridViewTextBoxColumn { Name = "colOs", HeaderText = "OS Vinc.", FillWeight = 8 },
+ new DataGridViewTextBoxColumn { Name = "colSituacao", HeaderText = "Situação", FillWeight = 10 },
+ });
+
+ dgvAgenda.CellDoubleClick += (_, e) =>
+ {
+ if (e.RowIndex >= 0) AbrirRegistroSelecionado();
+ };
+
+ dgvAgenda.CellFormatting += (_, e) =>
+ {
+ if (e.RowIndex < 0 || e.ColumnIndex < 0) return;
+ if (dgvAgenda.Columns[e.ColumnIndex].Name != "colSituacao") return;
+
+ bool realizado = e.Value?.ToString() == "Realizado";
+ e.CellStyle.ForeColor = realizado ? Color.FromArgb(22, 101, 52) : Color.FromArgb(146, 64, 14);
+ e.CellStyle.Font = new Font("Segoe UI", 8f, FontStyle.Bold);
+ e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor;
+ e.FormattingApplied = true;
+ };
+ }
+
+ // ══════════════════════════════════════════════════════════════════════
+ // LÓGICA
+ // ══════════════════════════════════════════════════════════════════════
+ private void AplicarFiltros()
+ {
+ _filtrados = _todos.ToList();
+
+ var termComp = txtFiltroCompromisso.Text.Trim().ToLower();
+ if (!string.IsNullOrEmpty(termComp))
+ _filtrados = _filtrados.Where(e => e.COMPROMISSO?.ToLower().Contains(termComp) == true).ToList();
+
+ var termFunc = txtFiltroFunc.Text.Trim().ToLower();
+ if (!string.IsNullOrEmpty(termFunc))
+ _filtrados = _filtrados.Where(e => e.FUNC?.ToLower().Contains(termFunc) == true).ToList();
+
+ var termOs = txtFiltroOsVinc.Text.Trim().ToLower();
+ if (!string.IsNullOrEmpty(termOs))
+ _filtrados = _filtrados.Where(e => e.OS_VINC?.ToLower().Contains(termOs) == true).ToList();
+
+ if (DateTime.TryParseExact(txtDataInicio.Text.Trim(), "dd/MM/yyyy",
+ System.Globalization.CultureInfo.InvariantCulture,
+ System.Globalization.DateTimeStyles.None, out var dtIni))
+ _filtrados = _filtrados.Where(e => ParseData(e.dDATA) >= dtIni).ToList();
+
+ if (DateTime.TryParseExact(txtDataFim.Text.Trim(), "dd/MM/yyyy",
+ System.Globalization.CultureInfo.InvariantCulture,
+ System.Globalization.DateTimeStyles.None, out var dtFim))
+ _filtrados = _filtrados.Where(e => ParseData(e.dDATA) <= dtFim).ToList();
+
+ var sit = cmbSituacao.SelectedItem?.ToString();
+ if (sit == "Realizado") _filtrados = _filtrados.Where(e => e.REALIZADO?.ToUpper() == "S").ToList();
+ else if (sit == "Pendente") _filtrados = _filtrados.Where(e => e.REALIZADO?.ToUpper() != "S").ToList();
+
+ PreencherGrid();
+ }
+
+ private void LimparFiltros()
+ {
+ txtFiltroCompromisso.Text = string.Empty;
+ txtFiltroFunc.Text = string.Empty;
+ txtFiltroOsVinc.Text = string.Empty;
+ txtDataInicio.Text = string.Empty;
+ txtDataFim.Text = string.Empty;
+ cmbSituacao.SelectedIndex = 0;
+ AplicarFiltros();
+ }
+
+ private void PreencherGrid()
+ {
+ dgvAgenda.Rows.Clear();
+
+ foreach (var ev in _filtrados.OrderBy(e => ParseData(e.dDATA)).ThenBy(e => e.HORA))
+ {
+ dgvAgenda.Rows.Add(
+ ev.ID_AGENDA, ev.CODIGO, ev.COMPROMISSO,
+ ev.dDATA, ev.HORA, ev.DIA, ev.FUNC, ev.AVISAR, ev.OS_VINC,
+ ev.REALIZADO?.ToUpper() == "S" ? "Realizado" : "Pendente"
+ );
+ }
+
+ int total = _filtrados.Count;
+ lblTotal.Text = total == 1 ? "1 registro encontrado" : $"{total} registros encontrados";
+ }
+
+ private void AbrirRegistroSelecionado()
+ {
+ if (dgvAgenda.SelectedRows.Count == 0)
+ {
+ MessageBox.Show("Selecione um registro na lista.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ var row = dgvAgenda.SelectedRows[0];
+ if (row.Cells["colId"].Value is int id)
+ {
+ var ev = _filtrados.FirstOrDefault(e => e.ID_AGENDA == id);
+ if (ev != null) OnAbrirCadastro?.Invoke(ev);
+ }
+ }
+
+ private void ExportarCSV()
+ {
+ if (!_filtrados.Any())
+ {
+ MessageBox.Show("Nenhum registro para exportar.",
+ "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ using var dlg = new SaveFileDialog
+ {
+ Filter = "CSV (*.csv)|*.csv",
+ FileName = $"Agenda_{DateTime.Today:yyyyMMdd}.csv"
+ };
+ if (dlg.ShowDialog() != DialogResult.OK) return;
+
+ var sb = new System.Text.StringBuilder();
+ sb.AppendLine("ID;Código;Compromisso;Data;Hora;Dia;Funcionário;Avisar;OS Vinculada;Situação");
+ foreach (var ev in _filtrados.OrderBy(e => ParseData(e.dDATA)).ThenBy(e => e.HORA))
+ sb.AppendLine($"{ev.ID_AGENDA};{ev.CODIGO};{ev.COMPROMISSO};{ev.dDATA};" +
+ $"{ev.HORA};{ev.DIA};{ev.FUNC};{ev.AVISAR};{ev.OS_VINC};" +
+ $"{(ev.REALIZADO?.ToUpper() == "S" ? "Realizado" : "Pendente")}");
+
+ System.IO.File.WriteAllText(dlg.FileName, sb.ToString(), System.Text.Encoding.UTF8);
+ MessageBox.Show($"Exportado com sucesso!\n{dlg.FileName}", "Exportar",
+ MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ // ── DADOS FAKE — substituir pelo repositório ──────────────────────────
+ private void CarregarDadosFake()
+ {
+ var hoje = DateTime.Today;
+ _todos = new List
+ {
+ new(1, "AG001", "Reunião com cliente", hoje.ToString("dd/MM/yyyy"), "30 minutos antes", "Carlos Silva", DiaSemana(hoje), "09:00", "S", "OS-1042"),
+ new(2, "AG002", "Visita técnica", hoje.ToString("dd/MM/yyyy"), "1 hora antes", "Ana Souza", DiaSemana(hoje), "14:00", "N", "OS-1055"),
+ new(3, "AG003", "Entrega de equipamento", hoje.AddDays(4).ToString("dd/MM/yyyy"), "1 dia antes", "Pedro Lima", DiaSemana(hoje.AddDays(4)), "10:30", "N", ""),
+ new(4, "AG004", "Manutenção preventiva", hoje.AddDays(-2).ToString("dd/MM/yyyy"), "30 minutos antes", "Carlos Silva", DiaSemana(hoje.AddDays(-2)), "08:00", "S", "OS-1030"),
+ new(5, "AG005", "Treinamento equipe", hoje.AddDays(7).ToString("dd/MM/yyyy"), "1 dia antes", "Ana Souza", DiaSemana(hoje.AddDays(7)), "13:00", "N", ""),
+ };
+ }//Carregar dados fakes para ixibição
+ private void CarregarDadosDoBanco()
+ {
+ BLLAgenda _agendaBLL = new BLLAgenda(_conexao);
+ _todos.Clear();
+ _todos.AddRange(_agendaBLL.Listar());
+ }//Carregar dados reais do banco de dados usando o repositório
+
+ private static DateTime ParseData(string? value)
+ {
+ if (string.IsNullOrWhiteSpace(value)) return DateTime.MinValue;
+ if (DateTime.TryParseExact(value, "dd/MM/yyyy",
+ System.Globalization.CultureInfo.InvariantCulture,
+ System.Globalization.DateTimeStyles.None, out var dt)) return dt;
+ if (DateTime.TryParse(value, out dt)) return dt;
+ return DateTime.MinValue;
+ }
+
+ private static string DiaSemana(DateTime d) =>
+ new System.Globalization.CultureInfo("pt-BR").DateTimeFormat.GetDayName(d.DayOfWeek);
+
+ private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton
+ {
+ Text = text,
+ Size = new Size(95, 32),
+ BackColor = color,
+ ForeColor = Color.White,
+ Font = new Font("Segoe UI Semibold", 8.5f),
+ Margin = new Padding(0, 0, 6, 0),
+ Cursor = Cursors.Hand
+ };
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Consultas/AgendaConsultaPanel.resx b/Dashboards/Consultas/AgendaConsultaPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Consultas/AgendaConsultaPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Dashmain/DashboardPanel.cs b/Dashboards/Dashmain/DashboardPanel.cs
new file mode 100644
index 0000000..98ca3b9
--- /dev/null
+++ b/Dashboards/Dashmain/DashboardPanel.cs
@@ -0,0 +1,368 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+using System.Globalization;
+namespace UI
+{
+ // ═══════════════════════════════════════════════════════════════════════
+ // DashboardPanel — painel principal com KPI cards + tabela de OS
+ // ═══════════════════════════════════════════════════════════════════════
+ public class DashboardPanel : UserControl
+ {
+ // ── Paleta ─────────────────────────────────────────────────────────
+ private static readonly Color Surface = Color.FromArgb(255, 255, 255);
+ private static readonly Color Surface2 = Color.FromArgb(248, 250, 252);
+ private static readonly Color Border = Color.FromArgb(226, 232, 240);
+ private static readonly Color TextPri = Color.FromArgb(15, 23, 42);
+ private static readonly Color TextSec = Color.FromArgb(71, 85, 105);
+ private static readonly Color TextMuted = Color.FromArgb(148, 163, 184);
+
+ private static readonly Color Blue = Color.FromArgb(37, 99, 235);
+ private static readonly Color BlueLight = Color.FromArgb(219, 234, 254);
+ private static readonly Color Green = Color.FromArgb(22, 163, 74);
+ private static readonly Color GreenL = Color.FromArgb(220, 252, 231);
+ private static readonly Color Amber = Color.FromArgb(217, 119, 6);
+ private static readonly Color AmberL = Color.FromArgb(254, 243, 199);
+ private static readonly Color Red = Color.FromArgb(220, 38, 38);
+ private static readonly Color RedL = Color.FromArgb(254, 226, 226);
+ private static readonly Color Orange = Color.FromArgb(234, 88, 12);
+ private static readonly Color OrangeL = Color.FromArgb(255, 237, 213);
+
+ // ── Dados de OS ────────────────────────────────────────────────────
+ private readonly List _ordens = new()
+ {
+ new OsRow("#0001", "João Silva", "Notebook Dell", OsStatus.EmAndamento, "07/04"),
+ new OsRow("#0002", "Maria Souza", "PC Gamer", OsStatus.Concluida, "07/04"),
+ new OsRow("#0003", "Carlos Mota", "Impressora HP", OsStatus.Aguardando, "08/04"),
+ new OsRow("#0004", "Ana Lima", "iPhone 14", OsStatus.EmAndamento, "08/04"),
+ new OsRow("#0005", "Pedro Rocha", "Monitor LG", OsStatus.Pendente, "09/04"),
+ };
+
+ // ── KPI cards ──────────────────────────────────────────────────────
+ // CORRIGIDO: instanciação posicional explícita (sem parâmetros nomeados)
+ private readonly KpiCard[] _kpis;
+
+ // ── Layout ─────────────────────────────────────────────────────────
+ private const int Pad = 24;
+ private const int KpiH = 110;
+ private const int KpiGap = 14;
+ private const int CardRad = 10;
+ private const int TopbarH = 56;
+ private const int RowH = 38;
+
+ private readonly Font _fLabel;
+ private readonly Font _fValue;
+ private readonly Font _fDelta;
+ private readonly Font _fHead;
+ private readonly Font _fCell;
+ private readonly Font _fColHead;
+ private readonly Font _fMono;
+ private readonly Font _fBtn;
+ private readonly Font _fTopTitle;
+ private readonly Font _fTopSub;
+
+ private Rectangle _btnNovaOS;
+ private int _hoverRow = -1;
+
+ public DashboardPanel()
+ {
+ // CORRIGIDO: inicialização dos campos Font no construtor
+ // (evita o aviso "campo não anulável precisa conter valor não nulo")
+ _fLabel = new Font("Segoe UI", 8.5f);
+ _fValue = new Font("Segoe UI Semibold", 18f);
+ _fDelta = new Font("Segoe UI", 9f);
+ _fHead = new Font("Segoe UI Semibold", 10.5f);
+ _fCell = new Font("Segoe UI", 10f);
+ _fColHead = new Font("Segoe UI", 8f);
+ _fMono = new Font("Consolas", 9.5f);
+ _fBtn = new Font("Segoe UI Semibold", 10f);
+ _fTopTitle = new Font("Segoe UI Semibold", 13f);
+ _fTopSub = new Font("Segoe UI", 9f);
+
+ // CORRIGIDO: KpiCard instanciado com construtor posicional explícito
+ _kpis = new[]
+ {
+ new KpiCard("OS Abertas", "12", "↑ 4 essa semana", true, BlueLight, Blue),
+ new KpiCard("OS Concluídas", "45", "↑ 12% vs mês", true, GreenL, Green),
+ new KpiCard("Faturamento", "22.5k", "↑ R$ 2.100", true, AmberL, Amber),
+ new KpiCard("Pendências", "3", "↓ aguardando", false, RedL, Red),
+ };
+
+ BackColor = Surface2;
+ DoubleBuffered = true;
+ SetStyle(ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.OptimizedDoubleBuffer |
+ ControlStyles.ResizeRedraw, true);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _fLabel.Dispose(); _fValue.Dispose(); _fDelta.Dispose();
+ _fHead.Dispose(); _fCell.Dispose(); _fColHead.Dispose();
+ _fMono.Dispose(); _fBtn.Dispose(); _fTopTitle.Dispose();
+ _fTopSub.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ // ── Paint ──────────────────────────────────────────────────────────
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ DrawTopbar(g);
+ int y = TopbarH + Pad;
+ DrawKpiRow(g, y);
+ y += KpiH + 20;
+ DrawOsCard(g, y);
+ }
+
+ // ── Topbar ─────────────────────────────────────────────────────────
+ private void DrawTopbar(Graphics g)
+ {
+ g.FillRectangle(new SolidBrush(Surface), 0, 0, Width, TopbarH);
+ g.DrawLine(new Pen(Border, 1), 0, TopbarH, Width, TopbarH);
+
+ g.DrawString("Home", _fTopTitle, new SolidBrush(TextPri), Pad, 12);
+ //g.DrawString("Quinta, 09 de abril de 2026", _fTopSub,
+ g.DrawString(
+ DateTime.Now.ToString("dd 'de' MMMM 'de' yyyy", new CultureInfo("pt-BR")), _fTopSub,
+ new SolidBrush(TextMuted), Pad, 32);
+
+ int bw = 110, bh = 32;
+ int bx = Width - Pad - bw;
+ int by = (TopbarH - bh) / 2;
+ _btnNovaOS = new Rectangle(bx, by, bw, bh);
+ FillRoundRect(g, new SolidBrush(Blue), _btnNovaOS, 6);
+
+ var sf = new StringFormat
+ {
+ Alignment = StringAlignment.Center,
+ LineAlignment = StringAlignment.Center
+ };
+ g.DrawString("+ Nova OS", _fBtn, new SolidBrush(Color.White), _btnNovaOS, sf);
+ }
+
+ // ── KPI Row ────────────────────────────────────────────────────────
+ private void DrawKpiRow(Graphics g, int y)
+ {
+ int totalGap = KpiGap * (_kpis.Length - 1);
+ int kpiW = (Width - Pad * 2 - totalGap) / _kpis.Length;
+
+ for (int i = 0; i < _kpis.Length; i++)
+ {
+ int x = Pad + i * (kpiW + KpiGap);
+ DrawKpiCard(g, _kpis[i], new Rectangle(x, y, kpiW, KpiH));
+ }
+ }
+
+ private void DrawKpiCard(Graphics g, KpiCard kpi, Rectangle r)
+ {
+ FillRoundRect(g, new SolidBrush(Surface), r, CardRad);
+ DrawRoundBorder(g, r, CardRad, Border);
+
+ int dotSize = 36;
+ var dotRect = new Rectangle(r.Right - 16 - dotSize, r.Y + 16, dotSize, dotSize);
+ FillRoundRect(g, new SolidBrush(kpi.DotBg), dotRect, 8);
+
+ g.DrawString(kpi.Label, _fLabel, new SolidBrush(TextMuted), r.X + 16, r.Y + 18);
+ g.DrawString(kpi.Value, _fValue, new SolidBrush(TextPri), r.X + 14, r.Y + 36);
+
+ var deltaColor = kpi.DeltaUp ? Green : Red;
+ g.DrawString(kpi.Delta, _fDelta, new SolidBrush(deltaColor), r.X + 16, r.Y + 76);
+ }
+
+ // ── OS Card ────────────────────────────────────────────────────────
+ private void DrawOsCard(Graphics g, int y)
+ {
+ string[] cols = { "OS", "Cliente", "Equipamento", "Status", "Data" };
+ float[] colW = { 0.08f, 0.22f, 0.28f, 0.22f, 0.10f };
+
+ int cardH = 28 + RowH + RowH * _ordens.Count + 16;
+ var card = new Rectangle(Pad, y, Width - Pad * 2, cardH);
+
+ FillRoundRect(g, new SolidBrush(Surface), card, CardRad);
+ DrawRoundBorder(g, card, CardRad, Border);
+
+ g.DrawString("Ordens de Serviço Recentes", _fHead,
+ new SolidBrush(TextPri), card.X + 18, card.Y + 16);
+
+ int tableY = card.Y + 44;
+ g.DrawLine(new Pen(Border, 0.5f), card.X, tableY, card.Right, tableY);
+
+ int cx = card.X;
+ for (int c = 0; c < cols.Length; c++)
+ {
+ int cw = (int)(card.Width * colW[c]);
+ var sf = new StringFormat { LineAlignment = StringAlignment.Center };
+ g.DrawString(cols[c].ToUpperInvariant(), _fColHead,
+ new SolidBrush(TextMuted), cx + 10, tableY + 2, sf);
+ cx += cw;
+ }
+
+ for (int i = 0; i < _ordens.Count; i++)
+ {
+ int ry = tableY + RowH + i * RowH;
+ var row = _ordens[i];
+
+ if (i == _hoverRow)
+ g.FillRectangle(new SolidBrush(Surface2),
+ card.X + 1, ry, card.Width - 2, RowH);
+
+ g.DrawLine(new Pen(Border, 0.5f), card.X, ry, card.Right, ry);
+
+ cx = card.X;
+ int[] cws = GetColWidths(card.Width, colW);
+
+ g.DrawString(row.OS, _fMono,
+ new SolidBrush(TextMuted), cx + 10, ry + (RowH - 14) / 2);
+ cx += cws[0];
+
+ g.DrawString(row.Cliente, _fCell,
+ new SolidBrush(TextPri), cx + 8, ry + (RowH - 14) / 2);
+ cx += cws[1];
+
+ g.DrawString(row.Equipamento, _fCell,
+ new SolidBrush(TextSec), cx + 8, ry + (RowH - 14) / 2);
+ cx += cws[2];
+
+ DrawBadge(g, row.Status, cx + 8, ry + (RowH - 20) / 2);
+ cx += cws[3];
+
+ g.DrawString(row.Data, _fCell,
+ new SolidBrush(TextMuted), cx + 8, ry + (RowH - 14) / 2);
+ }
+ }
+
+ private static int[] GetColWidths(int total, float[] ratios)
+ {
+ var ws = new int[ratios.Length];
+ for (int i = 0; i < ratios.Length; i++)
+ ws[i] = (int)(total * ratios[i]);
+ return ws;
+ }
+
+ private void DrawBadge(Graphics g, OsStatus status, int x, int y)
+ {
+ var (label, bg, fg) = status switch
+ {
+ OsStatus.Concluida => ("Concluída", GreenL, Color.FromArgb(22, 101, 52)),
+ OsStatus.EmAndamento => ("Em andamento", AmberL, Color.FromArgb(146, 64, 14)),
+ OsStatus.Aguardando => ("Aguardando", BlueLight, Color.FromArgb(30, 64, 175)),
+ OsStatus.Pendente => ("Pendente", RedL, Color.FromArgb(153, 27, 27)),
+ _ => ("—", Border, TextMuted)
+ };
+
+ var sz = g.MeasureString(label, _fDelta);
+ int bw = (int)sz.Width + 16;
+ int bh = 20;
+ var badge = new Rectangle(x, y, bw, bh);
+ FillRoundRect(g, new SolidBrush(bg), badge, 10);
+
+ var sf = new StringFormat
+ {
+ Alignment = StringAlignment.Center,
+ LineAlignment = StringAlignment.Center
+ };
+ g.DrawString(label, _fDelta, new SolidBrush(fg), badge, sf);
+ }
+
+ // ── Mouse ──────────────────────────────────────────────────────────
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ int row = HitTestRow(e.Y);
+ if (row != _hoverRow) { _hoverRow = row; Invalidate(); }
+ Cursor = _btnNovaOS.Contains(e.Location) ? Cursors.Hand : Cursors.Default;
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ _hoverRow = -1;
+ Invalidate();
+ }
+
+ private int HitTestRow(int mouseY)
+ {
+ int tableY = TopbarH + Pad + KpiH + 20 + 44 + RowH;
+ for (int i = 0; i < _ordens.Count; i++)
+ {
+ int ry = tableY + i * RowH;
+ if (mouseY >= ry && mouseY < ry + RowH) return i;
+ }
+ return -1;
+ }
+
+ // ── GDI Helpers ────────────────────────────────────────────────────
+ private static void FillRoundRect(Graphics g, Brush brush, Rectangle r, int radius)
+ {
+ using var path = RoundRectPath(r, radius);
+ g.FillPath(brush, path);
+ }
+
+ private static void DrawRoundBorder(Graphics g, Rectangle r, int radius, Color color)
+ {
+ using var path = RoundRectPath(r, radius);
+ g.DrawPath(new Pen(color, 0.5f), path);
+ }
+
+ private static GraphicsPath RoundRectPath(Rectangle r, int radius)
+ {
+ var path = new GraphicsPath();
+ path.AddArc(r.X, r.Y, radius * 2, radius * 2, 180, 90);
+ path.AddArc(r.Right - radius * 2, r.Y, radius * 2, radius * 2, 270, 90);
+ path.AddArc(r.Right - radius * 2, r.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
+ path.AddArc(r.X, r.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+
+ // ── Modelos ────────────────────────────────────────────────────────
+ // CORRIGIDO: usando classes simples em vez de records posicionais
+ // para compatibilidade com qualquer versão do compilador .NET 6+.
+ private class OsRow
+ {
+ public string OS { get; }
+ public string Cliente { get; }
+ public string Equipamento { get; }
+ public OsStatus Status { get; }
+ public string Data { get; }
+
+ public OsRow(string os, string cliente, string equipamento, OsStatus status, string data)
+ {
+ OS = os;
+ Cliente = cliente;
+ Equipamento = equipamento;
+ Status = status;
+ Data = data;
+ }
+ }
+
+ private class KpiCard
+ {
+ public string Label { get; }
+ public string Value { get; }
+ public string Delta { get; }
+ public bool DeltaUp { get; }
+ public Color DotBg { get; }
+ public Color DotFg { get; }
+
+ public KpiCard(string label, string value, string delta, bool deltaUp, Color dotBg, Color dotFg)
+ {
+ Label = label;
+ Value = value;
+ Delta = delta;
+ DeltaUp = deltaUp;
+ DotBg = dotBg;
+ DotFg = dotFg;
+ }
+ }
+
+ private enum OsStatus { Concluida, EmAndamento, Aguardando, Pendente }
+ }
+}
diff --git a/Dashboards/Dashmain/DashboardPanel.resx b/Dashboards/Dashmain/DashboardPanel.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Dashmain/DashboardPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Dashmain/MainForm.cs b/Dashboards/Dashmain/MainForm.cs
new file mode 100644
index 0000000..2f5d973
--- /dev/null
+++ b/Dashboards/Dashmain/MainForm.cs
@@ -0,0 +1,147 @@
+using CCH;
+using CustomMessageBox;
+using DAL;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using TLL;
+namespace UI
+{
+ public class MainForm : Form
+ {
+ private readonly SidebarControl _sidebar;
+ private readonly DashboardPanel _dashboard;
+
+ private readonly ClienteCadastroPanel _pClientes;
+ private readonly EmpresaCadastroPanel _pEmpresa;
+ private readonly EmpresaConfiguracoesPanel _pEmpresaConfig;
+ private readonly AgendaCadastroPanel _pAgendaCadastro;
+ private readonly AgendaConsultaPanel _pAgendaConsulta;
+ private readonly FuncionariosCadastroPanel _pfuncionariosCadastro ;
+ private readonly Panel _pOrdens;
+ private readonly Panel _pProdutos;
+ private readonly Panel _pEstoque;
+ private readonly Panel _pFinanceiro;
+
+ public MainForm()
+ {
+ Text = "LevelOS — Sistema ERP";
+ this.WindowState = FormWindowState.Maximized;
+ this.MinimumSize = new Size(1100, 750);
+ this.BackColor = Color.FromArgb(248, 250, 252);
+
+ _sidebar = new SidebarControl { Dock = DockStyle.Left };
+ _sidebar.NavItemClicked += OnNavItemClicked;
+
+ _dashboard = new DashboardPanel { Dock = DockStyle.Fill };
+
+ _pClientes = new ClienteCadastroPanel { Dock = DockStyle.Fill, Visible = false };
+ _pEmpresa = new EmpresaCadastroPanel { Dock = DockStyle.Fill, Visible = false };
+ _pEmpresaConfig = new EmpresaConfiguracoesPanel { Dock = DockStyle.Fill, Visible = false };
+ _pAgendaCadastro = new AgendaCadastroPanel { Dock = DockStyle.Fill, Visible = false };
+ _pAgendaConsulta = new AgendaConsultaPanel { Dock = DockStyle.Fill, Visible = false };
+ _pfuncionariosCadastro = new FuncionariosCadastroPanel { Dock = DockStyle.Fill, Visible = false };
+ _pOrdens = PlaceholderPanel("Ordens de Serviço", Color.FromArgb(22, 163, 74));
+ _pProdutos = PlaceholderPanel("Catálogo de Produtos", Color.FromArgb(217, 119, 6));
+ _pEstoque = PlaceholderPanel("Controle de Estoque", Color.FromArgb(234, 88, 12));
+ _pFinanceiro = PlaceholderPanel("Fluxo Financeiro", Color.FromArgb(124, 58, 237));
+
+ var mainContainer = new Panel { Dock = DockStyle.Fill };
+ mainContainer.Controls.Add(_pFinanceiro);
+ mainContainer.Controls.Add(_pAgendaCadastro);
+ mainContainer.Controls.Add(_pAgendaConsulta);
+ mainContainer.Controls.Add(_pEstoque);
+ mainContainer.Controls.Add(_pProdutos);
+ mainContainer.Controls.Add(_pOrdens);
+ mainContainer.Controls.Add(_pEmpresa);
+ mainContainer.Controls.Add(_pEmpresaConfig);
+ mainContainer.Controls.Add(_pClientes);
+ mainContainer.Controls.Add(_dashboard);
+ mainContainer.Controls.Add(_pfuncionariosCadastro);
+
+ Controls.Add(mainContainer);
+ Controls.Add(_sidebar);
+
+ ShowPanel(_dashboard);
+
+ }
+
+ private void OnNavItemClicked(object? sender, int index)
+ {
+ /* Índices:
+ 0 = Dashboard
+ 2 = Ordens de Serviço
+ 4 = Produtos
+ 5 = Estoque
+ 9 = Financeiro
+ 99 = Submenu → Clientes
+ 98 = Submenu → Empresa
+ */
+ switch (index)
+ {
+ case 0: ShowPanel(_dashboard); break;
+ case 2: ShowPanel(_pOrdens); break;
+ case 4: ShowPanel(_pProdutos); break;
+ case 5: ShowPanel(_pEstoque); break;
+ case 9: ShowPanel(_pFinanceiro); break;
+ case 99: ShowPanel(_pClientes); break;
+ case 103: ShowPanel(_pfuncionariosCadastro); break;
+ case 106: ShowPanel(_pEmpresa); break;
+
+
+ case 202: ShowPanel(_pEmpresaConfig); break;
+ case 300: ShowPanel(_pAgendaCadastro); break;
+ case 301: ShowPanel(_pAgendaConsulta); break;
+
+
+ }
+ }
+
+ private void ShowPanel(Control panelToShow)
+ {
+ _dashboard.Visible = (panelToShow == _dashboard);
+ _pClientes.Visible = (panelToShow == _pClientes);
+ _pEmpresa.Visible = (panelToShow == _pEmpresa);
+ _pEmpresaConfig.Visible = (panelToShow == _pEmpresaConfig);
+ _pOrdens.Visible = (panelToShow == _pOrdens);
+ _pProdutos.Visible = (panelToShow == _pProdutos);
+ _pEstoque.Visible = (panelToShow == _pEstoque);
+ _pFinanceiro.Visible = (panelToShow == _pFinanceiro);
+ _pAgendaCadastro.Visible = (panelToShow == _pAgendaCadastro);
+ _pAgendaConsulta.Visible = (panelToShow == _pAgendaConsulta);
+ _pfuncionariosCadastro.Visible = (panelToShow == _pfuncionariosCadastro);
+
+ if (panelToShow.Visible)
+ {
+ panelToShow.BringToFront();
+ panelToShow.Focus();
+ }
+ }
+
+ private static Panel PlaceholderPanel(string titulo, Color cor)
+ {
+ var p = new Panel { Dock = DockStyle.Fill, Visible = false };
+ p.Paint += (s, e) =>
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+ var r = p.ClientRectangle;
+ g.Clear(Color.FromArgb(248, 250, 252));
+
+ var circle = new Rectangle(r.Width / 2 - 40, r.Height / 2 - 80, 80, 80);
+ g.FillEllipse(new SolidBrush(Color.FromArgb(20, cor)), circle);
+
+ using var f1 = new Font("Segoe UI Semibold", 16f);
+ using var f2 = new Font("Segoe UI", 10f);
+ var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+
+ g.DrawString(titulo, f1, new SolidBrush(Color.FromArgb(30, 41, 59)),
+ new RectangleF(0, r.Height / 2f - 10, r.Width, 40), sf);
+
+ g.DrawString("Este módulo está sendo integrado ao LevelOS", f2, new SolidBrush(Color.FromArgb(148, 163, 184)),
+ new RectangleF(0, r.Height / 2f + 25, r.Width, 30), sf);
+ };
+ return p;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Dashboards/Dashmain/MainForm.resx b/Dashboards/Dashmain/MainForm.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/Dashboards/Dashmain/MainForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Dashboards/Dashmain/SidebarControl.cs b/Dashboards/Dashmain/SidebarControl.cs
new file mode 100644
index 0000000..6b5fbd8
--- /dev/null
+++ b/Dashboards/Dashmain/SidebarControl.cs
@@ -0,0 +1,423 @@
+using CustomMessageBox;
+using DAL;
+using DALL;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace UI
+{
+ public class SidebarControl : UserControl
+ {
+ //Recebendo a conexao
+ string _cx = DadosDaConexao.ObterConexao();
+
+ // ── Cores do Tema LevelOS ──────────────────────────────────────────
+ private static readonly Color NavyDark = Color.FromArgb(15, 30, 60);
+ private static readonly Color NavyMid = Color.FromArgb(26, 45, 80);
+ private static readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
+ private static readonly Color TextLight = Color.FromArgb(255, 255, 255);
+ private static readonly Color TextMuted = Color.FromArgb(148, 163, 184);
+ private static readonly Color Divider = Color.FromArgb(40, 255, 255, 255);
+
+ // ── Estado e Escalonamento (DPI) ───────────────────────────────────
+ private float _scale = 1.0f;
+ private int _activeIndex = 0;
+ private int _hoverIndex = -1;
+
+ // Propriedades Scaled (Ajustam-se ao monitor)
+ private int ScaledWidth => (int)(220 * _scale);
+ private int ScaledLogoH => (int)(55 * _scale);
+ private int ScaledSectionH => (int)(32 * _scale);
+ private int ScaledItemH => (int)(40 * _scale);
+ private int ScaledItemPadX => (int)(12 * _scale);
+ private int ScaledFooterH => (int)(50 * _scale);
+ private int ScaledIconSize => (int)(16 * _scale);
+
+ // ── Submenus ───────────────────────────────────────────────────────
+ private ContextMenuStrip _subMenuBanco, _subMenuConfiguracao,
+ _subMenuAjuda, _subMenuOrdemServico, _subMenuFinanceiro,_subMenuCadastro, _subMenuAgenda;
+
+ public string UserName = "Levelcode", UserFunction = "Administrador";
+ public event EventHandler? NavItemClicked;
+
+ private readonly List _items = new()
+ {
+ new NavItem("Dashboard", SvgIcon.Grid, "Principal", null),
+ new NavItem("Cadastro", SvgIcon.UserPlus, null, "3"),
+ new NavItem("Ordens de Serviço", SvgIcon.FileText, null, "12"),
+ new NavItem("Agenda", SvgIcon.Calendar, null, null),
+ new NavItem("Produtos", SvgIcon.Package, "Gestão", null),
+ new NavItem("Estoque", SvgIcon.Inventory, null, null),
+ //new NavItem("Serviços", SvgIcon.Briefcase, null, null),
+ //new NavItem("Transportadoras", SvgIcon.Truck, null, null),
+ new NavItem("Financeiro", SvgIcon.DollarSign, null, null),
+ new NavItem("Banco de Dados", SvgIcon.Database, "Sistema", null),
+ new NavItem("Configurações", SvgIcon.Settings, null, null),
+ new NavItem("Suporte Técnico", SvgIcon.Support, "Ajuda", null),
+ };
+
+ public void backupFull(string conexao)
+ {
+ var backupService = new DALLBackupService(conexao);
+ var resultadoFull = backupService.ExecutarBackupFull();
+
+ if (resultadoFull.Sucesso)
+ NT_MessageBox.Show($"✅ Backup FULL concluído em {resultadoFull.Duracao.TotalSeconds:F1}s","Backup do banco de dados",MessageBoxButtons.OK, MessageBoxIcon.Information);
+ else
+ NT_MessageBox.Show($"❌ Erro no Backup FULL: {resultadoFull.Erro}","Erro ao tentar executar backup do sistema.",MessageBoxButtons.OK,MessageBoxIcon.Error);
+ }//Criar backup full
+
+ public void backupDifrencial(string conexao)
+ {
+ var backupService = new DALLBackupService(conexao);
+ var resultadoDiff = backupService.ExecutarBackupDiferencial();
+
+ if (resultadoDiff.Sucesso)
+ NT_MessageBox.Show($"✅ Backup DIFERENCIAL concluído em {resultadoDiff.Duracao.TotalSeconds:F1}s","Backup diferencial concluido", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ else
+ NT_MessageBox.Show($"❌ Erro no Backup DIFERENCIAL: {resultadoDiff.Erro}","Erro ao tentar executar backup diferencial no sistema", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }//Criar backup diferencial
+
+
+
+ public SidebarControl()
+ {
+ BackColor = NavyDark;
+ DoubleBuffered = true;
+ this.SetStyle(ControlStyles.ResizeRedraw, true);
+ }
+
+ protected override void OnHandleCreated(EventArgs e)
+ {
+ base.OnHandleCreated(e);
+ using (Graphics g = this.CreateGraphics()) { _scale = g.DpiX / 96f; }
+ this.Width = ScaledWidth;
+ SetupSubMenus();
+ }
+
+ private void SetupSubMenus()
+ {
+ //Submenu Cadastro
+ _subMenuCadastro = CreateStyledMenu();
+ _subMenuCadastro.Items.Add("👤 Clientes", null, (s, e) => {NavItemClicked?.Invoke(this, 99);});
+ _subMenuCadastro.Items.Add("💻 Equipamentos", null, (s, e) => { NavItemClicked?.Invoke(this, 100); });
+ _subMenuCadastro.Items.Add("📜 Contratos", null, (s, e) => { NavItemClicked?.Invoke(this, 101); });
+ _subMenuCadastro.Items.Add("🏭 Fornecedores", null, (s, e) => { NavItemClicked?.Invoke(this, 102); });
+ _subMenuCadastro.Items.Add("👔 Funcionários", null, (s, e) => { NavItemClicked?.Invoke(this, 103); });
+ _subMenuCadastro.Items.Add("🚚 Transportadoras", null, (s, e) => { NavItemClicked?.Invoke(this, 104); });
+ _subMenuCadastro.Items.Add("🛠️ Serviços", null, (s, e) => { NavItemClicked?.Invoke(this, 105); });
+ _subMenuCadastro.Items.Add("🏢 Empresa", null, (s, e) => { NavItemClicked?.Invoke(this, 106); });
+ _subMenuCadastro.Items.Add("🔑 Usuários do Sistema", null, (s, e) => { NavItemClicked?.Invoke(this, 107); });
+
+ _subMenuBanco = CreateStyledMenu();
+ _subMenuBanco.Items.Add("⚙️ Configuração Database", null, (s, e) => showForms