resolvendo merge

This commit is contained in:
levelcode developed 2026-04-17 21:23:46 -03:00
commit 407b4b4b17
44 changed files with 8816 additions and 1 deletions

8
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vs/
<<<<<<< HEAD
**/.vs/
bin/
obj/
@ -8,3 +9,10 @@ obj/
*.cache
*.user
*.suo
=======
bin/
obj/
*.user
*.suo
*.cache
>>>>>>> 577e97245b8945b1bfd71ca7bb3971042d27d2a9

View File

@ -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 funcionários'),
('FUNCIONARIO_CADASTRAR', 'Cadastrar funcionários'),
('FUNCIONARIO_EDITAR', 'Editar funcionários'),
('FUNCIONARIO_EXCLUIR', 'Excluir funcionários'),
-- ========================
-- USUARIOS / SEGURANÇA
-- ========================
('USUARIO_VER', 'Visualizar usuários'),
('USUARIO_CADASTRAR', 'Cadastrar usuários'),
('USUARIO_EDITAR', 'Editar usuários'),
('USUARIO_EXCLUIR', 'Excluir usuários'),
('PERMISSAO_GERENCIAR', 'Gerenciar permissões'),
-- ========================
-- SERVIÇOS
-- ========================
('SERVICO_VER', 'Visualizar serviços'),
('SERVICO_CADASTRAR', 'Cadastrar serviços'),
('SERVICO_EDITAR', 'Editar serviços'),
('SERVICO_EXCLUIR', 'Excluir serviços'),
-- ========================
-- 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 relatórios financeiros'),
('RELATORIO_CLIENTES', 'Acessar relatórios de clientes'),
('RELATORIO_GERAL', 'Acessar relatórios gerais'),
-- ========================
-- SISTEMA
-- ========================
('SISTEMA_CONFIG', 'Alterar configurações 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'
);

View File

@ -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 PERMISSÕES
-- =========================
INSERT INTO PerfilPermissoes (PerfilId, PermissaoId)
SELECT
@PerfilId,
Id
FROM Permissoes
WHERE NOT EXISTS (
SELECT 1 FROM PerfilPermissoes
WHERE PerfilId = @PerfilId AND PermissaoId = Permissoes.Id
);

View File

@ -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 básicos
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,
-- Endereço
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
-- ================================
-- CONFIGURAÇÕES
-- ================================
CREATE TABLE Empresa_config (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmpresaId INT NOT NULL,
-- Aparência
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,
-- Comunicação
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
-- ================================
-- FUNCIONÁRIOS
-- ================================
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,
-- Permissão
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 básicos
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),
-- Endereço
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 básicos
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),
-- Endereço
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),
-- Classificação
Grupo VARCHAR(100),
-- Endereço
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
-- ================================
-- SERVIÇOS
-- ================================
CREATE TABLE Servicos (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmpresaId INT NOT NULL,
-- Dados básicos
Nome VARCHAR(255) NOT NULL,
Descricao VARCHAR(MAX),
-- Valores
ValorPadrao DECIMAL(10,2) NOT NULL,
Custo DECIMAL(10,2),
-- Comissão
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
-- ================================
-- Situação OS
-- ================================
CREATE TABLE SituacoesOS (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmpresaId INT NOT NULL,
-- Identificação
Descricao VARCHAR(255) NOT NULL,
-- Classificação (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,
-- Aparência (igual na tela)
CorFundo VARCHAR(20),
CorFonte VARCHAR(20),
-- Financeiro (opcional)
PlanoContasId INT NULL,
-- Controle
Ordem INT DEFAULT 0, -- ordem de exibição
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 padrão)
-- 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,
-- Identificação
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 lançar 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,
-- Classificação
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,
-- Observações
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,
-- Classificação
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,
-- Observações
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
-- ================================
-- USUÁRIOS 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 USUÁRIO
-- ================================
CREATE TABLE Perfis (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmpresaId INT NOT NULL,
Nome VARCHAR(100) NOT NULL, -- Admin, Técnico, etc
Descricao VARCHAR(255),
Ativo BIT NOT NULL DEFAULT 1,
CONSTRAINT FK_PerfilEmpresa
FOREIGN KEY (EmpresaId) REFERENCES Empresa(Id) ON DELETE CASCADE
);
GO
-- ================================
-- PERMISSÕES DE USUÁRIO
-- ================================
CREATE TABLE Permissoes (
Id INT IDENTITY(1,1) PRIMARY KEY,
Nome VARCHAR(100) NOT NULL, -- Ex: CLIENTE_CADASTRAR
Descricao VARCHAR(255)
);
GO
-- ================================
-- PERFIL - PERMISSÕES
-- ================================
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
-- ================================
-- USUÁRIO - 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

View File

@ -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}");
}

View File

@ -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');

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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';

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

151
ControlesCustom.cs Normal file
View File

@ -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;
}
}
}

