feat: gestion des dotations equipements et historique des paiements

This commit is contained in:
2026-05-19 22:15:33 +02:00
parent ee85d38c43
commit 5e0b9ab02a
15 changed files with 435 additions and 7 deletions
@@ -33,6 +33,9 @@ public class Licence {
@OneToMany(mappedBy = "licence", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(mappedBy = "licence", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Paiement> paiements = new ArrayList<>(); private List<Paiement> paiements = new ArrayList<>();
@OneToMany(mappedBy = "licence", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Dotation> dotations = new ArrayList<>();
// Logic for Reste A Payer // Logic for Reste A Payer
@Transient @Transient
public BigDecimal getResteAPayer() { public BigDecimal getResteAPayer() {
@@ -76,6 +79,9 @@ public class Licence {
public List<Paiement> getPaiements() { return paiements; } public List<Paiement> getPaiements() { return paiements; }
public void setPaiements(List<Paiement> paiements) { this.paiements = paiements; } public void setPaiements(List<Paiement> paiements) { this.paiements = paiements; }
public List<Dotation> getDotations() { return dotations; }
public void setDotations(List<Dotation> dotations) { this.dotations = dotations; }
public String getNumeroLicence() { return numeroLicence; } public String getNumeroLicence() { return numeroLicence; }
public void setNumeroLicence(String numeroLicence) { this.numeroLicence = numeroLicence; } public void setNumeroLicence(String numeroLicence) { this.numeroLicence = numeroLicence; }
@@ -88,4 +94,14 @@ public class Licence {
paiements.remove(paiement); paiements.remove(paiement);
paiement.setLicence(null); paiement.setLicence(null);
} }
public void addDotation(Dotation dotation) {
dotations.add(dotation);
dotation.setLicence(this);
}
public void removeDotation(Dotation dotation) {
dotations.remove(dotation);
dotation.setLicence(null);
}
} }
@@ -0,0 +1,38 @@
package com.astalange.web.controller;
import com.astalange.core.entity.Dotation;
import com.astalange.core.repository.DotationRepository;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class DotationController {
private final DotationRepository dotationRepository;
public DotationController(DotationRepository dotationRepository) {
this.dotationRepository = dotationRepository;
}
@PostMapping("/dotations/{id}")
public String updateDotation(
@PathVariable Long id,
@RequestParam Long adherentId,
@RequestParam(required = false, defaultValue = "") String taille,
@RequestParam(required = false, defaultValue = "") String flocage,
@RequestParam(required = false) Boolean fourni) {
Dotation dotation = dotationRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid Dotation ID: " + id));
dotation.setTaille(taille);
dotation.setFlocage(flocage);
dotation.setFourni(fourni != null && fourni);
dotationRepository.save(dotation);
return "redirect:/adherents/" + adherentId + "/edit";
}
}
@@ -0,0 +1,55 @@
package com.astalange.web.controller;
import com.astalange.core.entity.Equipement;
import com.astalange.core.repository.EquipementRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/equipements")
public class EquipementController {
private final EquipementRepository equipementRepository;
public EquipementController(EquipementRepository equipementRepository) {
this.equipementRepository = equipementRepository;
}
@GetMapping
public String listEquipements(Model model) {
model.addAttribute("equipements", equipementRepository.findAll());
return "parametrage/equipements_list";
}
@GetMapping("/new")
public String showCreateForm(Model model) {
model.addAttribute("equipement", new Equipement());
return "parametrage/equipements_form";
}
@GetMapping("/{id}/edit")
public String showEditForm(@PathVariable Long id, Model model) {
Equipement equipement = equipementRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + id));
model.addAttribute("equipement", equipement);
return "parametrage/equipements_form";
}
@PostMapping
public String saveEquipement(@ModelAttribute("equipement") Equipement equipement) {
if (equipement.getObligatoire() == null) {
equipement.setObligatoire(false);
}
equipementRepository.save(equipement);
return "redirect:/equipements";
}
@PostMapping("/{id}/delete")
public String deleteEquipement(@PathVariable Long id) {
Equipement equipement = equipementRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + id));
equipementRepository.delete(equipement);
return "redirect:/equipements";
}
}
@@ -3,25 +3,39 @@ package com.astalange.web.controller;
import com.astalange.core.entity.Adherent; import com.astalange.core.entity.Adherent;
import com.astalange.core.entity.Categorie; import com.astalange.core.entity.Categorie;
import com.astalange.core.entity.Licence; import com.astalange.core.entity.Licence;
import com.astalange.core.entity.Dotation;
import com.astalange.core.entity.Equipement;
import com.astalange.core.repository.AdherentRepository; import com.astalange.core.repository.AdherentRepository;
import com.astalange.core.repository.CategorieRepository; import com.astalange.core.repository.CategorieRepository;
import com.astalange.core.repository.LicenceRepository; import com.astalange.core.repository.LicenceRepository;
import com.astalange.core.repository.DotationRepository;
import com.astalange.core.repository.EquipementRepository;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller @Controller
public class LicenceController { public class LicenceController {
private final AdherentRepository adherentRepository; private final AdherentRepository adherentRepository;
private final CategorieRepository categorieRepository; private final CategorieRepository categorieRepository;
private final LicenceRepository licenceRepository; private final LicenceRepository licenceRepository;
private final EquipementRepository equipementRepository;
private final DotationRepository dotationRepository;
public LicenceController(AdherentRepository adherentRepository, CategorieRepository categorieRepository, LicenceRepository licenceRepository) { public LicenceController(AdherentRepository adherentRepository,
CategorieRepository categorieRepository,
LicenceRepository licenceRepository,
EquipementRepository equipementRepository,
DotationRepository dotationRepository) {
this.adherentRepository = adherentRepository; this.adherentRepository = adherentRepository;
this.categorieRepository = categorieRepository; this.categorieRepository = categorieRepository;
this.licenceRepository = licenceRepository; this.licenceRepository = licenceRepository;
this.equipementRepository = equipementRepository;
this.dotationRepository = dotationRepository;
} }
@PostMapping("/adherents/{id}/licences") @PostMapping("/adherents/{id}/licences")
@@ -29,7 +43,8 @@ public class LicenceController {
@PathVariable Long id, @PathVariable Long id,
@RequestParam Long categorieId, @RequestParam Long categorieId,
@RequestParam String saison, @RequestParam String saison,
@RequestParam String etat) { @RequestParam String etat,
@RequestParam(required = false) String numeroLicence) {
Adherent adherent = adherentRepository.findById(id) Adherent adherent = adherentRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid Adherent ID")); .orElseThrow(() -> new IllegalArgumentException("Invalid Adherent ID"));
@@ -41,8 +56,21 @@ public class LicenceController {
licence.setCategorie(categorie); licence.setCategorie(categorie);
licence.setSaison(saison); licence.setSaison(saison);
licence.setEtat(etat); licence.setEtat(etat);
licence.setNumeroLicence(numeroLicence);
licenceRepository.save(licence); licence = licenceRepository.save(licence);
// Auto-initialize dotations for all configured equipments
List<Equipement> equipements = equipementRepository.findAll();
for (Equipement eq : equipements) {
Dotation dotation = new Dotation();
dotation.setLicence(licence);
dotation.setEquipement(eq);
dotation.setTaille("");
dotation.setFlocage("");
dotation.setFourni(false);
dotationRepository.save(dotation);
}
return "redirect:/adherents/" + id + "/edit"; return "redirect:/adherents/" + id + "/edit";
} }
@@ -0,0 +1,23 @@
package com.astalange.web.controller;
import com.astalange.core.repository.PaiementRepository;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PaiementsController {
private final PaiementRepository paiementRepository;
public PaiementsController(PaiementRepository paiementRepository) {
this.paiementRepository = paiementRepository;
}
@GetMapping("/paiements")
public String listPaiements(Model model) {
model.addAttribute("paiements", paiementRepository.findAll(Sort.by(Sort.Direction.DESC, "datePaiement", "id")));
return "paiements/list";
}
}
@@ -21,10 +21,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Adhérents</a>
<a href="#" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a> <a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Équipements</a>
</nav> </nav>
<div class="p-4 border-t border-gray-200"> <div class="p-4 border-t border-gray-200">
<div class="text-sm font-medium text-gray-900 mb-1" th:text="${username != null ? username : 'Admin'}">Admin</div> <div class="text-sm font-medium text-gray-900 mb-1" th:text="${username != null ? username : 'Admin'}">Admin</div>
@@ -180,7 +181,7 @@
</td> </td>
</tr> </tr>
<tr th:if="${!#lists.isEmpty(lic.paiements)}" class="bg-gray-50/50"> <tr th:if="${!#lists.isEmpty(lic.paiements)}" class="bg-gray-50/50">
<td colspan="5" class="py-2 px-4"> <td colspan="6" class="py-2 px-4">
<div class="text-xs text-gray-500 mb-1 font-medium uppercase tracking-wider">Historique des paiements</div> <div class="text-xs text-gray-500 mb-1 font-medium uppercase tracking-wider">Historique des paiements</div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<span th:each="paiement : ${lic.paiements}" class="inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-medium bg-white border border-gray-200 text-gray-700"> <span th:each="paiement : ${lic.paiements}" class="inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-medium bg-white border border-gray-200 text-gray-700">
@@ -193,6 +194,51 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr class="bg-gray-50/30">
<td colspan="6" class="py-3 px-4 border-t border-gray-100">
<div class="text-xs text-gray-500 font-medium uppercase tracking-wider mb-2">Dotations Équipements</div>
<div th:if="${#lists.isEmpty(lic.dotations)}" class="text-xs text-gray-500 italic">Aucune dotation pour cette licence.</div>
<div th:if="${!#lists.isEmpty(lic.dotations)}" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
<div th:each="dot : ${lic.dotations}" class="bg-white p-3 rounded-lg border border-gray-200 shadow-sm flex flex-col justify-between">
<div>
<div class="flex justify-between items-start mb-1 gap-1">
<span class="font-semibold text-xs text-gray-800 truncate" th:text="${dot.equipement.nom}">Maillot</span>
<span class="text-[9px] px-1.5 py-0.5 rounded-full font-bold flex-shrink-0"
th:classappend="${dot.equipement.obligatoire ? 'bg-red-50 text-red-700 border border-red-100' : 'bg-gray-50 text-gray-600 border border-gray-100'}"
th:text="${dot.equipement.obligatoire ? 'Obligatoire' : 'Facultatif'}">Obligatoire</span>
</div>
<div class="text-[10px] text-gray-400 mb-2 truncate" th:title="${dot.equipement.description}" th:text="${dot.equipement.description != null and !dot.equipement.description.isEmpty() ? dot.equipement.description : 'Pas de description'}">Description</div>
</div>
<!-- Formulaire pour mettre à jour la Dotation -->
<form th:action="@{/dotations/{id}(id=${dot.id})}" method="post" class="space-y-2 pt-2 border-t border-gray-100">
<input type="hidden" name="adherentId" th:value="${adherent.id}">
<div class="grid grid-cols-2 gap-1.5">
<div>
<label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label>
<input type="text" name="taille" th:value="${dot.taille}" placeholder="S, M, L..."
class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none">
</div>
<div>
<label class="block text-[9px] uppercase font-bold text-gray-400">Flocage</label>
<input type="text" name="flocage" th:value="${dot.flocage}" placeholder="N°/Nom"
class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none">
</div>
</div>
<div class="flex justify-between items-center pt-1.5">
<label class="flex items-center space-x-1 cursor-pointer select-none">
<input type="checkbox" name="fourni" th:checked="${dot.fourni}" class="w-3.5 h-3.5 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<span class="text-[11px] text-gray-700">Fourni</span>
</label>
<button type="submit" class="bg-blue-50 text-blue-600 hover:bg-blue-100 text-[10px] px-2 py-0.5 rounded font-bold transition-colors">
Sauver
</button>
</div>
</form>
</div>
</div>
</td>
</tr>
</th:block> </th:block>
</tbody> </tbody>
</table> </table>
@@ -20,10 +20,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Adhérents</a>
<a href="#" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a> <a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Équipements</a>
</nav> </nav>
<div class="p-4 border-t border-gray-200"> <div class="p-4 border-t border-gray-200">
<div class="text-sm font-medium text-gray-900 mb-1" th:text="${username != null ? username : 'Admin'}">Admin</div> <div class="text-sm font-medium text-gray-900 mb-1" th:text="${username != null ? username : 'Admin'}">Admin</div>
@@ -20,10 +20,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Adhérents</a>
<a href="#" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a> <a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors">Équipements</a>
</nav> </nav>
<div class="p-4 border-t border-gray-200"> <div class="p-4 border-t border-gray-200">
<div class="text-sm font-medium text-gray-900 mb-1" th:text="${username}">Admin</div> <div class="text-sm font-medium text-gray-900 mb-1" th:text="${username}">Admin</div>
@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Paiements - AS Talange</title>
<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">
<style> body { font-family: 'Inter', sans-serif; } </style>
</head>
<body class="bg-gray-50 text-gray-900 flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside class="w-64 bg-white border-r border-gray-200 flex flex-col hidden md:flex">
<div class="h-16 flex items-center px-6 border-b border-gray-200">
<h1 class="text-xl font-bold text-blue-600">AS Talange</h1>
</div>
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Équipements</a>
</nav>
</aside>
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<header class="h-16 bg-white border-b border-gray-200 flex items-center px-6">
<h2 class="text-lg font-semibold text-gray-800">Historique des Paiements</h2>
</header>
<div class="flex-1 overflow-auto p-6">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold text-gray-900">Transactions Récentes</h3>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-left border-collapse">
<thead>
<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">Date</th>
<th class="py-3 px-6 font-medium text-left">Adhérent</th>
<th class="py-3 px-6 font-medium text-left">Catégorie</th>
<th class="py-3 px-6 font-medium text-center">Saison</th>
<th class="py-3 px-6 font-medium text-left">Mode de paiement</th>
<th class="py-3 px-6 font-medium text-right">Montant</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 text-sm">
<tr th:if="${#lists.isEmpty(paiements)}">
<td colspan="6" class="py-8 text-center text-gray-500">Aucun paiement enregistré pour le moment.</td>
</tr>
<tr th:each="p : ${paiements}" class="hover:bg-gray-50 transition-colors">
<td class="py-4 px-6 text-gray-600" th:text="${#temporals.format(p.datePaiement, 'dd/MM/yyyy')}">15/05/2026</td>
<td class="py-4 px-6 font-medium text-gray-900">
<a th:href="@{/adherents/{id}/edit(id=${p.licence.adherent.id})}"
class="text-blue-600 hover:text-blue-800 hover:underline"
th:text="${p.licence.adherent.prenom + ' ' + p.licence.adherent.nom}">Jean Dupont</a>
</td>
<td class="py-4 px-6 text-gray-600" th:text="${p.licence.categorie.nom}">U13</td>
<td class="py-4 px-6 text-center text-gray-600" th:text="${p.licence.saison}">2024-2025</td>
<td class="py-4 px-6 text-gray-600">
<span class="px-2 py-0.5 rounded border border-gray-200 bg-gray-50 text-gray-700 text-xs"
th:text="${p.modePaiement.nom}">Chèque</span>
</td>
<td class="py-4 px-6 text-right font-semibold text-green-600" th:text="${p.montant + ' €'}">50.00 €</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</body>
</html>
@@ -16,9 +16,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Équipements</a>
</nav> </nav>
</aside> </aside>
@@ -16,9 +16,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Équipements</a>
</nav> </nav>
</aside> </aside>
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Équipement - AS Talange</title>
<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">
<style> body { font-family: 'Inter', sans-serif; } </style>
</head>
<body class="bg-gray-50 text-gray-900 flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside class="w-64 bg-white border-r border-gray-200 flex flex-col hidden md:flex">
<div class="h-16 flex items-center px-6 border-b border-gray-200">
<h1 class="text-xl font-bold text-blue-600">AS Talange</h1>
</div>
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Équipements</a>
</nav>
</aside>
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<header class="h-16 bg-white border-b border-gray-200 flex items-center px-6">
<h2 class="text-lg font-semibold text-gray-800" th:text="${equipement.id == null ? 'Nouvel Équipement' : 'Modifier Équipement'}">Équipement</h2>
</header>
<div class="flex-1 overflow-auto p-6">
<div class="max-w-xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-8">
<form th:action="@{/equipements}" th:object="${equipement}" method="post" class="space-y-6">
<input type="hidden" th:field="*{id}" />
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Nom de l'équipement (ex: Maillot)</label>
<input type="text" th:field="*{nom}" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Description (ex: Maillot domicile officiel)</label>
<textarea th:field="*{description}" rows="3" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea>
</div>
<div class="flex items-center space-x-3">
<input type="checkbox" id="obligatoire" th:field="*{obligatoire}" class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
<label for="obligatoire" class="text-sm font-medium text-gray-700">
Cet équipement est obligatoire pour tous les licenciés (inclus dans la licence)
</label>
</div>
<div class="pt-4 flex justify-end space-x-3 border-t border-gray-100">
<a th:href="@{/equipements}" class="px-4 py-2 text-sm font-medium text-gray-700 border border-gray-300 rounded-lg hover:bg-gray-50">Annuler</a>
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">Enregistrer</button>
</div>
</form>
</div>
</div>
</main>
</body>
</html>
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Paramétrage - Équipements</title>
<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">
<style> body { font-family: 'Inter', sans-serif; } </style>
</head>
<body class="bg-gray-50 text-gray-900 flex h-screen overflow-hidden">
<!-- Sidebar -->
<aside class="w-64 bg-white border-r border-gray-200 flex flex-col hidden md:flex">
<div class="h-16 flex items-center px-6 border-b border-gray-200">
<h1 class="text-xl font-bold text-blue-600">AS Talange</h1>
</div>
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Équipements</a>
</nav>
</aside>
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<header class="h-16 bg-white border-b border-gray-200 flex items-center px-6">
<h2 class="text-lg font-semibold text-gray-800">Équipements du Club</h2>
</header>
<div class="flex-1 overflow-auto p-6">
<div class="flex justify-between items-center mb-6">
<h3 class="text-xl font-bold text-gray-900">Liste des Équipements</h3>
<a th:href="@{/equipements/new}" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700">
+ Nouvel Équipement
</a>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<table class="w-full text-left border-collapse">
<thead>
<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 l'équipement</th>
<th class="py-3 px-6 font-medium text-left">Description</th>
<th class="py-3 px-6 font-medium text-center">Obligatoire</th>
<th class="py-3 px-6 font-medium text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 text-sm">
<tr th:if="${#lists.isEmpty(equipements)}">
<td colspan="4" class="py-8 text-center text-gray-500">Aucun équipement n'est paramétré.</td>
</tr>
<tr th:each="eq : ${equipements}" class="hover:bg-gray-50 transition-colors">
<td class="py-4 px-6 font-medium text-gray-900" th:text="${eq.nom}">Maillot</td>
<td class="py-4 px-6 text-gray-600" th:text="${eq.description != null ? eq.description : '-'}">Maillot de match officiel</td>
<td class="py-4 px-6 text-center">
<span class="px-2.5 py-1 text-xs font-semibold rounded-full"
th:classappend="${eq.obligatoire ? 'bg-red-100 text-red-800' : 'bg-gray-100 text-gray-800'}"
th:text="${eq.obligatoire ? 'Oui' : 'Non'}">Oui</span>
</td>
<td class="py-4 px-6 text-right flex justify-end space-x-3 items-center">
<a th:href="@{/equipements/{id}/edit(id=${eq.id})}" class="text-indigo-600 hover:text-indigo-900 font-medium bg-indigo-50 px-3 py-1 rounded-lg">Modifier</a>
<form th:action="@{/equipements/{id}/delete(id=${eq.id})}" method="post" onsubmit="return confirm('Supprimer cet équipement ? Cela supprimera également les dotations associées de toutes les licences.');">
<button type="submit" class="text-red-600 hover:text-red-800 font-medium">Supprimer</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</body>
</html>
@@ -16,9 +16,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Équipements</a>
</nav> </nav>
</aside> </aside>
@@ -16,9 +16,11 @@
<nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1"> <nav class="flex-1 overflow-y-auto py-4 px-3 space-y-1">
<a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a> <a href="/" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Tableau de bord</a>
<a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a> <a href="/adherents" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Adhérents</a>
<a href="/paiements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Paiements</a>
<div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div> <div class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider">Paramétrage</div>
<a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a> <a href="/categories" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Catégories</a>
<a href="/modespaiement" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Modes de Paiement</a> <a href="/modespaiement" class="block px-3 py-2 rounded-lg bg-blue-50 text-blue-700 font-medium">Modes de Paiement</a>
<a href="/equipements" class="block px-3 py-2 rounded-lg text-gray-600 hover:bg-gray-50 transition-colors">Équipements</a>
</nav> </nav>
</aside> </aside>