1026 lines
36 KiB
PHP
Executable File
1026 lines
36 KiB
PHP
Executable File
<?php
|
|
$datejour = $this->datejour;
|
|
$estcouvert = ($_SESSION['dateEcheancePolice_C']>=$datejour);
|
|
|
|
$sorti = $this->nettoyer($beneficiaire['sorti']);
|
|
$dateSortieBeneficiaire = $this->nettoyer($beneficiaire['dateSortieBeneficiaire']);
|
|
|
|
$estsorti = false;
|
|
|
|
if($sorti=="1")
|
|
{
|
|
$estsorti = ($dateSortieBeneficiaire<=$datejour);
|
|
$estcouvert = ($estcouvert && ($dateSortieBeneficiaire>$datejour));
|
|
}
|
|
|
|
$dateEffetCouvert = $_SESSION['dateEffetCouvert'];
|
|
|
|
if (est_anglophone())
|
|
{
|
|
$produit = $beneficiaire['produitEng'];
|
|
$naturepiece = $beneficiaire['naturepieceEng'];
|
|
|
|
$lienparente = $beneficiaire['lienparenteEng'];
|
|
$motifsortie = $beneficiaire['motifsortieEng'];
|
|
$etatbeneficiaire = $beneficiaire['etatbeneficiaireEng'];
|
|
|
|
if($beneficiaire['sexe']=="M"){
|
|
$sexe = "Male";
|
|
}else{
|
|
$sexe = "Female";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$produit = $beneficiaire['produit'];
|
|
$naturepiece = $beneficiaire['naturepiece'];
|
|
|
|
$lienparente = $beneficiaire['lienparente'];
|
|
$motifsortie = $beneficiaire['motifsortie'];
|
|
$etatbeneficiaire = $beneficiaire['etatbeneficiaire'];
|
|
|
|
if($beneficiaire['sexe']=="M"){
|
|
$sexe = "Masculin";
|
|
}else{
|
|
$sexe = "Féminin";
|
|
}
|
|
}
|
|
|
|
$codeEtatBeneficiaire = $beneficiaire['codeEtatBeneficiaire'];
|
|
$codeLienParente = $beneficiaire['codeLienParente'];
|
|
|
|
$produit = $beneficiaire['produit'];
|
|
|
|
$faceRegistered = $this->nettoyer($beneficiaire['faceRegistered']);
|
|
$fingerActif = '0';
|
|
$faceActif = '0';
|
|
|
|
// $imgData = $_SESSION['photoAssureCrypte'];
|
|
$photoAssureCrypte = $_SESSION['photoAssureCrypte'];
|
|
|
|
function verifierTelephone($numero)
|
|
{
|
|
// On prend les 3 premiers caractères du numéro
|
|
$prefixe = substr($numero, 0, 3);
|
|
|
|
// Vérification si le préfixe est égal à "250"
|
|
if ($prefixe === "250") {
|
|
return "0";
|
|
} else {
|
|
return "1";
|
|
}
|
|
}
|
|
|
|
$telephone = $beneficiaire['telephonePortable'];
|
|
$format = strlen($telephone) > "4" ? verifierTelephone($telephone) : "0";
|
|
?>
|
|
|
|
<style>
|
|
.form-check-card {
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
border: 2px solid #dee2e6 !important;
|
|
}
|
|
|
|
.form-check-card:hover {
|
|
border-color: #0d6efd !important;
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.form-check-input:checked + .form-check-label {
|
|
color: #0d6efd;
|
|
|
|
}
|
|
|
|
.form-check-input {
|
|
border-color: #0d6efd !important;
|
|
}
|
|
|
|
.form-check-card:has(.form-check-input:checked) {
|
|
border-color: #0d6efd !important;
|
|
background-color: #e7f1ff;
|
|
}
|
|
|
|
/* Amélioration tactile pour mobile */
|
|
@media (max-width: 768px) {
|
|
.mx-auto[style*="max-width: 400px"] {
|
|
max-width: 95% !important;
|
|
padding: 5px 10px;
|
|
}
|
|
|
|
.form-check-label, .form-check-label i {
|
|
font-size: 1.8rem !important;
|
|
}
|
|
|
|
.form-check-card {
|
|
padding: 1rem !important;
|
|
min-height: 54px;
|
|
}
|
|
|
|
.btn, .form-control-lg {
|
|
min-height: 54px;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.fs-5 {
|
|
font-size: 1.2rem !important;
|
|
}
|
|
}
|
|
|
|
/* Pour les très petits écrans */
|
|
@media (max-width: 480px) {
|
|
.form-check-card {
|
|
padding: 0.75rem !important;
|
|
min-height: 50px;
|
|
}
|
|
|
|
.btn, .form-control-lg {
|
|
min-height: 50px;
|
|
font-size: 1rem;
|
|
}
|
|
}
|
|
|
|
/* Le Cercle Parfait */
|
|
.avatar-circle {
|
|
width: 130px;
|
|
height: 130px;
|
|
border-radius: 50% !important;
|
|
overflow: hidden; /* Coupe ce qui dépasse */
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.avatar-circle img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover; /* Remplit le cercle sans déformer */
|
|
}
|
|
|
|
.avatar-circle:hover {
|
|
border-color: var(--bs-primary) !important;
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
/* Bordure responsive uniquement sur large écran */
|
|
@media (min-width: 992px) {
|
|
.border-end-lg {
|
|
border-right: 1px solid #dee2e6 !important;
|
|
}
|
|
}
|
|
|
|
/* Zone d'ombre légère pour les blocs d'info */
|
|
.info-box {
|
|
border-left: 3px solid transparent;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.info-box:hover {
|
|
background-color: #f8f9fa;
|
|
border-left-color: var(--bs-primary);
|
|
}
|
|
</style>
|
|
|
|
<div class="container-fluid mt-3">
|
|
<!-- Champs cachés -->
|
|
<input class="sr-only" type="text" id="nomForm" name="nomForm" value="ficheBeneficiaire">
|
|
<input class="sr-only" type="text" id="primeArchive" name="primeArchive" value="<?= $beneficiaire['primeArchive'] ?>">
|
|
<input class="sr-only" type="text" id="idPolice" name="idPolice" value="<?= $beneficiaire['idPolice'] ?>">
|
|
<input class="sr-only" type="text" id="idCollege" name="idCollege" value="<?= $beneficiaire['idCollege'] ?>">
|
|
<input class="sr-only" type="text" id="idAdherent" name="idAdherent" value="<?= $beneficiaire['idAdherent'] ?>">
|
|
<input class="sr-only" type="text" id="idBeneficiaire" name="idBeneficiaire" value="<?= $_SESSION['idBeneficiaire_C'] ?>">
|
|
<input class="sr-only" type="text" id="numeroAdherent" name="numeroAdherent" value="<?= $beneficiaire['numeroAdherent'] ?>">
|
|
<input class="sr-only" type="text" id="numeroBeneficiaire" name="numeroBeneficiaire" value="<?= $beneficiaire['numeroBeneficiaire'] ?>">
|
|
<INPUT class="sr-only" TYPE="text" id="faceRegistered" name="faceRegistered" value="<?= $faceRegistered ?>">
|
|
|
|
<div id="div_patienter"></div>
|
|
|
|
<!-- Carte principale style interface.jpg -->
|
|
<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">
|
|
<h4 class="mb-0 text-center">
|
|
<i class="fas fa-id-card me-2"></i>
|
|
<?= ("Bénéficiaire")?>
|
|
</h4>
|
|
</div>
|
|
<div class="col-4 text-end">
|
|
<span class="badge bg-light text-dark fs-8">
|
|
<?= $beneficiaire['numeroBeneficiaire'] ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body p-4">
|
|
<div class="row g-4">
|
|
<div class="col-12 col-lg-3 border-end-lg text-center">
|
|
<div class="position-relative mb-3">
|
|
<div class="avatar-circle mx-auto shadow-sm border d-flex align-items-center justify-content-center bg-light">
|
|
<?php if ($beneficiaire['faceRegistered'] == "1"): ?>
|
|
<img src="<?= $photoAssureCrypte ?>"
|
|
class="img-fluid"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#pop_photo"
|
|
alt="Photo du bénéficiaire">
|
|
<?php else: ?>
|
|
<i class="fas fa-user fa-3x text-secondary"></i>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<span class="badge rounded-pill <?= $estcouvert ? 'bg-success' : 'bg-danger' ?> px-3 py-2">
|
|
<i class="fas <?= $estcouvert ? 'fa-check-circle' : 'fa-times-circle' ?> me-1"></i>
|
|
<?= $estcouvert ? $etatbeneficiaire : _("Non couvert") ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($beneficiaire['faceRegistered'] != "1" && $_SESSION['assureAjoutPhoto'] == "1"): ?>
|
|
<div class="mt-4">
|
|
<div class="btn-group btn-group-sm w-100 mb-3" role="group">
|
|
<input type="radio" class="btn-check" name="photo_choice" id="radio_cam" checked onchange="togglePhotoSource('cam')">
|
|
<label class="btn btn-outline-primary" for="radio_cam"><i class="fas fa-camera me-1"></i><?= _("Appareil") ?></label>
|
|
|
|
<input type="radio" class="btn-check" name="photo_choice" id="radio_file" onchange="togglePhotoSource('file')">
|
|
<label class="btn btn-outline-primary" for="radio_file"><i class="fas fa-folder-open me-1"></i><?= _("Fichier") ?></label>
|
|
</div>
|
|
|
|
<div id="area_prendre_photo" class="photo-zone">
|
|
<button class="btn btn-primary w-100 py-2 fw-bold" type="button" onClick="ebene_init_photo_face();">
|
|
<i class="fas fa-video me-2"></i><?= _("Lancer") ?>
|
|
</button>
|
|
<button class="d-none" id="btn_pop_save_face" data-bs-toggle="modal" data-bs-target="#pop_rec_faciale"></button>
|
|
</div>
|
|
|
|
<div id="area_choisir_photo" class="photo-zone d-none">
|
|
<form enctype="multipart/form-data" action="Fichebeneficiaire/<?= $_SESSION['idBeneficiaire_C'] ?>/" method="post">
|
|
<input class="form-control form-control-sm mb-2" name="fichier_upload" type="file" accept="image/*" onchange="this.form.submit()">
|
|
<button type="submit" name="submit" class="btn btn-success w-100 btn-sm"><i class="fas fa-upload me-2"></i><?= _("Envoyer") ?></button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="col-12 col-lg-9">
|
|
<div class="d-flex justify-content-between align-items-start mb-3 border-bottom pb-2">
|
|
<div>
|
|
<h2 class="h4 text-primary mb-0 fw-bold"><?= $beneficiaire['nomBeneficiaire'] . ' ' . $beneficiaire['prenomsBeneficiaire'] ?></h2>
|
|
<small class="text-muted fw-semibold"><?= _("N° Adhérent") ?> : <?= $beneficiaire['numeroAdherent'] ?></small>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="d-block text-muted small"><?= _("Âge") ?></span>
|
|
<span class="badge bg-light text-dark border fw-bold"><?= $this->nettoyer($beneficiaire['age']) ?> ans</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="info-box p-2 rounded bg-light-subtle">
|
|
<label class="text-muted small d-block mb-1"><i class="fas fa-calendar-alt me-1 text-primary"></i> <?= _("Né(e) le") ?></label>
|
|
<span class="fw-medium"><?= dateLang($this->nettoyer($beneficiaire['dateNaissance'])) ?></span>
|
|
</div>
|
|
<div class="info-box p-2 rounded mt-2">
|
|
<label class="text-muted small d-block mb-1"><i class="fas fa-users me-1 text-primary"></i> <?= _("Lien Parenté") ?></label>
|
|
<span class="fw-medium"><?= $lienparente ?></span>
|
|
</div>
|
|
<div class="info-box p-2 rounded mt-2">
|
|
<label class="text-muted small d-block mb-1"><i class="fas fa-venus-mars me-1 text-primary"></i> <?= _("Genre") ?></label>
|
|
<span class="fw-medium"><?= $sexe ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<?php if($codeLienParente == "A"): ?>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<label class="text-muted small"><i class="fas fa-mobile-alt me-1 text-primary"></i> <?= _("Mobile") ?></label>
|
|
<div class="form-check form-switch small">
|
|
<input class="form-check-input"
|
|
type="checkbox"
|
|
id="ignorerFormat"
|
|
name="ignorerFormat"
|
|
<?= ($format == "1") ? "checked" : "" ?>>
|
|
<label class="form-check-label text-muted" for="ignorerFormat"><?= _("Format libre") ?></label>
|
|
</div>
|
|
</div>
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="fas fa-phone-alt"></i></span>
|
|
<input type="tel" class="form-control" id="telephonePortable"
|
|
pattern="^250[0-9]{9,}$" value="<?= $beneficiaire['telephonePortable'] ?>"
|
|
onchange="maj_mobile_beneficiaire();">
|
|
<div id="aideTelephone" class="form-text text-muted small">
|
|
<i class="fas fa-info-circle"></i> <?= est_anglophone() ? 'Required format: 250XXXXXXXXX (min. 12 digits)' : 'Format requis : 250XXXXXXXXX (min. 12 chiffres)'; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="text-muted small mb-1"><i class="fas fa-envelope me-1 text-primary"></i> <?= _("E-mail") ?></label>
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text">@</span>
|
|
<input type="email" class="form-control" id="emailBeneficiaire"
|
|
value="<?= $beneficiaire['emailBeneficiaire'] ?>"
|
|
onchange="maj_email_beneficiaire();">
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="info-box p-2 rounded bg-light-subtle">
|
|
<label class="text-muted small d-block mb-1"><i class="fas fa-mobile-alt me-1 text-primary"></i> <?= _("Mobile") ?></label>
|
|
<span class="fw-medium"><?= $beneficiaire['telephonePortable'] ?: '-' ?></span>
|
|
</div>
|
|
<div class="info-box p-2 rounded mt-2">
|
|
<label class="text-muted small d-block mb-1"><i class="fas fa-envelope me-1 text-primary"></i> <?= _("E-mail") ?></label>
|
|
<span class="fw-medium text-break"><?= $beneficiaire['emailBeneficiaire'] ?: '-' ?></span>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-4 pt-3 border-top g-2">
|
|
<div class="col-sm-6 text-center text-sm-start">
|
|
<div class="p-2 bg-light rounded border-start border-primary border-4 shadow-sm">
|
|
<small class="text-muted d-block text-uppercase" style="font-size: 0.7rem;"><?= _("Souscripteur") ?></small>
|
|
<span class="fw-bold"><?= $beneficiaire['nomClient'] ?></span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 text-center text-sm-end">
|
|
<div class="p-2 bg-light rounded border-end border-secondary border-4 shadow-sm h-100">
|
|
<small class="text-muted d-block text-uppercase" style="font-size: 0.7rem;"><?= _("Collège / N° Police") ?></small>
|
|
<span class="fw-bold"><?= $beneficiaire['libelleCollege'] ?> / <?= $beneficiaire['numeroPolice'] ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section des plafonds et consommations -->
|
|
<?php
|
|
// --- 1. CALCULS FAMILLE (Police) ---
|
|
$plafondGlobalFamilleOut = $this->nettoyer($police['plafondOut']) ?: 0;
|
|
$consommationGlobaleFamilleOut = $this->nettoyer($police['consommationOut']) ?: 0;
|
|
$soldeConsommationOut = $this->nettoyer($police['soldeConsommationOut']) ?: 0;
|
|
$niveauGlobalConsoFamilleOut = ($plafondGlobalFamilleOut > 0) ? ($consommationGlobaleFamilleOut * 100) / $plafondGlobalFamilleOut : 0;
|
|
|
|
$plafondGlobalFamilleInp = $this->nettoyer($police['plafondInp']) ?: 0;
|
|
$consommationGlobaleFamilleInp = $this->nettoyer($police['consommationInp']) ?: 0;
|
|
$soldeConsommationInp = $this->nettoyer($police['soldeConsommationInp']) ?: 0;
|
|
$niveauGlobalConsoFamilleInp = ($plafondGlobalFamilleInp > 0) ? ($consommationGlobaleFamilleInp * 100) / $plafondGlobalFamilleInp : 0;
|
|
|
|
// --- 2. CALCULS INDIVIDUELS (Bénéficiaire) ---
|
|
$consommationGlobaleBenefOut = $this->nettoyer($college['consommation_individuOut']) ?: 0;
|
|
$niveauGlobalConsoBenefOut = ($plafondGlobalFamilleOut > 0) ? ($consommationGlobaleBenefOut * 100) / $plafondGlobalFamilleOut : 0;
|
|
|
|
$consommationGlobaleBenefInp = $this->nettoyer($college['consommation_individuInp']) ?: 0;
|
|
$niveauGlobalConsoBenefInp = ($plafondGlobalFamilleInp > 0) ? ($consommationGlobaleBenefInp * 100) / $plafondGlobalFamilleInp : 0;
|
|
?>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-12 col-xl-8">
|
|
<div class="card shadow-sm border-0 h-100">
|
|
<div class="card-header bg-dark text-white py-3">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0 fw-bold"><i class="fas fa-users-cog me-2 text-info"></i><?= _("Consommation Familiale") ?></h6>
|
|
<span class="badge bg-secondary"><?= $_SESSION['exercieReference_C'] ?></span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-4">
|
|
<div class="consumption-block mb-5">
|
|
<?php $colorOut = ($niveauGlobalConsoFamilleOut >= 90) ? 'danger' : (($niveauGlobalConsoFamilleOut >= 70) ? 'warning' : 'success'); ?>
|
|
<div class="d-flex justify-content-between align-items-end mb-2">
|
|
<h6 class="text-uppercase fw-bold text-muted small mb-0"><?= _("Soins Ambulatoires (OUT)") ?></h6>
|
|
<span class="fw-bold text-<?= $colorOut ?>"><?= number_format((float)$niveauGlobalConsoFamilleOut, 1) ?>%</span>
|
|
</div>
|
|
<div class="progress mb-3" style="height: 10px;">
|
|
<div class="progress-bar bg-<?= $colorOut ?> progress-bar-striped progress-bar-animated" style="width: <?= min($niveauGlobalConsoFamilleOut, 100) ?>%"></div>
|
|
</div>
|
|
<div class="row g-2 text-center">
|
|
<div class="col-4"><div class="p-2 border rounded bg-light"><small class="text-muted d-block"><?= _("Consommé") ?></small><span class="fw-bold"><?= format_N($consommationGlobaleFamilleOut) ?></span></div></div>
|
|
<div class="col-4"><div class="p-2 border rounded bg-light"><small class="text-muted d-block"><?= _("Plafond") ?></small><span class="fw-bold text-dark"><?= format_N($plafondGlobalFamilleOut) ?></span></div></div>
|
|
<div class="col-4"><div class="p-2 border rounded border-<?= $colorOut ?>-subtle bg-<?= $colorOut ?>-subtle"><small class="text-<?= $colorOut ?> d-block fw-bold"><?= _("Solde") ?></small><span class="fw-bold text-<?= $colorOut ?>"><?= format_N($soldeConsommationOut) ?></span></div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="consumption-block">
|
|
<?php $colorInp = ($niveauGlobalConsoFamilleInp >= 90) ? 'danger' : (($niveauGlobalConsoFamilleInp >= 70) ? 'warning' : 'success'); ?>
|
|
<div class="d-flex justify-content-between align-items-end mb-2">
|
|
<h6 class="text-uppercase fw-bold text-muted small mb-0"><?= _("Hospitalisation (INP)") ?></h6>
|
|
<span class="fw-bold text-<?= $colorInp ?>"><?= number_format((float)$niveauGlobalConsoFamilleInp, 1) ?>%</span>
|
|
</div>
|
|
<div class="progress mb-3" style="height: 10px;">
|
|
<div class="progress-bar bg-<?= $colorInp ?> progress-bar-striped progress-bar-animated" style="width: <?= min($niveauGlobalConsoFamilleInp, 100) ?>%"></div>
|
|
</div>
|
|
<div class="row g-2 text-center">
|
|
<div class="col-4"><div class="p-2 border rounded bg-light"><small class="text-muted d-block"><?= _("Consommé") ?></small><span class="fw-bold"><?= format_N($consommationGlobaleFamilleInp) ?></span></div></div>
|
|
<div class="col-4"><div class="p-2 border rounded bg-light"><small class="text-muted d-block"><?= _("Plafond") ?></small><span class="fw-bold text-dark"><?= format_N($plafondGlobalFamilleInp) ?></span></div></div>
|
|
<div class="col-4"><div class="p-2 border rounded border-<?= $colorInp ?>-subtle bg-<?= $colorInp ?>-subtle"><small class="text-<?= $colorInp ?> d-block fw-bold"><?= _("Solde") ?></small><span class="fw-bold text-<?= $colorInp ?>"><?= format_N($soldeConsommationInp) ?></span></div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 col-xl-4">
|
|
<div class="card shadow-sm border-0 border-top border-primary border-4 h-100">
|
|
<div class="card-header bg-white py-3">
|
|
<h6 class="mb-0 fw-bold text-primary"><i class="fas fa-user-tag me-2"></i><?= _("Dépenses Individuelles") ?></h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php
|
|
$indiv = [
|
|
['label' => 'OUTPATIENT', 'val' => $consommationGlobaleBenefOut, 'percent' => $niveauGlobalConsoBenefOut],
|
|
['label' => 'INPATIENT', 'val' => $consommationGlobaleBenefInp, 'percent' => $niveauGlobalConsoBenefInp]
|
|
];
|
|
foreach($indiv as $item):
|
|
?>
|
|
<div class="mb-4 p-3 border rounded shadow-xs bg-light-subtle">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<small class="fw-bold"><?= $item['label'] ?></small>
|
|
<small class="text-primary fw-bold"><?= format_N($item['val']) ?></small>
|
|
</div>
|
|
<div class="progress" style="height: 6px;">
|
|
<div class="progress-bar bg-primary" style="width: <?= min($item['percent'], 100) ?>%"></div>
|
|
</div>
|
|
<div class="text-end mt-1">
|
|
<small class="text-muted" style="font-size: 0.75rem;">
|
|
<?= _("Part du plafond familial") ?> : <?= number_format((float)$item['percent'], 1) ?>%
|
|
</small>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<div class="alert alert-info border-0 small shadow-sm"><i class="fas fa-info-circle me-1"></i><?= _("Consommation personnelle incluse dans le plafond familial.") ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Message si aucune donnée -->
|
|
<div class="card shadow-sm mb-4">
|
|
|
|
<div class="d-none d-md-block">
|
|
<div class="card-header bg-secondary py-2">
|
|
<h6 class="mb-0 text-center text-light">
|
|
<i class="fa fa-file-invoice-dollar me-2"></i><i class="fa fa-calendar-alt me-2"></i>
|
|
<?= _("Dépenses individuelles par mois") ?>
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
<!-- Affichage mobile (cartes) -->
|
|
<div class="d-md-none">
|
|
<div class="card-header bg-dark py-2">
|
|
<h6 class="mb-0 text-center text-light">
|
|
<i class="fa fa-file-invoice-dollar me-2"></i><i class="fa fa-calendar-alt me-2"></i>
|
|
<?= _("Dépenses individuelles par mois") ?>
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Responsive Area Chart -->
|
|
<div class="row mt-4">
|
|
<div class="col-12 mb-4">
|
|
|
|
<div class="card shadow h-100">
|
|
|
|
<div class="card-header py-3 d-flex flex-column flex-md-row justify-content-between align-items-center">
|
|
<div class="mt-2 mt-md-0">
|
|
<button id="exportTrendBtn" class="btn btn-sm btn-danger mr-2">
|
|
<i class="fas fa-download"></i> PDF
|
|
</button>
|
|
<small class="text-muted"><?= _("Évolution sur 12 mois") ?></small>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="chart-container" style="position: relative; height:300px; width:100%">
|
|
<canvas id="expenseTrendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<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 CFA' ?>';
|
|
|
|
// 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 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>
|
|
|
|
<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;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
}
|
|
</style>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<?php
|
|
if($faceRegistered!="1" && $_SESSION['assureAjoutPhoto']=="1")
|
|
{
|
|
include 'faceebene/ebenetraitementimage.php';
|
|
}
|
|
?>
|
|
|
|
<style>
|
|
/* Styles modernes inspirés de l'interface.jpg */
|
|
.card {
|
|
border: none;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.badge {
|
|
font-weight: 500;
|
|
}
|
|
|
|
|
|
/* Styles pour mobile */
|
|
@media (max-width: 768px) {
|
|
.card-body {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.btn {
|
|
padding: 0.3rem 0.6rem;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
}
|
|
|
|
/* Version ultra-mobile */
|
|
@media (max-width: 576px) {
|
|
.card-body {
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.btn-sm {
|
|
font-size: 0.7rem;
|
|
padding: 0.2rem 0.4rem;
|
|
}
|
|
|
|
.row.text-center .col {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
}
|
|
|
|
|
|
/* Style pour les badges VIP */
|
|
.bg-warning {
|
|
background: linear-gradient(45deg, #ffc107, #ff9800) !important;
|
|
}
|
|
|
|
/* Ombres portées */
|
|
.shadow-lg {
|
|
box-shadow: 0 4px 20px rgba(0,0,0,0.1) !important;
|
|
}
|
|
|
|
/* Bordures arrondies */
|
|
.rounded-circle {
|
|
border: 3px solid #e3f2fd;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// Initialisation des tooltips Bootstrap
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Tooltips
|
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
});
|
|
|
|
// Animation des progress bars
|
|
setTimeout(function() {
|
|
document.querySelectorAll('.progress-bar').forEach(function(bar) {
|
|
const width = bar.style.width;
|
|
bar.style.width = '0';
|
|
setTimeout(function() {
|
|
bar.style.width = width;
|
|
}, 100);
|
|
});
|
|
}, 500);
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const prendreRadio = document.getElementById('prendre_photo_radio');
|
|
const choisirRadio = document.getElementById('choisir_photo_radio');
|
|
const divPrendre = document.getElementById('div_prendre_photo');
|
|
const divChoisir = document.getElementById('div_choisir_photo');
|
|
|
|
// Fonction pour gérer l'affichage
|
|
function togglePhotoDivs() {
|
|
if (prendreRadio.checked) {
|
|
divPrendre.classList.remove('d-none');
|
|
divChoisir.classList.add('d-none');
|
|
} else {
|
|
divPrendre.classList.add('d-none');
|
|
divChoisir.classList.remove('d-none');
|
|
}
|
|
}
|
|
|
|
if(prendreRadio){
|
|
// Événements sur les boutons radios
|
|
prendreRadio.addEventListener('change', togglePhotoDivs);
|
|
choisirRadio.addEventListener('change', togglePhotoDivs);
|
|
|
|
// Initialisation
|
|
togglePhotoDivs();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
const inputTel = document.getElementById('telephonePortable');
|
|
const checkIgnorer = document.getElementById('ignorerFormat');
|
|
const aideTel = document.getElementById('aideTelephone');
|
|
|
|
// Messages traduits pour le JS
|
|
const msgErreur = "<?= est_anglophone() ? 'The number must start with 250 and contain at least 12 digits.' : 'Le numéro doit commencer par 250 et comporter au moins 12 chiffres.'; ?>";
|
|
|
|
// Fonction pour gérer les messages d'erreur personnalisés
|
|
window.validerMessageTelephone = function(input) {
|
|
if (!checkIgnorer.checked) {
|
|
input.setCustomValidity(msgErreur);
|
|
} else {
|
|
input.setCustomValidity('');
|
|
}
|
|
};
|
|
|
|
// Écouteur sur la checkbox pour activer/désactiver les règles
|
|
checkIgnorer.addEventListener('change', function() {
|
|
if (this.checked) {
|
|
// On retire les contraintes
|
|
inputTel.removeAttribute('pattern');
|
|
inputTel.removeAttribute('title');
|
|
inputTel.setCustomValidity('');
|
|
inputTel.classList.remove('is-invalid');
|
|
aideTel.style.display = 'none'; // On cache l'aide du format requis
|
|
inputTel.placeholder = "<?= _('Entrez le numéro'); ?>";
|
|
} else {
|
|
// On remet les contraintes par défaut
|
|
inputTel.setAttribute('pattern', '^250[0-9]{9,}$');
|
|
inputTel.setAttribute('title', msgErreur);
|
|
aideTel.style.display = 'block';
|
|
inputTel.placeholder = "Ex:250700000001";
|
|
|
|
// On redéclenche la validation visuelle
|
|
inputTel.dispatchEvent(new Event('input'));
|
|
}
|
|
});
|
|
|
|
// Validation visuelle en temps réel (is-valid / is-invalid)
|
|
inputTel.addEventListener('input', function() {
|
|
if (checkIgnorer.checked) {
|
|
this.classList.remove('is-invalid');
|
|
this.classList.add('is-valid');
|
|
return;
|
|
}
|
|
|
|
const pattern = /^250[0-9]{9,}$/;
|
|
if (pattern.test(this.value)) {
|
|
this.classList.remove('is-invalid');
|
|
this.classList.add('is-valid');
|
|
} else {
|
|
this.classList.remove('is-valid');
|
|
this.classList.add('is-invalid');
|
|
}
|
|
});
|
|
|
|
function togglePhotoSource(source) {
|
|
const areaCam = document.getElementById('area_prendre_photo');
|
|
const areaFile = document.getElementById('area_choisir_photo');
|
|
|
|
if (source === 'cam') {
|
|
areaCam.classList.remove('d-none');
|
|
areaFile.classList.add('d-none');
|
|
} else {
|
|
areaCam.classList.add('d-none');
|
|
areaFile.classList.remove('d-none');
|
|
}
|
|
}
|
|
</script>
|