up recherche home avec nominatim

This commit is contained in:
Tykayn 2025-07-18 14:21:55 +02:00 committed by tykayn
parent 7f79ec3a9f
commit c89751b45c
7 changed files with 601 additions and 220 deletions

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

15
.idea/php.xml generated
View file

@ -10,6 +10,11 @@
<option name="highlightLevel" value="WARNING" /> <option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="9bdd9bda-8cdb-43f2-af13-070085f7956e" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpIncludePathManager"> <component name="PhpIncludePathManager">
<include_path> <include_path>
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" /> <path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
@ -155,6 +160,11 @@
</include_path> </include_path>
</component> </component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" /> <component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
<component name="PhpStan">
<PhpStan_settings>
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="9bdd9bda-8cdb-43f2-af13-070085f7956e" timeout="60000" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration"> <component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>
@ -163,6 +173,11 @@
<PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml.dist" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" /> <PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml.dist" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" />
</phpunit_settings> </phpunit_settings>
</component> </component>
<component name="Psalm">
<Psalm_settings>
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="9bdd9bda-8cdb-43f2-af13-070085f7956e" timeout="60000" />
</Psalm_settings>
</component>
<component name="PsalmOptionsConfiguration"> <component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>

View file

@ -1967,7 +1967,7 @@ final class AdminController extends AbstractController
// Set OSM object type and OSM ID from the Place // Set OSM object type and OSM ID from the Place
$demande->setOsmObjectType($place->getOsmKind()); $demande->setOsmObjectType($place->getOsmKind());
$demande->setOsmId((int)$place->getOsmId()); $demande->setOsmId($place->getOsmId());
$this->entityManager->persist($demande); $this->entityManager->persist($demande);
$this->entityManager->flush(); $this->entityManager->flush();

View file

@ -148,10 +148,11 @@ class PublicController extends AbstractController
// Save the OSM ID if provided // Save the OSM ID if provided
if (isset($data['osmId']) && !empty($data['osmId'])) { if (isset($data['osmId']) && !empty($data['osmId'])) {
$demande->setOsmId((int)$data['osmId']); $demande->setOsmId($data['osmId']);
} }
// Check if a Place exists with the same OSM ID and type // Check if a Place exists with the same OSM ID and type
$place = null;
if ($demande->getOsmId() && $demande->getOsmObjectType()) { if ($demande->getOsmId() && $demande->getOsmObjectType()) {
$existingPlace = $this->entityManager->getRepository(Place::class)->findOneBy([ $existingPlace = $this->entityManager->getRepository(Place::class)->findOneBy([
'osm_kind' => $demande->getOsmObjectType(), 'osm_kind' => $demande->getOsmObjectType(),
@ -162,6 +163,45 @@ class PublicController extends AbstractController
// Link the Place UUID to the Demande // Link the Place UUID to the Demande
$demande->setPlaceUuid($existingPlace->getUuidForUrl()); $demande->setPlaceUuid($existingPlace->getUuidForUrl());
$demande->setPlace($existingPlace); $demande->setPlace($existingPlace);
$place = $existingPlace;
} else {
// Create a new Place if one doesn't exist
$place = new Place();
$place->setOsmId((string)$demande->getOsmId());
$place->setOsmKind($demande->getOsmObjectType());
// Get OSM data from Overpass API
$commerce_overpass = $this->motocultrice->get_osm_object_data($demande->getOsmObjectType(), $demande->getOsmId());
if ($commerce_overpass) {
// Update the Place with OSM data
$place->update_place_from_overpass_data($commerce_overpass);
// Link the Place to the Demande
$demande->setPlaceUuid($place->getUuidForUrl());
$demande->setPlace($place);
// Persist the Place
$this->entityManager->persist($place);
}
}
// Link the Place to a Stat object using the INSEE code
if ($place && $demande->getInsee()) {
$stats = $place->getStats();
if (!$stats) {
$stats_exist = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $demande->getInsee()]);
if ($stats_exist) {
$stats = $stats_exist;
} else {
$stats = new Stats();
$stats->setZone((string)$demande->getInsee());
$this->entityManager->persist($stats);
}
$stats->addPlace($place);
$place->setStats($stats);
}
} }
} }

