ghkkghkhgkhgkgh
ASAsjJjjjjjjJghkghkghkghkghkghkkhgkghkghgkhkghkghkghkghkgh ASAsjJjjjjjjJJjJhkgyjghjghghkghkg iopiopoiiopiopiopiopiopiopiopiopiopiopiopiopiopiopiopiop op[op[op[op[ J J J JJ po[op[op[op[op[op[po[op[op[ JJ J
bypass
/
home
/
onlyfibr
/
.trash
/
admin.2
/
Upload FileeE
HOME
<?php // HABILITAR ERROS PARA DEBUG - REMOVER/COMENTAR EM PRODUÇÃO! ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); // --------------------------------------------------------- // 1. Verifica se o admin está logado require_once 'includes/auth_check.php'; // Define o título desta página ANTES de incluir o header $pageTitle = 'Relatórios'; require_once '../includes/config/config.php'; // Necessário para $pdo e constantes require_once '../includes/functions/functions.php'; // Pode ser necessário para alguma função auxiliar // --- Processamento de Solicitações de Relatórios --- $mensagem_erro = $_SESSION['erro_dashboard'] ?? null; $mensagem_sucesso = $_SESSION['sucesso_dashboard'] ?? null; unset($_SESSION['erro_dashboard'], $_SESSION['sucesso_dashboard']); // Inicializa variáveis para estatísticas $relatorio_data = []; $graficos_data = []; $filtro_data_inicio = $_GET['data_inicio'] ?? date('Y-m-d', strtotime('-30 days')); $filtro_data_fim = $_GET['data_fim'] ?? date('Y-m-d'); $filtro_status = $_GET['status'] ?? ''; // --- Processamento do Formulário (Geração de Relatório) --- if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['gerar_relatorio'])) { // ADICIONAR VALIDAÇÃO CSRF TOKEN AQUI !!! // Captura e valida os filtros $filtro_data_inicio = filter_input(INPUT_POST, 'data_inicio', FILTER_SANITIZE_STRING) ?? date('Y-m-d', strtotime('-30 days')); $filtro_data_fim = filter_input(INPUT_POST, 'data_fim', FILTER_SANITIZE_STRING) ?? date('Y-m-d'); $filtro_status = filter_input(INPUT_POST, 'status', FILTER_SANITIZE_STRING) ?? ''; // Validação simples de datas if (strtotime($filtro_data_inicio) > strtotime($filtro_data_fim)) { $mensagem_erro = "Data inicial não pode ser maior que a data final."; } } // --- Geração dos Relatórios --- if ($pdo) { try { // Condições básicas para a consulta $where_conditions = ["1=1"]; // Sempre verdadeiro para facilitar concatenação de ANDs $query_params = []; // Adiciona condições com base nos filtros if (!empty($filtro_data_inicio) && !empty($filtro_data_fim)) { $where_conditions[] = "data_envio BETWEEN :data_inicio AND :data_fim"; $query_params[':data_inicio'] = $filtro_data_inicio . ' 00:00:00'; $query_params[':data_fim'] = $filtro_data_fim . ' 23:59:59'; } if (!empty($filtro_status)) { $where_conditions[] = "status_processamento = :status"; $query_params[':status'] = $filtro_status; } // Constrói a cláusula WHERE $where_clause = implode(' AND ', $where_conditions); // --------- Relatório 1: Cadastros por Status --------- $sql_status = " SELECT status_processamento, COUNT(*) as total FROM precadastros WHERE $where_clause GROUP BY status_processamento ORDER BY total DESC"; $stmt_status = $pdo->prepare($sql_status); foreach ($query_params as $param => $value) { $stmt_status->bindValue($param, $value); } $stmt_status->execute(); $relatorio_data['status'] = $stmt_status->fetchAll(PDO::FETCH_ASSOC); // Prepara dados para o gráfico de pizza de status $graficos_data['status_labels'] = []; $graficos_data['status_values'] = []; $graficos_data['status_colors'] = []; $colors = [ 'em_analise' => '#47076c', 'aguardando_contato' => '#e86d0f', 'aprovado' => '#046018', 'reprovado' => '#c90a0a', 'tentando_contato' => '#0b2bdf', 'contrato_realizado_no_sgp' => '#28f404', 'inviabilidade_tecnica' => '#fddf01' ]; foreach ($relatorio_data['status'] as $status_item) { $status_key = $status_item['status_processamento']; $status_label = ucwords(str_replace('_', ' ', $status_key)); $graficos_data['status_labels'][] = $status_label; $graficos_data['status_values'][] = (int)$status_item['total']; $graficos_data['status_colors'][] = $colors[$status_key] ?? '#999999'; } // --------- Relatório 2: Cadastros por Dia --------- $sql_diario = " SELECT DATE(data_envio) as data, COUNT(*) as total FROM precadastros WHERE $where_clause GROUP BY DATE(data_envio) ORDER BY data ASC"; $stmt_diario = $pdo->prepare($sql_diario); foreach ($query_params as $param => $value) { $stmt_diario->bindValue($param, $value); } $stmt_diario->execute(); $relatorio_data['diario'] = $stmt_diario->fetchAll(PDO::FETCH_ASSOC); // Prepara dados para o gráfico de linha diário $graficos_data['diario_labels'] = []; $graficos_data['diario_values'] = []; foreach ($relatorio_data['diario'] as $dia_item) { // Formata a data para exibição (DD/MM/YYYY) $data_formatada = date('d/m/Y', strtotime($dia_item['data'])); $graficos_data['diario_labels'][] = $data_formatada; $graficos_data['diario_values'][] = (int)$dia_item['total']; } // --------- Relatório 3: Top 5 Cidades --------- $sql_cidades = " SELECT cidade, estado, COUNT(*) as total FROM precadastros WHERE $where_clause GROUP BY cidade, estado ORDER BY total DESC LIMIT 5"; $stmt_cidades = $pdo->prepare($sql_cidades); foreach ($query_params as $param => $value) { $stmt_cidades->bindValue($param, $value); } $stmt_cidades->execute(); $relatorio_data['cidades'] = $stmt_cidades->fetchAll(PDO::FETCH_ASSOC); // Prepara dados para o gráfico de barras de cidades $graficos_data['cidades_labels'] = []; $graficos_data['cidades_values'] = []; foreach ($relatorio_data['cidades'] as $cidade_item) { $cidade_label = $cidade_item['cidade'] . '/' . $cidade_item['estado']; $graficos_data['cidades_labels'][] = $cidade_label; $graficos_data['cidades_values'][] = (int)$cidade_item['total']; } // --------- Relatório 4: Totais Gerais --------- // Total de cadastros no período $sql_total = " SELECT COUNT(*) as total FROM precadastros WHERE $where_clause"; $stmt_total = $pdo->prepare($sql_total); foreach ($query_params as $param => $value) { $stmt_total->bindValue($param, $value); } $stmt_total->execute(); $relatorio_data['total_geral'] = $stmt_total->fetchColumn(); // Total pendente de processamento $sql_pendentes = " SELECT COUNT(*) as total FROM precadastros WHERE $where_clause AND p_processar = TRUE"; $stmt_pendentes = $pdo->prepare($sql_pendentes); foreach ($query_params as $param => $value) { $stmt_pendentes->bindValue($param, $value); } $stmt_pendentes->execute(); $relatorio_data['total_pendentes'] = $stmt_pendentes->fetchColumn(); // Total processado com sucesso $sql_processados = " SELECT COUNT(*) as total FROM precadastros WHERE $where_clause AND p_processar = FALSE AND status_processamento = 'aprovado'"; $stmt_processados = $pdo->prepare($sql_processados); foreach ($query_params as $param => $value) { $stmt_processados->bindValue($param, $value); } $stmt_processados->execute(); $relatorio_data['total_processados'] = $stmt_processados->fetchColumn(); } catch (\PDOException $e) { $mensagem_erro = "Erro ao gerar relatórios: " . $e->getMessage(); error_log("Erro PDO ao gerar relatórios: " . $e->getMessage()); } } else { $mensagem_erro = "Erro crítico: Conexão com banco de dados falhou."; } // Lista de status para o filtro $status_options = [ 'em_analise' => 'Em Análise', 'aprovado' => 'Aprovado', 'reprovado' => 'Reprovado', 'aguardando_contato' => 'Aguardando Contato', 'tentando_contato' => 'Tentando Contato', 'inviabilidade_tecnica' => 'Inviabilidade Técnica', 'contrato_realizado_no_sgp' => 'Contrato Realizado no SGP' ]; // Obter os menus dinamicamente do banco de dados $menus = carregarMenus($pdo, $usuario_permissoes ?? ['admin']); // Incluir o header include_once 'includes/header.php'; ?> <div class="container"> <div class="d-flex justify-content-between align-items-center mb-4"> <div> <h1>Relatórios</h1> <p class="text-muted">Análise de pré-cadastros e métricas do sistema</p> </div> <div> <button type="button" class="btn btn-primary" id="btnImprimirRelatorio"> <i class="fas fa-print"></i> Imprimir Relatório </button> <button type="button" class="btn btn-outline-secondary ms-2" id="btnExportarPDF"> <i class="fas fa-file-pdf"></i> Exportar PDF </button> </div> </div> <?php if ($mensagem_erro): ?> <div class="alert alert-danger alert-dismissible fade show" role="alert"> <?php echo htmlspecialchars($mensagem_erro); ?> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> <?php endif; ?> <?php if ($mensagem_sucesso): ?> <div class="alert alert-success alert-dismissible fade show" role="alert"> <?php echo htmlspecialchars($mensagem_sucesso); ?> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> <?php endif; ?> <!-- Filtros do relatório --> <div class="card mb-4"> <div class="card-header bg-light"> <h5 class="mb-0"><i class="fas fa-filter me-2"></i>Filtros</h5> </div> <div class="card-body"> <form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" class="row g-3"> <input type="hidden" name="gerar_relatorio" value="1"> <!-- ADICIONE CSRF TOKEN AQUI !!! --> <div class="col-md-4"> <label for="data_inicio" class="form-label">Data Inicial</label> <input type="date" class="form-control" id="data_inicio" name="data_inicio" value="<?php echo htmlspecialchars($filtro_data_inicio); ?>"> </div> <div class="col-md-4"> <label for="data_fim" class="form-label">Data Final</label> <input type="date" class="form-control" id="data_fim" name="data_fim" value="<?php echo htmlspecialchars($filtro_data_fim); ?>"> </div> <div class="col-md-4"> <label for="status" class="form-label">Status</label> <select class="form-select" id="status" name="status"> <option value="">Todos os Status</option> <?php foreach ($status_options as $value => $label): ?> <option value="<?php echo htmlspecialchars($value); ?>" <?php echo ($filtro_status === $value) ? 'selected' : ''; ?>> <?php echo htmlspecialchars($label); ?> </option> <?php endforeach; ?> </select> </div> <div class="col-12 mt-3"> <button type="submit" class="btn btn-primary"> <i class="fas fa-search me-2"></i>Gerar Relatório </button> <a href="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" class="btn btn-outline-secondary ms-2"> <i class="fas fa-redo me-2"></i>Limpar Filtros </a> </div> </form> </div> </div> <!-- Resumo do relatório --> <div class="row mb-4"> <div class="col-md-4 mb-3"> <div class="card bg-primary text-white h-100"> <div class="card-body text-center"> <h5 class="card-title">Total de Cadastros</h5> <h2 class="display-4"><?php echo number_format($relatorio_data['total_geral'] ?? 0); ?></h2> <p class="card-text">No período selecionado</p> </div> </div> </div> <div class="col-md-4 mb-3"> <div class="card bg-warning text-dark h-100"> <div class="card-body text-center"> <h5 class="card-title">Pendentes de Processamento</h5> <h2 class="display-4"><?php echo number_format($relatorio_data['total_pendentes'] ?? 0); ?></h2> <p class="card-text">Aguardando envio para API</p> </div> </div> </div> <div class="col-md-4 mb-3"> <div class="card bg-success text-white h-100"> <div class="card-body text-center"> <h5 class="card-title">Processados com Sucesso</h5> <h2 class="display-4"><?php echo number_format($relatorio_data['total_processados'] ?? 0); ?></h2> <p class="card-text">Enviados para API</p> </div> </div> </div> </div> <!-- Gráficos --> <div class="row mb-4"> <div class="col-md-6 mb-4"> <div class="card h-100"> <div class="card-header bg-light"> <h5 class="mb-0"><i class="fas fa-chart-pie me-2"></i>Cadastros por Status</h5> </div> <div class="card-body"> <canvas id="chartStatus" width="400" height="300"></canvas> </div> </div> </div> <div class="col-md-6 mb-4"> <div class="card h-100"> <div class="card-header bg-light"> <h5 class="mb-0"><i class="fas fa-chart-line me-2"></i>Cadastros por Dia</h5> </div> <div class="card-body"> <canvas id="chartDiario" width="400" height="300"></canvas> </div> </div> </div> </div> <div class="row mb-4"> <div class="col-md-6 mb-4"> <div class="card h-100"> <div class="card-header bg-light"> <h5 class="mb-0"><i class="fas fa-chart-bar me-2"></i>Top 5 Cidades</h5> </div> <div class="card-body"> <canvas id="chartCidades" width="400" height="300"></canvas> </div> </div> </div> <div class="col-md-6 mb-4"> <div class="card h-100"> <div class="card-header bg-light"> <h5 class="mb-0"><i class="fas fa-table me-2"></i>Dados por Status</h5> </div> <div class="card-body"> <div class="table-responsive"> <table class="table table-striped table-hover"> <thead> <tr> <th>Status</th> <th class="text-center">Quantidade</th> <th class="text-center">Percentual</th> </tr> </thead> <tbody> <?php $total_geral = $relatorio_data['total_geral'] ?? 0; if (!empty($relatorio_data['status'])): foreach ($relatorio_data['status'] as $status_item): $percentual = ($total_geral > 0) ? ($status_item['total'] / $total_geral) * 100 : 0; ?> <tr> <td> <span class="badge status-<?php echo htmlspecialchars($status_item['status_processamento']); ?>"> <?php echo ucwords(str_replace('_', ' ', htmlspecialchars($status_item['status_processamento']))); ?> </span> </td> <td class="text-center"><?php echo number_format($status_item['total']); ?></td> <td class="text-center"><?php echo number_format($percentual, 1); ?>%</td> </tr> <?php endforeach; else: ?> <tr> <td colspan="3" class="text-center">Nenhum dado encontrado.</td> </tr> <?php endif; ?> </tbody> </table> </div> </div> </div> </div> </div> </div> <!-- Carregamento do Chart.js --> <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.1/dist/chart.min.js"></script> <script> // Dados dos gráficos const statusLabels = <?php echo json_encode($graficos_data['status_labels'] ?? []); ?>; const statusValues = <?php echo json_encode($graficos_data['status_values'] ?? []); ?>; const statusColors = <?php echo json_encode($graficos_data['status_colors'] ?? []); ?>; const diarioLabels = <?php echo json_encode($graficos_data['diario_labels'] ?? []); ?>; const diarioValues = <?php echo json_encode($graficos_data['diario_values'] ?? []); ?>; const cidadesLabels = <?php echo json_encode($graficos_data['cidades_labels'] ?? []); ?>; const cidadesValues = <?php echo json_encode($graficos_data['cidades_values'] ?? []); ?>; // Configuração dos gráficos document.addEventListener('DOMContentLoaded', function() { // Gráfico de Status (Pie Chart) if (document.getElementById('chartStatus')) { const ctxStatus = document.getElementById('chartStatus').getContext('2d'); const chartStatus = new Chart(ctxStatus, { type: 'pie', data: { labels: statusLabels, datasets: [{ data: statusValues, backgroundColor: statusColors, borderWidth: 1 }] }, options: { responsive: true, plugins: { legend: { position: 'right', }, tooltip: { callbacks: { label: function(context) { const label = context.label || ''; const value = context.raw || 0; const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = total > 0 ? Math.round((value / total) * 100) : 0; return `${label}: ${value} (${percentage}%)`; } } } } } }); } // Gráfico Diário (Line Chart) if (document.getElementById('chartDiario')) { const ctxDiario = document.getElementById('chartDiario').getContext('2d'); const chartDiario = new Chart(ctxDiario, { type: 'line', data: { labels: diarioLabels, datasets: [{ label: 'Cadastros por Dia', data: diarioValues, fill: false, borderColor: 'rgb(75, 192, 192)', tension: 0.1 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } // Gráfico de Cidades (Bar Chart) if (document.getElementById('chartCidades')) { const ctxCidades = document.getElementById('chartCidades').getContext('2d'); const chartCidades = new Chart(ctxCidades, { type: 'bar', data: { labels: cidadesLabels, datasets: [{ label: 'Cadastros por Cidade', data: cidadesValues, backgroundColor: [ 'rgba(255, 99, 132, 0.7)', 'rgba(54, 162, 235, 0.7)', 'rgba(255, 206, 86, 0.7)', 'rgba(75, 192, 192, 0.7)', 'rgba(153, 102, 255, 0.7)' ], borderColor: [ 'rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)' ], borderWidth: 1 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } }); // Função para impressão de relatório document.getElementById('btnImprimirRelatorio').addEventListener('click', function() { window.print(); }); // Função para exportar PDF (simulada) document.getElementById('btnExportarPDF').addEventListener('click', function() { alert('Esta função exportaria o relatório para PDF.\nPara implementação real, você precisaria de uma biblioteca como jsPDF ou enviar o pedido para o servidor gerar o PDF.'); }); </script> <?php // Incluir o footer include_once 'includes/footer.php'; ?>