120
ControlesCustom.resx Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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<MLL.ModeloAgenda> _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<AgendaConsultaPanel>("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<DateTime> GetDatesWithEvents()
{
var set = new HashSet<DateTime>();
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
};
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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
};
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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
};
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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();
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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;
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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<MLL.ModeloAgenda> _todos = new();
private List<MLL.ModeloAgenda> _filtrados = new();
// ── EVENTO: abre cadastro com o registro selecionado ──────────────────
public event Action<MLL.ModeloAgenda>? 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<MLL.ModeloAgenda>
{
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
};
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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<OsRow> _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 }
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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;
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -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<int>? NavItemClicked;
private readonly List<NavItem> _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<Form>()); // Substitua pelo seu Form
_subMenuBanco.Items.Add("💾 Backup de Dados (FULL)", null, (s, e) => backupFull(this._cx));
_subMenuBanco.Items.Add("⚡ Backup de Dados (DIFF)", null, (s, e) => backupDifrencial(this._cx));
_subMenuBanco.Items.Add("🔄 Restaurar Banco");
//submenu Configurações
_subMenuConfiguracao = CreateStyledMenu();
_subMenuConfiguracao.Items.Add("🖼️ Personalização de OS");
_subMenuConfiguracao.Items.Add("🖥️ Personalização de Sistema");
_subMenuConfiguracao.Items.Add(" Configuração Empresa", null, (s, e) => { NavItemClicked?.Invoke(this, 202); });
_subMenuConfiguracao.Items.Add(new ToolStripSeparator());
_subMenuConfiguracao.Items.Add("📂 FTP-Cliente"); // Pasta para arquivos
_subMenuConfiguracao.Items.Add("💬 Telegram-Cliente"); // Balão de conversa
_subMenuConfiguracao.Items.Add("📩 SMTP-Cliente"); // Envelope de saída
_subMenuConfiguracao.Items.Add(new ToolStripSeparator());
// Automação
_subMenuConfiguracao.Items.Add("⏰ Backups Automáticos"); // Relógio para agendamento
_subMenuConfiguracao.Items.Add("☁️ Backup em Nuvem");
_subMenuAjuda = CreateStyledMenu();
_subMenuAjuda.Items.Add("💬 Atendimento Online");
_subMenuAjuda.Items.Add("📖 Manual do Sistema");
_subMenuOrdemServico = CreateStyledMenu();
_subMenuOrdemServico.Items.Add("✚ Abrir nova O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("✎ Alterar O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("🔒 Encerrar O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("🔍 Localizar O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("📋 Orçamento de O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("📜 Histórico de O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("📊 Relatório de O.S", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("🖨 Imprimir", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("⚠️ Chamado Técnico", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("🌐 OS WEB", null, (s, e) => { /* Lógica */ });
_subMenuOrdemServico.Items.Add("🔓 Reabrir OS", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro = CreateStyledMenu();
_subMenuFinanceiro.Items.Add("💳 Contas", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("📈 Contas a receber", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("📉 Contas a pagar", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add(new ToolStripSeparator()); // Linha divisória
_subMenuFinanceiro.Items.Add("🛒 Compras", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("💰 Vendas", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("🧾 Notas Fiscais", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add(new ToolStripSeparator()); // Linha divisória
_subMenuFinanceiro.Items.Add("📂 Arquivo Contador", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("🏦 Dados Bancários", null, (s, e) => { /* Lógica */ });
_subMenuFinanceiro.Items.Add("🕒 Histórico Financeiro", null, (s, e) => { /* Lógica */ });
// private ContextMenuStrip _subMenuAgenda;
_subMenuAgenda = CreateStyledMenu();
//_subMenuAgenda.Items.Add(" Novo compromisso", null, (s, e) => {showForms<AgendaForm>();});
_subMenuAgenda.Items.Add(" Novo compromisso", null, (s, e) => { NavItemClicked?.Invoke(this, 300); });
_subMenuAgenda.Items.Add("🔍 Consultar compromissos", null, (s, e) => { NavItemClicked?.Invoke(this, 301); });
_subMenuAgenda.Items.Add("✏️ Alterar compromisso", null, (s, e) => { /* Lógica de edição */ });
_subMenuAgenda.Items.Add("🖨️ Imprimir", null, (s, e) => { /* Lógica de relatório */ });
}
private ContextMenuStrip CreateStyledMenu()
{
return new ContextMenuStrip
{
BackColor = NavyMid,
ForeColor = TextLight,
ShowImageMargin = false,
Font = new Font("Segoe UI", 10f * _scale),
Renderer = new ToolStripProfessionalRenderer(new SubMenuColorTable())
};
}
private void showForms<T>() where T : Form, new()
{
using (T form = new T())
{
form.StartPosition = FormStartPosition.CenterScreen;
form.ShowDialog();
}
}
//Abrir agenda
// AgendaForm.cs
public class AgendaForm : Form
{
public AgendaForm()
{
Text = "Agenda de Compromissos";
Size = new Size(1150, 780);
StartPosition = FormStartPosition.CenterScreen;
BackColor = Color.White;
var panel = new AgendaCadastroPanel();
Controls.Add(panel);
}
}//abrir agenda de compromissos
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
DrawLogo(g);
DrawNavItems(g, ScaledLogoH + (int)(8 * _scale));
DrawFooter(g);
}
private void DrawNavItems(Graphics g, int startY)
{
int y = startY;
using var fSection = new Font("Segoe UI", 8f * _scale, FontStyle.Bold);
using var fItem = new Font("Segoe UI", 10f * _scale);
using var fItemSel = new Font("Segoe UI Semibold", 10f * _scale);
for (int i = 0; i < _items.Count; i++)
{
var item = _items[i];
if (item.Section != null)
{
y += (int)(10 * _scale);
g.DrawString(item.Section.ToUpper(), fSection, new SolidBrush(Color.FromArgb(120, TextMuted)), ScaledItemPadX + (int)(8 * _scale), y);
y += ScaledSectionH;
}
bool isActive = (i == _activeIndex);
bool isHover = (i == _hoverIndex && !isActive);
var itemRect = new Rectangle(ScaledItemPadX, y, Width - ScaledItemPadX * 2, ScaledItemH);
if (isActive) FillRoundRect(g, new SolidBrush(AccentBlue), itemRect, (int)(6 * _scale));
else if (isHover) FillRoundRect(g, new SolidBrush(Color.FromArgb(20, 255, 255, 255)), itemRect, (int)(6 * _scale));
var color = isActive ? TextLight : Color.FromArgb(200, TextLight);
DrawIcon(g, item.Icon, ScaledItemPadX + (int)(10 * _scale), y + (ScaledItemH - ScaledIconSize) / 2, ScaledIconSize, color);
g.DrawString(item.Label, isActive ? fItemSel : fItem, new SolidBrush(color), ScaledItemPadX + (int)(36 * _scale), y + (ScaledItemH - (int)(16 * _scale)) / 2);
if (HasSubMenu(item.Label))
g.DrawString("", fItem, new SolidBrush(Color.FromArgb(100, TextLight)), Width - (int)(30 * _scale), y + (ScaledItemH - (int)(18 * _scale)) / 2);
y += ScaledItemH + (int)(2 * _scale);
}
}
// Adicione "Cadastro" na verificação
private bool HasSubMenu(string label) =>
label == "Cadastro" || // <-- Adicionado aqui
label == "Banco de Dados" ||
label == "Financeiro" ||
label == "Ordens de Serviço" ||
label == "Configurações" ||
label == "Suporte Técnico" ||
label == "Agenda"
;
protected override void OnMouseClick(MouseEventArgs e)
{
int hit = HitTest(e.Y);
if (hit >= 0)
{
_activeIndex = hit; Invalidate();
string label = _items[hit].Label;
Point menuPos = new Point(Width + 2, e.Y - (int)(15 * _scale));
if (label == "Banco de Dados") _subMenuBanco.Show(this, menuPos);
else if (label == "Financeiro") _subMenuFinanceiro.Show(this, menuPos);
else if(label == "Agenda") _subMenuAgenda.Show(this, menuPos);
else if (label == "Ordens de Serviço") _subMenuOrdemServico.Show(this, menuPos);
else if (label == "Configurações") _subMenuConfiguracao.Show(this, menuPos);
else if (label == "Suporte Técnico") _subMenuAjuda.Show(this, menuPos);
else if (label == "Cadastro") _subMenuCadastro.Show(this, menuPos);
else NavItemClicked?.Invoke(this, hit);
}
}
private int HitTest(int mouseY)
{
int y = ScaledLogoH + (int)(8 * _scale);
for (int i = 0; i < _items.Count; i++)
{
if (_items[i].Section != null) y += (int)(10 * _scale) + ScaledSectionH;
if (mouseY >= y && mouseY < y + ScaledItemH) return i;
y += ScaledItemH + (int)(2 * _scale);
}
return -1;
}
protected override void OnMouseMove(MouseEventArgs e) { int hit = HitTest(e.Y); if (hit != _hoverIndex) { _hoverIndex = hit; Invalidate(); } }
protected override void OnMouseLeave(EventArgs e) { _hoverIndex = -1; Invalidate(); }
private void DrawLogo(Graphics g)
{
var iconRect = new Rectangle((int)(16 * _scale), (int)(18 * _scale), (int)(34 * _scale), (int)(34 * _scale));
FillRoundRect(g, new SolidBrush(AccentBlue), iconRect, (int)(8 * _scale));
using var fIcon = new Font("Segoe UI", 14f * _scale, FontStyle.Bold);
DrawCenteredString(g, "S", fIcon, TextLight, iconRect);
g.DrawString("LevelOS", new Font("Segoe UI Semibold", 13f * _scale), new SolidBrush(TextLight), 58 * _scale, 20 * _scale);
g.DrawString("SISTEMA ERP", new Font("Segoe UI", 8f * _scale), new SolidBrush(TextMuted), 59 * _scale, 38 * _scale);
g.DrawLine(new Pen(Divider), 0, ScaledLogoH, Width, ScaledLogoH);
}
private void DrawFooter(Graphics g)
{
int fy = Height - ScaledFooterH;
g.DrawLine(new Pen(Divider), 0, fy, Width, fy);
var avRect = new Rectangle((int)(16 * _scale), fy + (int)(14 * _scale), (int)(32 * _scale), (int)(32 * _scale));
g.FillEllipse(new SolidBrush(AccentBlue), avRect);
DrawCenteredString(g, "AD", new Font("Segoe UI Semibold", 10f * _scale), TextLight, avRect);
g.DrawString(UserName, new Font("Segoe UI Semibold", 11f * _scale), new SolidBrush(TextLight), 56 * _scale, fy + (14 * _scale));
g.DrawString(UserFunction, new Font("Segoe UI", 9f * _scale), new SolidBrush(TextMuted), 57 * _scale, fy + (30 * _scale));
}
// ── MOTOR DE ÍCONES COMPLETO (GDI+) ────────────────────────────────
private void DrawIcon(Graphics g, SvgIcon icon, int x, int y, int size, Color color)
{
using var p = new Pen(color, 1.6f * _scale) { StartCap = LineCap.Round, EndCap = LineCap.Round, LineJoin = LineJoin.Round };
float s = size / 24f;
g.TranslateTransform(x, y); g.ScaleTransform(s, s);
switch (icon)
{
case SvgIcon.Grid:
g.DrawRectangle(p, 3, 3, 7, 7); g.DrawRectangle(p, 14, 3, 7, 7); g.DrawRectangle(p, 3, 14, 7, 7); g.DrawRectangle(p, 14, 14, 7, 7); break;
case SvgIcon.Users:
g.DrawEllipse(p, 5, 3, 8, 8); g.DrawArc(p, 2, 13, 14, 8, 180, 180); g.DrawArc(p, 15, 8, 6, 6, 270, 180); break;
case SvgIcon.FileText:
g.DrawLines(p, new PointF[] { new(6, 2), new(6, 22), new(18, 22), new(18, 8), new(14, 2), new(6, 2) }); g.DrawLine(p, 14, 2, 14, 8); g.DrawLine(p, 14, 8, 18, 8); break;
case SvgIcon.Package:
g.DrawLines(p, new PointF[] { new(12, 2), new(2, 7), new(12, 12), new(22, 7), new(12, 2) }); g.DrawLine(p, 2, 7, 2, 17); g.DrawLine(p, 22, 7, 22, 17); g.DrawLine(p, 12, 12, 12, 22); break;
case SvgIcon.DollarSign:
g.DrawLine(p, 12, 2, 12, 22); g.DrawArc(p, 7, 5, 10, 7, 110, 250); g.DrawArc(p, 7, 12, 10, 7, 270, 250); break;
case SvgIcon.Database:
g.DrawEllipse(p, 4, 3, 16, 6); g.DrawLine(p, 4, 6, 4, 18); g.DrawLine(p, 20, 6, 20, 18); g.DrawArc(p, 4, 15, 16, 6, 0, 180); break;
case SvgIcon.Calendar:
g.DrawRectangle(p, 3, 4, 18, 17); g.DrawLine(p, 3, 9, 21, 9); g.DrawLine(p, 8, 2, 8, 6); g.DrawLine(p, 16, 2, 16, 6); break;
case SvgIcon.Truck:
g.DrawRectangle(p, 1, 5, 13, 11); g.DrawLines(p, new PointF[] { new(14, 16), new(14, 8), new(19, 8), new(23, 12), new(23, 16) });
g.DrawEllipse(p, 3, 15, 4, 4); g.DrawEllipse(p, 17, 15, 4, 4); break;
case SvgIcon.Factory:
g.DrawRectangle(p, 2, 10, 20, 11); g.DrawLines(p, new PointF[] { new(2, 10), new(2, 5), new(8, 10), new(8, 5), new(14, 10) }); break;
case SvgIcon.Briefcase:
g.DrawRectangle(p, 3, 7, 18, 13); g.DrawArc(p, 9, 3, 6, 8, 180, 180); break;
case SvgIcon.Support:
g.DrawArc(p, 4, 4, 16, 16, 180, 180); g.DrawRectangle(p, 3, 13, 3, 5); g.DrawRectangle(p, 18, 13, 3, 5); break;
case SvgIcon.Inventory:
g.DrawRectangle(p, 3, 3, 18, 8); g.DrawRectangle(p, 3, 13, 18, 8); break;
case SvgIcon.Settings:
g.DrawEllipse(p, 9, 9, 6, 6);
for (int a = 0; a < 360; a += 45)
{
float r = a * (float)Math.PI / 180f;
g.DrawLine(p, 12 + (float)Math.Cos(r) * 8, 12 + (float)Math.Sin(r) * 8, 12 + (float)Math.Cos(r) * 11, 12 + (float)Math.Sin(r) * 11);
}
break;
case SvgIcon.User: g.DrawEllipse(p, 8, 2, 8, 8); g.DrawArc(p, 3, 14, 18, 8, 180, 180); break;
case SvgIcon.UserCheck: g.DrawEllipse(p, 8, 3, 8, 8); g.DrawArc(p, 3, 14, 18, 8, 180, 180); g.DrawRectangle(p, 10, 15, 4, 3); break;
case SvgIcon.UserPlus:
// Desenha o corpo do usuário (círculo e arco)
g.DrawEllipse(p, 4, 4, 8, 8); // Cabeça
g.DrawArc(p, 1, 14, 14, 8, 180, 180); // Ombros
// Desenha o sinal de "+" ao lado
// Vertical
g.DrawLine(p, 19, 7, 19, 15);
// Horizontal
g.DrawLine(p, 15, 11, 23, 11);
break;
}
g.ResetTransform();
}
// ── Helpers ──
private static void FillRoundRect(Graphics g, Brush b, Rectangle r, int rad) { using var path = RoundRectPath(r, rad); g.FillPath(b, path); }
private static GraphicsPath RoundRectPath(Rectangle r, int rad)
{
var path = new GraphicsPath(); int d = Math.Max(rad * 2, 1);
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;
}
private static void DrawCenteredString(Graphics g, string t, Font f, Color c, Rectangle r)
{
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
g.DrawString(t, f, new SolidBrush(c), r, sf);
}
private class NavItem
{
public string Label { get; }
public SvgIcon Icon { get; }
public string? Section { get; }
public string? Badge { get; }
public NavItem(string l, SvgIcon i, string? s, string? b) { Label = l; Icon = i; Section = s; Badge = b; }
}
private class SubMenuColorTable : ProfessionalColorTable
{
public override Color ToolStripDropDownBackground => NavyMid;
public override Color MenuItemSelected => AccentBlue;
public override Color MenuItemBorder => Color.Transparent;
public override Color MenuItemSelectedGradientBegin => AccentBlue;
public override Color MenuItemSelectedGradientEnd => AccentBlue;
}
}
public enum SvgIcon { Grid, Users, FileText, Package, DollarSign, User, Settings, Database, Truck, Factory, UserCheck, Briefcase, Support, Inventory, Wallet, ShoppingCart, ShoppingBag, Calendar , UserPlus}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

132
Dashboards/Main/Dash_main.Designer.cs generated Normal file
View File

@ -0,0 +1,132 @@
namespace UI
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
lbltest = new Label();
lV_button1 = new CPM.LV_BUTTON();
lV_button2 = new CPM.LV_BUTTON();
lV_button3 = new CPM.LV_BUTTON();
SuspendLayout();
//
// lbltest
//
lbltest.AutoSize = true;
lbltest.Location = new Point(12, 9);
lbltest.Name = "lbltest";
lbltest.Size = new Size(38, 15);
lbltest.TabIndex = 0;
lbltest.Text = "label1";
//
// lV_button1
//
lV_button1.BackColor = Color.MediumSlateBlue;
lV_button1.BackgroundColor = Color.MediumSlateBlue;
lV_button1.BorderColor = Color.PaleVioletRed;
lV_button1.BorderRadius = 0;
lV_button1.BorderSize = 0;
lV_button1.ClickColor = Color.DarkBlue;
lV_button1.FlatAppearance.BorderSize = 0;
lV_button1.FlatStyle = FlatStyle.Flat;
lV_button1.ForeColor = Color.White;
lV_button1.HoverColor = Color.LightBlue;
lV_button1.Location = new Point(734, 12);
lV_button1.Name = "lV_button1";
lV_button1.Size = new Size(148, 32);
lV_button1.TabIndex = 1;
lV_button1.Text = "FrmConfigDB";
lV_button1.TextColor = Color.White;
lV_button1.UseVisualStyleBackColor = false;
lV_button1.Click += lV_button1_Click;
//
// lV_button2
//
lV_button2.BackColor = Color.MediumSlateBlue;
lV_button2.BackgroundColor = Color.MediumSlateBlue;
lV_button2.BorderColor = Color.PaleVioletRed;
lV_button2.BorderRadius = 0;
lV_button2.BorderSize = 0;
lV_button2.ClickColor = Color.DarkBlue;
lV_button2.FlatAppearance.BorderSize = 0;
lV_button2.FlatStyle = FlatStyle.Flat;
lV_button2.ForeColor = Color.White;
lV_button2.HoverColor = Color.LightBlue;
lV_button2.Location = new Point(734, 50);
lV_button2.Name = "lV_button2";
lV_button2.Size = new Size(148, 32);
lV_button2.TabIndex = 2;
lV_button2.Text = "Dashmain";
lV_button2.TextColor = Color.White;
lV_button2.UseVisualStyleBackColor = false;
lV_button2.Click += lV_button2_Click;
//
// lV_button3
//
lV_button3.BackColor = Color.MediumSlateBlue;
lV_button3.BackgroundColor = Color.MediumSlateBlue;
lV_button3.BorderColor = Color.PaleVioletRed;
lV_button3.BorderRadius = 0;
lV_button3.BorderSize = 0;
lV_button3.ClickColor = Color.DarkBlue;
lV_button3.FlatAppearance.BorderSize = 0;
lV_button3.FlatStyle = FlatStyle.Flat;
lV_button3.ForeColor = Color.White;
lV_button3.HoverColor = Color.LightBlue;
lV_button3.Location = new Point(734, 88);
lV_button3.Name = "lV_button3";
lV_button3.Size = new Size(148, 32);
lV_button3.TabIndex = 3;
lV_button3.Text = "Arquivo Encryptado";
lV_button3.TextColor = Color.White;
lV_button3.UseVisualStyleBackColor = false;
lV_button3.Click += lV_button3_Click;
//
// Form1
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(894, 474);
Controls.Add(lV_button3);
Controls.Add(lV_button2);
Controls.Add(lV_button1);
Controls.Add(lbltest);
Name = "Form1";
Text = "Form1";
Load += Form1_Load;
ResumeLayout(false);
PerformLayout();
}
#endregion
private Label lbltest;
private CPM.LV_BUTTON lV_button1;
private CPM.LV_BUTTON lV_button2;
private CPM.LV_BUTTON lV_button3;
}
}

View File

@ -0,0 +1,75 @@
using CAB;
using CCH;
using CPM;
using CPT;
using CustomMessageBox;
using DAL;
using TLL;
using static CPT.SecurityManager;
namespace UI
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string caminhoIcone = AppFileSystem.AppFileIconSystem;
if (File.Exists(caminhoIcone))
{
this.Icon = new Icon(caminhoIcone);
}
}
private void Form1_Load(object sender, EventArgs e)
{
DadosDaConexao.Host = "206.42.13.180";
DadosDaConexao.Port = 1433;
DadosDaConexao.Banco = "Levelcode-LevelOS";
DadosDaConexao.Usuario = "nicolas";
DadosDaConexao.Senha = "Nike12122020*##";
DadosDaConexao.ConnectTimeout = 1000;
DadosDaConexao.Encrypt = false;
DadosDaConexao.TrustServerCertificate = false;
DatabaseHelper.Salvar(AppFileSystem.AppFileDBSystem, AppInfoSystem.AppKeyMasterCrip);
DatabaseHelper.Carregar(AppFileSystem.AppFileDBSystem, AppInfoSystem.AppKeyMasterCrip);
this.lbltest.Text = DadosDaConexao.Host;
if (DadosDaConexao.TestarConexao())
{
NT_MessageBox.Show("Conexão com o banco de dados concluida com sucesso!", "Teste de conexão");
}
}
private void lV_button1_Click(object sender, EventArgs e)
{
FrmConfigBanco f = new FrmConfigBanco();
f.ShowDialog();
}
private void lV_button2_Click(object sender, EventArgs e)
{
MainForm main = new MainForm();
main.ShowDialog();
}
private void lV_button3_Click(object sender, EventArgs e)
{
string caminhoKey = @"C:\Levelcode\LevelOS\Config\config.json";
DadosDaConexao.Host = "206.42.13.180";
DadosDaConexao.Port = 1433;
DadosDaConexao.Banco = "Levelcode-LevelOS";
DadosDaConexao.Usuario = "nicolas";
DadosDaConexao.Senha = "Nike12122020*##";
DadosDaConexao.ConnectTimeout = 1000;
DadosDaConexao.Encrypt = false;
DadosDaConexao.TrustServerCertificate = false;
DatabaseHelperCPT.Salvar(caminhoKey);
string conectionString = DatabaseHelperCPT.Carregar(caminhoKey);
NT_MessageBox.Show(conectionString);
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

67
Documentohelper.cs Normal file
View File

@ -0,0 +1,67 @@
using System.Windows.Forms;
namespace UI
{
/// <summary>
/// Formata CPF ou CNPJ automaticamente enquanto o usuário digita.
/// Uso: DocumentoHelper.Registrar(txtDocumento);
/// </summary>
public static class DocumentoHelper
{
public static void Registrar(RoundTextBox txt)
{
bool formatando = false;
txt.TextChanged += (_, _) =>
{
// Evita loop: TextChanged → altera Text → TextChanged → ...
if (formatando) return;
formatando = true;
try
{
// 1. Extrai só os dígitos
string digits = new string(txt.Text.Where(char.IsDigit).ToArray());
// 2. Limita ao tamanho máximo do CNPJ
if (digits.Length > 14) digits = digits[..14];
// 3. Formata conforme o tamanho
string formatado = digits.Length <= 11
? FormatarCPF(digits)
: FormatarCNPJ(digits);
// 4. Só atualiza se realmente mudou
if (txt.Text != formatado)
{
txt.Text = formatado;
txt.SelectionStart = formatado.Length; // cursor no final
}
}
finally
{
formatando = false;
}
};
}
// CPF: 000.000.000-00
private static string FormatarCPF(string d) => d.Length switch
{
<= 3 => d,
<= 6 => $"{d[..3]}.{d[3..]}",
<= 9 => $"{d[..3]}.{d[3..6]}.{d[6..]}",
_ => $"{d[..3]}.{d[3..6]}.{d[6..9]}-{d[9..]}"
};
// CNPJ: 00.000.000/0000-00
private static string FormatarCNPJ(string d) => d.Length switch
{
<= 2 => d,
<= 5 => $"{d[..2]}.{d[2..]}",
<= 8 => $"{d[..2]}.{d[2..5]}.{d[5..]}",
<= 12 => $"{d[..2]}.{d[2..5]}.{d[5..8]}/{d[8..]}",
_ => $"{d[..2]}.{d[2..5]}.{d[5..8]}/{d[8..12]}-{d[12..]}"
};
}
}

20
Program.cs Normal file
View File

@ -0,0 +1,20 @@
using CCH;
namespace UI
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
AppFolderSystem.CriarEstruturaPastas();
Application.Run(new Form1());
}
}
}

125
RoundComboBox.cs Normal file
View File

@ -0,0 +1,125 @@
using System.Drawing.Drawing2D;
namespace UI
{
public class RoundComboBox : UserControl
{
private ComboBox _comboBox = 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 ComboBoxStyle DropDownStyle
{
get => _comboBox.DropDownStyle;
set => _comboBox.DropDownStyle = value;
}
public ComboBox.ObjectCollection Items => _comboBox.Items;
public object? SelectedItem
{
get => _comboBox.SelectedItem;
set => _comboBox.SelectedItem = value;
}
public int SelectedIndex
{
get => _comboBox.SelectedIndex;
set => _comboBox.SelectedIndex = value;
}
public override string Text
{
get => _comboBox.Text;
set => _comboBox.Text = value;
}
public new Color BackColor
{
get => base.BackColor;
set { base.BackColor = value; if (_comboBox != null) _comboBox.BackColor = value; }
}
public new event EventHandler? SelectedIndexChanged
{
add => _comboBox.SelectedIndexChanged += value;
remove => _comboBox.SelectedIndexChanged -= value;
}
public RoundComboBox()
{
DoubleBuffered = true;
base.BackColor = Color.White;
_comboBox = new ComboBox
{
//BorderStyle = BorderStyle.None,
Font = new Font("Segoe UI", 9f),
BackColor = Color.White,
DropDownStyle = ComboBoxStyle.DropDownList,
FlatStyle = FlatStyle.Flat
};
_comboBox.GotFocus += (s, e) => { _focused = true; Invalidate(); };
_comboBox.LostFocus += (s, e) => { _focused = false; Invalidate(); };
_comboBox.TextChanged += (s, e) => OnTextChanged(e);
_comboBox.Leave += (s, e) => OnLeave(e);
Controls.Add(_comboBox);
SizeChanged += (s, e) => AjustarComboBox();
}
private void AjustarComboBox()
{
_comboBox.Width = Width - 12;
_comboBox.Location = new Point(6, (Height - _comboBox.PreferredHeight) / 2);
}
public object? DataSource
{
get => _comboBox.DataSource;
set => _comboBox.DataSource = value;
}
public string DisplayMember
{
get => _comboBox.DisplayMember;
set => _comboBox.DisplayMember = value;
}
public string ValueMember
{
get => _comboBox.ValueMember;
set => _comboBox.ValueMember = value;
}
public object? SelectedValue
{
get => _comboBox.SelectedValue;
set => _comboBox.SelectedValue = value;
}
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;
}
}
}

28
UI.csproj Normal file
View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BLL\BLL.csproj" />
<ProjectReference Include="..\CAB\CAB.csproj" />
<ProjectReference Include="..\CCH\CCH.csproj" />
<ProjectReference Include="..\CMB\CMB.csproj" />
<ProjectReference Include="..\CPM\CPM.csproj" />
<ProjectReference Include="..\CPT\CPT.csproj" />
<ProjectReference Include="..\DAL\DAL.csproj" />
<ProjectReference Include="..\MLL\MLL.csproj" />
<ProjectReference Include="..\TLL\TLL.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AForge.Video" Version="2.2.5" />
<PackageReference Include="AForge.Video.DirectShow" Version="2.2.5" />
</ItemGroup>
</Project>

79
UI.sln Normal file
View File

@ -0,0 +1,79 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.4.11626.88
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UI", "UI.csproj", "{344A1172-B978-A3CB-44BF-B15E6902C6C7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLL", "..\MLL\MLL.csproj", "{BD112D2C-C31D-4DC4-A3C8-2C5808C17D53}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CCH", "..\CCH\CCH.csproj", "{AD6AD8F1-7AE1-4BFC-A7EB-5F5B37EFE4C0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TLL", "..\TLL\TLL.csproj", "{A5F3E468-7A97-4C71-8E75-CD2CAF8B477C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPT", "..\CPT\CPT.csproj", "{970FFDEE-6F66-49FD-B41F-EBC8F50C547C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DAL", "..\DAL\DAL.csproj", "{9696C45A-97A2-445E-BE10-BB3EE320BFDF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BLL", "..\BLL\BLL.csproj", "{68B73E29-9E25-4BCA-B077-400F6B2B450B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPM", "..\CPM\CPM.csproj", "{20B46C82-119A-F5E9-8B57-A756A827F265}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CAB", "..\CAB\CAB.csproj", "{19039AC1-EE08-460B-821B-0277442D04D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CMB", "..\CMB\CMB.csproj", "{766E76F2-BAFF-4352-B376-15F6DE8BE4D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{344A1172-B978-A3CB-44BF-B15E6902C6C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{344A1172-B978-A3CB-44BF-B15E6902C6C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{344A1172-B978-A3CB-44BF-B15E6902C6C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{344A1172-B978-A3CB-44BF-B15E6902C6C7}.Release|Any CPU.Build.0 = Release|Any CPU
{BD112D2C-C31D-4DC4-A3C8-2C5808C17D53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD112D2C-C31D-4DC4-A3C8-2C5808C17D53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD112D2C-C31D-4DC4-A3C8-2C5808C17D53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD112D2C-C31D-4DC4-A3C8-2C5808C17D53}.Release|Any CPU.Build.0 = Release|Any CPU
{AD6AD8F1-7AE1-4BFC-A7EB-5F5B37EFE4C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD6AD8F1-7AE1-4BFC-A7EB-5F5B37EFE4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD6AD8F1-7AE1-4BFC-A7EB-5F5B37EFE4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD6AD8F1-7AE1-4BFC-A7EB-5F5B37EFE4C0}.Release|Any CPU.Build.0 = Release|Any CPU
{A5F3E468-7A97-4C71-8E75-CD2CAF8B477C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5F3E468-7A97-4C71-8E75-CD2CAF8B477C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5F3E468-7A97-4C71-8E75-CD2CAF8B477C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5F3E468-7A97-4C71-8E75-CD2CAF8B477C}.Release|Any CPU.Build.0 = Release|Any CPU
{970FFDEE-6F66-49FD-B41F-EBC8F50C547C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{970FFDEE-6F66-49FD-B41F-EBC8F50C547C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{970FFDEE-6F66-49FD-B41F-EBC8F50C547C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{970FFDEE-6F66-49FD-B41F-EBC8F50C547C}.Release|Any CPU.Build.0 = Release|Any CPU
{9696C45A-97A2-445E-BE10-BB3EE320BFDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9696C45A-97A2-445E-BE10-BB3EE320BFDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9696C45A-97A2-445E-BE10-BB3EE320BFDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9696C45A-97A2-445E-BE10-BB3EE320BFDF}.Release|Any CPU.Build.0 = Release|Any CPU
{68B73E29-9E25-4BCA-B077-400F6B2B450B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68B73E29-9E25-4BCA-B077-400F6B2B450B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68B73E29-9E25-4BCA-B077-400F6B2B450B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68B73E29-9E25-4BCA-B077-400F6B2B450B}.Release|Any CPU.Build.0 = Release|Any CPU
{20B46C82-119A-F5E9-8B57-A756A827F265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20B46C82-119A-F5E9-8B57-A756A827F265}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20B46C82-119A-F5E9-8B57-A756A827F265}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20B46C82-119A-F5E9-8B57-A756A827F265}.Release|Any CPU.Build.0 = Release|Any CPU
{19039AC1-EE08-460B-821B-0277442D04D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{19039AC1-EE08-460B-821B-0277442D04D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19039AC1-EE08-460B-821B-0277442D04D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19039AC1-EE08-460B-821B-0277442D04D1}.Release|Any CPU.Build.0 = Release|Any CPU
{766E76F2-BAFF-4352-B376-15F6DE8BE4D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{766E76F2-BAFF-4352-B376-15F6DE8BE4D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{766E76F2-BAFF-4352-B376-15F6DE8BE4D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{766E76F2-BAFF-4352-B376-15F6DE8BE4D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {15E7E724-DCEE-49C0-90C5-A55F1E2F06BD}
EndGlobalSection
EndGlobal