View file

@ -41,8 +41,8 @@ class Demande
#[ORM\Column(length: 10, nullable: true)] #[ORM\Column(length: 10, nullable: true)]
private ?string $osmObjectType = null; private ?string $osmObjectType = null;
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true, type: 'string', length: 255)]
private ?int $osmId = null; private ?string $osmId = null;
public function __construct() public function __construct()
{ {
@ -163,12 +163,12 @@ class Demande
return $this; return $this;
} }
public function getOsmId(): ?int public function getOsmId(): ?string
{ {
return $this->osmId; return $this->osmId;
} }
public function setOsmId(?int $osmId): static public function setOsmId(?string $osmId): static
{ {
$this->osmId = $osmId; $this->osmId = $osmId;

View file

@ -133,6 +133,13 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
// Enable the labourage form
if (typeof enableLabourageForm === 'function') {
enableLabourageForm();
} else {
console.error('enableLabourageForm function not found');
}
}); });
</script> </script>
{% endblock %} {% endblock %}

View file

@ -112,6 +112,15 @@
background-color: #f5f5f5; background-color: #f5f5f5;
color: #000; color: #000;
} }
#resultsMap {
height: 400px;
width: 100%;
margin-top: 1rem;
margin-bottom: 1rem;
border-radius: 8px;
display: none;
}
</style> </style>
{% endblock %} {% endblock %}
@ -162,7 +171,8 @@
<i class="bi bi-globe text-primary" style="font-size: 3rem;"></i> <i class="bi bi-globe text-primary" style="font-size: 3rem;"></i>
</div> </div>
<h3 class="card-title h5 fw-bold">Visibilité Maximale</h3> <h3 class="card-title h5 fw-bold">Visibilité Maximale</h3>
<p class="card-text">Vos informations apparaîtront sur TomTom, Apple Plans, Facebook, CoMaps et des <p class="card-text">Vos informations apparaîtront sur TomTom, Apple Plans, Facebook, CoMaps et
des
centaines d'autres applications.</p> centaines d'autres applications.</p>
</div> </div>
</div> </div>
@ -222,9 +232,23 @@
<div class="form-text"> <div class="form-text">
<i class="bi bi-info-circle"></i> Exemple: Café de la Place, Lyon <i class="bi bi-info-circle"></i> Exemple: Café de la Place, Lyon
</div> </div>
<div class="mt-2 d-none">
<label class="form-label">Moteur de recherche :</label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="searchEngine" id="nominatim"
value="nominatim" checked>
<label class="form-check-label" for="nominatim">Nominatim</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="searchEngine" id="addok"
value="addok">
<label class="form-check-label" for="addok">Addok</label>
</div>
</div>
</div> </div>
<div id="resultsList" class="mt-4"></div> <div id="resultsList" class="mt-4"></div>
<div id="resultsMap"></div>
<div id="proposeLink" class="d-none"></div> <div id="proposeLink" class="d-none"></div>
<div id="proposeMail" class="d-none"> <div id="proposeMail" class="d-none">
<div class="mb-3"> <div class="mb-3">
@ -328,15 +352,15 @@
{% block javascripts %} {% block javascripts %}
{{ parent() }} {{ parent() }}
<script> <script>
// Données des villes pour la carte // Données des villes pour la carte
const citiesData = {{ citiesForMap|json_encode|raw }}; const citiesData = {{ citiesForMap|json_encode|raw }};
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
console.log('content loaded') console.log('content loaded')
// Créer le formulaire email // Créer le formulaire email
const emailFormHtml = ` const emailFormHtml = `
<form id="emailForm" class="mt-3"> <form id="emailForm" class="mt-3">
<div class="mb-3"> <div class="mb-3">
<label for="email" class="form-label">Email</label> <label for="email" class="form-label">Email</label>
@ -346,87 +370,151 @@
</form> </form>
`; `;
// Créer les divs pour les messages // Créer les divs pour les messages
const proposeLinkHtml = ` const proposeLinkHtml = `
<div id="proposeLink" class="alert alert-success "> <div id="proposeLink" class="alert alert-success ">
Un email a déjà été enregistré pour ce commerce. Nous vous enverrons le lien de modification. Un email a déjà été enregistré pour ce commerce. Nous vous enverrons le lien de modification.
</div> </div>
`; `;
const proposeMailHtml = ` const proposeMailHtml = `
<div id="proposeMail" class="alert alert-info " > <div id="proposeMail" class="alert alert-info " >
Aucun email n'est enregistré pour ce commerce. Veuillez saisir votre email pour recevoir le lien de modification. Aucun email n'est enregistré pour ce commerce. Veuillez saisir votre email pour recevoir le lien de modification.
${emailFormHtml} ${emailFormHtml}
</div> </div>
`; `;
// Ajouter les éléments au DOM // Ajouter les éléments au DOM
// Initialize the elements only if they exist // Initialize the elements only if they exist
const proposeLinkElement = document.querySelector('#proposeLink'); const proposeLinkElement = document.querySelector('#proposeLink');
const proposeMailElement = document.querySelector('#proposeMail'); const proposeMailElement = document.querySelector('#proposeMail');
if (proposeLinkElement) { if (proposeLinkElement) {
proposeLinkElement.innerHTML = proposeLinkHtml; proposeLinkElement.innerHTML = proposeLinkHtml;
} }
if (proposeMailElement) { if (proposeMailElement) {
proposeMailElement.innerHTML = proposeMailHtml; proposeMailElement.innerHTML = proposeMailHtml;
} }
const searchInput = document.querySelector('#researchShop'); const searchInput = document.querySelector('#researchShop');
const resultsList = document.querySelector('#resultsList'); const resultsList = document.querySelector('#resultsList');
resultsList.classList.add('list-group', 'mt-2'); const resultsMap = document.querySelector('#resultsMap');
resultsList.classList.add('list-group', 'mt-2');
let timeoutId; // Initialize map
searchInput.addEventListener('input', function (e) { let map = null;
clearTimeout(timeoutId); let markers = [];
resultsList.innerHTML = '';
if (e.target.value.length < 2) return; function initMap() {
if (!map) {
map = new maplibregl.Map({
container: 'resultsMap',
style: 'https://api.maptiler.com/maps/streets/style.json?key={{ maptiler_token }}',
center: [2.213749, 46.227638], // Center of France
zoom: 5
});
const query_input_user = e.target.value; // Add navigation controls
map.addControl(new maplibregl.NavigationControl());
}
}
timeoutId = setTimeout(() => { function clearMarkers() {
fetch(`https://demo.addok.xyz/search?q=${query_input_user}&limit=5`) // Remove all existing markers
.then(response => response.json()) markers.forEach(marker => marker.remove());
.then(data => { markers = [];
console.log(data); }
resultsList.innerHTML = ''; function performSearch() {
const ul = document.createElement('ul'); clearTimeout(timeoutId);
ul.classList.add('list-group'); resultsList.innerHTML = '';
resultsList.appendChild(ul); resultsMap.style.display = 'none';
clearMarkers();
// Show a message if there are no results const query_input_user = searchInput.value;
if (data.features.length === 0) {
const noResultsMessage = document.createElement('div');
noResultsMessage.classList.add('alert', 'alert-info', 'mt-3');
noResultsMessage.innerHTML = `
<i class="bi bi-info-circle"></i> Aucun résultat trouvé pour votre recherche.
`;
resultsList.appendChild(noResultsMessage);
}
// Add "Not found" button if (query_input_user.length < 2) return;
if (true) {
const notFoundButton = document.createElement('div'); // Get the selected search engine
notFoundButton.classList.add('mt-3', 'text-center'); const searchEngine = document.querySelector('input[name="searchEngine"]:checked').value;
notFoundButton.innerHTML = `
let searchUrl;
if (searchEngine === 'nominatim') {
// Use Nominatim API
searchUrl = `https://nominatim.openstreetmap.org/search?q=${query_input_user}&format=json&limit=5&countrycodes=fr`;
} else {
// Use Addok API
searchUrl = `https://demo.addok.xyz/search?q=${query_input_user}&limit=5`;
}
fetch(searchUrl)
}
// Add event listeners to search engine radio buttons
document.querySelectorAll('input[name="searchEngine"]').forEach(radio => {
radio.addEventListener('change', performSearch);
});
let timeoutId;
searchInput.addEventListener('input', function (e) {
clearTimeout(timeoutId);
resultsList.innerHTML = '';
resultsMap.style.display = 'none';
clearMarkers();
if (e.target.value.length < 2) return;
const query_input_user = e.target.value;
timeoutId = setTimeout(() => {
// Get the selected search engine
const searchEngine = document.querySelector('input[name="searchEngine"]:checked').value;
let searchUrl;
if (searchEngine === 'nominatim') {
// Use Nominatim API
searchUrl = `https://nominatim.openstreetmap.org/search?q=${query_input_user}&format=json&limit=5&countrycodes=fr`;
} else {
// Use Addok API
searchUrl = `https://demo.addok.xyz/search?q=${query_input_user}&limit=5`;
}
fetch(searchUrl)
.then(response => response.json())
.then(data => {
console.log(data);
resultsList.innerHTML = '';
const ul = document.createElement('ul');
ul.classList.add('list-group');
resultsList.appendChild(ul);
// This section will be handled in the code below
// Add "Not found" button
if (true) {
const notFoundButton = document.createElement('div');
notFoundButton.classList.add('mt-3', 'text-center');
notFoundButton.innerHTML = `
<button id="notFoundButton" class="btn btn-outline-secondary"> <button id="notFoundButton" class="btn btn-outline-secondary">
<i class="bi bi-search"></i> Je ne trouve pas mon commerce dans ces propositions <i class="bi bi-search"></i> Je ne trouve pas mon commerce dans ces propositions
</button> </button>
`; `;
resultsList.appendChild(notFoundButton); resultsList.appendChild(notFoundButton);
// Add event listener to the "Not found" button // Add event listener to the "Not found" button
document.getElementById('notFoundButton').addEventListener('click', () => { document.getElementById('notFoundButton').addEventListener('click', () => {
// Clear results list // Clear results list
resultsList.innerHTML = ''; resultsList.innerHTML = '';
// Hide map and clear markers
resultsMap.style.display = 'none';
clearMarkers();
// Show form to enter email // Show form to enter email
const notFoundForm = document.createElement('div'); const notFoundForm = document.createElement('div');
notFoundForm.classList.add('card', 'mt-3'); notFoundForm.classList.add('card', 'mt-3');
notFoundForm.innerHTML = ` notFoundForm.innerHTML = `
<div class="card-header"> <div class="card-header">
<h5>Vous ne trouvez pas votre commerce ?</h5> <h5>Vous ne trouvez pas votre commerce ?</h5>
</div> </div>
@ -441,179 +529,405 @@
</form> </form>
</div> </div>
`; `;
resultsList.appendChild(notFoundForm); resultsList.appendChild(notFoundForm);
// Add event listener to the form // Add event listener to the form
document.getElementById('notFoundForm').addEventListener('submit', (e) => { document.getElementById('notFoundForm').addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
const email = document.getElementById('notFoundEmail').value; const email = document.getElementById('notFoundEmail').value;
const query = document.querySelector('#researchShop').value; const query = document.querySelector('#researchShop').value;
// Try to get the INSEE code from the first search result // Try to get the INSEE code from the first search result
let insee = null; let insee = null;
if (data.features && data.features.length > 0) { if (searchEngine === 'nominatim') {
insee = data.features[0].properties.citycode; if (data.length > 0 && data[0].address && data[0].address.postcode) {
} insee = data[0].address.postcode;
}
} else {
if (data.features && data.features.length > 0) {
insee = data.features[0].properties.citycode;
}
}
// Create a Demande with the business name and email // Create a Demande with the business name and email
fetch('/api/demande/create', { fetch('/api/demande/create', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
businessName: query, businessName: query,
insee: insee insee: insee
}), }),
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
const demandeId = data.id; const demandeId = data.id;
// Update the Demande with the email // Update the Demande with the email
fetch(`/api/demande/${demandeId}/email`, { fetch(`/api/demande/${demandeId}/email`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
email: email email: email
}), }),
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
// Show success message // Show success message
resultsList.innerHTML = ` resultsList.innerHTML = `
<div class="alert alert-success mt-3"> <div class="alert alert-success mt-3">
<i class="bi bi-check-circle"></i> Votre demande a été enregistrée. Nous vous contacterons bientôt. <i class="bi bi-check-circle"></i> Votre demande a été enregistrée. Nous vous contacterons bientôt.
</div> </div>
`; `;
} else { } else {
alert('Erreur: ' + data.message); alert('Erreur: ' + data.message);
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
alert('Une erreur est survenue lors de la mise à jour de la demande.'); alert('Une erreur est survenue lors de la mise à jour de la demande.');
}); });
} else { } else {
alert('Erreur: ' + data.message); alert('Erreur: ' + data.message);
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
alert('Une erreur est survenue lors de la création de la demande.'); alert('Une erreur est survenue lors de la création de la demande.');
}); });
}); });
}); });
} }
data.features.forEach(feature => { // Process the results based on the search engine
const li = document.createElement('li'); if (searchEngine === 'nominatim') {
li.classList.add('list-group-item', 'cursor-pointer'); // Show a message if there are no results
li.textContent = `${feature.properties.name}, ${feature.properties.city}`; if (data.length === 0) {
const noResultsMessage = document.createElement('div');
noResultsMessage.classList.add('alert', 'alert-info', 'mt-3');
noResultsMessage.innerHTML = `
<i class="bi bi-info-circle"></i> Aucun résultat trouvé pour votre recherche.
`;
resultsList.appendChild(noResultsMessage);
} else {
// Initialize map if we have results
initMap();
resultsMap.style.display = 'block';
li.addEventListener('click', () => { // Calculate bounds for the map
resultsList.innerHTML = ''; // Cacher la liste const bounds = new maplibregl.LngLatBounds();
const [lon, lat] = feature.geometry.coordinates;
const query = `[out:json];
(
node["shop"](around:100,${lat},${lon});
node["amenity"](around:100,${lat},${lon});
);
out body;`;
console.log('li clicked', li) // Add markers for each result
data.forEach((result, index) => {
const lat = parseFloat(result.lat);
const lon = parseFloat(result.lon);
fetch('https://overpass-api.de/api/interpreter', { // Extend bounds
method: 'POST', bounds.extend([lon, lat]);
body: query
})
.then(response => response.json())
.then(osmData => {
console.log('osmData', osmData)
if (osmData.elements.length > 0) {
console.log('osmData.elements', osmData.elements)
const place = osmData.elements[1]; // Create marker with result number
console.log('place', place) const marker = new maplibregl.Marker({color: '#007bff'})
console.log(`https://www.openstreetmap.org/${place.type}/${place.id}`, place.tags); .setLngLat([lon, lat])
.setPopup(new maplibregl.Popup().setHTML(`
<strong>${result.name || result.display_name}</strong><br>
${result.address ? result.address.city || '' : ''}
`));
// Add label with result number
const el = marker.getElement();
el.innerHTML = `<div style="position: absolute; top: -30px; left: 7px; background: white; border-radius: 50%; width: 20px; height: 20px; text-align: center; line-height: 20px; font-weight: bold; border: 2px solid #007bff;">${index + 1}</div>` + el.innerHTML;
marker.addTo(map);
markers.push(marker);
});
// Fit map to bounds with padding
if (!bounds.isEmpty()) {
map.fitBounds(bounds, {padding: 50});
}
}
data.forEach((result, index) => {
const li = document.createElement('li');
li.classList.add('list-group-item', 'cursor-pointer');
// Format the display text based on available properties
let displayText = result.display_name;
if (result.name) {
displayText = result.name;
if (result.address && result.address.city) {
displayText += `, ${result.address.city}`;
}
}
// Add result number
li.innerHTML = `<span class="badge bg-primary me-2">${index + 1}</span> ${displayText}`;
li.addEventListener('click', () => {
resultsList.innerHTML = ''; // Cacher la liste
resultsMap.style.display = 'none'; // Cacher la carte
clearMarkers(); // Supprimer les marqueurs
const lat = parseFloat(result.lat);
const lon = parseFloat(result.lon);
const query = `[out:json];
(
node["shop"](around:10,${lat},${lon});
node["amenity"](around:10,${lat},${lon});
);
out body;`;
console.log('result li clicked', result);
fetch('https://overpass-api.de/api/interpreter', {
method: 'POST',
body: query
})
.then(response => response.json())
.then(osmData => {
console.log('osmData', osmData);
if (osmData.elements.length > 0) {
console.log('osmData.elements', osmData.elements);
const place = osmData.elements[0];
console.log('place ', place);
console.log(`https://www.openstreetmap.org/${place.type}/${place.id}`, place.tags);
// Get INSEE code from result if available
let insee = null;
if (result.address && result.address.postcode) {
insee = result.address.postcode;
}
// Create a Demande with the business name
fetch('/api/demande/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
businessName: `${query_input_user} __ ${place.tags.name}` || displayText,
insee: insee,
osmObjectType: place.type,
osmId: place.id
}),
})
.then(response => response.json())
.then(data => {
if (data.success) {
const demandeId = data.id;
if (place.tags && (place.tags['contact:email'] || place.tags['email'])) {
document.querySelector('#proposeLink').classList.remove('d-none');
document.querySelector('#proposeMail').classList.add('d-none');
} else {
document.querySelector('#proposeMail').classList.remove('d-none');
document.querySelector('#proposeLink').classList.add('d-none');
const emailForm = document.querySelector('#proposeMail form');
emailForm.addEventListener('submit', (e) => {
e.preventDefault();
const email = emailForm.querySelector('#emailInput').value;
// Update the Demande with the email
fetch(`/api/demande/${demandeId}/email`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email
}),
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Redirect to the original route for backward compatibility
window.location.href = `/propose-email/${email}/${place.type}/${place.id}`;
} else {
alert('Erreur: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Une erreur est survenue lors de la mise à jour de la demande.');
});
});
}
} else {
alert('Erreur: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Une erreur est survenue lors de la création de la demande.');
});
}
});
});
ul.appendChild(li);
});
} else {
// Process Addok results
// Show a message if there are no results
if (data.features.length === 0) {
const noResultsMessage = document.createElement('div');
noResultsMessage.classList.add('alert', 'alert-info', 'mt-3');
noResultsMessage.innerHTML = `
<i class="bi bi-info-circle"></i> Aucun résultat trouvé pour votre recherche.
`;
resultsList.appendChild(noResultsMessage);
} else {
// Initialize map if we have results
initMap();
resultsMap.style.display = 'block';
// Calculate bounds for the map
const bounds = new maplibregl.LngLatBounds();
// Add markers for each result
data.features.forEach((feature, index) => {
const [lon, lat] = feature.geometry.coordinates;
// Extend bounds
bounds.extend([lon, lat]);
// Create marker with result number
const marker = new maplibregl.Marker({color: '#007bff'})
.setLngLat([lon, lat])
.setPopup(new maplibregl.Popup().setHTML(`
<strong>${feature.properties.name}</strong><br>
${feature.properties.city || ''}
`));
// Add label with result number
const el = marker.getElement();
el.innerHTML = `<div style="position: absolute; top: -30px; left: 7px; background: white; border-radius: 50%; width: 20px; height: 20px; text-align: center; line-height: 20px; font-weight: bold; border: 2px solid #007bff;">${index + 1}</div>` + el.innerHTML;
marker.addTo(map);
markers.push(marker);
});
// Fit map to bounds with padding
if (!bounds.isEmpty()) {
map.fitBounds(bounds, {padding: 50});
}
}
data.features.forEach((feature, index) => {
const li = document.createElement('li');
li.classList.add('list-group-item', 'cursor-pointer');
li.innerHTML = `<span class="badge bg-primary me-2">${index + 1}</span> ${feature.properties.name}, ${feature.properties.city}`;
li.addEventListener('click', () => {
resultsList.innerHTML = ''; // Cacher la liste
resultsMap.style.display = 'none'; // Cacher la carte
clearMarkers(); // Supprimer les marqueurs
const [lon, lat] = feature.geometry.coordinates;
const query = `[out:json];
(
node["shop"](around:100,${lat},${lon});
node["amenity"](around:100,${lat},${lon});
);
out body;`;
console.log('li clicked', li)
fetch('https://overpass-api.de/api/interpreter', {
method: 'POST',
body: query
})
.then(response => response.json())
.then(osmData => {
console.log('osmData', osmData)
if (osmData.elements.length > 0) {
console.log('osmData.elements', osmData.elements)
const place = osmData.elements[1];
console.log('place', place)
console.log(`https://www.openstreetmap.org/${place.type}/${place.id}`, place.tags);
// Create a Demande with the business name // Create a Demande with the business name
fetch('/api/demande/create', { fetch('/api/demande/create', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
businessName: `${query_input_user} __ ${place.tags.name}` || `${feature.properties.name}, ${feature.properties.city}`, businessName: `${query_input_user} __ ${place.tags.name}` || `${feature.properties.name}, ${feature.properties.city}`,
insee: feature.properties.citycode insee: feature.properties.citycode,
}), osmObjectType: place.type,
}) osmId: place.id
.then(response => response.json()) }),
.then(data => { })
if (data.success) { .then(response => response.json())
const demandeId = data.id; .then(data => {
if (data.success) {
const demandeId = data.id;
if (place.tags && (place.tags['contact:email'] || place.tags['email'])) { if (place.tags && (place.tags['contact:email'] || place.tags['email'])) {
document.querySelector('#proposeLink').classList.remove('d-none'); document.querySelector('#proposeLink').classList.remove('d-none');
document.querySelector('#proposeMail').classList.add('d-none'); document.querySelector('#proposeMail').classList.add('d-none');
} else { } else {
document.querySelector('#proposeMail').classList.remove('d-none'); document.querySelector('#proposeMail').classList.remove('d-none');
document.querySelector('#proposeLink').classList.add('d-none'); document.querySelector('#proposeLink').classList.add('d-none');
const emailForm = document.querySelector('#proposeMail form'); const emailForm = document.querySelector('#proposeMail form');
emailForm.addEventListener('submit', (e) => { emailForm.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
const email = emailForm.querySelector('#emailInput').value; const email = emailForm.querySelector('#emailInput').value;
// Update the Demande with the email // Update the Demande with the email
fetch(`/api/demande/${demandeId}/email`, { fetch(`/api/demande/${demandeId}/email`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
email: email email: email
}), }),
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
// Redirect to the original route for backward compatibility // Redirect to the original route for backward compatibility
window.location.href = `/propose-email/${email}/${place.type}/${place.id}`; window.location.href = `/propose-email/${email}/${place.type}/${place.id}`;
} else { } else {
alert('Erreur: ' + data.message); alert('Erreur: ' + data.message);
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
alert('Une erreur est survenue lors de la mise à jour de la demande.'); alert('Une erreur est survenue lors de la mise à jour de la demande.');
}); });
}); });
} }
} else { } else {
alert('Erreur: ' + data.message); alert('Erreur: ' + data.message);
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
alert('Une erreur est survenue lors de la création de la demande.'); alert('Une erreur est survenue lors de la création de la demande.');
}); });
} }
}); });
}); });
ul.appendChild(li); ul.appendChild(li);
}); });
}); }
}, 500); });
}); }, 500);
});
}); });
</script> </script>
{% endblock %} {% endblock %}