diff --git a/lemarche/api/perimeters/tests.py b/lemarche/api/perimeters/tests.py index dccf76c25..13c8ec9f6 100644 --- a/lemarche/api/perimeters/tests.py +++ b/lemarche/api/perimeters/tests.py @@ -93,6 +93,12 @@ def test_should_filter_perimeters_autocomplete_by_q_code(self): self.assertEqual(len(response.data), 1 + 1) self.assertEqual(response.data[0]["name"], "Isère") + def test_should_filter_perimeters_autocomplete_by_q_code_only_cities(self): + url = reverse("api:perimeters-autocomplete-list") + "?q=38&kind=CITY" # anonymous user + response = self.client.get(url) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]["name"], "Grenoble") + def test_should_filter_perimeters_autocomplete_by_q_post_code(self): url = reverse("api:perimeters-autocomplete-list") + "?q=38100" # anonymous user response = self.client.get(url) diff --git a/lemarche/api/perimeters/views.py b/lemarche/api/perimeters/views.py index 7ea491c97..18a4b4b9b 100644 --- a/lemarche/api/perimeters/views.py +++ b/lemarche/api/perimeters/views.py @@ -41,9 +41,11 @@ def list(self, request, *args, **kwargs): """ return super().list(request, args, kwargs) - -class CityAutocompleteViewSet(PerimeterAutocompleteViewSet): - queryset = Perimeter.objects.cities() + def get_queryset(self): + kind = self.request.query_params.get("kind", None) + if kind and kind in [id for (id, name) in Perimeter.KIND_CHOICES]: + return Perimeter.objects.filter(kind=kind) + return self.queryset class PerimeterKindViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): diff --git a/lemarche/api/urls.py b/lemarche/api/urls.py index 09a470253..0ab50ecfc 100644 --- a/lemarche/api/urls.py +++ b/lemarche/api/urls.py @@ -5,12 +5,7 @@ from lemarche.api.emails.views import InboundParsingEmailView from lemarche.api.networks.views import NetworkViewSet -from lemarche.api.perimeters.views import ( - CityAutocompleteViewSet, - PerimeterAutocompleteViewSet, - PerimeterKindViewSet, - PerimeterViewSet, -) +from lemarche.api.perimeters.views import PerimeterAutocompleteViewSet, PerimeterKindViewSet, PerimeterViewSet from lemarche.api.sectors.views import SectorViewSet from lemarche.api.siaes.views import SiaeKindViewSet, SiaePrestaTypeViewSet, SiaeViewSet from lemarche.api.tenders.views import TenderAmountViewSet, TenderKindViewSet, TenderViewSet @@ -27,7 +22,6 @@ router.register(r"networks", NetworkViewSet, basename="networks") router.register(r"perimeters/kinds", PerimeterKindViewSet, basename="perimeter-kinds") router.register(r"perimeters/autocomplete", PerimeterAutocompleteViewSet, basename="perimeters-autocomplete") -router.register(r"cities/autocomplete", CityAutocompleteViewSet, basename="cities-autocomplete") router.register(r"perimeters", PerimeterViewSet, basename="perimeters") router.register(r"tenders/kinds", TenderKindViewSet, basename="tender-kinds") router.register(r"tenders/amounts", TenderAmountViewSet, basename="tender-amounts") diff --git a/lemarche/static/js/city_autocomplete_field.js b/lemarche/static/js/city_autocomplete_field.js deleted file mode 100644 index 462316ca0..000000000 --- a/lemarche/static/js/city_autocomplete_field.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Accessible autocomplete for the city search form field - * https://alphagov.github.io/accessible-autocomplete/examples/ - * - * CityAutocomplete: - * - single city - */ -var API_ENDPOINT = '/api/cities/autocomplete/'; - -async function fetchSource(query) { - const res = await fetch(`${API_ENDPOINT}?q=${query}&results=10`); - const data = await res.json(); - return data; // data.results -} - -class CityAutocomplete { - constructor(city_container_name, city_input_id) { - this.city_container_name= city_container_name; - this.city_input_id= city_input_id; - this.city_name_input_id= `${this.city_input_id}_name`; - this.cityAutocompleteContainer = document.getElementById(city_container_name); - this.cityInput = document.getElementById(city_input_id); // hidden - this.initial_value_name = this.cityAutocompleteContainer.dataset.inputValue; - this.isInit = false; - } - - init() { - if(!this.isInit) { - this.isInit = true; - accessibleAutocomplete({ - element: this.cityAutocompleteContainer, - id: this.city_name_input_id, - name: this.city_name_input_id, // url GET param name - placeholder: 'Ville', - minLength: 2, - defaultValue: this.initial_value_name, - source: this.getSource, - displayMenu: 'overlay', - templates: { - inputValue: this.inputValue, // returns the string value to be inserted into the input - suggestion: this.inputValue, // used when rendering suggestions, and should return a string, which can contain HTML - }, - autoselect: true, - onConfirm: (confirmed) => { - this.inputValueHiddenField(confirmed); - }, - showNoOptionsFound: false, - // Internationalization - tNoResults: this.tNoResults, - tStatusQueryTooShort: this.tStatusQueryTooShort, - tStatusNoResults: this.tStatusNoResults, - tStatusSelectedOption: this.tStatusSelectedOption, - }); - - // after creation of input autocomplete, we set the div as attribute - this.cityInputName = document.getElementById(this.city_name_input_id); - } - } - - tNoResults = () => 'Aucun résultat' - tStatusQueryTooShort = (minQueryLength) => `Tapez au moins ${minQueryLength} caractères pour avoir des résultats` - tStatusNoResults = () => 'Aucun résultat pour cette recherche' - tStatusSelectedOption = (selectedOption, length, index) => `${selectedOption} ${index + 1} de ${length} est sélectionnée` - - async getSource(query, populateResults) { - const res = await fetchSource(query); - populateResults(res); - } - - inputValue(result) { - // strip html from suggestion - if(!result) { - return result; - } - let resultName, resultKind = ''; - - // build resultName & resultKind from the result object - if (typeof result === 'object') { - resultName = result.name; - resultKind = result.department_code; - } - - // Edge case: if there is an initial value - // reconstruct resultName & resultKind from the result string - if (typeof result === 'string') { - resultName = result.substring(0, result.lastIndexOf(' ')); - resultKind = result.includes('(') ? result.substring(result.lastIndexOf(' ') + 2, result.length - 1) : ''; - } - - let nameWithKind = '' + resultName + ''; - if (resultKind) { - nameWithKind += ' (' + resultKind + ')'; - } - return nameWithKind.replace(/(<([^>]+)>)/gi, ''); - } - - inputValueHiddenField(result) { - // we want to avoid clicks outside that return 'undefined' - if (result) { - if (typeof result === 'object') { - this.cityInput.value = result.slug; - } - } - } - - resetInputValueHiddenField() { - this.cityInput.value = ''; - } - - cleanCity() { - this.cityInputName.value =''; - this.cityInput.value =''; - } - -} diff --git a/lemarche/static/js/perimeter_autocomplete_field.js b/lemarche/static/js/perimeter_autocomplete_field.js index ea7b634ea..54176ec56 100644 --- a/lemarche/static/js/perimeter_autocomplete_field.js +++ b/lemarche/static/js/perimeter_autocomplete_field.js @@ -29,16 +29,18 @@ const debounce = (callback, wait) => { }; } -async function fetchSource(query) { - const res = await fetch(`${API_ENDPOINT}?q=${query}&results=10`); +async function fetchSource(query, kind = undefined) { + const res = await fetch(`${API_ENDPOINT}?q=${query}&results=10${kind ? `&kind=${kind}` : ''}`); const data = await res.json(); return data; // data.results } class PerimeterAutocomplete { - constructor(perimeter_container_name, perimeter_input_id) { + constructor(perimeter_container_name, perimeter_input_id, perimeter_placeholder='Région, ville…', perimeter_kind = undefined) { this.perimeter_container_name= perimeter_container_name; this.perimeter_input_id= perimeter_input_id; + this.perimeter_kind= perimeter_kind; + this.perimeter_placeholder= perimeter_placeholder; this.perimeter_name_input_id= `${this.perimeter_input_id}_name`; this.perimeterAutocompleteContainer = document.getElementById(perimeter_container_name); this.perimeterInput = document.getElementById(perimeter_input_id); // hidden @@ -53,10 +55,10 @@ class PerimeterAutocomplete { element: this.perimeterAutocompleteContainer, id: this.perimeter_name_input_id, name: this.perimeter_name_input_id, // url GET param name - placeholder: 'Région, ville…', // 'Autour de (Arras, Bobigny, Strasbourg…)', 'Région, département, ville' + placeholder: this.perimeter_placeholder, minLength: 2, defaultValue: this.initial_value_name, - source: this.getSource, + source: this.perimeter_kind === 'CITY' ? this.getSourceCity : this.getSource, displayMenu: 'overlay', templates: { inputValue: this.inputValue, // returns the string value to be inserted into the input @@ -89,6 +91,11 @@ class PerimeterAutocomplete { populateResults(res); } + async getSourceCity(query, populateResults) { + const res = await fetchSource(query, "CITY"); + populateResults(res); + } + inputValue(result) { // strip html from suggestion if(!result) { @@ -220,7 +227,7 @@ class PerimetersMultiAutocomplete { inputValue(result) { return ""; } - + inputValueHiddenField(result) { // we want to avoid clicks outside that return 'undefined' if (result) { @@ -239,20 +246,20 @@ class PerimetersMultiAutocomplete { suggestion(result) { // display suggestions as `name (kind)` let resultName, resultKind = ''; - + // build resultName & resultKind from the result object if (typeof result === 'object') { resultName = result.name; resultKind = (result.kind === 'CITY') ? result.department_code : KIND_MAPPING[result.kind]; } - + // Edge case: if there is an initial value // reconstruct resultName & resultKind from the result string if (typeof result === 'string') { resultName = result.substring(0, result.lastIndexOf(' ')); resultKind = result.includes('(') ? result.substring(result.lastIndexOf(' ') + 2, result.length - 1) : ''; } - + let nameWithKind = '' + resultName + ''; if (resultKind) { nameWithKind += ' (' + resultKind + ')'; @@ -266,7 +273,7 @@ class PerimetersMultiAutocomplete { $(`#${idRefInput}`).remove(); $(this).remove(); } - + createHiddenInputPerimeter(resultId, resultName) { let removeIcon = $('', { class: "ri-close-line font-weight-bold mr-0", "aria-hidden": true }); let resultIdString = `${this.perimeter_hidden_input_selector_prefix}-${resultId}`; diff --git a/lemarche/templates/siaes/search_results.html b/lemarche/templates/siaes/search_results.html index 4fd1b35be..f6a0c7d3d 100644 --- a/lemarche/templates/siaes/search_results.html +++ b/lemarche/templates/siaes/search_results.html @@ -310,7 +310,6 @@

{% block extra_js %} -