feat: display and manage adherent clothing and shoe sizes
This commit is contained in:
@@ -48,6 +48,8 @@ public class AdherentController {
|
||||
@org.springframework.web.bind.annotation.RequestParam(required = false) String licence,
|
||||
@org.springframework.web.bind.annotation.RequestParam(required = false) String email,
|
||||
@org.springframework.web.bind.annotation.RequestParam(required = false) String paiement,
|
||||
@org.springframework.web.bind.annotation.RequestParam(required = false, defaultValue = "nom") String sortField,
|
||||
@org.springframework.web.bind.annotation.RequestParam(required = false, defaultValue = "asc") String sortDirection,
|
||||
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "0") int page,
|
||||
@org.springframework.web.bind.annotation.RequestParam(defaultValue = "20") int size,
|
||||
Model model) {
|
||||
@@ -113,7 +115,29 @@ public class AdherentController {
|
||||
model.addAttribute("paiementFilter", paiement.trim());
|
||||
}
|
||||
|
||||
// 3. Paginate
|
||||
// 3. Sort
|
||||
java.util.Comparator<Adherent> comparator = (a1, a2) -> 0;
|
||||
if ("nom".equalsIgnoreCase(sortField)) {
|
||||
comparator = java.util.Comparator.comparing(a -> a.getNom() != null ? a.getNom() : "");
|
||||
} else if ("prenom".equalsIgnoreCase(sortField)) {
|
||||
comparator = java.util.Comparator.comparing(a -> a.getPrenom() != null ? a.getPrenom() : "");
|
||||
} else if ("categorie".equalsIgnoreCase(sortField)) {
|
||||
comparator = java.util.Comparator.comparing(a -> (a.getLicenceActuelle() != null && a.getLicenceActuelle().getCategorie() != null && a.getLicenceActuelle().getCategorie().getNom() != null) ? a.getLicenceActuelle().getCategorie().getNom() : "");
|
||||
} else if ("licence".equalsIgnoreCase(sortField)) {
|
||||
comparator = java.util.Comparator.comparing(a -> (a.getLicenceActuelle() != null && a.getLicenceActuelle().getNumeroLicence() != null) ? a.getLicenceActuelle().getNumeroLicence() : "");
|
||||
} else if ("email".equalsIgnoreCase(sortField)) {
|
||||
comparator = java.util.Comparator.comparing(a -> a.getEmail() != null ? a.getEmail() : "");
|
||||
}
|
||||
|
||||
if ("desc".equalsIgnoreCase(sortDirection)) {
|
||||
comparator = comparator.reversed();
|
||||
}
|
||||
adherents.sort(comparator);
|
||||
|
||||
model.addAttribute("sortField", sortField);
|
||||
model.addAttribute("sortDirection", sortDirection);
|
||||
|
||||
// 4. Paginate
|
||||
int totalElements = adherents.size();
|
||||
int totalPages = (int) Math.ceil((double) totalElements / size);
|
||||
if (page < 0) page = 0;
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<div class="mt-6" th:classappend="${adherent.typeDemande == 'RENOUVELLEMENT'} ? 'hidden-block' : ''">
|
||||
<label for="commentConnuClub" class="block text-sm font-medium text-gray-700 mb-1">Comment a-t-il connu le club ?</label>
|
||||
<input type="text" id="commentConnuClub" th:field="*{commentConnuClub}"
|
||||
class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors">
|
||||
@@ -413,26 +413,35 @@
|
||||
<div th:if="${dot.equipement.taillesDisponibles != null and !dot.equipement.taillesDisponibles.trim().isEmpty()}">
|
||||
<label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label>
|
||||
<select th:name="'taille_' + ${dot.id}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none">
|
||||
<option value="">Choisir...</option>
|
||||
<option value="" th:selected="${dot.taille == null or dot.taille.isEmpty()}">Choisir...</option>
|
||||
<option th:if="${dot.taille != null and !dot.taille.isEmpty()}"
|
||||
th:value="${dot.taille}"
|
||||
th:text="${dot.taille} + ' (Actuelle)'"
|
||||
selected></option>
|
||||
<option th:each="t : ${#strings.arraySplit(dot.equipement.taillesDisponibles, ',')}"
|
||||
th:if="${dot.taille == null or dot.taille != #strings.trim(t)}"
|
||||
th:value="${#strings.trim(t)}"
|
||||
th:text="${#strings.trim(t)}"
|
||||
th:selected="${(dot.taille != null and !dot.taille.isEmpty()) ? (dot.taille == #strings.trim(t)) : (#strings.trim(t) == adherent.tailleVetement or #strings.trim(t) == adherent.pointure)}"></option>
|
||||
th:selected="${dot.taille == null and (#strings.trim(t) == adherent.tailleVetement or #strings.trim(t) == adherent.pointure)}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div th:if="${(dot.equipement.taillesDisponibles == null or dot.equipement.taillesDisponibles.trim().isEmpty())}">
|
||||
<label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label>
|
||||
<input type="text" th:name="'taille_' + ${dot.id}" th:value="${dot.taille != null and !dot.taille.isEmpty() ? dot.taille : (adherent.tailleVetement != null ? adherent.tailleVetement : '')}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 outline-none bg-gray-50 text-gray-500" placeholder="Taille/Pointure">
|
||||
<input type="text" th:name="'taille_' + ${dot.id}" th:value="${dot.taille != null and !dot.taille.isEmpty() ? dot.taille : (adherent.tailleVetement != null ? adherent.tailleVetement : '')}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none" placeholder="Taille/Pointure">
|
||||
</div>
|
||||
|
||||
<div th:if="${dot.equipement.couleursDisponibles != null and !dot.equipement.couleursDisponibles.trim().isEmpty()}">
|
||||
<label class="block text-[9px] uppercase font-bold text-gray-400">Couleur</label>
|
||||
<select th:name="'couleur_' + ${dot.id}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none">
|
||||
<option value="">Choisir...</option>
|
||||
<option value="" th:selected="${dot.couleur == null or dot.couleur.isEmpty()}">Choisir...</option>
|
||||
<option th:if="${dot.couleur != null and !dot.couleur.isEmpty()}"
|
||||
th:value="${dot.couleur}"
|
||||
th:text="${dot.couleur} + ' (Actuelle)'"
|
||||
selected></option>
|
||||
<option th:each="c : ${#strings.arraySplit(dot.equipement.couleursDisponibles, ',')}"
|
||||
th:if="${dot.couleur == null or dot.couleur != #strings.trim(c)}"
|
||||
th:value="${#strings.trim(c)}"
|
||||
th:text="${#strings.trim(c)}"
|
||||
th:selected="${dot.couleur == #strings.trim(c)}"></option>
|
||||
th:text="${#strings.trim(c)}"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div th:if="${(dot.equipement.couleursDisponibles == null or dot.equipement.couleursDisponibles.trim().isEmpty()) and (dot.couleur != null and !dot.couleur.isEmpty())}">
|
||||
@@ -442,16 +451,16 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-1.5 mb-1.5">
|
||||
<div>
|
||||
<div th:if="${dot.equipement.hasFlocageNumero}">
|
||||
<label class="block text-[9px] uppercase font-bold text-gray-400">Numéro</label>
|
||||
<input type="text" th:name="'numero_' + ${dot.id}" th:value="${dot.numero}" placeholder="Ex: 10"
|
||||
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" th:name="'flocage_' + ${dot.id}" th:value="${dot.flocage}" placeholder="Nom du joueur"
|
||||
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 th:if="${dot.equipement.hasFlocagePrenom or dot.equipement.hasFlocageInitiales}">
|
||||
<label class="block text-[9px] uppercase font-bold text-gray-400 whitespace-nowrap truncate" title="Flocage" th:text="'Flocage (' + (${dot.equipement.hasFlocagePrenom and dot.equipement.hasFlocageInitiales} ? 'Prénom/Init.' : (${dot.equipement.hasFlocagePrenom} ? 'Prénom' : 'Initiales')) + ')'">Flocage</label>
|
||||
<input type="text" th:name="'flocage_' + ${dot.id}" th:value="${dot.flocage}" placeholder="À floquer"
|
||||
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 class="flex items-center pt-1.5">
|
||||
<label class="flex items-center space-x-1 cursor-pointer select-none">
|
||||
<input type="checkbox" th:name="'fourni_' + ${dot.id}" th:checked="${dot.fourni}" class="w-3.5 h-3.5 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
|
||||
|
||||
@@ -58,16 +58,63 @@
|
||||
<table class="w-full text-left border-collapse min-w-[1000px]">
|
||||
<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</th>
|
||||
<th class="py-3 px-6 font-medium text-left">Prénom</th>
|
||||
<th class="py-3 px-6 font-medium text-left">Catégorie</th>
|
||||
<th class="py-3 px-6 font-medium text-center">N° Licence</th>
|
||||
<th class="py-3 px-6 font-medium text-left">Email</th>
|
||||
<th class="py-3 px-6 font-medium text-left cursor-pointer hover:bg-gray-100 transition-colors group select-none" onclick="updateSort('nom')">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span>Nom</span>
|
||||
<span th:if="${sortField != 'nom'}"><svg class="w-3.5 h-3.5 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4"></path></svg></span>
|
||||
<span th:if="${sortField == 'nom'}">
|
||||
<svg th:if="${sortDirection == 'asc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path></svg>
|
||||
<svg th:if="${sortDirection == 'desc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 px-6 font-medium text-left cursor-pointer hover:bg-gray-100 transition-colors group select-none" onclick="updateSort('prenom')">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span>Prénom</span>
|
||||
<span th:if="${sortField != 'prenom'}"><svg class="w-3.5 h-3.5 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4"></path></svg></span>
|
||||
<span th:if="${sortField == 'prenom'}">
|
||||
<svg th:if="${sortDirection == 'asc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path></svg>
|
||||
<svg th:if="${sortDirection == 'desc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 px-6 font-medium text-left cursor-pointer hover:bg-gray-100 transition-colors group select-none" onclick="updateSort('categorie')">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span>Catégorie</span>
|
||||
<span th:if="${sortField != 'categorie'}"><svg class="w-3.5 h-3.5 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4"></path></svg></span>
|
||||
<span th:if="${sortField == 'categorie'}">
|
||||
<svg th:if="${sortDirection == 'asc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path></svg>
|
||||
<svg th:if="${sortDirection == 'desc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 px-6 font-medium text-center cursor-pointer hover:bg-gray-100 transition-colors group select-none" onclick="updateSort('licence')">
|
||||
<div class="flex items-center justify-center space-x-1">
|
||||
<span>N° Licence</span>
|
||||
<span th:if="${sortField != 'licence'}"><svg class="w-3.5 h-3.5 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4"></path></svg></span>
|
||||
<span th:if="${sortField == 'licence'}">
|
||||
<svg th:if="${sortDirection == 'asc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path></svg>
|
||||
<svg th:if="${sortDirection == 'desc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 px-6 font-medium text-left cursor-pointer hover:bg-gray-100 transition-colors group select-none" onclick="updateSort('email')">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span>Email</span>
|
||||
<span th:if="${sortField != 'email'}"><svg class="w-3.5 h-3.5 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4"></path></svg></span>
|
||||
<span th:if="${sortField == 'email'}">
|
||||
<svg th:if="${sortDirection == 'asc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"></path></svg>
|
||||
<svg th:if="${sortDirection == 'desc'}" class="w-3.5 h-3.5 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
||||
</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="py-3 px-6 font-medium text-center">Paiement</th>
|
||||
<th class="py-3 px-6 font-medium text-right">Actions</th>
|
||||
</tr>
|
||||
<!-- Filter Row -->
|
||||
<tr id="filter-row" class="bg-gray-100 border-b border-gray-200 text-xs">
|
||||
<input type="hidden" name="sortField" id="sortField" th:value="${sortField}">
|
||||
<input type="hidden" name="sortDirection" id="sortDirection" th:value="${sortDirection}">
|
||||
<td class="py-2 px-3">
|
||||
<input type="text" id="filterNom" name="nom" th:value="${nomFilter}" placeholder="Filtrer Nom..." class="w-full border border-gray-300 rounded px-2 py-1 text-xs focus:ring-1 focus:ring-blue-500 focus:outline-none">
|
||||
</td>
|
||||
@@ -217,5 +264,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
function updateSort(field) {
|
||||
let sf = document.getElementById('sortField');
|
||||
let sd = document.getElementById('sortDirection');
|
||||
if (sf.value === field) {
|
||||
sd.value = sd.value === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
sf.value = field;
|
||||
sd.value = 'asc';
|
||||
}
|
||||
sf.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user