Make all location champs autocomplete

This commit is contained in:
Paul Chavard 2019-12-18 17:07:41 +01:00
parent 0ef25ef36c
commit 22aa2d4ee0
19 changed files with 187 additions and 61 deletions

View file

@ -242,6 +242,16 @@
}
}
.select2-selection--single {
min-height: 62px;
// scss-lint:disable SelectorFormat
.select2-selection__arrow {
display: none;
}
// scss-lint:enable
}
// scss-lint:disable SelectorFormat
.select2-selection__rendered {
padding: $default-padding;

View file

@ -63,6 +63,10 @@
width: 200px;
margin-right: 10px;
}
.select2-container {
margin-bottom: 0;
}
}
}

View file

@ -117,6 +117,7 @@ class ApplicationController < ActionController::Base
def setup_javascript_settings
gon.autosave = Rails.application.config.ds_autosave
gon.autocomplete = Rails.application.secrets.autocomplete
end
def setup_tracking

View file

@ -1,46 +1,158 @@
import $ from 'jquery';
import 'select2';
const optionTemplate = email =>
const { api_geo_url, api_adresse_url } = gon.autocomplete || {};
const language = {
errorLoading: function() {
return 'Les résultats ne peuvent pas être chargés.';
},
inputTooLong: function(args) {
var overChars = args.input.length - args.maximum;
return 'Supprimez ' + overChars + ' caractère' + (overChars > 1 ? 's' : '');
},
inputTooShort: function(args) {
var remainingChars = args.minimum - args.input.length;
return (
'Saisissez au moins ' +
remainingChars +
' caractère' +
(remainingChars > 1 ? 's' : '')
);
},
loadingMore: function() {
return 'Chargement de résultats supplémentaires…';
},
maximumSelected: function(args) {
return (
'Vous pouvez seulement sélectionner ' +
args.maximum +
' élément' +
(args.maximum > 1 ? 's' : '')
);
},
noResults: function() {
return 'Aucun résultat trouvé';
},
searching: function() {
return 'Recherche en cours…';
},
removeAllItems: function() {
return 'Supprimer tous les éléments';
}
};
const baseOptions = {
language,
width: '100%'
};
const baseAjaxOptions = {
delay: 250,
cache: true,
data({ term: nom }) {
return {
nom,
fields: 'nom,code'
};
},
processResults(data) {
return {
results: data.map(({ nom }) => ({ id: nom, text: nom }))
};
}
};
const regionsOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: { url: `${api_geo_url}/regions`, ...baseAjaxOptions }
};
const communesOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: { url: `${api_geo_url}/communes`, ...baseAjaxOptions }
};
const etranger99 = { id: '99 - Étranger', text: '99 - Étranger' };
const departementsOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: {
...baseAjaxOptions,
url: `${api_geo_url}/departements`,
processResults(data) {
return {
results: data
.map(({ nom, code }) => ({
id: `${code} - ${nom}`,
text: `${code} - ${nom}`
}))
.concat([etranger99])
};
}
}
};
const adresseOptions = {
...baseOptions,
minimumInputLength: 2,
ajax: {
...baseAjaxOptions,
url: `${api_adresse_url}/search`,
data({ term: q }) {
return {
q,
limit: 5
};
},
processResults(data) {
return {
results: data.features.map(({ properties: { label }, geometry }) => ({
id: label,
text: label,
geometry
}))
};
}
}
};
const templateOption = ({ text }) =>
$(
'<span class="custom-select2-option"><span class="icon person"></span>' +
email.text +
'</span>'
`<span class="custom-select2-option"><span class="icon person"></span>${text}</span>`
);
addEventListener('ds:page:update', () => {
$('select.select2').select2({
language: 'fr',
width: '100%'
});
$('select.select2').select2(baseOptions);
$('select.select2.departements').select2(departementsOptions);
$('select.select2.regions').select2(regionsOptions);
$('select.select2.communes').select2(communesOptions);
$('select.select2.adresse').select2(adresseOptions);
$('.columns-form select.select2-limited').select2({
language: 'fr',
width: '300px',
placeholder: 'Sélectionnez des colonnes',
maximumSelectionLength: '5',
width: '300px'
maximumSelectionLength: '5'
});
$('.recipients-form select.select2-limited').select2({
language: 'fr',
language,
width: '300px',
placeholder: 'Sélectionnez des instructeurs',
maximumSelectionLength: '30',
width: '300px'
maximumSelectionLength: '30'
});
$('select.select2-limited.select-instructeurs').select2({
language: 'fr',
language,
dropdownParent: $('.instructeur-wrapper'),
placeholder: 'Saisir ladresse email de linstructeur',
tags: true,
tokenSeparators: [',', ' '],
templateResult: optionTemplate,
templateSelection: function(email) {
return $(
'<span class="custom-select2-option"><span class="icon person"></span>' +
email.text +
'</span>'
);
}
templateResult: templateOption,
templateSelection: templateOption
});
});

View file

@ -8,7 +8,6 @@ import '../shared/page-update-event';
import '../shared/activestorage/ujs';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
import '../shared/autocomplete';
import '../shared/remote-input';
import '../shared/franceconnect';

View file

@ -13,7 +13,6 @@ import '../shared/activestorage/ujs';
import '../shared/activestorage/attachment-checker';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
import '../shared/autocomplete';
import '../shared/remote-input';
import '../shared/franceconnect';
import '../shared/toggle-target';

View file

