fde
This commit is contained in:
parent
68910f3853
commit
be5fe99fc9
|
|
@ -370,3 +370,275 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php include 'faceebene/ebenetraitementimage.php'; ?>
|
<?php include 'faceebene/ebenetraitementimage.php'; ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Configuration commune
|
||||||
|
(function() {
|
||||||
|
// Format numérique simplifié (sans devise)
|
||||||
|
const formatMoney = (value) => {
|
||||||
|
return new Intl.NumberFormat('fr-FR').format(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format pourcentage sécurisé
|
||||||
|
const formatPercentage = (value, total) => {
|
||||||
|
if (total === 0) {
|
||||||
|
return '0%'; // ou 'N/A' si tu préfères
|
||||||
|
}
|
||||||
|
const percentage = (value * 100 / total).toFixed(1);
|
||||||
|
return percentage + '%';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Détection mobile
|
||||||
|
const isMobile = window.matchMedia("(max-width: 768px)").matches;
|
||||||
|
|
||||||
|
// Génération des couleurs
|
||||||
|
const generateColors = (count) => {
|
||||||
|
const palette = [
|
||||||
|
'#4e73df', '#1cc88a', '#36b9cc', '#f6c23e',
|
||||||
|
'#e74a3b', '#858796', '#5a5c69', '#3a3b45',
|
||||||
|
'#2e59a9', '#17a673', '#2c9faf', '#dda20a'
|
||||||
|
];
|
||||||
|
return palette.slice(0, count).concat(
|
||||||
|
Array.from({length: Math.max(0, count - palette.length)}, (_, i) => {
|
||||||
|
const hue = Math.floor(360 * (i / Math.max(1, count - palette.length)));
|
||||||
|
return `hsl(${hue}, 70%, 60%)`;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fonction pour générer une légende personnalisée
|
||||||
|
function generateCustomLegend(chart, containerId) {
|
||||||
|
const legendContainer = document.getElementById(containerId);
|
||||||
|
legendContainer.innerHTML = '';
|
||||||
|
|
||||||
|
const items = chart.data.datasets[0].data.map((value, i) => {
|
||||||
|
const total = chart.data.datasets[0].data.reduce((a, b) => a + b, 0);
|
||||||
|
const percentage = formatPercentage(value, total);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="d-inline-block mx-2 my-1">
|
||||||
|
<span class="legend-color" style="
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: ${chart.data.datasets[0].backgroundColor[i]};
|
||||||
|
border: 1px solid #fff;
|
||||||
|
vertical-align: middle;
|
||||||
|
"></span>
|
||||||
|
<span class="legend-text small ml-1">
|
||||||
|
${chart.data.labels[i]}: ${formatMoney(value)} (${percentage})
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
legendContainer.innerHTML = items.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction pour exporter un graphique en PDF
|
||||||
|
function exportChartToPDF(chartId, fileName) {
|
||||||
|
const { jsPDF } = window.jspdf;
|
||||||
|
const canvas = document.getElementById(chartId);
|
||||||
|
|
||||||
|
html2canvas(canvas).then(canvasImage => {
|
||||||
|
const imgData = canvasImage.toDataURL('image/png');
|
||||||
|
const pdf = new jsPDF({
|
||||||
|
orientation: canvasImage.width > canvasImage.height ? 'landscape' : 'portrait'
|
||||||
|
});
|
||||||
|
|
||||||
|
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||||
|
const pageHeight = pdf.internal.pageSize.getHeight();
|
||||||
|
|
||||||
|
const ratio = canvasImage.height / canvasImage.width;
|
||||||
|
let imgWidth = pageWidth - 20;
|
||||||
|
let imgHeight = imgWidth * ratio;
|
||||||
|
|
||||||
|
if (imgHeight > pageHeight - 20) {
|
||||||
|
imgHeight = pageHeight - 20;
|
||||||
|
imgWidth = imgHeight / ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf.addImage(imgData, 'PNG',
|
||||||
|
(pageWidth - imgWidth) / 2,
|
||||||
|
(pageHeight - imgHeight) / 2,
|
||||||
|
imgWidth,
|
||||||
|
imgHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
pdf.save(fileName + '.pdf');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graphique d'évolution des dépenses
|
||||||
|
const dataMois = <?= $dataConsoParMois ?>;
|
||||||
|
const trendCtx = document.getElementById('expenseTrendChart').getContext('2d');
|
||||||
|
const trendChart = new Chart(trendCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: dataMois.mois,
|
||||||
|
datasets: [{
|
||||||
|
label: '<?= _("Montant dépensé") ?>',
|
||||||
|
data: dataMois.consos,
|
||||||
|
backgroundColor: 'rgba(78, 115, 223, 0.05)',
|
||||||
|
borderColor: 'rgba(78, 115, 223, 1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: 'rgba(78, 115, 223, 1)',
|
||||||
|
pointRadius: isMobile ? 3 : 4,
|
||||||
|
pointHoverRadius: 6,
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return context.dataset.label + ': ' + formatMoney(context.parsed.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
maxRotation: isMobile ? 45 : 0,
|
||||||
|
autoSkip: true,
|
||||||
|
maxTicksLimit: isMobile ? 6 : 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: false,
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return formatMoney(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: 'rgba(0, 0, 0, 0.05)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'nearest',
|
||||||
|
axis: 'x',
|
||||||
|
intersect: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Graphique des dépenses par garantie
|
||||||
|
const dataConso = <?= $dataConsoParGaranties ?>;
|
||||||
|
const barCtx = document.getElementById('depensesChart').getContext('2d');
|
||||||
|
const barChart = new Chart(barCtx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: dataConso.garanties,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Dépenses',
|
||||||
|
data: dataConso.depenses,
|
||||||
|
backgroundColor: 'rgba(54, 162, 235, 0.7)',
|
||||||
|
borderColor: 'rgba(54, 162, 235, 1)',
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
annotation: {
|
||||||
|
annotations: dataConso.plafonds.map((plafond, index) => {
|
||||||
|
if (plafond === null) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'line',
|
||||||
|
yMin: plafond,
|
||||||
|
yMax: plafond,
|
||||||
|
borderColor: 'rgba(255, 99, 132, 0.7)',
|
||||||
|
borderWidth: 2,
|
||||||
|
borderDash: [6, 6],
|
||||||
|
label: {
|
||||||
|
content: `Plafond: ${formatMoney(plafond)}`,
|
||||||
|
enabled: true,
|
||||||
|
position: 'right'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}).filter(annotation => annotation !== null)
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
const plafond = dataConso.plafonds[context.dataIndex];
|
||||||
|
let tooltip = `Dépenses: ${formatMoney(context.parsed.y)}`;
|
||||||
|
|
||||||
|
if (plafond !== null) {
|
||||||
|
const pourcentage = Math.min(100, Math.round((context.parsed.y / plafond) * 100));
|
||||||
|
tooltip += ` (${pourcentage}% du plafond)`;
|
||||||
|
} else {
|
||||||
|
tooltip += ' (plafond illimité)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: `Montant des dépenses`,
|
||||||
|
font: {
|
||||||
|
weight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
callback: function(value) {
|
||||||
|
return formatMoney(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
suggestedMax: Math.max(...dataConso.depenses) * 1.5
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Garanties',
|
||||||
|
font: {
|
||||||
|
weight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Boutons d'export PDF
|
||||||
|
document.getElementById('exportTrendBtn').addEventListener('click', () => {
|
||||||
|
exportChartToPDF('expenseTrendChart', 'evolution_depenses_mensuelles');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('exportBarBtn').addEventListener('click', () => {
|
||||||
|
exportChartToPDF('depensesChart', 'depenses_par_garantie');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue
Block a user