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.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; public PlanningController(CreneauEntrainementRepository creneauRepository, TerrainRepository terrainRepository, CategorieRepository categorieRepository, SaisonRepository saisonRepository) { this.creneauRepository = creneauRepository; this.terrainRepository = terrainRepository; this.categorieRepository = categorieRepository; this.saisonRepository = saisonRepository; } @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); // Detect conflicts for (int i = 0; i < creneaux.size(); i++) { CreneauEntrainement c1 = creneaux.get(i); 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()); 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()); 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 (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()); 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(); } }