684 lines
32 KiB
C#
684 lines
32 KiB
C#
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() { }
|
||
}
|
||
} |