using BLL; using DAL; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace UI { public class AgendaConsultaPanel : UserControl { // ── CORES ───────────────────────────────────────────────────────────── private readonly Color AccentBlue = Color.FromArgb(37, 99, 235); private readonly Color TextDark = Color.FromArgb(30, 41, 59); private readonly Color BorderColor = Color.FromArgb(226, 232, 240); private readonly Color GreenColor = Color.FromArgb(34, 197, 94); private readonly Color MutedGray = Color.FromArgb(148, 163, 184); private readonly Color SurfaceColor = Color.FromArgb(248, 250, 252); // ── CONTROLES ───────────────────────────────────────────────────────── private Panel pnlToolbar = null!; private Panel pnlFiltros = null!; private Panel pnlRodape = null!; private DataGridView dgvAgenda = null!; private Label lblTotal = null!; // ── FILTROS ─────────────────────────────────────────────────────────── private RoundTextBox txtFiltroCompromisso = null!; private RoundTextBox txtFiltroFunc = null!; private RoundTextBox txtFiltroOsVinc = null!; private RoundTextBox txtDataInicio = null!; private RoundTextBox txtDataFim = null!; private ComboBox cmbSituacao = null!; // ── DADOS ───────────────────────────────────────────────────────────── private List _todos = new(); private List _filtrados = new(); // ── EVENTO: abre cadastro com o registro selecionado ────────────────── public event Action? OnAbrirCadastro; //Carrega a string conexao com o banco de dados, para ser usada no repositório private string _conexao = DadosDaConexao.ObterConexao(); // ── CONSTRUTOR ──────────────────────────────────────────────────────── public AgendaConsultaPanel() { Dock = DockStyle.Fill; BackColor = Color.White; DoubleBuffered = true; InitializeLayout(); //CarregarDadosFake(); CarregarDadosDoBanco(); AplicarFiltros(); } // ══════════════════════════════════════════════════════════════════════ // LAYOUT // // REGRA WINFORMS — ordem de adição ao Controls é INVERSA à exibição: // 1º adicionar → Fill (ocupa o centro) // 2º adicionar → Bottom // 3º adicionar → Top (empurra o Fill para baixo; último = mais alto) // // ══════════════════════════════════════════════════════════════════════ private void InitializeLayout() { Controls.Clear(); // ── 1º: GRID (Fill) ─────────────────────────────────────────────── BuildGrid(); Controls.Add(dgvAgenda); // ── 2º: RODAPÉ (Bottom) ─────────────────────────────────────────── pnlRodape = new Panel { Dock = DockStyle.Bottom, Height = 30, BackColor = SurfaceColor }; lblTotal = new Label { AutoSize = true, Location = new Point(16, 7), Font = new Font("Segoe UI", 8.5f, FontStyle.Bold), ForeColor = MutedGray, Text = "0 registros encontrados" }; pnlRodape.Controls.Add(lblTotal); Controls.Add(pnlRodape); // ── 3º: FILTROS (Top) ───────────────────────────────────────────── pnlFiltros = new Panel { Dock = DockStyle.Top, Height = 95, BackColor = SurfaceColor, Padding = new Padding(16, 8, 16, 8) }; BuildFiltros(); Controls.Add(pnlFiltros); // ── 4º: TOOLBAR (Top) — por último = fica acima dos filtros ─────── pnlToolbar = new Panel { Dock = DockStyle.Top, Height = 55, BackColor = SurfaceColor }; var flow = new FlowLayoutPanel { Dock = DockStyle.Fill, Padding = new Padding(12, 10, 0, 0), BackColor = Color.Transparent }; var btnPesquisar = CreateToolbarButton("Pesquisar", AccentBlue); var btnLimpar = CreateToolbarButton("Limpar", MutedGray); var btnAbrir = CreateToolbarButton("Abrir", GreenColor); var btnExportar = CreateToolbarButton("Exportar", Color.FromArgb(99, 102, 241)); btnPesquisar.Click += (_, _) => AplicarFiltros(); btnLimpar.Click += (_, _) => LimparFiltros(); btnAbrir.Click += (_, _) => AbrirRegistroSelecionado(); btnExportar.Click += (_, _) => ExportarCSV(); flow.Controls.AddRange(new Control[] { btnPesquisar, btnLimpar, btnAbrir, btnExportar }); pnlToolbar.Controls.Add(flow); Controls.Add(pnlToolbar); // ← último Top = aparece no topo } // ── FILTROS ─────────────────────────────────────────────────────────── private void BuildFiltros() { // Linha 1 txtFiltroCompromisso = AddFiltroInput(pnlFiltros, "Compromisso", 0, 8, 260); txtFiltroFunc = AddFiltroInput(pnlFiltros, "Funcionário", 270, 8, 200); txtFiltroOsVinc = AddFiltroInput(pnlFiltros, "OS Vinculada", 480, 8, 130); // Linha 2 txtDataInicio = AddFiltroInput(pnlFiltros, "Data Início", 0, 50, 130); txtDataFim = AddFiltroInput(pnlFiltros, "Data Fim", 140, 50, 130); pnlFiltros.Controls.Add(new Label { Text = "Situação", Location = new Point(280, 50), Font = new Font("Segoe UI", 7.5f, FontStyle.Bold), ForeColor = TextDark, AutoSize = true }); cmbSituacao = new ComboBox { Location = new Point(280, 66), Size = new Size(160, 26), DropDownStyle = ComboBoxStyle.DropDownList, Font = new Font("Segoe UI", 8.5f), FlatStyle = FlatStyle.Flat }; cmbSituacao.Items.AddRange(new object[] { "Todos", "Pendente", "Realizado" }); cmbSituacao.SelectedIndex = 0; pnlFiltros.Controls.Add(cmbSituacao); foreach (var txt in new[] { txtFiltroCompromisso, txtFiltroFunc, txtFiltroOsVinc, txtDataInicio, txtDataFim }) txt.KeyDown += (_, e) => { if (e.KeyCode == Keys.Enter) AplicarFiltros(); }; } private RoundTextBox AddFiltroInput(Control parent, string label, int x, int y, int width) { parent.Controls.Add(new Label { Text = label, Location = new Point(x, y), Font = new Font("Segoe UI", 7.5f, FontStyle.Bold), ForeColor = TextDark, AutoSize = true }); var txt = new RoundTextBox { Location = new Point(x, y + 16), Size = new Size(width, 26), Radius = 4, BorderColor = BorderColor, FocusColor = AccentBlue, BackColor = Color.White }; parent.Controls.Add(txt); return txt; } // ── GRID ────────────────────────────────────────────────────────────── private void BuildGrid() { dgvAgenda = new DataGridView { Dock = DockStyle.Fill, BackgroundColor = Color.White, BorderStyle = BorderStyle.None, CellBorderStyle = DataGridViewCellBorderStyle.SingleHorizontal, ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single, GridColor = BorderColor, RowHeadersVisible = false, AllowUserToAddRows = false, AllowUserToDeleteRows = false, ReadOnly = true, SelectionMode = DataGridViewSelectionMode.FullRowSelect, MultiSelect = false, Font = new Font("Segoe UI", 8.5f), AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill, ColumnHeadersHeight = 36, ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing, RowTemplate = { Height = 32 } }; dgvAgenda.ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle { BackColor = SurfaceColor, ForeColor = TextDark, Font = new Font("Segoe UI", 8.5f, FontStyle.Bold), Alignment = DataGridViewContentAlignment.MiddleLeft, Padding = new Padding(8, 0, 0, 0), SelectionBackColor = SurfaceColor, SelectionForeColor = TextDark }; dgvAgenda.DefaultCellStyle = new DataGridViewCellStyle { ForeColor = TextDark, BackColor = Color.White, SelectionBackColor = Color.FromArgb(239, 246, 255), SelectionForeColor = AccentBlue, Padding = new Padding(8, 0, 0, 0) }; dgvAgenda.AlternatingRowsDefaultCellStyle = new DataGridViewCellStyle { BackColor = SurfaceColor, SelectionBackColor = Color.FromArgb(239, 246, 255), SelectionForeColor = AccentBlue }; dgvAgenda.Columns.AddRange(new DataGridViewColumn[] { new DataGridViewTextBoxColumn { Name = "colId", HeaderText = "ID", FillWeight = 5 }, new DataGridViewTextBoxColumn { Name = "colCodigo", HeaderText = "Código", FillWeight = 8 }, new DataGridViewTextBoxColumn { Name = "colCompromisso", HeaderText = "Compromisso", FillWeight = 26 }, new DataGridViewTextBoxColumn { Name = "colData", HeaderText = "Data", FillWeight = 10 }, new DataGridViewTextBoxColumn { Name = "colHora", HeaderText = "Hora", FillWeight = 7 }, new DataGridViewTextBoxColumn { Name = "colDia", HeaderText = "Dia", FillWeight = 10 }, new DataGridViewTextBoxColumn { Name = "colFunc", HeaderText = "Funcionário", FillWeight = 16 }, new DataGridViewTextBoxColumn { Name = "colAvisar", HeaderText = "Avisar", FillWeight = 12 }, new DataGridViewTextBoxColumn { Name = "colOs", HeaderText = "OS Vinc.", FillWeight = 8 }, new DataGridViewTextBoxColumn { Name = "colSituacao", HeaderText = "Situação", FillWeight = 10 }, }); dgvAgenda.CellDoubleClick += (_, e) => { if (e.RowIndex >= 0) AbrirRegistroSelecionado(); }; dgvAgenda.CellFormatting += (_, e) => { if (e.RowIndex < 0 || e.ColumnIndex < 0) return; if (dgvAgenda.Columns[e.ColumnIndex].Name != "colSituacao") return; bool realizado = e.Value?.ToString() == "Realizado"; e.CellStyle.ForeColor = realizado ? Color.FromArgb(22, 101, 52) : Color.FromArgb(146, 64, 14); e.CellStyle.Font = new Font("Segoe UI", 8f, FontStyle.Bold); e.CellStyle.SelectionForeColor = e.CellStyle.ForeColor; e.FormattingApplied = true; }; } // ══════════════════════════════════════════════════════════════════════ // LÓGICA // ══════════════════════════════════════════════════════════════════════ private void AplicarFiltros() { _filtrados = _todos.ToList(); var termComp = txtFiltroCompromisso.Text.Trim().ToLower(); if (!string.IsNullOrEmpty(termComp)) _filtrados = _filtrados.Where(e => e.COMPROMISSO?.ToLower().Contains(termComp) == true).ToList(); var termFunc = txtFiltroFunc.Text.Trim().ToLower(); if (!string.IsNullOrEmpty(termFunc)) _filtrados = _filtrados.Where(e => e.FUNC?.ToLower().Contains(termFunc) == true).ToList(); var termOs = txtFiltroOsVinc.Text.Trim().ToLower(); if (!string.IsNullOrEmpty(termOs)) _filtrados = _filtrados.Where(e => e.OS_VINC?.ToLower().Contains(termOs) == true).ToList(); if (DateTime.TryParseExact(txtDataInicio.Text.Trim(), "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var dtIni)) _filtrados = _filtrados.Where(e => ParseData(e.dDATA) >= dtIni).ToList(); if (DateTime.TryParseExact(txtDataFim.Text.Trim(), "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var dtFim)) _filtrados = _filtrados.Where(e => ParseData(e.dDATA) <= dtFim).ToList(); var sit = cmbSituacao.SelectedItem?.ToString(); if (sit == "Realizado") _filtrados = _filtrados.Where(e => e.REALIZADO?.ToUpper() == "S").ToList(); else if (sit == "Pendente") _filtrados = _filtrados.Where(e => e.REALIZADO?.ToUpper() != "S").ToList(); PreencherGrid(); } private void LimparFiltros() { txtFiltroCompromisso.Text = string.Empty; txtFiltroFunc.Text = string.Empty; txtFiltroOsVinc.Text = string.Empty; txtDataInicio.Text = string.Empty; txtDataFim.Text = string.Empty; cmbSituacao.SelectedIndex = 0; AplicarFiltros(); } private void PreencherGrid() { dgvAgenda.Rows.Clear(); foreach (var ev in _filtrados.OrderBy(e => ParseData(e.dDATA)).ThenBy(e => e.HORA)) { dgvAgenda.Rows.Add( ev.ID_AGENDA, ev.CODIGO, ev.COMPROMISSO, ev.dDATA, ev.HORA, ev.DIA, ev.FUNC, ev.AVISAR, ev.OS_VINC, ev.REALIZADO?.ToUpper() == "S" ? "Realizado" : "Pendente" ); } int total = _filtrados.Count; lblTotal.Text = total == 1 ? "1 registro encontrado" : $"{total} registros encontrados"; } private void AbrirRegistroSelecionado() { if (dgvAgenda.SelectedRows.Count == 0) { MessageBox.Show("Selecione um registro na lista.", "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } var row = dgvAgenda.SelectedRows[0]; if (row.Cells["colId"].Value is int id) { var ev = _filtrados.FirstOrDefault(e => e.ID_AGENDA == id); if (ev != null) OnAbrirCadastro?.Invoke(ev); } } private void ExportarCSV() { if (!_filtrados.Any()) { MessageBox.Show("Nenhum registro para exportar.", "Atenção", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } using var dlg = new SaveFileDialog { Filter = "CSV (*.csv)|*.csv", FileName = $"Agenda_{DateTime.Today:yyyyMMdd}.csv" }; if (dlg.ShowDialog() != DialogResult.OK) return; var sb = new System.Text.StringBuilder(); sb.AppendLine("ID;Código;Compromisso;Data;Hora;Dia;Funcionário;Avisar;OS Vinculada;Situação"); foreach (var ev in _filtrados.OrderBy(e => ParseData(e.dDATA)).ThenBy(e => e.HORA)) sb.AppendLine($"{ev.ID_AGENDA};{ev.CODIGO};{ev.COMPROMISSO};{ev.dDATA};" + $"{ev.HORA};{ev.DIA};{ev.FUNC};{ev.AVISAR};{ev.OS_VINC};" + $"{(ev.REALIZADO?.ToUpper() == "S" ? "Realizado" : "Pendente")}"); System.IO.File.WriteAllText(dlg.FileName, sb.ToString(), System.Text.Encoding.UTF8); MessageBox.Show($"Exportado com sucesso!\n{dlg.FileName}", "Exportar", MessageBoxButtons.OK, MessageBoxIcon.Information); } // ── DADOS FAKE — substituir pelo repositório ────────────────────────── private void CarregarDadosFake() { var hoje = DateTime.Today; _todos = new List { new(1, "AG001", "Reunião com cliente", hoje.ToString("dd/MM/yyyy"), "30 minutos antes", "Carlos Silva", DiaSemana(hoje), "09:00", "S", "OS-1042"), new(2, "AG002", "Visita técnica", hoje.ToString("dd/MM/yyyy"), "1 hora antes", "Ana Souza", DiaSemana(hoje), "14:00", "N", "OS-1055"), new(3, "AG003", "Entrega de equipamento", hoje.AddDays(4).ToString("dd/MM/yyyy"), "1 dia antes", "Pedro Lima", DiaSemana(hoje.AddDays(4)), "10:30", "N", ""), new(4, "AG004", "Manutenção preventiva", hoje.AddDays(-2).ToString("dd/MM/yyyy"), "30 minutos antes", "Carlos Silva", DiaSemana(hoje.AddDays(-2)), "08:00", "S", "OS-1030"), new(5, "AG005", "Treinamento equipe", hoje.AddDays(7).ToString("dd/MM/yyyy"), "1 dia antes", "Ana Souza", DiaSemana(hoje.AddDays(7)), "13:00", "N", ""), }; }//Carregar dados fakes para ixibição private void CarregarDadosDoBanco() { BLLAgenda _agendaBLL = new BLLAgenda(_conexao); _todos.Clear(); _todos.AddRange(_agendaBLL.Listar()); }//Carregar dados reais do banco de dados usando o repositório private static DateTime ParseData(string? value) { if (string.IsNullOrWhiteSpace(value)) return DateTime.MinValue; if (DateTime.TryParseExact(value, "dd/MM/yyyy", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var dt)) return dt; if (DateTime.TryParse(value, out dt)) return dt; return DateTime.MinValue; } private static string DiaSemana(DateTime d) => new System.Globalization.CultureInfo("pt-BR").DateTimeFormat.GetDayName(d.DayOfWeek); private RoundButton CreateToolbarButton(string text, Color color) => new RoundButton { Text = text, Size = new Size(95, 32), BackColor = color, ForeColor = Color.White, Font = new Font("Segoe UI Semibold", 8.5f), Margin = new Padding(0, 0, 6, 0), Cursor = Cursors.Hand }; } }