radiantrh/Js/fonctions.js
2025-12-31 14:26:00 +00:00

1087 lines
34 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* INITIALISATION GÉNÉRALE
*/
/**
* Gère la pré-connexion via cookie (chargement du formulaire et focus intelligent).
* Récupère le login mémorisé et place le curseur dans le champ approprié.
*/
function connexion_cookie() {
const racine = $("#racineWeb").val() || "";
const msgErreur = $("#msgErreur").val();
$.ajax({
url: racine + "Ajaxconnexioncookie/",
type: 'POST',
cache: false,
// Utilisation d'un objet pour un encodage automatique sécurisé
data: { msgErreur: msgErreur },
success: function(data) {
// Injection du formulaire de connexion
$("#div_ajaxconnexion").html(data);
// Initialisation des composants stylisés
if ($.isFunction($.fn.selectpicker)) {
$(".selectpicker").selectpicker('refresh');
}
},
error: function(xhr, status, error) {
console.error("Erreur lors du chargement des cookies de connexion:", error);
},
complete: function() {
// Gestion intelligente du focus
const $loginField = $("#login");
const $mdpField = $("#mdp");
const $codeSocieteField = $("#codeSociete");
// Si un login est déjà présent (issu du cookie)
if ($loginField.val() && $loginField.val().trim() !== "") {
// On place le curseur sur le mot de passe
$mdpField.focus();
} else {
// Sinon, on commence par le début (Code Société ou Login)
if ($codeSocieteField.length > 0) {
$codeSocieteField.focus();
} else {
$loginField.focus();
}
}
}
});
}
$(function() {
// Initialisation des composants au chargement de la page
appliquerDataTable('.tabliste');
const vueOuverte = $("#vue").val();
if(vueOuverte === "Accueil"){
reset_graphique();
}
});
/**
* Fonction principale de rafraîchissement du gabarit (UI et Session)
* Gère la mise à jour des données et la redirection forcée en cas de déconnexion.
*/
function raffraichier_gabarit() {
const racine = $("#racineWeb").val() || "/";
const vueActuelle = $("#vue").val();
const codeLangue = $("#codeLangue").val() || "fr_FR";
$.ajax({
url: racine + "Ajaxgabarit/",
type: 'GET',
cache: false,
success: function(data) {
// 1. On transforme la chaîne 'data' en objet jQuery pour manipulation
const $dataFrag = $(data);
// 2. Injection dans le DOM
$("#div_ajaxgabarit").html($dataFrag);
// 3. RECUPERATION SECURISEE DU CODE SOCIETE
// On essaie d'abord dans le DOM, puis dans le fragment reçu si le DOM est lent
let codeSociete = $("#div_ajaxgabarit").find("#codeSociete").val()
|| $dataFrag.filter("#codeSociete").val()
|| $("#codeSociete").val();
// Debug en console pour vérifier la capture
console.log("Check Session - codeSociete trouvé :", codeSociete);
// 4. VERIFICATION DE LA SESSION
if ((!codeSociete || codeSociete.trim() === "") && vueActuelle !== "Connexion") {
const msg = "Votre session a expiré. Veuillez vous reconnecter.";
const msgEng = "Your session has expired. Please log in again.";
// On bloque l'écran avec confirm_ebene
confirm_ebene(msg, msgEng).then(() => {
window.location.assign(racine + "Connexion/deconnecter/");
});
return;
}
// Si on arrive ici, la session est valide
},
error: function(xhr) {
// Si le serveur renvoie une erreur HTTP (401, 403, 500)
if (xhr.status === 401 && vueActuelle !== "Connexion") {
window.location.assign(racine + "Connexion/deconnecter/");
}
},
complete: function() {
// Ré-initialisation des composants sur le nouveau HTML
if ($.isFunction($.fn.datepicker)) {
$(".datepicker").datepicker();
}
/*
if (typeof raffraichier_messagerie === "function") {
raffraichier_messagerie();
}
*/
}
});
}
/**
* GESTION DU MENU BURGER
* Gère l'affichage mobile et la fermeture lors d'un clic extérieur.
*/
document.addEventListener('DOMContentLoaded', () => {
const burgerToggle = document.getElementById('burgerMenuToggle');
const burgerDropdown = document.getElementById('burgerDropdown');
if (burgerToggle && burgerDropdown) {
// Alterne la classe 'show' au clic sur le bouton
burgerToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
burgerDropdown.classList.toggle('show');
});
// Ferme le menu si on clique n'importe où ailleurs sur le document
document.addEventListener('click', (e) => {
if (!e.target.closest('.burger-menu-container')) {
burgerDropdown.classList.remove('show');
}
});
// Empêche la fermeture si on clique à l'intérieur du menu lui-même
burgerDropdown.addEventListener('click', (e) => e.stopPropagation());
}
});
/**
* FORMATAGE DES MESSAGES (Responsive)
* Ajoute des balises <br> pour forcer le retour à la ligne selon la taille d'écran.
*/
function formatMessageForSwal(message) {
if (!message) return '';
const screenWidth = window.innerWidth;
// Détermine la limite de caractères avant le saut de ligne
const maxLineLength = screenWidth < 576 ? 40 : (screenWidth < 768 ? 60 : 80);
if (message.length <= maxLineLength && !message.includes('\n')) return message;
const words = message.split(' ');
let lines = [];
let currentLine = '';
words.forEach(word => {
if ((currentLine + ' ' + word).length > maxLineLength && currentLine !== '') {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = currentLine ? currentLine + ' ' + word : word;
}
});
if (currentLine) lines.push(currentLine);
return lines.join('<br>');
}
/**
* AJUSTEMENT DYNAMIQUE SWEETALERT
* Redimensionne la popup et gère le scroll interne pour les longs textes.
*/
function adjustSwalContent() {
const popup = Swal.getPopup();
const title = Swal.getTitle();
const htmlContainer = Swal.getHtmlContainer();
if (popup && title) {
const screenWidth = window.innerWidth;
// Application de styles responsives directs
if (screenWidth < 576) {
Object.assign(popup.style, { maxWidth: '95vw', width: '95vw', margin: '10px' });
} else {
Object.assign(popup.style, { maxWidth: screenWidth < 768 ? '85vw' : '500px', width: '100%' });
}
// Gestion du scroll si le titre est trop grand
const maxTitleHeight = Math.min(window.innerHeight * 0.6, 400);
if (title.scrollHeight > maxTitleHeight) {
Object.assign(title.style, { overflowY: 'auto', maxHeight: maxTitleHeight + 'px', paddingRight: '10px' });
}
}
}
/**
* WRAPPER GÉNÉRIQUE SWEETALERT
* Centralise les paramètres communs pour alert_ebene, confirm_ebene, etc.
*/
function baseSwal(options) {
return Swal.fire({
...options,
customClass: {
popup: 'responsive-swal-popup',
title: 'responsive-swal-title',
htmlContainer: 'responsive-swal-html'
},
didOpen: adjustSwalContent,
willOpen: () => { document.body.style.overflow = 'hidden'; },
willClose: () => { document.body.style.overflow = 'auto'; }
});
}
/**
* ALERTE SIMPLE
* Affiche une information bilingue.
*/
function alert_ebene(p_msg, p_msg_eng) {
const codeLangue = $("#codeLangue").val();
const message = (codeLangue === "en_US") ? p_msg_eng : p_msg;
baseSwal({
title: formatMessageForSwal(message),
icon: 'info',
confirmButtonText: codeLangue === "en_US" ? 'OK' : 'D\'accord'
});
}
/**
* CONFIRMATION
* Affiche une boîte de dialogue Oui/Non et retourne une promesse.
*/
function confirm_ebene(p_msg, p_msg_eng) {
const codeLangue = $("#codeLangue").val();
const message = (codeLangue === "en_US") ? p_msg_eng : p_msg;
return baseSwal({
title: formatMessageForSwal(message),
icon: 'warning',
showCancelButton: true,
confirmButtonText: codeLangue === "en_US" ? 'Yes' : 'Oui',
cancelButtonText: codeLangue === "en_US" ? 'No' : 'Non'
}).then(result => result.isConfirmed);
}
/**
* SAISIE TEXTE (PROMPT)
* Ouvre une modale avec champ de saisie et exécute un callback avec la valeur.
*/
function prompt_ebene(p_msg, p_msg_eng, p_retour, callback) {
const codeLangue = $("#codeLangue").val();
const message = (codeLangue === "en_US") ? p_msg_eng : p_msg;
baseSwal({
title: formatMessageForSwal(message),
input: 'text',
inputValue: p_retour,
showCancelButton: true,
confirmButtonText: 'OK',
cancelButtonText: codeLangue === "en_US" ? 'Cancel' : 'Annuler'
}).then(result => {
callback(result.isConfirmed ? result.value : null);
});
}
/**
* CHANGEMENT DE MOT DE PASSE
* Demande confirmation avant redirection.
*/
function change_password() {
const v_msg = "Attention, vous serez déconnecté par la suite! Voulez-vous changer votre mot de passe?";
const v_msgEng = "Attention, you will be logged out afterwards! Do you want to change your password?";
confirm_ebene(v_msg, v_msgEng).then(isConfirmed => {
if (isConfirmed) {
window.location.assign($("#racineWeb").val() + "Changermotpass/");
}
});
}
/**
* AFFICHAGE BÉNÉFICIAIRE
* Prépare le contexte serveur avant de charger la fiche bénéficiaire.
*/
function ajax_context_beneficiaire_afficher(idBeneficiaire, okId) {
// Affiche un loader pendant le chargement
$("#contenu").html('<div class="spinner-responsive"><span><i class="fa fa-spinner fa-spin"></i></span></div>');
$.ajax({
url: $("#racineWeb").val() + "Ajaxcontextbeneficiaire/",
type: 'post',
data: { idBeneficiaire, okId },
complete: () => {
window.location.assign($("#racineWeb").val() + "Fichebeneficiaire/" + idBeneficiaire);
}
});
}
/**
* GESTION DE LA LANGUE À LA CONNEXION
* Met à jour le bloc de connexion via AJAX lors du changement de langue.
*/
function changer_langue_connexion() {
const codeLangue = $("#langue").val();
$.ajax({
url: $("#racineWeb").val() + "Ajaxconnexioncookie/changerlangue/",
type: 'post',
data: { codeLangue },
success: (data) => $("#div_detail_connexion").html(data),
complete: () => $(".selectpicker").selectpicker('refresh')
});
}
/**
* CONFIGURATION DATATABLES
* Initialise un tableau DataTable avec options génériques.
*
* @param {string|jQuery} selector - Sélecteur du tableau (ex: '.tabliste' ou '#myTable')
* @param {object} options - Options personnalisées (langue, boutons, ordre, etc.)
*/
function appliquerDataTable(selector = '.tabliste', options = {}) {
const codeLangue = $("#codeLangue").val() || 'fr_FR';
const translations = {
fr_FR: {
lengthMenu: "Affiche _MENU_ par page",
zeroRecords: "Aucune donnée trouvée",
info: "_PAGE_ sur _PAGES_",
search: "Recherche:",
paginate: { next: "►", previous: "◄" }
},
en_US: {
lengthMenu: "Display _MENU_ records",
zeroRecords: "Nothing found",
info: "Showing page _PAGE_ of _PAGES_",
search: "Search:",
paginate: { next: "►", previous: "◄" }
}
};
$(selector).each(function() {
const $table = $(this);
// Colonnes marquées comme 'data-hidden'
const hiddenTargets = [];
$table.find('thead th').each((idx, th) => {
if ($(th).data('hidden')) hiddenTargets.push(idx);
});
const instance = $table.DataTable($.extend(true, {
destroy: true,
responsive: true,
order: [[0, "desc"]],
language: translations[codeLangue] || translations.fr_FR,
columnDefs: [{ targets: hiddenTargets, visible: false }],
dom: 'Bfrtip',
buttons: ['copy', 'csv', 'excel', 'pdf', 'print']
}, options));
// Ajustement après rendu
$table.on('init.dt', function () {
if (instance && instance.responsive) {
instance.columns.adjust();
instance.responsive.recalc();
}
});
});
}
/**
* MESSAGERIE ET NOTIFICATIONS
* Récupère le nombre de messages et déconnecte si session expirée.
*/
function raffraichier_messagerie() {
if (!navigator.onLine) {
$("#test_connexion").css('background-color', 'red');
return;
}
$.ajax({
url: $("#racineWeb").val() + "Ajaxmessagerie/",
success: (data) => {
$("#nbMessagesNonLus").html(data);
$("#span_notification").text($("#msgNonLus").val());
// Sécurité : redirection forcée si le serveur indique une déconnexion nécessaire
if ($("#deconnexion").val() === '1') {
window.location.assign($("#racineWeb").val() + "Connexion/deconnecter/");
}
}
});
}
// Réajuster les popups SweetAlert lors du redimensionnement de la fenêtre
window.addEventListener('resize', () => {
if (Swal.isVisible()) setTimeout(adjustSwalContent, 100);
});
/**
* Sélectionne une police d'assurance et met à jour les champs cachés globaux.
* Cette fonction est généralement appelée lors d'un clic sur une ligne de tableau
* ou un bouton de sélection dans une liste de polices.
*/
function selectionner_police(id, no) {
debugger;
// Mise à jour de l'ID de la police dans le champ caché prévu pour le traitement serveur
$("#idPolice_C").val(id);
// Mise à jour du numéro de police pour l'affichage ou les recherches secondaires
$("#numeroPolice_C").val(no);
// Note : On pourrait ajouter ici un retour visuel (ex: surbrillance de la ligne sélectionnée)
console.log(`Police sélectionnée : ID ${id} / N° ${no}`);
afficher_police_id(id);
}
/**
* Vérifie la sélection d'une police et lance la mise à jour du contexte.
* Utilisée généralement avant d'afficher le détail d'un contrat.
*/
function afficher_police_id(idPolice) {
debugger;
// Récupération de l'ID depuis le champ caché
//const idPolice = $("#idPolice_C").val();
// Vérifie si l'ID est présent et non composé uniquement d'espaces
if (idPolice > "0") {
ajax_context_police_afficher(idPolice);
} else {
// Optionnel : Alerte si aucune police n'est sélectionnée
console.warn("Aucun identifiant de police trouvé pour l'affichage.");
}
}
/**
* Définit le contexte de la police côté serveur via AJAX avant redirection.
* @param {string|number} idPolice - L'identifiant de la police à mettre en session.
*/
function ajax_context_police_afficher(idPolice) {
debugger;
const racine = $("#racineWeb").val() || "/";
$.ajax({
url: racine + "Ajaxcontextpolice/",
type: 'POST',
// On envoie un objet pour que jQuery gère proprement l'encodage
data: { idPolice: idPolice },
beforeSend: function() {
// Optionnel : On pourrait afficher un petit loader ici
},
success: function() {
// Le contexte est mis à jour avec succès
console.log("Contexte police mis à jour.");
},
error: function(xhr, status, error) {
// Alerte l'utilisateur en cas de problème technique
const msg = "Erreur lors de la préparation du dossier police.";
const msgEng = "Error while preparing the policy file.";
if (typeof alert_ebene === "function") {
alert_ebene(msg, msgEng);
} else {
alert(msg);
}
},
complete: function() {
// Redirection vers la fiche de la police après le traitement AJAX
window.location.assign(racine + "Fichepolice/");
}
});
}
/**
* Charge la liste des quittances pour une police sur une période donnée.
*/
function afficher_quittances_police_periode() {
debugger;
// 1. Récupération des paramètres
const racine = $("#racineWeb").val() || "/";
const idPolice = $("#idPolice_C").val();
const debut = $("#debut").val();
const fin = $("#fin").val();
const $conteneur = $("#div_quittancepolice");
// 2. Vérification de l'ID Police (Correction du bug numeroPolice)
if (!idPolice || idPolice.trim() === "") {
console.warn("Affichage quittances impossible : ID Police manquant.");
return;
}
// 3. Préparation de l'interface (Loader)
showLoader("#div_quittancepolice", { size: 3 });
// 4. Appel AJAX
$.ajax({
url: racine + "Ajaxfichepolice/",
type: 'POST',
data: {
idPolice: idPolice,
debut: debut,
fin: fin
},
success: function(data) {
// Injection du résultat
$conteneur.hide().html(data).fadeIn();
appliquerDataTable('.tabliste');
},
error: function(xhr, status, error) {
$conteneur.html(`
<div class="alert alert-danger shadow-sm m-3">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
${_("Erreur lors du chargement des quittances.")}
</div>
`);
console.error("Erreur AJAX Quittances:", status, error);
}
});
}
function afficher_emission(idEmission)
{
if (idEmission>"0")
{
window.location.assign($("#racineWeb" ).val()+"Emission/"+idEmission+"/");
}
}
function texte_cp()
{
window.location.assign($("#racineWeb" ).val()+"Textecp/");
}
/**
* Ouvre le modal d'impression de quittance et charge son contenu via AJAX.
* Utilise le déplacement DOM (Solution 3) pour éviter les conflits de z-index.
*
* @param {number|string} idQuittance - Identifiant de la quittance à imprimer
*/
function imprimer_quittance_client(idQuittance) {
/* ===================================================
* 1. Validation de lidentifiant
* =================================================== */
if (!idQuittance || parseInt(idQuittance) <= 0) {
const v_msg = "Rien à imprimer !";
const v_msgEng = "Nothing to print!";
if (typeof alert_ebene === "function") {
alert_ebene(v_msg, v_msgEng);
} else {
alert(v_msg);
}
return;
}
/* ===================================================
* 2. Initialisation et EXTRACTION du Modal (Solution 3)
* =================================================== */
const modalEl = document.getElementById('pop_export_quittance');
if (!modalEl) {
console.error("Erreur : Le modal #pop_export_quittance est introuvable dans le DOM.");
return;
}
// On déplace le modal directement sous <body> s'il n'y est pas déjà.
// Cela permet de passer outre les z-index des conteneurs parents (Sidebar, Header).
if (modalEl.parentElement !== document.body) {
document.body.appendChild(modalEl);
}
const racine = $("#racineWeb").val() || "/";
const divExport = document.getElementById('div_export_quittance');
/* ===================================================
* 3. Préparation visuelle (Spinner)
* =================================================== */
showLoader("#div_export_quittance", { size: 3 });
/* ===================================================
* 4. Initialisation de l'instance Bootstrap
* =================================================== */
const modal = bootstrap.Modal.getOrCreateInstance(modalEl, {
backdrop: 'static',
keyboard: false
});
/* ===================================================
* 5. Gestion de l'événement d'affichage et AJAX
* =================================================== */
// On utilise 'shown.bs.modal' pour lancer l'AJAX une fois le modal visible
$(modalEl).one('shown.bs.modal', function () {
$.ajax({
url: racine + "Ajaxexporterunequittanceclient/",
type: 'POST',
data: { idQuittance: idQuittance },
success: function (data) {
divExport.innerHTML = data;
},
error: function (xhr, status, error) {
divExport.innerHTML = `
<div class="alert alert-danger m-3 shadow-sm">
<strong>Erreur :</strong> Impossible de générer la quittance.
<small class="d-block text-muted mt-1">${error}</small>
</div>
`;
}
});
});
/* ===================================================
* 6. Affichage final
* =================================================== */
modal.show();
}
function college_police()
{
if ($("#idPolice_C" ).val()>"")
{
window.location.assign($("#racineWeb" ).val()+"Collegepolice/");
}
}
function assurance_familiale()
{
codeTypeContrat = $("#codeTypeContrat_C").val();
if (codeTypeContrat!="F")
{
v_msg="Ce n\'est pas une police FAMILIALE!";
v_msgEng="This is not a FAMILIY policy!";
alert_ebene(v_msg, v_msgEng);
return;
}
window.location.assign($("#racineWeb" ).val()+"Assuranceindividuelle/");
}
function est_ce_police_famille()
{
codeTypeContrat = $("#codeTypeContrat_C").val();
return (codeTypeContrat=="F");
}
function creer_adherents()
{
// 06/11/2020
if(est_ce_police_famille())
{
window.location.assign($("#racineWeb" ).val()+"Assuranceindividuelle/");
return;
}
nbAdh = $("#nbAdh_C").val();
codeTypeContrat = $("#codeTypeContrat_C").val();
if ( (codeTypeContrat!="G") && (nbAdh>0) )
{
v_msg="Ce n\'est pas une police GROUPE!";
v_msgEng="This is not a GROUP policy!";
alert_ebene(v_msg, v_msgEng);
return;
}
etat=$("#codeEtatPolice_C").val();
if (etat=="RE")
{
v_msg="Attention! Police résiliée!";
v_msgEng="Warning! Terminated policy!";
alert_ebene(v_msg, v_msgEng);
return;
}
if (etat=="SU")
{
v_msg="Attention! Police suspendue!";
v_msgEng="Warning! Suspended policy!";
alert_ebene(v_msg, v_msgEng);
return;
}
if (etat=="AN")
{
v_msg="Attention! Police annulée!";
v_msgEng="Warning! Canceled policy!";
alert_ebene(v_msg, v_msgEng);
return;
}
window.location.assign($("#racineWeb" ).val()+"Creeradherent/");
}
function prorater_prime_adherent()
{
idCollege=$("#idCollege").val();
dateEntree=$("#dateEntree").val();
prorata=$("#prorata").val();
if (idCollege<=" ")
{
v_msg="Veuillez sélectionner un collège!";
v_msgEng="Please select a college!";
alert_ebene(v_msg, v_msgEng);
return;
}
donnees = 'idCollege='+idCollege;
donnees += '&dateEntree='+dateEntree;
donnees += '&prorata='+prorata;
$.ajax({
url: $("#racineWeb").val()+"Ajaxproraterprime/",
type : 'post',
data: donnees,
error: function(errorData) {
},
success: function(data) {
$("#div_prime").html(data);
},
complete: function() {
}
});
}
function imprimer_contrat()
{
window.location.assign($("#racineWeb" ).val()+"Contrat/");
}
function adherents_police()
{
if ($("#idPolice_C" ).val()>"")
{
window.location.assign($("#racineWeb" ).val()+"Listeadherent/");
}
}
function afficher_adherents_police()
{
showLoader("#div_liste_adherent", { size: 3 });
$.ajax({
url: $("#racineWeb").val()+"Ajaxlisteadherent/",
type : 'post',
// data: donnees,
error: function(errorData) {
},
success: function(data) {
$("#div_liste_adherent").html(data);
}
});
}
// --- I18n dictionary ---
const I18N = {
fr_FR: {
loading: "Affichage en cours...",
exportPdf: "Exporter les graphiques en PDF",
dashboardTitle: "Graphiques des sinistres",
charts: {
claimsTitle: "Répartition des sinistres",
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."
}
}
};
// --- Helpers ---
function getLang() {
const codeLangue = $("#codeLangue").val() || "fr_FR";
return I18N[codeLangue] ? codeLangue : "fr_FR";
}
function t(path) {
const lang = getLang();
return path.split(".").reduce((acc, key) => (acc && acc[key] != null ? acc[key] : null), I18N[lang]);
}
// Optional: normalize backend month keys to localized labels
function localizeMonths(months) {
const map = {
Jan: "Jan", Feb: "Feb", Mar: "Mar", Apr: "Apr", May: "May", Jun: "Jun",
Jul: "Jul", Aug: "Aug", Sep: "Sep", Oct: "Oct", Nov: "Nov", Dec: "Dec"
};
const langMonths = t("months");
// If backend already provides localized months, keep them; else map to language months by index
if (!Array.isArray(months) || months.length !== 12) return months;
const canonical = months.map(m => map[m] || m);
// Use the language month names but preserve array length and order
return langMonths && langMonths.length === 12 ? langMonths : canonical;
}
function showLoader(selector, options = {}) {
const { message = null, size = 3 } = options;
const text = message || t("loading");
const loaderHtml = `
<div class="text-center my-5 py-5 js-generic-loader">
<div class="spinner-border text-primary" role="status" style="width: ${size}rem; height: ${size}rem;">
<span class="visually-hidden">${text}</span>
</div>
<p class="mt-3 text-muted fw-bold">${text}</p>
</div>
`;
$(selector).html(loaderHtml);
}
function hideLoader(selector, contentHtml = "") {
$(selector).html(contentHtml);
}
function graphique_sinistre() {
showLoader("#div_graphique", { size: 3 });
$.ajax({
url: $("#racineWeb").val() + "Ajaxgraphiquesinistres/api/",
type: "get",
dataType: "json",
success: function (data) {
const exportLabel = t("exportPdf");
const html = `
<div class="text-end my-3">
<button id="exportPdfBtn" class="btn btn-danger">
<i class="fas fa-file-pdf"></i> ${exportLabel}
</button>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title text-primary">${t("charts.claimsTitle")}</h5>
<canvas id="claimsChart"></canvas>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title text-success">${t("charts.monthlyTitle")}</h5>
<canvas id="claimsMonthChart"></canvas>
</div>
</div>
</div>
<div class="col-md-12 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title text-danger">${t("charts.lossRatioTitle")}</h5>
<canvas id="lossRatioChart"></canvas>
</div>
</div>
</div>
</div>
`;
hideLoader("#div_graphique", html);
// Render charts with localized labels
renderClaimsChart(data.claims);
renderClaimsMonthChart(data.claimsMonth);
renderLossRatioChart(data.lossRatio);
setupExportPdf();
},
error: function (err) {
console.error("Erreur AJAX:", err);
hideLoader("#div_graphique", `
<div class="alert alert-danger">${t("errors.ajax")}</div>
`);
}
});
}
function reset_graphique() {
// Vide complètement la zone des graphiques
$("#div_graphique").empty();
}
// --- Charts ---
function renderClaimsChart(claims) {
new Chart(document.getElementById("claimsChart"), {
type: "bar",
data: {
labels: claims.claimsLabels,
datasets: [{
label: t("charts.claimsCount"),
data: claims.claimsValues,
backgroundColor: "rgba(54, 162, 235, 0.6)"
}]
},
options: {
plugins: {
legend: { display: true }
},
scales: {
x: { title: { display: false } },
y: { title: { display: false }, beginAtZero: true }
}
}
});
}
function renderClaimsMonthChart(claimsMonth) {
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);
}
}
function charger_contrats(){
showLoader("#div_liste_contrats", { size: 3 });
$.ajax({
url: $("#racineWeb").val()+"Ajaxlistepolicesclient/",
type : 'post',
// data: donnees,
error: function(errorData) {
},
success: function(data) {
$("#div_liste_contrats").html(data);
appliquerDataTable('.tabliste');
}
});
}
function reset_contrats()
{
$("#div_liste_contrats").empty();
}