From 1bd6407c394317754f82177c70a91f5daa1d7844 Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 7 Aug 2026 23:30:18 +0200 Subject: [PATCH] =?UTF-8?q?feat(mail):=20ajout=20du=20service=20de=20relan?= =?UTF-8?q?ces=20par=20e-mail=20et=20int=C3=A9gration=20HTMX=20dans=20l'ad?= =?UTF-8?q?min?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/RelanceEmailService.java | 309 ++++++++++++++++++ .../core/service/RelanceEmailServiceTest.java | 151 +++++++++ .../web/controller/AdherentController.java | 54 ++- .../AdminPreInscriptionController.java | 22 +- .../resources/templates/adherents/form.html | 19 ++ .../resources/templates/adherents/list.html | 20 ++ .../templates/preinscriptions/list.html | 16 +- 7 files changed, 586 insertions(+), 5 deletions(-) create mode 100644 as-talange-core/src/main/java/com/astalange/core/service/RelanceEmailService.java create mode 100644 as-talange-core/src/test/java/com/astalange/core/service/RelanceEmailServiceTest.java diff --git a/as-talange-core/src/main/java/com/astalange/core/service/RelanceEmailService.java b/as-talange-core/src/main/java/com/astalange/core/service/RelanceEmailService.java new file mode 100644 index 0000000..ebeccad --- /dev/null +++ b/as-talange-core/src/main/java/com/astalange/core/service/RelanceEmailService.java @@ -0,0 +1,309 @@ +package com.astalange.core.service; + +import com.astalange.core.entity.Adherent; +import com.astalange.core.entity.Licence; +import com.astalange.core.entity.PreInscription; +import jakarta.mail.internet.MimeMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.Period; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + +@Service +public class RelanceEmailService { + + private static final Logger log = LoggerFactory.getLogger(RelanceEmailService.class); + + private final JavaMailSender mailSender; + + @Value("${app.mail.from:${spring.mail.username:astalange1933@gmail.com}}") + private String fromEmail = "astalange1933@gmail.com"; + + public RelanceEmailService(@Autowired(required = false) JavaMailSender mailSender) { + this.mailSender = mailSender; + } + + private String getEffectiveFromEmail() { + if (fromEmail != null && !fromEmail.trim().isEmpty() && fromEmail.contains("@")) { + return fromEmail.trim(); + } + return "astalange1933@gmail.com"; + } + + public boolean isMineur(LocalDate dateNaissance) { + if (dateNaissance == null) return false; + return Period.between(dateNaissance, LocalDate.now()).getYears() < 18; + } + + /** + * Envoie une relance aux personnes pré-inscrites pour leur demander de se présenter au club. + */ + public boolean sendRelancePreInscription(PreInscription preInscription) { + if (preInscription == null) { + log.warn("Impossible d'envoyer l'e-mail de relance : la pré-inscription est nulle."); + return false; + } + + String recipientEmail = preInscription.getEmail(); + if (recipientEmail == null || recipientEmail.trim().isEmpty()) { + log.warn("Pré-inscription ID {}: aucun e-mail renseigné. Relance non envoyée.", preInscription.getId()); + return false; + } + + boolean estMineur = isMineur(preInscription.getDateNaissance()); + String adherentNomComplet = preInscription.getPrenom() + " " + preInscription.getNom(); + + String greeting; + String subject; + + if (estMineur) { + String rep = preInscription.getRepresentantLegal(); + if (rep != null && !rep.trim().isEmpty()) { + greeting = "Bonjour " + rep.trim() + " (Représentant légal de " + adherentNomComplet + ")"; + } else { + greeting = "Bonjour (Représentant légal de " + adherentNomComplet + ")"; + } + subject = "AS Talange - Relance demande d'inscription pour " + adherentNomComplet; + } else { + greeting = "Bonjour " + adherentNomComplet; + subject = "AS Talange - Relance concernant votre demande d'inscription"; + } + + String htmlContent = buildRelancePreInscriptionHtml(preInscription, greeting, adherentNomComplet, estMineur); + + CompletableFuture.runAsync(() -> { + if (mailSender != null) { + try { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + helper.setFrom(getEffectiveFromEmail()); + helper.setTo(recipientEmail.trim()); + helper.setSubject(subject); + helper.setText(htmlContent, true); + + mailSender.send(message); + log.info("E-mail de relance pré-inscription envoyé avec succès à {} pour {}", recipientEmail, adherentNomComplet); + } catch (Exception e) { + log.warn("Erreur envoi SMTP relance pré-inscription à {}: {}. Log en mode simulation.", recipientEmail, e.getMessage()); + logDevEmail(recipientEmail, subject, htmlContent); + } + } else { + log.info("Mode DEV (MailSender non configuré) : simulation relance pré-inscription."); + logDevEmail(recipientEmail, subject, htmlContent); + } + }); + + return true; + } + + /** + * Envoie une relance pour les paiements de cotisation incomplets. + */ + public boolean sendRelancePaiementCotisation(Licence licence) { + if (licence == null || licence.getAdherent() == null) { + log.warn("Impossible d'envoyer l'e-mail de relance paiement : licence ou adhérent nul."); + return false; + } + + Adherent adherent = licence.getAdherent(); + String recipientEmail = adherent.getEmail(); + if (recipientEmail == null || recipientEmail.trim().isEmpty()) { + log.warn("Adhérent ID {}: aucun e-mail renseigné. Relance paiement non envoyée.", adherent.getId()); + return false; + } + + BigDecimal resteAPayer = licence.getResteAPayer(); + if (resteAPayer.compareTo(BigDecimal.ZERO) <= 0) { + log.info("Licence ID {}: aucun solde restant à payer (Reste = 0 €). Relance non nécessaire.", licence.getId()); + return false; + } + + boolean estMineur = isMineur(adherent.getDateNaissance()); + String adherentNomComplet = adherent.getPrenom() + " " + adherent.getNom(); + + String greeting; + String subject; + + if (estMineur) { + String rep = adherent.getRepresentantLegal(); + if (rep != null && !rep.trim().isEmpty()) { + greeting = "Bonjour " + rep.trim() + " (Représentant légal de " + adherentNomComplet + ")"; + } else { + greeting = "Bonjour (Représentant légal de " + adherentNomComplet + ")"; + } + subject = "AS Talange - Rappel solde de cotisation pour " + adherentNomComplet; + } else { + greeting = "Bonjour " + adherentNomComplet; + subject = "AS Talange - Rappel important : Solde de cotisation impayé"; + } + + String htmlContent = buildRelancePaiementHtml(licence, greeting, adherentNomComplet, estMineur, resteAPayer); + + CompletableFuture.runAsync(() -> { + if (mailSender != null) { + try { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + helper.setFrom(getEffectiveFromEmail()); + helper.setTo(recipientEmail.trim()); + helper.setSubject(subject); + helper.setText(htmlContent, true); + + mailSender.send(message); + log.info("E-mail de relance paiement envoyé avec succès à {} (Reste: {} €)", recipientEmail, resteAPayer); + } catch (Exception e) { + log.warn("Erreur envoi SMTP relance paiement à {}: {}. Log en mode simulation.", recipientEmail, e.getMessage()); + logDevEmail(recipientEmail, subject, htmlContent); + } + } else { + log.info("Mode DEV (MailSender non configuré) : simulation relance paiement."); + logDevEmail(recipientEmail, subject, htmlContent); + } + }); + + return true; + } + + private void logDevEmail(String recipient, String subject, String body) { + log.info("==================== [SIMULATION E-MAIL RELANCE] ===================="); + log.info("Destinataire: {}", recipient); + log.info("Sujet: {}", subject); + log.info("Contenu:\n{}", body); + log.info("======================================================================"); + } + + private String buildRelancePreInscriptionHtml(PreInscription pre, String greeting, String adherentNomComplet, boolean estMineur) { + String saisonNom = pre.getSaison() != null ? pre.getSaison().getNom() : ""; + String termeSujet = estMineur ? "la demande d'inscription pour votre enfant " + adherentNomComplet + "" : "votre demande d'inscription"; + + return """ + + + + + + + +
+
+

