26/04/2026 - Implementando novos recursos.

This commit is contained in:
levelcode developed 2026-04-26 02:01:56 -03:00
parent b7a285004e
commit 4b8a2acdd4
26 changed files with 2285 additions and 32 deletions

67
MLL/ModeloBackup.cs Normal file
View File

@ -0,0 +1,67 @@
using System;
namespace MLL
{
public class ModeloBackup
{
public ModeloBackup()
{
}
public ModeloBackup(int id, string nome, string tipoBackup, string origem, string destinoTipo, int? destinoId, string caminhoDestino, bool compactar, bool usarCriptografia, string algoritmoCriptografia, string frequencia, TimeSpan? horario, int manterUltimos, string statusUltimoBackup, DateTime? dataUltimoBackup, string logUltimoBackup, bool ativo, DateTime criadoEm, DateTime? atualizadoEm)
{
Id = id;
Nome = nome;
TipoBackup = tipoBackup;
Origem = origem;
DestinoTipo = destinoTipo;
DestinoId = destinoId;
CaminhoDestino = caminhoDestino;
Compactar = compactar;
UsarCriptografia = usarCriptografia;
AlgoritmoCriptografia = algoritmoCriptografia;
Frequencia = frequencia;
Horario = horario;
ManterUltimos = manterUltimos;
StatusUltimoBackup = statusUltimoBackup;
DataUltimoBackup = dataUltimoBackup;
LogUltimoBackup = logUltimoBackup;
Ativo = ativo;
CriadoEm = criadoEm;
AtualizadoEm = atualizadoEm;
}
public int Id { get; set; }
public string Nome { get; set; }
public string TipoBackup { get; set; }
public string Origem { get; set; }
public string DestinoTipo { get; set; }
public int? DestinoId { get; set; }
public string CaminhoDestino { get; set; }
public bool Compactar { get; set; }
public bool UsarCriptografia { get; set; }
public string AlgoritmoCriptografia { get; set; }
public string Frequencia { get; set; }
public TimeSpan? Horario { get; set; }
public int ManterUltimos { get; set; }
public string StatusUltimoBackup { get; set; }
public DateTime? DataUltimoBackup { get; set; }
public string LogUltimoBackup { get; set; }
public bool Ativo { get; set; }
public DateTime CriadoEm { get; set; }
public DateTime? AtualizadoEm { get; set; }
}
}

View File

