Merge branch 'Marvin'

This commit is contained in:
Olivier PARPAILLON
2024-11-20 12:53:30 +01:00
8 changed files with 361 additions and 92 deletions

182
assets/js/add-lieu.js Normal file
View File

@@ -0,0 +1,182 @@
document.addEventListener("DOMContentLoaded", () => {
console.log("Script chargé");
const addLieuButton = document.getElementById("add-lieu-button");
const addLieuModal = document.getElementById("add-lieu-modal");
const cancelAddLieu = document.getElementById("cancel-add-lieu");
const selectLocationButton = document.getElementById("select-location");
const addressDisplay = document.getElementById("lieu-details");
let map, marker, selectedAddress;
if (addLieuButton && addLieuModal && cancelAddLieu && selectLocationButton) {
// Affiche la modal
addLieuButton.addEventListener("click", () => {
const villeId = document.getElementById("sortie_ville").value;
if (!villeId) {
alert("Veuillez sélectionner une ville avant d'ajouter un lieu.");
return;
}
addLieuModal.classList.remove("hidden");
// Détruire la carte si elle existe déjà
if (map) {
map.remove();
map = null;
marker = null;
}
// Récupère les limites de la ville
fetch(`/get-bounds/${villeId}`)
.then(response => {
if (!response.ok) {
throw new Error("Erreur lors de la récupération des limites de la ville");
}
return response.json();
})
.then(data => {
console.log("Bounds de la ville :", data);
// Initialise la carte
map = L.map("map").setView([data.centerLat, data.centerLng], 13);
// Ajout du fond de carte
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19,
}).addTo(map);
// Limite le déplacement de la carte aux bounds
const bounds = L.latLngBounds(
[data.south, data.west],
[data.north, data.east]
);
map.setMaxBounds(bounds);
map.fitBounds(bounds);
// Empêche de dézoomer au-delà de la ville
map.setMinZoom(map.getZoom());
// Gestion de la sélection d'un lieu sur la carte
map.on("click", async (e) => {
const { lat, lng } = e.latlng;
// Place un marqueur
if (marker) {
marker.setLatLng([lat, lng]);
} else {
marker = L.marker([lat, lng]).addTo(map);
}
// Récupère l'adresse à partir des coordonnées
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`
);
if (!response.ok) {
throw new Error("Erreur lors de la récupération des informations du lieu.");
}
const data = await response.json();
console.log("Détails du lieu :", data);
selectedAddress = data.display_name;
// Affiche les détails du lieu
const addressDetails = `
<p><strong>Rue :</strong> ${data.address.road || "Non disponible"}</p>
<p><strong>Ville :</strong> ${data.address.city || data.address.town || "Non disponible"}</p>
<p><strong>Code postal :</strong> ${data.address.postcode || "Non disponible"}</p>
<p><strong>Pays :</strong> ${data.address.country || "Non disponible"}</p>
`;
addressDisplay.innerHTML = addressDetails;
} catch (error) {
console.error("Erreur lors de la récupération des informations :", error);
addressDisplay.textContent = "Impossible de récupérer l'adresse.";
}
});
})
.catch(error => {
console.error("Erreur :", error);
});
});
// Ferme la modal sans enregistrer
cancelAddLieu.addEventListener("click", () => {
addLieuModal.classList.add("hidden");
addressDisplay.innerHTML = ""; // Réinitialise les détails de l'adresse
});
// Sélectionne l'emplacement et envoie les données au serveur
selectLocationButton.addEventListener("click", () => {
if (marker) {
const lat = marker.getLatLng().lat;
const lng = marker.getLatLng().lng;
const villeId = document.getElementById("sortie_ville").value;
if (!villeId) {
alert("Veuillez sélectionner une ville avant d'ajouter un lieu.");
return;
}
if (!selectedAddress) {
alert("Veuillez sélectionner un emplacement sur la carte.");
return;
}
// Demande le nom du lieu à l'utilisateur
const nom = prompt("Nom du lieu ?");
if (!nom) {
alert("Le nom du lieu est obligatoire.");
return;
}
// Envoie les données au serveur
fetch("/lieu/set", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
nom,
rue: selectedAddress,
latitude: lat,
longitude: lng,
villeId,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("Erreur lors de l'ajout du lieu.");
}
return response.json();
})
.then((data) => {
console.log("Lieu ajouté :", data);
// Ajoute le lieu à la liste déroulante
const lieuSelect = document.getElementById("sortie_lieu");
const option = document.createElement("option");
option.value = data.id;
option.textContent = data.nom;
lieuSelect.appendChild(option);
// Sélectionne automatiquement le nouveau lieu
lieuSelect.value = data.id;
// Ferme la modal
addLieuModal.classList.add("hidden");
})
.catch((error) => {
console.error("Erreur :", error);
});
} else {
alert("Veuillez sélectionner un emplacement sur la carte.");
}
});
} else {
console.error("Éléments requis pour ajouter un lieu introuvables.");
}
});

62
assets/js/lieu.js Normal file
View File

@@ -0,0 +1,62 @@
document.addEventListener('DOMContentLoaded', function () {
const villeSelect = document.getElementById('sortie_ville');
const lieuSelect = document.getElementById('sortie_lieu');
const rueLabel = document.getElementById('rue-value');
const codePostalLabel = document.getElementById('codePostal-value');
const latitudeLabel = document.getElementById('latitude-value');
const longitudeLabel = document.getElementById('longitude-value');
if (villeSelect && lieuSelect) {
villeSelect.addEventListener('change', function () {
const villeId = villeSelect.value;
lieuSelect.innerHTML = '<option value="">Sélectionnez un lieu</option>';
lieuSelect.disabled = true;
if (villeId) {
fetch(`/get-lieux/${villeId}`)
.then(response => {
if (!response.ok) {
throw new Error('Erreur lors de la récupération des lieux');
}
return response.json();
})
.then(data => {
lieuSelect.innerHTML = '<option value="">Sélectionnez un lieu</option>';
data.forEach(lieu => {
const option = document.createElement('option');
option.value = lieu.id;
option.textContent = lieu.nom;
option.dataset.details = JSON.stringify(lieu);
lieuSelect.appendChild(option);
});
lieuSelect.disabled = false;
})
.catch(error => {
console.error('Erreur lors de la récupération des lieux :', error);
});
}
});
lieuSelect.addEventListener('change', function () {
const selectedOption = lieuSelect.options[lieuSelect.selectedIndex];
if (selectedOption && selectedOption.dataset.details) {
const lieuDetails = JSON.parse(selectedOption.dataset.details);
rueLabel.textContent = lieuDetails.rue || 'Non renseignée';
codePostalLabel.textContent = lieuDetails.codePostal || 'Non renseigné';
latitudeLabel.textContent = lieuDetails.latitude || 'Non renseignée';
longitudeLabel.textContent = lieuDetails.longitude || 'Non renseignée';
} else {
rueLabel.textContent = 'Renseigner avec le lieu';
codePostalLabel.textContent = 'Renseigner avec le lieu';
latitudeLabel.textContent = 'Renseigner avec le lieu';
longitudeLabel.textContent = 'Renseigner avec le lieu';
}
});
} else {
console.error('Les champs ville-select ou lieu-select sont introuvables.');
}
});

View File

@@ -2,7 +2,13 @@
namespace App\Controller;
use App\Entity\Lieu;
use App\Entity\Ville;
use App\Repository\VilleRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -15,4 +21,93 @@ class LieuController extends AbstractController
'controller_name' => 'LieuController',
]);
}
#[Route('/lieu/set', name: 'lieu_set', methods: ['POST'])]
public function setLieu(
Request $request,
VilleRepository $villeRepository,
EntityManagerInterface $entityManager
): JsonResponse {
$data = json_decode($request->getContent(), true);
if (!isset($data['nom'], $data['rue'], $data['latitude'], $data['longitude'], $data['villeId'])) {
return new JsonResponse(['error' => 'Données manquantes'], Response::HTTP_BAD_REQUEST);
}
$ville = $villeRepository->find($data['villeId']);
if (!$ville) {
return new JsonResponse(['error' => 'Ville non trouvée'], Response::HTTP_NOT_FOUND);
}
$lieu = new Lieu();
$lieu->setNom($data['nom']);
$lieu->setRue($data['rue']);
$lieu->setLatitude($data['latitude']);
$lieu->setLongitude($data['longitude']);
$lieu->setVille($ville);
$entityManager->persist($lieu);
$entityManager->flush();
return new JsonResponse([
'id' => $lieu->getIdLieu(),
'nom' => $lieu->getNom(),
'rue' => $lieu->getRue(),
'latitude' => $lieu->getLatitude(),
'longitude' => $lieu->getLongitude(),
], Response::HTTP_CREATED);
}
#[Route('/get-bounds/{villeId}', name: 'get_bounds', methods: ['GET'])]
public function getBounds(VilleRepository $villeRepository, string $villeId): JsonResponse
{
$ville = $villeRepository->find($villeId);
if (!$ville) {
return new JsonResponse(['error' => 'Ville non trouvée'], Response::HTTP_NOT_FOUND);
}
// Utilisez l'API Nominatim pour récupérer les bounds
$params = [
'q' => $ville->getNom(),
'format' => 'json',
'polygon_geojson' => 1,
];
$url = 'https://nominatim.openstreetmap.org/search?' . http_build_query($params);
try {
$context = stream_context_create([
'http' => [
'header' => "User-Agent: MyApp/1.0 (contact@myapp.com)\r\n",
],
]);
$response = file_get_contents($url, false, $context);
$data = json_decode($response, true);
if (!empty($data[0]['boundingbox'])) {
$boundingBox = $data[0]['boundingbox'];
$centerLat = ($boundingBox[0] + $boundingBox[1]) / 2;
$centerLng = ($boundingBox[2] + $boundingBox[3]) / 2;
return new JsonResponse([
'south' => $boundingBox[0],
'north' => $boundingBox[1],
'west' => $boundingBox[2],
'east' => $boundingBox[3],
'centerLat' => $centerLat,
'centerLng' => $centerLng,
]);
}
return new JsonResponse(['error' => 'Bounding box non trouvée'], Response::HTTP_NOT_FOUND);
} catch (\Exception $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
}

View File

@@ -30,7 +30,7 @@ class Etat
$this->sorties = new ArrayCollection();
}
public function getIdEtat(): ?string // Changement ici
public function getIdEtat(): ?string
{
return $this->idEtat;
}
@@ -68,7 +68,6 @@ class Etat
public function removeSortie(Sortie $sortie): self
{
if ($this->sorties->removeElement($sortie)) {
// Set the owning side to null (unless already changed)
if ($sortie->getEtat() === $this) {
$sortie->setEtat(null);
}

View File

@@ -137,4 +137,6 @@ class Lieu
return $this;
}
}

View File

@@ -65,7 +65,7 @@ class SortieType extends AbstractType
'choice_label' => 'nom',
'label' => 'Lieu',
'placeholder' => 'Sélectionnez une ville d\'abord',
'choices' => [], // Pas de choix au début
'choices' => [],
'attr' => ['id' => 'lieu-select'],
]);
}

View File

@@ -8,21 +8,24 @@
{{ encore_entry_link_tags('app') }}
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet-geosearch/dist/geosearch.css" />
<style>
#map {
width: 100%;
height: 500px;
}
</style>
{% endblock %}
</head>
{% endblock %}
{% block content %}
<div class="min-h-screen flex items-center justify-center bg-gray-100">
<!-- Carte blanche centrée -->
<div class="w-full max-w-3xl bg-white p-8 rounded-lg shadow-lg">
<h1 class="text-2xl font-bold text-gray-800 mb-6 text-center">Créer une sortie</h1>
<!-- Début du formulaire -->
{{ form_start(form, { 'attr': { 'class': 'space-y-6' } }) }}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Partie gauche du formulaire -->
<div class="space-y-4">
{{ form_row(form.nom, {'label': 'Nom de la sortie', 'attr': {'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500'}}) }}
{{ form_row(form.dateHeureDebut, {'label': 'Date et heure de la sortie', 'attr': {'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500'}}) }}
@@ -32,7 +35,6 @@
{{ form_row(form.infosSortie, {'label': 'Description et infos', 'attr': {'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500'}}) }}
</div>
<!-- Partie droite du formulaire -->
<div class="space-y-4">
{{ form_row(form.ville, {'label': 'Ville organisatrice', 'attr': {'class': 'block w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500', 'id': 'ville-select'}}) }}
<div class="flex items-center space-x-2">
@@ -40,7 +42,6 @@
<button type="button" id="add-lieu-button" class="p-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-300">+</button>
</div>
<!-- Champs dépendants -->
<div>
<label class="block text-sm font-medium text-gray-700">Rue :</label>
<span id="rue-value" class="block w-full p-3 border border-gray-300 rounded-lg bg-gray-100">Renseigner avec le lieu</span>
@@ -60,7 +61,6 @@
</div>
</div>
<!-- Boutons -->
<div class="mt-6 flex justify-center space-x-4">
<button type="submit" name="action" value="save" class="px-6 py-3 bg-green-500 text-white rounded-lg shadow hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-300">
Enregistrer
@@ -74,11 +74,12 @@
</div>
</div>
<!-- Modal pour ajouter un lieu avec une carte -->
<!-- Modal -->
<div id="add-lieu-modal" class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 hidden">
<div class="bg-white p-6 rounded-lg shadow-lg w-full max-w-3xl">
<div class="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl aspect-w-16 aspect-h-9">
<h2 class="text-xl font-bold mb-4">Ajouter un lieu</h2>
<div id="map" class="w-full h-64 mb-4"></div>
<div id="map" class="w-full h-full mb-4 rounded-lg"></div>
<div id="lieu-details" class="mb-4 text-gray-700"></div>
<div class="flex justify-end space-x-4">
<button type="button" id="cancel-add-lieu" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-300">Annuler</button>
<button type="button" id="select-location" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-300">Sélectionner</button>
@@ -86,84 +87,10 @@
</div>
</div>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-geosearch/dist/geosearch.umd.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const addLieuButton = document.getElementById('add-lieu-button');
const addLieuModal = document.getElementById('add-lieu-modal');
const cancelAddLieuButton = document.getElementById('cancel-add-lieu');
const selectLocationButton = document.getElementById('select-location');
const lieuSelect = document.getElementById('lieu-select');
const rueField = document.getElementById('rue-value');
const codePostalField = document.getElementById('codePostal-value');
const latitudeField = document.getElementById('latitude-value');
const longitudeField = document.getElementById('longitude-value');
let map, marker;
// Initialize the map
addLieuButton.addEventListener('click', function () {
addLieuModal.classList.remove('hidden');
if (!map) {
map = L.map('map').setView([48.8566, 2.3522], 13); // Default view on Paris
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
const provider = new window.GeoSearch.OpenStreetMapProvider();
const searchControl = new window.GeoSearch.GeoSearchControl({
provider: provider,
style: 'bar',
autoComplete: true,
autoCompleteDelay: 250,
});
map.addControl(searchControl);
map.on('geosearch/showlocation', function (e) {
const latlng = e.location;
map.setView(latlng, 13);
if (marker) {
marker.setLatLng(latlng);
} else {
marker = L.marker(latlng).addTo(map);
}
document.getElementById('latitude-value').textContent = latlng.y;
document.getElementById('longitude-value').textContent = latlng.x;
});
map.on('click', function (e) {
const { lat, lng } = e.latlng;
if (marker) {
marker.setLatLng([lat, lng]);
} else {
marker = L.marker([lat, lng]).addTo(map);
}
document.getElementById('latitude-value').textContent = lat;
document.getElementById('longitude-value').textContent = lng;
});
}
});
cancelAddLieuButton.addEventListener('click', function () {
addLieuModal.classList.add('hidden');
});
selectLocationButton.addEventListener('click', function () {
if (marker) {
const { lat, lng } = marker.getLatLng();
latitudeField.textContent = lat;
longitudeField.textContent = lng;
addLieuModal.classList.add('hidden');
}
});
});
</script>
{% block javascripts %}
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-geosearch/dist/geosearch.umd.js"></script>
{{ encore_entry_script_tags('add-lieu') }}
{{ encore_entry_script_tags('lieu') }}
{% endblock %}
{% endblock %}

View File

@@ -21,6 +21,8 @@ Encore
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
.addEntry('lieu', './assets/js/lieu.js')
.addEntry('add-lieu', './assets/js/add-lieu.js')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()