<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Documentation UTB Blog</title>
        <link>https://your-docusaurus-site.example.com/changelog</link>
        <description>Documentation UTB Blog</description>
        <lastBuildDate>Thu, 13 Aug 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Nettoyage de code mort, correctifs de configuration, et ajustements d'interface publique]]></title>
            <link>https://your-docusaurus-site.example.com/changelog/nettoyage-et-ajustements-interface</link>
            <guid>https://your-docusaurus-site.example.com/changelog/nettoyage-et-ajustements-interface</guid>
            <pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Regroupe une série de corrections plus ponctuelles : suppression de code inutilisé confirmé, deux petits bugs de configuration corrigés, et plusieurs ajustements visuels sur le site public.]]></description>
            <content:encoded><![CDATA[<p>Regroupe une série de corrections plus ponctuelles : suppression de code inutilisé confirmé, deux petits bugs de configuration corrigés, et plusieurs ajustements visuels sur le site public.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="nettoyage-de-code-confirmé-inutilisé">Nettoyage de code confirmé inutilisé<a href="https://your-docusaurus-site.example.com/changelog/nettoyage-et-ajustements-interface#nettoyage-de-code-confirm%C3%A9-inutilis%C3%A9" class="hash-link" aria-label="Direct link to Nettoyage de code confirmé inutilisé" title="Direct link to Nettoyage de code confirmé inutilisé" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>banniere</code>/<code>page</code></strong> (contrôleur, service, routes, montage <code>/api/pages</code>/<code>/api/bannieres</code>) — remplacés de longue date par <code>slides</code>/<code>slide_elements</code> pour les bannières de la page d'accueil, mais jamais retirés. Recherche exhaustive faite dans le frontend avant suppression : aucun appel nulle part. Les tables Prisma associées n'ont pas été touchées (suppression de schéma volontairement hors périmètre).</li>
<li class=""><strong><code>account.controller.js</code></strong> (+ ses routes, montage <code>/api/account</code>) — doublon de <code>account_requests.controller.js</code> sur la même table, plus utilisé par le frontend depuis la correction de son bug de routing (voir le post sécurité). Vérifié inutilisé côté frontend et backend avant suppression.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="corrections-de-configuration">Corrections de configuration<a href="https://your-docusaurus-site.example.com/changelog/nettoyage-et-ajustements-interface#corrections-de-configuration" class="hash-link" aria-label="Direct link to Corrections de configuration" title="Direct link to Corrections de configuration" translate="no">​</a></h2>
<ul>
<li class=""><strong>Sauvegarde des bannières d'accueil</strong> : l'éditeur (<code>SlidesEditor.vue</code>/<code>ContentView.vue</code>) enchaînait une suppression puis une recréation complète via plusieurs appels DELETE/POST successifs, sans transaction — un échec en cours de séquence pouvait laisser la page d'accueil sans aucune bannière active. Remplacé par un unique endpoint <code>PUT /api/slides</code>, qui effectue le remplacement dans une seule transaction Prisma.</li>
<li class=""><strong><code>bancassurance</code> invisible dans les écrans de configuration des prêts</strong> : la liste des types de prêt sélectionnables dans <code>LoanDocumentsConfigView.vue</code>/<code>LoanFieldsConfigView.vue</code> était codée en dur et omettait ce type, alors que <code>loan_parameters</code> le gérait déjà — impossible jusqu'ici de lui configurer des documents ou champs requis. Ajouté aux deux écrans.</li>
<li class=""><strong>Garde-fou manquant sur le rôle <code>SUPER_ADMIN</code></strong> : rien n'empêchait de sauvegarder ce rôle avec un tableau de permissions vide depuis l'écran "Rôles" (tout décoché par erreur, par exemple). <code>updateRole</code> refuse désormais explicitement cette sauvegarde.</li>
<li class=""><strong>CNI des signataires/membres du bureau/promoteur</strong> (formulaire d'ouverture de compte) : ces pièces restaient imbriquées en base64 dans <code>additional_data</code>, sans jamais être créées dans la table <code>documents</code> — invisibles depuis la fiche backoffice. Elles sont désormais incluses dans <code>additional_data.documents</code> comme les autres pièces, sans changement backend nécessaire (le mécanisme d'extraction était déjà générique).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="site-public">Site public<a href="https://your-docusaurus-site.example.com/changelog/nettoyage-et-ajustements-interface#site-public" class="hash-link" aria-label="Direct link to Site public" title="Direct link to Site public" translate="no">​</a></h2>
<ul>
<li class=""><strong>Bloc horaires d'agence</strong> (<code>/agences</code>) : le message "les horaires peuvent varier..." était en italique gris clair, se lisant comme une note secondaire alors qu'il s'agit d'un avertissement réel. Passé au code couleur ambre déjà utilisé ailleurs sur le site pour signaler "à vérifier".</li>
<li class=""><strong>Section "E-UTB" de la page d'accueil</strong> : refonte à la demande — image et boutons "Particulier"/"Entreprise" retirés, mentions "(OTR)"/"(CNSS)" retirées du texte, nouvelle mention "E-UTB, disponible 24h/24."</li>
<li class=""><strong>Carousel de la page d'accueil, affichage mobile</strong> : les flèches précédent/suivant sont masquées sur mobile (previously visibles et peu adaptées au tactile) ; les éléments de chaque diapositive (texte/boutons) passent d'un positionnement absolu en pourcentage — qui pouvait se chevaucher sur petit écran — à un empilement vertical centré.</li>
<li class=""><strong>Police de caractère personnalisée</strong> pour le nom de la banque dans le menu latéral mobile (<code>Navbar.vue</code>), via une nouvelle déclaration <code>@font-face</code>.</li>
</ul>]]></content:encoded>
            <category>Backoffice</category>
            <category>Site public</category>
        </item>
        <item>
            <title><![CDATA[Demandes de prêt : chargement à la demande par type puis par agence, au lieu de tout charger d'un coup]]></title>
            <link>https://your-docusaurus-site.example.com/changelog/nouvelle-vue-demandes-de-pret</link>
            <guid>https://your-docusaurus-site.example.com/changelog/nouvelle-vue-demandes-de-pret</guid>
            <pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[La page backoffice "Demandes de prêt" chargeait systématiquement l'intégralité des dossiers du périmètre de l'utilisateur en un seul appel, puis regroupait et filtrait tout côté client en mémoire. Elle affiche désormais uniquement des compteurs au chargement, et ne va chercher une liste réelle qu'au clic sur un type de prêt puis, si nécessaire, une agence.]]></description>
            <content:encoded><![CDATA[<p>La page backoffice "Demandes de prêt" chargeait systématiquement l'intégralité des dossiers du périmètre de l'utilisateur en un seul appel, puis regroupait et filtrait tout côté client en mémoire. Elle affiche désormais uniquement des compteurs au chargement, et ne va chercher une liste réelle qu'au clic sur un type de prêt puis, si nécessaire, une agence.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pourquoi">Pourquoi<a href="https://your-docusaurus-site.example.com/changelog/nouvelle-vue-demandes-de-pret#pourquoi" class="hash-link" aria-label="Direct link to Pourquoi" title="Direct link to Pourquoi" translate="no">​</a></h2>
<p>Sans conséquence visible avec le faible volume de données actuel, cette approche ne tient pas à l'échelle : charger l'intégralité des demandes de prêt à chaque ouverture de la page devient coûteux dès que le nombre de dossiers grandit. L'interface elle-même (des dossiers "Prêt Immobilier — 0 demande(s)" etc. tous dépliés en même temps) était aussi peu lisible.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ce-qui-change">Ce qui change<a href="https://your-docusaurus-site.example.com/changelog/nouvelle-vue-demandes-de-pret#ce-qui-change" class="hash-link" aria-label="Direct link to Ce qui change" title="Direct link to Ce qui change" translate="no">​</a></h2>
<ul>
<li class=""><strong>Nouvel endpoint <code>GET /api/loan-requests/counts</code></strong> : renvoie uniquement des compteurs par type de prêt (et, pour chaque type, par agence) — jamais les dossiers eux-mêmes. C'est ce qui peuple les boutons "Prêt Scolaire — 1 demande(s)" etc. sans coût de chargement.</li>
<li class=""><strong><code>GET /api/loan-requests</code> accepte désormais <code>?loan_type=</code> et <code>?id_agence=</code></strong> (optionnels), appelé uniquement au clic. Le filtre <code>id_agence</code> est revérifié côté serveur — impossible d'obtenir les dossiers d'une agence hors de son périmètre en modifiant les paramètres de la requête.</li>
<li class=""><strong>Une seule agence concernée par un type ? Aucune étape intermédiaire</strong> : la liste se charge directement au clic sur le type. C'est systématiquement le cas pour un agent/chef d'agence (une seule agence possible), et parfois aussi pour un chef de zone ou un super admin si un type donné n'a de dossiers que dans une seule agence.</li>
<li class=""><strong>Chaque liste chargée est mise en cache</strong> — revenir sur un type/agence déjà consulté ne redéclenche pas d'appel réseau.</li>
<li class=""><strong>Recherche et filtre de statut</strong> limités à ce qui est déjà affiché à l'écran, plus de recherche globale cross-type/cross-agence (décision assumée, pas une limitation technique).</li>
<li class=""><strong>Le bouton "Statut" a été retiré des cartes de la liste</strong> — changer le statut d'un dossier n'est désormais possible que depuis sa fiche détail, pour éviter une action à fort impact accessible en un clic depuis une vue de survol.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="page-de-doc">Page de doc<a href="https://your-docusaurus-site.example.com/changelog/nouvelle-vue-demandes-de-pret#page-de-doc" class="hash-link" aria-label="Direct link to Page de doc" title="Direct link to Page de doc" translate="no">​</a></h2>
<p><a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/liste-prets">Liste des demandes de prêt</a> — détaille le format de réponse de <code>/counts</code>, le piège d'ordre des routes Express évité (<code>/counts</code> déclarée avant <code>/:id</code>), et la dépendance directe sur le filtrage par agence/zone.</p>]]></content:encoded>
            <category>Backoffice</category>
            <category>API Backend</category>
        </item>
        <item>
            <title><![CDATA[Sécurité : plusieurs accès non protégés corrigés, et un filtre par zone qui ne fonctionnait pas réellement]]></title>
            <link>https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone</link>
            <guid>https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone</guid>
            <pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Une passe d'audit ciblée a mis au jour plusieurs endpoints backend accessibles sans authentification ou sans vérification du périmètre agence/zone de l'utilisateur — tous corrigés. Le cas le plus instructif : un correctif déjà appliqué au filtrage par zone s'est révélé totalement inopérant, découvert seulement en testant avec un vrai compte connecté plutôt qu'en relisant le code.]]></description>
            <content:encoded><![CDATA[<p>Une passe d'audit ciblée a mis au jour plusieurs endpoints backend accessibles sans authentification ou sans vérification du périmètre agence/zone de l'utilisateur — tous corrigés. Le cas le plus instructif : un correctif déjà appliqué au filtrage par zone s'est révélé <strong>totalement inopérant</strong>, découvert seulement en testant avec un vrai compte connecté plutôt qu'en relisant le code.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ce-qui-était-exposé">Ce qui était exposé<a href="https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone#ce-qui-%C3%A9tait-expos%C3%A9" class="hash-link" aria-label="Direct link to Ce qui était exposé" title="Direct link to Ce qui était exposé" translate="no">​</a></h2>
<ul>
<li class=""><strong><code>PUT /api/loan-parameters/:id</code></strong> — aucune authentification requise : n'importe qui pouvait modifier un taux d'intérêt ou un plafond de prêt.</li>
<li class=""><strong><code>GET /api/alertes</code></strong> et <strong><code>GET /api/alertes/:id</code></strong> — aucune authentification requise : n'importe qui pouvait lister l'intégralité des signalements confidentiels (fraude, questions sensibles).</li>
<li class=""><strong><code>POST/PUT/DELETE /api/users</code></strong> — authentification requise mais sans restriction de rôle : n'importe quel compte backoffice pouvait créer, modifier ou désactiver un autre utilisateur, y compris se promouvoir <code>SUPER_ADMIN</code>.</li>
<li class=""><strong><code>GET /api/account-requests</code></strong> et <strong><code>GET /api/account-requests/:id</code></strong> — aucune authentification requise : identité, téléphone, email, adresse et données FATCA/TIN de toutes les demandes d'ouverture de compte étaient publiquement listables.</li>
<li class=""><strong><code>PATCH /api/loan-requests/:id</code></strong> (<code>updateLoanStatus</code>) — authentifié, mais sans aucune vérification d'appartenance à une agence : n'importe quel utilisateur backoffice pouvait changer le statut de n'importe quelle demande de prêt, y compris celles d'une autre agence. Le point le plus sensible du contrôleur puisqu'il exécute la décision, pas une simple consultation.</li>
</ul>
<p>Toutes ces routes exigent désormais une authentification (<code>verifyToken</code>), et les deux dernières sont en plus scopées par agence/zone.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="le-filtre-par-zone-chef_zone--corrigé-cassé-à-nouveau-puis-vraiment-corrigé">Le filtre par zone (<code>CHEF_ZONE</code>) : corrigé, cassé à nouveau, puis vraiment corrigé<a href="https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone#le-filtre-par-zone-chef_zone--corrig%C3%A9-cass%C3%A9-%C3%A0-nouveau-puis-vraiment-corrig%C3%A9" class="hash-link" aria-label="Direct link to le-filtre-par-zone-chef_zone--corrigé-cassé-à-nouveau-puis-vraiment-corrigé" title="Direct link to le-filtre-par-zone-chef_zone--corrigé-cassé-à-nouveau-puis-vraiment-corrigé" translate="no">​</a></h2>
<p>Un module partagé (<code>backend/src/utils/agency-scope.js</code>, <code>agencyScopeWhere</code>/<code>canAccessAgency</code>) avait été écrit pour que le rôle <code>CHEF_ZONE</code> ne voie que les agences de sa propre zone, au lieu de tout voir comme un <code>SUPER_ADMIN</code>. Ce correctif est <strong>resté inopérant</strong> pour deux raisons cumulées, découvertes uniquement en connectant un vrai compte <code>CHEF_ZONE</code> (login + 2FA + appels API réels, pas une relecture de code) :</p>
<ol>
<li class=""><code>CHEF_ZONE</code> avait la permission <code>view_all_agencies</code> dans <code>seed/seed_roles.js</code> — cette permission court-circuite en premier toute la logique de filtrage, rendant la restriction par zone écrite juste en dessous inatteignable. Retirée des permissions du rôle.</li>
<li class="">Une fois ce point corrigé, plus aucun dossier n'était visible pour <code>CHEF_ZONE</code> — régression inverse. Cause : <code>id_zone</code> n'était <strong>jamais inclus dans le JWT</strong> émis à la connexion (<code>verify2fa</code>), seul <code>id_agence</code> l'était. Un <code>CHEF_ZONE</code> n'a pas de <code>id_agence</code> (<code>null</code>), donc le filtre de repli ne matchait jamais rien. Corrigé en ajoutant <code>id_zone</code> au payload du token et à <code>getMe</code>.</li>
</ol>
<p>Vérifié par un test de bout en bout : un <code>CHEF_ZONE</code> de la zone Maritime voit les dossiers de son agence, et reçoit un 403 sur une agence d'une autre zone.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pourquoi-ce-post-existe">Pourquoi ce post existe<a href="https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone#pourquoi-ce-post-existe" class="hash-link" aria-label="Direct link to Pourquoi ce post existe" title="Direct link to Pourquoi ce post existe" translate="no">​</a></h2>
<p>Le premier correctif du filtre par zone avait été documenté comme terminé sans avoir été testé en conditions réelles — il avait l'air correct en lecture de code tout en étant complètement sans effet. La leçon retenue et appliquée depuis : vérifier par un vrai appel authentifié avant de marquer un correctif de sécurité comme résolu, pas seulement relire le code.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pages-de-doc-à-jour">Pages de doc à jour<a href="https://your-docusaurus-site.example.com/changelog/securite-acces-agence-zone#pages-de-doc-%C3%A0-jour" class="hash-link" aria-label="Direct link to Pages de doc à jour" title="Direct link to Pages de doc à jour" translate="no">​</a></h2>
<p><a class="" href="https://your-docusaurus-site.example.com/docs/conventions-dette/bugs-securite-connus">Bugs &amp; failles actives connues</a>, <a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/agences-zones-gab">Agences, zones &amp; GAB</a>, <a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/auth-2fa">Authentification &amp; 2FA</a>, <a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/fiche-compte">Fiche détail d'une demande de compte</a>.</p>]]></content:encoded>
            <category>Backoffice</category>
            <category>API Backend</category>
            <category>Sécurité</category>
        </item>
        <item>
            <title><![CDATA[Fiche prêt backoffice : aperçu de l'email avant d'enregistrer une décision]]></title>
            <link>https://your-docusaurus-site.example.com/changelog/decision-pret-apercu-email</link>
            <guid>https://your-docusaurus-site.example.com/changelog/decision-pret-apercu-email</guid>
            <pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Sur la fiche détail d'une demande de prêt, un gestionnaire qui change le statut d'un dossier voit désormais l'email exact qui sera envoyé au client avant de valider sa décision, au lieu d'un envoi immédiat et silencieux.]]></description>
            <content:encoded><![CDATA[<p>Sur la <a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/fiche-pret#2-d%C3%A9cision-statut--note--avec-aper%C3%A7u-de-lemail-avant-envoi">fiche détail d'une demande de prêt</a>, un gestionnaire qui change le statut d'un dossier voit désormais <strong>l'email exact qui sera envoyé au client</strong> avant de valider sa décision, au lieu d'un envoi immédiat et silencieux.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ce-qui-change">Ce qui change<a href="https://your-docusaurus-site.example.com/changelog/decision-pret-apercu-email#ce-qui-change" class="hash-link" aria-label="Direct link to Ce qui change" title="Direct link to Ce qui change" translate="no">​</a></h2>
<ul>
<li class="">Clic sur "Enregistrer la Décision" → un modal affiche l'objet et le corps complet de l'email qui partira au client (ou "Aucun email ne sera envoyé" si le nouveau statut n'en déclenche pas, ex. retour à <code>EN_ATTENTE</code>).</li>
<li class="">Le gestionnaire valide explicitement ("OK, envoyer") ou annule — le statut n'est enregistré et l'email n'est envoyé qu'à cette confirmation.</li>
<li class="">Si le statut choisi est identique au statut actuel, l'enregistrement se fait directement sans passer par l'aperçu (aucun email en jeu).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pourquoi">Pourquoi<a href="https://your-docusaurus-site.example.com/changelog/decision-pret-apercu-email#pourquoi" class="hash-link" aria-label="Direct link to Pourquoi" title="Direct link to Pourquoi" translate="no">​</a></h2>
<p>Avant ce changement, un changement de statut envoyait l'email immédiatement, sans que le gestionnaire ait pu relire le contenu exact (montant retenu, motif saisi) avant l'envoi effectif au client. L'aperçu réutilise la même fonction de construction du contenu que l'envoi réel (<code>buildStatusUpdateEmailContent</code>) — ce qui est affiché correspond garanti à ce qui part, pas une approximation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="doc-impactée">Doc impactée<a href="https://your-docusaurus-site.example.com/changelog/decision-pret-apercu-email#doc-impact%C3%A9e" class="hash-link" aria-label="Direct link to Doc impactée" title="Direct link to Doc impactée" translate="no">​</a></h2>
<p><a class="" href="https://your-docusaurus-site.example.com/docs/backoffice/fiche-pret">Fiche détail d'une demande de prêt</a> — section "Décision (statut + note) — avec aperçu de l'email avant envoi", déjà mise à jour.</p>]]></content:encoded>
            <category>Backoffice</category>
            <category>API Backend</category>
            <category>Sécurité</category>
        </item>
    </channel>
</rss>