719 lines
33 KiB
Twig
719 lines
33 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}Stats détaillées - {{ stats.name }}{% endblock %}
|
|
|
|
{% block stylesheets %}
|
|
{{ parent() }}
|
|
<link href='{{ asset('css/city-sidebar.css') }}' rel='stylesheet'/>
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<!-- Sidebar de navigation -->
|
|
<div class="col-12">
|
|
{% include 'admin/_city_sidebar.html.twig' with {'stats': stats, 'active_menu': 'followup_detailed_stats'} %}
|
|
</div>
|
|
|
|
<!-- Contenu principal -->
|
|
<div class="col-md-9 col-lg-10 main-content">
|
|
<div class="p-4">
|
|
<h1>Stats détaillées - {{ stats.name }} ({{ stats.zone }})</h1>
|
|
<p class="text-muted">Sélectionnez les thématiques à afficher pour comparer les courbes de décompte et complétion.</p>
|
|
|
|
<!-- Sélection des thématiques -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-check2-square"></i> Sélection des thématiques</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
{% for theme, label in followup_labels %}
|
|
<div class="col-md-4 col-lg-3 mb-2">
|
|
<div class="form-check">
|
|
<input class="form-check-input theme-checkbox" type="checkbox"
|
|
value="{{ theme }}" id="theme_{{ theme }}"
|
|
data-label="{{ label }}"
|
|
data-icon="{{ followup_icons[theme]|default('bi-question-circle') }}">
|
|
<label class="form-check-label" for="theme_{{ theme }}">
|
|
<i class="bi {{ followup_icons[theme]|default('bi-question-circle') }}"></i> {{ label }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="mt-3">
|
|
<button type="button" class="btn btn-sm btn-outline-primary" id="selectAll">Tout sélectionner</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" id="deselectAll">Tout désélectionner</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Options d'affichage -->
|
|
<div class="card mb-3">
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="showCount" checked>
|
|
<label class="form-check-label" for="showCount">
|
|
Afficher les décomptes
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="showCompletion" checked>
|
|
<label class="form-check-label" for="showCompletion">
|
|
Afficher les complétions
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="separateCharts" checked>
|
|
<label class="form-check-label" for="separateCharts">
|
|
Afficher deux graphiques séparés (décomptes et complétions)
|
|
</label>
|
|
</div>
|
|
<small class="text-muted">Décochez pour afficher un seul graphique combiné avec deux axes Y</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Graphique combiné (un seul) -->
|
|
<div class="card mb-4" id="combinedChartCard" style="display: none;">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-graph-up"></i> Graphique combiné</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="combinedChartContainer">
|
|
<canvas id="combinedChart" style="max-height: 500px;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Graphique des décomptes -->
|
|
<div class="card mb-4" id="countChartCard">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-bar-chart"></i> Graphique des décomptes</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="countChartContainer">
|
|
<canvas id="countChart" style="max-height: 500px;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Graphique des complétions -->
|
|
<div class="card" id="completionChartCard">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-percent"></i> Graphique des complétions (%)</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="completionChartContainer">
|
|
<canvas id="completionChart" style="max-height: 500px;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}"
|
|
class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Retour à la fiche ville</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block javascripts %}
|
|
{{ parent() }}
|
|
<script src="/js/chartjs/chart.umd.js"></script>
|
|
<script src="/js/chartjs/chartjs-adapter-date-fns.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>
|
|
<script>
|
|
const inseeCode = '{{ stats.zone }}';
|
|
const apiUrl = '{{ path('api_stats_followup_by_insee', {'insee': stats.zone}) }}';
|
|
let countChartInstance = null;
|
|
let completionChartInstance = null;
|
|
let combinedChartInstance = null;
|
|
let allData = null;
|
|
|
|
// Couleurs pour les différentes thématiques
|
|
const colors = [
|
|
{ count: 'rgba(54, 162, 235, 1)', completion: 'rgba(75, 192, 192, 1)' },
|
|
{ count: 'rgba(255, 99, 132, 1)', completion: 'rgba(255, 159, 64, 1)' },
|
|
{ count: 'rgba(153, 102, 255, 1)', completion: 'rgba(201, 203, 207, 1)' },
|
|
{ count: 'rgba(255, 205, 86, 1)', completion: 'rgba(54, 162, 235, 0.5)' },
|
|
{ count: 'rgba(75, 192, 192, 1)', completion: 'rgba(255, 99, 132, 0.5)' },
|
|
{ count: 'rgba(255, 159, 64, 1)', completion: 'rgba(153, 102, 255, 0.5)' },
|
|
{ count: 'rgba(201, 203, 207, 1)', completion: 'rgba(255, 205, 86, 0.5)' },
|
|
{ count: 'rgba(255, 99, 132, 0.5)', completion: 'rgba(75, 192, 192, 0.5)' },
|
|
];
|
|
|
|
// Charger les données depuis l'API
|
|
async function loadData() {
|
|
try {
|
|
console.log('Chargement des données depuis:', apiUrl);
|
|
const response = await fetch(apiUrl);
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error('Erreur HTTP:', response.status, errorText);
|
|
throw new Error(`Erreur ${response.status}: ${errorText}`);
|
|
}
|
|
allData = await response.json();
|
|
console.log('Données chargées:', allData);
|
|
updateChart();
|
|
} catch (error) {
|
|
console.error('Erreur:', error);
|
|
const countContainer = document.getElementById('countChartContainer');
|
|
const completionContainer = document.getElementById('completionChartContainer');
|
|
if (countContainer) {
|
|
countContainer.innerHTML = '<p class="text-danger">Erreur lors du chargement des données: ' + error.message + '</p>';
|
|
}
|
|
if (completionContainer) {
|
|
completionContainer.innerHTML = '<p class="text-danger">Erreur lors du chargement des données: ' + error.message + '</p>';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mettre à jour les graphiques
|
|
function updateChart() {
|
|
if (!allData) {
|
|
console.warn('Aucune donnée chargée');
|
|
return;
|
|
}
|
|
|
|
const showCount = document.getElementById('showCount').checked;
|
|
const showCompletion = document.getElementById('showCompletion').checked;
|
|
const separateCharts = document.getElementById('separateCharts').checked;
|
|
const selectedThemes = Array.from(document.querySelectorAll('.theme-checkbox:checked'))
|
|
.map(cb => cb.value);
|
|
|
|
// Afficher/masquer les conteneurs selon le mode
|
|
const combinedCard = document.getElementById('combinedChartCard');
|
|
const countCard = document.getElementById('countChartCard');
|
|
const completionCard = document.getElementById('completionChartCard');
|
|
|
|
if (separateCharts) {
|
|
combinedCard.style.display = 'none';
|
|
countCard.style.display = showCount ? 'block' : 'none';
|
|
completionCard.style.display = showCompletion ? 'block' : 'none';
|
|
} else {
|
|
combinedCard.style.display = (showCount || showCompletion) ? 'block' : 'none';
|
|
countCard.style.display = 'none';
|
|
completionCard.style.display = 'none';
|
|
}
|
|
|
|
if (selectedThemes.length === 0) {
|
|
// Détruire les graphiques existants
|
|
if (countChartInstance) {
|
|
countChartInstance.destroy();
|
|
countChartInstance = null;
|
|
}
|
|
if (completionChartInstance) {
|
|
completionChartInstance.destroy();
|
|
completionChartInstance = null;
|
|
}
|
|
if (combinedChartInstance) {
|
|
combinedChartInstance.destroy();
|
|
combinedChartInstance = null;
|
|
}
|
|
document.getElementById('countChartContainer').innerHTML = '<p class="text-muted">Sélectionnez au moins une thématique pour afficher le graphique.</p>';
|
|
document.getElementById('completionChartContainer').innerHTML = '<p class="text-muted">Sélectionnez au moins une thématique pour afficher le graphique.</p>';
|
|
document.getElementById('combinedChartContainer').innerHTML = '<p class="text-muted">Sélectionnez au moins une thématique pour afficher le graphique.</p>';
|
|
return;
|
|
}
|
|
|
|
// Préparer les données pour les décomptes
|
|
const countDatasets = [];
|
|
const completionDatasets = [];
|
|
let colorIndex = 0;
|
|
|
|
selectedThemes.forEach(theme => {
|
|
const themeData = allData.themes[theme];
|
|
if (!themeData) {
|
|
console.warn(`Pas de données pour le thème: ${theme}`);
|
|
return;
|
|
}
|
|
|
|
const checkbox = document.getElementById('theme_' + theme);
|
|
const label = checkbox ? checkbox.dataset.label : theme;
|
|
const color = colors[colorIndex % colors.length];
|
|
|
|
// Graphique des décomptes (seulement si showCount est coché)
|
|
if (showCount && themeData.count && themeData.count.length > 0) {
|
|
countDatasets.push({
|
|
label: label,
|
|
data: themeData.count.map(d => ({ x: d.date, y: d.value })),
|
|
borderColor: color.count,
|
|
backgroundColor: color.count.replace('1)', '0.1)'),
|
|
fill: false,
|
|
tension: 0.3,
|
|
});
|
|
}
|
|
|
|
// Graphique des complétions (seulement si showCompletion est coché)
|
|
if (showCompletion && themeData.completion && themeData.completion.length > 0) {
|
|
completionDatasets.push({
|
|
label: label,
|
|
data: themeData.completion.map(d => ({ x: d.date, y: d.value })),
|
|
borderColor: color.completion,
|
|
backgroundColor: color.completion.replace('1)', '0.1)'),
|
|
fill: false,
|
|
tension: 0.3,
|
|
});
|
|
}
|
|
|
|
colorIndex++;
|
|
});
|
|
|
|
if (separateCharts) {
|
|
// Créer/mettre à jour les graphiques séparés selon les cases cochées
|
|
if (showCount) {
|
|
updateCountChart(countDatasets);
|
|
} else {
|
|
if (countChartInstance) {
|
|
countChartInstance.destroy();
|
|
countChartInstance = null;
|
|
}
|
|
}
|
|
|
|
if (showCompletion) {
|
|
updateCompletionChart(completionDatasets);
|
|
} else {
|
|
if (completionChartInstance) {
|
|
completionChartInstance.destroy();
|
|
completionChartInstance = null;
|
|
}
|
|
}
|
|
|
|
// Détruire le graphique combiné s'il existe
|
|
if (combinedChartInstance) {
|
|
combinedChartInstance.destroy();
|
|
combinedChartInstance = null;
|
|
}
|
|
} else {
|
|
// Créer/mettre à jour le graphique combiné
|
|
if (showCount || showCompletion) {
|
|
updateCombinedChart(countDatasets, completionDatasets);
|
|
} else {
|
|
if (combinedChartInstance) {
|
|
combinedChartInstance.destroy();
|
|
combinedChartInstance = null;
|
|
}
|
|
}
|
|
|
|
// Détruire les graphiques séparés s'ils existent
|
|
if (countChartInstance) {
|
|
countChartInstance.destroy();
|
|
countChartInstance = null;
|
|
}
|
|
if (completionChartInstance) {
|
|
completionChartInstance.destroy();
|
|
completionChartInstance = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mettre à jour le graphique des décomptes
|
|
function updateCountChart(datasets) {
|
|
const container = document.getElementById('countChartContainer');
|
|
if (!container) return;
|
|
|
|
if (datasets.length === 0) {
|
|
if (countChartInstance) {
|
|
countChartInstance.destroy();
|
|
countChartInstance = null;
|
|
}
|
|
container.innerHTML = '<p class="text-muted">Aucune donnée de décompte disponible pour les thématiques sélectionnées.</p>';
|
|
return;
|
|
}
|
|
|
|
// Restaurer le canvas si nécessaire
|
|
if (container.innerHTML.includes('<p')) {
|
|
container.innerHTML = '<canvas id="countChart" style="max-height: 500px;"></canvas>';
|
|
}
|
|
|
|
const canvas = document.getElementById('countChart');
|
|
if (!canvas) {
|
|
console.error('Impossible de trouver le canvas des décomptes');
|
|
return;
|
|
}
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) {
|
|
console.error('Impossible d\'obtenir le contexte 2D pour les décomptes');
|
|
return;
|
|
}
|
|
|
|
if (countChartInstance) {
|
|
countChartInstance.destroy();
|
|
}
|
|
|
|
try {
|
|
countChartInstance = new Chart(ctx, {
|
|
type: 'line',
|
|
data: { datasets },
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
parsing: {
|
|
xAxisKey: 'x',
|
|
yAxisKey: 'y'
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'top',
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
callbacks: {
|
|
title: function(context) {
|
|
if (context && context.length > 0 && context[0].parsed && context[0].parsed.x) {
|
|
return new Date(context[0].parsed.x).toLocaleDateString('fr-FR');
|
|
}
|
|
return '';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'day',
|
|
displayFormats: {
|
|
day: 'dd/MM/yyyy'
|
|
}
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: 'Date'
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: "Nombre d'objets"
|
|
},
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
console.log('Graphique des décomptes créé avec succès', datasets.length, 'datasets');
|
|
} catch (error) {
|
|
console.error('Erreur lors de la création du graphique des décomptes:', error);
|
|
}
|
|
}
|
|
|
|
// Mettre à jour le graphique des complétions
|
|
function updateCompletionChart(datasets) {
|
|
const container = document.getElementById('completionChartContainer');
|
|
if (!container) return;
|
|
|
|
if (datasets.length === 0) {
|
|
if (completionChartInstance) {
|
|
completionChartInstance.destroy();
|
|
completionChartInstance = null;
|
|
}
|
|
container.innerHTML = '<p class="text-muted">Aucune donnée de complétion disponible pour les thématiques sélectionnées.</p>';
|
|
return;
|
|
}
|
|
|
|
// Restaurer le canvas si nécessaire
|
|
if (container.innerHTML.includes('<p')) {
|
|
container.innerHTML = '<canvas id="completionChart" style="max-height: 500px;"></canvas>';
|
|
}
|
|
|
|
const canvas = document.getElementById('completionChart');
|
|
if (!canvas) {
|
|
console.error('Impossible de trouver le canvas des complétions');
|
|
return;
|
|
}
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) {
|
|
console.error('Impossible d\'obtenir le contexte 2D pour les complétions');
|
|
return;
|
|
}
|
|
|
|
if (completionChartInstance) {
|
|
completionChartInstance.destroy();
|
|
}
|
|
|
|
try {
|
|
completionChartInstance = new Chart(ctx, {
|
|
type: 'line',
|
|
data: { datasets },
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
parsing: {
|
|
xAxisKey: 'x',
|
|
yAxisKey: 'y'
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'top',
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
callbacks: {
|
|
title: function(context) {
|
|
if (context && context.length > 0 && context[0].parsed && context[0].parsed.x) {
|
|
return new Date(context[0].parsed.x).toLocaleDateString('fr-FR');
|
|
}
|
|
return '';
|
|
},
|
|
label: function(context) {
|
|
return context.dataset.label + ': ' + context.parsed.y.toFixed(1) + '%';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'day',
|
|
displayFormats: {
|
|
day: 'dd/MM/yyyy'
|
|
}
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: 'Date'
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: 'Complétion (%)'
|
|
},
|
|
min: 0,
|
|
max: 100,
|
|
ticks: {
|
|
callback: function(value) {
|
|
return value + '%';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
console.log('Graphique des complétions créé avec succès', datasets.length, 'datasets');
|
|
} catch (error) {
|
|
console.error('Erreur lors de la création du graphique des complétions:', error);
|
|
}
|
|
}
|
|
|
|
// Mettre à jour le graphique combiné
|
|
function updateCombinedChart(countDatasets, completionDatasets) {
|
|
const container = document.getElementById('combinedChartContainer');
|
|
if (!container) return;
|
|
|
|
const showCount = document.getElementById('showCount').checked;
|
|
const showCompletion = document.getElementById('showCompletion').checked;
|
|
|
|
// Combiner les datasets
|
|
const allDatasets = [];
|
|
let colorIndex = 0;
|
|
|
|
// Ajouter les datasets de décompte (seulement si showCount est coché)
|
|
if (showCount) {
|
|
countDatasets.forEach(dataset => {
|
|
allDatasets.push({
|
|
...dataset,
|
|
borderColor: colors[colorIndex % colors.length].count,
|
|
backgroundColor: colors[colorIndex % colors.length].count.replace('1)', '0.1)'),
|
|
yAxisID: 'y',
|
|
});
|
|
colorIndex++;
|
|
});
|
|
}
|
|
|
|
// Réinitialiser l'index pour les complétions si on a des décomptes
|
|
if (!showCount) {
|
|
colorIndex = 0;
|
|
}
|
|
|
|
// Ajouter les datasets de complétion (seulement si showCompletion est coché)
|
|
if (showCompletion) {
|
|
completionDatasets.forEach(dataset => {
|
|
allDatasets.push({
|
|
...dataset,
|
|
borderColor: colors[colorIndex % colors.length].completion,
|
|
backgroundColor: colors[colorIndex % colors.length].completion.replace('1)', '0.1)'),
|
|
yAxisID: showCount ? 'y1' : 'y', // Utiliser y1 seulement si on a aussi des décomptes
|
|
borderDash: showCount ? [5, 5] : [], // Pointillés seulement si on a aussi des décomptes
|
|
});
|
|
colorIndex++;
|
|
});
|
|
}
|
|
|
|
if (allDatasets.length === 0) {
|
|
if (combinedChartInstance) {
|
|
combinedChartInstance.destroy();
|
|
combinedChartInstance = null;
|
|
}
|
|
container.innerHTML = '<p class="text-muted">Aucune donnée disponible pour les thématiques sélectionnées.</p>';
|
|
return;
|
|
}
|
|
|
|
// Restaurer le canvas si nécessaire
|
|
if (container.innerHTML.includes('<p')) {
|
|
container.innerHTML = '<canvas id="combinedChart" style="max-height: 500px;"></canvas>';
|
|
}
|
|
|
|
const canvas = document.getElementById('combinedChart');
|
|
if (!canvas) {
|
|
console.error('Impossible de trouver le canvas combiné');
|
|
return;
|
|
}
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) {
|
|
console.error('Impossible d\'obtenir le contexte 2D pour le graphique combiné');
|
|
return;
|
|
}
|
|
|
|
if (combinedChartInstance) {
|
|
combinedChartInstance.destroy();
|
|
}
|
|
|
|
try {
|
|
combinedChartInstance = new Chart(ctx, {
|
|
type: 'line',
|
|
data: { datasets: allDatasets },
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
parsing: {
|
|
xAxisKey: 'x',
|
|
yAxisKey: 'y'
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: true,
|
|
position: 'top',
|
|
},
|
|
tooltip: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
callbacks: {
|
|
title: function(context) {
|
|
if (context && context.length > 0 && context[0].parsed && context[0].parsed.x) {
|
|
return new Date(context[0].parsed.x).toLocaleDateString('fr-FR');
|
|
}
|
|
return '';
|
|
},
|
|
label: function(context) {
|
|
let label = context.dataset.label || '';
|
|
if (context.parsed.y !== null) {
|
|
// Si c'est une complétion (axe y1), ajouter le %
|
|
if (context.dataset.yAxisID === 'y1') {
|
|
label += ': ' + context.parsed.y.toFixed(1) + '%';
|
|
} else {
|
|
label += ': ' + context.parsed.y;
|
|
}
|
|
}
|
|
return label;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'day',
|
|
displayFormats: {
|
|
day: 'dd/MM/yyyy'
|
|
}
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: 'Date'
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: showCount && showCompletion ? "Nombre d'objets" : (showCount ? "Nombre d'objets" : 'Complétion (%)')
|
|
},
|
|
beginAtZero: showCount || !showCompletion,
|
|
...(showCompletion && !showCount ? {
|
|
min: 0,
|
|
max: 100,
|
|
ticks: {
|
|
callback: function(value) {
|
|
return value + '%';
|
|
}
|
|
}
|
|
} : {})
|
|
},
|
|
...(showCount && showCompletion ? {
|
|
y1: {
|
|
type: 'linear',
|
|
position: 'right',
|
|
title: {
|
|
display: true,
|
|
text: 'Complétion (%)'
|
|
},
|
|
min: 0,
|
|
max: 100,
|
|
grid: {
|
|
drawOnChartArea: false
|
|
},
|
|
ticks: {
|
|
callback: function(value) {
|
|
return value + '%';
|
|
}
|
|
}
|
|
}
|
|
} : {})
|
|
}
|
|
}
|
|
});
|
|
console.log('Graphique combiné créé avec succès', allDatasets.length, 'datasets');
|
|
} catch (error) {
|
|
console.error('Erreur lors de la création du graphique combiné:', error);
|
|
}
|
|
}
|
|
|
|
// Événements
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadData();
|
|
|
|
// Écouter les changements de sélection
|
|
document.querySelectorAll('.theme-checkbox').forEach(cb => {
|
|
cb.addEventListener('change', updateChart);
|
|
});
|
|
|
|
// Écouter les changements d'options d'affichage
|
|
document.getElementById('showCount').addEventListener('change', updateChart);
|
|
document.getElementById('showCompletion').addEventListener('change', updateChart);
|
|
document.getElementById('separateCharts').addEventListener('change', updateChart);
|
|
|
|
// Boutons sélection/désélection
|
|
document.getElementById('selectAll').addEventListener('click', function() {
|
|
document.querySelectorAll('.theme-checkbox').forEach(cb => cb.checked = true);
|
|
updateChart();
|
|
});
|
|
|
|
document.getElementById('deselectAll').addEventListener('click', function() {
|
|
document.querySelectorAll('.theme-checkbox').forEach(cb => cb.checked = false);
|
|
updateChart();
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|