AS Talange - Finalisation de votre inscription

+
+
+

%s,

+

Sauf erreur de notre part, nous avons bien reçu %s pour la saison %s, mais celle-ci n'a pas encore été finalisée au sein de notre club.

+ +
+ Pour finaliser l'inscription : Nous vous invitons à vous présenter directement au secrétariat de l'AS Talange lors de nos permanences pour valider l'inscription. +
+ +

Nous restant à votre entière disposition pour tout renseignement complémentaire.

Sportivement,
L'équipe de l'AS Talange

+
+ +
+ + + """.formatted(greeting, termeSujet, saisonNom); + } + + private String buildRelancePaiementHtml(Licence licence, String greeting, String adherentNomComplet, boolean estMineur, BigDecimal resteAPayer) { + String saisonNom = licence.getSaison() != null ? licence.getSaison().getNom() : ""; + String categorieNom = licence.getCategorie() != null ? licence.getCategorie().getNom() : ""; + String termeSujet = estMineur ? "la cotisation de votre enfant " + adherentNomComplet + "" : "votre cotisation"; + + String prixTotalStr = String.format(Locale.FRANCE, "%.2f €", licence.getPrixTotal()); + String totalPayeStr = String.format(Locale.FRANCE, "%.2f €", licence.getSommePayee()); + String resteStr = String.format(Locale.FRANCE, "%.2f €", resteAPayer); + + return """ + + + + + + + +
+
+