@ -1,5 +1,6 @@
/* globals FreeDraw L */
import { fire, getJSON, delegate } from '@utils';
import { fire, delegate } from '@utils';
import $ from 'jquery';
import polygonArea from './polygon_area';
@ -113,14 +114,6 @@ function drawParcellesAgricoles(map, { parcellesAgricoles }, editable = false) {
);
}
function geocodeAddress(map, query) {
getJSON('/address/geocode', { request: query }).then(data => {
if (data.lat !== null) {
map.setView(new L.LatLng(data.lat, data.lon), data.zoom);
}
});
}
function getCurrentMap(element) {
if (!element.matches('.carte')) {
const closestCarteElement = element.closest('.carte');
@ -246,10 +239,12 @@ delegate('click', '.toolbar .new-area', event => {
}
});
delegate('autocomplete:select', '.toolbar [data-address]', event => {
$(document).on('select2:select', 'select[data-address]', event => {
const map = getCurrentMap(event.target);
const { geometry } = event.params.data;
if (map) {
geocodeAddress(map, event.detail.label);
if (map && geometry && geometry.type === 'Point') {
const [lon, lat] = geometry.coordinates;
map.setView(new L.LatLng(lat, lon), 14);
}
});

View file

@ -1,5 +1,2 @@
class Champs::DepartementChamp < Champs::TextChamp
def self.departements
ApiGeo::API.departements.map { |liste| "#{liste[:code]} - #{liste[:nom]}" }.push('99 - Étranger')
end
end

View file

@ -1,6 +1,8 @@
class Champs::PaysChamp < Champs::TextChamp
PAYS = JSON.parse(Rails.root.join('app', 'lib', 'api_geo', 'pays.json').read, symbolize_names: true)
def self.pays
ApiGeo::API.pays.pluck(:nom)
PAYS.pluck(:nom)
end
def self.disabled_options

View file

@ -1,5 +1,2 @@
class Champs::RegionChamp < Champs::TextChamp
def self.regions
ApiGeo::API.regions.sort_by { |e| e[:nom] }.pluck(:nom)
end
end

View file

@ -1,4 +1,4 @@
= form.text_field :value,
data: { address: true, autocomplete: 'address' },
placeholder: champ.libelle,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 adresse'

View file

@ -1,6 +1,6 @@
.toolbar
%button.button.primary.new-area Ajouter une zone
%input.address{ data: { address: true, autocomplete: 'address' }, placeholder: 'Saisissez une adresse ou positionner la carte' }
%select.select2.adresse{ data: { address: true }, placeholder: 'Saisissez une adresse ou positionner la carte' }
.carte.edit{ data: { geo: geo_data(champ) }, class: "carte-#{form.index}" }

View file

@ -1,4 +1,4 @@
= form.select :value,
Champs::DepartementChamp.departements,
include_blank: true,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 departements'

View file

@ -1,5 +1,5 @@
= form.select :value,
Champs::PaysChamp.pays,
disabled: Champs::PaysChamp.disabled_options,
include_blank: true,
required: champ.mandatory?
{ disabled: Champs::PaysChamp.disabled_options, include_blank: true },
required: champ.mandatory?,
class: 'select2 pays'

View file

@ -1,4 +1,4 @@
= form.select :value,
Champs::RegionChamp.regions,
include_blank: true,
required: champ.mandatory?
= form.select :value, [champ.value].compact,
{ include_blank: true },
required: champ.mandatory?,
class: 'select2 regions'

View file

@ -105,3 +105,7 @@ LOGRAGE_ENABLED="disabled"
# Service externe d'horodatage des changements de statut des dossiers (effectué quotidiennement)
UNIVERSIGN_API_URL=""
UNIVERSIGN_USERPWD=""
# API Geo / Adresse
API_ADRESSE_URL="https://api-adresse.data.gouv.fr"
API_GEO_URL="https://geo.api.gouv.fr"

View file

@ -5,6 +5,7 @@ Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path
Rails.application.config.assets.paths << Rails.root.join('node_modules', 'trix', 'dist')
Rails.application.config.assets.paths << Rails.root.join('node_modules', 'select2', 'dist', 'css')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.

View file

@ -13,7 +13,7 @@ Rails.application.config.content_security_policy do |policy|
# c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée:
# c'est hardcodé dans les vues, donc pas injectable).
policy.style_src :self, "*.crisp.chat", "crisp.chat", :unsafe_inline
policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io"
policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "geo.api.gouv.fr", "api-adresse.data.gouv.fr"
# Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous
# et dans la notification on inclue la source de l'erreur
policy.default_src :self, :data, :report_sample, "fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "static.demarches-simplifiees.fr", "*.crisp.chat", "crisp.chat", "*.crisp.help", "*.sibautomation.com", "sibautomation.com", "data"

View file

@ -68,6 +68,9 @@ defaults: &defaults
client_key: <%= ENV['CRISP_CLIENT_KEY'] %>
universign:
userpwd: <%= ENV['UNIVERSIGN_USERPWD'] %>
autocomplete:
api_geo_url: <%= ENV['API_GEO_URL'] %>
api_adresse_url: <%= ENV['API_ADRESSE_URL'] %>
@ -94,6 +97,8 @@ test:
logout_endpoint: https://bidon.com/endpoint
universign:
userpwd: 'fake:fake'
autocomplete:
api_geo_url: /test/api_geo
# Do not keep production secrets in the repository,
# instead read values from the environment.