LevelOS-Core/UI/Dashboards/Financeiro/DashboardFinanceiroPanel.cs

684 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Collections.Generic;
namespace UI
{
public partial class DashboardFinanceiroPanel : FormularioModelo
{
// ── Paleta ────────────────────────────────────────────────
private readonly Color C_Verde = Color.FromArgb(22, 163, 74);
private readonly Color C_Vermelho = Color.FromArgb(220, 38, 38);
private readonly Color C_Azul = Color.FromArgb(37, 99, 235);
private readonly Color C_Laranja = Color.FromArgb(234, 88, 12);
private readonly Color C_Roxo = Color.FromArgb(124, 58, 237);
private readonly Color C_Fundo = Color.FromArgb(248, 250, 252);
private readonly Color C_Borda = Color.FromArgb(226, 232, 240);
private readonly Color C_Texto = Color.FromArgb(30, 41, 59);
private readonly Color C_Cinza = Color.FromArgb(100, 116, 139);
// ── Helpers estáticos ─────────────────────────────────────
private static decimal R(decimal v) => v;
private static DateTime DH(int dias) => DateTime.Today.AddDays(dias);
// ── Dados simulados: Financeiro ───────────────────────────
private readonly decimal[] _vendasMes = { 48200, 53400, 41800, 62100, 57300, 68900, 72400, 65800, 71200, 84300, 79600, 91200 };
private readonly string[] _meses = { "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez" };
private readonly (string Cliente, decimal Valor, int DiasAtraso)[] _inadimplentes =
{
("Comércio Silva Ltda", R(3200), 15),
("Distribuidora ABC", R(8750), 32),
("Mercado São João", R(1450), 8),
("Atacado Norte S/A", R(12300), 47),
("Loja Central ME", R(980), 3),
};
private readonly (string Descricao, decimal Valor, DateTime Venc, bool APagar)[] _lancamentos =
{
("Fornecedor XYZ", R(4500), DH(3), true),
("Aluguel Loja", R(3200), DH(5), true),
("Receb. Cliente 001", R(7800), DH(2), false),
("Energia Elétrica", R(890), DH(8), true),
("Receb. Cliente 002", R(3200), DH(4), false),
("Folha de Pagamento", R(18000), DH(10), true),
("Receb. Cliente 003", R(5500), DH(1), false),
("Manutenção Equipamentos", R(1200), DH(6), true),
};
// ── Dados simulados: Ordens de Serviço ───────────────────
private readonly (string OS, string Cliente, string Responsavel, decimal Valor, string Status, DateTime Prazo)[] _ordensServico =
{
("OS-0041", "Comércio Silva Ltda", "Carlos", R(1200), "Em andamento", DH(2)),
("OS-0042", "Distribuidora ABC", "Marcos", R(3500), "Aberta", DH(0)),
("OS-0043", "Mercado São João", "Carlos", R(800), "Atrasada", DH(-3)),
("OS-0044", "Atacado Norte S/A", "Julia", R(5200), "Concluída", DH(-1)),
("OS-0045", "Loja Central ME", "Marcos", R(950), "Atrasada", DH(-7)),
("OS-0046", "Fornecedor XYZ", "Julia", R(2100), "Em andamento", DH(5)),
("OS-0047", "Cliente Novo Ltda", "Carlos", R(1750), "Concluída", DH(-2)),
("OS-0048", "Empresa Teste S/A", "Marcos", R(4300), "Aberta", DH(4)),
};
// ─────────────────────────────────────────────────────────
public DashboardFinanceiroPanel()
{
Titulo = "Dashboard Financeiro";
MontarDashboard();
}
private void MontarDashboard()
{
content.Controls.Clear();
content.BackColor = C_Fundo;
content.Width = 1100;
int y = 16;
y = AdicionarFiltro(y);
y = AdicionarKPIs(y + 10);
y = AdicionarGraficoEFluxo(y + 14);
y = AdicionarTabelasInferiores(y + 14);
y = AdicionarBlocoOS(y + 14);
content.Height = y + 24;
}
// ══════════════════════════════════════════════════════════
// FILTRO
// ══════════════════════════════════════════════════════════
private int AdicionarFiltro(int y)
{
var pnl = new Panel
{
Location = new Point(16, y),
Size = new Size(1068, 44),
BackColor = Color.White
};
pnl.Paint += PaintCard;
var lblDe = Lbl("Período:", 14, 14, bold: true); lblDe.ForeColor = C_Cinza;
var dtDe = new DateTimePicker { Location = new Point(74, 10), Width = 120, Format = DateTimePickerFormat.Short, Value = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1) };
var lblAte = Lbl("até", 204, 14); lblAte.ForeColor = C_Cinza;
var dtAte = new DateTimePicker { Location = new Point(228, 10), Width = 120, Format = DateTimePickerFormat.Short, Value = DateTime.Today };
var btnAtualizar = new Button
{
Text = "⟳ Atualizar",
Location = new Point(368, 8),
Size = new Size(110, 28),
FlatStyle = FlatStyle.Flat,
BackColor = C_Azul,
ForeColor = Color.White,
Font = new Font("Segoe UI Semibold", 8.5f),
Cursor = Cursors.Hand
};
btnAtualizar.FlatAppearance.BorderSize = 0;
pnl.Controls.AddRange(new Control[] { lblDe, dtDe, lblAte, dtAte, btnAtualizar });
string[] periodos = { "Mês Atual", "Trimestre", "Ano" };
int bx = 500;
foreach (var p in periodos)
{
var b = new Button
{
Text = p,
Location = new Point(bx, 8),
Size = new Size(88, 28),
FlatStyle = FlatStyle.Flat,
BackColor = C_Fundo,
ForeColor = C_Texto,
Font = new Font("Segoe UI", 8.5f),
Cursor = Cursors.Hand
};
b.FlatAppearance.BorderColor = C_Borda;
b.FlatAppearance.BorderSize = 1;
pnl.Controls.Add(b);
bx += 96;
}
content.Controls.Add(pnl);
return y + pnl.Height;
}
// ══════════════════════════════════════════════════════════
// KPI CARDS — FINANCEIRO
// ══════════════════════════════════════════════════════════
private int AdicionarKPIs(int y)
{
var dados = new[]
{
("Vendas do Dia", "R$ 4.250,00", "↑ 12% vs ontem", C_Verde),
("Vendas do Mês", "R$ 91.200,00", "↑ 8% vs mês anterior", C_Azul),
("Contas a Receber", "R$ 38.600,00", "12 títulos em aberto", C_Verde),
("Contas a Pagar", "R$ 27.790,00", "8 vencimentos próximos", C_Vermelho),
("Inadimplência", "R$ 26.680,00", "5 clientes em atraso", C_Laranja),
};
int cardW = 200, gap = 13, x = 16;
foreach (var (titulo, valor, sub, cor) in dados)
{
var card = new Panel { Location = new Point(x, y), Size = new Size(cardW, 90), BackColor = Color.White };
card.Paint += PaintCard;
var acento = new Panel { Dock = DockStyle.Top, Height = 4, BackColor = cor };
var lblT = Lbl(titulo, 14, 14); lblT.ForeColor = C_Cinza; lblT.Font = new Font("Segoe UI", 8f);
var lblV = Lbl(valor, 14, 30); lblV.Font = new Font("Segoe UI", 13f, FontStyle.Bold); lblV.ForeColor = C_Texto;
var lblS = Lbl(sub, 14, 62); lblS.ForeColor = cor; lblS.Font = new Font("Segoe UI", 7.5f);
card.Controls.AddRange(new Control[] { acento, lblT, lblV, lblS });
content.Controls.Add(card);
x += cardW + gap;
}
return y + 90;
}
// ══════════════════════════════════════════════════════════
// GRÁFICO DE BARRAS + FLUXO DE CAIXA
// ══════════════════════════════════════════════════════════
private int AdicionarGraficoEFluxo(int y)
{
const int alturaBloco = 300;
var pnlGrafico = new Panel { Location = new Point(16, y), Size = new Size(680, alturaBloco), BackColor = Color.White };
pnlGrafico.Paint += (s, e) => { PaintCard(s, e); DesenharGraficoBarras(e.Graphics, pnlGrafico.ClientRectangle); };
content.Controls.Add(pnlGrafico);
var pnlFluxo = new Panel { Location = new Point(704, y), Size = new Size(380, alturaBloco), BackColor = Color.White };
pnlFluxo.Paint += PaintCard;
PreencherFluxoCaixa(pnlFluxo);
content.Controls.Add(pnlFluxo);
return y + alturaBloco;
}
private void DesenharGraficoBarras(Graphics g, Rectangle area)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
var fTitulo = new Font("Segoe UI", 10f, FontStyle.Bold);
var fLabel = new Font("Segoe UI", 7f);
var fValor = new Font("Segoe UI", 6.5f);
g.DrawString("Vendas por Mês (R$)", fTitulo, new SolidBrush(C_Texto), new Point(16, 14));
int padL = 50, padR = 16, padT = 44, padB = 40;
int chartW = area.Width - padL - padR;
int chartH = area.Height - padT - padB;
decimal maxVal = 0;
foreach (var v in _vendasMes) if (v > maxVal) maxVal = v;
maxVal = Math.Ceiling(maxVal / 10000m) * 10000m;
int linhas = 5;
using var penGrade = new Pen(Color.FromArgb(235, 239, 244), 1f);
for (int i = 0; i <= linhas; i++)
{
int gy = padT + (int)(chartH * i / linhas);
g.DrawLine(penGrade, padL, gy, padL + chartW, gy);
decimal rotulo = maxVal * (linhas - i) / linhas;
g.DrawString("R$" + (rotulo / 1000m).ToString("0k"), fLabel, new SolidBrush(C_Cinza), new PointF(2, gy - 7));
}
int n = _vendasMes.Length;
float barW = (float)chartW / n * 0.55f;
float slotW = (float)chartW / n;
for (int i = 0; i < n; i++)
{
float barH = (float)(_vendasMes[i] / maxVal) * chartH;
float bx = padL + i * slotW + (slotW - barW) / 2f;
float by = padT + chartH - barH;
var rect = new RectangleF(bx, by, barW, barH);
using var brush = new LinearGradientBrush(rect, Color.FromArgb(99, 132, 255), C_Azul, LinearGradientMode.Vertical);
g.FillRectangle(brush, rect);
g.DrawString(_meses[i], fLabel, new SolidBrush(C_Cinza), new PointF(bx + barW / 2f - 8, padT + chartH + 6));
string val = "R$" + (_vendasMes[i] / 1000m).ToString("0.0k");
g.DrawString(val, fValor, new SolidBrush(C_Azul), new PointF(bx - 2, by - 14));
}
using var penEixo = new Pen(C_Borda, 1f);
g.DrawLine(penEixo, padL, padT, padL, padT + chartH);
}
private void PreencherFluxoCaixa(Panel pnl)
{
pnl.Controls.Add(TituloSecao("Fluxo de Caixa — " + DateTime.Today.ToString("MMM/yyyy"), 16, 14));
var itens = new[]
{
("Saldo Inicial", "R$ 45.000,00", C_Texto),
("(+) Entradas", "R$ 38.600,00", C_Verde),
("(-) Saídas", "R$ 27.790,00", C_Vermelho),
("(=) Saldo Final", "R$ 55.810,00", C_Azul),
};
int yi = 50;
foreach (var (desc, val, cor) in itens)
{
bool isFinal = desc.StartsWith("(=)");
var row = new Panel
{
Location = new Point(16, yi),
Size = new Size(348, isFinal ? 44 : 36),
BackColor = isFinal ? Color.FromArgb(239, 246, 255) : Color.Transparent
};
if (isFinal) row.Paint += PaintCard;
var lD = Lbl(desc, 10, isFinal ? 13 : 10);
lD.Font = new Font("Segoe UI", isFinal ? 9f : 8.5f, isFinal ? FontStyle.Bold : FontStyle.Regular);
lD.ForeColor = C_Texto;
var lV = new Label
{
Text = val,
AutoSize = true,
Location = new Point(220, isFinal ? 13 : 10),
Font = new Font("Segoe UI", isFinal ? 9f : 8.5f, FontStyle.Bold),
ForeColor = cor
};
row.Controls.AddRange(new Control[] { lD, lV });
pnl.Controls.Add(row);
yi += isFinal ? 50 : 38;
}
var linha = new Panel { Location = new Point(16, yi - 55), Size = new Size(348, 1), BackColor = C_Borda };
pnl.Controls.Add(linha);
yi += 10;
pnl.Controls.Add(TituloSecao("Indicadores do Mês", 16, yi)); yi += 28;
var indicadores = new[]
{
("Ticket Médio", "R$ 354,17", C_Azul),
("Margem Líquida", "23,4%", C_Verde),
("Giro de Caixa", "8,2×", C_Roxo),
};
foreach (var (ind, vl, cor) in indicadores)
{
var chip = new Panel { Location = new Point(16, yi), Size = new Size(348, 32), BackColor = Color.Transparent };
var lI = Lbl(ind, 0, 8); lI.ForeColor = C_Cinza; lI.Font = new Font("Segoe UI", 8f);
var lV2 = new Label { Text = vl, AutoSize = true, Location = new Point(240, 8), Font = new Font("Segoe UI Semibold", 8.5f), ForeColor = cor };
chip.Controls.AddRange(new Control[] { lI, lV2 });
pnl.Controls.Add(chip);
yi += 32;
}
}
// ══════════════════════════════════════════════════════════
// CONTAS A PAGAR/RECEBER + INADIMPLÊNCIA
// ══════════════════════════════════════════════════════════
private int AdicionarTabelasInferiores(int y)
{
const int alturaBloco = 320;
var pnlContas = new Panel { Location = new Point(16, y), Size = new Size(540, alturaBloco), BackColor = Color.White };
pnlContas.Paint += PaintCard;
PreencherContas(pnlContas);
content.Controls.Add(pnlContas);
var pnlInad = new Panel { Location = new Point(564, y), Size = new Size(520, alturaBloco), BackColor = Color.White };
pnlInad.Paint += PaintCard;
PreencherInadimplencia(pnlInad);
content.Controls.Add(pnlInad);
return y + alturaBloco;
}
private void PreencherContas(Panel pnl)
{
pnl.Controls.Add(TituloSecao("Contas a Pagar / Receber — Próximos Vencimentos", 14, 14));
pnl.Controls.Add(CriarLinhaTabela(
("Descrição", 180), ("Valor", 100), ("Venc.", 80), ("Tipo", 70), ("Status", 80),
isHeader: true, y: 44));
int yi = 72;
foreach (var (desc, valor, venc, aPagar) in _lancamentos)
{
int dias = (venc - DateTime.Today).Days;
string status = dias < 0 ? "Vencido" : dias == 0 ? "Hoje" : $"{dias}d";
Color corStatus = dias < 0 ? C_Vermelho : dias <= 3 ? C_Laranja : C_Verde;
pnl.Controls.Add(CriarLinhaTabela(
(desc, 180),
("R$ " + valor.ToString("N2"), 100),
(venc.ToString("dd/MM"), 80),
(aPagar ? "Pagar" : "Receber", 70),
(status, 80),
isHeader: false, y: yi,
corColuna4: aPagar ? C_Vermelho : C_Verde,
corColuna5: corStatus));
yi += 28;
}
}
private void PreencherInadimplencia(Panel pnl)
{
pnl.Controls.Add(TituloSecao("Inadimplência — Clientes em Atraso", 14, 14));
pnl.Controls.Add(CriarLinhaTabela(
("Cliente", 200), ("Valor", 100), ("Dias Atraso", 90), ("Situação", 100),
isHeader: true, y: 44));
int yi = 72;
foreach (var (cliente, valor, dias) in _inadimplentes)
{
string situacao = dias <= 15 ? "Em negociação" : dias <= 30 ? "Notificado" : "Jurídico";
Color corSit = dias <= 15 ? C_Laranja : dias <= 30 ? C_Vermelho : Color.FromArgb(127, 29, 29);
pnl.Controls.Add(CriarLinhaTabela(
(cliente, 200),
("R$ " + valor.ToString("N2"), 100),
(dias + " dias", 90),
(situacao, 100),
isHeader: false, y: yi,
corColuna4: corSit));
yi += 28;
}
decimal totalInad = 0;
foreach (var i in _inadimplentes) totalInad += i.Valor;
var tot = new Panel { Location = new Point(14, yi + 6), Size = new Size(490, 32), BackColor = Color.FromArgb(255, 241, 242) };
tot.Paint += PaintCard;
var lT = Lbl("Total inadimplente:", 10, 8); lT.Font = new Font("Segoe UI Semibold", 8.5f);
var lV = new Label { Text = "R$ " + totalInad.ToString("N2"), AutoSize = true, Location = new Point(310, 8), Font = new Font("Segoe UI", 9f, FontStyle.Bold), ForeColor = C_Vermelho };
tot.Controls.AddRange(new Control[] { lT, lV });
pnl.Controls.Add(tot);
}
// ══════════════════════════════════════════════════════════
// ORDENS DE SERVIÇO
// ══════════════════════════════════════════════════════════
private int AdicionarBlocoOS(int y)
{
const int alturaBloco = 370;
y = AdicionarKPIsOS(y);
var pnlTabela = new Panel { Location = new Point(16, y + 10), Size = new Size(690, alturaBloco), BackColor = Color.White };
pnlTabela.Paint += PaintCard;
PreencherTabelaOS(pnlTabela);
content.Controls.Add(pnlTabela);
var pnlRanking = new Panel { Location = new Point(714, y + 10), Size = new Size(370, alturaBloco), BackColor = Color.White };
pnlRanking.Paint += PaintCard;
PreencherRankingTecnicos(pnlRanking);
content.Controls.Add(pnlRanking);
return y + 10 + alturaBloco;
}
private int AdicionarKPIsOS(int y)
{
int abertas = 0, andamento = 0, atrasadas = 0, concluidas = 0;
decimal totalValor = 0;
foreach (var os in _ordensServico)
{
totalValor += os.Valor;
switch (os.Status)
{
case "Aberta": abertas++; break;
case "Em andamento": andamento++; break;
case "Atrasada": atrasadas++; break;
case "Concluída": concluidas++; break;
}
}
var kpis = new[]
{
("OS Abertas", abertas.ToString(), "Aguardando início", C_Azul),
("Em Andamento", andamento.ToString(), "Em execução agora", C_Roxo),
("Atrasadas", atrasadas.ToString(), "⚠ Requerem atenção", C_Vermelho),
("Concluídas no Mês", concluidas.ToString(), "Finalizadas este mês", C_Verde),
("Valor Total OS", "R$ " + totalValor.ToString("N2"), "Misto: cobrado + interno", C_Laranja),
};
int cardW = 200, gap = 13, x = 16;
foreach (var (titulo, valor, sub, cor) in kpis)
{
var card = new Panel { Location = new Point(x, y), Size = new Size(cardW, 80), BackColor = Color.White };
card.Paint += PaintCard;
var acento = new Panel { Dock = DockStyle.Top, Height = 4, BackColor = cor };
var lblT = Lbl(titulo, 14, 12); lblT.ForeColor = C_Cinza; lblT.Font = new Font("Segoe UI", 8f);
var lblV = Lbl(valor, 14, 28); lblV.Font = new Font("Segoe UI", 13f, FontStyle.Bold); lblV.ForeColor = C_Texto;
var lblS = Lbl(sub, 14, 56); lblS.ForeColor = cor; lblS.Font = new Font("Segoe UI", 7.5f);
card.Controls.AddRange(new Control[] { acento, lblT, lblV, lblS });
content.Controls.Add(card);
x += cardW + gap;
}
return y + 80;
}
private void PreencherTabelaOS(Panel pnl)
{
pnl.Controls.Add(TituloSecao("Ordens de Serviço — Visão Geral", 14, 14));
pnl.Controls.Add(CriarLinhaOS(
("Nº OS", 65), ("Cliente", 160), ("Responsável", 100),
("Valor", 90), ("Prazo", 90), ("Status", 90),
isHeader: true, y: 44));
int yi = 72;
foreach (var (os, cliente, resp, valor, status, prazo) in _ordensServico)
{
int dias = (prazo - DateTime.Today).Days;
string prazoTxt = dias < 0 ? $"{Math.Abs(dias)}d atraso" : dias == 0 ? "Hoje" : $"{dias}d restantes";
Color corPrazo = dias < 0 ? C_Vermelho : dias <= 2 ? C_Laranja : C_Verde;
Color corStatus = status switch
{
"Concluída" => C_Verde,
"Em andamento" => C_Azul,
"Atrasada" => C_Vermelho,
_ => C_Cinza
};
pnl.Controls.Add(CriarLinhaOS(
(os, 65), (cliente, 160), (resp, 100),
("R$ " + valor.ToString("N2"), 90), (prazoTxt, 90), (status, 90),
isHeader: false, y: yi,
corColuna5: corPrazo, corColuna6: corStatus));
yi += 28;
}
decimal total = 0;
foreach (var o in _ordensServico) total += o.Valor;
var tot = new Panel { Location = new Point(14, yi + 6), Size = new Size(660, 32), BackColor = Color.FromArgb(241, 245, 249) };
tot.Paint += PaintCard;
var lT = Lbl("Total em OS:", 10, 8); lT.Font = new Font("Segoe UI Semibold", 8.5f);
var lV = new Label { Text = "R$ " + total.ToString("N2"), AutoSize = true, Location = new Point(540, 8), Font = new Font("Segoe UI", 9f, FontStyle.Bold), ForeColor = C_Azul };
tot.Controls.AddRange(new Control[] { lT, lV });
pnl.Controls.Add(tot);
}
private void PreencherRankingTecnicos(Panel pnl)
{
pnl.Controls.Add(TituloSecao("Ranking de Responsáveis", 14, 14));
var dic = new Dictionary<string, (int total, int concluidas, int atrasadas, decimal valor)>();
foreach (var os in _ordensServico)
{
if (!dic.ContainsKey(os.Responsavel))
dic[os.Responsavel] = (0, 0, 0, 0);
var cur = dic[os.Responsavel];
dic[os.Responsavel] = (
cur.total + 1,
cur.concluidas + (os.Status == "Concluída" ? 1 : 0),
cur.atrasadas + (os.Status == "Atrasada" ? 1 : 0),
cur.valor + os.Valor
);
}
int yi = 48, pos = 1;
foreach (var kv in dic)
{
var (total, conc, atras, valor) = kv.Value;
float pct = total > 0 ? (float)conc / total : 0f;
var card = new Panel { Location = new Point(14, yi), Size = new Size(342, 88), BackColor = C_Fundo };
card.Paint += PaintCard;
var lblPos = new Label
{
Text = $"#{pos}",
Location = new Point(10, 10),
Size = new Size(30, 30),
Font = new Font("Segoe UI", 11f, FontStyle.Bold),
ForeColor = pos == 1 ? Color.FromArgb(202, 138, 4) : C_Cinza,
TextAlign = ContentAlignment.MiddleCenter
};
var lblNome = Lbl(kv.Key, 48, 10); lblNome.Font = new Font("Segoe UI Semibold", 9.5f);
var lblTotal = Lbl($"{total} OS • R$ {valor:N2}", 48, 28); lblTotal.ForeColor = C_Cinza; lblTotal.Font = new Font("Segoe UI", 8f);
var lblConc = Lbl($"✔ {conc} concluída(s)", 48, 46); lblConc.ForeColor = C_Verde; lblConc.Font = new Font("Segoe UI", 7.5f);
var lblAtras = Lbl($"⚠ {atras} atrasada(s)", 170, 46); lblAtras.ForeColor = C_Vermelho; lblAtras.Font = new Font("Segoe UI", 7.5f);
var barBg = new Panel { Location = new Point(48, 66), Size = new Size(284, 8), BackColor = C_Borda };
var barFill = new Panel { Location = new Point(0, 0), Size = new Size((int)(284 * pct), 8), BackColor = pct >= 0.7f ? C_Verde : pct >= 0.4f ? C_Laranja : C_Vermelho };
barBg.Controls.Add(barFill);
var lblPct = Lbl($"{pct:P0}", 336, 60); lblPct.Font = new Font("Segoe UI", 7f); lblPct.ForeColor = C_Cinza;
card.Controls.AddRange(new Control[] { lblPos, lblNome, lblTotal, lblConc, lblAtras, barBg, lblPct });
pnl.Controls.Add(card);
yi += 98;
pos++;
}
}
// ══════════════════════════════════════════════════════════
// HELPERS DE LAYOUT
// ══════════════════════════════════════════════════════════
private Panel CriarLinhaTabela(
(string texto, int largura) c1,
(string texto, int largura) c2,
(string texto, int largura) c3,
(string texto, int largura) c4,
(string texto, int largura) c5 = default,
bool isHeader = false, int y = 0,
Color? corColuna4 = null,
Color? corColuna5 = null)
{
var row = new Panel
{
Location = new Point(14, y),
Size = new Size(510, isHeader ? 24 : 26),
BackColor = isHeader ? Color.FromArgb(241, 245, 249) : Color.Transparent
};
var cols = new[] { c1, c2, c3, c4, c5 };
Color[] cores = { C_Texto, C_Texto, C_Texto, corColuna4 ?? C_Texto, corColuna5 ?? C_Texto };
int x = 0;
for (int i = 0; i < cols.Length; i++)
{
if (cols[i].largura == 0) continue;
var lbl = new Label
{
Text = cols[i].texto,
Location = new Point(x + 4, isHeader ? 5 : 6),
Size = new Size(cols[i].largura, 18),
Font = new Font("Segoe UI", isHeader ? 7.5f : 8f, isHeader ? FontStyle.Bold : FontStyle.Regular),
ForeColor = isHeader ? C_Cinza : cores[i]
};
row.Controls.Add(lbl);
x += cols[i].largura;
}
if (!isHeader)
row.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Color.FromArgb(241, 245, 249)), 0, row.Height - 1, row.Width, row.Height - 1);
return row;
}
private Panel CriarLinhaOS(
(string texto, int largura) c1,
(string texto, int largura) c2,
(string texto, int largura) c3,
(string texto, int largura) c4,
(string texto, int largura) c5,
(string texto, int largura) c6,
bool isHeader = false, int y = 0,
Color? corColuna5 = null,
Color? corColuna6 = null)
{
var row = new Panel
{
Location = new Point(14, y),
Size = new Size(660, isHeader ? 24 : 26),
BackColor = isHeader ? Color.FromArgb(241, 245, 249) : Color.Transparent
};
var cols = new[] { c1, c2, c3, c4, c5, c6 };
Color[] cores = { C_Texto, C_Texto, C_Texto, C_Texto, corColuna5 ?? C_Texto, corColuna6 ?? C_Texto };
int x = 0;
for (int i = 0; i < cols.Length; i++)
{
var lbl = new Label
{
Text = cols[i].texto,
Location = new Point(x + 4, isHeader ? 5 : 6),
Size = new Size(cols[i].largura, 18),
Font = new Font("Segoe UI", isHeader ? 7.5f : 8f, isHeader ? FontStyle.Bold : FontStyle.Regular),
ForeColor = isHeader ? C_Cinza : cores[i]
};
row.Controls.Add(lbl);
x += cols[i].largura;
}
if (!isHeader)
row.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Color.FromArgb(241, 245, 249)), 0, row.Height - 1, row.Width, row.Height - 1);
return row;
}
private Label TituloSecao(string texto, int x, int y) => new Label
{
Text = texto,
Location = new Point(x, y),
AutoSize = true,
Font = new Font("Segoe UI Semibold", 9f),
ForeColor = C_Texto
};
private Label Lbl(string texto, int x, int y, bool bold = false) => new Label
{
Text = texto,
Location = new Point(x, y),
AutoSize = true,
Font = new Font("Segoe UI", 8.5f, bold ? FontStyle.Bold : FontStyle.Regular),
ForeColor = C_Texto
};
private void PaintCard(object? sender, PaintEventArgs e)
{
if (sender is not Control c) return;
using var pen = new Pen(C_Borda);
e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, c.Width - 1, c.Height - 1));
}
// ══════════════════════════════════════════════════════════
// MÉTODOS OBRIGATÓRIOS
// ══════════════════════════════════════════════════════════
protected override void OnNovo() { }
protected override void OnSalvar() { }
protected override void OnAlterar() { }
protected override void OnCancelar() { }
protected override void OnLocalizar() { }
protected override void OnExcluir() { }
}
}