Refatoração para modularidade e melhorias visuais

- Código refatorado para maior modularidade e legibilidade, com criação de métodos auxiliares como `ExibirCabecalho`, `CriarTabelaLigacoes` e outros.
- Substituição de código inline por métodos reutilizáveis, reduzindo duplicação e centralizando lógica.
- Melhorias na exibição de dados com tabelas e painéis mais organizados.
- Lógica de consulta e contagem de registros encapsulada em métodos dedicados (`ObterRegistrosUsuario`, `ContarRegistros`).
- Introdução de formatação condicional para contagens e valores numéricos.
- Uso de LINQ para simplificar manipulação de coleções.
- Remoção de código redundante, dependências desnecessárias e variáveis globais fixas.
- Mensagens de erro mais informativas e consistentes.
- Adição de comentários explicativos e organização com `#region Helpers`.
- Funcionalidade principal mantida, com melhorias na experiência do usuário.
This commit is contained in:
Giuliano Paschoalino 2025-07-21 17:41:49 -03:00
parent ad6bd1f479
commit 5e5618dd6d

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Common;
using System.Numerics;
using System.Threading.Tasks;
using Npgsql;
using Spectre.Console;
@ -13,6 +10,7 @@ class Program
static bool _isAuthenticated = false;
static readonly string _connectionString = "Server=192.168.10.248;Port=5432;Database=pipefy_move_cards;User Id=postgres;Password=gds21;";
static readonly string _windowsID = Environment.UserName;
//static readonly string _windowsID = "comercial6";
static readonly List<(string actionId, string actionName)> _actionIds = // Tuplas de IDs e nomes das fases que queremos monitorar
[
new ("318642783-318906957", "Agendou reunião 1"),
@ -88,122 +86,35 @@ class Program
public string? AcaoID { get; set; }
public required string Acao { get; set; }
}
static void AtualizarDados()
{
var records = new List<Record>();
AnsiConsole.Clear();
// Painel de cabeçalho que mostra quantos usuários foram carregados
ExibirCabecalho();
var status = new Align(new Panel($"[bold]WindowsID[/]: {_windowsID} | [bold]Usuários encontrados[/]: {_pipeUsers.Count}"), HorizontalAlignment.Center, VerticalAlignment.Top);
var header = new Panel(status)
.Header("[yellow]Resumo Diário por Usuário[/]",Justify.Center)
.Expand();
AnsiConsole.Write(new Align(header, HorizontalAlignment.Center, VerticalAlignment.Top));
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
var tableContLigacoes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
tableContLigacoes.Border(TableBorder.Rounded);
tableContLigacoes.AddColumn(new TableColumn("[green]Nome[/]").Centered());
tableContLigacoes.AddColumn(new TableColumn("[green]UserID[/]").Centered());
tableContLigacoes.AddColumn(new TableColumn("[green]Registros Hoje[/]").Centered());
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
var tableContReunioes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
tableContReunioes.Border(TableBorder.Rounded);
tableContReunioes.AddColumn(new TableColumn("[green]Nome[/]").Centered());
tableContReunioes.AddColumn(new TableColumn("[green]UserID[/]").Centered());
tableContReunioes.AddColumn(new TableColumn("[green]Agendadas[/]").Centered());
tableContReunioes.AddColumn(new TableColumn("[green]Realizadas[/]").Centered());
tableContReunioes.AddColumn(new TableColumn("[green]Canceladas[/]").Centered());
var tableContLigacoes = CriarTabelaLigacoes();
var tableContReunioes = CriarTabelaReunioes();
// Para cada usuário, rodar uma query COUNT(*) e adicionar linha na tabela
foreach (var (userId, nome) in _pipeUsers)
{
var userRecords = new List<Record>();
int count = 0;
int countR = 0;
int countA = 0;
int countC = 0;
AnsiConsole.Status()
.Spinner(Spinner.Known.Line)
.Start($"[blue]Obtendo registros de [yellow]{nome}[/]...[/]", ctx =>
{
const string query = @"
SELECT * FROM public.""ActionsHistory""
WHERE ""UserID"" = @pipeUser AND
""MovedAt"" >= date_trunc('month', CURRENT_DATE)";
try
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(query, conn);
cmd.Parameters.AddWithValue("@pipeUser", userId);
var reader = cmd.ExecuteReader();
using (reader)
{
while (reader.Read())
{
var temp = new Record
{
Id = ToIntOrNull(reader, "Id"),
Action = ToStringOrNull(reader, "Action"),
User = _pipeUsers.Find(u => u.UserId == ToIntOrNull(reader, "UserID")).Nome,
FieldID = ToStringOrNull(reader, "FieldID"),
From = ToIntOrNull(reader, "From"),
To = ToIntOrNull(reader, "To"),
CardID = ToIntOrNull(reader, "CardID"),
PipeSUID = ToStringOrNull(reader, "PipeSUID"),
MovedAt = (DateTime)(reader["MovedAt"] ?? DateTime.MinValue),
Title = ToStringOrNull(reader, "Title") ?? "-",
Acao = "Desconhecida"
};
temp.AcaoID = temp.From + "-" + temp.To;
temp.Acao = _actionIds.Find(p => p.actionId == temp.AcaoID).actionName;
var userRecords = ObterRegistrosUsuario(userId, nome);
var (count, countA, countR, countC) = ContarRegistros(userRecords);
userRecords.Add(temp);
}
}
}
catch (Exception ex)
{
// Em caso de erro, exibimos zero e continuamos
count = 0;
AnsiConsole.MarkupLine($"\n[red]Erro ao consultar {nome}:[/] {ex.Message}");
}
});
count = userRecords.Where(x => (x.MovedAt > DateTime.Today && x.FieldID is not null)).ToList().Count;
countR = userRecords.Where(x => ((x.Acao ?? "").StartsWith("Realizou"))).ToList().Count;
countA = userRecords.Where(x => ((x.Acao ?? "").StartsWith("Agendou"))).ToList().Count;
countC = userRecords.Where(x => ((x.Acao ?? "").EndsWith("cancelada"))).ToList().Count;
tableContLigacoes.AddRow(nome, userId.ToString(), $"[bold yellow]{count}[/]");
tableContReunioes.AddRow(nome, userId.ToString(), $"[bold yellow]{countA}[/]", $"[bold yellow]{countR}[/]", $"[bold yellow]{countC}[/]");
var format = ObterFormatoCor(count);
tableContLigacoes.AddRow(nome, userId.ToString(), $"{format} {count} [/]");
tableContReunioes.AddRow(nome, userId.ToString(), $"[yellow] {countA:#;-#;} [/]", $"[green] {countR:#;-#;} [/]", $"[red] {countC:#;-#;} [/]");
records.AddRange(userRecords);
}
var panel = new Panel(new Align(tableContLigacoes, HorizontalAlignment.Center, VerticalAlignment.Top))
.Header("[blue bold] Ligações [/]", Justify.Center)
.Border(BoxBorder.Double)
.Expand();
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
ExibirPainel(tableContLigacoes, "[blue bold] Ligações [/]");
ExibirPainel(tableContReunioes, "[blue bold] Reuniões Agendadas e Realizadas [/]");
panel = new Panel(new Align(tableContReunioes, HorizontalAlignment.Center, VerticalAlignment.Top))
.Header("[blue bold] Reuniões Agendadas e Realizadas [/]", Justify.Center)
.Border(BoxBorder.Double)
.Expand();
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
// Montamos uma tabela com: Nome do usuário | UserID | Registros Hoje
var tableReunioes = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
tableReunioes.AddColumn(new TableColumn("[green]Data[/]").Centered());
tableReunioes.AddColumn(new TableColumn("[green]Status[/]").Centered());
tableReunioes.AddColumn(new TableColumn("[green]Empresa[/]").Centered());
tableReunioes.AddColumn(new TableColumn("[green]Card ID\n(Ctrl + click)[/]").Centered());
tableReunioes.AddColumn(new TableColumn("[green]Prospectante[/]").Centered());
records = [.. records.OrderBy(x => x.MovedAt.Date).OrderBy(x => x.Acao).OrderBy(y => y.User)];
// Para cada usuário, rodar uma query COUNT(*) e adicionar linha na tabela
foreach (var record in records)
var tableReunioes = CriarTabelaDetalhesReunioes();
foreach (var record in records.OrderBy(x => x.User).ThenBy(x => x.Acao).ThenBy(x => x.MovedAt.Date))
{
if (record.Acao is not null)
{
@ -216,15 +127,132 @@ class Program
);
}
}
panel = new Panel(new Align(tableReunioes, HorizontalAlignment.Center, VerticalAlignment.Top))
.Header("[blue bold] Reuniões no Mês [/]", Justify.Center)
.Border(BoxBorder.Double)
.Expand();
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
ExibirPainel(tableReunioes, "[blue bold] Reuniões no Mês [/]");
AnsiConsole.MarkupLine("\n[gray]Pressione [green]ENTER[/] para atualizar agora ou qualquer outra tecla para sair.[/]");
}
static void ExibirCabecalho()
{
// Painel de cabeçalho que mostra quantos usuários foram carregados
var status = new Align(new Panel($"[bold]WindowsID[/]: {_windowsID} | [bold]Usuários encontrados[/]: {_pipeUsers.Count}"), HorizontalAlignment.Center, VerticalAlignment.Top);
var header = new Panel(status)
.Header("[yellow]Resumo Diário por Usuário[/]", Justify.Center)
.Expand();
AnsiConsole.Write(new Align(header, HorizontalAlignment.Center, VerticalAlignment.Top));
}
static Table CriarTabelaLigacoes()
{
var table = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
table.AddColumn(new TableColumn("[green]Nome[/]").Centered());
table.AddColumn(new TableColumn("[green]UserID[/]").Centered());
table.AddColumn(new TableColumn("[green]Registros Hoje[/]").Centered());
return table;
}
static Table CriarTabelaReunioes()
{
var table = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
table.AddColumn(new TableColumn("[white]Nome[/]").Centered());
table.AddColumn(new TableColumn("[white]UserID[/]").Centered());
table.AddColumn(new TableColumn("[yellow]Agendadas[/]").Centered());
table.AddColumn(new TableColumn("[green]Realizadas[/]").Centered());
table.AddColumn(new TableColumn("[red]Canceladas[/]").Centered());
return table;
}
static Table CriarTabelaDetalhesReunioes()
{
var table = new Table().Border(TableBorder.Rounded).Alignment(Justify.Center);
table.AddColumn(new TableColumn("[green]Data[/]").Centered());
table.AddColumn(new TableColumn("[green]Status[/]").Centered());
table.AddColumn(new TableColumn("[green]Empresa[/]").Centered());
table.AddColumn(new TableColumn("[green]Card ID\n(Ctrl + click)[/]").Centered());
table.AddColumn(new TableColumn("[green]Prospectante[/]").Centered());
return table;
}
static void ExibirPainel(Table table, string titulo)
{
var panel = new Panel(new Align(table, HorizontalAlignment.Center, VerticalAlignment.Top))
.Header(titulo, Justify.Center)
.Border(BoxBorder.Double)
.Expand();
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
}
static List<Record> ObterRegistrosUsuario(BigInteger userId, string nome)
{
var userRecords = new List<Record>();
AnsiConsole.Status()
.Spinner(Spinner.Known.Line)
.Start($"[blue]Obtendo registros de [yellow]{nome}[/]...[/]", ctx =>
{
const string query = @"
SELECT * FROM public.""ActionsHistory""
WHERE ""UserID"" = @pipeUser AND
""MovedAt"" >= date_trunc('month', CURRENT_DATE)";
try
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(query, conn);
cmd.Parameters.AddWithValue("@pipeUser", userId);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
var temp = new Record
{
Id = ToIntOrNull(reader, "Id"),
Action = ToStringOrNull(reader, "Action"),
User = _pipeUsers.Find(u => u.UserId == ToIntOrNull(reader, "UserID")).Nome,
FieldID = ToStringOrNull(reader, "FieldID"),
From = ToIntOrNull(reader, "From"),
To = ToIntOrNull(reader, "To"),
CardID = ToIntOrNull(reader, "CardID"),
PipeSUID = ToStringOrNull(reader, "PipeSUID"),
MovedAt = (DateTime)(reader["MovedAt"] ?? DateTime.MinValue),
Title = ToStringOrNull(reader, "Title") ?? "-",
Acao = "Desconhecida"
};
temp.AcaoID = temp.From + "-" + temp.To;
temp.Acao = _actionIds.Find(p => p.actionId == temp.AcaoID).actionName;
userRecords.Add(temp);
}
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"\n[red]Erro ao consultar {nome}:[/] {ex.Message}");
}
});
return userRecords;
}
static (int count, int countA, int countR, int countC) ContarRegistros(List<Record> userRecords)
{
int count = userRecords.Count(x => x.MovedAt > DateTime.Today && x.FieldID is not null);
int countR = userRecords.Count(x => (x.Acao ?? "").StartsWith("Realizou"));
int countA = userRecords.Count(x => (x.Acao ?? "").StartsWith("Agendou"));
int countC = userRecords.Count(x => (x.Acao ?? "").EndsWith("cancelada"));
return (count, countA, countR, countC);
}
static string ObterFormatoCor(int count)
{
var format = "";
switch (count)
{
case int n when (n < 20):
format = "[red]";
break;
case int n when (n >= 20 && n < 40):
format = "[yellow]";
break;
case int n when (n >= 40 && n < 60):
format = "[blue]";
break;
case int n when (n >= 60):
format = "[white on green bold]";
break;
}
return format;
}
static void AguardarEntrada()
{
// Tempo de espera em segundos (ex.: 10 minutos = 600s)
@ -268,7 +296,6 @@ class Program
// Garante que, ao sair do loop, o cursor fique embaixo do texto
Console.WriteLine("");
}
static void Autenticacao()
{
AnsiConsole.Status()
@ -312,6 +339,7 @@ class Program
}
});
}
#region Helpers
static int? ToIntOrNull(DbDataReader r, string col)