From 50c0955465810c9f4b5edded18250921938c119d Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Sun, 20 Sep 2020 13:18:36 +0200 Subject: [PATCH 01/21] =?UTF-8?q?Avoid=20an=20error=20if=20area=20can?= =?UTF-8?q?=E2=80=99t=20be=20computed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/helpers/champ_helper.rb | 16 ++++++++++++---- app/models/geo_area.rb | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/helpers/champ_helper.rb b/app/helpers/champ_helper.rb index 2af656d69..a9c25965b 100644 --- a/app/helpers/champ_helper.rb +++ b/app/helpers/champ_helper.rb @@ -45,12 +45,20 @@ module ChampHelper "Culture : #{geo_area.culture} - Surface : #{geo_area.surface} ha" when GeoArea.sources.fetch(:selection_utilisateur) if geo_area.polygon? - capture do - concat "Une aire de surface #{geo_area.area} m" - concat content_tag(:sup, "2") + if geo_area.area.present? + capture do + concat "Une aire de surface #{geo_area.area} m" + concat content_tag(:sup, "2") + end + else + "Une aire de surface inconnue" end elsif geo_area.line? - "Une ligne longue de #{geo_area.length} m" + if geo_area.length.present? + "Une ligne longue de #{geo_area.length} m" + else + "Une ligne de longueur inconnue" + end elsif geo_area.point? "Un point situé à #{geo_area.location}" end diff --git a/app/models/geo_area.rb b/app/models/geo_area.rb index 3682dc8d8..9bc30fc67 100644 --- a/app/models/geo_area.rb +++ b/app/models/geo_area.rb @@ -79,13 +79,13 @@ class GeoArea < ApplicationRecord def area if polygon? && RGeo::Geos.supported? - rgeo_geometry.area.round(1) + rgeo_geometry.area&.round(1) end end def length if line? && RGeo::Geos.supported? - rgeo_geometry.length.round(1) + rgeo_geometry.length&.round(1) end end From f71d2a608e029418daa6bc63cdb7e9221584752a Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Sun, 20 Sep 2020 13:18:53 +0200 Subject: [PATCH 02/21] Fix MultiLineString geometries --- ...20200813111957_fix_geo_areas_geometry.rake | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/tasks/deployment/20200813111957_fix_geo_areas_geometry.rake b/lib/tasks/deployment/20200813111957_fix_geo_areas_geometry.rake index 1b8e162ab..3969494b9 100644 --- a/lib/tasks/deployment/20200813111957_fix_geo_areas_geometry.rake +++ b/lib/tasks/deployment/20200813111957_fix_geo_areas_geometry.rake @@ -5,6 +5,7 @@ namespace :after_party do geometry_collections = GeoArea.where("geometry -> 'type' = '\"GeometryCollection\"'") multi_polygons = GeoArea.where("geometry -> 'type' = '\"MultiPolygon\"'") + multi_line_strings = GeoArea.where("geometry -> 'type' = '\"MultiLineString\"'") def valid_geometry?(geometry) RGeo::GeoJSON.decode(geometry.to_json, geo_factory: RGeo::Geographic.simple_mercator_factory) @@ -26,6 +27,24 @@ namespace :after_party do end progress.finish + progress = ProgressReport.new(multi_line_strings.count) + multi_line_strings.find_each do |multi_line_string| + multi_line_string.geometry['coordinates'].each do |coordinates| + geometry = { + type: 'LineString', + coordinates: coordinates + } + + if valid_geometry?(geometry) + multi_line_string.champ.geo_areas.create!(geometry: geometry, source: 'selection_utilisateur') + end + end + + multi_line_string.destroy + progress.inc + end + progress.finish + progress = ProgressReport.new(multi_polygons.count) multi_polygons.find_each do |multi_polygon| multi_polygon.geometry['coordinates'].each do |coordinates| From 512cdeb6ffaa0eb554d2dee193731aef9ecd80e1 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Sun, 20 Sep 2020 13:19:09 +0200 Subject: [PATCH 03/21] normalize features on import --- app/javascript/components/MapEditor/index.js | 23 ++---- app/javascript/components/MapEditor/utils.js | 80 ++++++++++++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/app/javascript/components/MapEditor/index.js b/app/javascript/components/MapEditor/index.js index c025f5fb3..d3ab35a1c 100644 --- a/app/javascript/components/MapEditor/index.js +++ b/app/javascript/components/MapEditor/index.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types'; import mapboxgl from 'mapbox-gl'; import ReactMapboxGl, { GeoJSONLayer, ZoomControl } from 'react-mapbox-gl'; import DrawControl from 'react-mapbox-gl-draw'; -import { gpx, kml } from '@tmcw/togeojson/dist/togeojson.es.js'; import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; import { getJSON, ajax, fire } from '@utils'; @@ -11,7 +10,11 @@ import { getJSON, ajax, fire } from '@utils'; import { getMapStyle, SwitchMapStyle } from '../MapStyles'; import SearchInput from './SearchInput'; -import { polygonCadastresFill, polygonCadastresLine } from './utils'; +import { + polygonCadastresFill, + polygonCadastresLine, + readGeoFile +} from './utils'; import { noop, filterFeatureCollection, @@ -149,19 +152,7 @@ function MapEditor({ featureCollection, url, preview, options }) { } const onFileImport = (e, inputId) => { - const isGpxFile = e.target.files[0].name.includes('.gpx'); - let reader = new FileReader(); - reader.readAsText(e.target.files[0], 'UTF-8'); - reader.onload = async (event) => { - let featureCollection; - isGpxFile - ? (featureCollection = gpx( - new DOMParser().parseFromString(event.target.result, 'text/xml') - )) - : (featureCollection = kml( - new DOMParser().parseFromString(event.target.result, 'text/xml') - )); - + readGeoFile(e.target.files[0]).then(async (featureCollection) => { const resultFeatureCollection = await getJSON( `${url}/import`, featureCollection, @@ -196,7 +187,7 @@ function MapEditor({ featureCollection, url, preview, options }) { updateFeaturesList(resultFeatureCollection.features); setImportInputs(setInputs); setBbox(resultFeatureCollection.bbox); - }; + }); }; const addInputFile = (e) => { diff --git a/app/javascript/components/MapEditor/utils.js b/app/javascript/components/MapEditor/utils.js index 30311a836..955c92859 100644 --- a/app/javascript/components/MapEditor/utils.js +++ b/app/javascript/components/MapEditor/utils.js @@ -1,3 +1,5 @@ +import { gpx, kml } from '@tmcw/togeojson/dist/togeojson.es.js'; + export const polygonCadastresFill = { 'fill-color': '#EC3323', 'fill-opacity': 0.3 @@ -8,3 +10,81 @@ export const polygonCadastresLine = { 'line-width': 4, 'line-dasharray': [1, 1] }; + +export function normalizeFeatureCollection(featureCollection) { + const features = []; + for (const feature of featureCollection.features) { + switch (feature.geometry.type) { + case 'MultiPoint': + for (const coordinates of feature.geometry.coordinates) { + features.push({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates + }, + properties: feature.properties + }); + } + break; + case 'MultiLineString': + for (const coordinates of feature.geometry.coordinates) { + features.push({ + type: 'Feature', + geometry: { + type: 'LineString', + coordinates + }, + properties: feature.properties + }); + } + break; + case 'MultiPolygon': + for (const coordinates of feature.geometry.coordinates) { + features.push({ + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates + }, + properties: feature.properties + }); + } + break; + case 'GeometryCollection': + for (const geometry of feature.geometry.geometries) { + features.push({ + type: 'Feature', + geometry, + properties: feature.properties + }); + } + break; + default: + features.push(feature); + } + } + + featureCollection.features = features; + return featureCollection; +} + +export function readGeoFile(file) { + const isGpxFile = file.name.includes('.gpx'); + const reader = new FileReader(); + + return new Promise((resolve) => { + reader.onload = (event) => { + const xml = new DOMParser().parseFromString( + event.target.result, + 'text/xml' + ); + const featureCollection = normalizeFeatureCollection( + isGpxFile ? gpx(xml) : kml(xml) + ); + + resolve(featureCollection); + }; + reader.readAsText(file, 'UTF-8'); + }); +} From 4c17acf349366d2d63c796f5ac0dea60749ec0b7 Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 21 Sep 2020 20:38:05 +0200 Subject: [PATCH 04/21] ajout du code effectif entreprise dans une spec graphql nominale --- spec/controllers/api/v2/graphql_controller_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index b1c200c2f..acddbc61b 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -400,6 +400,7 @@ describe API::V2::GraphqlController do siren dateCreation capitalSocial + codeEffectifEntreprise } } } @@ -423,7 +424,8 @@ describe API::V2::GraphqlController do entreprise: { siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, - capitalSocial: dossier.etablissement.entreprise_capital_social.to_s + capitalSocial: dossier.etablissement.entreprise_capital_social.to_s, + codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s } } }) From adb2a916405971fe4e8a1c224e62f4ba98676c3f Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 21 Sep 2020 20:39:54 +0200 Subject: [PATCH 05/21] add a broken test case --- .../api/v2/graphql_controller_spec.rb | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index acddbc61b..b987c5f53 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -407,28 +407,58 @@ describe API::V2::GraphqlController do } }" end - - it "should be returned" do - expect(gql_errors).to eq(nil) - expect(gql_data).to eq(dossier: { - id: dossier.to_typed_id, - number: dossier.id, - usager: { - id: dossier.user.to_typed_id, - email: dossier.user.email - }, - demandeur: { - id: dossier.etablissement.to_typed_id, - siret: dossier.etablissement.siret, - siegeSocial: dossier.etablissement.siege_social, - entreprise: { - siren: dossier.etablissement.entreprise_siren, - dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, - capitalSocial: dossier.etablissement.entreprise_capital_social.to_s, - codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s + context "in the nominal case" do + it "should be returned" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossier: { + id: dossier.to_typed_id, + number: dossier.id, + usager: { + id: dossier.user.to_typed_id, + email: dossier.user.email + }, + demandeur: { + id: dossier.etablissement.to_typed_id, + siret: dossier.etablissement.siret, + siegeSocial: dossier.etablissement.siege_social, + entreprise: { + siren: dossier.etablissement.entreprise_siren, + dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, + capitalSocial: dossier.etablissement.entreprise_capital_social.to_s, + codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s + } } - } - }) + }) + end + end + + context "when there are missing data" do + before do + dossier.etablissement.update!(entreprise_code_effectif_entreprise: nil, entreprise_capital_social: nil) + end + + it "should be returned" do + expect(gql_errors).to eq(nil) + expect(gql_data).to eq(dossier: { + id: dossier.to_typed_id, + number: dossier.id, + usager: { + id: dossier.user.to_typed_id, + email: dossier.user.email + }, + demandeur: { + id: dossier.etablissement.to_typed_id, + siret: dossier.etablissement.siret, + siegeSocial: dossier.etablissement.siege_social, + entreprise: { + siren: dossier.etablissement.entreprise_siren, + dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, + capitalSocial: '', + codeEffectifEntreprise: '' + } + } + }) + end end end end From 1173f1e459d8c1f7b1230dbedfd12a3ece52d335 Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Mon, 21 Sep 2020 21:16:58 +0200 Subject: [PATCH 06/21] add default values when nil --- app/graphql/types/personne_morale_type.rb | 12 +++++++++++- spec/controllers/api/v2/graphql_controller_spec.rb | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/graphql/types/personne_morale_type.rb b/app/graphql/types/personne_morale_type.rb index 769554ce6..40b243d0a 100644 --- a/app/graphql/types/personne_morale_type.rb +++ b/app/graphql/types/personne_morale_type.rb @@ -14,7 +14,7 @@ module Types field :nom_commercial, String, null: false field :raison_sociale, String, null: false field :siret_siege_social, String, null: false - field :code_effectif_entreprise, String, null: false + field :code_effectif_entreprise, String, null: true field :effectif_mensuel, EffectifType, null: true, description: "effectif pour un mois donné" field :effectif_annuel, EffectifType, null: true, description: "effectif moyen d'une année" field :date_creation, GraphQL::Types::ISO8601Date, null: false @@ -41,6 +41,16 @@ module Types end end + def capital_social + # capital_social is defined as a BigInt, so we can't return an empty string when value is unknown + # 0 could appear to be a legitimate value, so a negative value helps to ensure the value is not known + object.capital_social || '-1' + end + + def code_effectif_entreprise + object.code_effectif_entreprise || '' + end + def effectif_annuel if object.effectif_annuel.present? { diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index b987c5f53..8c6fae99e 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -453,7 +453,7 @@ describe API::V2::GraphqlController do entreprise: { siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, - capitalSocial: '', + capitalSocial: '-1', codeEffectifEntreprise: '' } } From 412a87e532a5a81813abf59c1fe13def7f782621 Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Tue, 22 Sep 2020 09:26:46 +0200 Subject: [PATCH 07/21] add fix for nil values on numeroVoie and typeVoie --- app/graphql/types/personne_morale_type.rb | 9 +++++---- spec/controllers/api/v2/graphql_controller_spec.rb | 11 +++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/graphql/types/personne_morale_type.rb b/app/graphql/types/personne_morale_type.rb index 40b243d0a..bef7c6d02 100644 --- a/app/graphql/types/personne_morale_type.rb +++ b/app/graphql/types/personne_morale_type.rb @@ -7,7 +7,7 @@ module Types end field :siren, String, null: false - field :capital_social, GraphQL::Types::BigInt, null: false + field :capital_social, GraphQL::Types::BigInt, null: false, description: "capital social de l’entreprise. -1 si inconnu." field :numero_tva_intracommunautaire, String, null: false field :forme_juridique, String, null: false field :forme_juridique_code, String, null: false @@ -48,7 +48,8 @@ module Types end def code_effectif_entreprise - object.code_effectif_entreprise || '' + # we need this in order to bypass Hashie::Dash deserialization issue on nil values + object.code_effectif_entreprise end def effectif_annuel @@ -86,8 +87,8 @@ module Types field :naf, String, null: false field :libelle_naf, String, null: false field :adresse, String, null: false - field :numero_voie, String, null: false - field :type_voie, String, null: false + field :numero_voie, String, null: true + field :type_voie, String, null: true field :nom_voie, String, null: false field :complement_adresse, String, null: false field :code_postal, String, null: false diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index 8c6fae99e..d1dffa0e0 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -396,6 +396,8 @@ describe API::V2::GraphqlController do ... on PersonneMorale { siret siegeSocial + numeroVoie + typeVoie entreprise { siren dateCreation @@ -421,11 +423,13 @@ describe API::V2::GraphqlController do id: dossier.etablissement.to_typed_id, siret: dossier.etablissement.siret, siegeSocial: dossier.etablissement.siege_social, + numeroVoie: dossier.etablissement.numero_voie.to_s, + typeVoie: dossier.etablissement.type_voie.to_s, entreprise: { siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, capitalSocial: dossier.etablissement.entreprise_capital_social.to_s, - codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s + codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s, } } }) @@ -434,7 +438,8 @@ describe API::V2::GraphqlController do context "when there are missing data" do before do - dossier.etablissement.update!(entreprise_code_effectif_entreprise: nil, entreprise_capital_social: nil) + dossier.etablissement.update!(entreprise_code_effectif_entreprise: nil, entreprise_capital_social: nil, + numero_voie: nil, type_voie: nil) end it "should be returned" do @@ -450,6 +455,8 @@ describe API::V2::GraphqlController do id: dossier.etablissement.to_typed_id, siret: dossier.etablissement.siret, siegeSocial: dossier.etablissement.siege_social, + numeroVoie: nil, + typeVoie: nil, entreprise: { siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, From 7ba8ab9e6a32dcf74c49207c839994bf88b9b43e Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Tue, 22 Sep 2020 09:30:28 +0200 Subject: [PATCH 08/21] maj du schema json --- app/graphql/schema.graphql | 10 +++++++--- spec/controllers/api/v2/graphql_controller_spec.rb | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index a81e39392..bf39ff900 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -713,8 +713,12 @@ type Effectif { type Entreprise { attestationFiscaleAttachment: File attestationSocialeAttachment: File + + """ + capital social de l’entreprise. -1 si inconnu. + """ capitalSocial: BigInt! - codeEffectifEntreprise: String! + codeEffectifEntreprise: String dateCreation: ISO8601Date! """ @@ -1011,10 +1015,10 @@ type PersonneMorale implements Demandeur { localite: String! naf: String! nomVoie: String! - numeroVoie: String! + numeroVoie: String siegeSocial: Boolean! siret: String! - typeVoie: String! + typeVoie: String } type PersonnePhysique implements Demandeur { diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index d1dffa0e0..33711e8b1 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -461,7 +461,7 @@ describe API::V2::GraphqlController do siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, capitalSocial: '-1', - codeEffectifEntreprise: '' + codeEffectifEntreprise: nil } } }) From 005c244438fc0a224483f932e2027053ef6f3c3b Mon Sep 17 00:00:00 2001 From: clemkeirua Date: Tue, 22 Sep 2020 10:17:54 +0200 Subject: [PATCH 09/21] fixup! add fix for nil values on numeroVoie and typeVoie --- spec/controllers/api/v2/graphql_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index 33711e8b1..feaf61eb2 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -429,7 +429,7 @@ describe API::V2::GraphqlController do siren: dossier.etablissement.entreprise_siren, dateCreation: dossier.etablissement.entreprise_date_creation.iso8601, capitalSocial: dossier.etablissement.entreprise_capital_social.to_s, - codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s, + codeEffectifEntreprise: dossier.etablissement.entreprise_code_effectif_entreprise.to_s } } }) From a911a71db95c5651594d69c4f71b082e46aa3890 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 9 Sep 2020 15:04:58 +0200 Subject: [PATCH 10/21] Log dossier archiver/desarchiver operations --- app/controllers/instructeurs/dossiers_controller.rb | 4 ++-- app/models/dossier.rb | 10 ++++++++++ app/models/dossier_operation_log.rb | 4 +++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 78529a9ba..85bb998bf 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -97,12 +97,12 @@ module Instructeurs end def archive - dossier.update(archived: true) + dossier.archiver!(current_instructeur) redirect_back(fallback_location: instructeur_procedures_url) end def unarchive - dossier.update(archived: false) + dossier.desarchiver!(current_instructeur) redirect_back(fallback_location: instructeur_procedures_url) end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 9fe7a7242..a2c69c6f4 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -416,6 +416,16 @@ class Dossier < ApplicationRecord end end + def archiver!(author) + update!(archived: true) + log_dossier_operation(author, :archiver) + end + + def desarchiver!(author) + update!(archived: false) + log_dossier_operation(author, :desarchiver) + end + def text_summary if brouillon? parts = [ diff --git a/app/models/dossier_operation_log.rb b/app/models/dossier_operation_log.rb index 301dd1b3a..f52254f31 100644 --- a/app/models/dossier_operation_log.rb +++ b/app/models/dossier_operation_log.rb @@ -26,7 +26,9 @@ class DossierOperationLog < ApplicationRecord supprimer: 'supprimer', restaurer: 'restaurer', modifier_annotation: 'modifier_annotation', - demander_un_avis: 'demander_un_avis' + demander_un_avis: 'demander_un_avis', + archiver: 'archiver', + desarchiver: 'desarchiver' } has_one_attached :serialized From 061a74375996344601c9fb498350f21bfb4ef78d Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 19 Aug 2020 10:42:42 +0200 Subject: [PATCH 11/21] [GraphQL] Add archiver mutation --- app/graphql/mutations/dossier_archiver.rb | 25 +++++++++++ app/graphql/schema.graphql | 37 ++++++++++++++++ app/graphql/types/mutation_type.rb | 1 + .../api/v2/graphql_controller_spec.rb | 42 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 app/graphql/mutations/dossier_archiver.rb diff --git a/app/graphql/mutations/dossier_archiver.rb b/app/graphql/mutations/dossier_archiver.rb new file mode 100644 index 000000000..aad9e79e0 --- /dev/null +++ b/app/graphql/mutations/dossier_archiver.rb @@ -0,0 +1,25 @@ +module Mutations + class DossierArchiver < Mutations::BaseMutation + description "Archiver le dossier." + + argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType + argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + + field :dossier, Types::DossierType, null: true + field :errors, [Types::ValidationErrorType], null: true + + def resolve(dossier:, instructeur:) + if dossier.termine? + dossier.archiver!(instructeur) + + { dossier: dossier } + else + { errors: ["Un dossier ne peut être archivé qu’une fois le traitement terminé"] } + end + end + + def authorized?(dossier:, instructeur:) + instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id) + end + end +end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index bf39ff900..e8cad479c 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -457,6 +457,38 @@ type DossierAccepterPayload { errors: [ValidationError!] } +""" +Autogenerated input type of DossierArchiver +""" +input DossierArchiverInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + Dossier ID + """ + dossierId: ID! + + """ + Instructeur qui prend la décision sur le dossier. + """ + instructeurId: ID! +} + +""" +Autogenerated return type of DossierArchiver +""" +type DossierArchiverPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + dossier: Dossier + errors: [ValidationError!] +} + """ Autogenerated input type of DossierChangerGroupeInstructeur """ @@ -925,6 +957,11 @@ type Mutation { """ dossierAccepter(input: DossierAccepterInput!): DossierAccepterPayload + """ + Archiver le dossier. + """ + dossierArchiver(input: DossierArchiverInput!): DossierArchiverPayload + """ Changer le grope instructeur du dossier. """ diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 1a0e8e32f..1651ed5fd 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -7,6 +7,7 @@ module Types field :dossier_classer_sans_suite, mutation: Mutations::DossierClasserSansSuite field :dossier_refuser, mutation: Mutations::DossierRefuser field :dossier_accepter, mutation: Mutations::DossierAccepter + field :dossier_archiver, mutation: Mutations::DossierArchiver field :dossier_changer_groupe_instructeur, mutation: Mutations::DossierChangerGroupeInstructeur end end diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index feaf61eb2..02aa269b5 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -885,6 +885,48 @@ describe API::V2::GraphqlController do end end end + + describe 'dossierArchiver' do + let(:query) do + "mutation { + dossierArchiver(input: { + dossierId: \"#{dossier.to_typed_id}\", + instructeurId: \"#{instructeur.to_typed_id}\" + }) { + dossier { + archived + } + errors { + message + } + } + }" + end + + it "validation error" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierArchiver: { + dossier: nil, + errors: [{ message: "Un dossier ne peut être archivé qu’une fois le traitement terminé" }] + }) + end + + context "should archive dossier" do + let(:dossier) { create(:dossier, :sans_suite, :with_individual, procedure: procedure) } + + it "change made" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierArchiver: { + dossier: { + archived: true + }, + errors: nil + }) + end + end + end end end From a3f3b8a8e0f1da5f998710da42269baf64721f92 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 17 Sep 2020 11:15:21 +0200 Subject: [PATCH 12/21] procedure.dossiers through revisions --- app/models/procedure.rb | 7 +++++-- app/models/procedure_presentation.rb | 2 +- app/models/procedure_revision.rb | 2 ++ spec/models/procedure_presentation_spec.rb | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 9f31c345b..af7bc9a9d 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -56,7 +56,7 @@ class Procedure < ApplicationRecord MAX_DUREE_CONSERVATION = 36 MAX_DUREE_CONSERVATION_EXPORT = 3.hours - has_many :revisions, -> { order(:id) }, class_name: 'ProcedureRevision', inverse_of: :procedure, dependent: :destroy + has_many :revisions, -> { order(:id) }, class_name: 'ProcedureRevision', inverse_of: :procedure belongs_to :draft_revision, class_name: 'ProcedureRevision', optional: false belongs_to :published_revision, class_name: 'ProcedureRevision', optional: true has_many :deleted_dossiers, dependent: :destroy @@ -90,7 +90,10 @@ class Procedure < ApplicationRecord has_many :groupe_instructeurs, dependent: :destroy has_many :instructeurs, through: :groupe_instructeurs - has_many :dossiers, through: :groupe_instructeurs, dependent: :restrict_with_exception + # This relationship is used in following dossiers through. We can not use revisions relationship + # as order scope introduces invalid sql in some combinations. + has_many :unordered_revisions, class_name: 'ProcedureRevision', inverse_of: :procedure, dependent: :destroy + has_many :dossiers, through: :unordered_revisions, dependent: :restrict_with_exception has_one :initiated_mail, class_name: "Mails::InitiatedMail", dependent: :destroy has_one :received_mail, class_name: "Mails::ReceivedMail", dependent: :destroy diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index ed086a8c9..64515f196 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -160,7 +160,7 @@ class ProcedurePresentation < ApplicationRecord .filter_ilike(table, column, values) when 'groupe_instructeur' dossiers - .includes(table) + .joins(:groupe_instructeur) .filter_ilike(table, column, values) end.pluck(:id) end.reduce(:&) diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index b97564455..54fe8b729 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -11,6 +11,8 @@ class ProcedureRevision < ApplicationRecord self.implicit_order_column = :created_at belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions, optional: false + has_many :dossiers, inverse_of: :revision, foreign_key: :revision_id + has_many :revision_types_de_champ, -> { public_only.ordered }, class_name: 'ProcedureRevisionTypeDeChamp', foreign_key: :revision_id, dependent: :destroy, inverse_of: :revision has_many :revision_types_de_champ_private, -> { private_only.ordered }, class_name: 'ProcedureRevisionTypeDeChamp', foreign_key: :revision_id, dependent: :destroy, inverse_of: :revision has_many :types_de_champ, through: :revision_types_de_champ, source: :type_de_champ diff --git a/spec/models/procedure_presentation_spec.rb b/spec/models/procedure_presentation_spec.rb index 6f9415bf2..1c118c7ad 100644 --- a/spec/models/procedure_presentation_spec.rb +++ b/spec/models/procedure_presentation_spec.rb @@ -734,7 +734,7 @@ describe ProcedurePresentation do let!(:gi_3) { procedure.groupe_instructeurs.create(label: '3') } let!(:kept_dossier) { create(:dossier, procedure: procedure) } - let!(:discarded_dossier) { create(:dossier, groupe_instructeur: gi_2) } + let!(:discarded_dossier) { create(:dossier, procedure: procedure, groupe_instructeur: gi_2) } it { is_expected.to contain_exactly(kept_dossier.id) } @@ -746,7 +746,7 @@ describe ProcedurePresentation do ] end - let!(:other_kept_dossier) { create(:dossier, groupe_instructeur: gi_3) } + let!(:other_kept_dossier) { create(:dossier, procedure: procedure, groupe_instructeur: gi_3) } it 'returns every dossier that matches any of the search criteria for a given column' do is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id) From 3d3d0259a00431f9be7713e70579bbe2a1ba6220 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 15 Sep 2020 18:53:51 +0200 Subject: [PATCH 13/21] Remove TypeDeChamp.to_stable_id --- .../types_de_champ_controller.rb | 14 ++++---------- app/models/type_de_champ.rb | 15 --------------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/app/controllers/new_administrateur/types_de_champ_controller.rb b/app/controllers/new_administrateur/types_de_champ_controller.rb index 655950b0a..2f31b0118 100644 --- a/app/controllers/new_administrateur/types_de_champ_controller.rb +++ b/app/controllers/new_administrateur/types_de_champ_controller.rb @@ -15,7 +15,7 @@ module NewAdministrateur end def update - type_de_champ = @procedure.draft_revision.find_or_clone_type_de_champ(TypeDeChamp.to_stable_id(params[:id])) + type_de_champ = @procedure.draft_revision.find_or_clone_type_de_champ(params[:id]) if type_de_champ.update(type_de_champ_update_params) reset_procedure @@ -26,13 +26,13 @@ module NewAdministrateur end def move - @procedure.draft_revision.move_type_de_champ(TypeDeChamp.to_stable_id(params[:id]), (params[:position] || params[:order_place]).to_i) + @procedure.draft_revision.move_type_de_champ(params[:id], (params[:position] || params[:order_place]).to_i) head :no_content end def destroy - @procedure.draft_revision.remove_type_de_champ(TypeDeChamp.to_stable_id(params[:id])) + @procedure.draft_revision.remove_type_de_champ(params[:id]) reset_procedure head :no_content @@ -67,7 +67,7 @@ module NewAdministrateur end def type_de_champ_create_params - type_de_champ_params = params.required(:type_de_champ).permit(:type_champ, + params.required(:type_de_champ).permit(:type_champ, :libelle, :description, :mandatory, @@ -77,12 +77,6 @@ module NewAdministrateur :piece_justificative_template, :cadastres, :mnhn) - - if type_de_champ_params[:parent_id].present? - type_de_champ_params[:parent_id] = TypeDeChamp.to_stable_id(type_de_champ_params[:parent_id]) - end - - type_de_champ_params end def type_de_champ_update_params diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 9974ef1f2..f7a44ae6f 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -309,21 +309,6 @@ class TypeDeChamp < ApplicationRecord end end - # FIXME: We are changing how id is exposed to the editor. - # We used to expose type_de_champ.id as primary key to the editor. With revisions - # we need primary key to be type_de_champ.stable_id because any update can create - # a new version but we do not want editor to know about this. - # This is only needed for a clean migration without downtime. We want to ensure - # that if editor send a simple id because it was loaded before deployment - # we would still do the right thing. - def self.to_stable_id(id_or_stable_id) - if id_or_stable_id.to_s =~ /^stable:/ - id_or_stable_id.to_s.gsub(/^stable:/, '') - else - id_or_stable_id - end - end - private def parse_drop_down_list_value(value) From c9f22249911aa867d0ad402f882e72e6652d4c15 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 2 Sep 2020 12:34:43 +0200 Subject: [PATCH 14/21] Remove ignored_columns from type_de_champ --- app/models/type_de_champ.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index f7a44ae6f..7a5835616 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -17,8 +17,6 @@ # stable_id :bigint # class TypeDeChamp < ApplicationRecord - self.ignored_columns = ['procedure_id'] - enum type_champs: { text: 'text', textarea: 'textarea', From 61ea73580f2d4d84dd00b2102d3e70a1bf922f8b Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Sep 2020 20:58:31 +0200 Subject: [PATCH 15/21] use buttons instead of select for contact page --- app/assets/stylesheets/new_design/common.scss | 4 ++ app/lib/helpscout/form_adapter.rb | 8 ++-- app/views/support/index.html.haml | 39 +++++++------------ config/locales/views/support/index.en.yml | 2 +- config/locales/views/support/index.fr.yml | 2 +- 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/new_design/common.scss b/app/assets/stylesheets/new_design/common.scss index 4b6e2f772..8128e3c79 100644 --- a/app/assets/stylesheets/new_design/common.scss +++ b/app/assets/stylesheets/new_design/common.scss @@ -13,6 +13,10 @@ select { line-height: 1.42857143; } +dt { + margin-bottom: 0.5em; +} + .page-wrapper { position: relative; min-height: 100%; diff --git a/app/lib/helpscout/form_adapter.rb b/app/lib/helpscout/form_adapter.rb index d9aaea738..6e4dcdaac 100644 --- a/app/lib/helpscout/form_adapter.rb +++ b/app/lib/helpscout/form_adapter.rb @@ -3,10 +3,10 @@ class Helpscout::FormAdapter def self.options [ - [I18n.t(TYPE_INFO, scope: [:support, :question]), TYPE_INFO], - [I18n.t(TYPE_PERDU, scope: [:support, :question]), TYPE_PERDU], - [I18n.t(TYPE_INSTRUCTION, scope: [:support, :question]), TYPE_INSTRUCTION], - [I18n.t(TYPE_AMELIORATION, scope: [:support, :question]), TYPE_AMELIORATION], + [I18n.t(TYPE_INFO, scope: [:support, :question]), TYPE_INFO, FAQ_CONTACTER_SERVICE_EN_CHARGE_URL], + [I18n.t(TYPE_PERDU, scope: [:support, :question]), TYPE_PERDU, LISTE_DES_DEMARCHES_URL], + [I18n.t(TYPE_INSTRUCTION, scope: [:support, :question]), TYPE_INSTRUCTION, FAQ_OU_EN_EST_MON_DOSSIER_URL], + [I18n.t(TYPE_AMELIORATION, scope: [:support, :question]), TYPE_AMELIORATION, FEATURE_UPVOTE_URL], [I18n.t(TYPE_AUTRE, scope: [:support, :question]), TYPE_AUTRE] ] end diff --git a/app/views/support/index.html.haml b/app/views/support/index.html.haml index 28729416f..15888e09b 100644 --- a/app/views/support/index.html.haml +++ b/app/views/support/index.html.haml @@ -25,31 +25,20 @@ = label_tag :type do = t('your_question', scope: [:support, :question]) %span.mandatory * - = select_tag :type, options_for_select(@options, params[:type]), include_blank: t('choose_question', scope: [:support, :question]), required: true - - .support.card.featured.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_INFO } } - .card-title - = t('our_answer', scope: [:support, :response]) - .card-content - = t('procedure_info_html', scope: [:support, :response], link_procedure_info: FAQ_CONTACTER_SERVICE_EN_CHARGE_URL) - - .support.card.featured.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_PERDU } } - .card-title - = t('our_answer', scope: [:support, :response]) - .card-content - = t('lost_user_html', scope: [:support, :response], base_url: APPLICATION_BASE_URL, link_lost_user: LISTE_DES_DEMARCHES_URL) - - .support.card.featured.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_INSTRUCTION } } - .card-title - = t('our_answer', scope: [:support, :response]) - .card-content - = t('instruction_info_html', scope: [:support, :response], link_instruction: FAQ_OU_EN_EST_MON_DOSSIER_URL) - - .support.card.featured.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } } - .card-title - = t('our_answer', scope: [:support, :response]) - .card-content - = t('product_html', scope: [:support, :response], link_product: FEATURE_UPVOTE_URL) + %dl + - @options.each do |(question, question_type, link)| + %dt + - if link.present? + %button.button{ 'aria-expanded': 'false', 'aria-controls': question_type, data: { 'question-type': question_type } }= question + - else + %button.button.button-without-hint{ data: { 'question-type': question_type } }= question + - if link.present? + %dd + .support.card.featured.hidden{ id: question_type } + .card-title + = t('our_answer', scope: [:support, :response]) + .card-content + = t("#{question_type}_html", scope: [:support, :response], base_url: APPLICATION_BASE_URL, "link_#{question_type}": link) .contact-champ = label_tag :dossier_id, t('file_number', scope: [:utils]) diff --git a/config/locales/views/support/index.en.yml b/config/locales/views/support/index.en.yml index 8d92f06f3..f804c581c 100644 --- a/config/locales/views/support/index.en.yml +++ b/config/locales/views/support/index.en.yml @@ -16,7 +16,7 @@ en:

