radiantassure/Vue/Accueilassure/index.php
2025-12-30 13:49:40 +00:00

834 lines
22 KiB
PHP
Executable File

<?php
/*
$plafondGlobalFamille = $limite_adherent['plafond'];
$consommationGlobaleFamille = $limite_adherent['consommation'];
$niveauGlobalConsoFamille = ($consommationGlobaleFamille*100)/$plafondGlobalFamille;
*/
$plafondGlobalFamilleOut = $limite_adherent['plafondOut'];
$consommationGlobaleFamilleOut = $limite_adherent['consommationOut'];
$niveauGlobalConsoFamilleOut = ($consommationGlobaleFamilleOut*100)/$plafondGlobalFamilleOut;
$plafondGlobalFamilleInp = $limite_adherent['plafondInp'];
$consommationGlobaleFamilleInp = $limite_adherent['consommationInp'];
$niveauGlobalConsoFamilleInp = ($consommationGlobaleFamilleInp*100)/$plafondGlobalFamilleInp;
?>
<style>
/* === Correctifs affichage textes et tables === */
body, h1, h2, h3, h4, h5, h6, p, span, a, label, td, th, strong, em, button, input {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
transform: none !important;
}
/* Tables responsive fallback - web par défaut */
.table.table-responsive, table.table-responsive {
display: table;
width: 100%;
}
#div_membre{
background-color: #f1f5fc;
}
.whatsapp-link {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 22px;
color: #25D366; /* couleur officielle WhatsApp */
text-decoration: none;
transition: transform 0.2s ease, color 0.2s ease;
cursor: pointer; /* curseur en forme de main */
}
.whatsapp-link:hover {
color: #128C7E; /* vert plus foncé au survol */
transform: scale(1.1); /* léger zoom */
}
.card-header {
padding: 0.5rem 1rem !important;
}
.align-same-line {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.left-content {
display: flex;
align-items: center;
gap: 10px;
}
.badge {
font-size: 0.9rem;
}
.exercice {
font-size: 1.8rem !important;
margin: 0;
}
/* Amélioration de la lisibilité sur desktop */
@media (min-width: 768px) {
.table {
font-size: 0.9rem;
}
}
/* Style pour les icônes */
.bi {
font-size: 1.1rem;
}
/* Amélioration de l'affichage mobile */
@media (max-width: 767.98px) {
.card.mb-3 {
margin-bottom: 1rem !important;
}
.card-header {
padding: 0.5rem 1rem;
}
.card-body {
padding: 0.75rem;
}
.accordion-button {
font-size: 0.8rem;
padding: 0.5rem;
}
.accordion-body {
padding: 0.5rem;
font-size: 0.85rem;
}
.btn-sm {
padding: 0.2rem 0.4rem;
font-size: 0.75rem;
}
/* Style pour la modal en mode mobile */
.modal-dialog {
margin: 0.5rem;
}
/* Amélioration de l'affichage des badges */
.badge {
font-size: 0.7rem;
}
}
/* Style pour les types de facture */
.badge.bg-primary {
font-size: 0.75rem;
}
/* Style pour les lignes du tableau desktop */
.table-hover tbody tr:hover {
background-color: rgba(0, 0, 0, 0.075);
}
</style>
<?php
$this->titre = "Intersanté - Accueil";
$pourcentageOut = round((100*$limite_adherent['consommationOut'])/$limite_adherent['plafondOut']);
$pourcentageInp = round((100*$limite_adherent['consommationInp'])/$limite_adherent['plafondInp']);
?>
<input type="text" id="nomForm" name="nomForm" class="sr-only" value="frmaccueil">
debut
<div class="card shadow-lg border-0 mb-4" style="border-radius: 15px;">
<div class="card-header bg-primary text-white py-3" style="border-radius: 15px 15px 0 0;">
<div class="row align-items-center">
<div class="col-8">
<div class="d-none d-md-block">
<h4 id="h4_titre" class="mb-0 text-center text-truncate">
<i class="fas fa-home me-2"></i>
<?= _("Bonjour Famille") ?> <?= $adherent['nomAdherent'] ?>
</h4>
</div>
<!-- Affichage mobile (cartes) -->
<div class="d-md-none">
<h4 id="h4_titre" class="mb-0 text-center text-truncate" style="max-width: 200px;">
<i class="fas fa-home me-2"></i>
<?= _("Famille") ?> <?= $adherent['nomAdherent'] ?>
</h4>
</div>
</div>
<div class="col-4 text-end">
<span class="badge bg-light text-dark fs-8">
<?= $_SESSION['numeroAdherent_C'] ?>
</span>
</div>
</div>
</div>
<!-- Message d'information -->
<?php if($_SESSION['envoismswhatsappactif'] =='1'): ?>
<div class="alert alert-info mt-3" style="margin-bottom:10px;">
<small>
<i class="fas fa-info-circle me-2"></i>
<?= _("En cas de réclamation ou tout autre sujet lié à votre police d'assurance, cliquez ici")." " ?>
<a class="whatsapp-link" href="javascript:envoyer_message();">
<i class="fab fa-whatsapp"></i>
</a>
</small>
</div>
<?php endif; ?>
<!-- Cards Section -->
<div class="row">
<!-- Taux de couverture -->
<h1 class="fw-bold w100 text-center text-dark" style="margin-top:-10px;"><?= _("Année")." : ".$_SESSION['exercieReference_C']?></h1>
<!-- Plafond annuel -->
<div class="col-xl-4 col-6 mb-4">
<div class="card shadow h-100 border border-primary">
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="h4 text-primary text-uppercase mb-1" >
<?= _("Plafond") . " OUT/ INP" ?>
</div>
<div class="h4 mb-0" style="font-weight:bold">
<?= format_N($limite_adherent['plafondOut'])." / ".format_N($limite_adherent['plafondInp']). " ".substr($_SESSION['devise_C'],0,1) ?>
</div>
</div>
<div class="ml-2">
<i class="fas fa-minus-circle fa-2x text-primary"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Consommation Annuelle -->
<div class="col-xl-4 col-6 mb-4">
<div class="card shadow h-100 border border-info">
<div class="card-body p-3">
<div class="d-flex flex-column">
<div class="h4 text-info text-uppercase mb-1" >
<?= _("Dépenses") ?>
</div>
<div class="d-flex align-items-center">
<div class="h4 mb-0 mr-2 me-2" style="font-weight:bold">
<?= format_N($limite_adherent['consommationOut'])." / ".format_N($limite_adherent['consommationInp']). " ".substr($_SESSION['devise_C'],0,1) ?>
</div>
<div class="progress flex-grow-1" style="height: 6px;">
<div class="progress-bar bg-info border border-info" role="progressbar"
style="width: <?= $pourcentage; ?>%"
aria-valuenow="<?= $pourcentageOut; ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Solde Annuel -->
<div class="col-xl-4 col-6 mb-4">
<div class="card shadow h-100 border border-danger">
<div class="card-body p-3">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="h4 text-danger text-uppercase mb-1" >
<?= _("Solde") ?>
</div>
<div class="h4 mb-0" style="font-weight:bold">
<?= format_N($limite_adherent['soldeConsommationOut'])." / ".format_N($limite_adherent['soldeConsommationInp']). " ".substr($_SESSION['devise_C'],0,1) ?>
</div>
</div>
<div class="ml-2">
<i class="fas fa-money-bill-wave fa-2x text-danger"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- progress-bar -->
<div class="progress mb-3" >
<div class="progress-bar <?= $niveauGlobalConsoFamilleOut >= 80 ? 'bg-danger' : ($niveauGlobalConsoFamilleOut >= 50 ? 'bg-warning' : 'bg-success') ?>"
style="width: <?= min($niveauGlobalConsoFamilleOut, 100) ?>%">
<?= number_format($niveauGlobalConsoFamilleOut, 1) ?>%
</div>
</div>
</div>
fin
<script>
// Configuration commune
(function() {
// Récupération de la devise depuis PHP
const userCurrency = '<?= isset($_SESSION['devise_C']) ? substr($_SESSION['devise_C'],0,1) : 'F' ?>';
// Configuration des devises
const currencyFormats = {
'F CFA': {
symbol: 'F CFA',
format: (value) => new Intl.NumberFormat('fr-FR').format(value) + ' F'
},
'€': {
symbol: '€',
format: (value) => new Intl.NumberFormat('fr-FR', {style: 'currency', currency: 'EUR'}).format(value)
},
'$': {
symbol: '$',
format: (value) => new Intl.NumberFormat('fr-FR', {style: 'currency', currency: 'USD'}).format(value)
},
'XOF': {
symbol: 'F CFA',
format: (value) => new Intl.NumberFormat('fr-FR').format(value) + ' F'
}
};
// Format monétaire dynamique
const formatMoney = (value) => {
const currencyConfig = currencyFormats[userCurrency] || currencyFormats['F CFA'];
return currencyConfig.format(value);
};
// Format pourcentage
const formatPercentage = (value, total) => {
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 trendCtx = document.getElementById('expenseTrendChart').getContext('2d');
const trendChart = new Chart(trendCtx, {
type: 'line',
data: {
labels: <?= $labels_mois; ?>,
datasets: [{
label: '<?= _("Montant dépensé") ?>',
data: <?= $data_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 de répartition
const distributionCtx = document.getElementById('expenseDistributionChart').getContext('2d');
const distributionChart = new Chart(distributionCtx, {
type: 'pie',
data: {
labels: <?= $labels_liens; ?>,
datasets: [{
data: <?= $data_liens; ?>,
backgroundColor: generateColors(<?= $nbreLienParente; ?>),
borderColor: '#fff',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
datalabels: {
formatter: (value, ctx) => {
const sum = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0);
return formatPercentage(value, sum);
},
color: '#fff',
font: {
weight: 'bold',
size: isMobile ? 10 : 12
}
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.raw || 0;
const sum = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = formatPercentage(value, sum);
return `${label}: ${formatMoney(value)} (${percentage})`;
}
}
}
},
cutout: isMobile ? '60%' : '50%'
},
plugins: [ChartDataLabels]
});
// Générer la légende au chargement et lors du redimensionnement
generateCustomLegend(distributionChart, 'pieChartLegend');
window.addEventListener('resize', function() {
generateCustomLegend(distributionChart, 'pieChartLegend');
});
// Boutons d'export PDF
document.getElementById('exportTrendBtn').addEventListener('click', () => {
exportChartToPDF('expenseTrendChart', 'evolution_depenses_mensuelles');
});
document.getElementById('exportPieBtn').addEventListener('click', () => {
exportChartToPDF('expenseDistributionChart', 'repartition_depenses_parente');
});
document.getElementById('exportBarBtn').addEventListener('click', () => {
exportChartToPDF('depensesChart', 'depenses_par_garantie');
});
})();
</script>
<style>
/* Styles communs pour tous les graphiques */
.chart-container {
position: relative;
width: 100%;
min-height: 300px;
}
#pieChartLegend {
font-size: 0.8rem;
line-height: 1.4;
white-space: normal !important;
word-wrap: break-word !important;
}
.legend-text {
vertical-align: middle;
}
.btn-export {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
@media (max-width: 768px) {
#pieChartLegend {
font-size: 0.7rem;
}
.chart-container {
height: 60vh;
}
.card-header {
flex-direction: column;
align-items: flex-start !important;
}
.btn-export {
margin-top: 0.5rem;
align-self: flex-end;
}
.legend-table {
font-size: 8pt; /* Légère réduction de la taille de police */
}
.color-cell {
width: 25px;
min-width: 25px;
height: 18px;
}
}
.legend-container {
width: 100%;
margin: 5px;
overflow-x: auto;
}
.legend-table {
width: 100%;
margin: auto;
background-color: #f8f8ff;
font-size: 9pt;
white-space: nowrap;
border-collapse: collapse;
min-width: 500px; /* Largeur minimale pour assurer la lisibilité */
}
.legend-table td {
padding: 5px 3px;
}
.text-cell {
font-weight: bold;
text-align: right;
background-color: #f8f8ff;
padding-right: 5px;
}
.color-cell {
width: 30px; /* Largeur fixe pour les carrés de couleur */
min-width: 30px;
height: 20px; /* Hauteur fixe pour mieux voir les couleurs */
border: 1px solid #ddd; /* Contour pour mieux distinguer les couleurs claires */
}
.spacer-cell {
width: 1%;
background-color: #f8f8ff;
}
.form-container {
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.form-section {
margin-bottom: 15px;
}
.form-label {
font-weight: bold;
margin-bottom: 5px;
font-size: 9pt;
}
.date-input-group {
display: flex;
align-items: center;
gap: 10px;
}
.btn-actualiser {
margin-top: 25px;
width: 100%;
font-size: 12pt !important;
}
/* Pour les écrans plus larges */
@media (min-width: 768px) {
.form-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: end;
}
.form-section {
flex: 1;
margin-bottom: 0;
min-width: 180px;
}
.btn-section {
flex: 0 0 auto;
}
.btn-actualiser {
margin-top: 0;
width: auto;
min-width: 140px;
}
}
/* Pour les très petits écrans */
@media (max-width: 380px) {
.date-input-group {
flex-direction: column;
align-items: stretch;
gap: 5px;
}
.date-separator {
text-align: center;
}
.btn-actualiser i{
font-size: 1.5rem !important;
}
}
/* Correction du z-index pour Bootstrap SelectPicker */
.bootstrap-select .dropdown-menu {
z-index: 1060 !important;
}
.bootstrap-select {
z-index: 1060 !important;
}
/* Pour les modales ou autres éléments avec z-index élevé */
.modal .bootstrap-select .dropdown-menu {
z-index: 1060 !important;
}
</style>
<style>
.accordion-section {
margin-bottom: 2px;
border: 1px solid #ddd;
border-radius: 5px;
overflow: hidden;
}
.accordion-header {
display: flex;
align-items: center;
padding: 12px 15px;
background-color: #f1f5fc;
cursor: pointer;
font-weight: bold;
font-size: 1.1rem;
transition: background-color 0.3s;
margin: 0;
}
.accordion-header:hover {
background-color: #e9ecef;
}
.accordion-header.active {
background-color: #e2e6ea;
}
.accordion-header i:first-child {
margin-right: 10px;
font-size: 1.2rem;
}
.accordion-title {
flex-grow: 1;
}
.accordion-icon {
margin-left: auto;
transition: transform 0.3s ease;
}
.accordion-header.active .accordion-icon {
transform: rotate(180deg);
}
.accordion-content {
display: none;
padding: 2px;
background-color: #fff;
border-top: 1px solid #ddd;
}
/* Styles pour la responsivité */
@media (max-width: 768px) {
.accordion-header, .accordion-header i, .accordion-header span {
font-size: 1.8rem !important;
font-weight: bold !important;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Laisser Bootstrap gérer les accordéons - suppression du script conflictuel
// Bootstrap s'occupe déjà du fonctionnement des accordéons
// Si vous voulez garder votre système personnalisé pour les grandes sections,
// assurez-vous qu'il ne cible pas les mêmes éléments que Bootstrap
const sectionHeaders = document.querySelectorAll('.accordion-section .accordion-header');
sectionHeaders.forEach(header => {
header.addEventListener('click', function() {
// Vérifier si cet accordéon est géré par Bootstrap
if (!this.querySelector('[data-bs-toggle="collapse"]')) {
const content = this.nextElementSibling;
const isCurrentlyActive = this.classList.contains('active');
// Fermer toutes les sections d'abord
document.querySelectorAll('.accordion-section .accordion-content').forEach(content => {
content.style.display = 'none';
});
document.querySelectorAll('.accordion-section .accordion-header').forEach(header => {
header.classList.remove('active');
});
// Si la section n'était pas active, l'ouvrir
if (!isCurrentlyActive) {
this.classList.add('active');
content.style.display = 'block';
}
}
});
});
});
// Correction pour les icônes Bootstrap des accordéons
document.querySelectorAll('.accordion-button').forEach(button => {
button.addEventListener('click', function() {
const icon = this.querySelector('.bi');
if (icon) {
// Bootstrap gère déjà l'animation des icônes
// Cette fonctionnalité est incluse dans Bootstrap 5
}
});
});
</script>