diff --git a/Gemfile.lock b/Gemfile.lock index 143c83271..6fd9b7f5e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -245,7 +245,7 @@ GEM et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.1) geo_coord (0.1.0) - geocoder (1.6.0) + geocoder (1.6.1) globalid (0.4.2) activesupport (>= 4.2.0) gon (6.3.2) diff --git a/app/assets/stylesheets/new_design/carte.scss b/app/assets/stylesheets/new_design/carte.scss new file mode 100644 index 000000000..b23ec84e6 --- /dev/null +++ b/app/assets/stylesheets/new_design/carte.scss @@ -0,0 +1,13 @@ +.areas-title { + font-weight: bold; + margin-top: 5px; + margin-bottom: 5px; +} + +.areas { + margin-bottom: 10px; + + input { + margin-top: 5px; + } +} diff --git a/app/assets/stylesheets/new_design/map.scss b/app/assets/stylesheets/new_design/map.scss deleted file mode 100644 index 588030137..000000000 --- a/app/assets/stylesheets/new_design/map.scss +++ /dev/null @@ -1,80 +0,0 @@ -.carte { - height: 400px; - margin-bottom: 16px; - z-index: 0; -} - -.leaflet-container path { - cursor: url("/assets/edit.png"), default !important; -} - -.carte { - &.edit { - g path.leaflet-polygon { - transition: all 0.25s; - stroke-width: 4px; - stroke-opacity: 1; - stroke: #D7217E; - position: absolute; - z-index: 100; - fill: #D7217E; - fill-opacity: 0.75; - } - - div.leaflet-edge { - box-shadow: 0 0 0 2px #FFFFFF, 0 0 10px rgba(0, 0, 0, 0.35); - border: 5px solid #D7217E; - border-radius: 10px; - transition: opacity 0.25s; - cursor: move; - opacity: 0; - pointer-events: none; - box-sizing: border-box; - width: 0 !important; - height: 0 !important; - } - } - - &.mode-create { - cursor: url("/assets/pencil.png"), crosshair !important; - } - - &.mode-edit div.leaflet-edge { - opacity: 1; - pointer-events: all; - } - - &.mode-delete path.leaflet-polygon { - cursor: no-drop !important; - - &:hover { - fill: #4D4D4D !important; - } - } -} - -.editable-champ-carte { - .toolbar { - display: flex; - align-items: center; - margin-bottom: 10px; - - .button { - width: 200px; - margin-right: 10px; - } - - .select2-container { - margin-bottom: 0; - } - } -} - -.areas-title { - font-weight: bold; - margin-bottom: 5px; -} - -.areas { - margin-bottom: 10px; -} diff --git a/app/assets/stylesheets/new_design/utils.scss b/app/assets/stylesheets/new_design/utils.scss index 9e5b14290..62e44fab0 100644 --- a/app/assets/stylesheets/new_design/utils.scss +++ b/app/assets/stylesheets/new_design/utils.scss @@ -53,3 +53,7 @@ .mt-2 { margin-top: 2 * $default-spacer; } + +.mb-2 { + margin-bottom: 2 * $default-spacer; +} diff --git a/app/controllers/champs/carte_controller.rb b/app/controllers/champs/carte_controller.rb index 6790a9767..fc9a55833 100644 --- a/app/controllers/champs/carte_controller.rb +++ b/app/controllers/champs/carte_controller.rb @@ -19,7 +19,7 @@ class Champs::CarteController < ApplicationController def create champ = policy_scope(Champ).find(params[:champ_id]) geo_area = champ.geo_areas.selections_utilisateur.new - save_geometry!(geo_area, params_feature) + save_feature!(geo_area, params_feature) render json: { feature: geo_area.to_feature }, status: :created end @@ -27,7 +27,7 @@ class Champs::CarteController < ApplicationController def update champ = policy_scope(Champ).find(params[:champ_id]) geo_area = champ.geo_areas.selections_utilisateur.find(params[:id]) - save_geometry!(geo_area, params_feature) + save_feature!(geo_area, params_feature) head :no_content end @@ -43,7 +43,7 @@ class Champs::CarteController < ApplicationController champ = policy_scope(Champ).find(params[:champ_id]) params_features.each do |feature| geo_area = champ.geo_areas.selections_utilisateur.new - save_geometry!(geo_area, feature) + save_feature!(geo_area, feature) end render json: champ.to_feature_collection, status: :created @@ -59,8 +59,13 @@ class Champs::CarteController < ApplicationController params[:features] end - def save_geometry!(geo_area, feature) - geo_area.geometry = feature[:geometry] + def save_feature!(geo_area, feature) + if feature[:geometry] + geo_area.geometry = feature[:geometry] + end + if feature[:properties] && feature[:properties][:description] + geo_area.description = feature[:properties][:description] + end geo_area.save! end diff --git a/app/controllers/champs/siret_controller.rb b/app/controllers/champs/siret_controller.rb index 169134aab..98ebb81ee 100644 --- a/app/controllers/champs/siret_controller.rb +++ b/app/controllers/champs/siret_controller.rb @@ -46,7 +46,7 @@ class Champs::SiretController < ApplicationController @champ = policy_scope(Champ).find(params[:champ_id]) @etablissement = @champ&.etablissement end - @procedure_id = @champ&.dossier&.procedure_id || 'aperçu' + @procedure_id = @champ&.dossier&.procedure&.id || 'aperçu' end def find_etablissement_with_siret diff --git a/app/controllers/instructeurs/recherche_controller.rb b/app/controllers/instructeurs/recherche_controller.rb index 8d14ce2f8..298abb3ea 100644 --- a/app/controllers/instructeurs/recherche_controller.rb +++ b/app/controllers/instructeurs/recherche_controller.rb @@ -5,7 +5,7 @@ module Instructeurs @dossiers = DossierSearchService.matching_dossiers_for_instructeur(@search_terms, current_instructeur) @followed_dossiers_id = current_instructeur .followed_dossiers - .where(procedure_id: @dossiers.pluck(:procedure_id)) + .where(groupe_instructeur_id: @dossiers.pluck(:groupe_instructeur_id)) .pluck(:id) end end diff --git a/app/javascript/components/MapEditor/index.js b/app/javascript/components/MapEditor/index.js index d98fbdc13..7d3921fc7 100644 --- a/app/javascript/components/MapEditor/index.js +++ b/app/javascript/components/MapEditor/index.js @@ -1,69 +1,102 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useCallback, useRef } from 'react'; 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 SwitchMapStyle from './SwitchMapStyle'; -import SearchInput from './SearchInput'; -import { getJSON, ajax } from '@utils'; import { gpx, kml } from '@tmcw/togeojson/dist/togeojson.es.js'; -import ortho from '../MapStyles/ortho.json'; -import orthoCadastre from '../MapStyles/orthoCadastre.json'; -import vector from '../MapStyles/vector.json'; -import vectorCadastre from '../MapStyles/vectorCadastre.json'; -import { polygonCadastresFill, polygonCadastresLine } from './utils'; import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; +import { getJSON, ajax, fire } from '@utils'; + +import SwitchMapStyle from './SwitchMapStyle'; +import { getMapStyle } from '../MapStyles'; + +import SearchInput from './SearchInput'; +import { polygonCadastresFill, polygonCadastresLine } from './utils'; +import { + noop, + filterFeatureCollection, + fitBounds, + generateId, + useEvent, + findFeature +} from '../shared/map'; + const Map = ReactMapboxGl({}); -function filterFeatureCollection(featureCollection, source) { - return { - type: 'FeatureCollection', - features: featureCollection.features.filter( - (feature) => feature.properties.source === source - ) - }; -} - -function noop() {} - function MapEditor({ featureCollection, url, preview, hasCadastres }) { const drawControl = useRef(null); + const [currentMap, setCurrentMap] = useState(null); + const [style, setStyle] = useState('ortho'); const [coords, setCoords] = useState([1.7, 46.9]); const [zoom, setZoom] = useState([5]); - const [currentMap, setCurrentMap] = useState({}); const [bbox, setBbox] = useState(featureCollection.bbox); const [importInputs, setImportInputs] = useState([]); - let mapStyle = style === 'ortho' ? ortho : vector; + const [cadastresFeatureCollection, setCadastresFeatureCollection] = useState( + filterFeatureCollection(featureCollection, 'cadastre') + ); + const mapStyle = getMapStyle(style, hasCadastres); - if (hasCadastres) { - mapStyle = style === 'ortho' ? orthoCadastre : vectorCadastre; - } - - const cadastresFeatureCollection = filterFeatureCollection( - featureCollection, - 'cadastre' + const onFeatureFocus = useCallback( + ({ detail }) => { + const { id } = detail; + const featureCollection = drawControl.current.draw.getAll(); + const feature = findFeature(featureCollection, id); + if (feature) { + fitBounds(currentMap, feature); + } + }, + [currentMap, drawControl.current] ); - function updateFeaturesList(features) { - const cadastres = features.find( - ({ geometry }) => geometry.type === 'Polygon' + const onFeatureUpdate = useCallback( + async ({ detail }) => { + const { id, properties } = detail; + const featureCollection = drawControl.current.draw.getAll(); + const feature = findFeature(featureCollection, id); + + if (feature) { + getJSON(`${url}/${id}`, { feature: { properties } }, 'patch'); + } + }, + [url, drawControl.current] + ); + + const updateFeaturesList = useCallback( + async (features) => { + const cadastres = features.find( + ({ geometry }) => geometry.type === 'Polygon' + ); + await ajax({ + url, + type: 'get', + data: cadastres ? 'cadastres=update' : '' + }); + fire(document, 'ds:page:update'); + }, + [url] + ); + + const onCadastresUpdate = useCallback(({ detail }) => { + setCadastresFeatureCollection( + filterFeatureCollection(detail.featureCollection, 'cadastre') ); - ajax({ url, type: 'get', data: cadastres ? 'cadastres=update' : '' }); - } + }, []); + + useEvent('map:feature:focus', onFeatureFocus); + useEvent('map:feature:update', onFeatureUpdate); + useEvent('cadastres:update', onCadastresUpdate); function setFeatureId(lid, feature) { const draw = drawControl.current.draw; draw.setFeatureProperty(lid, 'id', feature.properties.id); } - const generateId = () => Math.random().toString(20).substr(2, 6); - - const updateImportInputs = (inputs, inputId) => { + function updateImportInputs(inputs, inputId) { const updatedInputs = inputs.filter((input) => input.id !== inputId); setImportInputs(updatedInputs); - }; + } async function onDrawCreate({ features }) { for (const feature of features) { @@ -92,23 +125,13 @@ function MapEditor({ featureCollection, url, preview, hasCadastres }) { updateFeaturesList(features); } - const onMapLoad = (map) => { + function onMapLoad(map) { setCurrentMap(map); drawControl.current.draw.set( filterFeatureCollection(featureCollection, 'selection_utilisateur') ); - }; - - const onCadastresUpdate = (evt) => { - if (currentMap) { - currentMap - .getSource('cadastres-layer') - .setData( - filterFeatureCollection(evt.detail.featureCollection, 'cadastre') - ); - } - }; + } const onFileImport = (e, inputId) => { const isGpxFile = e.target.files[0].name.includes('.gpx'); @@ -190,11 +213,6 @@ function MapEditor({ featureCollection, url, preview, hasCadastres }) { updateImportInputs(inputs, inputId); }; - useEffect(() => { - addEventListener('cadastres:update', onCadastresUpdate); - return () => removeEventListener('cadastres:update', onCadastresUpdate); - }); - if (!mapboxgl.supported()) { return (

diff --git a/app/javascript/components/MapReader/index.js b/app/javascript/components/MapReader/index.js index c2d4e7d1c..145007b0a 100644 --- a/app/javascript/components/MapReader/index.js +++ b/app/javascript/components/MapReader/index.js @@ -1,25 +1,100 @@ -import React, { useState } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import ReactMapboxGl, { ZoomControl, GeoJSONLayer } from 'react-mapbox-gl'; -import mapboxgl from 'mapbox-gl'; -import SwitchMapStyle from './SwitchMapStyle'; -import ortho from '../MapStyles/ortho.json'; -import orthoCadastre from '../MapStyles/orthoCadastre.json'; -import vector from '../MapStyles/vector.json'; -import vectorCadastre from '../MapStyles/vectorCadastre.json'; +import mapboxgl, { Popup } from 'mapbox-gl'; import PropTypes from 'prop-types'; +import SwitchMapStyle from './SwitchMapStyle'; +import { getMapStyle } from '../MapStyles'; + +import { + filterFeatureCollection, + filterFeatureCollectionByGeometryType, + useEvent, + findFeature, + fitBounds, + getCenter +} from '../shared/map'; + const Map = ReactMapboxGl({}); const MapReader = ({ featureCollection }) => { + const [currentMap, setCurrentMap] = useState(null); const [style, setStyle] = useState('ortho'); - const hasCadastres = featureCollection.features.find( - (feature) => feature.properties.source === 'cadastre' + const cadastresFeatureCollection = useMemo( + () => filterFeatureCollection(featureCollection, 'cadastre'), + [featureCollection] + ); + const selectionsUtilisateurFeatureCollection = useMemo( + () => filterFeatureCollection(featureCollection, 'selection_utilisateur'), + [featureCollection] + ); + const selectionsLineFeatureCollection = useMemo( + () => + filterFeatureCollectionByGeometryType( + selectionsUtilisateurFeatureCollection, + 'LineString' + ), + [selectionsUtilisateurFeatureCollection] + ); + const selectionsPolygonFeatureCollection = useMemo( + () => + filterFeatureCollectionByGeometryType( + selectionsUtilisateurFeatureCollection, + 'Polygon' + ), + [selectionsUtilisateurFeatureCollection] + ); + const selectionsPointFeatureCollection = useMemo( + () => + filterFeatureCollectionByGeometryType( + selectionsUtilisateurFeatureCollection, + 'Point' + ), + [selectionsUtilisateurFeatureCollection] + ); + const mapStyle = useMemo( + () => getMapStyle(style, cadastresFeatureCollection.length), + [style, cadastresFeatureCollection] + ); + const popup = useMemo( + () => + new Popup({ + closeButton: false, + closeOnClick: false + }) ); - let mapStyle = style === 'ortho' ? ortho : vector; - if (hasCadastres) { - mapStyle = style === 'ortho' ? orthoCadastre : vectorCadastre; - } + const onMouseEnter = useCallback( + (event) => { + const feature = event.features[0]; + if (feature.properties && feature.properties.description) { + const coordinates = getCenter(feature.geometry, event.lngLat); + const description = feature.properties.description; + currentMap.getCanvas().style.cursor = 'pointer'; + popup.setLngLat(coordinates).setHTML(description).addTo(currentMap); + } else { + popup.remove(); + } + }, + [currentMap, popup] + ); + + const onMouseLeave = useCallback(() => { + currentMap.getCanvas().style.cursor = ''; + popup.remove(); + }, [currentMap, popup]); + + const onFeatureFocus = useCallback( + ({ detail }) => { + const feature = findFeature(featureCollection, detail.id); + if (feature) { + fitBounds(currentMap, feature); + } + }, + [currentMap, featureCollection] + ); + + useEvent('map:feature:focus', onFeatureFocus); const [a1, a2, b1, b2] = featureCollection.bbox; const boundData = [ @@ -27,26 +102,6 @@ const MapReader = ({ featureCollection }) => { [b1, b2] ]; - const cadastresFeatureCollection = { - type: 'FeatureCollection', - features: [] - }; - - const selectionsLineFeatureCollection = { - type: 'FeatureCollection', - features: [] - }; - - const selectionsPolygonFeatureCollection = { - type: 'FeatureCollection', - features: [] - }; - - const selectionsPointFeatureCollection = { - type: 'FeatureCollection', - features: [] - }; - const polygonSelectionFill = { 'fill-color': '#EC3323', 'fill-opacity': 0.5 @@ -77,25 +132,8 @@ const MapReader = ({ featureCollection }) => { 'line-dasharray': [1, 1] }; - for (let feature of featureCollection.features) { - switch (feature.properties.source) { - case 'selection_utilisateur': - switch (feature.geometry.type) { - case 'LineString': - selectionsLineFeatureCollection.features.push(feature); - break; - case 'Polygon': - selectionsPolygonFeatureCollection.features.push(feature); - break; - case 'Point': - selectionsPointFeatureCollection.features.push(feature); - break; - } - break; - case 'cadastre': - cadastresFeatureCollection.features.push(feature); - break; - } + function onMapLoad(map) { + setCurrentMap(map); } if (!mapboxgl.supported()) { @@ -110,6 +148,7 @@ const MapReader = ({ featureCollection }) => { return ( onMapLoad(map)} fitBounds={boundData} fitBoundsOptions={{ padding: 100 }} style={mapStyle} @@ -122,14 +161,20 @@ const MapReader = ({ featureCollection }) => { data={selectionsPolygonFeatureCollection} fillPaint={polygonSelectionFill} linePaint={polygonSelectionLine} + fillOnMouseEnter={onMouseEnter} + fillOnMouseLeave={onMouseLeave} /> feature.properties.id === id + ); +} + +export function filterFeatureCollection(featureCollection, source) { + return { + type: 'FeatureCollection', + features: featureCollection.features.filter( + (feature) => feature.properties.source === source + ) + }; +} + +export function filterFeatureCollectionByGeometryType(featureCollection, type) { + return { + type: 'FeatureCollection', + features: featureCollection.features.filter( + (feature) => feature.geometry.type === type + ) + }; +} + +export function noop() {} + +export function generateId() { + return Math.random().toString(20).substr(2, 6); +} + +export function useEvent(eventName, callback) { + return useEffect(() => { + addEventListener(eventName, callback); + return () => removeEventListener(eventName, callback); + }, [eventName, callback]); +} + +export function getCenter(geometry, lngLat) { + const bbox = new LngLatBounds(); + + switch (geometry.type) { + case 'Point': + return [...geometry.coordinates]; + case 'LineString': + return [lngLat.lng, lngLat.lat]; + default: + for (const coordinate of geometry.coordinates[0]) { + bbox.extend(coordinate); + } + return bbox.getCenter(); + } +} diff --git a/app/javascript/new_design/champs/carte.js b/app/javascript/new_design/champs/carte.js new file mode 100644 index 000000000..dbb569c50 --- /dev/null +++ b/app/javascript/new_design/champs/carte.js @@ -0,0 +1,40 @@ +import { delegate, fire, debounce } from '@utils'; + +const inputHandlers = new Map(); + +addEventListener('ds:page:update', () => { + const inputs = document.querySelectorAll('.areas input[data-geo-area]'); + + for (const input of inputs) { + input.addEventListener('focus', (event) => { + const id = parseInt(event.target.dataset.geoArea); + fire(document, 'map:feature:focus', { id }); + }); + } +}); + +delegate('click', '.areas a[data-geo-area]', (event) => { + event.preventDefault(); + const id = parseInt(event.target.dataset.geoArea); + fire(document, 'map:feature:focus', { id }); +}); + +delegate('input', '.areas input[data-geo-area]', (event) => { + const id = parseInt(event.target.dataset.geoArea); + + let handler = inputHandlers.get(id); + if (!handler) { + handler = debounce(() => { + const input = document.querySelector(`input[data-geo-area="${id}"]`); + if (input) { + fire(document, 'map:feature:update', { + id, + properties: { description: input.value.trim() } + }); + } + }, 200); + inputHandlers.set(id, handler); + } + + handler(); +}); diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index bbafd9344..70897efcc 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -24,6 +24,7 @@ import '../new_design/support'; import '../new_design/dossiers/auto-save'; import '../new_design/dossiers/auto-upload'; +import '../new_design/champs/carte'; import '../new_design/champs/linked-drop-down-list'; import '../new_design/champs/repetition'; diff --git a/app/jobs/api_entreprise/effectifs_job.rb b/app/jobs/api_entreprise/effectifs_job.rb index 4ca19ab8d..65aeb278a 100644 --- a/app/jobs/api_entreprise/effectifs_job.rb +++ b/app/jobs/api_entreprise/effectifs_job.rb @@ -1,8 +1,8 @@ class ApiEntreprise::EffectifsJob < ApiEntreprise::Job def perform(etablissement_id, procedure_id) etablissement = Etablissement.find(etablissement_id) - # effectifs endpoint currently only works when asking for february 2020 month - etablissement_params = ApiEntreprise::EffectifsAdapter.new(etablissement.siret, procedure_id, "2020", "02").to_params + # april 2020 is at the moment the most actual info for effectifs endpoint + etablissement_params = ApiEntreprise::EffectifsAdapter.new(etablissement.siret, procedure_id, "2020", "04").to_params etablissement.update!(etablissement_params) end diff --git a/app/jobs/api_entreprise/exercices_job.rb b/app/jobs/api_entreprise/exercices_job.rb index fd8b5c682..f288c6773 100644 --- a/app/jobs/api_entreprise/exercices_job.rb +++ b/app/jobs/api_entreprise/exercices_job.rb @@ -1,4 +1,7 @@ class ApiEntreprise::ExercicesJob < ApiEntreprise::Job + rescue_from(ApiEntreprise::API::BadFormatRequest) do |exception| + end + def perform(etablissement_id, procedure_id) etablissement = Etablissement.find(etablissement_id) etablissement_params = ApiEntreprise::ExercicesAdapter.new(etablissement.siret, procedure_id).to_params diff --git a/app/jobs/api_entreprise/job.rb b/app/jobs/api_entreprise/job.rb index 45d21b1ad..7853100ce 100644 --- a/app/jobs/api_entreprise/job.rb +++ b/app/jobs/api_entreprise/job.rb @@ -1,5 +1,14 @@ class ApiEntreprise::Job < ApplicationJob DEFAULT_MAX_ATTEMPTS_API_ENTREPRISE_JOBS = 5 + + rescue_from(ApiEntreprise::API::ResourceNotFound) do |exception| + error(self, exception) + end + + rescue_from(ApiEntreprise::API::BadFormatRequest) do |exception| + error(self, exception) + end + def max_attempts ENV[MAX_ATTEMPTS_API_ENTREPRISE_JOBS].to_i || DEFAULT_MAX_ATTEMPTS_API_ENTREPRISE_JOBS end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d7de32266..b914d7bd9 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -9,14 +9,6 @@ class ApplicationJob < ActiveJob::Base Rails.logger.info("#{job.class.name} ended at #{Time.zone.now}") end - rescue_from(ApiEntreprise::API::ResourceNotFound) do |exception| - error(self, exception) - end - - rescue_from(ApiEntreprise::API::BadFormatRequest) do |exception| - error(self, exception) - end - def error(job, exception) Raven.capture_exception(exception) end diff --git a/app/jobs/etablissement_update_job.rb b/app/jobs/etablissement_update_job.rb index 134d9b26d..578bf3cb4 100644 --- a/app/jobs/etablissement_update_job.rb +++ b/app/jobs/etablissement_update_job.rb @@ -1,7 +1,7 @@ class EtablissementUpdateJob < ApplicationJob def perform(dossier, siret) begin - etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, dossier.procedure_id) + etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, dossier.procedure.id) rescue return end diff --git a/app/lib/api_entreprise/api.rb b/app/lib/api_entreprise/api.rb index 17725743a..b302ac2b9 100644 --- a/app/lib/api_entreprise/api.rb +++ b/app/lib/api_entreprise/api.rb @@ -78,7 +78,7 @@ class ApiEntreprise::API elsif response.code == 400 raise BadFormatRequest, "url: #{url}" else - raise RequestFailed, "HTTP Error Code: #{response.code} for #{url}" + raise RequestFailed, "HTTP Error Code: #{response.code} for #{url}\nheaders: #{response.headers}\nbody: #{response.body}" end end diff --git a/app/lib/api_entreprise/effectifs_adapter.rb b/app/lib/api_entreprise/effectifs_adapter.rb index ceb5f9906..d53cf63c7 100644 --- a/app/lib/api_entreprise/effectifs_adapter.rb +++ b/app/lib/api_entreprise/effectifs_adapter.rb @@ -16,8 +16,8 @@ class ApiEntreprise::EffectifsAdapter < ApiEntreprise::Adapter if data_source[:effectifs_mensuels].present? { entreprise_effectif_mensuel: data_source[:effectifs_mensuels], - entreprise_effectif_mois: @mois, - entreprise_effectif_annee: @annee + entreprise_effectif_mois: data_source[:mois], + entreprise_effectif_annee: data_source[:annee] } else {} diff --git a/app/models/dossier.rb b/app/models/dossier.rb index ea11bc25b..b3241ba37 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -1,5 +1,5 @@ class Dossier < ApplicationRecord - self.ignored_columns = ['json_latlngs'] + self.ignored_columns = ['procedure_id'] include DossierFilteringConcern include Discard::Model diff --git a/app/models/geo_area.rb b/app/models/geo_area.rb index fe0ca9f2f..e576ccbe8 100644 --- a/app/models/geo_area.rb +++ b/app/models/geo_area.rb @@ -2,6 +2,7 @@ class GeoArea < ApplicationRecord belongs_to :champ store :properties, accessors: [ + :description, :surface_intersection, :surface_parcelle, :numero, diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index ea1070d2a..82ddd9406 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -1,10 +1,10 @@ class GroupeInstructeur < ApplicationRecord DEFAULT_LABEL = 'défaut' belongs_to :procedure, -> { with_discarded }, inverse_of: :groupe_instructeurs - has_many :assign_tos - has_many :instructeurs, through: :assign_tos, dependent: :destroy + has_many :assign_tos, dependent: :destroy + has_many :instructeurs, through: :assign_tos has_many :dossiers - has_and_belongs_to_many :exports + has_and_belongs_to_many :exports, dependent: :destroy validates :label, presence: { message: 'doit être renseigné' }, allow_nil: false validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' } diff --git a/app/views/champs/carte/index.js.erb b/app/views/champs/carte/index.js.erb index 0d7fdfded..c77c1eb8f 100644 --- a/app/views/champs/carte/index.js.erb +++ b/app/views/champs/carte/index.js.erb @@ -2,7 +2,7 @@ <%= render_to_element("#{@selector} + .geo-areas", partial: 'shared/champs/carte/geo_areas', - locals: { champ: @champ }) %> + locals: { champ: @champ, editing: true }) %> <% if @update_cadastres %> <%= fire_event('cadastres:update', { featureCollection: @champ.to_feature_collection }.to_json) %> diff --git a/app/views/shared/champs/carte/_geo_areas.html.haml b/app/views/shared/champs/carte/_geo_areas.html.haml index 180c2b18f..f5a7348f5 100644 --- a/app/views/shared/champs/carte/_geo_areas.html.haml +++ b/app/views/shared/champs/carte/_geo_areas.html.haml @@ -3,19 +3,17 @@ .areas %ul - champ.selections_utilisateur.each do |geo_area| - %li= geo_area_label(geo_area) - -- if champ.quartiers_prioritaires? - .areas-title Quartiers prioritaires - .areas - - if !champ.geometry? - Aucune zone tracée - - elsif champ.quartiers_prioritaires.blank? - = t('errors.messages.quartiers_prioritaires_empty', count: champ.selections_utilisateur.size) - - else - %ul - - champ.quartiers_prioritaires.each do |geo_area| - %li= geo_area_label(geo_area) + %li{ class: editing ? '' : 'flex column mb-2' } + - if editing + = link_to '#', data: { geo_area: geo_area.id } do + = geo_area_label(geo_area) + = text_field_tag :description, geo_area.description, data: { geo_area: geo_area.id }, placeholder: 'Description de la sélection' + - else + = link_to '#', data: { geo_area: geo_area.id } do + = geo_area_label(geo_area) + - if geo_area.description.present? + %span + = geo_area.description - if champ.cadastres? .areas-title Parcelles cadastrales @@ -27,16 +25,6 @@ - else %ul - champ.cadastres.each do |geo_area| - %li= geo_area_label(geo_area) - -- if champ.parcelles_agricoles? - .areas-title Parcelles agricoles (RPG) - .areas - - if !champ.geometry? - Aucune zone tracée - - elsif champ.parcelles_agricoles.blank? - = t('errors.messages.parcelles_agricoles_empty', count: champ.selections_utilisateur.size) - - else - %ul - - champ.parcelles_agricoles.each do |geo_area| - %li= geo_area_label(geo_area) + %li.flex.column.mb-2 + = link_to '#', data: { geo_area: geo_area.id } do + = geo_area_label(geo_area) diff --git a/app/views/shared/champs/carte/_show.html.haml b/app/views/shared/champs/carte/_show.html.haml index b59690562..122b94f64 100644 --- a/app/views/shared/champs/carte/_show.html.haml +++ b/app/views/shared/champs/carte/_show.html.haml @@ -1,4 +1,4 @@ - if champ.geometry? = react_component("MapReader", { featureCollection: champ.to_feature_collection } ) .geo-areas - = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ } + = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: false } diff --git a/app/views/shared/dossiers/editable_champs/_carte.html.haml b/app/views/shared/dossiers/editable_champs/_carte.html.haml index 25fe30db8..21328444b 100644 --- a/app/views/shared/dossiers/editable_champs/_carte.html.haml +++ b/app/views/shared/dossiers/editable_champs/_carte.html.haml @@ -2,4 +2,4 @@ = react_component("MapEditor", { featureCollection: champ.to_feature_collection, url: champs_carte_features_path(preview ? 'preview' : champ), preview: preview, hasCadastres: !!champ.cadastres? }, class: "carte-#{champ.id}") .geo-areas - = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ } + = render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, editing: true } diff --git a/db/migrate/20200611122406_remove_dossier_procedure_id.rb b/db/migrate/20200611122406_remove_dossier_procedure_id.rb new file mode 100644 index 000000000..1aff885f9 --- /dev/null +++ b/db/migrate/20200611122406_remove_dossier_procedure_id.rb @@ -0,0 +1,5 @@ +class RemoveDossierProcedureId < ActiveRecord::Migration[5.2] + def change + remove_column :dossiers, :procedure_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 455e700d5..9572bb9d8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_04_29_191305) do +ActiveRecord::Schema.define(version: 2020_06_11_122406) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -238,7 +238,6 @@ ActiveRecord::Schema.define(version: 2020_04_29_191305) do create_table "dossiers", id: :serial, force: :cascade do |t| t.boolean "autorisation_donnees" - t.integer "procedure_id" t.datetime "created_at" t.datetime "updated_at" t.string "state" @@ -262,7 +261,6 @@ ActiveRecord::Schema.define(version: 2020_04_29_191305) do t.index ["archived"], name: "index_dossiers_on_archived" t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id" t.index ["hidden_at"], name: "index_dossiers_on_hidden_at" - t.index ["procedure_id"], name: "index_dossiers_on_procedure_id" t.index ["state"], name: "index_dossiers_on_state" t.index ["user_id"], name: "index_dossiers_on_user_id" end diff --git a/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake b/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake deleted file mode 100644 index aeb50123b..000000000 --- a/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake +++ /dev/null @@ -1,15 +0,0 @@ -namespace :after_party do - desc 'Deployment task: link_dossier_and_groupe_instructeur' - task link_dossier_and_groupe_instructeur: :environment do - sql = <<~SQL - UPDATE dossiers AS d1 SET groupe_instructeur_id = g.id - FROM groupe_instructeurs AS g - WHERE g.procedure_id = d1.procedure_id - and d1.groupe_instructeur_id is null; - SQL - - ActiveRecord::Base.connection.execute(sql) - - AfterParty::TaskRecord.create version: '20190826153115' - end -end diff --git a/spec/controllers/champs/carte_controller_spec.rb b/spec/controllers/champs/carte_controller_spec.rb index 30a30a393..da7f2a7af 100644 --- a/spec/controllers/champs/carte_controller_spec.rb +++ b/spec/controllers/champs/carte_controller_spec.rb @@ -44,19 +44,43 @@ describe Champs::CarteController, type: :controller do end describe 'PATCH #update' do - let(:params) do - { - champ_id: champ.id, - id: geo_area.id, - feature: feature - } - end - before do patch :update, params: params end - it { expect(response.status).to eq 204 } + context 'update geometry' do + let(:params) do + { + champ_id: champ.id, + id: geo_area.id, + feature: feature + } + end + + it { expect(response.status).to eq 204 } + end + + context 'update description' do + let(:feature) do + { + properties: { + description: 'un point' + } + } + end + let(:params) do + { + champ_id: champ.id, + id: geo_area.id, + feature: feature + } + end + + it { + expect(response.status).to eq 204 + expect(geo_area.reload.description).to eq('un point') + } + end end describe 'DELETE #destroy' do diff --git a/spec/jobs/api_entreprise/effectifs_job_spec.rb b/spec/jobs/api_entreprise/effectifs_job_spec.rb index 77e87bf36..1aaed9309 100644 --- a/spec/jobs/api_entreprise/effectifs_job_spec.rb +++ b/spec/jobs/api_entreprise/effectifs_job_spec.rb @@ -6,7 +6,7 @@ RSpec.describe ApiEntreprise::EffectifsJob, type: :job do let(:procedure_id) { procedure.id } let(:now) { Time.zone.local(2020, 3, 12) } let(:annee) { "2020" } - let(:mois) { "02" } + let(:mois) { "04" } let(:body) { File.read('spec/fixtures/files/api_entreprise/effectifs.json') } let(:status) { 200 } diff --git a/spec/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake_spec.rb b/spec/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake_spec.rb deleted file mode 100644 index 381e6aa14..000000000 --- a/spec/lib/tasks/deployment/20190826153115_link_dossier_and_groupe_instructeur.rake_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -describe '20190826153115_link_dossier_and_groupe_instructeur.rake' do - let(:rake_task) { Rake::Task['after_party:link_dossier_and_groupe_instructeur'] } - - subject { rake_task.invoke } - after { rake_task.reenable } - - context 'with 3 dossiers' do - let!(:procedure) { create(:procedure) } - let!(:procedure2) { create(:procedure) } - let!(:other_procedure_needed_to_create_dossier) { create(:procedure) } - let!(:other_gi) { other_procedure_needed_to_create_dossier.defaut_groupe_instructeur } - let!(:dossier) { Dossier.create(user: create(:user), procedure_id: procedure.id, groupe_instructeur: other_gi) } - let!(:dossier2) { Dossier.create(user: create(:user), procedure_id: procedure2.id, groupe_instructeur: other_gi) } - let!(:dossier3) { Dossier.create(user: create(:user), procedure_id: procedure2.id, groupe_instructeur: other_gi) } - - before do - [dossier, dossier2, dossier3].each do |d| - d.update_column('groupe_instructeur_id', nil) - end - - other_procedure_needed_to_create_dossier.groupe_instructeurs.destroy_all - other_procedure_needed_to_create_dossier.destroy - end - - it do - expect(dossier.reload.groupe_instructeur_id).to be_nil - subject - expect(Dossier.count).to eq(3) - expect(Procedure.count).to eq(2) - expect(GroupeInstructeur.count).to eq(2) - expect(dossier.reload.groupe_instructeur_id).to eq(procedure.defaut_groupe_instructeur.id) - expect(dossier2.reload.groupe_instructeur_id).to eq(procedure2.defaut_groupe_instructeur.id) - expect(dossier3.reload.groupe_instructeur_id).to eq(procedure2.defaut_groupe_instructeur.id) - end - end -end