Sfoglia il codice sorgente

refactor into full ajax loading

Olivier Massot 4 anni fa
parent
commit
ca1dcad6f7
27 ha cambiato i file con 611 aggiunte e 192 eliminazioni
  1. 62 6
      ot_core/Classes/Domain/Model/FederationStructure.php
  2. 4 1
      ot_core/Classes/Domain/Repository/FederationStructureRepository.php
  3. 102 0
      ot_templating/Classes/ViewHelpers/Organizations/GetChildFederationViewHelper.php
  4. 40 23
      ot_templating/Classes/ViewHelpers/Organizations/GetFederationStructuresViewHelper.php
  5. 3 0
      ot_templating/Resources/Private/Language/locallang.xlf
  6. 37 71
      ot_templating/Resources/Private/Layouts/Classic/StructuresFrame.html
  7. 319 86
      ot_templating/Resources/Public/assets/Classic/script/structures.js
  8. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-blue.css
  9. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-blue.css.map
  10. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-green.css
  11. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-green.css.map
  12. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-grey.css
  13. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-grey.css.map
  14. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-light-blue.css
  15. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-light-blue.css.map
  16. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-light-red.css
  17. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-light-red.css.map
  18. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-orange.css
  19. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-orange.css.map
  20. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-purple.css
  21. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-purple.css.map
  22. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-red.css
  23. 0 0
      ot_templating/Resources/Public/assets/Classic/style/classic-red.css.map
  24. 44 5
      ot_templating/Resources/Public/assets/Classic/style/module/_structures.scss
  25. 0 0
      ot_templating/Resources/Public/assets/Classic/style/style.css
  26. 0 0
      ot_templating/Resources/Public/assets/Classic/style/style.css.map
  27. BIN
      ot_templating/Resources/Public/media/gear.gif

+ 62 - 6
ot_core/Classes/Domain/Model/FederationStructure.php

@@ -32,6 +32,13 @@ class FederationStructure extends AbstractEntity
      */
     protected $principalType;
 
+    /**
+     * website
+     *
+     * @var string
+     */
+    protected $website;
+
     /**
      * addressCity
      *
@@ -88,12 +95,19 @@ class FederationStructure extends AbstractEntity
      */
     protected $logoUri;
 
+    /**
+     * parent_name
+     *
+     * @var int
+     */
+    protected $parentId;
+
     /**
      * parent_name
      *
      * @var string
      */
-    protected $parent_name;
+    protected $parentName;
 
     /**
      * parents
@@ -165,6 +179,27 @@ class FederationStructure extends AbstractEntity
         $this->principalType = $principalType;
     }
 
+    /**
+     * Returns the website
+     *
+     * @return string $website
+     */
+    public function getWebsite(): ?string
+    {
+        return $this->website;
+    }
+
+    /**
+     * Sets the website
+     *
+     * @param string|null $website
+     * @return void
+     */
+    public function setWebsite(?string $website)
+    {
+        $this->website = $website;
+    }
+
     /**
      * Returns the addressCity
      *
@@ -333,22 +368,43 @@ class FederationStructure extends AbstractEntity
     /**
      * Returns the parent name
      *
-     * @return string $parent_name
+     * @return string $parentId
+     */
+    public function getParentId(): ?string
+    {
+        return $this->parentId;
+    }
+
+    /**
+     * Sets the parent id
+     *
+     * @param string|null $parent_name
+     * @return void
+     */
+    public function setParentId(?string $parentId)
+    {
+        $this->parentId = $parentId;
+    }
+
+    /**
+     * Returns the parent name
+     *
+     * @return string $parentName
      */
     public function getParentName(): ?string
     {
-        return $this->parent_name;
+        return $this->parentName;
     }
 
     /**
      * Sets the parent name
      *
-     * @param string|null $parent_name
+     * @param string|null $parentName
      * @return void
      */
