This commit is contained in:
KONE SOREL 2025-12-31 10:58:28 +00:00
parent ea2242f79a
commit 503d141996
2 changed files with 270 additions and 158 deletions

View File

@ -61,8 +61,9 @@ $(function() {
// On ne lance le timer que si l'utilisateur est connecté (pas sur la vue Connexion) // On ne lance le timer que si l'utilisateur est connecté (pas sur la vue Connexion)
if (vueActuelle !== "Connexion") { if (vueActuelle !== "Connexion") {
setInterval(function() { setInterval(function() {
console.log("Actualisation gabarit");
raffraichier_gabarit(); raffraichier_gabarit();
/* On vérifie si l'onglet est actif pour éviter des requêtes inutiles /* On vérifie si l'onglet est actif pour éviter des requêtes inutiles
if (!document.hidden) { if (!document.hidden) {
@ -523,14 +524,7 @@ function afficher_quittances_police_periode() {
} }
// 3. Préparation de l'interface (Loader) // 3. Préparation de l'interface (Loader)
$conteneur.html(` showLoader("#div_quittancepolice", { size: 3 });
<div class="text-center my-5 py-5">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Chargement...</span>
</div>
<p class="mt-2 text-primary fw-bold">Chargement des quittances...</p>
</div>
`);
// 4. Appel AJAX // 4. Appel AJAX
$.ajax({ $.ajax({
@ -617,14 +611,8 @@ function imprimer_quittance_client(idQuittance) {
/* =================================================== /* ===================================================
* 3. Préparation visuelle (Spinner) * 3. Préparation visuelle (Spinner)
* =================================================== */ * =================================================== */
divExport.innerHTML = ` showLoader("#div_export_quittance", { size: 3 });
<div class="text-center my-5 py-5">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;">
<span class="visually-hidden">Chargement...</span>
</div>
<p class="mt-3 text-muted fw-bold">Génération du document client en cours...</p>
</div>
`;
/* =================================================== /* ===================================================
* 4. Initialisation de l'instance Bootstrap * 4. Initialisation de l'instance Bootstrap
@ -794,7 +782,7 @@ function adherents_police()
function afficher_adherents_police() function afficher_adherents_police()
{ {
$("#div_liste_adherent").html('<div style="padding-top:80px;"><img src="Bootstrap/images/loading.gif"/>&nbsp;&nbsp;<span style="font-size:15pt;">' + 'Veuillez patienter... / Please wait...' + '</span></div>'); showLoader("#div_liste_adherent", { size: 3 });
$.ajax({ $.ajax({
url: $("#racineWeb").val()+"Ajaxlisteadherent/", url: $("#racineWeb").val()+"Ajaxlisteadherent/",
@ -809,150 +797,274 @@ function afficher_adherents_police()
} }
function graphique_sinistre() { // --- I18n dictionary ---
// Spinner pendant le chargement const I18N = {
$("#div_graphique").html(` fr_FR: {
<div class="text-center my-5 py-5"> loading: "Affichage en cours...",
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"> exportPdf: "Exporter les graphiques en PDF",
<span class="visually-hidden">Chargement...</span> dashboardTitle: "Graphiques des sinistres",
</div> charts: {
<p class="mt-3 text-muted fw-bold">Affichage des graphiques en cours...</p> claimsTitle: "Répartition des sinistres",
</div> claimsCount: "Nombre de sinistres",
`); monthlyTitle: "Évolution mensuelle",
monthlyCumulative: "Sinistres cumulés",
monthlySingle: "Sinistres uniques",
lossRatioTitle: "Ratio de sinistralité",
lossRatioLabel: "Ratio de sinistralité"
},
months: ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Août", "Sep", "Oct", "Nov", "Déc"],
errors: {
ajax: "Impossible de charger les graphiques.",
pdfLib: "Erreur : jsPDF n'est pas chargé."
}
},
en_US: {
loading: "Loading in progress...",
exportPdf: "Export charts to PDF",
dashboardTitle: "Claims Charts",
charts: {
claimsTitle: "Claims distribution",
claimsCount: "Number of claims",
monthlyTitle: "Monthly trend",
monthlyCumulative: "Claims cumulative",
monthlySingle: "Claims single",
lossRatioTitle: "Loss ratio",
lossRatioLabel: "Loss ratio"
},
months: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
errors: {
ajax: "Unable to load charts.",
pdfLib: "Error: jsPDF is not loaded."
}
}
};
$.ajax({ // --- Helpers ---
url: $("#racineWeb").val() + "Ajaxgraphiquesinistres/api/", function getLang() {
type: 'get', const codeLangue = $("#codeLangue").val() || "fr_FR";
dataType: 'json', return I18N[codeLangue] ? codeLangue : "fr_FR";
success: function(data) { }
console.log("Réponse JSON reçue:", data);
function t(path) {
// Layout HTML const lang = getLang();
$("#div_graphique").html(` return path.split(".").reduce((acc, key) => (acc && acc[key] != null ? acc[key] : null), I18N[lang]);
<div class="text-end my-3"> }
<button id="exportPdfBtn" class="btn btn-danger">
<i class="fas fa-file-pdf"></i> Exporter le tableau de bord en PDF // Optional: normalize backend month keys to localized labels
</button> function localizeMonths(months) {
</div> const map = {
<div class="row"> Jan: "Jan", Feb: "Feb", Mar: "Mar", Apr: "Apr", May: "May", Jun: "Jun",
<div class="col-md-6 mb-4"> Jul: "Jul", Aug: "Aug", Sep: "Sep", Oct: "Oct", Nov: "Nov", Dec: "Dec"
<div class="card shadow-sm"> };
<div class="card-body"> const langMonths = t("months");
<h5 class="card-title text-primary">Répartition des sinistres</h5> // If backend already provides localized months, keep them; else map to language months by index
<canvas id="claimsChart"></canvas> if (!Array.isArray(months) || months.length !== 12) return months;
</div> const canonical = months.map(m => map[m] || m);
</div> // Use the language month names but preserve array length and order
</div> return langMonths && langMonths.length === 12 ? langMonths : canonical;
<div class="col-md-6 mb-4"> }
<div class="card shadow-sm">
<div class="card-body"> function showLoader(selector, options = {}) {
<h5 class="card-title text-success">Évolution mensuelle</h5> const { message = null, size = 3 } = options;
<canvas id="claimsMonthChart"></canvas> const text = message || t("loading");
</div>
</div> const loaderHtml = `
</div> <div class="text-center my-5 py-5 js-generic-loader">
<div class="col-md-12 mb-4"> <div class="spinner-border text-primary" role="status" style="width: ${size}rem; height: ${size}rem;">
<div class="card shadow-sm"> <span class="visually-hidden">${text}</span>
<div class="card-body"> </div>
<h5 class="card-title text-danger">Ratio de sinistralité</h5> <p class="mt-3 text-muted fw-bold">${text}</p>
<canvas id="lossRatioChart"></canvas> </div>
</div> `;
</div> $(selector).html(loaderHtml);
</div> }
</div>
`); function hideLoader(selector, contentHtml = "") {
$(selector).html(contentHtml);
// --- Graphiques Chart.js --- }
// 1. Répartition function graphique_sinistre() {
new Chart(document.getElementById('claimsChart'), { showLoader("#div_graphique", { size: 3 });
type: 'bar',
data: { $.ajax({
labels: data.claims.claimsLabels, url: $("#racineWeb").val() + "Ajaxgraphiquesinistres/api/",
datasets: [{ type: "get",
label: 'Nombre de sinistres', dataType: "json",
data: data.claims.claimsValues, success: function (data) {
backgroundColor: 'rgba(54, 162, 235, 0.6)' const exportLabel = t("exportPdf");
}]
} const html = `
}); <div class="text-end my-3">
<button id="exportPdfBtn" class="btn btn-danger">
// 2. Évolution mensuelle <i class="fas fa-file-pdf"></i> ${exportLabel}
new Chart(document.getElementById('claimsMonthChart'), { </button>
type: 'line', </div>
data: { <div class="row">
labels: data.claimsMonth.months, <div class="col-md-6 mb-4">
datasets: [ <div class="card shadow-sm">
{ <div class="card-body">
label: 'Sinistres mensuels', <h5 class="card-title text-primary">${t("charts.claimsTitle")}</h5>
data: data.claimsMonth.monthlyClaims, <canvas id="claimsChart"></canvas>
borderColor: 'rgba(255, 99, 132, 0.8)', </div>
fill: false </div>
}, </div>
{ <div class="col-md-6 mb-4">
label: 'Sinistres uniques', <div class="card shadow-sm">
data: data.claimsMonth.singleClaims, <div class="card-body">
borderColor: 'rgba(75, 192, 192, 0.8)', <h5 class="card-title text-success">${t("charts.monthlyTitle")}</h5>
fill: false <canvas id="claimsMonthChart"></canvas>
} </div>
] </div>
} </div>
}); <div class="col-md-12 mb-4">
<div class="card shadow-sm">
// 3. Ratio de sinistralité <div class="card-body">
new Chart(document.getElementById('lossRatioChart'), { <h5 class="card-title text-danger">${t("charts.lossRatioTitle")}</h5>
type: 'line', <canvas id="lossRatioChart"></canvas>
data: { </div>
labels: data.lossRatio.lossRatioLabels, </div>
datasets: [{ </div>
label: 'Ratio de sinistralité', </div>
data: data.lossRatio.lossRatioValues, `;
borderColor: 'rgba(255, 206, 86, 0.8)', hideLoader("#div_graphique", html);
fill: false
}] // Render charts with localized labels
} renderClaimsChart(data.claims);
}); renderClaimsMonthChart(data.claimsMonth);
renderLossRatioChart(data.lossRatio);
// --- Export PDF ---
$("#exportPdfBtn").on("click", function() { setupExportPdf();
if (!window.jspdf) { },
alert("Erreur : jsPDF n'est pas chargé."); error: function (err) {
return; console.error("Erreur AJAX:", err);
} hideLoader("#div_graphique", `
<div class="alert alert-danger">${t("errors.ajax")}</div>
const { jsPDF } = window.jspdf; `);
const pdf = new jsPDF('p', 'mm', 'a4'); }
});
pdf.setFontSize(18); }
pdf.text("Tableau de bord - Synthèse", 10, 20);
// --- Charts ---
// Ajout des graphiques
addChartToPdf(pdf, 'claimsChart', 'Répartition des sinistres', 40); function renderClaimsChart(claims) {
addChartToPdf(pdf, 'claimsMonthChart', 'Évolution mensuelle', 120); new Chart(document.getElementById("claimsChart"), {
type: "bar",
pdf.addPage(); data: {
addChartToPdf(pdf, 'lossRatioChart', 'Ratio de sinistralité', 40); labels: claims.claimsLabels,
datasets: [{
pdf.save('Tableau_de_bord.pdf'); label: t("charts.claimsCount"),
}); data: claims.claimsValues,
backgroundColor: "rgba(54, 162, 235, 0.6)"
function addChartToPdf(pdf, canvasId, title, startY) { }]
const canvas = document.getElementById(canvasId); },
if (canvas) { options: {
const imgData = canvas.toDataURL('image/png', 1.0); plugins: {
pdf.setFontSize(14); legend: { display: true }
pdf.text(title, 10, startY); },
pdf.addImage(imgData, 'PNG', 10, startY + 5, 180, 60); scales: {
} x: { title: { display: false } },
} y: { title: { display: false }, beginAtZero: true }
}, }
error: function(err) { }
console.error("Erreur AJAX:", err); });
$("#div_graphique").html(` }
<div class="alert alert-danger">
Impossible de charger les graphiques. function renderClaimsMonthChart(claimsMonth) {
</div> const labels = localizeMonths(claimsMonth.months);
`);
} new Chart(document.getElementById("claimsMonthChart"), {
}); type: "line",
data: {
labels,
datasets: [
{
label: t("charts.monthlyCumulative"),
data: claimsMonth.monthlyClaims,
borderColor: "rgba(255, 99, 132, 0.8)",
backgroundColor: "rgba(255, 99, 132, 0.2)",
tension: 0.25,
fill: false
},
{
label: t("charts.monthlySingle"),
data: claimsMonth.singleClaims,
borderColor: "rgba(75, 192, 192, 0.8)",
backgroundColor: "rgba(75, 192, 192, 0.2)",
tension: 0.25,
fill: false
}
]
},
options: {
plugins: {
legend: { display: true }
},
scales: {
x: { title: { display: false } },
y: { title: { display: false }, beginAtZero: true }
}
}
});
}
function renderLossRatioChart(lossRatio) {
const labels = localizeMonths(lossRatio.lossRatioLabels);
new Chart(document.getElementById("lossRatioChart"), {
type: "line",
data: {
labels,
datasets: [{
label: t("charts.lossRatioLabel"),
data: lossRatio.lossRatioValues,
borderColor: "rgba(255, 206, 86, 0.8)",
backgroundColor: "rgba(255, 206, 86, 0.2)",
tension: 0.25,
fill: false
}]
},
options: {
plugins: {
legend: { display: true }
},
scales: {
x: { title: { display: false } },
y: { title: { display: false }, beginAtZero: true }
}
}
});
}
// --- Export PDF ---
function setupExportPdf() {
$("#exportPdfBtn").on("click", function () {
if (!window.jspdf) {
alert(t("errors.pdfLib"));
return;
}
const { jsPDF } = window.jspdf;
const pdf = new jsPDF("p", "mm", "a4");
pdf.setFontSize(18);
pdf.text(t("dashboardTitle"), 10, 20);
addChartToPdf(pdf, "claimsChart", t("charts.claimsTitle"), 40);
addChartToPdf(pdf, "claimsMonthChart", t("charts.monthlyTitle"), 120);
pdf.addPage();
addChartToPdf(pdf, "lossRatioChart", t("charts.lossRatioTitle"), 40);
pdf.save("Tableau_de_bord.pdf"); // you can also localize the filename if needed
});
}
function addChartToPdf(pdf, canvasId, title, startY) {
const canvas = document.getElementById(canvasId);
if (canvas) {
const imgData = canvas.toDataURL("image/png", 1.0);
pdf.setFontSize(14);
pdf.text(title, 10, startY);
pdf.addImage(imgData, "PNG", 10, startY + 5, 180, 60);
}
} }

View File

@ -599,7 +599,7 @@ $activeChildId = $menuData['child'];
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<!-- Application Scripts --> <!-- Application Scripts -->
<script src="/Js/fonctions.js?ver=2025.12.31.03"></script> <script src="/Js/fonctions.js?ver=2025.12.31.04"></script>
<?php if (est_anglophone()): ?> <?php if (est_anglophone()): ?>
<script src="/Js/datepicker-eng.js"></script> <script src="/Js/datepicker-eng.js"></script>