AS Talange - Rappel de solde de cotisation

+
+
+

%s,

+

Nous vous contactons concernant %s (saison %s, catégorie %s) pour laquelle un solde reste actuellement à régler.

+ +
+

Situation Financière

+ + + + + + + + + + + + + + + + + +
Adhérent :%s
Montant total cotisation :%s
Total versé à ce jour :%s
Solde restant à régler :%s
+
+ +
+ Important :
+ Conformément aux statuts et au règlement intérieur de l'AS Talange, tant que le paiement de la cotisation n'est pas intégralement finalisé, l'inscription et la licence ne seront pas valides. L'accès aux entraînements et aux rencontres officielles pourra être suspendu. +
+ +

Nous vous demandons de bien vouloir vous présenter au secrétariat du club lors des permanences afin d'effectuer le règlement du solde restant (%s).

+ +

Comptant sur votre prompt réajustement,
Sportivement,
Le Bureau de l'AS Talange

+
+ +
+ + + """.formatted(greeting, termeSujet, saisonNom, categorieNom, adherentNomComplet, prixTotalStr, totalPayeStr, resteStr, resteStr); + } +} diff --git a/as-talange-core/src/test/java/com/astalange/core/service/RelanceEmailServiceTest.java b/as-talange-core/src/test/java/com/astalange/core/service/RelanceEmailServiceTest.java new file mode 100644 index 0000000..5b6b529 --- /dev/null +++ b/as-talange-core/src/test/java/com/astalange/core/service/RelanceEmailServiceTest.java @@ -0,0 +1,151 @@ +package com.astalange.core.service; + +import com.astalange.core.entity.Adherent; +import com.astalange.core.entity.Categorie; +import com.astalange.core.entity.Licence; +import com.astalange.core.entity.PreInscription; +import com.astalange.core.entity.Saison; +import jakarta.mail.internet.MimeMessage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.mail.javamail.JavaMailSender; + +import java.math.BigDecimal; +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class RelanceEmailServiceTest { + + private RelanceEmailService relanceEmailService; + private JavaMailSender mailSenderMock; + + @BeforeEach + void setUp() { + mailSenderMock = mock(JavaMailSender.class); + MimeMessage mimeMessage = new MimeMessage((jakarta.mail.Session) null); + when(mailSenderMock.createMimeMessage()).thenReturn(mimeMessage); + relanceEmailService = new RelanceEmailService(mailSenderMock); + } + + @Test + void testIsMineur() { + LocalDate minorDob = LocalDate.now().minusYears(10); + LocalDate adultDob = LocalDate.now().minusYears(20); + + assertTrue(relanceEmailService.isMineur(minorDob)); + assertFalse(relanceEmailService.isMineur(adultDob)); + assertFalse(relanceEmailService.isMineur(null)); + } + + @Test + void testSendRelancePreInscription_Mineur() { + PreInscription pre = new PreInscription(); + pre.setId(1L); + pre.setNom("MARTIN"); + pre.setPrenom("Lucas"); + pre.setEmail("parent.martin@example.com"); + pre.setDateNaissance(LocalDate.now().minusYears(12)); + pre.setRepresentantLegal("Marc MARTIN"); + + Saison saison = new Saison(); + saison.setNom("2026/2027"); + pre.setSaison(saison); + + boolean sent = relanceEmailService.sendRelancePreInscription(pre); + assertTrue(sent); + + verify(mailSenderMock, timeout(2000).times(1)).send(any(MimeMessage.class)); + } + + @Test + void testSendRelancePreInscription_Majeur() { + PreInscription pre = new PreInscription(); + pre.setId(2L); + pre.setNom("DUBOIS"); + pre.setPrenom("Alexandre"); + pre.setEmail("alex.dubois@example.com"); + pre.setDateNaissance(LocalDate.now().minusYears(25)); + + Saison saison = new Saison(); + saison.setNom("2026/2027"); + pre.setSaison(saison); + + boolean sent = relanceEmailService.sendRelancePreInscription(pre); + assertTrue(sent); + + verify(mailSenderMock, timeout(2000).times(1)).send(any(MimeMessage.class)); + } + + @Test + void testSendRelancePreInscription_SansEmail() { + PreInscription pre = new PreInscription(); + pre.setId(3L); + pre.setEmail(null); + + boolean sent = relanceEmailService.sendRelancePreInscription(pre); + assertFalse(sent); + verify(mailSenderMock, never()).send(any(MimeMessage.class)); + } + + @Test + void testSendRelancePaiementCotisation_MineurAvecReste() { + Adherent adherent = new Adherent(); + adherent.setId(10L); + adherent.setNom("GARCIA"); + adherent.setPrenom("Leo"); + adherent.setEmail("parent.garcia@example.com"); + adherent.setDateNaissance(LocalDate.now().minusYears(14)); + adherent.setRepresentantLegal("Sophie GARCIA"); + + Categorie cat = new Categorie(); + cat.setNom("U15"); + cat.setTarifBase(new BigDecimal("180.00")); + cat.setTarifExterieur(new BigDecimal("180.00")); + + Saison saison = new Saison(); + saison.setNom("2026/2027"); + + Licence licence = new Licence(); + licence.setId(100L); + licence.setAdherent(adherent); + licence.setCategorie(cat); + licence.setSaison(saison); + + boolean sent = relanceEmailService.sendRelancePaiementCotisation(licence); + assertTrue(sent); + + verify(mailSenderMock, timeout(2000).times(1)).send(any(MimeMessage.class)); + } + + @Test + void testSendRelancePaiementCotisation_SoldeZero() { + Adherent adherent = new Adherent(); + adherent.setId(11L); + adherent.setNom("ROUX"); + adherent.setPrenom("Thomas"); + adherent.setEmail("thomas.roux@example.com"); + adherent.setDateNaissance(LocalDate.now().minusYears(22)); + + Categorie cat = new Categorie(); + cat.setNom("Senior"); + cat.setTarifBase(new BigDecimal("200.00")); + cat.setTarifExterieur(new BigDecimal("200.00")); + + Licence licence = new Licence(); + licence.setId(101L); + licence.setAdherent(adherent); + licence.setCategorie(cat); + + // Simulation d'un paiement intégral + com.astalange.core.entity.Paiement p = new com.astalange.core.entity.Paiement(); + p.setMontant(new BigDecimal("200.00")); + licence.addPaiement(p); + + boolean sent = relanceEmailService.sendRelancePaiementCotisation(licence); + assertFalse(sent, "Aucune relance ne doit être envoyée si le reste à payer est égal à 0"); + verify(mailSenderMock, never()).send(any(MimeMessage.class)); + } +} diff --git a/as-talange-web/src/main/java/com/astalange/web/controller/AdherentController.java b/as-talange-web/src/main/java/com/astalange/web/controller/AdherentController.java index 62bd049..3ca7c40 100644 --- a/as-talange-web/src/main/java/com/astalange/web/controller/AdherentController.java +++ b/as-talange-web/src/main/java/com/astalange/web/controller/AdherentController.java @@ -25,8 +25,9 @@ public class AdherentController { private final SaisonRepository saisonRepository; private final com.astalange.core.service.CategorieService categorieService; private final com.astalange.core.service.SportEasyEmailService sportEasyEmailService; + private final com.astalange.core.service.RelanceEmailService relanceEmailService; - public AdherentController(AdherentRepository adherentRepository, CategorieRepository categorieRepository, com.astalange.core.repository.LicenceRepository licenceRepository, com.astalange.core.repository.ModePaiementRepository modePaiementRepository, SaisonRepository saisonRepository, com.astalange.core.service.CategorieService categorieService, com.astalange.core.service.SportEasyEmailService sportEasyEmailService) { + public AdherentController(AdherentRepository adherentRepository, CategorieRepository categorieRepository, com.astalange.core.repository.LicenceRepository licenceRepository, com.astalange.core.repository.ModePaiementRepository modePaiementRepository, SaisonRepository saisonRepository, com.astalange.core.service.CategorieService categorieService, com.astalange.core.service.SportEasyEmailService sportEasyEmailService, com.astalange.core.service.RelanceEmailService relanceEmailService) { this.adherentRepository = adherentRepository; this.categorieRepository = categorieRepository; this.licenceRepository = licenceRepository; @@ -34,6 +35,7 @@ public class AdherentController { this.saisonRepository = saisonRepository; this.categorieService = categorieService; this.sportEasyEmailService = sportEasyEmailService; + this.relanceEmailService = relanceEmailService; } @org.springframework.web.bind.annotation.ModelAttribute("equipes") @@ -382,4 +384,54 @@ public class AdherentController { return "redirect:/adherents"; } + + @org.springframework.web.bind.annotation.PostMapping("/{id}/relancer-paiement") + public String relancerPaiement( + @org.springframework.web.bind.annotation.PathVariable Long id, + org.springframework.web.servlet.mvc.support.RedirectAttributes redirectAttributes) { + + Adherent adherent = adherentRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("Adhérent invalide : " + id)); + + Licence licence = adherent.getLicenceActuelle(); + if (licence == null) { + redirectAttributes.addFlashAttribute("errorMessage", "Cet adhérent n'a pas de licence enregistrée."); + return "redirect:/adherents"; + } + + if (licence.getResteAPayer().compareTo(java.math.BigDecimal.ZERO) <= 0) { + redirectAttributes.addFlashAttribute("infoMessage", "Cet adhérent est déjà à jour de sa cotisation."); + return "redirect:/adherents"; + } + + boolean sent = relanceEmailService.sendRelancePaiementCotisation(licence); + if (sent) { + redirectAttributes.addFlashAttribute("successMessage", "L'e-mail de relance de paiement pour " + adherent.getPrenom() + " " + adherent.getNom() + " a été envoyé avec succès."); + } else { + redirectAttributes.addFlashAttribute("errorMessage", "Impossible d'envoyer l'e-mail de relance (adresse e-mail manquante)."); + } + + return "redirect:/adherents"; + } + + @org.springframework.web.bind.annotation.PostMapping("/{id}/relancer-paiement-htmx") + @org.springframework.web.bind.annotation.ResponseBody + public String relancerPaiementHtmx(@org.springframework.web.bind.annotation.PathVariable Long id) { + Adherent adherent = adherentRepository.findById(id).orElse(null); + if (adherent == null || adherent.getLicenceActuelle() == null) { + return "Introuvable"; + } + + Licence licence = adherent.getLicenceActuelle(); + if (licence.getResteAPayer().compareTo(java.math.BigDecimal.ZERO) <= 0) { + return "Déjà réglé"; + } + + boolean sent = relanceEmailService.sendRelancePaiementCotisation(licence); + if (sent) { + return " Relance envoyée"; + } else { + return "Email manquant"; + } + } } diff --git a/as-talange-web/src/main/java/com/astalange/web/controller/AdminPreInscriptionController.java b/as-talange-web/src/main/java/com/astalange/web/controller/AdminPreInscriptionController.java index b21d309..4297dc7 100644 --- a/as-talange-web/src/main/java/com/astalange/web/controller/AdminPreInscriptionController.java +++ b/as-talange-web/src/main/java/com/astalange/web/controller/AdminPreInscriptionController.java @@ -8,17 +8,23 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import com.astalange.core.entity.PreInscription; +import com.astalange.core.service.RelanceEmailService; + @Controller @RequestMapping("/admin/pre-inscriptions") public class AdminPreInscriptionController { private final PreInscriptionRepository preInscriptionRepository; private final PreInscriptionService preInscriptionService; + private final RelanceEmailService relanceEmailService; public AdminPreInscriptionController(PreInscriptionRepository preInscriptionRepository, - PreInscriptionService preInscriptionService) { + PreInscriptionService preInscriptionService, + RelanceEmailService relanceEmailService) { this.preInscriptionRepository = preInscriptionRepository; this.preInscriptionService = preInscriptionService; + this.relanceEmailService = relanceEmailService; } @GetMapping @@ -46,6 +52,20 @@ public class AdminPreInscriptionController { return ""; // HTMX target la ligne avec outerHTML -> supprime la ligne } + @PostMapping("/{id}/relancer") + @ResponseBody + public String relancer(@PathVariable Long id) { + PreInscription pre = preInscriptionRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("Pré-inscription introuvable : " + id)); + + boolean sent = relanceEmailService.sendRelancePreInscription(pre); + if (sent) { + return " Relance envoyée"; + } else { + return "Erreur (sans email)"; + } + } + @GetMapping("/count") @ResponseBody public String countBadge() { diff --git a/as-talange-web/src/main/resources/templates/adherents/form.html b/as-talange-web/src/main/resources/templates/adherents/form.html index 6bf08bc..d197332 100644 --- a/as-talange-web/src/main/resources/templates/adherents/form.html +++ b/as-talange-web/src/main/resources/templates/adherents/form.html @@ -10,6 +10,8 @@ body { font-family: 'Inter', sans-serif; } .hidden-block { display: none; } + + @@ -306,6 +308,15 @@ th:data-reste-a-payer="${lic.getResteAPayer()}" onclick="openPaiementModal(this.getAttribute('data-licence-id'), this.getAttribute('data-reste-a-payer'))" class="text-green-600 hover:text-green-800 font-medium bg-green-50 px-3 py-1 rounded-lg btn-payer">Payer + + + @@ -1081,6 +1092,14 @@ if (typeMaillotSelect) { typeMaillotSelect.addEventListener('change', updateEquipmentStyleLabels); } + + document.body.addEventListener('htmx:configRequest', function(evt) { + var csrfHeader = document.querySelector('meta[name="_csrf_header"]'); + var csrfToken = document.querySelector('meta[name="_csrf"]'); + if (csrfHeader && csrfToken) { + evt.detail.headers[csrfHeader.content] = csrfToken.content; + } + }); }); diff --git a/as-talange-web/src/main/resources/templates/adherents/list.html b/as-talange-web/src/main/resources/templates/adherents/list.html index 800e561..a653935 100644 --- a/as-talange-web/src/main/resources/templates/adherents/list.html +++ b/as-talange-web/src/main/resources/templates/adherents/list.html @@ -9,6 +9,8 @@ + + @@ -279,6 +281,16 @@ 50 € + +
+ +
@@ -415,6 +427,14 @@ closeAllEquipementPopovers(); } }); + + document.body.addEventListener('htmx:configRequest', function(evt) { + var csrfHeader = document.querySelector('meta[name="_csrf_header"]'); + var csrfToken = document.querySelector('meta[name="_csrf"]'); + if (csrfHeader && csrfToken) { + evt.detail.headers[csrfHeader.content] = csrfToken.content; + } + }); diff --git a/as-talange-web/src/main/resources/templates/preinscriptions/list.html b/as-talange-web/src/main/resources/templates/preinscriptions/list.html index e0d4bb6..904dc0a 100644 --- a/as-talange-web/src/main/resources/templates/preinscriptions/list.html +++ b/as-talange-web/src/main/resources/templates/preinscriptions/list.html @@ -45,15 +45,25 @@
- + + + +