If you have questions about the information requested, contact the service in charge of the procedure.

Find more information

" instruction_info_html: "

If you have questions about the instruction of your application (response delay for example), contact directly the instructor via our mail system.

-

Find more information

+

Find more information


If you are facing technical issues on the website, use the form below. We will not be able to inform you about the instruction of your application.

" product_html: "

Got an idea? Please check our enhancement dashboard

diff --git a/config/locales/views/support/index.fr.yml b/config/locales/views/support/index.fr.yml index 8c09f38e1..0043277ca 100644 --- a/config/locales/views/support/index.fr.yml +++ b/config/locales/views/support/index.fr.yml @@ -16,7 +16,7 @@ fr:

Si vous avez des questions sur les informations à saisir, contactez les services en charge de la démarche.

En savoir plus

" instruction_info_html: "

Si vous avez des questions sur l’instruction de votre dossier (par exemple sur les délais), nous vous invitons à contacter directement les services qui instruisent votre dossier par votre messagerie.

-

En savoir plus

+

En savoir plus


Si vous souhaitez poser une question pour un problème technique sur le site, utilisez le formulaire ci-dessous. Nous ne pourrons pas vous renseigner sur l'instruction de votre dossier.

" product_html: "

Une idée ? Pensez à consulter notre tableau de bord des améliorations

