package com.astalange.web.controller; import com.astalange.core.entity.*; import com.astalange.core.repository.CategorieRepository; import com.astalange.core.repository.CreneauEntrainementRepository; import com.astalange.core.repository.EducateurRepository; import com.astalange.core.repository.EquipeRepository; import com.astalange.core.repository.SaisonRepository; import com.astalange.core.repository.TerrainRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import java.util.*; @Controller @RequestMapping("/planning") public class PlanningController { private final CreneauEntrainementRepository creneauRepository; private final TerrainRepository terrainRepository; private final CategorieRepository categorieRepository; private final SaisonRepository saisonRepository; private final EquipeRepository equipeRepository; private final EducateurRepository educateurRepository; public PlanningController(CreneauEntrainementRepository creneauRepository, TerrainRepository terrainRepository, CategorieRepository categorieRepository, SaisonRepository saisonRepository, EquipeRepository equipeRepository, EducateurRepository educateurRepository) { this.creneauRepository = creneauRepository; this.terrainRepository = terrainRepository; this.categorieRepository = categorieRepository; this.saisonRepository = saisonRepository; this.equipeRepository = equipeRepository; this.educateurRepository = educateurRepository; } @GetMapping public String showPlanning(@RequestParam(required = false) JourSemaine jour, Model model) { Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null); if (saisonActive == null) { model.addAttribute("noActiveSeason", true); return "planning/planning"; } JourSemaine selectedDay = (jour != null) ? jour : JourSemaine.LUNDI; List terrains = terrainRepository.findAll(); // Sort terrains to ensure consistent order (Terrain Vert then Terrain Synthétique) terrains.sort(Comparator.comparing(Terrain::getId)); Map terrainIndices = new HashMap<>(); for (int i = 0; i < terrains.size(); i++) { terrainIndices.put(terrains.get(i).getId(), i); } List creneaux = creneauRepository.findBySaisonAndJourSemaineFetch(saisonActive, selectedDay); List educateurs = educateurRepository.findAllWithCategorieAndEquipe(); // Detect conflicts and populate educator initials for (int i = 0; i < creneaux.size(); i++) { CreneauEntrainement c1 = creneaux.get(i); // Populate educator initials List initials = new ArrayList<>(); for (Educateur ed : educateurs) { boolean match = false; if (c1.getEquipe() != null) { if (ed.getEquipe() != null && ed.getEquipe().getId().equals(c1.getEquipe().getId())) { match = true; } } else if (c1.getCategorie() != null) { if (ed.getCategorie() != null && ed.getCategorie().getId().equals(c1.getCategorie().getId())) { match = true; } else if (ed.getEquipe() != null && ed.getEquipe().getCategorie() != null && ed.getEquipe().getCategorie().getId().equals(c1.getCategorie().getId())) { match = true; } } if (match) { initials.add(ed.getInitiales()); } } c1.setInitialesEducateurs(String.join(", ", initials)); for (int j = 0; j < creneaux.size(); j++) { if (i == j) continue; CreneauEntrainement c2 = creneaux.get(j); if (c1.overlapsWith(c2)) { c1.setEnConflit(true); c1.getCategoriesEnConflit().add(c2.getCategorie().getNom()); } } } model.addAttribute("saisonActive", saisonActive); model.addAttribute("selectedDay", selectedDay); model.addAttribute("jours", JourSemaine.values()); model.addAttribute("terrains", terrains); model.addAttribute("terrainIndices", terrainIndices); model.addAttribute("creneaux", creneaux); return "planning/planning"; } @GetMapping("/new") public String showCreateForm(Model model, RedirectAttributes redirectAttributes) { Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null); if (saisonActive == null) { redirectAttributes.addFlashAttribute("error", "Veuillez activer une saison avant de planifier un créneau."); return "redirect:/saisons"; } CreneauEntrainement creneau = new CreneauEntrainement(); creneau.setSaison(saisonActive); model.addAttribute("creneau", creneau); model.addAttribute("categories", categorieRepository.findBySaison(saisonActive)); model.addAttribute("terrains", terrainRepository.findAll()); model.addAttribute("zones", ZoneTerrain.values()); model.addAttribute("jours", JourSemaine.values()); model.addAttribute("equipes", List.of()); return "planning/form"; } @GetMapping("/{id}/edit") public String showEditForm(@PathVariable Long id, Model model, RedirectAttributes redirectAttributes) { CreneauEntrainement creneau = creneauRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Créneau introuvable : " + id)); model.addAttribute("creneau", creneau); model.addAttribute("categories", categorieRepository.findBySaison(creneau.getSaison())); model.addAttribute("terrains", terrainRepository.findAll()); model.addAttribute("zones", ZoneTerrain.values()); model.addAttribute("jours", JourSemaine.values()); if (creneau.getCategorie() != null) { model.addAttribute("equipes", equipeRepository.findByCategorieId(creneau.getCategorie().getId())); } else { model.addAttribute("equipes", List.of()); } return "planning/form"; } @PostMapping public String saveCreneau(@ModelAttribute("creneau") CreneauEntrainement creneau, BindingResult result, Model model, RedirectAttributes redirectAttributes) { // Validation if (creneau.getHeureDebut() != null && creneau.getHeureFin() != null) { if (!creneau.getHeureDebut().isBefore(creneau.getHeureFin())) { result.rejectValue("heureDebut", "error.heureDebut", "L'heure de début doit être antérieure à l'heure de fin."); } if (creneau.getHeureDebut().isBefore(java.time.LocalTime.of(8, 0)) || creneau.getHeureFin().isAfter(java.time.LocalTime.of(22, 0))) { result.rejectValue("heureDebut", "error.heureDebut", "Les entraînements doivent se dérouler entre 08:00 et 22:00."); } } if (creneau.getEquipe() != null && creneau.getEquipe().getId() == null) { creneau.setEquipe(null); } if (result.hasErrors()) { Saison saison = creneau.getSaison(); if (saison == null) { saison = saisonRepository.findByEstActiveTrue().orElseThrow(); creneau.setSaison(saison); } model.addAttribute("categories", categorieRepository.findBySaison(saison)); model.addAttribute("terrains", terrainRepository.findAll()); model.addAttribute("zones", ZoneTerrain.values()); model.addAttribute("jours", JourSemaine.values()); if (creneau.getCategorie() != null && creneau.getCategorie().getId() != null) { model.addAttribute("equipes", equipeRepository.findByCategorieId(creneau.getCategorie().getId())); } else { model.addAttribute("equipes", List.of()); } return "planning/form"; } // Set active season if not set (for new items) if (creneau.getSaison() == null) { Saison saisonActive = saisonRepository.findByEstActiveTrue().orElseThrow(); creneau.setSaison(saisonActive); } creneauRepository.save(creneau); redirectAttributes.addFlashAttribute("success", "Créneau d'entraînement enregistré avec succès."); return "redirect:/planning?jour=" + creneau.getJourSemaine().name(); } @PostMapping("/{id}/delete") public String deleteCreneau(@PathVariable Long id, RedirectAttributes redirectAttributes) { CreneauEntrainement creneau = creneauRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Créneau introuvable : " + id)); JourSemaine jour = creneau.getJourSemaine(); creneauRepository.delete(creneau); redirectAttributes.addFlashAttribute("success", "Créneau d'entraînement supprimé avec succès."); return "redirect:/planning?jour=" + jour.name(); } }