diff --git a/public/js/utils.js b/public/js/utils.js new file mode 100644 index 0000000..cfa61a7 --- /dev/null +++ b/public/js/utils.js @@ -0,0 +1,116 @@ +// Fonction pour gérer la recherche de villes +function setupCitySearch(searchInputId, suggestionListId, onCitySelected) { + const searchInput = document.getElementById(searchInputId); + const suggestionList = document.getElementById(suggestionListId); + + // Vérifier si les éléments existent avant de continuer + if (!searchInput || !suggestionList) { + console.debug(`Éléments de recherche non trouvés: ${searchInputId}, ${suggestionListId}`); + return; + } + + let searchTimeout; + + // Fonction pour nettoyer la liste des suggestions + function clearSuggestions() { + suggestionList.innerHTML = ''; + suggestionList.style.display = 'none'; + } + + // Fonction pour afficher les suggestions + function displaySuggestions(suggestions) { + clearSuggestions(); + if (suggestions.length === 0) { + return; + } + + suggestions.forEach(suggestion => { + const item = document.createElement('div'); + item.className = 'suggestion-item'; + item.innerHTML = ` +
${suggestion.display_name}
+
+ ${suggestion.type} + ${suggestion.postcode || ''} +
+ `; + + item.addEventListener('click', () => { + searchInput.value = suggestion.display_name; + clearSuggestions(); + if (onCitySelected) { + onCitySelected(suggestion); + } + }); + + suggestionList.appendChild(item); + }); + + suggestionList.style.display = 'block'; + } + + // Fonction pour effectuer la recherche + async function performSearch(query) { + if (!query || query.length < 3) { + clearSuggestions(); + return; + } + + try { + // Recherche avec Nominatim + const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&countrycodes=fr&limit=5`); + const data = await response.json(); + + // Filtrer pour ne garder que les villes + const citySuggestions = data.filter(item => + item.type === 'city' || + item.type === 'town' || + item.type === 'village' || + item.type === 'administrative' + ); + + displaySuggestions(citySuggestions); + } catch (error) { + console.error('Erreur lors de la recherche:', error); + clearSuggestions(); + } + } + + // Gestionnaire d'événements pour l'input + searchInput.addEventListener('input', (e) => { + const query = e.target.value.trim(); + + // Annuler la recherche précédente + if (searchTimeout) { + clearTimeout(searchTimeout); + } + + // Attendre 300ms après la dernière frappe avant de lancer la recherche + searchTimeout = setTimeout(() => { + performSearch(query); + }, 300); + }); + + // Fermer la liste des suggestions en cliquant en dehors + document.addEventListener('click', (e) => { + if (!searchInput.contains(e.target) && !suggestionList.contains(e.target)) { + clearSuggestions(); + } + }); +} + +// Fonction pour formater l'URL de labourage +function getLabourerUrl(zipCode) { + return `/admin/labourer/${zipCode}`; +} + +// Fonction pour gérer la soumission du formulaire d'ajout de ville +window.handleAddCityFormSubmit = function(event) { + event.preventDefault(); + const form = event.target; + const zipCode = form.querySelector('input[name="zip_code"]').value; + + if (zipCode) { + window.location.href = getLabourerUrl(zipCode); + } +}; \ No newline at end of file diff --git a/src/Controller/PublicController.php b/src/Controller/PublicController.php index 7dff42e..593c560 100644 --- a/src/Controller/PublicController.php +++ b/src/Controller/PublicController.php @@ -102,7 +102,7 @@ class PublicController extends AbstractController $message = (new Email()) ->from('contact@osm-commerce.fr') ->to($destinataire) - ->subject('Votre lien de modification Ope nStreetMap') + ->subject('Votre lien de modification OpenStreetMap') ->text('Bonjour, nous sommes des bénévoles d\'OpenStreetMap France et nous vous proposons de modifier les informations de votre commerce. Voici votre lien unique de modification: ' . $this->generateUrl('app_public_edit', [ 'zipcode' => $zipCode, 'name' => $place_name, @@ -117,10 +117,11 @@ class PublicController extends AbstractController #[Route('/', name: 'app_public_index')] public function index(): Response { + $stats = $this->entityManager->getRepository(Stats::class)->findAll(); return $this->render('public/home.html.twig', [ 'controller_name' => 'PublicController', - + 'stats' => $stats ]); } @@ -190,7 +191,6 @@ class PublicController extends AbstractController #[Route('/modify/{osm_object_id}/{version}/{changesetID}', name: 'app_public_submit')] public function submit($osm_object_id, $version, $changesetID): Response { - $place = $this->entityManager->getRepository(Place::class)->findOneBy(['osmId' => $osm_object_id]); if (!$place) { $this->addFlash('warning', 'Ce commerce n\'existe pas.'); @@ -212,11 +212,9 @@ class PublicController extends AbstractController $request_post = $request->request->all(); - $request_post = $this->motocultrice->map_post_values($request_post); foreach ($request_post as $key => $value) { - if (strpos($key, 'commerce_tag_value__') === 0) { $tagKey = str_replace('commerce_tag_value__', '', $key); if (!empty($value)) { @@ -236,7 +234,6 @@ class PublicController extends AbstractController // Récupérer le token OSM depuis les variables d'environnement $osm_api_token = $_ENV['APP_OSM_BEARER']; - $exception = false; $exception_message = ""; try { @@ -293,7 +290,6 @@ class PublicController extends AbstractController // Debug du XML généré $xmlString = $xml->asXML(); - // echo('xml :
'.$xmlString); $response = $client->put("https://api.openstreetmap.org/api/0.6/{$osm_kind}/" . $osm_object_id, [ 'body' => $xmlString, @@ -327,15 +323,13 @@ class PublicController extends AbstractController } // après envoi on récupère les données - $commerce = $this->motocultrice->get_osm_object_data($osm_kind, $osm_object_id); + $commerce_overpass = $this->motocultrice->get_osm_object_data($osm_kind, $osm_object_id); - - $place->update_place_from_overpass_data($commerce); + $place->update_place_from_overpass_data($commerce_overpass); $this->entityManager->persist($place); $this->entityManager->flush(); $this->entityManager->clear(); - $stats = $place->getStats(); if(!$stats) { $stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zip_code' => $place->getZipCode()]); @@ -345,19 +339,20 @@ class PublicController extends AbstractController $stats->setZipCode($place->getZipCode()); } - $stats->addPlace($place); - $place->setStats($stats); - $place->setModifiedDate(new \DateTime()); + $stats->addPlace($place); + $place->setStats($stats); + $place->setModifiedDate(new \DateTime()); - $stats->computeCompletionPercent(); - $this->entityManager->persist($stats); - $this->entityManager->persist($place); - $this->entityManager->flush(); - $this->entityManager->clear(); + $stats->computeCompletionPercent(); + $this->entityManager->persist($stats); + $this->entityManager->persist($place); + $this->entityManager->flush(); + $this->entityManager->clear(); return $this->render('public/view.html.twig', [ 'controller_name' => 'PublicController', - 'commerce' => $commerce, + 'commerce' => $commerce_overpass, + 'commerce_overpass' => $commerce_overpass, 'place' => $place, 'status' => $status, 'exception' => $exception, diff --git a/src/Entity/Stats.php b/src/Entity/Stats.php index 872fda9..57a06d3 100644 --- a/src/Entity/Stats.php +++ b/src/Entity/Stats.php @@ -17,7 +17,7 @@ class Stats private ?int $id = null; #[ORM\Column(length: 255)] - private ?string $zone = null; + private ?string $zone = null; // code insee de la zone #[ORM\Column(type: Types::SMALLINT)] private ?int $completion_percent = null; diff --git a/templates/admin/stats.html.twig b/templates/admin/stats.html.twig index 933415a..ac20d2b 100644 --- a/templates/admin/stats.html.twig +++ b/templates/admin/stats.html.twig @@ -16,6 +16,9 @@ height: 300px; margin: 20px 0; } + .completion-info { + margin-bottom: 2rem; + } {% endblock %} @@ -78,6 +81,29 @@ commerces avec note renseignée. + +
+
+
+ +

Comment est calculé le score de complétion ?

+ +
+ +
+
@@ -101,12 +127,9 @@
- -
-

Tableau des {{ stats.places |length }} lieux

@@ -128,11 +151,19 @@
-

requête overpass

-
-    {{query_places|raw}}
-    
-
+
+
+

Requête Overpass

+
+
+
+            {{query_places|raw}}
+            
+ + Exécuter dans Overpass Turbo + +
+
@@ -692,5 +723,20 @@ sortTable(); }); + + function toggleCompletionInfo() { + const content = document.getElementById('completionInfoContent'); + const icon = document.getElementById('completionInfoIcon'); + + if (content.style.display === 'none') { + content.style.display = 'block'; + icon.classList.remove('bi-chevron-down'); + icon.classList.add('bi-chevron-up'); + } else { + content.style.display = 'none'; + icon.classList.remove('bi-chevron-up'); + icon.classList.add('bi-chevron-down'); + } + } {% endblock %} \ No newline at end of file diff --git a/templates/public/dashboard.html.twig b/templates/public/dashboard.html.twig index a317a01..35f6a52 100644 --- a/templates/public/dashboard.html.twig +++ b/templates/public/dashboard.html.twig @@ -14,216 +14,120 @@ width: 100%; margin-bottom: 1rem; } + .suggestion-list { + position: absolute; + background: white; + border: 1px solid #ddd; + border-radius: 4px; + max-height: 200px; + overflow-y: auto; + width: 100%; + z-index: 1000; + display: none; + } + .suggestion-item { + padding: 8px 12px; + cursor: pointer; + border-bottom: 1px solid #eee; + } + .suggestion-item:hover { + background-color: #f5f5f5; + } + .suggestion-name { + font-weight: bold; + } + .suggestion-details { + font-size: 0.9em; + color: #666; + } + .suggestion-type { + margin-right: 8px; + } + .search-container { + position: relative; + margin-bottom: 1rem; + } {% endblock %} {% block javascripts %} {{ parent() }} - - - + + {% endblock %} {% block body %} -
+
-

Dashboard

+

Tableau de bord

+
+ +
-

Statistiques : {{ stats|length }} codes postaux

- - - - - - - - - - - - - - {% for stat in stats %} - - - - - - - {% endfor %} - -
ZoneNombre de lieuxComplétude %Actions
- {{ stat.zone }} {{ stat.name }} - {{ stat.placesCount }}{{ stat.completionPercent }} - - - -
- - -

{{ places_count }} Lieux

-

-
-
+
+
+

Statistiques par ville

+
+ + + + + + + + + + + + {% for stat in stats %} + + + + + + + + {% endfor %} + +
VilleCode postalComplétionNombre de commercesActions
{{ stat.name }}{{ stat.zone }}{{ stat.completionPercent }}%{{ stat.places|length }} + Voir les statistiques +
+
+
{% endblock %} diff --git a/templates/public/edit.html.twig b/templates/public/edit.html.twig index e8cf7a2..3077d1e 100644 --- a/templates/public/edit.html.twig +++ b/templates/public/edit.html.twig @@ -6,10 +6,6 @@ {% block body %}
-
-    {{ dump(commerce_overpass) }}
-
-

{{ 'display.welcome'|trans }} {{ commerce_overpass.tags_converted.name }}

@@ -144,9 +140,14 @@
+ {% if commerce.stats %} + + zone {{commerce.stats.zone}} + + {% endif %} + - - Dashboard + Dashboard
diff --git a/templates/public/edit/tags.html.twig b/templates/public/edit/tags.html.twig index ee403ef..35500ac 100644 --- a/templates/public/edit/tags.html.twig +++ b/templates/public/edit/tags.html.twig @@ -1,43 +1,45 @@ {% block tags %}
- - {% for k, v in commerce_overpass.tags_converted %} - -
-
- {% if k not in excluded_tags_to_render %} - {% if k == 'phone' or k == 'contact:phone' %} - - {% elseif k == 'addr:housenumber' or k == 'addr:street' or k == 'addr:city' or k == 'addr:postcode' or k == 'addr:country' %} - - {% elseif k == 'contact:mastodon' %} - - {% elseif k == 'email' or k == 'contact:email' %} - - {% elseif k == 'website' or k == 'contact:website' %} - {% elseif k == 'opening_hours' %} - - {% else %} - - {% endif %} - {% endif %} - - {% if k not in excluded_tags_to_render %} - - {{ ('display.keys.' ~ k)|trans }} - {% endif %} -
-
+ {% for k, v in commerce_overpass.tags_converted %} +
+
{% if k not in excluded_tags_to_render %} - - +
+ {% if k == 'phone' or k == 'contact:phone' %} + + {% elseif k == 'addr:housenumber' or k == 'addr:street' or k == 'addr:city' or k == 'addr:postcode' or k == 'addr:country' %} + + {% elseif k == 'contact:mastodon' %} + + {% elseif k == 'email' or k == 'contact:email' %} + + {% elseif k == 'website' or k == 'contact:website' %} + + {% elseif k == 'opening_hours' %} + + {% else %} + + {% endif %} + {{ ('display.keys.' ~ k)|trans }} +
+ {% endif %} -
- - {% endfor %} - +
+ {% if k not in excluded_tags_to_render %} +
+ +
+ {% endif %} +
+
+ {% endfor %}
{% endblock %} \ No newline at end of file diff --git a/templates/public/home.html.twig b/templates/public/home.html.twig index f85eda6..d711a3c 100644 --- a/templates/public/home.html.twig +++ b/templates/public/home.html.twig @@ -6,7 +6,55 @@ {{ parent() }} {% endblock %} @@ -14,40 +62,61 @@
+

+ Mon Commerce OSM +

+

+ Bonjour, ce site permet de modifier les informations de votre commerce sur OpenStreetMap afin de gagner en visibilité sur des milliers de sites web à la fois en une minute, c'est gratuit et sans engagement. +
Nous sommes bénévoles dans une association à but non lucratif. +
Nous vous enverrons un lien unique pour cela par email, et si vous en avez besoin, nous pouvons vous aider. +

+
+
+ + +
+
+ +
+ +
+ + +
+
- -

- Mon Commerce OSM -

-

- Bonjour, ce site permet de modifier les informations de votre commerce sur OpenStreetMap afin de gagner en visibilité sur des milliers de sites web à la fois en une minute, c'est gratuit et sans engagement. -
Nous sommes bénévoles dans une association à but non lucratif. -
Nous vous enverrons un lien unique pour cela par email, et si vous en avez besoin, nous pouvons vous aider. -

-

- - -

-
- - - -
-
- -
- -
- - - -
-
- +
+
+ +
+

Villes disponibles

+

Visualisez un tableau de bord de la complétion des commerces et autres lieux d'intérêt pour votre ville grâce à OpenStreetMap

+ +
+
+ +
+
+
+
+ Ajoutez votre ville + +
+
+
+