@ -0,0 +1,50 @@
using System;
namespace MLL
{
public class ModeloBackupAutomatico
{
public ModeloBackupAutomatico(int id, int idBackup, bool ativo, string frequencia, int? intervaloMinutos, TimeSpan? horario, int? diaSemana, int? diaMes, DateTime? ultimaExecucao, DateTime? proximaExecucao, bool executando, DateTime criadoEm, DateTime? atualizadoEm)
{
Id = id;
IdBackup = idBackup;
Ativo = ativo;
Frequencia = frequencia;
IntervaloMinutos = intervaloMinutos;
Horario = horario;
DiaSemana = diaSemana;
DiaMes = diaMes;
UltimaExecucao = ultimaExecucao;
ProximaExecucao = proximaExecucao;
Executando = executando;
CriadoEm = criadoEm;
AtualizadoEm = atualizadoEm;
}
public ModeloBackupAutomatico()
{
}
public int Id { get; set; }
public int IdBackup { get; set; }
public bool Ativo { get; set; }
public string Frequencia { get; set; }
public int? IntervaloMinutos { get; set; }
public TimeSpan? Horario { get; set; }
public int? DiaSemana { get; set; }
public int? DiaMes { get; set; }
public DateTime? UltimaExecucao { get; set; }
public DateTime? ProximaExecucao { get; set; }
public bool Executando { get; set; }
public DateTime CriadoEm { get; set; }
public DateTime? AtualizadoEm { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using System;
namespace MLL
{
public class ModeloBackupExecucao
{
public ModeloBackupExecucao(int id, int idBackup, DateTime dataExecucao, string status, string mensagem, long? tamanhoArquivo, int? duracaoSegundos)
{
Id = id;
IdBackup = idBackup;
DataExecucao = dataExecucao;
Status = status;
Mensagem = mensagem;
TamanhoArquivo = tamanhoArquivo;
DuracaoSegundos = duracaoSegundos;
}
public ModeloBackupExecucao()
{
}
public int Id { get; set; }
public int IdBackup { get; set; }
public DateTime DataExecucao { get; set; }
public string Status { get; set; }
public string Mensagem { get; set; }
public long? TamanhoArquivo { get; set; }
public int? DuracaoSegundos { get; set; }
}
}

74
MLL/ModeloCloudStorage.cs Normal file
View File

@ -0,0 +1,74 @@
using System;
namespace MLL
{
public class ModeloCloudStorage
{
public int Id { get; set; }
public string Nome { get; set; }
public string Tipo { get; set; } // 'gdrive', 'onedrive', 'webdav'
// OAuth
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public DateTime? TokenExpiraEm { get; set; }
// WebDAV
public string Url { get; set; }
public string Usuario { get; set; }
public string Senha { get; set; }
// Geral
public string PastaBase { get; set; }
public bool Ativo { get; set; }
public DateTime CriadoEm { get; set; }
public DateTime? AtualizadoEm { get; set; }
// =========================
// 🏗️ CONSTRUTORES
// =========================
// 🔹 Construtor vazio (ORM / DAL)
public ModeloCloudStorage()
{
Ativo = true;
CriadoEm = DateTime.Now;
}
// 🔹 Construtor com parâmetros principais
public ModeloCloudStorage(
string nome,
string tipo,
string pastaBase = null,
string clientId = null,
string clientSecret = null,
string accessToken = null,
string refreshToken = null,
DateTime? tokenExpiraEm = null,
string url = null,
string usuario = null,
string senha = null
) : this()
{
Nome = nome;
Tipo = tipo;
PastaBase = pastaBase;
ClientId = clientId;
ClientSecret = clientSecret;
AccessToken = accessToken;
RefreshToken = refreshToken;
TokenExpiraEm = tokenExpiraEm;
Url = url;
Usuario = usuario;
Senha = senha;
}
}
}

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace MLL
{
internal class ModeloSshCliente
public class ModeloSshCliente
{
public ModeloSshCliente()
{

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MLL
{
public class ModeloTelegramCliente
{
public ModeloTelegramCliente()
{
Id = 0;
Nome = "";
Tipo = "";
BotToken = "";
ApiId = 0;
ApiHash = "";
Telefone = "";
SessionString = "";
ChatId = "";
Ativo = false;
CriadoEm = DateTime.MinValue;
AtualizadoEm = DateTime.MinValue;
}
public ModeloTelegramCliente(int id, string nome, string tipo, string botToken, int? apiId, string apiHash, string telefone, string sessionString, string chatId, bool ativo, DateTime criadoEm, DateTime? atualizadoEm)
{
Id = id;
Nome = nome;
Tipo = tipo;
BotToken = botToken;
ApiId = apiId;
ApiHash = apiHash;
Telefone = telefone;
SessionString = sessionString;
ChatId = chatId;
Ativo = ativo;
CriadoEm = criadoEm;
AtualizadoEm = atualizadoEm;
}
public int Id { get; set; }
public string Nome { get; set; }
public string Tipo { get; set; }
public string BotToken { get; set; }
public int? ApiId { get; set; }
public string ApiHash { get; set; }
public string Telefone { get; set; }
public string SessionString { get; set; }
public string ChatId { get; set; }
public bool Ativo { get; set; }
public DateTime CriadoEm { get; set; }
public DateTime? AtualizadoEm { get; set; }
}
}

View File

@ -0,0 +1,115 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class BackupAutomaticoCadastroPanel : FormularioModelo
{
private ModeloBackupAutomatico _auto = new ModeloBackupAutomatico();
// Controles - Vínculo e Status
private LV_TEXTBOX1 txtId, txtIdBackup, txtNomeBackup;
private CheckBox chkAtivo, chkExecutando;
// Controles - Recorrência
private ComboBox cbFrequencia, cbDiaSemana;
private NumericUpDown numIntervalo, numDiaMes;
private DateTimePicker dtpHorario;
// Controles - Monitoramento de Tempo
private LV_TEXTBOX1 txtUltimaExec, txtProximaExec;
public BackupAutomaticoCadastroPanel()
{
this.Titulo = "Agendamento Inteligente de Backup";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação do Job ---
content.Controls.Add(CreateSectionHeader("VÍNCULO DA TAREFA", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtIdBackup = AddInput(content, "ID BACKUP", 100, 50, 100, 30);
txtNomeBackup = AddInput(content, "ROTINA ASSOCIADA", 210, 50, 350, 30, true);
chkAtivo = new CheckBox { Text = "AGENDAMENTO ATIVO", Location = new Point(580, 66), AutoSize = true, Font = new Font("Segoe UI", 9, FontStyle.Bold) };
chkExecutando = new CheckBox { Text = "EM EXECUÇÃO AGORA", Location = new Point(735, 66), AutoSize = true, Enabled = false };
content.Controls.Add(chkAtivo);
content.Controls.Add(chkExecutando);
// --- SEÇÃO 2: Regras de Frequência ---
content.Controls.Add(CreateSectionHeader("PERIODICIDADE E REGRAS", 115));
Label lblFreq = new Label { Text = "FREQUÊNCIA", Location = new Point(20, 143), AutoSize = true };
cbFrequencia = new ComboBox { Location = new Point(20, 160), Size = new Size(150, 30), DropDownStyle = ComboBoxStyle.DropDownList };
cbFrequencia.Items.AddRange(new object[] { "INTERVALO", "DIARIO", "SEMANAL", "MENSAL" });
Label lblInt = new Label { Text = "INTERVALO (MIN)", Location = new Point(180, 143), AutoSize = true };
numIntervalo = new NumericUpDown { Location = new Point(180, 160), Size = new Size(100, 30), Maximum = 1440 };
Label lblHora = new Label { Text = "HORÁRIO FIXO", Location = new Point(290, 143), AutoSize = true };
dtpHorario = new DateTimePicker { Location = new Point(290, 160), Size = new Size(100, 30), Format = DateTimePickerFormat.Time, ShowUpDown = true };
Label lblSemana = new Label { Text = "DIA DA SEMANA", Location = new Point(400, 143), AutoSize = true };
cbDiaSemana = new ComboBox { Location = new Point(400, 160), Size = new Size(130, 30), DropDownStyle = ComboBoxStyle.DropDownList };
cbDiaSemana.Items.AddRange(new object[] { "Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado" });
Label lblDiaMes = new Label { Text = "DIA DO MÊS", Location = new Point(540, 143), AutoSize = true };
numDiaMes = new NumericUpDown { Location = new Point(540, 160), Size = new Size(80, 30), Minimum = 1, Maximum = 31 };
content.Controls.Add(lblFreq); content.Controls.Add(cbFrequencia);
content.Controls.Add(lblInt); content.Controls.Add(numIntervalo);
content.Controls.Add(lblHora); content.Controls.Add(dtpHorario);
content.Controls.Add(lblSemana); content.Controls.Add(cbDiaSemana);
content.Controls.Add(lblDiaMes); content.Controls.Add(numDiaMes);
// --- SEÇÃO 3: Linha do Tempo ---
content.Controls.Add(CreateSectionHeader("PRÓXIMAS ETAPAS", 220));
txtUltimaExec = AddInput(content, "ÚLTIMA VEZ QUE RODOU", 20, 250, 250, 30, true);
txtProximaExec = AddInput(content, "AGENDADO PARA", 280, 250, 250, 30, true);
txtProximaExec.BackColor = Color.LightYellow;
content.Height = 350;
// Lógica para habilitar/desabilitar campos baseado na frequência
cbFrequencia.SelectedIndexChanged += (s, e) => ActualizarInterfaceFrequencia();
}
private void ActualizarInterfaceFrequencia()
{
string freq = cbFrequencia.Text;
numIntervalo.Enabled = (freq == "INTERVALO");
dtpHorario.Enabled = (freq != "INTERVALO");
cbDiaSemana.Enabled = (freq == "SEMANAL");
numDiaMes.Enabled = (freq == "MENSAL");
}
private void PreencherModel()
{
_auto.IdBackup = int.TryParse(txtIdBackup.Text, out int idB) ? idB : 0;
_auto.Ativo = chkAtivo.Checked;
_auto.Frequencia = cbFrequencia.Text;
_auto.IntervaloMinutos = (int)numIntervalo.Value;
_auto.Horario = dtpHorario.Value.TimeOfDay;
_auto.DiaSemana = cbDiaSemana.SelectedIndex;
_auto.DiaMes = (int)numDiaMes.Value;
}
protected override void OnNovo() { _auto = new ModeloBackupAutomatico(); cbFrequencia.SelectedIndex = 1; }
protected override void OnSalvar() { PreencherModel(); MessageBox.Show("Agendamento automático configurado!", "LevelOS Scheduler"); }
protected override void OnCancelar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnAlterar() { }
}
}

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,119 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class BackupCadastroPanel : FormularioModelo
{
private ModeloBackup _backup = new ModeloBackup();
// Controles - Configuração Geral
private LV_TEXTBOX1 txtId, txtNome, txtOrigem, txtCaminhoDestino;
private ComboBox cbTipoBackup, cbDestinoTipo, cbFrequencia;
// Controles - Segurança e Compactação
private CheckBox chkCompactar, chkCriptografia, chkAtivo;
private LV_TEXTBOX1 txtAlgoritmo, txtManterUltimos;
// Controles - Agendamento
private DateTimePicker dtpHorario;
// Controles - Status e Logs (Somente Leitura)
private LV_TEXTBOX1 txtStatusUltimo, txtDataUltimo, txtLogUltimo;
public BackupCadastroPanel()
{
this.Titulo = "Configuração e Agendamento de Backup";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Definição do Backup ---
content.Controls.Add(CreateSectionHeader("O QUE SERÁ SALVO?", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA ROTINA (EX: BACKUP DIÁRIO DB)", 100, 50, 300, 30);
Label lblTipo = new Label { Text = "TIPO", Location = new Point(410, 33), AutoSize = true };
cbTipoBackup = new ComboBox { Location = new Point(410, 50), Size = new Size(130, 30), DropDownStyle = ComboBoxStyle.DropDownList };
cbTipoBackup.Items.AddRange(new object[] { "database", "arquivos", "completo" });
txtOrigem = AddInput(content, "ORIGEM (NOME DO BANCO OU DIRETÓRIO)", 20, 105, 520, 30);
chkAtivo = new CheckBox { Text = "ATIVO", Location = new Point(560, 121), AutoSize = true, Checked = true };
content.Controls.Add(lblTipo);
content.Controls.Add(cbTipoBackup);
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: Destino e Frequência ---
content.Controls.Add(CreateSectionHeader("ONDE E QUANDO?", 165));
Label lblDest = new Label { Text = "DESTINO", Location = new Point(20, 178), AutoSize = true };
cbDestinoTipo = new ComboBox { Location = new Point(20, 195), Size = new Size(120, 30), DropDownStyle = ComboBoxStyle.DropDownList };
cbDestinoTipo.Items.AddRange(new object[] { "local", "ftp", "ssh", "nuvem" });
txtCaminhoDestino = AddInput(content, "CAMINHO DE DESTINO / PASTA REMOTA", 150, 195, 390, 30);
Label lblFreq = new Label { Text = "FREQUÊNCIA", Location = new Point(20, 233), AutoSize = true };
cbFrequencia = new ComboBox { Location = new Point(20, 250), Size = new Size(120, 30), DropDownStyle = ComboBoxStyle.DropDownList };
cbFrequencia.Items.AddRange(new object[] { "manual", "diario", "semanal", "mensal" });
Label lblHora = new Label { Text = "HORÁRIO", Location = new Point(150, 233), AutoSize = true };
dtpHorario = new DateTimePicker { Location = new Point(150, 250), Size = new Size(100, 30), Format = DateTimePickerFormat.Time, ShowUpDown = true };
txtManterUltimos = AddInput(content, "RETENÇÃO (QTD)", 270, 250, 100, 30);
content.Controls.Add(lblDest);
content.Controls.Add(cbDestinoTipo);
content.Controls.Add(lblFreq);
content.Controls.Add(cbFrequencia);
content.Controls.Add(lblHora);
content.Controls.Add(dtpHorario);
// --- SEÇÃO 3: Segurança ---
content.Controls.Add(CreateSectionHeader("OPÇÕES DE ARQUIVO", 305));
chkCompactar = new CheckBox { Text = "COMPACTAR (.ZIP)", Location = new Point(20, 335), AutoSize = true };
chkCriptografia = new CheckBox { Text = "CRIPTOGRAFAR", Location = new Point(160, 335), AutoSize = true };
txtAlgoritmo = AddInput(content, "ALGORITMO", 300, 325, 150, 30);
content.Controls.Add(chkCompactar);
content.Controls.Add(chkCriptografia);
// --- SEÇÃO 4: Última Execução (Monitoramento) ---
content.Controls.Add(CreateSectionHeader("MONITORAMENTO", 380));
txtStatusUltimo = AddInput(content, "STATUS", 20, 410, 120, 30, true);
txtDataUltimo = AddInput(content, "DATA ÚLT. EXECUÇÃO", 150, 410, 180, 30, true);
txtLogUltimo = AddInput(content, "LOG / MENSAGEM", 340, 410, 495, 30, true);
content.Height = 480;
}
private void PreencherModel()
{
_backup.Nome = txtNome.Text;
_backup.TipoBackup = cbTipoBackup.Text;
_backup.Origem = txtOrigem.Text;
_backup.DestinoTipo = cbDestinoTipo.Text;
_backup.CaminhoDestino = txtCaminhoDestino.Text;
_backup.Compactar = chkCompactar.Checked;
_backup.UsarCriptografia = chkCriptografia.Checked;
_backup.AlgoritmoCriptografia = txtAlgoritmo.Text;
_backup.Frequencia = cbFrequencia.Text;
_backup.Horario = dtpHorario.Value.TimeOfDay;
_backup.ManterUltimos = int.TryParse(txtManterUltimos.Text, out int m) ? m : 5;
_backup.Ativo = chkAtivo.Checked;
}
protected override void OnNovo() { _backup = new ModeloBackup(); cbTipoBackup.SelectedIndex = 0; }
protected override void OnSalvar() { PreencherModel(); MessageBox.Show("Rotina de backup agendada!", "LevelOS Security"); }
protected override void OnCancelar() { }
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar(){ }
}
}

View File

@ -0,0 +1,136 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class CloudStorageCadastroPanel : FormularioModelo
{
private ModeloCloudStorage _cloud = new ModeloCloudStorage();
private LV_TEXTBOX1 txtId, txtNome, txtPastaBase;
private ComboBox cbTipo;
private CheckBox chkAtivo;
private LV_TEXTBOX1 txtClientId, txtClientSecret, txtTokenStatus;
private Button btnAutenticar;
private LV_TEXTBOX1 txtUrl, txtUsuario, txtSenha;
public CloudStorageCadastroPanel()
{
this.Titulo = "Configuração de Armazenamento em Nuvem";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação ---
content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DA CONTA", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA CONEXÃO", 100, 50, 290, 30);
cbTipo = AddComboBox(content, "PROVEDOR", 400, 50, 150);
cbTipo.Items.AddRange(new object[] { "gdrive", "onedrive", "webdav" });
cbTipo.SelectedIndex = 0;
txtPastaBase = AddInput(content, "PASTA DESTINO (EX: /LevelOS_Backups)", 560, 50, 255, 30);
chkAtivo = new CheckBox
{
Text = "ATIVO",
Location = new Point(20, 100),
AutoSize = true,
Checked = true,
Font = new Font("Segoe UI", 8.5f, FontStyle.Bold)
};
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: OAuth2 ---
content.Controls.Add(CreateSectionHeader("AUTENTICAÇÃO OAUTH2 (GOOGLE / ONEDRIVE)", 125));
txtClientId = AddInput(content, "CLIENT ID", 20, 155, 350, 30);
txtClientSecret = AddInput(content, "CLIENT SECRET", 380, 155, 350, 30);
txtClientSecret.PasswordChar = '●';
// Botão alinhado com os inputs acima (Y = 155 + 16 = 171 do input, botão na próxima linha)
btnAutenticar = new Button
{
Text = "🔗 Vincular Conta",
Location = new Point(20, 200),
Size = new Size(150, 30),
BackColor = Color.FromArgb(66, 133, 244),
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9, FontStyle.Bold),
FlatAppearance = { BorderSize = 0 }
};
btnAutenticar.Click += (s, e) =>
MessageBox.Show("Abrindo navegador para autorização...", "LevelOS Cloud");
content.Controls.Add(btnAutenticar);
// STATUS DO TOKEN em linha própria, label Y=245, input Y=261
txtTokenStatus = AddInput(content, "STATUS DO TOKEN", 20, 245, 710, 30, true);
// --- SEÇÃO 3: WebDAV ---
content.Controls.Add(CreateSectionHeader("CONFIGURAÇÃO WEBDAV (NEXTCLOUD / OWNCLOUD)", 295));
txtUrl = AddInput(content, "URL DO SERVIDOR (EX: https://nuvem.seucloud.com/remote.php/dav/files/user/)", 20, 325, 490, 30);
txtUsuario = AddInput(content, "USUÁRIO", 520, 325, 150, 30);
txtSenha = AddInput(content, "SENHA / APP KEY", 680, 325, 135, 30);
txtSenha.PasswordChar = '●';
content.Height = 395;
// Lógica para alternar habilitação por tipo
cbTipo.SelectedIndexChanged += (s, e) =>
{
bool isWebDav = cbTipo.Text == "webdav";
txtClientId.Enabled = !isWebDav;
txtClientSecret.Enabled = !isWebDav;
btnAutenticar.Enabled = !isWebDav;
txtUrl.Enabled = isWebDav;
txtUsuario.Enabled = isWebDav;
txtSenha.Enabled = isWebDav;
};
}
private void PreencherModel()
{
_cloud.Nome = txtNome.Text;
_cloud.Tipo = cbTipo.Text;
_cloud.PastaBase = txtPastaBase.Text;
_cloud.ClientId = txtClientId.Text;
_cloud.ClientSecret = txtClientSecret.Text;
_cloud.Url = txtUrl.Text;
_cloud.Usuario = txtUsuario.Text;
_cloud.Senha = txtSenha.Text;
_cloud.Ativo = chkAtivo.Checked;
}
protected override void OnSalvar()
{
PreencherModel();
MessageBox.Show($"Configuração {cbTipo.Text} salva com sucesso!", "LevelOS Cloud");
}
protected override void OnNovo()
{
_cloud = new ModeloCloudStorage();
cbTipo.SelectedIndex = 0;
}
protected override void OnExcluir()
{
MessageBox.Show("Configuração excluída.", "LevelOS Cloud");
}
protected override void OnAlterar() { throw new NotImplementedException(); }
protected override void OnLocalizar() { throw new NotImplementedException(); }
protected override void OnCancelar() { }
}
}

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,130 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class ItensSaidaCadastroPanel : FormularioModelo
{
private ModeloItensSaida _saida = new ModeloItensSaida();
// Controles - Identificação e Item
private LV_TEXTBOX1 txtId, txtCodigo, txtCodItem, txtDescricaoItem;
private Button btnBuscaItem;
// Controles - Quantidade e Responsável
private LV_TEXTBOX1 txtQtdSai, txtDataSaida, txtFuncionario;
// Controles - Origem e Documentação (OS, OP, OV, NOTA)
private LV_TEXTBOX1 txtOs, txtOp, txtOv, txtNota, txtNatureza;
// Controles - Notas
private LV_TEXTBOX1 txtObs;
public ItensSaidaCadastroPanel()
{
this.Titulo = "Lançamento de Saída / Baixa de Estoque";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Item Selecionado ---
content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DO PRODUTO", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtCodigo = AddInput(content, "CÓD. MOV.", 100, 50, 110, 30);
txtCodItem = AddInput(content, "CÓD. ITEM", 220, 50, 100, 30);
btnBuscaItem = CriarBotaoLupa(325, 66, OnBuscaItem);
txtDescricaoItem = AddInput(content, "DESCRIÇÃO DO PRODUTO", 365, 50, 470, 30, true);
// --- SEÇÃO 2: Dados da Movimentação ---
content.Controls.Add(CreateSectionHeader("DADOS DA BAIXA", 115));
txtQtdSai = AddInput(content, "QTD. SAÍDA", 20, 145, 130, 30);
txtQtdSai.BackColor = Color.FromArgb(255, 240, 240); // Tom avermelhado para indicar saída/baixa
txtDataSaida = AddInput(content, "DATA SAÍDA", 160, 145, 130, 30);
txtFuncionario = AddInput(content, "RESPONSÁVEL PELA BAIXA", 300, 145, 330, 30);
txtNatureza = AddInput(content, "NATUREZA (EX: VENDA/AVARIA)", 640, 145, 195, 30);
// --- SEÇÃO 3: Vínculos e Documentos (OS, OP, OV, NOTA) ---
content.Controls.Add(CreateSectionHeader("DOCUMENTOS DE ORIGEM", 210));
txtOs = AddInput(content, "ORDEM SERV. (OS)", 20, 240, 150, 30);
txtOp = AddInput(content, "ORDEM PROD. (OP)", 180, 240, 150, 30);
txtOv = AddInput(content, "ORDEM VENDA (OV)", 340, 240, 150, 30);
txtNota = AddInput(content, "NOTA FISCAL", 500, 240, 180, 30);
// --- SEÇÃO 4: Observações ---
content.Controls.Add(CreateSectionHeader("OBSERVAÇÕES GERAIS", 305));
txtObs = AddInput(content, "MOTIVO DETALHADO DA SAÍDA", 20, 335, 815, 60);
content.Height = 430;
}
private Button CriarBotaoLupa(int x, int y, EventHandler clickEvent)
{
var btn = new Button
{
Text = "🔍",
Location = new Point(x, y),
Size = new Size(32, 30),
BackColor = AccentBlue,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Cursor = Cursors.Hand
};
btn.FlatAppearance.BorderSize = 0;
btn.Click += clickEvent;
content.Controls.Add(btn);
return btn;
}
private void OnBuscaItem(object sender, EventArgs e) => MessageBox.Show("Busca de Itens");
private void PreencherModel()
{
_saida.CODIGO = txtCodigo.Text;
_saida.COD_ITEM = txtCodItem.Text;
_saida.QTD_SAI = txtQtdSai.Text;
_saida.FUNCIONARIO = txtFuncionario.Text;
_saida.NOTA = txtNota.Text;
_saida.OS = txtOs.Text;
_saida.OP = txtOp.Text;
_saida.OBSERVACAO = txtObs.Text;
_saida.DATA_SAIDA = txtDataSaida.Text;
_saida.NATUREZA = txtNatureza.Text;
_saida.OV = txtOv.Text;
}
protected override void OnNovo()
{
_saida = new ModeloItensSaida();
txtDataSaida.Text = DateTime.Now.ToString("dd/MM/yyyy");
txtCodItem.Focus();
}
protected override void OnSalvar()
{
try
{
PreencherModel();
MessageBox.Show("Baixa de estoque registrada!", "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Erro ao salvar: " + ex.Message);
}
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { OnNovo(); }
}
}

View File

@ -0,0 +1,141 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class ItensSerialCadastroPanel : FormularioModelo
{
private ModeloItensSerial _serial = new ModeloItensSerial();
// Controles - Identificação
private LV_TEXTBOX1 txtId, txtCodigo, txtCodItem, txtSerial, txtNomeItem;
private Button btnBuscaItem;
// Controles - Histórico de Entrada
private LV_TEXTBOX1 txtCodEntrada, txtNfEntrada;
// Controles - Histórico de Saída
private LV_TEXTBOX1 txtCodSaida, txtNfSaida, txtDataMovim;
// Status e Notas
private LV_TEXTBOX1 txtObs;
private ComboBox cbBaixado;
public ItensSerialCadastroPanel()
{
this.Titulo = "Rastreamento de Número de Série / Garantia";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: O Produto e seu RG (Serial) ---
content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DO HARDWARE", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtCodigo = AddInput(content, "CÓD. VÍNCULO", 100, 50, 110, 30);
txtCodItem = AddInput(content, "CÓD. ITEM", 220, 50, 90, 30);
btnBuscaItem = CriarBotaoLupa(315, 66, OnBuscaItem);
txtNomeItem = AddInput(content, "NOME DO PRODUTO", 355, 50, 480, 30, true);
txtSerial = AddInput(content, "NÚMERO DE SÉRIE (S/N)", 20, 105, 380, 30);
txtSerial.BackColor = Color.LightYellow;
txtSerial.Font = new Font("Segoe UI", 10, FontStyle.Bold);
Label lblStatus = new Label { Text = "ESTÁ BAIXADO?", Location = new Point(415, 88), AutoSize = true };
cbBaixado = new ComboBox
{
Location = new Point(415, 105),
Size = new Size(120, 30),
DropDownStyle = ComboBoxStyle.DropDownList,
FlatStyle = FlatStyle.Flat
};
cbBaixado.Items.AddRange(new object[] { "NÃO", "SIM" });
cbBaixado.SelectedIndex = 0;
content.Controls.Add(lblStatus);
content.Controls.Add(cbBaixado);
// --- SEÇÃO 2: Origem (Entrada) ---
content.Controls.Add(CreateSectionHeader("HISTÓRICO DE ENTRADA / COMPRA", 165));
txtCodEntrada = AddInput(content, "ID MOV. ENTRADA", 20, 195, 150, 30);
txtNfEntrada = AddInput(content, "NF-e ENTRADA", 185, 195, 150, 30);
// --- SEÇÃO 3: Destino (Saída) ---
content.Controls.Add(CreateSectionHeader("HISTÓRICO DE SAÍDA / VENDA", 250));
txtCodSaida = AddInput(content, "ID MOV. SAÍDA", 20, 280, 150, 30);
txtNfSaida = AddInput(content, "NF-e SAÍDA", 185, 280, 150, 30);
txtDataMovim = AddInput(content, "DATA ÚLT. MOV.", 350, 280, 150, 30);
// --- SEÇÃO 4: Observações de Garantia ---
content.Controls.Add(CreateSectionHeader("OBSERVAÇÕES TÉCNICAS / ESTADO", 345));
txtObs = AddInput(content, "EX: ESTADO FÍSICO, DETALHES DE GARANTIA, ETC.", 20, 375, 815, 50);
content.Height = 460;
}
private Button CriarBotaoLupa(int x, int y, EventHandler clickEvent)
{
var btn = new Button
{
Text = "🔍",
Location = new Point(x, y),
Size = new Size(32, 30),
BackColor = AccentBlue,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Cursor = Cursors.Hand
};
btn.FlatAppearance.BorderSize = 0;
btn.Click += clickEvent;
content.Controls.Add(btn);
return btn;
}
private void OnBuscaItem(object sender, EventArgs e) => MessageBox.Show("Busca de Itens");
private void PreencherModel()
{
_serial.CODIGO = txtCodigo.Text;
_serial.COD_ITEM = txtCodItem.Text;
_serial.SERIAL = txtSerial.Text;
_serial.COD_NF_ENTRADA = txtNfEntrada.Text;
_serial.COD_NF_SAIDA = txtNfSaida.Text;
_serial.COD_ENTRADA = txtCodEntrada.Text;
_serial.COD_SAIDA = txtCodSaida.Text;
_serial.DATA_MOVIM = txtDataMovim.Text;
_serial.BAIXADO = cbBaixado.SelectedItem.ToString();
_serial.OBS = txtObs.Text;
}
protected override void OnNovo()
{
_serial = new ModeloItensSerial();
txtSerial.Focus();
}
protected override void OnSalvar()
{
try
{
PreencherModel();
MessageBox.Show("Número de série registrado no histórico!", "Garantia LevelOS", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Erro: " + ex.Message);
}
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { OnNovo(); }
}
}

View File

@ -0,0 +1,148 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class ItensVendaCadastroPanel : FormularioModelo
{
private ModeloItensVenda _itemVenda = new ModeloItensVenda();
// Controles - Vínculos e Identificação
private LV_TEXTBOX1 txtId, txtCodigo, txtItemCodigo, txtBarcode, txtDescricao;
private Button btnBuscaItem;
// Controles - Comercial (Valores e Lucro)
private LV_TEXTBOX1 txtQtd, txtVlrUn, txtDesconto, txtVlrTotal, txtVendaTabela, txtCusto;
// Controles - Rastreabilidade e Fiscal
private LV_TEXTBOX1 txtSeriais, txtLotes, txtXped, txtNitemped, txtInfAdic;
// Auxiliares
private LV_TEXTBOX1 txtUn, txtServico, txtDia;
public ItensVendaCadastroPanel()
{
this.Titulo = "Detalhamento do Item da Venda";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Dados do Produto ---
content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO DO ITEM", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtCodigo = AddInput(content, "CÓD. VENDA", 100, 50, 100, 30);
txtItemCodigo = AddInput(content, "CÓD. ITEM", 210, 50, 100, 30);
btnBuscaItem = CriarBotaoLupa(315, 66, OnBuscaItem);
txtBarcode = AddInput(content, "BARCODE", 355, 50, 160, 30);
txtDescricao = AddInput(content, "DESCRIÇÃO DO ITEM", 20, 105, 495, 30);
txtUn = AddInput(content, "UN", 525, 105, 60, 30);
txtServico = AddInput(content, "S/P", 595, 105, 60, 30); // Serviço ou Produto
// --- SEÇÃO 2: Financeiro e Precificação ---
content.Controls.Add(CreateSectionHeader("VALORES E NEGOCIAÇÃO", 175));
txtCusto = AddInput(content, "CUSTO UNIT. (REF)", 20, 205, 130, 30, true);
txtVendaTabela = AddInput(content, "PREÇO TABELA", 160, 205, 130, 30, true);
txtQtd = AddInput(content, "QUANTIDADE", 300, 205, 100, 30);
txtVlrUn = AddInput(content, "PREÇO PRATICADO", 410, 205, 130, 30);
txtVlrUn.BackColor = Color.LightCyan;
txtDesconto = AddInput(content, "DESCONTO UNIT.", 550, 205, 105, 30);
txtVlrTotal = AddInput(content, "TOTAL LÍQUIDO", 665, 205, 170, 30, true);
txtVlrTotal.BackColor = Color.FromArgb(235, 255, 235);
txtVlrTotal.Font = new Font("Segoe UI", 11, FontStyle.Bold);
// --- SEÇÃO 3: Rastreabilidade (Seriais e Lotes) ---
content.Controls.Add(CreateSectionHeader("CONTROLE DE ESTOQUE ESPECÍFICO", 275));
txtSeriais = AddInput(content, "NÚMEROS DE SÉRIE (GARANTIA)", 20, 305, 400, 40);
txtLotes = AddInput(content, "IDENTIFICAÇÃO DE LOTES", 430, 305, 405, 40);
// --- SEÇÃO 4: Dados Fiscais e Pedido Externo ---
content.Controls.Add(CreateSectionHeader("REFERÊNCIAS FISCAIS (NF-e)", 375));
txtXped = AddInput(content, "PEDIDO COMPRA CLIENTE (xPed)", 20, 405, 200, 30);
txtNitemped = AddInput(content, "ITEM DO PEDIDO (nItemPed)", 230, 405, 150, 30);
txtDia = AddInput(content, "DIA", 390, 405, 60, 30);
txtInfAdic = AddInput(content, "INFORMAÇÕES ADICIONAIS DO ITEM", 460, 405, 375, 40);
content.Height = 480;
}
private Button CriarBotaoLupa(int x, int y, EventHandler clickEvent)
{
var btn = new Button
{
Text = "🔍",
Location = new Point(x, y),
Size = new Size(32, 30),
BackColor = AccentBlue,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Cursor = Cursors.Hand
};
btn.FlatAppearance.BorderSize = 0;
btn.Click += clickEvent;
content.Controls.Add(btn);
return btn;
}
private void OnBuscaItem(object sender, EventArgs e) => MessageBox.Show("Selecionar Item para Venda");
private void PreencherModel()
{
_itemVenda.CODIGO = txtCodigo.Text;
_itemVenda.ITEM_CODIGO = txtItemCodigo.Text;
_itemVenda.BARCODE = txtBarcode.Text;
_itemVenda.DESCRICAO = txtDescricao.Text;
_itemVenda.SERVICO = txtServico.Text;
_itemVenda.QTD = txtQtd.Text;
_itemVenda.VRL_UN = txtVlrUn.Text;
_itemVenda.DESCONTO = txtDesconto.Text;
_itemVenda.VLR_TOTAL = txtVlrTotal.Text;
_itemVenda.UN = txtUn.Text;
_itemVenda.VENDA = txtVendaTabela.Text;
_itemVenda.DIA = txtDia.Text;
_itemVenda.SERIAIS_IN = txtSeriais.Text;
_itemVenda.XPED = txtXped.Text;
_itemVenda.NITEMPED = txtNitemped.Text;
_itemVenda.CUSTO = txtCusto.Text;
_itemVenda.VINF_ADIC = txtInfAdic.Text;
_itemVenda.LOTES = txtLotes.Text;
}
protected override void OnNovo()
{
_itemVenda = new ModeloItensVenda();
txtBarcode.Focus();
}
protected override void OnSalvar()
{
try
{
PreencherModel();
MessageBox.Show("Item adicionado à venda!", "LevelOS PDV", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Erro: " + ex.Message);
}
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { OnNovo(); }
}
}

View File

@ -0,0 +1,93 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class LCP116CadastroPanel : FormularioModelo
{
private ModeloLCP116 _lcp = new ModeloLCP116();
// Controles - Identificação
private LV_TEXTBOX1 txtId, txtCodigo, txtDataCadastro;
// Controles - Hierarquia e Descrição
private LV_TEXTBOX1 txtCodigoPai, txtCodigoFilho, txtDescricao;
public LCP116CadastroPanel()
{
this.Titulo = "Lista de Serviços LC 116/03 (ISS)";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação do Registro ---
content.Controls.Add(CreateSectionHeader("IDENTIFICAÇÃO FISCAL", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtCodigo = AddInput(content, "CÓD. INTERNO", 100, 50, 110, 30);
txtDataCadastro = AddInput(content, "DATA CADASTRO", 220, 50, 150, 30, true);
// --- SEÇÃO 2: Estrutura do Código de Serviço ---
content.Controls.Add(CreateSectionHeader("HIERARQUIA DO SERVIÇO", 115));
txtCodigoPai = AddInput(content, "GRUPO (PAI)", 20, 145, 120, 30);
txtCodigoFilho = AddInput(content, "SUB-ITEM (FILHO)", 150, 145, 120, 30);
// Exemplo visual: 14.02 (Assistência Técnica)
Label lblExemplo = new Label
{
Text = "Ex: Pai [14] - Filho [02] = 14.02",
Location = new Point(280, 161),
AutoSize = true,
ForeColor = Color.Gray,
Font = new Font("Segoe UI", 8, FontStyle.Italic)
};
content.Controls.Add(lblExemplo);
// --- SEÇÃO 3: Descrição Oficial ---
content.Controls.Add(CreateSectionHeader("DESCRIÇÃO DA ATIVIDADE", 210));
txtDescricao = AddInput(content, "TEXTO CONFORME LEI COMPLEMENTAR", 20, 240, 815, 60);
content.Height = 330;
}
private void PreencherModel()
{
_lcp.CODIGO = txtCodigo.Text;
_lcp.CODIGO_PAI = txtCodigoPai.Text;
_lcp.CODIGO_FILHO = txtCodigoFilho.Text;
_lcp.DESCRICAO = txtDescricao.Text;
_lcp.DATA_CADASTRO = txtDataCadastro.Text;
}
protected override void OnNovo()
{
_lcp = new ModeloLCP116();
txtDataCadastro.Text = DateTime.Now.ToString("dd/MM/yyyy");
txtCodigoPai.Focus();
}
protected override void OnSalvar()
{
try
{
PreencherModel();
MessageBox.Show("Código de serviço LC116 registrado!", "LevelOS Fiscal", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Erro: " + ex.Message);
}
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { OnNovo(); }
}
}

View File

@ -0,0 +1,111 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class FtpClienteCadastroPanel : FormularioModelo
{
private ModeloFtpCliente _ftp = new ModeloFtpCliente();
// Controles - Conexão
private LV_TEXTBOX1 txtId, txtNome, txtHost, txtPorta, txtDiretorio;
// Controles - Autenticação
private LV_TEXTBOX1 txtUsuario, txtSenha;
private CheckBox chkSsl, chkPassivo, chkAtivo;
// Controles - Info e Ação
private LV_TEXTBOX1 txtCriadoEm, txtAtualizadoEm;
private Button btnTestarFtp;
public FtpClienteCadastroPanel()
{
this.Titulo = "Configuração de Cliente FTP / Storage";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Endereçamento ---
content.Controls.Add(CreateSectionHeader("SERVIDOR DE ARQUIVOS", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA CONEXÃO (EX: BACKUP CLOUD)", 100, 50, 350, 30);
txtHost = AddInput(content, "HOST / IP (EX: ftp.meusite.com)", 20, 105, 330, 30);
txtPorta = AddInput(content, "PORTA", 360, 105, 90, 30);
chkSsl = new CheckBox { Text = "USAR SSL/TLS", Location = new Point(470, 121), AutoSize = true };
chkPassivo = new CheckBox { Text = "MODO PASSIVO", Location = new Point(580, 121), AutoSize = true, Checked = true };
chkAtivo = new CheckBox { Text = "ATIVO", Location = new Point(700, 121), AutoSize = true, Checked = true };
content.Controls.Add(chkSsl);
content.Controls.Add(chkPassivo);
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: Credenciais e Caminhos ---
content.Controls.Add(CreateSectionHeader("AUTENTICAÇÃO E DIRETÓRIO", 165));
txtUsuario = AddInput(content, "USUÁRIO", 20, 195, 250, 30);
txtSenha = AddInput(content, "SENHA", 280, 195, 200, 30);
txtSenha.PasswordChar = '●';
txtDiretorio = AddInput(content, "DIRETÓRIO RAIZ (EX: /public_html/backups)", 20, 250, 460, 30);
btnTestarFtp = new Button
{
Text = "📁 Testar Acesso",
Location = new Point(500, 266),
Size = new Size(150, 30),
BackColor = Color.FromArgb(0, 122, 204),
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9, FontStyle.Bold)
};
btnTestarFtp.Click += (s, e) => MessageBox.Show("Tentando conectar ao servidor FTP...");
content.Controls.Add(btnTestarFtp);
// --- SEÇÃO 3: Datas ---
content.Controls.Add(CreateSectionHeader("LOG DE REGISTRO", 310));
txtCriadoEm = AddInput(content, "CRIADO EM", 20, 340, 185, 30, true);
txtAtualizadoEm = AddInput(content, "ÚLT. ATUALIZAÇÃO", 215, 340, 185, 30, true);
content.Height = 400;
}
private void PreencherModel()
{
_ftp.Nome = txtNome.Text;
_ftp.Host = txtHost.Text;
_ftp.Porta = int.TryParse(txtPorta.Text, out int p) ? p : 21;
_ftp.Usuario = txtUsuario.Text;
_ftp.Senha = txtSenha.Text;
_ftp.UsarSsl = chkSsl.Checked;
_ftp.ModoPassivo = chkPassivo.Checked;
_ftp.DiretorioRaiz = txtDiretorio.Text;
_ftp.Ativo = chkAtivo.Checked;
}
protected override void OnNovo()
{
_ftp = new ModeloFtpCliente();
txtPorta.Text = "21";
txtNome.Focus();
}
protected override void OnSalvar()
{
PreencherModel();
MessageBox.Show("Configuração FTP salva com sucesso!", "LevelOS Storage", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { }
}
}

View File

@ -0,0 +1,116 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class SmtpClienteCadastroPanel : FormularioModelo
{
private ModeloSmtpCliente _smtp = new ModeloSmtpCliente();
// Controles - Servidor
private LV_TEXTBOX1 txtId, txtNome, txtServidor, txtPorta;
// Controles - Autenticação
private LV_TEXTBOX1 txtUsuario, txtSenha;
private CheckBox chkSsl, chkTls, chkAtivo;
// Controles - Remetente
private LV_TEXTBOX1 txtEmailRemetente, txtNomeRemetente;
// Controles - Info e Ações
private LV_TEXTBOX1 txtCriadoEm, txtAtualizadoEm;
private Button btnTestarSmtp;
public SmtpClienteCadastroPanel()
{
this.Titulo = "Configuração de Servidor de E-mail (SMTP)";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Servidor ---
content.Controls.Add(CreateSectionHeader("SERVIDOR DE SAÍDA", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA CONFIG. (EX: GMAIL OFICIAL)", 100, 50, 350, 30);
txtServidor = AddInput(content, "HOST SMTP (EX: smtp.gmail.com)", 20, 105, 330, 30);
txtPorta = AddInput(content, "PORTA", 360, 105, 90, 30);
chkSsl = new CheckBox { Text = "USAR SSL", Location = new Point(470, 121), AutoSize = true };
chkTls = new CheckBox { Text = "USAR TLS", Location = new Point(560, 121), AutoSize = true };
chkAtivo = new CheckBox { Text = "ATIVO", Location = new Point(650, 121), AutoSize = true, Checked = true };
content.Controls.Add(chkSsl);
content.Controls.Add(chkTls);
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: Autenticação e Remetente ---
content.Controls.Add(CreateSectionHeader("CONTA E IDENTIFICAÇÃO", 165));
txtUsuario = AddInput(content, "USUÁRIO / LOGIN", 20, 195, 250, 30);
txtSenha = AddInput(content, "SENHA / APP PASSWORD", 280, 195, 200, 30);
txtSenha.PasswordChar = '●';
txtEmailRemetente = AddInput(content, "E-MAIL DO REMETENTE", 20, 250, 250, 30);
txtNomeRemetente = AddInput(content, "NOME EXIBIDO NO ENVIO", 280, 250, 200, 30);
btnTestarSmtp = new Button
{
Text = "⚡ Testar Conexão",
Location = new Point(500, 266),
Size = new Size(150, 30),
BackColor = Color.FromArgb(40, 167, 69),
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9, FontStyle.Bold)
};
btnTestarSmtp.Click += (s, e) => MessageBox.Show("Enviando e-mail de teste...");
content.Controls.Add(btnTestarSmtp);
// --- SEÇÃO 3: Datas ---
content.Controls.Add(CreateSectionHeader("HISTÓRICO", 310));
txtCriadoEm = AddInput(content, "CRIADO EM", 20, 340, 185, 30, true);
txtAtualizadoEm = AddInput(content, "ÚLT. ATUALIZAÇÃO", 215, 340, 185, 30, true);
content.Height = 400;
}
private void PreencherModel()
{
_smtp.Nome = txtNome.Text;
_smtp.ServidorSmtp = txtServidor.Text;
_smtp.Porta = int.TryParse(txtPorta.Text, out int p) ? p : 587;
_smtp.Usuario = txtUsuario.Text;
_smtp.Senha = txtSenha.Text;
_smtp.UsarSsl = chkSsl.Checked;
_smtp.UsarTls = chkTls.Checked;
_smtp.EmailRemetente = txtEmailRemetente.Text;
_smtp.NomeRemetente = txtNomeRemetente.Text;
_smtp.Ativo = chkAtivo.Checked;
}
protected override void OnNovo()
{
_smtp = new ModeloSmtpCliente();
txtPorta.Text = "587";
txtNome.Focus();
}
protected override void OnSalvar()
{
PreencherModel();
MessageBox.Show("Configuração SMTP salva com sucesso!", "LevelOS Mail", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { }
}
}

View File

@ -0,0 +1,133 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class SshClienteCadastroPanel : FormularioModelo
{
private ModeloSshCliente _ssh = new ModeloSshCliente();
// Controles - Identificação e Conexão
private LV_TEXTBOX1 txtId, txtNome, txtHost, txtPorta, txtUsuario;
// Controles - Autenticação
private LV_TEXTBOX1 txtSenha, txtPassphrase, txtCaminhoChave;
private ComboBox cbTipoAutenticacao;
private CheckBox chkUsarChave, chkAtivo;
private Button btnSelecionarChave;
// Controles - Informações Técnicas
private LV_TEXTBOX1 txtFingerprint, txtCriadoEm, txtAtualizadoEm;
public SshClienteCadastroPanel()
{
this.Titulo = "Configuração de Conexão SSH";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Endereçamento ---
content.Controls.Add(CreateSectionHeader("SERVIDOR DESTINO", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA CONEXÃO (EX: SERVER CLOUD)", 100, 50, 350, 30);
txtHost = AddInput(content, "HOST / IP", 20, 105, 330, 30);
txtPorta = AddInput(content, "PORTA", 360, 105, 90, 30);
txtUsuario = AddInput(content, "USUÁRIO SSH", 460, 105, 180, 30);
chkAtivo = new CheckBox { Text = "ATIVO", Location = new Point(660, 121), AutoSize = true, Checked = true };
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: Autenticação ---
content.Controls.Add(CreateSectionHeader("SEGURANÇA E ACESSO", 165));
// ✅ Usando AddComboBox — label e input no mesmo padrão do AddInput
cbTipoAutenticacao = AddComboBox(content, "MÉTODO", 20, 195, 150);
cbTipoAutenticacao.Items.AddRange(new object[] { "Senha", "Chave Privada" });
cbTipoAutenticacao.SelectedIndex = 0;
// ✅ Y=195 no AddInput → label em 195, input em 211 — alinhado com o ComboBox
txtSenha = AddInput(content, "SENHA", 180, 195, 200, 30);
txtSenha.PasswordChar = '●';
chkUsarChave = new CheckBox
{
Text = "USAR CHAVE (.PEM / .PPK)",
Location = new Point(400, 213),
AutoSize = true
};
content.Controls.Add(chkUsarChave);
txtCaminhoChave = AddInput(content, "CAMINHO DA CHAVE PRIVADA", 20, 260, 580, 30);
btnSelecionarChave = new Button
{
Text = "...",
Location = new Point(610, 276),
Size = new Size(40, 30),
BackColor = Color.Gainsboro,
FlatStyle = FlatStyle.Flat
};
new ToolTip().SetToolTip(btnSelecionarChave, "Selecionar arquivo de chave (.PEM / .PPK)");
btnSelecionarChave.Click += (s, e) =>
{
using var ofd = new OpenFileDialog
{
Filter = "Arquivos de Chave (*.pem;*.ppk)|*.pem;*.ppk|Todos (*.*)|*.*"
};
if (ofd.ShowDialog() == DialogResult.OK)
txtCaminhoChave.Text = ofd.FileName;
};
content.Controls.Add(btnSelecionarChave);
txtPassphrase = AddInput(content, "PASSPHRASE DA CHAVE", 660, 260, 175, 30);
txtPassphrase.PasswordChar = '●';
// --- SEÇÃO 3: Metadados ---
content.Controls.Add(CreateSectionHeader("DADOS TÉCNICOS", 330));
txtFingerprint = AddInput(content, "SSH FINGERPRINT (AUTOMÁTICO)", 20, 360, 430, 30, true);
txtCriadoEm = AddInput(content, "CRIADO EM", 460, 360, 185, 30, true);
txtAtualizadoEm = AddInput(content, "ÚLT. ATUALIZAÇÃO", 655, 360, 185, 30, true);
content.Height = 420;
}
private void PreencherModel()
{
_ssh.Nome = txtNome.Text;
_ssh.Host = txtHost.Text;
_ssh.Porta = int.TryParse(txtPorta.Text, out int p) ? p : 22;
_ssh.Usuario = txtUsuario.Text;
_ssh.TipoAutenticacao = cbTipoAutenticacao.Text;
_ssh.Senha = txtSenha.Text;
_ssh.CaminhoChave = txtCaminhoChave.Text;
_ssh.Passphrase = txtPassphrase.Text;
_ssh.UsarChave = chkUsarChave.Checked;
_ssh.Ativo = chkAtivo.Checked;
}
protected override void OnNovo()
{
_ssh = new ModeloSshCliente();
txtPorta.Text = "22";
cbTipoAutenticacao.SelectedIndex = 0;
txtNome.Focus();
}
protected override void OnSalvar()
{
PreencherModel();
MessageBox.Show("Configuração de conexão SSH salva!", "LevelOS Network", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { }
}
}

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,134 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class TelegramClienteCadastroPanel : FormularioModelo
{
private ModeloTelegramCliente _tg = new ModeloTelegramCliente();
// Controles - Básicos
private LV_TEXTBOX1 txtId, txtNome, txtChatId;
private ComboBox cbTipo;
private CheckBox chkAtivo;
// Controles - Bot API (Simples)
private LV_TEXTBOX1 txtBotToken;
// Controles - User API (Avançado)
private LV_TEXTBOX1 txtApiId, txtApiHash, txtTelefone, txtSession;
// Controles - Info
private LV_TEXTBOX1 txtCriadoEm, txtAtualizadoEm;
private Button btnTestarBot;
public TelegramClienteCadastroPanel()
{
this.Titulo = "Integração com Telegram (Bot & User API)";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação ---
content.Controls.Add(CreateSectionHeader("CONFIGURAÇÃO GERAL", 20));
txtId = AddInput(content, "ID", 20, 50, 70, 30, true);
txtNome = AddInput(content, "NOME DA INTEGRAÇÃO", 100, 50, 320, 30);
Label lblTipo = new Label { Text = "TIPO DE CONTA", Location = new Point(435, 33), AutoSize = true };
cbTipo = new ComboBox
{
Location = new Point(435, 50),
Size = new Size(150, 30),
DropDownStyle = ComboBoxStyle.DropDownList,
FlatStyle = FlatStyle.Flat
};
cbTipo.Items.AddRange(new object[] { "BOT (API)", "USER (SESSION)" });
cbTipo.SelectedIndex = 0;
content.Controls.Add(lblTipo);
content.Controls.Add(cbTipo);
txtChatId = AddInput(content, "ID DO CHAT DESTINO (CHAT_ID)", 600, 50, 160, 30);
chkAtivo = new CheckBox { Text = "ATIVO", Location = new Point(770, 66), AutoSize = true, Checked = true };
content.Controls.Add(chkAtivo);
// --- SEÇÃO 2: Bot API ---
content.Controls.Add(CreateSectionHeader("BOT API (SIMPLES)", 115));
txtBotToken = AddInput(content, "BOT TOKEN (FORNECIDO PELO @BOTFATHER)", 20, 145, 565, 30);
btnTestarBot = new Button
{
Text = "🔔 Testar Envio",
Location = new Point(600, 161),
Size = new Size(160, 30),
BackColor = Color.FromArgb(0, 136, 204), // Azul Telegram
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9, FontStyle.Bold)
};
btnTestarBot.Click += (s, e) => MessageBox.Show("Enviando mensagem de teste via Telegram...");
content.Controls.Add(btnTestarBot);
// --- SEÇÃO 3: User API / MTProto (Avançado) ---
content.Controls.Add(CreateSectionHeader("USER API / MTPROTO (AVANÇADO)", 210));
txtApiId = AddInput(content, "API ID", 20, 240, 120, 30);
txtApiHash = AddInput(content, "API HASH", 150, 240, 300, 30);
txtTelefone = AddInput(content, "TELEFONE (+55...)", 460, 240, 180, 30);
txtSession = AddInput(content, "SESSION STRING (PARA CONEXÕES PERSISTENTES)", 20, 295, 815, 30);
txtSession.PasswordChar = '●';
// --- SEÇÃO 4: Rodapé ---
content.Controls.Add(CreateSectionHeader("HISTÓRICO", 360));
txtCriadoEm = AddInput(content, "CRIADO EM", 20, 390, 185, 30, true);
txtAtualizadoEm = AddInput(content, "ÚLT. ATUALIZAÇÃO", 215, 390, 185, 30, true);
content.Height = 450;
// Logica visual para alternar campos
cbTipo.SelectedIndexChanged += (s, e) => {
bool isBot = cbTipo.SelectedIndex == 0;
txtBotToken.Enabled = isBot;
txtApiId.Enabled = !isBot;
txtApiHash.Enabled = !isBot;
txtTelefone.Enabled = !isBot;
txtSession.Enabled = !isBot;
};
}
private void PreencherModel()
{
_tg.Nome = txtNome.Text;
_tg.Tipo = cbTipo.Text;
_tg.BotToken = txtBotToken.Text;
_tg.ApiId = int.TryParse(txtApiId.Text, out int aid) ? aid : 0;
_tg.ApiHash = txtApiHash.Text;
_tg.Telefone = txtTelefone.Text;
_tg.SessionString = txtSession.Text;
_tg.ChatId = txtChatId.Text;
_tg.Ativo = chkAtivo.Checked;
}
protected override void OnNovo()
{
_tg = new ModeloTelegramCliente();
txtNome.Focus();
}
protected override void OnSalvar()
{
PreencherModel();
MessageBox.Show("Configuração de Telegram salva com sucesso!", "LevelOS Messenger", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
protected override void OnCancelar() { }
}
}

View File

@ -0,0 +1,92 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class BackupExecucaoDetalhePanel : FormularioModelo
{
private ModeloBackupExecucao _exec = new ModeloBackupExecucao();
// Controles - Identificação
private LV_TEXTBOX1 txtId, txtIdBackup, txtStatus, txtData;
// Controles - Métricas de Performance
private LV_TEXTBOX1 txtTamanho, txtDuracao;
// Controles - Resultado
private LV_TEXTBOX1 txtMensagem;
public BackupExecucaoDetalhePanel()
{
this.Titulo = "Detalhes da Execução de Backup";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação do Evento ---
content.Controls.Add(CreateSectionHeader("INFORMAÇÕES DA EXECUÇÃO", 20));
txtId = AddInput(content, "ID LOG", 20, 50, 80, 30, true);
txtIdBackup = AddInput(content, "ID CONFIG. ORIGEM", 110, 50, 120, 30, true);
txtData = AddInput(content, "DATA/HORA EXECUÇÃO", 240, 50, 200, 30, true);
txtStatus = AddInput(content, "STATUS FINAL", 450, 50, 150, 30, true);
txtStatus.Font = new Font("Segoe UI", 10, FontStyle.Bold);
// --- SEÇÃO 2: Métricas ---
content.Controls.Add(CreateSectionHeader("MÉTRICAS E DESEMPENHO", 115));
txtTamanho = AddInput(content, "TAMANHO DO ARQUIVO (BYTES)", 20, 145, 230, 30, true);
txtDuracao = AddInput(content, "DURAÇÃO (SEGUNDOS)", 260, 145, 180, 30, true);
// --- SEÇÃO 3: Log Completo ---
content.Controls.Add(CreateSectionHeader("MENSAGEM DO SISTEMA / ERRO", 210));
txtMensagem = AddInput(content, "DETALHAMENTO DO PROCESSO", 20, 240, 815, 80, true);
txtMensagem.Multiline = true;
content.Height = 350;
}
// Método para carregar os dados (usado pela tela de listagem/grid)
public void CarregarDados(ModeloBackupExecucao execucao)
{
_exec = execucao;
txtId.Text = _exec.Id.ToString();
txtIdBackup.Text = _exec.IdBackup.ToString();
txtData.Text = _exec.DataExecucao.ToString("dd/MM/yyyy HH:mm:ss");
txtStatus.Text = _exec.Status.ToUpper();
txtTamanho.Text = _exec.TamanhoArquivo?.ToString("N0") ?? "0";
txtDuracao.Text = _exec.DuracaoSegundos?.ToString() + "s";
txtMensagem.Text = _exec.Mensagem;
// Alerta visual para falhas
if (_exec.Status.ToLower() == "erro" || _exec.Status.ToLower() == "falha")
{
txtStatus.BackColor = Color.FromArgb(255, 230, 230);
txtStatus.ForeColor = Color.Red;
}
else
{
txtStatus.BackColor = Color.FromArgb(230, 255, 230);
txtStatus.ForeColor = Color.DarkGreen;
}
}
protected override void OnNovo() { } // Bloqueado para logs
protected override void OnSalvar() { } // Bloqueado para logs
protected override void OnAlterar()
{
}
protected override void OnCancelar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { }
}
}

View File

@ -0,0 +1,94 @@
using CPM;
using MLL;
using System;
using System.Drawing;
using System.Windows.Forms;
using UI;
namespace UI
{
public partial class LogUserConsultaPanel : FormularioModelo
{
private ModeloLogUser _log = new ModeloLogUser();
// Controles - Quem e Quando
private LV_TEXTBOX1 txtId, txtCodigo, txtUsuario, txtMicro, txtDiaLog;
// Controles - O Quê e Onde
private LV_TEXTBOX1 txtAcao, txtEnderCli;
// Controles - Nível de Severidade
private ComboBox cbRisco;
public LogUserConsultaPanel()
{
this.Titulo = "Auditoria de Eventos e Logs de Usuário";
MontarInterface();
}
private void MontarInterface()
{
// --- SEÇÃO 1: Identificação do Evento ---
content.Controls.Add(CreateSectionHeader("DETALHES DA OCORRÊNCIA", 20));
txtId = AddInput(content, "ID LOG", 20, 50, 70, 30, true);
txtCodigo = AddInput(content, "CÓD. REF", 100, 50, 110, 30, true);
txtDiaLog = AddInput(content, "DATA/HORA DO EVENTO", 220, 50, 200, 30, true);
// --- SEÇÃO 2: Rastreabilidade de Origem ---
content.Controls.Add(CreateSectionHeader("ORIGEM E USUÁRIO", 115));
txtUsuario = AddInput(content, "OPERADOR", 20, 145, 250, 30, true);
txtMicro = AddInput(content, "NOME DA MÁQUINA (HOSTNAME)", 280, 145, 250, 30, true);
txtEnderCli = AddInput(content, "IP / ENDEREÇO CLIENTE", 540, 145, 295, 30, true);
// --- SEÇÃO 3: Ação e Impacto ---
content.Controls.Add(CreateSectionHeader("DESCRIÇÃO DA ATIVIDADE E RISCO", 210));
txtAcao = AddInput(content, "AÇÃO EXECUTADA", 20, 240, 500, 30, true);
Label lblRisco = new Label { Text = "NÍVEL DE RISCO", Location = new Point(530, 223), AutoSize = true };
cbRisco = new ComboBox
{
Location = new Point(530, 240),
Size = new Size(150, 30),
DropDownStyle = ComboBoxStyle.DropDownList,
Enabled = false // Logs geralmente são imutáveis
};
cbRisco.Items.AddRange(new object[] { "BAIXO", "MÉDIO", "ALTO", "CRÍTICO" });
content.Controls.Add(lblRisco);
content.Controls.Add(cbRisco);
// Destaque visual para o risco se necessário
cbRisco.SelectedIndexChanged += (s, e) => {
if (cbRisco.Text == "CRÍTICO" || cbRisco.Text == "ALTO")
cbRisco.BackColor = Color.LightCoral;
};
content.Height = 320;
}
private void PreencherModel()
{
_log.CODIGO = txtCodigo.Text;
_log.USUARIO = txtUsuario.Text;
_log.MICRO = txtMicro.Text;
_log.ACAO = txtAcao.Text;
_log.RISCO = cbRisco.Text;
_log.DIA_LOG = txtDiaLog.Text;
_log.ENDER_CLI = txtEnderCli.Text;
}
protected override void OnNovo() { /* Logs não são criados manualmente */ }
protected override void OnSalvar()
{
MessageBox.Show("Registros de auditoria são protegidos e não podem ser alterados.", "LevelOS Security", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
protected override void OnAlterar() { }
protected override void OnExcluir() { }
protected override void OnLocalizar() { /* Implementar busca por data ou usuário */ }
protected override void OnCancelar() { }
}
}

View File

@ -30,9 +30,14 @@ namespace UI
private readonly ChamadoCadastroPanel _pChamadoCadastroPanel;
//Sistma
//Sistema
private readonly ConfigCadastroPanel _pconfigCadastroPanel;
private readonly SshClienteCadastroPanel _psshClienteCadastroPanel;
private readonly SmtpClienteCadastroPanel _psmtpClienteCadastroPanel;
private readonly FtpClienteCadastroPanel _pftpClienteCadastroPanel;
private readonly TelegramClienteCadastroPanel _ptelegramClienteCadastroPanel;
private readonly BackupAutomaticoCadastroPanel _pBackupAutomaticoCadastroPanel;
private readonly CloudStorageCadastroPanel _pCloudStorageCadastroPanel;
//Painel financeiro
private readonly BoletoCadastroPanel _pboletoCadastroPanel;
private readonly ContaPagarCadastroPanel _pcontaPagarCadastroPanel;
@ -73,6 +78,12 @@ namespace UI
_pcontaPagarCadastroPanel = new ContaPagarCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_pcontaReceberCadastroPanel = new ContaReceberCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_pcontasCadastroPanel = new ContasCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_psshClienteCadastroPanel = new SshClienteCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_psmtpClienteCadastroPanel = new SmtpClienteCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_pftpClienteCadastroPanel = new FtpClienteCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_ptelegramClienteCadastroPanel = new TelegramClienteCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_pBackupAutomaticoCadastroPanel = new BackupAutomaticoCadastroPanel { Dock = DockStyle.Fill, Visible = false };
_pCloudStorageCadastroPanel = new CloudStorageCadastroPanel { 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));
@ -102,6 +113,12 @@ namespace UI
mainContainer.Controls.Add(_pfuncionariosCadastro);
mainContainer.Controls.Add(_pcontaPagarCadastroPanel);
mainContainer.Controls.Add(_pcontasCadastroPanel);
mainContainer.Controls.Add(_psshClienteCadastroPanel);
mainContainer.Controls.Add(_psmtpClienteCadastroPanel);
mainContainer.Controls.Add(_pftpClienteCadastroPanel);
mainContainer.Controls.Add(_ptelegramClienteCadastroPanel);
mainContainer.Controls.Add(_pBackupAutomaticoCadastroPanel);
mainContainer.Controls.Add(_pCloudStorageCadastroPanel);
Controls.Add(mainContainer);
Controls.Add(_sidebar);
@ -139,6 +156,12 @@ namespace UI
case 201: ShowPanel(_pconfigCadastroPanel); break;
case 202: ShowPanel(_pEmpresaConfig); break;
case 203: ShowPanel(_pftpClienteCadastroPanel); break;
case 204: ShowPanel(_ptelegramClienteCadastroPanel); break;
case 205: ShowPanel(_psmtpClienteCadastroPanel); break;
case 206: ShowPanel(_psshClienteCadastroPanel); break;
case 207: ShowPanel(_pBackupAutomaticoCadastroPanel); break;
case 208: ShowPanel(_pCloudStorageCadastroPanel); break;
case 300: ShowPanel(_pAgendaCadastro); break;
case 301: ShowPanel(_pAgendaConsulta); break;
@ -178,6 +201,12 @@ namespace UI
_pcontaPagarCadastroPanel.Visible = (panelToShow == _pcontaPagarCadastroPanel);
_pcontaReceberCadastroPanel.Visible = (panelToShow == _pcontaReceberCadastroPanel);
_pcontasCadastroPanel.Visible = (panelToShow == _pcontasCadastroPanel);
_psshClienteCadastroPanel.Visible = (panelToShow == _psshClienteCadastroPanel);
_psmtpClienteCadastroPanel.Visible = (panelToShow == _psmtpClienteCadastroPanel);
_pftpClienteCadastroPanel.Visible = (panelToShow == _pftpClienteCadastroPanel);
_ptelegramClienteCadastroPanel.Visible = (panelToShow == _ptelegramClienteCadastroPanel);
_pBackupAutomaticoCadastroPanel.Visible = (panelToShow == _pBackupAutomaticoCadastroPanel);
_pCloudStorageCadastroPanel.Visible = (panelToShow == _pCloudStorageCadastroPanel);
if (panelToShow.Visible)
{
panelToShow.BringToFront();

View File

@ -141,9 +141,11 @@ namespace UI
_subMenuConfiguracao.Items.Add(CreateItem("📂 FTP-Cliente", (s, e) => NavItemClicked?.Invoke(this, 203)));
_subMenuConfiguracao.Items.Add(CreateItem("💬 Telegram-Cliente", (s, e) => NavItemClicked?.Invoke(this, 204)));
_subMenuConfiguracao.Items.Add(CreateItem("📩 SMTP-Cliente", (s, e) => NavItemClicked?.Invoke(this, 205)));
// Adicionando ao sub-menu de configurações
_subMenuConfiguracao.Items.Add(CreateItem(">_ SSH-Cliente", (s, e) => NavItemClicked?.Invoke(this, 206)));
_subMenuConfiguracao.Items.Add(new ToolStripSeparator());
_subMenuConfiguracao.Items.Add(CreateItem("⏰ Backups Automáticos", (s, e) => NavItemClicked?.Invoke(this, 206)));
_subMenuConfiguracao.Items.Add(CreateItem("☁️ Backup em Nuvem", (s, e) => NavItemClicked?.Invoke(this, 207)));
_subMenuConfiguracao.Items.Add(CreateItem("⏰ Backups Automáticos", (s, e) => NavItemClicked?.Invoke(this, 207)));
_subMenuConfiguracao.Items.Add(CreateItem("☁️ Backup em Nuvem", (s, e) => NavItemClicked?.Invoke(this, 208)));
// ── AJUDA ─────────────────────────────────────────────────────────
_subMenuAjuda = CreateStyledMenu();
_subMenuAjuda.Items.Add(CreateItem("💬 Atendimento Online"));

View File

@ -6,16 +6,10 @@ using System.Windows.Forms;
namespace UI
{
/// <summary>
/// Modelo base para todos os formulários do sistema.
/// Herde esta classe e implemente os membros abstratos.
/// </summary>
public abstract class FormularioModelo : UserControl
{
// ── CONEXÃO ───────────────────────────────────────────────────────────
protected string _cx = DadosDaConexao.ObterConexao();
// ── CORES DO SISTEMA ──────────────────────────────────────────────────
protected readonly Color AccentBlue = Color.FromArgb(37, 99, 235);
protected readonly Color TextDark = Color.FromArgb(30, 41, 59);
protected readonly Color BorderColor = Color.FromArgb(226, 232, 240);
@ -23,31 +17,27 @@ namespace UI
protected readonly Color ReadOnlyBg = Color.FromArgb(241, 245, 249);
protected readonly Color ReadOnlyBorder = Color.FromArgb(203, 213, 225);
// ── PAINÉIS ESTRUTURAIS ───────────────────────────────────────────────
protected Panel pnlToolbar = null!;
protected Panel pnlTitulo = null!;
protected Panel mainScroll = null!;
protected Panel content = null!;
// ── BOTÕES DA TOOLBAR ─────────────────────────────────────────────────
protected Button btnNovo = null!;
protected Button btnAlterar = null!;
protected Button btnExcluir = null!;
protected Button btnLocalizar = null!;
protected Button btnSalvar = null!;
protected Button btnCancelar = null!;
protected Button btnFechar = null!; // ✅ NOVO
// ── TÍTULO ────────────────────────────────────────────────────────────
private Label lblTitulo = null!;
/// <summary>Título exibido no cabeçalho do formulário.</summary>
public string Titulo
{
get => lblTitulo.Text;
set => lblTitulo.Text = value;
}
// ── CONSTRUTOR ────────────────────────────────────────────────────────
protected FormularioModelo()
{
Dock = DockStyle.Fill;
@ -58,16 +48,24 @@ namespace UI
BuildTitulo();
BuildScrollArea();
// Liga os eventos abstratos
btnNovo.Click += (s, e) => OnNovo();
btnAlterar.Click += (s, e) => OnAlterar();
btnExcluir.Click += (s, e) => OnExcluir();
btnLocalizar.Click += (s, e) => OnLocalizar();
btnSalvar.Click += (s, e) => OnSalvar();
btnCancelar.Click += (s, e) => OnCancelar();
}
// ── CONSTRUÇÃO DA INTERFACE ───────────────────────────────────────────
// ✅ Fechar: remove o UserControl do pai
btnFechar.Click += (s, e) =>
{
var pai = this.Parent;
if (pai != null)
{
pai.Controls.Remove(this);
this.Dispose();
}
};
}
private void BuildToolbar()
{
@ -91,9 +89,10 @@ namespace UI
btnLocalizar = CreateToolbarButton("Localizar", AccentBlue);
btnSalvar = CreateToolbarButton("Salvar", AccentBlue);
btnCancelar = CreateToolbarButton("Cancelar", Color.FromArgb(148, 163, 184));
btnFechar = CreateToolbarButton("Fechar", Color.FromArgb(100, 116, 139)); // ✅ NOVO
flow.Controls.AddRange(new Control[]
{ btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar, btnCancelar });
{ btnNovo, btnAlterar, btnExcluir, btnLocalizar, btnSalvar, btnCancelar, btnFechar });
pnlToolbar.Controls.Add(flow);
this.Controls.Add(pnlToolbar);
@ -108,7 +107,6 @@ namespace UI
BackColor = Color.White
};
// Linha separadora no topo do título
var linha = new Panel
{
Dock = DockStyle.Top,
@ -153,9 +151,6 @@ namespace UI
mainScroll.BringToFront();
}
// ── HELPERS DISPONÍVEIS PARA AS SUBCLASSES ────────────────────────────
/// <summary>Cria um cabeçalho de seção com linha separadora.</summary>
protected Panel CreateSectionHeader(string title, int y)
{
var pnl = new Panel { Location = new Point(20, y), Width = 1000, Height = 26 };
@ -182,7 +177,6 @@ namespace UI
return pnl;
}
/// <summary>Cria um campo LV_TEXTBOX1 com label acima.</summary>
protected LV_TEXTBOX1 AddInput(Control parent, string label,
int x, int y, int width, int height,
bool readOnly = false)
@ -198,7 +192,7 @@ namespace UI
var txt = new LV_TEXTBOX1
{
Location = new Point(x, y + 16),
Location = new Point(x, y + 16), // ← input sempre 16px abaixo do label
Size = new Size(width, height),
BorderColor = readOnly ? ReadOnlyBorder : BorderColor,
BorderFocusColor = AccentBlue,
@ -211,7 +205,36 @@ namespace UI
return txt;
}
/// <summary>Cria um CheckBox padrão do sistema.</summary>
/// <summary>
/// Cria um label + ComboBox seguindo o mesmo padrão visual do AddInput.
/// ✅ Resolve o bug do label sendo coberto pelo ComboBox.
/// </summary>
protected ComboBox AddComboBox(Control parent, string label,
int x, int y, int width)
{
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 ComboBox
{
Location = new Point(x, y + 16), // ← mesmo padrão do AddInput
Size = new Size(width, 26),
DropDownStyle = ComboBoxStyle.DropDownList,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9f)
};
parent.Controls.Add(lbl);
parent.Controls.Add(cb);
return cb;
}
protected CheckBox CreateCheckBox(string text, int x, int y) => new CheckBox
{
Text = text,
@ -221,7 +244,6 @@ namespace UI
AutoSize = true
};
/// <summary>Cria um botão extra para a toolbar (ex: Ficha PDF, Certificado).</summary>
protected Button CreateToolbarButton(string text, Color color) => new Button
{
Text = text,
@ -235,15 +257,12 @@ namespace UI
FlatAppearance = { BorderSize = 0 }
};
/// <summary>Adiciona um botão extra na toolbar (após os padrões).</summary>
protected void AddToolbarButton(Button btn)
{
var flow = pnlToolbar.Controls[0] as FlowLayoutPanel;
flow?.Controls.Add(btn);
}
// ── EVENTOS ABSTRATOS (cada formulário implementa o seu) ──────────────
protected abstract void OnNovo();
protected abstract void OnAlterar();
protected abstract void OnExcluir();