document.addEventListener('DOMContentLoaded', function() { // --- REGISTRO DO PLUGIN DATALABELS --- // Esta linha é crucial para que o plugin ChartDataLabels seja reconhecido e usado. Chart.register(ChartDataLabels); // // --- FIM DO REGISTRO --- // --- Funções Auxiliares para Cores --- // Paleta de cores padrão para gráficos, se nenhuma cor específica for definida. // Cores mais fortes (alpha ajustado para 1.0 para opacidade total ou mais próximo) const CHART_COLORS = [ // 'rgba(237, 50, 55, 1.0)', // Vermelho 'rgba(54, 162, 235, 1.0)', // Azul 'rgba(255, 206, 86, 1.0)', // Amarelo 'rgba(81, 220, 53)', // Verde-água 'rgba(153, 102, 255, 1.0)', // Roxo 'rgba(255, 159, 64, 1.0)', // Laranja 'rgba(199, 199, 199, 1.0)', // Cinza 'rgba(83, 102, 255, 1.0)', // Azul mais escuro 'rgba(200, 50, 100, 1.0)', // Rosa choque 'rgba(50, 200, 150, 1.0)' // Verde menta ]; const CHART_BORDERS = [ // 'rgba(237, 50, 55, 1.0)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)', 'rgba(199, 199, 199, 1)', 'rgba(83, 102, 255, 1)', 'rgba(200, 50, 100, 1)', 'rgba(50, 200, 150, 1)' ]; // Retorna cores consistentes para os status de condicionante. function getStatusColors(status) { // switch (status) { // case 'Concluído': return { bg: 'rgba(81, 220, 53)', border: 'rgba(81, 220, 53)' }; // Verde puro e forte case 'Em andamento': return { bg: 'rgba(255, 206, 86, 1.0)', border: 'rgba(255, 206, 86, 1)' }; // Amarelo case 'Não iniciado': return { bg: 'rgba(237, 50, 55, 1.0)', border: 'rgba(237, 50, 55, 1.0)' }; // Vermelho puro e forte default: return { bg: 'rgba(199, 199, 199, 1.0)', border: 'rgba(199, 199, 199, 1)' }; // Cinza para outros } } // Retorna cores consistentes para as prioridades/importância de condicionante. function getPrioridadeColors(prioridade) { // switch (prioridade) { // case 'Alto': return { bg: 'rgba(237, 50, 55, 1.0)', border: 'rgba(237, 50, 55, 1.0)' }; // Vermelho case 'Médio': return { bg: 'rgba(255, 206, 86, 1.0)', border: 'rgba(255, 206, 86, 1)' }; // Amarelo case 'Baixo': return { bg: 'rgba(81, 220, 53)', border: 'rgba(81, 220, 53)' }; // Verde puro e forte default: return { bg: 'rgba(199, 199, 199, 1.0)', border: 'rgba(199, 199, 199, 1)' }; // Cinza } } // --- Função Genérica para Renderizar Gráficos Chart.js --- function renderChart(canvasId, chartData, title, loadingId, errorId, noDataId, chartType = 'pie') { // const chartCanvas = document.getElementById(canvasId); // const loadingMessage = document.getElementById(loadingId); // const errorMessage = document.getElementById(errorId); // const noDataMessage = document.getElementById(noDataId); // // Oculta todos os estados e mostra apenas o de carregamento inicialmente. chartCanvas.style.display = 'none'; // errorMessage.style.display = 'none'; // noDataMessage.style.display = 'none'; // loadingMessage.style.display = 'block'; // // Verifica se há dados para renderizar. if (!chartData || (Array.isArray(chartData) && chartData.length === 0) || (!Array.isArray(chartData) && (!chartData.labels || chartData.labels.length === 0))) { // loadingMessage.style.display = 'none'; // noDataMessage.style.display = 'block'; // Mostra mensagem de "sem dados". return; // } // Se há dados, oculta o carregamento e mostra o canvas. loadingMessage.style.display = 'none'; // chartCanvas.style.display = 'block'; // let labels = []; // let datasets = []; // // Lógica para stackedBar e o novo 'bar' (contagem por categoria) if (chartType === 'stackedBar' || (chartType === 'bar' && chartData.datasets)) { // chartData.datasets indica o formato do novo PHP labels = chartData.labels; // if (chartType === 'stackedBar') { // datasets = chartData.datasets.map(dataset => { // const colors = getPrioridadeColors(dataset.label); // return { // label: dataset.label, // data: dataset.data, // backgroundColor: colors.bg, // borderColor: colors.border, // borderWidth: 1, // stack: 'condicionantes' // }; }); } else { // Para o novo gráfico de barras simples por categoria (myCategoriaChart) datasets = chartData.datasets.map(dataset => { // const bgColors = dataset.data.map((_, index) => CHART_COLORS[index % CHART_COLORS.length]); // const borderColors = dataset.data.map((_, index) => CHART_BORDERS[index % CHART_BORDERS.length]); // return { // label: dataset.label, // data: dataset.data, // backgroundColor: bgColors, // borderColor: borderColors, // borderWidth: 1 // }; }); } } else { // Lógica existente para gráficos de pizza e barras normais (municípios, locais) labels = chartData.map(item => item.status || item.status_local || item.municipio || item.prioridade || item.nome_local); // const values = chartData.map(item => item.total || item.total_condicionantes); // let backgroundColors = []; // let borderColors = []; // if (chartType === 'pie') { // backgroundColors = labels.map(label => { // let color = getStatusColors(label); // if (color.bg === 'rgba(199, 199, 199, 1.0)') { // const prioridadeColor = getPrioridadeColors(label); // // CORREÇÃO APLICADA AQUI: Removemos a linha duplicada/errada e corrigimos o typo if (prioridadeColor.bg !== 'rgba(199, 199, 199, 1.0)') { // A string de cor deve ter 4 números (199, 199, 199, 1.0) color = prioridadeColor; // } else { const index = labels.indexOf(label); // color = { bg: CHART_COLORS[index % CHART_COLORS.length], border: CHART_BORDERS[index % CHART_BORDERS.length] }; // } } return color.bg; // }); borderColors = labels.map(label => { // let color = getStatusColors(label); // if (color.border === 'rgba(199, 199, 199, 1)') { // const prioridadeColor = getPrioridadeColors(label); // // CORREÇÃO APLICADA AQUI: Garantindo que a string de comparação esteja correta if (prioridadeColor.border !== 'rgba(199, 199, 199, 1)') { // color = prioridadeColor; // } else { const index = labels.indexOf(label); // color = { bg: CHART_COLORS[index % CHART_COLORS.length], border: CHART_BORDERS[index % CHART_COLORS.length] }; // } } return color.border; // }); } else if (chartType === 'bar') { // Para gráficos de barra normais (municípios, locais) backgroundColors = labels.map((_, index) => CHART_COLORS[index % CHART_COLORS.length]); // borderColors = labels.map((_, index) => CHART_BORDERS[index % CHART_BORDERS.length]); // } datasets = [{ // label: '', // data: values, // backgroundColor: backgroundColors, // borderColor: borderColors, // borderWidth: 1 // }]; } // Configura as opções de escala para gráficos de barra (eixos). let scalesOptions = {}; // if (chartType === 'bar' || chartType === 'stackedBar') { // scalesOptions = { // y: { // beginAtZero: true, // stacked: chartType === 'stackedBar', // title: { display: true, text: 'Número de Condicionantes' }, // ticks: { precision: 0 } // }, x: { // stacked: chartType === 'stackedBar', // title: { // display: true, // text: (canvasId === 'condicionantesMunicipioChart') ? 'Município' : // ((canvasId === 'condicionantesPorLocalChart') ? 'Local' : // ((canvasId === 'categoriaImportanciaChart') ? 'Categoria' : // ((canvasId === 'myCategoriaChart') ? 'Categoria' : ''))) // }, ticks: { // autoSkip: true, // maxRotation: 45, // minRotation: 0 // } } }; } // Destrói qualquer instância de gráfico Chart.js existente no canvas para evitar sobreposições. if (Chart.getChart(canvasId)) { // Chart.getChart(canvasId).destroy(); // } // Cria e renderiza o gráfico usando Chart.js. const ctx = chartCanvas.getContext('2d'); // new Chart(ctx, { // type: (chartType === 'stackedBar' || chartType === 'bar') ? 'bar' : chartType, // data: { // labels: labels, // datasets: datasets // }, options: { // responsive: true, // maintainAspectRatio: false, // scales: scalesOptions, // plugins: { // legend: { position: (chartType === 'pie') ? 'right' : 'top' }, // title: { display: true, text: title }, // // --- CONFIGURAÇÃO PARA DATALABELS --- datalabels: { // display: true, // Ativa os datalabels color: '#444', // Cor do texto dos números (preto mais claro) font: { // weight: 'bold', // size: 10 // }, formatter: function(value, context) { // // Garante que o valor seja um número inteiro e não mostre 0 se não houver dados return value > 0 ? value : ''; // }, // Posição para gráficos de barra e pizza anchor: 'center', // 'start', 'center', 'end' align: 'center', // 'start', 'center', 'end' offset: 0 // Distância do label da barra/fatia (0 para centralizar) } // --- FIM DA CONFIGURAÇÃO --- } } }); } // --- NOVA FUNÇÃO REUTILIZÁVEL PARA GRÁFICOS DE STATUS POR LOCAL --- function fetchAndRenderLocalStatusChart(canvasId, loadingId, errorId, noDataId) { const chartCanvas = document.getElementById(canvasId); if (!chartCanvas) return; const loadingMessage = document.getElementById(loadingId); const errorMessage = document.getElementById(errorId); const noDataMessage = document.getElementById(noDataId); if (loadingMessage) loadingMessage.style.display = 'block'; if (chartCanvas) chartCanvas.style.display = 'none'; if (errorMessage) errorMessage.style.display = 'none'; if (noDataMessage) noDataMessage.style.display = 'none'; fetch('get_condicionantes_status_global_local.php') .then(response => { if (loadingMessage) loadingMessage.style.display = 'none'; if (!response.ok) { if (errorMessage) errorMessage.style.display = 'block'; throw new Error('Erro HTTP ao buscar dados de status: ' + response.status); } return response.json(); }) .then(data => { if (data.error) { if (errorMessage) { errorMessage.style.display = 'block'; errorMessage.querySelector('p').innerText = 'Erro do servidor: ' + data.error; } console.error("Erro do servidor:", data.error); return; } if (data.local_status.length === 0) { if (noDataMessage) noDataMessage.style.display = 'block'; return; } // O local_status é um array de objetos, perfeito para o renderChart como 'pie' renderChart(canvasId, data.local_status, 'Status das ações', loadingId, errorId, noDataId, 'pie'); }) .catch(error => { if (loadingMessage) loadingMessage.style.display = 'none'; if (errorMessage) errorMessage.style.display = 'block'; console.error(`Erro ao carregar dados dos gráficos de status por local para ${canvasId}:`, error); }); } // --- FIM DA FUNÇÃO REUTILIZÁVEL --- // --- Requisições de Dados e Renderização para Cada Gráfico --- // 1. Fetch para os Dados de Status Combinado (Global) const chartCanvasGlobalStatus = document.getElementById('globalStatusChart'); // if (chartCanvasGlobalStatus) { // const loadingMessageGlobal = document.getElementById('loadingMessageGlobal'); // const errorMessageGlobal = document.getElementById('errorMessageGlobal'); // const noDataMessageGlobal = document.getElementById('noDataMessageGlobal'); // loadingMessageGlobal.style.display = 'block'; // chartCanvasGlobalStatus.style.display = 'none'; // errorMessageGlobal.style.display = 'none'; // noDataMessageGlobal.style.display = 'none'; // fetch('get_condicionantes_status_global_local.php') // .then(response => { // loadingMessageGlobal.style.display = 'none'; // if (!response.ok) { // errorMessageGlobal.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de status: ' + response.status); // } return response.json(); // }) .then(data => { // if (data.error) { // errorMessageGlobal.style.display = 'block'; // errorMessageGlobal.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (data.global_status.length === 0) { // noDataMessageGlobal.style.display = 'block'; // return; // } renderChart('globalStatusChart', data.global_status, 'Distribuição de Status Global', 'loadingMessageGlobal', 'errorMessageGlobal', 'noDataMessageGlobal', 'pie'); // }) .catch(error => { // loadingMessageGlobal.style.display = 'none'; // errorMessageGlobal.style.display = 'block'; // console.error('Erro ao carregar dados dos gráficos de status global:', error); // }); } // 2. Fetch para o Gráfico de Status por Local (Gráfico Original - Agora usando a função reutilizável) const chartCanvasLocalStatus = document.getElementById('localStatusChart'); if (chartCanvasLocalStatus) { fetchAndRenderLocalStatusChart( 'localStatusChart', 'loadingMessageLocal', 'errorMessageLocal', 'noDataMessageLocal' ); } // 3. NOVO GRÁFICO: Gráfico de Status por Local (Gráfico Duplicado/Dependente - usando a mesma função) const chartCanvasOutraLocalStatus = document.getElementById('outraLocalStatusChart'); if (chartCanvasOutraLocalStatus) { fetchAndRenderLocalStatusChart( 'outraLocalStatusChart', // O novo ID do canvas (que foi adicionado no index.php) 'loadingOutraLocal', // O novo ID de loading 'errorMessageOutraLocal', // O novo ID de erro 'noDataOutraLocal' // O novo ID de sem dados ); } // FIM DOS GRÁFICOS DE STATUS POR LOCAL // 4. Fetch para o Gráfico de Condicionantes por Município const chartCanvasMunicipio = document.getElementById('condicionantesMunicipioChart'); // if (chartCanvasMunicipio) { // const loadingMessageMunicipio = document.getElementById('loadingMessageMunicipio'); // const errorMessageMunicipio = document.getElementById('errorMessageMunicipio'); // const noDataMessageMunicipio = document.getElementById('noDataMessageMunicipio'); // loadingMessageMunicipio.style.display = 'block'; // chartCanvasMunicipio.style.display = 'none'; // errorMessageMunicipio.style.display = 'none'; // noDataMessageMunicipio.style.display = 'none'; // fetch('get_condicionantes_por_municipio.php') // .then(response => { // loadingMessageMunicipio.style.display = 'none'; // if (!response.ok) { // errorMessageMunicipio.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de município: ' + response.status); // } return response.json(); // }) .then(data => { // if (data.error) { // errorMessageMunicipio.style.display = 'block'; // errorMessageMunicipio.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (data.length === 0) { // noDataMessageMunicipio.style.display = 'block'; // return; // } renderChart('condicionantesMunicipioChart', data, 'Total de Condicionantes por Município', 'loadingMessageMunicipio', 'errorMessageMunicipio', 'noDataMessageMunicipio', 'bar'); // }) .catch(error => { // loadingMessageMunicipio.style.display = 'none'; // errorMessageMunicipio.style.display = 'block'; // console.error('Erro ao carregar dados do gráfico de condicionantes por município:', error); // }); } // 5. Fetch para o Gráfico de Condicionantes por Prioridade const chartCanvasPrioridade = document.getElementById('prioridadeChart'); // if (chartCanvasPrioridade) { // const loadingMessagePrioridade = document.getElementById('loadingMessagePrioridade'); // const errorMessagePrioridade = document.getElementById('errorMessagePrioridade'); // const noDataMessagePrioridade = document.getElementById('noDataMessagePrioridade'); // loadingMessagePrioridade.style.display = 'block'; // chartCanvasPrioridade.style.display = 'none'; // errorMessagePrioridade.style.display = 'none'; // noDataMessagePrioridade.style.display = 'none'; // fetch('get_condicionantes_por_prioridade.php') // .then(response => { // loadingMessagePrioridade.style.display = 'none'; // if (!response.ok) { // errorMessagePrioridade.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de prioridade: ' + response.status); // } return response.json(); // }) .then(data => { // if (data.error) { // errorMessagePrioridade.style.display = 'block'; // errorMessagePrioridade.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (data.length === 0) { // noDataMessagePrioridade.style.display = 'block'; // return; // } renderChart('prioridadeChart', data, 'Distribuição por Prioridade', 'loadingMessagePrioridade', 'errorMessagePrioridade', 'noDataMessagePrioridade', 'pie'); // }) .catch(error => { // loadingMessagePrioridade.style.display = 'none'; // errorMessagePrioridade.style.display = 'block'; // console.error('Erro ao carregar dados do gráfico de prioridade:', error); // }); } // 6. Fetch para o Gráfico de Condicionantes por Local const chartCanvasLocalCount = document.getElementById('condicionantesPorLocalChart'); // if (chartCanvasLocalCount) { // const loadingMessageLocalCount = document.getElementById('loadingMessageLocalCount'); // const errorMessageLocalCount = document.getElementById('errorMessageLocalCount'); // const noDataMessageLocalCount = document.getElementById('noDataMessageLocalCount'); // loadingMessageLocalCount.style.display = 'block'; // chartCanvasLocalCount.style.display = 'none'; // errorMessageLocalCount.style.display = 'none'; // noDataMessageLocalCount.style.display = 'none'; // fetch('get_condicionantes_por_local.php') // .then(response => { // loadingMessageLocalCount.style.display = 'none'; // if (!response.ok) { // errorMessageLocalCount.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de condicionantes por local: ' + response.status); // } return response.json(); // }) .then(data => { // if (data.error) { // errorMessageLocalCount.style.display = 'block'; // errorMessageLocalCount.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (data.length === 0) { // noDataMessageLocalCount.style.display = 'block'; // return; // } renderChart('condicionantesPorLocalChart', data, 'Total de Condicionantes por Local', 'loadingMessageLocalCount', 'errorMessageLocalCount', 'noDataMessageLocalCount', 'bar'); // }) .catch(error => { // loadingMessageLocalCount.style.display = 'none'; // errorMessageLocalCount.style.display = 'block'; // console.error('Erro ao carregar dados do gráfico de condicionantes por local:', error); // }); } // 7. Fetch para o Gráfico de Condicionantes por Categoria e Importância (Stacked Bar) const chartCanvasCategoriaImportancia = document.getElementById('categoriaImportanciaChart'); // if (chartCanvasCategoriaImportancia) { // const loadingMessageCategoriaImportancia = document.getElementById('loadingMessageCategoriaImportancia'); // const errorMessageCategoriaImportancia = document.getElementById('errorMessageCategoriaImportancia'); // const noDataMessageCategoriaImportancia = document.getElementById('noDataMessageCategoriaImportancia'); // loadingMessageCategoriaImportancia.style.display = 'block'; // chartCanvasCategoriaImportancia.style.display = 'none'; // errorMessageCategoriaImportancia.style.display = 'none'; // noDataMessageCategoriaImportancia.style.display = 'none'; // fetch('get_condicionantes_por_categoria_importancia.php') // .then(response => { // loadingMessageCategoriaImportancia.style.display = 'none'; // if (!response.ok) { // errorMessageCategoriaImportancia.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de categoria e importância: ' + response.status); // } return response.json(); // }) .then(data => { // if (data.error) { // errorMessageCategoriaImportancia.style.display = 'block'; // errorMessageCategoriaImportancia.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (!data.labels || data.labels.length === 0) { // noDataMessageCategoriaImportancia.style.display = 'block'; // return; // } renderChart('categoriaImportanciaChart', data, 'Condicionantes por Categoria e Importância', 'loadingMessageCategoriaImportancia', 'errorMessageCategoriaImportancia', 'noDataMessageCategoriaImportancia', 'stackedBar'); // }) .catch(error => { // loadingMessageCategoriaImportancia.style.display = 'none'; // errorMessageCategoriaImportancia.style.display = 'block'; // console.error('Erro ao carregar dados do gráfico de categoria e importância:', error); // }); } // 8. Fetch para o Gráfico de Contagem Total por Categoria (Barra Simples) const chartCanvasCategoriaTotal = document.getElementById('myCategoriaChart'); // if (chartCanvasCategoriaTotal) { // const loadingMessageCategoriaTotal = document.getElementById('loadingMessageCategoriaTotal'); // const errorMessageCategoriaTotal = document.getElementById('errorMessageCategoriaTotal'); // const noDataMessageCategoriaTotal = document.getElementById('noDataMessageCategoriaTotal'); // loadingMessageCategoriaTotal.style.display = 'block'; // chartCanvasCategoriaTotal.style.display = 'none'; // errorMessageCategoriaTotal.style.display = 'none'; // noDataMessageCategoriaTotal.style.display = 'none'; // fetch('get_condicionantes_por_categoria.php') // .then(response => { // loadingMessageCategoriaTotal.style.display = 'none'; // if (!response.ok) { // errorMessageCategoriaTotal.style.display = 'block'; // throw new Error('Erro HTTP ao buscar dados de categoria: ' + response.status); // } return response.json(); // }) .then(data => { // console.log("Dados recebidos para o gráfico de Categoria Total:", data); // if (data.error) { // errorMessageCategoriaTotal.style.display = 'block'; // errorMessageCategoriaTotal.querySelector('p').innerText = 'Erro do servidor: ' + data.error; // console.error("Erro do servidor:", data.error); // return; // } if (!data.labels || data.labels.length === 0 || !data.datasets || data.datasets.length === 0) { // noDataMessageCategoriaTotal.style.display = 'block'; // return; // } renderChart( // 'myCategoriaChart', // data, // 'Total de Condicionantes por Categoria', // 'loadingMessageCategoriaTotal', // 'errorMessageCategoriaTotal', // 'noDataMessageCategoriaTotal', // 'bar' // ); }) .catch(error => { // loadingMessageCategoriaTotal.style.display = 'none'; // errorMessageCategoriaTotal.style.display = 'block'; // console.error('Erro ao carregar dados do gráfico de contagem por categoria:', error); // }); } }); // FIM DO document.addEventListener('DOMContentLoaded')