-    public function setParentName(?string $parent_name)
+    public function setParentName(?string $parentName)
     {
-        $this->parent_name = $parent_name;
+        $this->parentName = $parentName;
     }
 
     /**

+ 4 - 1
ot_core/Classes/Domain/Repository/FederationStructureRepository.php

@@ -26,11 +26,12 @@ class FederationStructureRepository extends BaseApiRepository
      * @return ApiPagedCollection
      * @throws ApiRequestException
      */
-    public function findChildrenById(int $id, $searchParams = [], $page = 1): ApiPagedCollection
+    public function findChildrenById(int $id, $searchParams = [], $page = 1, $depth = 5): ApiPagedCollection
     {
         $params = [];
         $params['parent'] = $id;
         $params['page'] = $page;
+        $params['depth'] = $depth;
 
         $params = array_merge($params, $searchParams);
 
@@ -53,6 +54,7 @@ class FederationStructureRepository extends BaseApiRepository
         $federationStructure->setId(end($a));
         $federationStructure->setName($record['name']);
         $federationStructure->setPrincipalType($record['principalType']);
+        $federationStructure->setWebsite($record['website']);
         $federationStructure->setAddressCity($record['addressCity']);
         $federationStructure->setPostalCode($record['postalCode']);
         $federationStructure->setStreetAddress($record['streetAddress']);
@@ -63,6 +65,7 @@ class FederationStructureRepository extends BaseApiRepository
         if ($record['logoId']) {
             $federationStructure->setLogoUri($this->apiService->getApiUri('_internal/secure/files/') . $record['logoId']);
         }
+        $federationStructure->setParentId($record['n1Id']);
         $federationStructure->setParentName($record['n1Name']);
         $federationStructure->setParents($record['parents']);
         return $federationStructure;

+ 102 - 0
ot_templating/Classes/ViewHelpers/Organizations/GetChildFederationViewHelper.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace Opentalent\OtTemplating\ViewHelpers\Organizations;
+
+use FluidTYPO3\Vhs\Traits\TemplateVariableViewHelperTrait;
+use Opentalent\OtCore\Domain\Repository\FederationStructureRepository;
+use Opentalent\OtCore\ViewHelpers\OtAbstractViewHelper;
+
+/**
+ *   This view helper returns all of the children federations as an ApiPagedCollection, without pagination
+ *
+ *     {namespace ot=Opentalent\OtTemplating\ViewHelpers}
+ *
+ *     <ot:organizations.getChildFederation as="organization"
+ *                               organizationId="1">
+ *          <f:debug>{organization}</f:debug>
+ *     </ot:organizations.getChildFederation>
+ *
+ * @package Opentalent\OtTemplating\ViewHelpers
+ */
+class GetChildFederationViewHelper extends OtAbstractViewHelper {
+
+    use TemplateVariableViewHelperTrait;
+
+    /**
+     * >> Required to prevent typo3 to escape the html output
+     * @var boolean
+     */
+    protected $escapeOutput = false;
+
+    /**
+     * @var FederationStructureRepository
+     *
+     */
+    protected $federationRepository;
+
+    /**
+     * -- This method is expected by Fluid --
+     * Declares the viewhelper's parameters
+     */
+    public function initializeArguments()
+    {
+        $this->registerArgument(
+            'as',
+            'string',
+            'Name of the returned array',
+            true
+        );
+        $this->registerArgument(
+            'parentId',
+            'integer',
+            'Id of the parent organization',
+            true
+        );
+        $this->registerArgument(
+            'itemsPerPage',
+            'string',
+            'Number of items per page',
+            false,
+            10
+        );
+    }
+
+    /**
+     * -- This method is expected by Fluid --
+     * Renders the content as html
+     *
+     * @return string
+     * @throws \Opentalent\OtCore\Exception\ApiRequestException
+     */
+    public function render()
+    {
+        $as = $this->arguments['as'];
+        $parentId = $this->arguments['parentId'];
+        $itemsPerPage = $this->arguments['itemsPerPage'];
+        $page = $_REQUEST['page'] ?? 1;
+
+        if ($itemsPerPage == 'all') {
+            $itemsPerPage = 99999; // the 'all' keyword raise an error during the api call
+            $page = 1;
+        }
+
+        $searchParams = ['itemsPerPage' => $itemsPerPage];
+
+        $searchParams['filter[where][n1Id]'] = $parentId;
+        $searchParams['filter[end][principalType]'] = 'FEDERATION';
+        $organizations = $this->federationRepository->findChildrenById($parentId, $searchParams, $page, 1);
+
+        $variables = [
+            $as => $organizations
+        ];
+        return $this->renderChildrenWithVariables($variables);
+    }
+
+    /**
+     * @param FederationStructureRepository $federationRepository
+     */
+    public function injectFederationRepository(FederationStructureRepository $federationRepository)
+    {
+        $this->federationRepository = $federationRepository;
+    }
+}

+ 40 - 23
ot_templating/Classes/ViewHelpers/Organizations/GetFederationStructuresViewHelper.php

@@ -59,6 +59,13 @@ class GetFederationStructuresViewHelper extends OtAbstractViewHelper {
             false,
             10
         );
+        $this->registerArgument(
+            'depth',
+            'integer',
+            'Max level of depth',
+            false,
+            5
+        );
     }
 
     /**
@@ -66,6 +73,7 @@ class GetFederationStructuresViewHelper extends OtAbstractViewHelper {
      * Renders the content as html
      *
      * @return string
+     * @throws \Opentalent\OtCore\Exception\ApiRequestException
      */
     public function render()
     {
@@ -73,6 +81,7 @@ class GetFederationStructuresViewHelper extends OtAbstractViewHelper {
         $parentId = $this->arguments['parentId'];
         $itemsPerPage = $this->arguments['itemsPerPage'];
         $page = $_REQUEST['page'] ?? 1;
+        $depth = $_REQUEST['depth'] ?? 5;
 
         if ($itemsPerPage == 'all') {
             $itemsPerPage = 99999; // the 'all' keyword raise an error during the api call
@@ -81,29 +90,37 @@ class GetFederationStructuresViewHelper extends OtAbstractViewHelper {
 
         $searchParams = ['itemsPerPage' => $itemsPerPage];
 
-        // If a federation was selected in the filters, it is used as parent
-        if($_REQUEST['search-federation']) {
-            $parentId = $_REQUEST['search-federation'];
-        }
-
-        // Search structures by name
-        if($_REQUEST['search-query']) {
-            $searchParams['what'] = $_REQUEST['search-query'];
-        }
-
-        // Filters
-        if($_REQUEST['search-category']) {
-            $searchParams['category'] = $_REQUEST['search-category'];
-        }
-        if($_REQUEST['search-province']) {
-            $province = ltrim($_REQUEST['search-province'], '_'); // a trailing dot may be used to avoid fluid to strip the zero from the postal code
-            $searchParams['province'] = $province;
-        }
-        if($_REQUEST['search-distance-max']) {
-            $searchParams['category'] = $_REQUEST['search-distance-max'];
-        }
-
-        $organizations = $this->federationRepository->findChildrenById($parentId, $searchParams, $page);
+//        // If a federation was selected in the filters, it is used as parent
+//        if($_REQUEST['search-federation']) {
+//            $parentId = (int)$_REQUEST['search-federation'];
+//        }
+//
+//        // Search structures by name
+//        if($_REQUEST['search-query']) {
+//            $searchParams['what'] = $_REQUEST['search-query'];
+//        }
+//
+//        // Filters
+//        if($_REQUEST['search-category']) {
+//            $searchParams['category'] = $_REQUEST['search-category'];
+//        }
+//        if($_REQUEST['search-province']) {
+//            // A leading '_' may have been added to prevent the '0' from being stripped by fluid
+//            $province = ltrim($_REQUEST['search-province'], '_');
+//            $searchParams['province'] = $province;
+//        }
+//        if($_REQUEST['lat']) {
+//            $radius = (int)$_REQUEST['search-radius'] ?? 0;
+//
+//            // radius is always increased of 10km to take into account the size of the city itself
+//            $radius += 10;
+//
+//            $searchParams['lat'] = $_REQUEST['lat'];
+//            $searchParams['long'] = $_REQUEST['long'];
+//            $searchParams['radius'] = $radius;
+//        }
+
+        $organizations = $this->federationRepository->findChildrenById($parentId, $searchParams, $page, $depth);
 
         $variables = [
             $as => $organizations

+ 3 - 0
ot_templating/Resources/Private/Language/locallang.xlf

@@ -163,6 +163,9 @@
 			<trans-unit id="no-result">
 				<source>Aucun résultat</source>
 			</trans-unit>
+			<trans-unit id="no-result">
+				<source>Une erreur s'est produite</source>
+			</trans-unit>
 			<trans-unit id="find">
 				<source>Trouver</source>
 			</trans-unit>

File diff suppressed because it is too large
+ 37 - 71
ot_templating/Resources/Private/Layouts/Classic/StructuresFrame.html


+ 319 - 86
ot_templating/Resources/Public/assets/Classic/script/structures.js

@@ -1,4 +1,13 @@
 
+const variants_uris = {
+    "preprod.opentalent.fr": "https://api.preprod.opentalent.fr",
+    "local.sub.opentalent.fr": "https://local.api.opentalent.fr",
+    "typo3": "http://docker.nginx.opentalent.fr"
+};
+
+const base_uri = variants_uris[$(location).attr('hostname')];
+let apiGetUrl = base_uri + '/api/public/federation_structures?_format=json&page=1&itemsPerPage=99999';
+
 let structures_categories = {
     '1MC': 'Musique',
     '2TH': 'Théatre',
@@ -11,106 +20,208 @@ let structures_categories = {
     'OTAU': 'Autres'
 }
 
+// Converts numeric degrees to radians
+function toRad(Value)
+{
+    return Value * Math.PI / 180;
+}
+
+//This function takes in latitude and longitude of two location and returns the distance between them as the crow flies (in km)
+function sphericDistance(lat1, lon1, lat2, lon2)
+{
+    let R = 6371; // km
+    let dLat = toRad(lat2-lat1);
+    let dLon = toRad(lon2-lon1);
+    lat1 = toRad(lat1);
+    lat2 = toRad(lat2);
+
+    let a = Math.sin(dLat/2) * Math.sin(dLat/2) +
+        Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
+    let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+    return R * c;
+}
+
 // Specific JS used for the Structures layout
 // > Needs to be loaded after the main.js script
 $(document).ready(function() {
 
-    // #### Load the structures data
-    let structures = $('.ot-structures-frame').data('structures');
-    console.log(structures);
+    // Init
+    let structureFrame = $('.ot-structures-frame').first();
+
+    let organizationId = structureFrame.data('org-id');
+
+    let resultsPageDiv = structureFrame.find('.structures-page').first();
+    let pleaseWaitSpan = structureFrame.find('.please-wait').first();
+    let noResultSpan = structureFrame.find('.no-result').first();
+    let cardDivModel = structureFrame.find('.structure-card-model').first();
+    let paginationBar = structureFrame.find('.pagination-bar').first();
+    let paginationList = structureFrame.find('.pagination-list').first();
+    let gotoFirstPage = structureFrame.find('.goto-first-page').first();
+    let gotoLastPage = structureFrame.find('.goto-last-page').first();
+
+    let form = $("#structure-search-form");
+    let whatInput = form.find("input[name='search-query']").first();
+    let cityInput = form.find("input[name='search-city']").first();
+    let latInput = form.find("input[name='lat']").first();
+    let longInput = form.find("input[name='long']").first();
+    let categorySelect = form.find("select[name='search-category']").first();
+    let provinceSelect = form.find("select[name='search-province']").first();
+    let federationSelect = form.find("select[name='search-federation']").first();
+    let radiusSelect = form.find("select[name='search-radius']").first();
+
+    // #### Instanciate and populate leaflet map
+    let mapDiv = $('#structure-map').first();
+    let mapId = $(mapDiv).attr("id");
+
+    // Instanciate the map object  @see https://leafletjs.com/reference-1.6.0.html#map-factory
+    const mapOptions = {scrollWheelZoom: false, zoomSnap: 0.25};
+    map = L.map(mapId, mapOptions);
+    map.setView([46.71, 2.14], 6);
+
+    // Add the tile layer
+    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
+        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
+    }).addTo(map);
+
+    // Set the view
+    bounds = L.latLngBounds(
+        L.latLng(51.03, -5.78),
+        L.latLng(41.2, 9.70)
+    );
+    map.fitBounds(bounds);
+
+    var clusters = null;
+
+
+    // #### Filters
+
+    // The current query
+    var query = {};
+
+    // Update the current query with form data
+    let updateQuery = function() {
+        query['what'] = whatInput.val();
+        query['lat'] = latInput.val();
+        query['long'] = longInput.val();
+        query['category'] = categorySelect.val();
+        query['province'] = provinceSelect.val();
+        query['federation'] = federationSelect.val();
+        query['radius'] = radiusSelect.val();
+    }
 
+    // Does the given structure match the current query
+    let matchCurrentQuery = function(structure) {
 
-    // Display map on network structures page
-    if ($('.ot-structures-frame #structure-map').length) {
-        // #### Instanciate and populate leaflet map
-        let mapDiv = $('#structure-map').first();
-        let mapId = $(mapDiv).attr("id");
-        if (!mapId) {
-            console.error('missing id attribute for map');
-            return;
+        // Filter by name
+        if (query['what'] && !structure.name.toLowerCase().includes(query['what'].toLowerCase())) {
+            return false;
         }
-        // Instanciate the map object  @see https://leafletjs.com/reference-1.6.0.html#map-factory
-        const mapOptions = {scrollWheelZoom: false, zoomSnap: 0.25};
-        map = L.map(mapId, mapOptions);
-        map.setView([46.71, 2.14], 6);
-
-        // Add the tile layer
-        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
-            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
-        }).addTo(map);
-
-        // Set the view
-        bounds = L.latLngBounds(
-            L.latLng(51.03, -5.78),
-            L.latLng(41.2, 9.70)
-        );
-        map.fitBounds(bounds);
 
-        // Load clustered markers on leaflet map
-        var clusters = L.markerClusterGroup();
+        // filter by geographical position
+        if (query['lat'] && query['long']) {
+            if (!structure.latitude || !structure.longitude) {
+                return false;
+            }
 
-        structures.forEach(function (item) {
-            if (item.longitude && item.latitude) {
-                let marker = L.marker([item.latitude, item.longitude]);
-                marker.bindPopup(`<b>${item.name}</b><br/>${item.postalCode} ${item.addressCity}<br/><a href="${item.otherWebsite}" target="_blank">${item.otherWebsite}</a>`);
-                clusters.addLayer(marker);
+            let radius = Number(query['radius']) ?? 0;
+
+            // radius is increased by 10km to approximate the city radius
+            radius += 10;
+
+            if (sphericDistance(query['lat'], query['long'], structure.latitude, structure.longitude) > radius) {
+                return false;
             }
-        });
-        map.addLayer(clusters);
-    }
+        }
 
-    // #### Populate results list
-    let resultsPageDiv = $('.ot-structures-frame .structures-page');
-    let pleaseWaitSpan = $('.ot-structures-frame .please-wait');
-    let noResultSpan = $('.ot-structures-frame .no-result');
-    let cardDivModel = $('.ot-structures-frame .structure-card-model');
-    let paginationBar = $('.ot-structures-frame .pagination-bar');
-    let paginationList = $('.ot-structures-frame .pagination-list');
-    let gotoFirstPage = $(".ot-structures-frame .goto-first-page")
-    let gotoLastPage = $(".ot-structures-frame .goto-last-page")
+        // filter by category
+        if (query['category'] && !structure.categories.includes(query['category'])) {
+            return false;
+        }
 
-    let totalStructures = structures.length;
-    let itemsPerPage = 10;
+        if (query['province']) {
+            let province = query['province'];
 
-    pleaseWaitSpan.show();
+            // A leading '_' may have been added to prevent the '0' from being stripped by fluid
+            province = province.replace('_', '');
 
-    if (totalStructures === 0) {
-        noResultSpan.show();
+            if (!structure.postalCode.startsWith(province)) {
+                return false
+            }
+        }
+        if (query['federation'] && structure.parentId !== query['federation']) {
+            return false;
+        }
+        // structure match every filter
+        return true;
+    }
+
+    let populateFederationsSelect = function () {
+        federationSelect.children('option:not(:first)').remove();
+        let has_options = false;
+        structures.forEach(function (structure) {
+            if (structure.n1Id === organizationId && structure.principalType.endsWith('FEDERATION')) {
+                let id = structure['@id'].split('/').pop()
+                let option = '<option value="' + id + '">' + structure.name + '</option>';
+                federationSelect.append(option);
+                has_options = true;
+            }
+        })
+        if (has_options) {
+            federationSelect.prop("disabled", false);
+        }
     }
 
-    totalPages = Math.floor(totalStructures / itemsPerPage);
 
-    // update the 'goto last page' link
-    gotoLastPage.data("page", totalPages);
+    // #### Display results
+    const itemsPerPage = 10;
+    let currentPage = 1;
+
+    updateQuery();
 
     // on page link clicked event
     let pageLinkClicked = function(e) {
         e.preventDefault();
         let page = $(this).data('page');
-        showPage(page);
+        if (page > 0) {
+            currentPage = page;
+        }
+        refresh();
         $('body,html').animate({scrollTop: 0},500);
     }
+    gotoFirstPage.on('click', pageLinkClicked);
+    gotoLastPage.on('click', pageLinkClicked);
 
-    gotoFirstPage.on('click', pageLinkClicked)
-    gotoLastPage.on('click', pageLinkClicked)
-
-    function showPage(page) {
-
-        let index = 0;
-        $('.ot-structures-frame .structures-page .structure-card').remove();
+    let refresh = function() {
 
+        // Reinitialize current results
         pleaseWaitSpan.show();
 
+        resultsPageDiv.find('.structure-card').remove();
+        paginationList.find('.goto-page-li').remove();
+        paginationList.hide();
+        if (clusters !== null) {
+            map.removeLayer(clusters);
+        }
+
+        // ** Update results
+        let results = [];
         structures.forEach(function(structure) {
+            if (matchCurrentQuery(structure)) {
+                results.push(structure)
+            }
+        });
 
-            if (((page - 1) * itemsPerPage) <= index && index < (page * itemsPerPage)) {
+        // ** Show the results for the current page
+        let index = 0;
 
+        results.forEach(function(structure) {
+            if (((currentPage - 1) * itemsPerPage) <= index && index < (currentPage * itemsPerPage)) {
                 let cardDiv = $(cardDivModel).clone();
 
                 cardDiv.data('id', structure.id);
 
                 let categoryTagModel = cardDiv.find('.structure-category-model').first();
-                structure.categories.forEach(function(cat) {
+                structure.categories.forEach(function (cat) {
                     let tag = categoryTagModel.clone();
                     tag.text(structures_categories[cat]);
                     tag.removeClass('structure-category-model')
@@ -134,12 +245,27 @@ $(document).ready(function() {
             index++;
         });
 
-        // Update the pagination
-        $('.ot-structures-frame .pagination-list .goto-page-li').remove();
+        // ** Update results on map
+        clusters = L.markerClusterGroup();
+
+        results.forEach(function (item) {
+            if (item.longitude && item.latitude) {
+                let marker = L.marker([item.latitude, item.longitude]);
+                marker.bindPopup(`<b>${item.name}</b><br/>${item.postalCode} ${item.addressCity}<br/><a href="${item.website}" target="_blank">${item.website}</a>`);
+                clusters.addLayer(marker);
+            }
+        });
+        map.addLayer(clusters);
+
+        // ** Update pagination
+        let resultsCount = results.length;
+        let pagesCount = Math.floor(resultsCount / itemsPerPage) + 1;
+        gotoLastPage.data("page", pagesCount);
+
         let pageLiModel = paginationList.find('.goto-page-li-model').first();
 
-        page_min = page > 5 ? page - 5 : 1;
-        page_max = page < (totalPages - 5) ? page + 5 : totalPages;
+        let page_min = currentPage > 5 ? currentPage - 5 : 1;
+        let page_max = currentPage < (pagesCount - 5) ? currentPage + 5 : pagesCount;
 
         for (let i = page_min; i <= page_max; i++) {
             let pageLi = pageLiModel.clone();
@@ -150,7 +276,7 @@ $(document).ready(function() {
             pageLink.attr("data-page", i);
             pageLink.on('click', pageLinkClicked)
 
-            if (i === page) {
+            if (i === currentPage) {
                 pageLi.addClass('current');
             }
             pageLi.removeClass('goto-page-li-model')
@@ -160,10 +286,59 @@ $(document).ready(function() {
 
             pageLi.appendTo(paginationList);
         }
+        paginationBar.show()
+        paginationList.show()
+
+        // Finalize
         pleaseWaitSpan.hide();
+
     }
 
-    showPage(1);
+    // #### Load structures data and refresh
+    var structures = [];
+    $.ajax({
+        type: 'GET',
+        url: apiGetUrl,
+        dataType: "json",
+        contentType: "application/json; charset=utf-8"
+    })
+    .done(function(res) {
+        structures = res["hydra:member"];
+        console.log(structures);
+        populateFederationsSelect();
+        refresh();
+    })
+    .fail(function() {
+        structureFrame.find(".error-message").show()
+    });
+
+
+    // #### Events
+
+    // Update query
+    form.on('submit', function(e) {
+        e.preventDefault();
+        updateQuery();
+        currentPage = 1;
+        refresh();
+    });
+
+    $('button[name="submit-search"]').on('click', function() {
+        form.submit();
+    });
+
+    // Reset search fields
+    $('.reset-search').on('click', function (e) {
+        e.preventDefault();
+        let form = $(this).closest('form');
+        form.find('input').each(function () {
+            $(this).val('');
+        });
+        form.find('select').each(function () {
+            $(this).val('');
+        });
+        form.submit();
+    });
 
     // Map goto commands
     $('img[data-map-fit]').on('click', function (e) {
@@ -201,26 +376,84 @@ $(document).ready(function() {
     })
 
     // Form
-    $('.search-submit').on('click', function() {
-        $('#structure-search-form').submit();
+
+    // city search
+    let addressApiUrl = "https://api-adresse.data.gouv.fr/search/?type=municipality&autocomplete=1&limit=5&q=";
+
+    let resultDropdownDiv = form.find('.city-search-dropdown').first();
+    let input_name = resultDropdownDiv.siblings("input[name='search-city']").first();
+    let input_lat = resultDropdownDiv.siblings("input[name='lat']").first();
+    let input_long = resultDropdownDiv.siblings("input[name='long']").first();
+    let resultDiv = resultDropdownDiv.find('.city-search-results').first();
+    let loadingDiv = resultDropdownDiv.find('.city-search-loading').first();
+    let noResultDiv = resultDropdownDiv.find('.city-search-no-result').first();
+
+    $('body').click(function() {
+        resultDropdownDiv.hide();
+
+        // if no city was selected, clear the input
+        if (!input_lat.val()) {
+            input_name.val('');
+            resultDiv.empty();
+        }
     });
 
-    // Filters updated
-    $('form select').on('change', function (e) {
-        e.preventDefault();
-        $(this).closest('form').submit();
+    input_name.click(function(e) {
+        if (resultDiv.children('.city-search-item').length > 0) {
+            resultDropdownDiv.show();
+        }
+        e.stopPropagation();
     });
 
-    // Reset search fields
-    $('.reset-search').on('click', function (e) {
-        e.preventDefault();
-        let form = $(this).closest('form');
-        form.find('input[type="text"]').each(function () {
-            $(this).val('');
-        });
-        form.find('select').each(function () {
-            $(this).val('');
+    $('.city-search-results').on('click', '.city-search-item', function (e) {
+        input_name.val($(this).text());
+        input_long.val($(this).data("x"));
+        input_lat.val($(this).data("y"));
+    });
+
+    $("input[name='search-city']").on('input', function(e) {
+        let query = $(this).val();
+        let url = addressApiUrl + encodeURI(query);
+
+        if (!query) {
+            resultDropdownDiv.hide();
+            return;
+        }
+
+        input_lat.val('');
+        input_long.val('');
+        loadingDiv.show();
+        resultDiv.empty();
+        resultDropdownDiv.show();
+        noResultDiv.hide();
+
+        $.ajax({
+            type: 'GET',
+            url: url,
+            dataType: "json",
+            contentType: "application/json; charset=utf-8"
+        })
+        .done(function(res) {
+            let features = res.features;
+            if (!features.length > 0) {
+                noResultDiv.show();
+                return;
+            }
+            features.forEach(function (f) {
+                let x = f.geometry.coordinates[0];
+                let y = f.geometry.coordinates[1];
+                let name = f.properties.name;
+                let postcode = f.properties.postcode;
+                let span = '<div class="city-search-item" data-x="' + x + '" data-y="' + y + '">' + name + ' (' + postcode + ')</div>';
+                resultDiv.append(span);
+            });
+            loadingDiv.hide();
+        })
+        .fail(function(e) {
+            let span = "<div>Une erreur s'est produite</div>";
+            console.error(e);
+            resultDiv.append(span);
+            loadingDiv.hide();
         });
-        form.submit();
     });
 });

File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-blue.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-blue.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-green.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-green.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-grey.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-grey.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-light-blue.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-light-blue.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-light-red.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-light-red.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-orange.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-orange.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-purple.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-purple.css.map


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-red.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/classic-red.css.map


+ 44 - 5
ot_templating/Resources/Public/assets/Classic/style/module/_structures.scss

@@ -265,7 +265,8 @@ $input-border-color: #bfbfbf;
     height: 34px;
     padding: 8px 24px;
     border: solid 1px $input-border-color;
-    margin: 6px auto;
+    margin: 6px 6px;
+    max-width: 216px;
   }
 
   .search-bar {
@@ -275,7 +276,7 @@ $input-border-color: #bfbfbf;
     height: auto;
     font-size: 18px;
     border: none;
-    width: 190px;
+    width: 184px;
   }
 
   .search-bar-btn {
@@ -293,19 +294,57 @@ $input-border-color: #bfbfbf;
     color: darken($input-border-color, 20);
   }
 
+  .city-search-dropdown {
+    min-width: 100%;
+    position: relative;
+    display: inline-block;
+    top: -24px;
+    left: -24px;
+
+  }
+
+  .city-search-results, .city-search-no-result, .city-search-loading {
+    position: absolute;
+    min-width: 100%;
+    background: #EEEEEE;
+    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
+    z-index: 99;
+  }
+
+  .city-search-item {
+    display: block;
+    padding: 12px 24px;
+    white-space: nowrap;
+  }
+
+  .city-search-item:hover {
+    background-color: #d9d9d9;
+    cursor: pointer;
+  }
+
   .structure-search select {
     color: $btn-background-color;
     padding: 4px 6px;
-    margin-right: 10px;
+    margin: 3px 6px;
     font-weight: 750;
-    max-width: 110px;
+    width: 127px;
   }
 
   .structure-search .reset-search {
+    background: none;
+    color: $btn-background-color;
+    margin: 0 6px;
+    cursor: pointer;
+    height: 36px;
+    border: solid 2px $btn-background-color;
+    border-radius: 0;
+  }
+
+  .structure-search .submit-search {
     background-color: $btn-background-color;
     color: $btn-text-color;
     font-weight: 750;
-    margin-left: auto;
+    margin: 0 6px;
     cursor: pointer;
     height: 36px;
     border: none;

File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/style.css


File diff suppressed because it is too large
+ 0 - 0
ot_templating/Resources/Public/assets/Classic/style/style.css.map


BIN
ot_templating/Resources/Public/media/gear.gif


Some files were not shown because too many files changed in this diff