From df9a15a22490f3b328ce302fba2fb15a6f5984da Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Sep 2020 21:10:23 +0200 Subject: [PATCH 16/21] expand buttons for contact page --- app/javascript/new_design/support.js | 132 ++++++++++++++++++++++++--- 1 file changed, 121 insertions(+), 11 deletions(-) diff --git a/app/javascript/new_design/support.js b/app/javascript/new_design/support.js index 1c637ed88..0ee784058 100644 --- a/app/javascript/new_design/support.js +++ b/app/javascript/new_design/support.js @@ -1,16 +1,126 @@ -import { show, hide, delegate } from '@utils'; +/* +* This content is inspired by w3c aria example +* https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html +*/ -function updateContactElementsVisibility() { - const contactSelect = document.querySelector('#contact-form #type'); - if (contactSelect) { - const type = contactSelect.value; - const visibleElements = `[data-contact-type-only="${type}"]`; - const hiddenElements = `[data-contact-type-only]:not([data-contact-type-only="${type}"])`; +var ButtonExpand = function (domNode) { - document.querySelectorAll(visibleElements).forEach(show); - document.querySelectorAll(hiddenElements).forEach(hide); + this.domNode = domNode; + + this.keyCode = Object.freeze({ + 'RETURN': 13 + }); +}; + +ButtonExpand.prototype.init = function () { + + this.allButtons = []; + this.controlledNode = false; + + var id = this.domNode.getAttribute('aria-controls'); + + if (id) { + this.controlledNode = document.getElementById(id); } + + this.domNode.setAttribute('aria-expanded', 'false'); + this.hideContent(); + + this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); + this.domNode.addEventListener('click', this.handleClick.bind(this)); + this.domNode.addEventListener('focus', this.handleFocus.bind(this)); + this.domNode.addEventListener('blur', this.handleBlur.bind(this)); + +}; + +ButtonExpand.prototype.showContent = function () { + + this.domNode.setAttribute('aria-expanded', 'true'); + this.domNode.classList.add('primary'); + if (this.controlledNode) { + this.controlledNode.classList.remove('hidden'); + + } + + this.allButtons.forEach((b) => { + if (b != this) { + b.hideContent(); + } + }); +}; + +ButtonExpand.prototype.hideContent = function () { + + this.domNode.setAttribute('aria-expanded', 'false'); + this.domNode.classList.remove('primary'); + if (this.controlledNode) { + this.controlledNode.classList.add('hidden'); + } + +}; + +ButtonExpand.prototype.toggleExpand = function () { + console.log("toggleExpanding..."); + + if (this.domNode.getAttribute('aria-expanded') === 'true') { + this.hideContent(); + } + else { + this.showContent(); + } + +}; + +ButtonExpand.prototype.setAllButtons = function(buttons) { + this.allButtons = buttons; } -addEventListener('ds:page:update', updateContactElementsVisibility); -delegate('change', '#contact-form #type', updateContactElementsVisibility); +/* EVENT HANDLERS */ + +ButtonExpand.prototype.handleKeydown = function (event) { + + switch (event.keyCode) { + + case this.keyCode.RETURN: + + this.toggleExpand(); + + event.stopPropagation(); + event.preventDefault(); + break; + + default: + break; + } + +}; + +ButtonExpand.prototype.handleClick = function (event) { + event.stopPropagation(); + event.preventDefault(); + this.toggleExpand(); +}; + +ButtonExpand.prototype.handleFocus = function (event) { + this.domNode.classList.add('focus'); +}; + +ButtonExpand.prototype.handleBlur = function (event) { + this.domNode.classList.remove('focus'); +}; + +/* Initialize Hide/Show Buttons */ + +window.addEventListener('load', function (event) { + + var buttons = document.querySelectorAll('button[aria-expanded][aria-controls], button.button-without-hint'); + var expandButtons = []; + + buttons.forEach((button) => { + var be = new ButtonExpand(button); + be.init(); + expandButtons.push(be); + }); + expandButtons.forEach((button) => button.setAllButtons(expandButtons)); + +}, false); From 9be015752a7df1048df8cf4a68b88264ea15040c Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 16 Sep 2020 21:41:02 +0200 Subject: [PATCH 17/21] set input value for question type of contact page --- app/javascript/new_design/support.js | 7 +++++++ app/views/support/index.html.haml | 1 + 2 files changed, 8 insertions(+) diff --git a/app/javascript/new_design/support.js b/app/javascript/new_design/support.js index 0ee784058..c3b084b17 100644 --- a/app/javascript/new_design/support.js +++ b/app/javascript/new_design/support.js @@ -41,6 +41,7 @@ ButtonExpand.prototype.showContent = function () { this.controlledNode.classList.remove('hidden'); } + this.formInput.value = this.domNode.getAttribute('data-question-type'); this.allButtons.forEach((b) => { if (b != this) { @@ -75,6 +76,10 @@ ButtonExpand.prototype.setAllButtons = function(buttons) { this.allButtons = buttons; } +ButtonExpand.prototype.setFormInput = function(formInput) { + this.formInput = formInput; +} + /* EVENT HANDLERS */ ButtonExpand.prototype.handleKeydown = function (event) { @@ -115,6 +120,7 @@ window.addEventListener('load', function (event) { var buttons = document.querySelectorAll('button[aria-expanded][aria-controls], button.button-without-hint'); var expandButtons = []; + var formInput = document.querySelector('form input#type'); buttons.forEach((button) => { var be = new ButtonExpand(button); @@ -122,5 +128,6 @@ window.addEventListener('load', function (event) { expandButtons.push(be); }); expandButtons.forEach((button) => button.setAllButtons(expandButtons)); + expandButtons.forEach((button) => button.setFormInput(formInput)); }, false); diff --git a/app/views/support/index.html.haml b/app/views/support/index.html.haml index 15888e09b..5f5f23806 100644 --- a/app/views/support/index.html.haml +++ b/app/views/support/index.html.haml @@ -25,6 +25,7 @@ = label_tag :type do = t('your_question', scope: [:support, :question]) %span.mandatory * + = hidden_field_tag :type, params[:type] %dl - @options.each do |(question, question_type, link)| %dt From 5bd4644c2c1c6605e4dea8724f89d3aedb7a3768 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Thu, 17 Sep 2020 09:06:57 +0200 Subject: [PATCH 18/21] fix js lint errors --- app/javascript/new_design/support.js | 79 ++++++++++++---------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/app/javascript/new_design/support.js b/app/javascript/new_design/support.js index c3b084b17..aec653c8f 100644 --- a/app/javascript/new_design/support.js +++ b/app/javascript/new_design/support.js @@ -1,19 +1,17 @@ -/* -* This content is inspired by w3c aria example -* https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html -*/ +// +// This content is inspired by w3c aria example +// https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html +// var ButtonExpand = function (domNode) { - this.domNode = domNode; this.keyCode = Object.freeze({ - 'RETURN': 13 + RETURN: 13 }); }; ButtonExpand.prototype.init = function () { - this.allButtons = []; this.controlledNode = false; @@ -26,20 +24,17 @@ ButtonExpand.prototype.init = function () { this.domNode.setAttribute('aria-expanded', 'false'); this.hideContent(); - this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('focus', this.handleFocus.bind(this)); - this.domNode.addEventListener('blur', this.handleBlur.bind(this)); - + this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); + this.domNode.addEventListener('click', this.handleClick.bind(this)); + this.domNode.addEventListener('focus', this.handleFocus.bind(this)); + this.domNode.addEventListener('blur', this.handleBlur.bind(this)); }; ButtonExpand.prototype.showContent = function () { - this.domNode.setAttribute('aria-expanded', 'true'); this.domNode.classList.add('primary'); if (this.controlledNode) { this.controlledNode.classList.remove('hidden'); - } this.formInput.value = this.domNode.getAttribute('data-question-type'); @@ -51,43 +46,34 @@ ButtonExpand.prototype.showContent = function () { }; ButtonExpand.prototype.hideContent = function () { - this.domNode.setAttribute('aria-expanded', 'false'); this.domNode.classList.remove('primary'); if (this.controlledNode) { this.controlledNode.classList.add('hidden'); } - }; ButtonExpand.prototype.toggleExpand = function () { - console.log("toggleExpanding..."); - if (this.domNode.getAttribute('aria-expanded') === 'true') { this.hideContent(); - } - else { + } else { this.showContent(); } - }; -ButtonExpand.prototype.setAllButtons = function(buttons) { +ButtonExpand.prototype.setAllButtons = function (buttons) { this.allButtons = buttons; -} +}; -ButtonExpand.prototype.setFormInput = function(formInput) { +ButtonExpand.prototype.setFormInput = function (formInput) { this.formInput = formInput; -} +}; /* EVENT HANDLERS */ ButtonExpand.prototype.handleKeydown = function (event) { - switch (event.keyCode) { - case this.keyCode.RETURN: - this.toggleExpand(); event.stopPropagation(); @@ -97,7 +83,6 @@ ButtonExpand.prototype.handleKeydown = function (event) { default: break; } - }; ButtonExpand.prototype.handleClick = function (event) { @@ -106,28 +91,32 @@ ButtonExpand.prototype.handleClick = function (event) { this.toggleExpand(); }; -ButtonExpand.prototype.handleFocus = function (event) { +ButtonExpand.prototype.handleFocus = function () { this.domNode.classList.add('focus'); }; -ButtonExpand.prototype.handleBlur = function (event) { +ButtonExpand.prototype.handleBlur = function () { this.domNode.classList.remove('focus'); }; /* Initialize Hide/Show Buttons */ -window.addEventListener('load', function (event) { +window.addEventListener( + 'load', + function () { + var buttons = document.querySelectorAll( + 'button[aria-expanded][aria-controls], button.button-without-hint' + ); + var expandButtons = []; + var formInput = document.querySelector('form input#type'); - var buttons = document.querySelectorAll('button[aria-expanded][aria-controls], button.button-without-hint'); - var expandButtons = []; - var formInput = document.querySelector('form input#type'); - - buttons.forEach((button) => { - var be = new ButtonExpand(button); - be.init(); - expandButtons.push(be); - }); - expandButtons.forEach((button) => button.setAllButtons(expandButtons)); - expandButtons.forEach((button) => button.setFormInput(formInput)); - -}, false); + buttons.forEach((button) => { + var be = new ButtonExpand(button); + be.init(); + expandButtons.push(be); + }); + expandButtons.forEach((button) => button.setAllButtons(expandButtons)); + expandButtons.forEach((button) => button.setFormInput(formInput)); + }, + false +); From 8baaee8810b7bc695bcea86a0cf24ea9c73050b5 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Thu, 17 Sep 2020 13:23:34 +0200 Subject: [PATCH 19/21] load expanded buttons only for contact form --- app/javascript/new_design/support.js | 38 +++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/app/javascript/new_design/support.js b/app/javascript/new_design/support.js index aec653c8f..3a2d812e5 100644 --- a/app/javascript/new_design/support.js +++ b/app/javascript/new_design/support.js @@ -101,22 +101,24 @@ ButtonExpand.prototype.handleBlur = function () { /* Initialize Hide/Show Buttons */ -window.addEventListener( - 'load', - function () { - var buttons = document.querySelectorAll( - 'button[aria-expanded][aria-controls], button.button-without-hint' - ); - var expandButtons = []; - var formInput = document.querySelector('form input#type'); +if (document.querySelector('#contact-form')) { + window.addEventListener( + 'load', + function () { + var buttons = document.querySelectorAll( + 'button[aria-expanded][aria-controls], button.button-without-hint' + ); + var expandButtons = []; + var formInput = document.querySelector('form input#type'); - buttons.forEach((button) => { - var be = new ButtonExpand(button); - be.init(); - expandButtons.push(be); - }); - expandButtons.forEach((button) => button.setAllButtons(expandButtons)); - expandButtons.forEach((button) => button.setFormInput(formInput)); - }, - false -); + buttons.forEach((button) => { + var be = new ButtonExpand(button); + be.init(); + expandButtons.push(be); + }); + expandButtons.forEach((button) => button.setAllButtons(expandButtons)); + expandButtons.forEach((button) => button.setFormInput(formInput)); + }, + false + ); +} From 6fd0134b6da10a226e3f784b30f69cb0e4a2b0c5 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 22 Sep 2020 12:14:02 +0200 Subject: [PATCH 20/21] convert to es6 classes --- app/javascript/new_design/support.js | 163 +++++++++++++-------------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/app/javascript/new_design/support.js b/app/javascript/new_design/support.js index 3a2d812e5..8c590ae00 100644 --- a/app/javascript/new_design/support.js +++ b/app/javascript/new_design/support.js @@ -3,107 +3,105 @@ // https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html // -var ButtonExpand = function (domNode) { - this.domNode = domNode; +class ButtonExpand { + constructor(domNode) { + this.domNode = domNode; - this.keyCode = Object.freeze({ - RETURN: 13 - }); -}; + this.keyCode = Object.freeze({ + RETURN: 13 + }); -ButtonExpand.prototype.init = function () { - this.allButtons = []; - this.controlledNode = false; + this.allButtons = []; + this.controlledNode = false; - var id = this.domNode.getAttribute('aria-controls'); + var id = this.domNode.getAttribute('aria-controls'); - if (id) { - this.controlledNode = document.getElementById(id); - } - - this.domNode.setAttribute('aria-expanded', 'false'); - this.hideContent(); - - this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); - this.domNode.addEventListener('click', this.handleClick.bind(this)); - this.domNode.addEventListener('focus', this.handleFocus.bind(this)); - this.domNode.addEventListener('blur', this.handleBlur.bind(this)); -}; - -ButtonExpand.prototype.showContent = function () { - this.domNode.setAttribute('aria-expanded', 'true'); - this.domNode.classList.add('primary'); - if (this.controlledNode) { - this.controlledNode.classList.remove('hidden'); - } - this.formInput.value = this.domNode.getAttribute('data-question-type'); - - this.allButtons.forEach((b) => { - if (b != this) { - b.hideContent(); + if (id) { + this.controlledNode = document.getElementById(id); } - }); -}; -ButtonExpand.prototype.hideContent = function () { - this.domNode.setAttribute('aria-expanded', 'false'); - this.domNode.classList.remove('primary'); - if (this.controlledNode) { - this.controlledNode.classList.add('hidden'); - } -}; - -ButtonExpand.prototype.toggleExpand = function () { - if (this.domNode.getAttribute('aria-expanded') === 'true') { + this.domNode.setAttribute('aria-expanded', 'false'); this.hideContent(); - } else { - this.showContent(); + + this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); + this.domNode.addEventListener('click', this.handleClick.bind(this)); + this.domNode.addEventListener('focus', this.handleFocus.bind(this)); + this.domNode.addEventListener('blur', this.handleBlur.bind(this)); } -}; -ButtonExpand.prototype.setAllButtons = function (buttons) { - this.allButtons = buttons; -}; + showContent() { + this.domNode.setAttribute('aria-expanded', 'true'); + this.domNode.classList.add('primary'); + if (this.controlledNode) { + this.controlledNode.classList.remove('hidden'); + } + this.formInput.value = this.domNode.getAttribute('data-question-type'); -ButtonExpand.prototype.setFormInput = function (formInput) { - this.formInput = formInput; -}; - -/* EVENT HANDLERS */ - -ButtonExpand.prototype.handleKeydown = function (event) { - switch (event.keyCode) { - case this.keyCode.RETURN: - this.toggleExpand(); - - event.stopPropagation(); - event.preventDefault(); - break; - - default: - break; + this.allButtons.forEach((b) => { + if (b != this) { + b.hideContent(); + } + }); } -}; -ButtonExpand.prototype.handleClick = function (event) { - event.stopPropagation(); - event.preventDefault(); - this.toggleExpand(); -}; + hideContent() { + this.domNode.setAttribute('aria-expanded', 'false'); + this.domNode.classList.remove('primary'); + if (this.controlledNode) { + this.controlledNode.classList.add('hidden'); + } + } -ButtonExpand.prototype.handleFocus = function () { - this.domNode.classList.add('focus'); -}; + toggleExpand() { + if (this.domNode.getAttribute('aria-expanded') === 'true') { + this.hideContent(); + } else { + this.showContent(); + } + } -ButtonExpand.prototype.handleBlur = function () { - this.domNode.classList.remove('focus'); -}; + setAllButtons(buttons) { + this.allButtons = buttons; + } + + setFormInput(formInput) { + this.formInput = formInput; + } + + handleKeydown() { + switch (event.keyCode) { + case this.keyCode.RETURN: + this.toggleExpand(); + + event.stopPropagation(); + event.preventDefault(); + break; + + default: + break; + } + } + + handleClick() { + event.stopPropagation(); + event.preventDefault(); + this.toggleExpand(); + } + + handleFocus = function () { + this.domNode.classList.add('focus'); + }; + + handleBlur() { + this.domNode.classList.remove('focus'); + } +} /* Initialize Hide/Show Buttons */ if (document.querySelector('#contact-form')) { window.addEventListener( - 'load', + 'ds:page:update', function () { var buttons = document.querySelectorAll( 'button[aria-expanded][aria-controls], button.button-without-hint' @@ -113,7 +111,6 @@ if (document.querySelector('#contact-form')) { buttons.forEach((button) => { var be = new ButtonExpand(button); - be.init(); expandButtons.push(be); }); expandButtons.forEach((button) => button.setAllButtons(expandButtons)); From 973973ab6a21d09586a71949d796cbee53334cbb Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 22 Sep 2020 17:03:19 +0200 Subject: [PATCH 21/21] Do not enqueue web hooks for empty urls --- app/models/dossier.rb | 2 +- spec/models/dossier_spec.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index a2c69c6f4..f3546e7cb 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -822,7 +822,7 @@ class Dossier < ApplicationRecord end def send_web_hook - if saved_change_to_state? && !brouillon? && procedure.web_hook_url + if saved_change_to_state? && !brouillon? && procedure.web_hook_url.present? WebHookJob.perform_later( procedure, self diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index ff6cffc32..8a6780d37 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -813,6 +813,22 @@ describe Dossier do }.to_not have_enqueued_job(WebHookJob) end + it 'should not call webhook with empty value' do + dossier.procedure.update_column(:web_hook_url, '') + + expect { + dossier.accepte! + }.to_not have_enqueued_job(WebHookJob) + end + + it 'should not call webhook with blank value' do + dossier.procedure.update_column(:web_hook_url, ' ') + + expect { + dossier.accepte! + }.to_not have_enqueued_job(WebHookJob) + end + it 'should call webhook' do dossier.procedure.update_column(:web_hook_url, '/webhook.json')