Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 87c65c6ebd | |||
| bb24363500 | |||
| 19139124ca |
@@ -17,6 +17,9 @@ public class Saison {
|
|||||||
@Column(name = "est_active", nullable = false)
|
@Column(name = "est_active", nullable = false)
|
||||||
private Boolean estActive = false;
|
private Boolean estActive = false;
|
||||||
|
|
||||||
|
@Column(name = "inscriptions_ouvertes", nullable = false)
|
||||||
|
private Boolean inscriptionsOuvertes = false;
|
||||||
|
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
@@ -43,10 +46,20 @@ public class Saison {
|
|||||||
this.estActive = estActive;
|
this.estActive = estActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getInscriptionsOuvertes() {
|
||||||
|
return inscriptionsOuvertes != null ? inscriptionsOuvertes : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInscriptionsOuvertes(Boolean inscriptionsOuvertes) {
|
||||||
|
this.inscriptionsOuvertes = inscriptionsOuvertes;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o)
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
Saison saison = (Saison) o;
|
Saison saison = (Saison) o;
|
||||||
return Objects.equals(id, saison.id);
|
return Objects.equals(id, saison.id);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ import java.util.Map;
|
|||||||
@Service
|
@Service
|
||||||
public class CaptchaValidationService {
|
public class CaptchaValidationService {
|
||||||
|
|
||||||
@Value("${captcha.secret:dummy-secret}")
|
@Value("${captcha.secret:1x0000000000000000000000000000000AA}")
|
||||||
private String captchaSecret;
|
private String captchaSecret;
|
||||||
|
|
||||||
@Value("${captcha.url:https://challenges.cloudflare.com/turnstile/v0/siteverify}")
|
@Value("${captcha.url:https://challenges.cloudflare.com/turnstile/v0/siteverify}")
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE saison ADD COLUMN inscriptions_ouvertes BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
+1
@@ -28,6 +28,7 @@ public class AdminPreInscriptionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/valider")
|
@PostMapping("/{id}/valider")
|
||||||
|
@ResponseBody
|
||||||
public String valider(@PathVariable Long id, HttpServletResponse response) {
|
public String valider(@PathVariable Long id, HttpServletResponse response) {
|
||||||
Adherent adherent = preInscriptionService.validerPreInscription(id, null);
|
Adherent adherent = preInscriptionService.validerPreInscription(id, null);
|
||||||
response.setHeader("HX-Trigger", "refreshBadge");
|
response.setHeader("HX-Trigger", "refreshBadge");
|
||||||
|
|||||||
+19
-4
@@ -35,12 +35,21 @@ public class PublicInscriptionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/demarrer")
|
@GetMapping("/demarrer")
|
||||||
public String showDemarrer() {
|
public String showDemarrer(Model model) {
|
||||||
|
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
|
if (activeSaison == null || !activeSaison.getInscriptionsOuvertes()) {
|
||||||
|
model.addAttribute("inscriptionsFermees", true);
|
||||||
|
}
|
||||||
return "public/demarrer";
|
return "public/demarrer";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/demarrer")
|
@PostMapping("/demarrer")
|
||||||
public String processDemarrer(@RequestParam("cf-turnstile-response") String captchaResponse, RedirectAttributes redirectAttributes) {
|
public String processDemarrer(@RequestParam("cf-turnstile-response") String captchaResponse, RedirectAttributes redirectAttributes) {
|
||||||
|
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
|
if (activeSaison == null || !activeSaison.getInscriptionsOuvertes()) {
|
||||||
|
redirectAttributes.addFlashAttribute("error", "Les inscriptions ne sont pas ouvertes pour le moment, veuillez contacter un responsable du club.");
|
||||||
|
return "redirect:/inscription-public/demarrer";
|
||||||
|
}
|
||||||
// En vrai, captchaResponse dépend du fournisseur. Cloudflare Turnstile = cf-turnstile-response
|
// En vrai, captchaResponse dépend du fournisseur. Cloudflare Turnstile = cf-turnstile-response
|
||||||
if (!captchaValidationService.validateCaptcha(captchaResponse)) {
|
if (!captchaValidationService.validateCaptcha(captchaResponse)) {
|
||||||
redirectAttributes.addFlashAttribute("error", "Validation CAPTCHA échouée. Veuillez réessayer.");
|
redirectAttributes.addFlashAttribute("error", "Validation CAPTCHA échouée. Veuillez réessayer.");
|
||||||
@@ -56,7 +65,13 @@ public class PublicInscriptionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/formulaire")
|
@GetMapping("/formulaire")
|
||||||
public String showFormulaire(@RequestParam("token") String tokenUuid, Model model) {
|
public String showFormulaire(@RequestParam("token") String tokenUuid, Model model, RedirectAttributes redirectAttributes) {
|
||||||
|
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
|
if (activeSaison == null || !activeSaison.getInscriptionsOuvertes()) {
|
||||||
|
redirectAttributes.addFlashAttribute("error", "Les inscriptions ne sont pas ouvertes pour le moment, veuillez contacter un responsable du club.");
|
||||||
|
return "redirect:/inscription-public/demarrer";
|
||||||
|
}
|
||||||
|
|
||||||
TokenPreInscription token = tokenRepository.findByValeurUuid(tokenUuid).orElse(null);
|
TokenPreInscription token = tokenRepository.findByValeurUuid(tokenUuid).orElse(null);
|
||||||
if (token == null || !token.isValide()) {
|
if (token == null || !token.isValide()) {
|
||||||
return "public/lien-expire";
|
return "public/lien-expire";
|
||||||
@@ -77,8 +92,8 @@ public class PublicInscriptionController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
|
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
if (activeSaison == null) {
|
if (activeSaison == null || !activeSaison.getInscriptionsOuvertes()) {
|
||||||
redirectAttributes.addFlashAttribute("error", "Les inscriptions sont actuellement fermées.");
|
redirectAttributes.addFlashAttribute("error", "Les inscriptions ne sont pas ouvertes pour le moment, veuillez contacter un responsable du club.");
|
||||||
return "redirect:/inscription-public/demarrer";
|
return "redirect:/inscription-public/demarrer";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,4 +58,13 @@ public class SaisonController {
|
|||||||
|
|
||||||
return "redirect:/saisons";
|
return "redirect:/saisons";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/toggle-inscriptions")
|
||||||
|
public String toggleInscriptions(@PathVariable Long id) {
|
||||||
|
Saison saison = saisonRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Saison introuvable : " + id));
|
||||||
|
saison.setInscriptionsOuvertes(!saison.getInscriptionsOuvertes());
|
||||||
|
saisonRepository.save(saison);
|
||||||
|
return "redirect:/saisons";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -30,12 +30,13 @@
|
|||||||
<tr class="bg-gray-50 text-gray-500 text-sm uppercase tracking-wider border-b border-gray-200">
|
<tr class="bg-gray-50 text-gray-500 text-sm uppercase tracking-wider border-b border-gray-200">
|
||||||
<th class="py-3 px-6 font-medium text-left">Nom de la Saison</th>
|
<th class="py-3 px-6 font-medium text-left">Nom de la Saison</th>
|
||||||
<th class="py-3 px-6 font-medium text-center">Statut</th>
|
<th class="py-3 px-6 font-medium text-center">Statut</th>
|
||||||
|
<th class="py-3 px-6 font-medium text-center">Inscriptions Publiques</th>
|
||||||
<th class="py-3 px-6 font-medium text-right">Actions</th>
|
<th class="py-3 px-6 font-medium text-right">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200 text-sm">
|
<tbody class="divide-y divide-gray-200 text-sm">
|
||||||
<tr th:if="${#lists.isEmpty(saisons)}">
|
<tr th:if="${#lists.isEmpty(saisons)}">
|
||||||
<td colspan="3" class="py-8 text-center text-gray-500">Aucune saison configurée.</td>
|
<td colspan="4" class="py-8 text-center text-gray-500">Aucune saison configurée.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr th:each="saison : ${saisons}" class="hover:bg-gray-50 transition-colors">
|
<tr th:each="saison : ${saisons}" class="hover:bg-gray-50 transition-colors">
|
||||||
<td class="py-4 px-6 font-medium text-gray-900" th:text="${saison.nom}">Saison 2024-2025</td>
|
<td class="py-4 px-6 font-medium text-gray-900" th:text="${saison.nom}">Saison 2024-2025</td>
|
||||||
@@ -47,6 +48,14 @@
|
|||||||
Clôturée
|
Clôturée
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="py-4 px-6 text-center">
|
||||||
|
<form th:if="${saison.estActive}" th:action="@{/saisons/{id}/toggle-inscriptions(id=${saison.id})}" method="post">
|
||||||
|
<button type="submit" th:class="${saison.inscriptionsOuvertes ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'} + ' px-3 py-1 rounded-full text-xs font-medium hover:opacity-80 transition-opacity'" title="Cliquez pour ouvrir/fermer">
|
||||||
|
<span th:text="${saison.inscriptionsOuvertes ? 'Ouvertes' : 'Fermées'}"></span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<span th:unless="${saison.estActive}" class="text-gray-400 text-xs">-</span>
|
||||||
|
</td>
|
||||||
<td class="py-4 px-6 text-right flex justify-end space-x-3 items-center">
|
<td class="py-4 px-6 text-right flex justify-end space-x-3 items-center">
|
||||||
<form th:if="${!saison.estActive}" th:action="@{/saisons/{id}/activer(id=${saison.id})}" method="post">
|
<form th:if="${!saison.estActive}" th:action="@{/saisons/{id}/activer(id=${saison.id})}" method="post">
|
||||||
<button type="submit" class="text-indigo-600 hover:text-indigo-900 font-medium bg-indigo-50 px-3 py-1 rounded-lg">Définir Active</button>
|
<button type="submit" class="text-indigo-600 hover:text-indigo-900 font-medium bg-indigo-50 px-3 py-1 rounded-lg">Définir Active</button>
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
<script src="https://unpkg.com/htmx.org@1.9.11"></script>
|
<script src="https://unpkg.com/htmx.org@1.9.11"></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<style> body { font-family: 'Inter', sans-serif; } </style>
|
<style> body { font-family: 'Inter', sans-serif; } </style>
|
||||||
|
<meta name="_csrf" th:content="${_csrf.token}"/>
|
||||||
|
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50 text-gray-900 flex h-screen overflow-hidden">
|
<body class="bg-gray-50 text-gray-900 flex h-screen overflow-hidden">
|
||||||
<!-- Sidebar -->
|
<!-- Sidebar -->
|
||||||
@@ -44,12 +46,11 @@
|
|||||||
<div class="text-xs text-gray-500" th:text="${pre.email}"></div>
|
<div class="text-xs text-gray-500" th:text="${pre.email}"></div>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-4 px-6 text-right space-x-2">
|
<td class="py-4 px-6 text-right space-x-2">
|
||||||
<button th:hx-post="@{/admin/pre-inscriptions/{id}/valider(id=${pre.id})}"
|
<button th:attr="hx-post=@{/admin/pre-inscriptions/{id}/valider(id=${pre.id})}"
|
||||||
class="bg-blue-600 text-white px-3 py-1.5 rounded hover:bg-blue-700 transition-colors">
|
class="bg-blue-600 text-white px-3 py-1.5 rounded hover:bg-blue-700 transition-colors">
|
||||||
Créer la fiche
|
Créer la fiche
|
||||||
</button>
|
</button>
|
||||||
<button th:hx-post="@{/admin/pre-inscriptions/{id}/rejeter(id=${pre.id})}"
|
<button th:attr="hx-post=@{/admin/pre-inscriptions/{id}/rejeter(id=${pre.id})}, hx-target=|#pre-${pre.id}|"
|
||||||
th:hx-target="'#pre-' + ${pre.id}"
|
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
hx-confirm="Confirmer le rejet du dossier ?"
|
hx-confirm="Confirmer le rejet du dossier ?"
|
||||||
class="bg-red-50 text-red-600 border border-red-200 px-3 py-1.5 rounded hover:bg-red-100 transition-colors">
|
class="bg-red-50 text-red-600 border border-red-200 px-3 py-1.5 rounded hover:bg-red-100 transition-colors">
|
||||||
@@ -62,5 +63,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.body.addEventListener('htmx:configRequest', function(evt) {
|
||||||
|
evt.detail.headers[document.querySelector('meta[name="_csrf_header"]').content] =
|
||||||
|
document.querySelector('meta[name="_csrf"]').content;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,30 +1,42 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns:th="http://www.thymeleaf.org">
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>AS Talange - Pré-inscription</title>
|
<title>AS Talange - Pré-inscription</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
<style> body { font-family: 'Inter', sans-serif; } </style>
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-50 flex items-center justify-center min-h-screen">
|
<body class="bg-gray-50 flex items-center justify-center min-h-screen">
|
||||||
<div class="max-w-md w-full bg-white rounded-xl shadow-md p-8 border border-gray-100 text-center">
|
<div class="max-w-md w-full bg-white rounded-xl shadow-md p-8 border border-gray-100 text-center">
|
||||||
<img src="/images/logo.png" alt="AS Talange" class="h-24 mx-auto mb-6 opacity-80" onerror="this.style.display='none'">
|
<img src="/images/logo.png" alt="AS Talange" class="h-32 w-auto mx-auto mb-6 object-contain drop-shadow-sm"
|
||||||
|
onerror="this.style.display='none'">
|
||||||
<h1 class="text-2xl font-bold text-gray-900 mb-2">Inscription au club</h1>
|
<h1 class="text-2xl font-bold text-gray-900 mb-2">Inscription au club</h1>
|
||||||
<p class="text-gray-600 mb-8">Veuillez valider le captcha pour accéder au formulaire sécurisé.</p>
|
<p th:unless="${inscriptionsFermees}" class="text-gray-600 mb-8">Veuillez valider le captcha pour accéder au formulaire sécurisé.</p>
|
||||||
|
|
||||||
<div th:if="${error}" class="bg-red-50 text-red-600 p-3 rounded-lg mb-6 text-sm" th:text="${error}"></div>
|
<div th:if="${error != null and (inscriptionsFermees == null or !inscriptionsFermees)}" class="bg-red-50 text-red-600 p-3 rounded-lg mb-6 text-sm" th:text="${error}"></div>
|
||||||
<div th:if="${param.error}" class="bg-red-50 text-red-600 p-3 rounded-lg mb-6 text-sm">Les inscriptions sont actuellement fermées.</div>
|
|
||||||
|
|
||||||
<form th:action="@{/inscription-public/demarrer}" method="post" class="flex flex-col items-center">
|
<div th:if="${inscriptionsFermees}" class="bg-red-50 text-red-600 p-4 rounded-lg mb-6 text-sm font-medium">
|
||||||
|
Les inscriptions ne sont pas ouvertes pour le moment, veuillez contacter un responsable du club.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form th:unless="${inscriptionsFermees}" th:action="@{/inscription-public/demarrer}" method="post" class="flex flex-col items-center">
|
||||||
<!-- Remplacer par la vraie clé de site Cloudflare Turnstile -->
|
<!-- Remplacer par la vraie clé de site Cloudflare Turnstile -->
|
||||||
<div class="cf-turnstile mb-6" data-sitekey="1x00000000000000000000AA"></div>
|
<div class="cf-turnstile mb-6" data-sitekey="1x00000000000000000000AA"></div>
|
||||||
|
|
||||||
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors">
|
<button type="submit"
|
||||||
|
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors">
|
||||||
Accéder au formulaire
|
Accéder au formulaire
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50 flex items-center justify-center min-h-screen py-12">
|
<body class="bg-gray-50 flex items-center justify-center min-h-screen py-12">
|
||||||
<div class="max-w-xl w-full bg-white rounded-xl shadow-md p-8 border border-gray-100">
|
<div class="max-w-xl w-full bg-white rounded-xl shadow-md p-8 border border-gray-100">
|
||||||
<h2 class="text-2xl font-bold text-gray-900 mb-6 text-center">Formulaire de pré-inscription</h2>
|
<img src="/images/logo.png" alt="AS Talange" class="h-24 w-auto mx-auto mb-4 object-contain drop-shadow-sm" onerror="this.style.display='none'">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-2 text-center">Formulaire de pré-inscription</h2>
|
||||||
<p class="text-sm text-gray-500 mb-8 text-center">Veuillez remplir les informations concernant le futur licencié.</p>
|
<p class="text-sm text-gray-500 mb-8 text-center">Veuillez remplir les informations concernant le futur licencié.</p>
|
||||||
|
|
||||||
<form th:action="@{/inscription-public/formulaire}" th:object="${preInscription}" method="post" class="space-y-6">
|
<form th:action="@{/inscription-public/formulaire}" th:object="${preInscription}" method="post" class="space-y-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user