diff --git a/Gemfile b/Gemfile index 9b4d7a0bf..a68d6e3f1 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,7 @@ gem 'bootstrap-sass', '~> 3.3.5' # Pagination gem 'will_paginate-bootstrap' +gem 'kaminari' # Decorators gem 'draper', '~> 3.0.0.pre1' @@ -95,10 +96,11 @@ gem 'newrelic_rpm' gem 'scenic' -# Sidekiq -gem 'sidekiq' -gem 'sidekiq-cron', '~> 0.4.4' -gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git', require: false +# Cron jobs +gem 'delayed_job_active_record' +gem "daemons" +gem 'delayed_cron_job' +gem "delayed_job_web" gem 'select2-rails' diff --git a/Gemfile.lock b/Gemfile.lock index fca30b5c4..6ce018ead 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,18 +7,6 @@ GIT open4 (~> 1.3.4) rake -GIT - remote: https://github.com/sinatra/sinatra.git - revision: d0c4053fd459be9f2c207cfeec5c0606461c014b - specs: - rack-protection (2.0.0.rc1) - rack - sinatra (2.0.0.rc1) - mustermann (= 1.0.0) - rack (~> 2.0) - rack-protection (= 2.0.0.rc1) - tilt (~> 2.0) - GEM remote: https://rubygems.org/ specs: @@ -121,15 +109,26 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) - connection_pool (2.2.1) copy_carrierwave_file (1.3.0) carrierwave (>= 0.9) crack (0.4.3) safe_yaml (~> 1.0.0) + daemons (1.2.4) database_cleaner (1.5.3) debug_inspector (0.0.2) deep_cloneable (2.2.2) activerecord (>= 3.1.0, < 5.2.0) + delayed_cron_job (0.7.2) + delayed_job (>= 4.1) + delayed_job (4.1.3) + activesupport (>= 3.0, < 5.2) + delayed_job_active_record (4.1.2) + activerecord (>= 3.0, < 5.2) + delayed_job (>= 3.0, < 5) + delayed_job_web (1.4) + activerecord (> 3.0.0) + delayed_job (> 2.0.3) + sinatra (>= 1.4.4) devise (4.2.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -396,7 +395,7 @@ GEM minitest (5.10.1) multi_json (1.12.1) multipart-post (2.0.0) - mustermann (1.0.0) + mustermann (1.0.1) nenv (0.3.0) netrc (0.11.0) newrelic_rpm (3.18.1.330) @@ -456,6 +455,8 @@ GEM httpclient (>= 2.4) multi_json (>= 1.3.6) rack (>= 1.1) + rack-protection (2.0.0) + rack rack-test (0.6.3) rack (>= 1.0) rails (5.0.0.1) @@ -499,8 +500,6 @@ GEM trollop (~> 2.1) rdoc (4.3.0) redis (3.3.3) - redis-namespace (1.5.3) - redis (~> 3.0, >= 3.0.4) ref (2.0.0) request_store (1.3.1) responders (2.3.0) @@ -549,8 +548,6 @@ GEM ruby_parser (3.8.3) sexp_processor (~> 4.1) rubyzip (1.0.0) - rufus-scheduler (3.3.4) - tzinfo safe_yaml (1.0.4) sass (3.4.22) sass-rails (5.0.6) @@ -577,18 +574,14 @@ GEM shellany (0.0.1) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) - sidekiq (4.2.9) - concurrent-ruby (~> 1.0) - connection_pool (~> 2.2, >= 2.2.0) - rack-protection (>= 1.5.0) - redis (~> 3.2, >= 3.2.1) - sidekiq-cron (0.4.5) - redis-namespace (>= 1.5.2) - rufus-scheduler (>= 2.0.24) - sidekiq (>= 4.2.1) simple_form (3.4.0) actionpack (> 4, < 5.1) activemodel (> 4, < 5.1) + sinatra (2.0.0) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.0) + tilt (~> 2.0) slop (3.6.0) smart_listing (1.2.0) coffee-rails @@ -694,8 +687,12 @@ DEPENDENCIES chunky_png clamav-client copy_carrierwave_file + daemons database_cleaner deep_cloneable (~> 2.2.1) + delayed_cron_job + delayed_job_active_record + delayed_job_web devise dotenv-rails draper (~> 3.0.0.pre1) @@ -710,6 +707,7 @@ DEPENDENCIES haml-rails hashie jquery-rails + kaminari launchy leaflet-draw-rails leaflet-markercluster-rails (~> 0.7.0) @@ -742,10 +740,7 @@ DEPENDENCIES select2-rails sentry-raven shoulda-matchers - sidekiq - sidekiq-cron (~> 0.4.4) simple_form - sinatra! smart_listing spreadsheet_architect spring diff --git a/README.md b/README.md index d395433ee..23c936df1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Téléprocédures Simplifiées, ou TPS pour les intimes, est une plateforme 100 ### Tous environnements - postgresql -- redis ### Développement @@ -61,11 +60,14 @@ Afin de générer la BDD de l'application, il est nécessaire d'éxécuter les c ## Lancement de l'application - redis-server - sidekiq + bin/delayed_job run mailcatcher -f rails s +## Lancement des workers + + Delayed::Job.enqueue(AutoArchiveProcedureWorker.new, cron: "* * * * *") + Delayed::Job.enqueue(WeeklyOverviewWorker.new, cron: "0 8 * * 0") ## Exécution des tests (RSpec) diff --git a/app/assets/images/icons/accept.svg b/app/assets/images/icons/accept.svg new file mode 100644 index 000000000..71fde1f5e --- /dev/null +++ b/app/assets/images/icons/accept.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/archive.svg b/app/assets/images/icons/archive.svg new file mode 100644 index 000000000..845cd1e70 --- /dev/null +++ b/app/assets/images/icons/archive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/attachment.svg b/app/assets/images/icons/attachment.svg new file mode 100644 index 000000000..75d78782d --- /dev/null +++ b/app/assets/images/icons/attachment.svg @@ -0,0 +1 @@ +Shape \ No newline at end of file diff --git a/app/assets/images/icons/blue-person.svg b/app/assets/images/icons/blue-person.svg new file mode 100644 index 000000000..a5693cac9 --- /dev/null +++ b/app/assets/images/icons/blue-person.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/bubble.svg b/app/assets/images/icons/bubble.svg new file mode 100644 index 000000000..d423f2f4b --- /dev/null +++ b/app/assets/images/icons/bubble.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/chevron-down.svg b/app/assets/images/icons/chevron-down.svg new file mode 100644 index 000000000..22a1562fd --- /dev/null +++ b/app/assets/images/icons/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/close.svg b/app/assets/images/icons/close.svg new file mode 100644 index 000000000..ddb477bd9 --- /dev/null +++ b/app/assets/images/icons/close.svg @@ -0,0 +1 @@ +ic_close_circle_white \ No newline at end of file diff --git a/app/assets/images/icons/edit-folder-blue.svg b/app/assets/images/icons/edit-folder-blue.svg new file mode 100644 index 000000000..1c4e0d187 --- /dev/null +++ b/app/assets/images/icons/edit-folder-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/folder.svg b/app/assets/images/icons/folder.svg new file mode 100644 index 000000000..55cccb64e --- /dev/null +++ b/app/assets/images/icons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/follow-folder.svg b/app/assets/images/icons/follow-folder.svg new file mode 100644 index 000000000..b5c7878b9 --- /dev/null +++ b/app/assets/images/icons/follow-folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/in-progress-blue.svg b/app/assets/images/icons/in-progress-blue.svg new file mode 100644 index 000000000..29d7b574e --- /dev/null +++ b/app/assets/images/icons/in-progress-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/lock.svg b/app/assets/images/icons/lock.svg new file mode 100644 index 000000000..a2aa4f2ab --- /dev/null +++ b/app/assets/images/icons/lock.svg @@ -0,0 +1 @@ + diff --git a/app/assets/images/icons/mail.svg b/app/assets/images/icons/mail.svg new file mode 100644 index 000000000..7a84b97da --- /dev/null +++ b/app/assets/images/icons/mail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/super-admin.svg b/app/assets/images/icons/super-admin.svg new file mode 100644 index 000000000..bb9584ed5 --- /dev/null +++ b/app/assets/images/icons/super-admin.svg @@ -0,0 +1 @@ +Shape \ No newline at end of file diff --git a/app/assets/images/icons/unarchive.svg b/app/assets/images/icons/unarchive.svg new file mode 100644 index 000000000..b0098fd53 --- /dev/null +++ b/app/assets/images/icons/unarchive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/unfollow-folder.svg b/app/assets/images/icons/unfollow-folder.svg new file mode 100644 index 000000000..272efc828 --- /dev/null +++ b/app/assets/images/icons/unfollow-folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/without-continuation.svg b/app/assets/images/icons/without-continuation.svg new file mode 100644 index 000000000..8c33fa2af --- /dev/null +++ b/app/assets/images/icons/without-continuation.svg @@ -0,0 +1 @@ +sans-suite \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f17bf80df..d5ce61307 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,7 +15,7 @@ //= require turbolinks //= require highcharts //= require chartkick -//= require_tree . +//= require_tree ./old_design //= require bootstrap-sprockets //= require bootstrap-datepicker/core //= require bootstrap-datepicker/locales/bootstrap-datepicker.fr.js diff --git a/app/assets/javascripts/new_design/application.js b/app/assets/javascripts/new_design/application.js index 700bc74fb..56b132233 100644 --- a/app/assets/javascripts/new_design/application.js +++ b/app/assets/javascripts/new_design/application.js @@ -10,9 +10,13 @@ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details // about supported directives. // +//= require ./init //= require jquery //= require jquery_ujs //= require turbolinks +//= require leaflet.1.1.0 //= require highcharts //= require chartkick +//= require select2 +//= require typeahead.bundle //= require_tree . diff --git a/app/assets/javascripts/new_design/buttons.js b/app/assets/javascripts/new_design/buttons.js new file mode 100644 index 000000000..3e134c807 --- /dev/null +++ b/app/assets/javascripts/new_design/buttons.js @@ -0,0 +1,11 @@ +$(document).on("click", "body", function () { + $(".button.dropdown").removeClass("open"); +}); + +$(document).on("click", ".button.dropdown", function(event) { + event.stopPropagation(); + var $target = $(event.target); + if($target.hasClass("button", "dropdown")){ + $target.toggleClass("open"); + } +}); diff --git a/app/assets/javascripts/new_design/carte/cadastre.js b/app/assets/javascripts/new_design/carte/cadastre.js new file mode 100644 index 000000000..40d4ad764 --- /dev/null +++ b/app/assets/javascripts/new_design/carte/cadastre.js @@ -0,0 +1,10 @@ +function drawCadastre (map) { + drawLayerWithItems(map, dossierCadastres, { + fillColor: '#8A6D3B', + weight: 2, + opacity: 0.7, + color: '#8A6D3B', + dashArray: '3', + fillOpacity: 0.5 + }); +} diff --git a/app/assets/javascripts/new_design/carte/carte.js b/app/assets/javascripts/new_design/carte/carte.js new file mode 100644 index 000000000..27ed444ab --- /dev/null +++ b/app/assets/javascripts/new_design/carte/carte.js @@ -0,0 +1,61 @@ +function initCarto() { + if ($("#map").length > 0) { + var position = getPosition() || defaultGestionnairePosition(); + + var map = L.map('map', { + scrollWheelZoom: false + }).setView([position.lat, position.lon], position.zoom); + + L.tileLayer('http://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(map); + + // draw external polygons + drawCadastre(map); + drawQuartiersPrioritaires(map); + + // draw user polygon + drawUserSelection(map); + } +} + +$(document).on('turbolinks:load', initCarto); + +function drawUserSelection(map) { + if (dossierJsonLatLngs.length > 0) { + var polygon = L.polygon(dossierJsonLatLngs, { color: 'red', zIndex: 3 }).addTo(map); + map.fitBounds(polygon.getBounds()); + } +} + +function defaultGestionnairePosition() { + var LON = '2.428462'; + var LAT = '46.538192'; + return { lon: LON, lat: LAT, zoom: 5 } +} + +function getPosition() { + var position; + + $.ajax({ + url: getPositionUrl, + dataType: 'json', + async: false + }).done(function (data) { + position = data + }); + + return position; +} + +function drawLayerWithItems(map, items, style) { + if (Array.isArray(items) && items.length > 0) { + var layer = new L.GeoJSON(); + + items.forEach(function (item) { + layer.addData(item.geometry); + }); + + layer.setStyle(style).addTo(map); + } +} diff --git a/app/assets/javascripts/new_design/carte/quartiers_prioritaires.js b/app/assets/javascripts/new_design/carte/quartiers_prioritaires.js new file mode 100644 index 000000000..e97b25763 --- /dev/null +++ b/app/assets/javascripts/new_design/carte/quartiers_prioritaires.js @@ -0,0 +1,10 @@ +function drawQuartiersPrioritaires (map) { + drawLayerWithItems(map, dossierQuartiersPrioritaires, { + fillColor: '#31708F', + weight: 2, + opacity: 0.7, + color: '#31708F', + dashArray: '3', + fillOpacity: 0.5 + }); +} diff --git a/app/assets/javascripts/new_design/champs/address.js b/app/assets/javascripts/new_design/champs/address.js new file mode 100644 index 000000000..7b9339b9e --- /dev/null +++ b/app/assets/javascripts/new_design/champs/address.js @@ -0,0 +1,27 @@ +(function () { + var display = 'label'; + + var bloodhound = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace(display), + queryTokenizer: Bloodhound.tokenizers.whitespace, + + remote: { + url: '/ban/search?request=%QUERY', + wildcard: '%QUERY' + } + }); + + bloodhound.initialize(); + + var bindTypeahead = function() { + $("input[data-address='true']").typeahead({ + minLength: 1 + }, { + display: display, + source: bloodhound, + limit: 5 + }); + }; + + document.addEventListener('turbolinks:load', bindTypeahead); +})(); diff --git a/app/assets/javascripts/new-description.js b/app/assets/javascripts/new_design/champs/dossier_link.js similarity index 100% rename from app/assets/javascripts/new-description.js rename to app/assets/javascripts/new_design/champs/dossier_link.js diff --git a/app/assets/javascripts/new_design/champs/multiple_drop_down_list.js b/app/assets/javascripts/new_design/champs/multiple_drop_down_list.js new file mode 100644 index 000000000..f76aa5913 --- /dev/null +++ b/app/assets/javascripts/new_design/champs/multiple_drop_down_list.js @@ -0,0 +1,3 @@ +document.addEventListener('turbolinks:load', function() { + $('select.select2').select2(); +}); diff --git a/app/assets/javascripts/new_design/form_validation.js b/app/assets/javascripts/new_design/form_validation.js new file mode 100644 index 000000000..8eef828af --- /dev/null +++ b/app/assets/javascripts/new_design/form_validation.js @@ -0,0 +1,8 @@ +$(document).on('blur keydown', 'input, textarea', function() { + $(this).addClass('touched'); +}); + +$(document).on('click', 'input[type="submit"]:not([formnovalidate])', function() { + var $form = $(this).closest('form'); + $('input, textarea', $form).addClass('touched'); +}); diff --git a/app/assets/javascripts/new_design/header.js b/app/assets/javascripts/new_design/header.js index 903b0ff96..479190296 100644 --- a/app/assets/javascripts/new_design/header.js +++ b/app/assets/javascripts/new_design/header.js @@ -1,5 +1,3 @@ -window.TPS = window.TPS || {}; - $(document).on("click", "body", function () { $(".header-menu").removeClass("open fade-in-down"); }); diff --git a/app/assets/javascripts/new_design/init.js b/app/assets/javascripts/new_design/init.js new file mode 100644 index 000000000..4e62f8a6e --- /dev/null +++ b/app/assets/javascripts/new_design/init.js @@ -0,0 +1,2 @@ +// namespace +window.TPS = window.TPS || {}; diff --git a/app/assets/javascripts/new_design/messagerie.js b/app/assets/javascripts/new_design/messagerie.js new file mode 100644 index 000000000..1b8d1d324 --- /dev/null +++ b/app/assets/javascripts/new_design/messagerie.js @@ -0,0 +1,8 @@ +TPS.scrollMessagerie = function () { + var $ul = $(".messagerie ul").first(); + if($ul.length) { + $ul.scrollTop($ul.prop('scrollHeight')); + } +}; + +document.addEventListener("turbolinks:load", TPS.scrollMessagerie); diff --git a/app/assets/javascripts/new_design/state_button.js b/app/assets/javascripts/new_design/state_button.js new file mode 100644 index 000000000..71e32310e --- /dev/null +++ b/app/assets/javascripts/new_design/state_button.js @@ -0,0 +1,9 @@ +TPS.acceptDossier = function () { + $(".motivation").show(); + $(".dropdown-items").hide(); +} + +TPS.motivationCancel = function () { + $(".motivation").hide(); + $(".dropdown-items").show(); +} diff --git a/app/assets/javascripts/action_btn_rules.js b/app/assets/javascripts/old_design/action_btn_rules.js similarity index 100% rename from app/assets/javascripts/action_btn_rules.js rename to app/assets/javascripts/old_design/action_btn_rules.js diff --git a/app/assets/javascripts/address_typeahead.js b/app/assets/javascripts/old_design/address_typeahead.js similarity index 100% rename from app/assets/javascripts/address_typeahead.js rename to app/assets/javascripts/old_design/address_typeahead.js diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/old_design/admin.js similarity index 100% rename from app/assets/javascripts/admin.js rename to app/assets/javascripts/old_design/admin.js diff --git a/app/assets/javascripts/admin_procedures_modal.js b/app/assets/javascripts/old_design/admin_procedures_modal.js similarity index 100% rename from app/assets/javascripts/admin_procedures_modal.js rename to app/assets/javascripts/old_design/admin_procedures_modal.js diff --git a/app/assets/javascripts/archive.js b/app/assets/javascripts/old_design/archive.js similarity index 100% rename from app/assets/javascripts/archive.js rename to app/assets/javascripts/old_design/archive.js diff --git a/app/assets/javascripts/bootstrap_wysihtml5.js b/app/assets/javascripts/old_design/bootstrap_wysihtml5.js similarity index 100% rename from app/assets/javascripts/bootstrap_wysihtml5.js rename to app/assets/javascripts/old_design/bootstrap_wysihtml5.js diff --git a/app/assets/javascripts/carte/cadastre.js b/app/assets/javascripts/old_design/carte/cadastre.js similarity index 100% rename from app/assets/javascripts/carte/cadastre.js rename to app/assets/javascripts/old_design/carte/cadastre.js diff --git a/app/assets/javascripts/carte/carte.js b/app/assets/javascripts/old_design/carte/carte.js similarity index 100% rename from app/assets/javascripts/carte/carte.js rename to app/assets/javascripts/old_design/carte/carte.js diff --git a/app/assets/javascripts/carte/qp.js b/app/assets/javascripts/old_design/carte/qp.js similarity index 100% rename from app/assets/javascripts/carte/qp.js rename to app/assets/javascripts/old_design/carte/qp.js diff --git a/app/assets/javascripts/cgu.js b/app/assets/javascripts/old_design/cgu.js similarity index 100% rename from app/assets/javascripts/cgu.js rename to app/assets/javascripts/old_design/cgu.js diff --git a/app/assets/javascripts/default_data_block.js b/app/assets/javascripts/old_design/default_data_block.js similarity index 100% rename from app/assets/javascripts/default_data_block.js rename to app/assets/javascripts/old_design/default_data_block.js diff --git a/app/assets/javascripts/description.js b/app/assets/javascripts/old_design/description.js similarity index 100% rename from app/assets/javascripts/description.js rename to app/assets/javascripts/old_design/description.js diff --git a/app/assets/javascripts/dossier_commentaires_modal.js b/app/assets/javascripts/old_design/dossier_commentaires_modal.js similarity index 100% rename from app/assets/javascripts/dossier_commentaires_modal.js rename to app/assets/javascripts/old_design/dossier_commentaires_modal.js diff --git a/app/assets/javascripts/dossiers.js b/app/assets/javascripts/old_design/dossiers.js similarity index 100% rename from app/assets/javascripts/dossiers.js rename to app/assets/javascripts/old_design/dossiers.js diff --git a/app/assets/javascripts/dossiers_list_filter.js b/app/assets/javascripts/old_design/dossiers_list_filter.js similarity index 100% rename from app/assets/javascripts/dossiers_list_filter.js rename to app/assets/javascripts/old_design/dossiers_list_filter.js diff --git a/app/assets/javascripts/dossiers_list_link.js b/app/assets/javascripts/old_design/dossiers_list_link.js similarity index 100% rename from app/assets/javascripts/dossiers_list_link.js rename to app/assets/javascripts/old_design/dossiers_list_link.js diff --git a/app/assets/javascripts/franceconnect_kit.js b/app/assets/javascripts/old_design/franceconnect_kit.js similarity index 100% rename from app/assets/javascripts/franceconnect_kit.js rename to app/assets/javascripts/old_design/franceconnect_kit.js diff --git a/app/assets/javascripts/gestionnaire_dossier_modal.js b/app/assets/javascripts/old_design/gestionnaire_dossier_modal.js similarity index 100% rename from app/assets/javascripts/gestionnaire_dossier_modal.js rename to app/assets/javascripts/old_design/gestionnaire_dossier_modal.js diff --git a/app/assets/javascripts/old_design/new-description.js b/app/assets/javascripts/old_design/new-description.js new file mode 100644 index 000000000..3b32782c4 --- /dev/null +++ b/app/assets/javascripts/old_design/new-description.js @@ -0,0 +1,36 @@ +(function() { + var showNotFound = function() { + $('.dossier-link .text-info').hide(); + $('.dossier-link .text-warning').show(); + }; + + var showData = function(data) { + $('.dossier-link .dossier-text-summary').text(data.textSummary); + $('.dossier-link .text-info').show(); + $('.dossier-link .text-warning').hide(); + }; + + var hideEverything = function() { + $('.dossier-link .text-info').hide(); + $('.dossier-link .text-warning').hide(); + }; + + var fetchProcedureLibelle = function(e) { + var dossierId = $(e.target).val(); + if(dossierId) { + $.get('/users/dossiers/' + dossierId + '/text_summary') + .done(showData) + .fail(showNotFound); + } else { + hideEverything(); + } + }; + + var timeOut = null; + var debounceFetchProcedureLibelle = function(e) { + if(timeOut){ clearTimeout(timeOut); } + timeOut = setTimeout(function() { fetchProcedureLibelle(e); }, 300); + }; + + $(document).on('input', '[data-type=dossier-link]', debounceFetchProcedureLibelle); +})(); diff --git a/app/assets/javascripts/pref_list_dossier.js b/app/assets/javascripts/old_design/pref_list_dossier.js similarity index 100% rename from app/assets/javascripts/pref_list_dossier.js rename to app/assets/javascripts/old_design/pref_list_dossier.js diff --git a/app/assets/javascripts/procedure.js b/app/assets/javascripts/old_design/procedure.js similarity index 100% rename from app/assets/javascripts/procedure.js rename to app/assets/javascripts/old_design/procedure.js diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/old_design/search.js similarity index 100% rename from app/assets/javascripts/search.js rename to app/assets/javascripts/old_design/search.js diff --git a/app/assets/javascripts/start.js b/app/assets/javascripts/old_design/start.js similarity index 100% rename from app/assets/javascripts/start.js rename to app/assets/javascripts/old_design/start.js diff --git a/app/assets/javascripts/user/description.js b/app/assets/javascripts/old_design/user/description.js similarity index 100% rename from app/assets/javascripts/user/description.js rename to app/assets/javascripts/old_design/user/description.js diff --git a/app/assets/stylesheets/new_design/_colors.scss b/app/assets/stylesheets/new_design/_colors.scss index ab61a6c21..c46c85b3d 100644 --- a/app/assets/stylesheets/new_design/_colors.scss +++ b/app/assets/stylesheets/new_design/_colors.scss @@ -7,3 +7,8 @@ $border-grey: #CCCCCC; $dark-red: #A94442; $light-red: #EBCCD1; $lighter-red: #F2DEDE; +$green: #35D49E; +$lighter-green: lighten($green, 30%); +$light-green: lighten($green, 25%); +$dark-green: darken($green, 20%); +$orange: #F59415; diff --git a/app/assets/stylesheets/new_design/_constants.scss b/app/assets/stylesheets/new_design/_constants.scss index 897512e1d..17689108b 100644 --- a/app/assets/stylesheets/new_design/_constants.scss +++ b/app/assets/stylesheets/new_design/_constants.scss @@ -1,3 +1,7 @@ $page-width: 1040px; -$default-padding: 15px; +$default-spacer: 8px; +$default-padding: 2 * $default-spacer; + +$footer-height: 267px; +$footer-height-mobile: 531px; diff --git a/app/assets/stylesheets/new_design/_placeholders.scss b/app/assets/stylesheets/new_design/_placeholders.scss index 086e5ecfe..53e98918e 100644 --- a/app/assets/stylesheets/new_design/_placeholders.scss +++ b/app/assets/stylesheets/new_design/_placeholders.scss @@ -1,5 +1,4 @@ @import "constants"; -@import "mixins"; %horizontal-list { list-style-type: none; @@ -14,12 +13,6 @@ display: inline-block; } -%page-width-container { - @include horizontal-padding($default-padding); - max-width: $page-width + 2 * $default-padding; - margin: 0 auto; -} - %animation { animation-fill-mode: forwards; animation-duration: 0.3s; diff --git a/app/assets/stylesheets/new_design/_tabs.scss b/app/assets/stylesheets/new_design/_tabs.scss new file mode 100644 index 000000000..c6447a5ee --- /dev/null +++ b/app/assets/stylesheets/new_design/_tabs.scss @@ -0,0 +1,51 @@ +@import "colors"; +@import "mixins"; + +.tabs { + li { + display: inline-block; + line-height: 36px; + position: relative; + text-align: center; + font-size: 14px; + border-radius: 3px 3px 0 0; + border: 1px solid transparent; + + a { + display: block; + padding-left: 20px; + padding-right: 20px; + color: $black; + } + + &.active { + background-color: #FFFFFF; + border-top: 1px solid $border-grey; + border-left: 1px solid $border-grey; + border-right: 1px solid $border-grey; + + a { + color: $blue; + } + + .badge { + color: $blue; + } + } + + &:hover { + a { + color: $blue; + } + + .badge { + color: $blue; + } + } + + .notifications { + top: 3px; + right: 3px; + } + } +} diff --git a/app/assets/stylesheets/new_design/auth.scss b/app/assets/stylesheets/new_design/auth.scss index 6f292b928..87f29d6ea 100644 --- a/app/assets/stylesheets/new_design/auth.scss +++ b/app/assets/stylesheets/new_design/auth.scss @@ -1,4 +1,5 @@ @import "colors"; +@import "constants"; @import "placeholders"; @import "mixins"; @@ -59,10 +60,13 @@ $auth-breakpoint: 820px; } .auth-form { - font-size: 14px; - .reset-password { - margin-top: 8px; + margin-top: - 3 * $default-spacer; + margin-bottom: $default-spacer; + } + + .remember-me { + display: inline-block; } .separation { diff --git a/app/assets/stylesheets/new_design/avis.scss b/app/assets/stylesheets/new_design/avis.scss new file mode 100644 index 000000000..2686d8311 --- /dev/null +++ b/app/assets/stylesheets/new_design/avis.scss @@ -0,0 +1,141 @@ +@import "colors"; +@import "common"; +@import "constants"; + +.give-avis { + h1 { + font-size: 18px; + font-weight: bold; + margin-bottom: $default-padding; + } + + .lock { + margin-right: $default-spacer; + } + + h2 { + margin-bottom: $default-padding; + + .email { + font-weight: bold; + } + } + + .introduction { + margin-bottom: $default-padding; + } + + .confidentiel { + color: $grey; + font-weight: normal; + margin-bottom: 2 * $default-padding; + } + + .date { + font-size: 12px; + color: $grey; + float: right; + } +} + +.ask-avis { + h1 { + font-size: 18px; + font-weight: bold; + margin-bottom: $default-padding; + } + + .avis-notice { + font-size: 14px; + color: $grey; + margin-bottom: 2 * $default-padding; + } + + input[type=email] { + max-width: 500px; + } + + form > label { + display: inline-block; + } + + .confidentiel { + color: $grey; + font-weight: normal; + margin-bottom: 2 * $default-padding; + } + + .confidentiel-wrapper { + label, + select { + display: inline-block; + } + } +} + +.list-avis { + .title { + font-size: 18px; + font-weight: bold; + margin-bottom: $default-padding; + + .count { + display: inline-block; + width: 20px; + height: 20px; + border-radius: 10px; + border: 1px solid $grey; + text-align: center; + font-size: 12px; + font-weight: normal; + margin-left: 8px; + } + } + + .one-avis { + border-top: 1px solid $grey; + padding: $default-padding 0; + + .lock { + margin-right: $default-spacer; + } + + h2 { + margin-bottom: $default-spacer; + + .email { + font-weight: bold; + } + } + + .answer { + margin-top: $default-padding; + } + + .avis-icon { + margin-right: $default-spacer; + } + + .confidentiel { + color: $grey; + font-size: 12px; + + .lock { + width: 12px; + height: 12px; + background-size: 12px 12px; + vertical-align: sub; + } + } + } + + .date, + .waiting { + font-size: 12px; + color: $grey; + } + + .date { + float: right; + } +} diff --git a/app/assets/stylesheets/new_design/backoffice.scss b/app/assets/stylesheets/new_design/backoffice.scss new file mode 100644 index 000000000..4b129546c --- /dev/null +++ b/app/assets/stylesheets/new_design/backoffice.scss @@ -0,0 +1,20 @@ +@import "colors"; +@import "constants"; + +.backoffice-title { + font-size: 30px; + font-weight: normal; + margin-top: 3 * $default-spacer; + margin-bottom: 3 * $default-spacer; +} + +.backoffice-header { + background-color: $light-grey; + padding-top: $default-padding; + margin-bottom: 2 * $default-spacer; + border-bottom: 1px solid $border-grey; + + .container { + margin-bottom: -1px; + } +} diff --git a/app/assets/stylesheets/new_design/badges.scss b/app/assets/stylesheets/new_design/badges.scss new file mode 100644 index 000000000..6151b9d47 --- /dev/null +++ b/app/assets/stylesheets/new_design/badges.scss @@ -0,0 +1,18 @@ +@import "colors"; +@import "constants"; + +.badge { + padding: 0 5px; + font-size: 14px; + font-weight: bold; + text-align: center; + white-space: nowrap; + border-radius: 100px; + background-color: rgba(0, 0, 0, 0.08); + vertical-align: top; + + &.warning { + background-color: $orange; + color: #FFFFFF; + } +} diff --git a/app/assets/stylesheets/new_design/breadcrumb.scss b/app/assets/stylesheets/new_design/breadcrumb.scss new file mode 100644 index 000000000..a1692a871 --- /dev/null +++ b/app/assets/stylesheets/new_design/breadcrumb.scss @@ -0,0 +1,28 @@ +@import "colors"; +@import "constants"; + +.breadcrumbs { + margin: $default-spacer 0 3 * $default-spacer; + + li { + display: inline-block; + font-weight: bold; + font-size: 14px; + + a { + color: $black; + } + + &::after { + content: " > "; + } + + &:last-child { + color: $blue; + + &::after { + content: none; + } + } + } +} diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index bd4a512b8..6c9874b33 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -1,4 +1,5 @@ @import "colors"; +@import "constants"; .button { display: inline-block; @@ -6,15 +7,22 @@ border-radius: 30px; border: 1px solid $border-grey; font-size: 14px; + line-height: 20px; background-color: #FFFFFF; color: $black; cursor: pointer; + -webkit-appearance: none; &:hover { background: $light-grey; text-decoration: none; } + &:active, + &:focus { + outline: none; + } + &.primary { color: #FFFFFF; border-color: $blue; @@ -36,14 +44,132 @@ } } + &.success { + color: #FFFFFF; + border-color: $green; + background-color: $green; + + &:hover { + color: $green; + background-color: #FFFFFF; + } + } + &.large { font-size: 18px; + line-height: 26px; padding: 15px 32px; } &.expand { width: 100%; } + + > i { + width: 18px; + height: 18px; + background-size: 18px 18px; + vertical-align: middle; + margin-right: $default-spacer; + } + + &.dropdown { + position: relative; + + &::after { + content: "▾"; + margin-left: $default-spacer; + font-weight: bold; + } + + .dropdown-content { + display: none; + } + + &.open { + .dropdown-content { + display: block; + } + } + } +} + +.dropdown-content { + border: 1px solid $border-grey; + background: #FFFFFF; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + position: absolute; + right: 0; + top: 5 * $default-spacer; + cursor: default; + z-index: 10; + + &.terminated { + width: 600px; + color: $black; + padding: $default-padding; + + h4 { + font-size: 24px; + } + + .dossier-motivation { + margin-top: 2 * $default-padding; + } + + .attestation { + margin: $default-padding 0; + color: $grey; + } + } +} + +.dropdown-items { + li { + display: flex; + padding: 2 * $default-spacer; + color: $grey; + border-bottom: 1px solid $border-grey; + font-size: 12px; + min-width: 300px; + cursor: pointer; + + &.selected { + cursor: default; + + h4 { + color: $blue; + } + } + + &.selected, + &:hover { + background: $light-grey; + } + + &:last-child { + border-bottom: none; + } + + a { + display: flex; + color: $grey; + } + + i { + flex-shrink: 0; + } + + div { + padding-left: $default-spacer; + } + } + + h4 { + font-size: 14px; + color: $black; + margin-bottom: $default-spacer; + } } .link { diff --git a/app/assets/stylesheets/new_design/card.scss b/app/assets/stylesheets/new_design/card.scss new file mode 100644 index 000000000..c7c982d3c --- /dev/null +++ b/app/assets/stylesheets/new_design/card.scss @@ -0,0 +1,22 @@ +@import "colors"; +@import "constants"; + +.card { + padding: ($default-spacer * 3) ($default-spacer * 2); + border: 1px solid $border-grey; + margin-bottom: $default-spacer * 2; + + &.featured { + border-top: 8px solid $blue; + + .card-title { + color: $blue; + } + } + + .card-title { + font-weight: bold; + font-size: 20px; + margin-bottom: $default-spacer * 2; + } +} diff --git a/app/assets/stylesheets/new_design/common.scss b/app/assets/stylesheets/new_design/common.scss index 2ee0e05d7..fc2345111 100644 --- a/app/assets/stylesheets/new_design/common.scss +++ b/app/assets/stylesheets/new_design/common.scss @@ -1,12 +1,39 @@ +@import "colors"; +@import "constants"; +@import "mixins"; +@import "placeholders"; @import "typography"; -body { +body, +input, +textarea, +select { @extend %new-type; font-size: 16px; line-height: 1.42857143; } +.page-wrapper { + position: relative; + padding-bottom: $footer-height; + min-height: 100%; + + @media (max-width: 1000px) { + padding-bottom: $footer-height-mobile; + } +} + h1 { font-size: 36px; font-weight: bold; } + +a { + color: $blue; +} + +.container { + @include horizontal-padding($default-padding); + max-width: $page-width + 2 * $default-padding; + margin: 0 auto; +} diff --git a/app/assets/stylesheets/new_design/dossier-link.scss b/app/assets/stylesheets/new_design/dossier-link.scss new file mode 100644 index 000000000..5cb9aaa6e --- /dev/null +++ b/app/assets/stylesheets/new_design/dossier-link.scss @@ -0,0 +1,17 @@ +@import "constants"; +@import "colors"; + +.dossier-link { + .help-block > p { + margin-top: - $default-padding; + margin-bottom: 2 * $default-padding; + } + + .text-info { + color: $blue; + } + + .text-warning { + color: $dark-red; + } +} diff --git a/app/assets/stylesheets/new_design/dossier_annotations_privees.scss b/app/assets/stylesheets/new_design/dossier_annotations_privees.scss new file mode 100644 index 000000000..f92b704a0 --- /dev/null +++ b/app/assets/stylesheets/new_design/dossier_annotations_privees.scss @@ -0,0 +1,11 @@ +@import "colors"; +@import "common"; +@import "constants"; + +#dossier-annotations-privees { + h1 { + font-size: 18px; + font-weight: bold; + margin-bottom: $default-padding; + } +} diff --git a/app/assets/stylesheets/new_design/dossiers_table.scss b/app/assets/stylesheets/new_design/dossiers_table.scss new file mode 100644 index 000000000..e0a29a808 --- /dev/null +++ b/app/assets/stylesheets/new_design/dossiers_table.scss @@ -0,0 +1,39 @@ +@import "colors"; +@import "constants"; + +.table.dossiers-table { + td { + padding: 0; + } + + .cell-link { + color: $black; + padding: (3 * $default-spacer) 2px; + display: block; + } + + i.folder { + margin-right: $default-spacer; + position: relative; + + .notifications { + top: 0px; + right: -10px; + } + } + + .number-col, + .status-col { + width: 130px; + } + + .status-col span { + width: 110px; + text-align: center; + } + + .follow-col { + width: 200px; + text-align: center; + } +} diff --git a/app/assets/stylesheets/new_design/flex.scss b/app/assets/stylesheets/new_design/flex.scss new file mode 100644 index 000000000..43416c333 --- /dev/null +++ b/app/assets/stylesheets/new_design/flex.scss @@ -0,0 +1,19 @@ +.flex { + display: flex; + + &.align-center { + align-items: center; + } + + &.align-start { + align-items: flex-start; + } + + &.align-baseline { + align-items: baseline; + } + + &.justify-between { + justify-content: space-between; + } +} diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss index e1616d1e7..759b8b643 100644 --- a/app/assets/stylesheets/new_design/forms.scss +++ b/app/assets/stylesheets/new_design/forms.scss @@ -1,3 +1,4 @@ +@import "constants"; @import "colors"; .form { @@ -6,32 +7,175 @@ margin-bottom: 20px; } - label, - input { - font-size: 14px; - } - - label, - input[type=submit] { - margin-top: 24px; - } - label { - margin-bottom: 8px; - display: inline-block; + margin-bottom: $default-padding; + display: block; + + .mandatory { + color: $dark-red; + } + + .notice { + font-size: 14px; + display: block; + margin-top: $default-spacer; + color: $grey; + } } - input[type=text], + input[type=text]:not([data-address='true']), input[type=email], - input[type=password] { + input[type=password], + input[type=date], + input[type=number], + input[type=tel], + textarea, + select { display: block; - width: 100%; border-radius: 4px; border: solid 1px $border-grey; - padding: 16px; + margin-bottom: 2 * $default-padding; + padding: $default-padding; &:disabled { background-color: $border-grey; } } + + input[type=text], + input[type=email], + input[type=password], + input[type=date], + input[type=number], + input[type=tel], + textarea { + width: 100%; + } + + input[type=email], + input[type=number], + input[type=tel], { + max-width: 500px; + } + + input[type=checkbox], + input[type=radio] { + margin-bottom: 2 * $default-padding; + } + + input[type=date] { + max-width: 180px; + } + + input:invalid, + textarea:invalid { + box-shadow: none; + } + + input.touched:invalid, + textarea.touched:invalid { + border-color: $dark-red; + box-shadow: 0px 0px 5px $dark-red; + } + + select, + .select2-selection { + // hack found here: https://stackoverflow.com/questions/1895476/how-to-style-a-select-dropdown-with-css-only-without-javascript + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: image-url("icons/chevron-down.svg") no-repeat; + background-size: 14px; + background-position: right 10px center; + padding-right: 3 * $default-spacer; + + // CAUTION: IE hackery ahead + &::-ms-expand { + display: none; // remove default arrow in IE 10 and 11 */ + } + + // target Internet Explorer 9 to undo the custom arrow */ + @media screen and (min-width:0\0) { + select { + background: none\9; + } + } + } + + .select2 { + min-width: 50%; + } + + .select2-container { + display: block; + margin-bottom: 2 * $default-padding; + + &.select2-container--focus { + .select2-selection { + border-color: $border-grey; + } + } + + // scss-lint:disable SelectorFormat + .select2-selection__rendered { + padding: $default-padding; + } + + .select2-selection__choice { + background-color: #FFFFFF; + } + // scss-lint:enable + } + + .twitter-typeahead { + margin-bottom: 2 * $default-padding; + } + + input.tt-input, + input.tt-hint { + border-radius: 4px; + border: solid 1px $border-grey; + padding: $default-padding; + } + + input.tt-hint { + color: $grey; + } + + .datetime { + input[type=date] { + display: inline-block; + } + + select { + display: inline-block; + } + } + + .header-section { + color: #4393F3; + font-weight: bold; + font-size: 20px; + margin-bottom: 2 * $default-padding; + } + + .explication-libelle { + font-weight: bold; + font-size: 20px; + margin-bottom: $default-padding; + } + + .explication { + background-color: $light-grey; + padding: $default-padding; + margin-bottom: 2 * $default-padding; + } + + .send-wrapper { + text-align: right; + + .send { + margin-bottom: $default-padding; + } + } } diff --git a/app/assets/stylesheets/new_design/icons.scss b/app/assets/stylesheets/new_design/icons.scss new file mode 100644 index 000000000..2e09bac25 --- /dev/null +++ b/app/assets/stylesheets/new_design/icons.scss @@ -0,0 +1,59 @@ +i { + display: inline-block; + width: 24px; + height: 24px; + background-size: 24px 24px; + vertical-align: bottom; + + &.follow { + background-image: image-url("icons/follow-folder.svg"); + } + + &.unfollow { + background-image: image-url("icons/unfollow-folder.svg"); + } + + &.archive { + background-image: image-url("icons/archive.svg"); + } + + &.unarchive { + background-image: image-url("icons/unarchive.svg"); + } + + &.folder { + background-image: image-url("icons/folder.svg"); + } + + &.accept { + background-image: image-url("icons/accept.svg"); + } + + &.close { + background-image: image-url("icons/close.svg"); + } + + &.without-continuation { + background-image: image-url("icons/without-continuation.svg"); + } + + &.edit { + background-image: image-url("icons/edit-folder-blue.svg"); + } + + &.in-progress { + background-image: image-url("icons/in-progress-blue.svg"); + } + + &.bubble { + background-image: image-url("icons/bubble.svg"); + } + + &.attachment { + background-image: image-url("icons/attachment.svg"); + } + + &.lock { + background-image: image-url("icons/lock.svg"); + } +} diff --git a/app/assets/stylesheets/new_design/labels.scss b/app/assets/stylesheets/new_design/labels.scss new file mode 100644 index 000000000..5c9de758c --- /dev/null +++ b/app/assets/stylesheets/new_design/labels.scss @@ -0,0 +1,36 @@ +@import "colors"; +@import "constants"; + +.label { + display: inline-block; + padding: 4px $default-spacer; + background: $grey; + border: 1px solid transparent; + color: #FFFFFF; + border-radius: 4px; + font-size: 12px; + + &.instruction { + background-color: #FFFFFF; + color: $blue; + border: 1px solid $blue; + } + + &.construction { + background-color: #FFFFFF; + color: $black; + border: 1px solid $black; + } + + &.closed { + background-color: $green; + } + + &.refused { + background-color: $dark-red; + } + + &.without-continuation { + background-color: $black; + } +} diff --git a/app/assets/stylesheets/new_design/landing.scss b/app/assets/stylesheets/new_design/landing.scss index 769f49622..39b2159e6 100644 --- a/app/assets/stylesheets/new_design/landing.scss +++ b/app/assets/stylesheets/new_design/landing.scss @@ -11,10 +11,6 @@ @include vertical-padding(60px); } -.landing-panel-inner-content { - @extend %page-width-container; -} - $landing-breakpoint: 1040px; .hero-wrapper { diff --git a/app/assets/stylesheets/new_design/layouts.scss b/app/assets/stylesheets/new_design/layouts.scss index 98fcfe043..467f18ff0 100644 --- a/app/assets/stylesheets/new_design/layouts.scss +++ b/app/assets/stylesheets/new_design/layouts.scss @@ -1,6 +1,6 @@ @import "colors"; +@import "common"; @import "constants"; -@import "placeholders"; .two-columns { $column-padding: 60px; @@ -9,7 +9,7 @@ background: linear-gradient(to right, #FFFFFF 0%, #FFFFFF 50%, $light-grey 50%, $light-grey 100%); .columns-container { - @extend %page-width-container; + @extend .container; display: flex; flex-direction: row; align-items: center; diff --git a/app/assets/stylesheets/new_design/map.scss b/app/assets/stylesheets/new_design/map.scss new file mode 100644 index 000000000..dde4e1494 --- /dev/null +++ b/app/assets/stylesheets/new_design/map.scss @@ -0,0 +1,4 @@ +#map { + height: 400px; + margin-bottom: 16px; +} diff --git a/app/assets/stylesheets/new_design/messagerie.scss b/app/assets/stylesheets/new_design/messagerie.scss new file mode 100644 index 000000000..eb5901dfe --- /dev/null +++ b/app/assets/stylesheets/new_design/messagerie.scss @@ -0,0 +1,45 @@ +@import "colors"; +@import "common"; +@import "constants"; + +.messagerie { + ul { + max-height: 350px; + overflow-y: scroll; + border: 1px solid $border-grey; + padding: 2 * $default-spacer; + margin-bottom: $default-spacer; + } + + li { + display: flex; + align-items: flex-start; + margin-bottom: 2 * $default-padding; + } + + .person-icon { + margin-right: $default-spacer; + } + + h2 { + margin-bottom: $default-spacer; + } + + .mail { + font-weight: bold; + } + + .guest, + .date { + font-size: 12px; + color: $grey; + } + + .date { + float: right; + } + + .attachment-link { + margin-top: $default-spacer; + } +} diff --git a/app/assets/stylesheets/new_design/motivation.scss b/app/assets/stylesheets/new_design/motivation.scss new file mode 100644 index 000000000..e8e91d334 --- /dev/null +++ b/app/assets/stylesheets/new_design/motivation.scss @@ -0,0 +1,29 @@ +@import "colors"; +@import "constants"; + +.motivation { + display: none; + padding: $default-padding; + color: $black; + width: 450px; + + h3 { + font-size: 22px; + margin-bottom: $default-spacer * 2; + + i { + vertical-align: sub; + margin-right: $default-spacer; + } + } + + textarea { + margin-bottom: $default-spacer; + } + + .help { + color: $grey; + font-size: 11px; + margin-bottom: $default-spacer * 2; + } +} diff --git a/app/assets/stylesheets/new_design/new_alert.scss b/app/assets/stylesheets/new_design/new_alert.scss index c57594c31..d88c9d6f0 100644 --- a/app/assets/stylesheets/new_design/new_alert.scss +++ b/app/assets/stylesheets/new_design/new_alert.scss @@ -10,3 +10,9 @@ border-color: $light-red; color: $dark-red; } + +.alert-success { + background-color: $lighter-green; + border-color: $light-green; + color: $dark-green; +} diff --git a/app/assets/stylesheets/new_design/new_application.scss b/app/assets/stylesheets/new_design/new_application.scss index 82fb5fc2a..e575f56c5 100644 --- a/app/assets/stylesheets/new_design/new_application.scss +++ b/app/assets/stylesheets/new_design/new_application.scss @@ -3,4 +3,7 @@ // = require ./common // = require ./utils // = require ./fonts +// = require leaflet.1.1.0 +// = require select2 +// = require typeahead // = require_tree . diff --git a/app/assets/stylesheets/new_design/new_footer.scss b/app/assets/stylesheets/new_design/new_footer.scss index c88b853cd..2c414559b 100644 --- a/app/assets/stylesheets/new_design/new_footer.scss +++ b/app/assets/stylesheets/new_design/new_footer.scss @@ -3,14 +3,18 @@ @import "mixins"; @import "placeholders"; -.footer { +footer { @include vertical-padding(72px); background-color: $light-grey; border-top: 1px solid $border-grey; -} + position: absolute; + bottom: 0; + width: 100%; + height: $footer-height; -.footer-inner-content { - @extend %page-width-container; + @media (max-width: 1000px) { + height: $footer-height-mobile; + } } .footer-columns { diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index 9ea00b625..380f78ae0 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -1,7 +1,7 @@ -@import "constants"; @import "colors"; +@import "common"; +@import "constants"; @import "mixins"; -@import "placeholders"; // FIXME: Rename when the header is generalized .new-header { @@ -14,10 +14,37 @@ } .header-inner-content { - @extend %page-width-container; + @extend .container; display: flex; justify-content: space-between; - padding-top: 17px; + height: 100%; +} + +.header-logo { + display: inline-block; + margin-right: 4 * $default-spacer; +} + +.header-tabs { + li { + display: inline-block; + } + + a { + display: inline-block; + padding: 23px $default-padding; + font-size: 18px; + color: $black; + + &.active { + color: $blue; + border-bottom: 2px solid $blue; + } + + &:not(.active):hover { + background-color: $light-grey; + } + } } .header-right-content { @@ -41,10 +68,11 @@ padding-right: 42px; float: right; width: 300px; + margin: 0; } button { - padding: 6px 9px; + padding: 9px; border: none; background: none; cursor: pointer; @@ -80,6 +108,7 @@ border: 1px solid $border-grey; min-width: 270px; max-width: 340px; + z-index: 20; &.open { display: block; diff --git a/app/assets/stylesheets/new_design/notifications.scss b/app/assets/stylesheets/new_design/notifications.scss new file mode 100644 index 000000000..d9e6a9931 --- /dev/null +++ b/app/assets/stylesheets/new_design/notifications.scss @@ -0,0 +1,9 @@ +@import "colors"; + +span.notifications { + position: absolute; + width: 8px; + height: 8px; + border-radius: 4px; + background-color: $orange; +} diff --git a/app/assets/stylesheets/new_design/pagination.scss b/app/assets/stylesheets/new_design/pagination.scss new file mode 100644 index 000000000..464ed74ab --- /dev/null +++ b/app/assets/stylesheets/new_design/pagination.scss @@ -0,0 +1,10 @@ +@import "constants"; + +.pagination { + text-align: center; + margin-bottom: 3 * $default-spacer; + + > span { + margin: 0 $default-spacer; + } +} diff --git a/app/assets/stylesheets/new_design/patron.scss b/app/assets/stylesheets/new_design/patron.scss index 54bc0e0d3..9c255eb6f 100644 --- a/app/assets/stylesheets/new_design/patron.scss +++ b/app/assets/stylesheets/new_design/patron.scss @@ -1,10 +1,6 @@ @import "placeholders"; .patron { - .patron-container { - @extend %page-width-container; - } - p { margin-bottom: 20px; } diff --git a/app/assets/stylesheets/new_design/procedure-logo.scss b/app/assets/stylesheets/new_design/procedure-logo.scss new file mode 100644 index 000000000..e6de617d9 --- /dev/null +++ b/app/assets/stylesheets/new_design/procedure-logo.scss @@ -0,0 +1,17 @@ +@import "colors"; +@import "constants"; + +.procedure-logo { + display: flex; + background: #FFFFFF; + border: 1px solid $border-grey; + height: 84px; + width: 84px; + margin-right: 2 * $default-spacer; + + img { + margin: auto; + max-width: 80%; + max-height: 80%; + } +} diff --git a/app/assets/stylesheets/new_design/procedure_list.scss b/app/assets/stylesheets/new_design/procedure_list.scss new file mode 100644 index 000000000..210410596 --- /dev/null +++ b/app/assets/stylesheets/new_design/procedure_list.scss @@ -0,0 +1,77 @@ +@import "colors"; +@import "constants"; +@import "mixins"; + +.procedure-list { + .procedure-item { + border-bottom: 1px solid $border-grey; + + &:last-child { + border-bottom: none; + } + + a { + @include vertical-padding(24px); + color: $black; + width: 100%; + + &:hover { + background-color: $light-grey; + + .procedure-title { + text-decoration: underline; + } + } + } + } + + .procedure-logo { + height: 93px; + width: 93px; + margin-right: 3 * $default-spacer; + flex-shrink: 0; + } + + .procedure-title { + min-height: 40px; + font-size: 20px; + margin-bottom: 16px; + } + + .procedure-status { + margin-left: auto; + } + + .procedure-stats { + li { + min-height: 36px; + border-left: 1px solid $border-grey; + width: 90px; + position: relative; + + &:last-child { + border-right: 1px solid $border-grey; + } + + .stats-number, + .stats-legend { + text-align: center; + } + + .stats-number { + font-size: 14px; + font-weight: bold; + } + + .stats-legend { + font-size: 12px; + color: $grey; + } + } + + .notifications { + top: 3px; + right: 18px; + } + } +} diff --git a/app/assets/stylesheets/new_design/procedures_show.scss b/app/assets/stylesheets/new_design/procedures_show.scss new file mode 100644 index 000000000..340d1068d --- /dev/null +++ b/app/assets/stylesheets/new_design/procedures_show.scss @@ -0,0 +1,29 @@ +@import "colors"; +@import "common"; +@import "constants"; + +#procedure-show { + .procedure-logo { + margin-right: $default-padding; + flex-shrink: 0; + } + + h1 { + color: $black; + font-size: 22px; + margin-bottom: 2 * $default-padding; + } + + .dossiers-table { + margin: (3 * $default-spacer) auto; + } + + .procedure-actions { + margin-left: auto; + flex-shrink: 0; + + .dropdown-items li { + min-width: 150px; + } + } +} diff --git a/app/assets/stylesheets/new_design/table.scss b/app/assets/stylesheets/new_design/table.scss new file mode 100644 index 000000000..43b37eb77 --- /dev/null +++ b/app/assets/stylesheets/new_design/table.scss @@ -0,0 +1,52 @@ +@import "colors"; +@import "constants"; +@import "mixins"; + +.table { // TODO : tester de remplacer par l'élément table uniquement + width: 100%; + + tbody tr { + border-top: 1px solid $border-grey; + } + + td { + @include vertical-padding($default-spacer); + vertical-align: middle; + } + + th { + text-align: left; + font-weight: bold; + padding: (3 * $default-spacer) 2px; + } + + &.hoverable { + tbody tr:hover { + background: $light-grey; + } + } + + &.vertical { + font-size: 14px; + line-height: 22px; + + tr { + border-top: none; + } + + th { + @include vertical-padding($default-spacer); + font-weight: normal; + + &.header-section { + color: $blue; + font-weight: bold; + font-size: 20px; + } + } + + td { + font-weight: bold; + } + } +} diff --git a/app/assets/stylesheets/new_design/utils.scss b/app/assets/stylesheets/new_design/utils.scss index 2880cfe30..599609dd5 100644 --- a/app/assets/stylesheets/new_design/utils.scss +++ b/app/assets/stylesheets/new_design/utils.scss @@ -21,3 +21,14 @@ .hidden { display: none; } + +.width-100 { + width: 100%; +} + +.empty-text { + font-size: 20px; + font-weight: bold; + text-align: center; + margin: 60px 0; +} diff --git a/app/assets/stylesheets/search.scss b/app/assets/stylesheets/search.scss index 26e91409a..17514154a 100644 --- a/app/assets/stylesheets/search.scss +++ b/app/assets/stylesheets/search.scss @@ -1,6 +1,21 @@ +.new-design-button { + color: #FFFFFF; + display: block; + font-size: 20px; + margin: 16px 0; + padding: 8px; + text-align: center; + border: 1px solid #FFFFFF; + border-radius: 15px; + + &:hover { + background-color: #668ABD; + color: #FFFFFF; + } +} + #search-block { margin: 15px 10px 0 10px; - height: 30px; } #search-button { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fbaba5b73..524746fec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -8,7 +8,7 @@ class ApplicationController < ActionController::Base before_action :authorize_request_for_profiler def authorize_request_for_profiler - if Rails.env.production? && administration_signed_in? + if administration_signed_in? Rack::MiniProfiler.authorize_request end end diff --git a/app/controllers/backoffice/avis_controller.rb b/app/controllers/backoffice/avis_controller.rb index ec76c663a..d2375e74f 100644 --- a/app/controllers/backoffice/avis_controller.rb +++ b/app/controllers/backoffice/avis_controller.rb @@ -4,7 +4,7 @@ class Backoffice::AvisController < ApplicationController before_action :check_avis_exists_and_email_belongs_to_avis, only: [:sign_up, :create_gestionnaire] def create - avis = Avis.new(create_params.merge(claimant: current_gestionnaire, dossier: dossier)) + avis = Avis.new(create_params.merge(claimant: current_gestionnaire, dossier: dossier, confidentiel: true)) if avis.save flash[:notice] = "Votre demande d'avis a bien été envoyée à #{avis.email_to_display}" diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 1cced017a..4a9412636 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -98,9 +98,10 @@ class Backoffice::DossiersController < Backoffice::DossiersListController dossier = Dossier.find(params[:dossier_id]) dossier.received! - flash.notice = 'Dossier considéré comme reçu.' + current_gestionnaire.follow(dossier) + flash.notice = 'Dossier passé en instruction.' - redirect_to backoffice_dossier_path(id: dossier.id) + redirect_to_dossier(dossier) end def process_dossier @@ -142,16 +143,7 @@ class Backoffice::DossiersController < Backoffice::DossiersListController NotificationMailer.send_notification(dossier, template, attestation_pdf).deliver_now! - redirect_to backoffice_dossier_path(id: dossier.id) - end - - def follow - follow = current_gestionnaire.toggle_follow_dossier params[:dossier_id] - - current_gestionnaire.dossiers.find(params[:dossier_id]).next_step! 'gestionnaire', 'follow' - - flash.notice = (follow.class == Follow ? 'Dossier suivi' : 'Dossier relaché') - redirect_to request.referer + redirect_to_dossier(dossier) end def reload_smartlisting @@ -187,14 +179,22 @@ class Backoffice::DossiersController < Backoffice::DossiersListController def reopen create_dossier_facade params[:dossier_id] - @facade.dossier.replied! - flash.notice = 'Dossier réouvert.' + @facade.dossier.initiated! + flash.notice = 'Dossier repassé en construction.' - redirect_to backoffice_dossiers_path + redirect_to_dossier(@facade.dossier) end private + def redirect_to_dossier(dossier) + if params[:new_ui] # TODO delete new_ui when old UI is no longer used + redirect_to dossier_path(dossier.procedure, dossier) + else + redirect_to backoffice_dossier_path(id: dossier.id) + end + end + def check_attestation_emailable(dossier) if dossier&.attestation&.emailable? == false human_size = number_to_human_size(dossier.attestation.pdf.size) diff --git a/app/controllers/commentaires_controller.rb b/app/controllers/commentaires_controller.rb index 7dd8ac1d0..fbb9532d3 100644 --- a/app/controllers/commentaires_controller.rb +++ b/app/controllers/commentaires_controller.rb @@ -44,9 +44,7 @@ class CommentairesController < ApplicationController end if is_gestionnaire? - unless current_gestionnaire.follow? @commentaire.dossier - current_gestionnaire.toggle_follow_dossier @commentaire.dossier - end + current_gestionnaire.follow(@commentaire.dossier) redirect_to url_for(controller: 'backoffice/dossiers', action: :show, id: params['dossier_id']) else diff --git a/app/controllers/new_gestionnaire/avis_controller.rb b/app/controllers/new_gestionnaire/avis_controller.rb new file mode 100644 index 000000000..03dc94fec --- /dev/null +++ b/app/controllers/new_gestionnaire/avis_controller.rb @@ -0,0 +1,75 @@ +module NewGestionnaire + class AvisController < ApplicationController + layout 'new_application' + + A_DONNER_STATUS = 'a-donner' + DONNES_STATUS = 'donnes' + + def index + gestionnaire_avis = current_gestionnaire.avis.includes(dossier: [:procedure, :user]) + @avis_a_donner = gestionnaire_avis.without_answer + @avis_donnes = gestionnaire_avis.with_answer + + @statut = params[:statut].present? ? params[:statut] : A_DONNER_STATUS + + @avis = case @statut + when A_DONNER_STATUS + @avis_a_donner + when DONNES_STATUS + @avis_donnes + end + + @avis = @avis.page([params[:page].to_i, 1].max) + end + + def show + @avis = avis + @dossier = avis.dossier + end + + def instruction + @avis = avis + @dossier = avis.dossier + end + + def update + avis.update_attributes(avis_params) + flash.notice = 'Votre réponse est enregistrée.' + redirect_to instruction_avis_path(avis) + end + + def messagerie + @avis = avis + @dossier = avis.dossier + end + + def create_commentaire + Commentaire.create(commentaire_params.merge(email: current_gestionnaire.email, dossier: avis.dossier)) + redirect_to messagerie_avis_path(avis) + end + + def create_avis + confidentiel = avis.confidentiel || params[:avis][:confidentiel] + Avis.create(create_avis_params.merge(claimant: current_gestionnaire, dossier: avis.dossier, confidentiel: confidentiel)) + redirect_to instruction_avis_path(avis) + end + + private + + def avis + current_gestionnaire.avis.includes(dossier: [:avis, :commentaires]).find(params[:id]) + end + + def avis_params + params.require(:avis).permit(:answer) + end + + def commentaire_params + params.require(:commentaire).permit(:body) + end + + def create_avis_params + params.require(:avis).permit(:email, :introduction) + end + end +end diff --git a/app/controllers/new_gestionnaire/dossiers_controller.rb b/app/controllers/new_gestionnaire/dossiers_controller.rb index 417e90ea8..8bd5cac96 100644 --- a/app/controllers/new_gestionnaire/dossiers_controller.rb +++ b/app/controllers/new_gestionnaire/dossiers_controller.rb @@ -4,10 +4,99 @@ module NewGestionnaire send_data(dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf') end + def show + @dossier = dossier + dossier.notifications.demande.mark_as_read + end + + def messagerie + @dossier = dossier + dossier.notifications.messagerie.mark_as_read + end + + def annotations_privees + @dossier = dossier + dossier.notifications.annotations_privees.mark_as_read + end + + def avis + @dossier = dossier + dossier.notifications.avis.mark_as_read + end + + def follow + current_gestionnaire.follow(dossier) + dossier.next_step!('gestionnaire', 'follow') + flash.notice = 'Dossier suivi' + redirect_back(fallback_location: procedures_url) + end + + def unfollow + current_gestionnaire.followed_dossiers.delete(dossier) + flash.notice = "Vous ne suivez plus le dossier nº #{dossier.id}" + + redirect_back(fallback_location: procedures_url) + end + + def archive + dossier.update_attributes(archived: true) + redirect_back(fallback_location: procedures_url) + end + + def unarchive + dossier.update_attributes(archived: false) + redirect_back(fallback_location: procedures_url) + end + + def create_commentaire + Commentaire.create(commentaire_params.merge(email: current_gestionnaire.email, dossier: dossier)) + flash.notice = "Message envoyé" + redirect_to messagerie_dossier_path(dossier.procedure, dossier) + end + + def position + etablissement = dossier.etablissement + point = Carto::Geocodeur.convert_adresse_to_point(etablissement.geo_adresse) unless etablissement.nil? + + lon = "2.428462" + lat = "46.538192" + zoom = "13" + + unless point.nil? + lon = point.x.to_s + lat = point.y.to_s + end + + render json: { lon: lon, lat: lat, zoom: zoom, dossier_id: params[:dossier_id] } + end + + def create_avis + Avis.create(avis_params.merge(claimant: current_gestionnaire, dossier: dossier)) + redirect_to avis_dossier_path(dossier.procedure, dossier) + end + + def update_annotations + dossier = current_gestionnaire.dossiers.includes(champs_private: :type_de_champ).find(params[:dossier_id]) + dossier.update_attributes(champs_private_params) + redirect_to annotations_privees_dossier_path(dossier.procedure, dossier) + end + private def dossier - Dossier.find(params[:dossier_id]) + current_gestionnaire.dossiers.find(params[:dossier_id]) + end + + def commentaire_params + params.require(:commentaire).permit(:body) + end + + def avis_params + params.require(:avis).permit(:email, :introduction, :confidentiel) + end + + def champs_private_params + params.require(:dossier).permit(champs_private_attributes: [:id, :value, value: []]) end end end diff --git a/app/controllers/new_gestionnaire/gestionnaire_controller.rb b/app/controllers/new_gestionnaire/gestionnaire_controller.rb index 76ef0b937..c675f9566 100644 --- a/app/controllers/new_gestionnaire/gestionnaire_controller.rb +++ b/app/controllers/new_gestionnaire/gestionnaire_controller.rb @@ -1,5 +1,7 @@ module NewGestionnaire class GestionnaireController < ApplicationController + layout "new_application" + before_action :authenticate_gestionnaire! end end diff --git a/app/controllers/new_gestionnaire/procedures_controller.rb b/app/controllers/new_gestionnaire/procedures_controller.rb index bfffad1fa..6aa514c45 100644 --- a/app/controllers/new_gestionnaire/procedures_controller.rb +++ b/app/controllers/new_gestionnaire/procedures_controller.rb @@ -1,6 +1,71 @@ module NewGestionnaire class ProceduresController < GestionnaireController - before_action :ensure_ownership! + before_action :ensure_ownership!, except: [:index] + before_action :redirect_to_avis_if_needed, only: [:index] + + def index + @procedures = current_gestionnaire.procedures.order(archived_at: :desc, published_at: :desc) + + dossiers = current_gestionnaire.dossiers + @dossiers_count_per_procedure = dossiers.all_state.group(:procedure_id).reorder(nil).count + @dossiers_a_suivre_count_per_procedure = dossiers.without_followers.en_cours.group(:procedure_id).reorder(nil).count + @dossiers_archived_count_per_procedure = dossiers.archived.group(:procedure_id).count + @dossiers_termines_count_per_procedure = dossiers.termine.group(:procedure_id).reorder(nil).count + + @followed_dossiers_count_per_procedure = current_gestionnaire + .followed_dossiers + .en_cours + .where(procedure: @procedures) + .group(:procedure_id) + .reorder(nil) + .count + + @notifications_count_per_procedure = current_gestionnaire.notifications_count_per_procedure + end + + def show + @procedure = procedure + + @a_suivre_dossiers = procedure + .dossiers + .includes(:user) + .without_followers + .en_cours + + @followed_dossiers = current_gestionnaire + .followed_dossiers + .includes(:user, :notifications) + .where(procedure: @procedure) + .en_cours + + @followed_dossiers_id = current_gestionnaire + .followed_dossiers + .where(procedure: @procedure) + .pluck(:id) + + @termines_dossiers = procedure.dossiers.includes(:user).termine + + @all_state_dossiers = procedure.dossiers.includes(:user).all_state + + @archived_dossiers = procedure.dossiers.includes(:user).archived + + @statut = params[:statut].present? ? params[:statut] : 'a-suivre' + + @dossiers = case @statut + when 'a-suivre' + @a_suivre_dossiers + when 'suivis' + @followed_dossiers + when 'traites' + @termines_dossiers + when 'tous' + @all_state_dossiers + when 'archives' + @archived_dossiers + end + + @dossiers = @dossiers.page([params[:page].to_i, 1].max) + end private @@ -14,5 +79,11 @@ module NewGestionnaire redirect_to root_path end end + + def redirect_to_avis_if_needed + if current_gestionnaire.procedures.count == 0 && current_gestionnaire.avis.count > 0 + redirect_to avis_index_path + end + end end end diff --git a/app/controllers/new_gestionnaire/recherches_controller.rb b/app/controllers/new_gestionnaire/recherches_controller.rb new file mode 100644 index 000000000..d0d493805 --- /dev/null +++ b/app/controllers/new_gestionnaire/recherches_controller.rb @@ -0,0 +1,23 @@ +module NewGestionnaire + class RecherchesController < GestionnaireController + def index + @search_terms = params[:q] + + # exact id match? + if @search_terms.to_i != 0 + @dossiers = current_gestionnaire.dossiers.where(id: @search_terms.to_i) + end + + @dossiers = Dossier.none if @dossiers.nil? + + # full text search + if @dossiers.empty? + @dossiers = Search.new( + gestionnaire: current_gestionnaire, + query: @search_terms, + page: params[:page] + ).results + end + end + end +end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index b65437fa7..0cd686880 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -2,6 +2,10 @@ class RootController < ApplicationController layout 'new_application' def index + if params[:new_ui] && gestionnaire_signed_in? # TODO delete new_ui when old UI is no longer used + return redirect_to procedures_path + end + if administrateur_signed_in? return redirect_to admin_procedures_path @@ -32,5 +36,45 @@ class RootController < ApplicationController end def patron + description = 'a not so long description' + + all_champs = TypeDeChamp.type_champs + .map { |name, _| TypeDeChamp.new(type_champ: name, libelle: name, description: description, mandatory: true) } + .map { |type_de_champ| ChampPublic.new(type_de_champ: type_de_champ) } + .map.with_index do |champ, i| + champ.id = i + champ + end + + all_champs + .select { |champ| champ.type_champ == 'header_section' } + .each { |champ| champ.type_de_champ.libelle = 'un super titre de section' } + + all_champs + .select { |champ| %w(drop_down_list multiple_drop_down_list).include?(champ.type_champ) } + .each do |champ| + champ.type_de_champ.drop_down_list = DropDownList.new(type_de_champ: champ.type_de_champ) + champ.drop_down_list.value = +"option A +option B +-- avant l'option C -- +option C" + champ.value = '["option B", "option C"]' + end + + type_champ_values = { + 'date': '2016-07-26', + 'datetime': '26/07/2016 07:35', + 'textarea': 'Une description de mon projet', + 'explication': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In erat mauris, faucibus quis pharetra sit amet, pretium ac libero. Etiam vehicula eleifend bibendum. Morbi gravida metus ut sapien condimentum sodales mollis augue sodales. Vestibulum quis quam at sem placerat aliquet', + } + + type_champ_values.each do |(type_champ, value)| + all_champs + .select { |champ| champ.type_champ == type_champ.to_s } + .each { |champ| champ.value = value } + end + + @dossier = Dossier.new(champs: all_champs) end end diff --git a/app/controllers/users/carte_controller.rb b/app/controllers/users/carte_controller.rb index 3c10c7df9..09c8dd0ec 100644 --- a/app/controllers/users/carte_controller.rb +++ b/app/controllers/users/carte_controller.rb @@ -56,7 +56,7 @@ class Users::CarteController < UsersController def self.route_authorization { - states: [:draft, :initiated, :replied, :updated], + states: [:draft, :initiated], api_carto: true } end diff --git a/app/controllers/users/description_controller.rb b/app/controllers/users/description_controller.rb index 0771c13ea..ac5abf6ab 100644 --- a/app/controllers/users/description_controller.rb +++ b/app/controllers/users/description_controller.rb @@ -93,7 +93,7 @@ class Users::DescriptionController < UsersController def self.route_authorization { - states: [:draft, :initiated, :replied, :updated] + states: [:draft, :initiated] } end diff --git a/app/controllers/users/recapitulatif_controller.rb b/app/controllers/users/recapitulatif_controller.rb index 88dd4d2f8..5066c9dc1 100644 --- a/app/controllers/users/recapitulatif_controller.rb +++ b/app/controllers/users/recapitulatif_controller.rb @@ -18,7 +18,7 @@ class Users::RecapitulatifController < UsersController def self.route_authorization { - states: [:initiated, :replied, :updated, :received, :without_continuation, :closed, :refused] + states: [:initiated, :received, :without_continuation, :closed, :refused] } end diff --git a/app/decorators/champ_decorator.rb b/app/decorators/champ_decorator.rb index 8af930c2d..4652d5d08 100644 --- a/app/decorators/champ_decorator.rb +++ b/app/decorators/champ_decorator.rb @@ -4,8 +4,14 @@ class ChampDecorator < Draper::Decorator def value if type_champ == "date" && object.value.present? Date.parse(object.value).strftime("%d/%m/%Y") - elsif type_champ == 'checkbox' + elsif type_champ.in? ["checkbox", "engagement"] object.value == 'on' ? 'Oui' : 'Non' + elsif type_champ == 'yes_no' + if object.value == 'true' + 'Oui' + elsif object.value == 'false' + 'Non' + end elsif type_champ == 'multiple_drop_down_list' && object.value.present? JSON.parse(object.value).join(', ') else diff --git a/app/facades/admin_procedures_show_facades.rb b/app/facades/admin_procedures_show_facades.rb index 297f74358..b099f5c43 100644 --- a/app/facades/admin_procedures_show_facades.rb +++ b/app/facades/admin_procedures_show_facades.rb @@ -32,14 +32,6 @@ class AdminProceduresShowFacades dossiers.size end - def dossiers_waiting_gestionnaire_total - dossiers.waiting_for_gestionnaire.size - end - - def dossiers_waiting_user_total - dossiers.waiting_for_user.size - end - def dossiers_termine_total dossiers.where(state: :termine).size end diff --git a/app/models/champ.rb b/app/models/champ.rb index 58ad82378..950a6b70b 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -6,6 +6,9 @@ class Champ < ActiveRecord::Base delegate :libelle, :type_champ, :order_place, :mandatory, :description, :drop_down_list, to: :type_de_champ before_save :format_date_to_iso, if: Proc.new { type_champ == 'date' } + before_save :serialize_datetime_if_needed, if: Proc.new { type_champ == 'datetime' } + before_save :multiple_select_to_string, if: Proc.new { type_champ == 'multiple_drop_down_list' } + after_save :internal_notification, if: Proc.new { !dossier.nil? } def mandatory? @@ -65,9 +68,39 @@ class Champ < ActiveRecord::Base self.value = date end + def serialize_datetime_if_needed + if (value =~ /=>/).present? + date = begin + hash_date = YAML.safe_load(value.gsub('=>', ': ')) + year, month, day, hour, minute = hash_date.values_at(1,2,3,4,5) + DateTime.new(year, month, day, hour, minute).strftime("%d/%m/%Y %H:%M") + rescue + nil + end + + self.value = date + end + end + def internal_notification - unless dossier.state == 'draft' - NotificationService.new('champs', self.dossier.id, self.libelle).notify + if dossier.state != 'draft' + if type == 'ChampPublic' + NotificationService.new('champs', self.dossier.id, self.libelle).notify + else + NotificationService.new('annotations_privees', self.dossier.id, self.libelle).notify + end + end + end + + def multiple_select_to_string + if value.present? + json = JSON.parse(value) + if json == [''] + self.value = nil + else + json = json - [''] + self.value = json.to_s + end end end end diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index d62e87eac..cf88b094a 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -4,6 +4,8 @@ class Commentaire < ActiveRecord::Base belongs_to :piece_justificative + scope :ordered, -> { order(created_at: :asc) } + after_create :notify def header diff --git a/app/models/dossier.rb b/app/models/dossier.rb index eb0c573f0..94cf0aa5c 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -2,8 +2,6 @@ class Dossier < ActiveRecord::Base enum state: { draft: 'draft', initiated: 'initiated', - replied: 'replied', # action utilisateur demandé - updated: 'updated', # etude par l'administration en cours received: 'received', closed: 'closed', refused: 'refused', @@ -12,12 +10,9 @@ class Dossier < ActiveRecord::Base BROUILLON = %w(draft) NOUVEAUX = %w(initiated) - OUVERT = %w(updated replied) - WAITING_FOR_GESTIONNAIRE = %w(updated) - WAITING_FOR_USER = %w(replied) - EN_CONSTRUCTION = %w(initiated updated replied) + EN_CONSTRUCTION = %w(initiated) EN_INSTRUCTION = %w(received) - A_INSTRUIRE = %w(received) + EN_CONSTRUCTION_OU_INSTRUCTION = EN_CONSTRUCTION + EN_INSTRUCTION TERMINE = %w(closed refused without_continuation) has_one :etablissement, dependent: :destroy @@ -42,31 +37,31 @@ class Dossier < ActiveRecord::Base belongs_to :procedure belongs_to :user + accepts_nested_attributes_for :champs + accepts_nested_attributes_for :champs_private + default_scope { where(hidden_at: nil) } - scope :state_brouillon, -> { where(state: BROUILLON) } - scope :state_not_brouillon, -> { where.not(state: BROUILLON) } - scope :state_nouveaux, -> { where(state: NOUVEAUX) } - scope :state_ouvert, -> { where(state: OUVERT) } - scope :state_waiting_for_gestionnaire, -> { where(state: WAITING_FOR_GESTIONNAIRE) } - scope :state_waiting_for_user, -> { where(state: WAITING_FOR_USER) } - scope :state_en_construction, -> { where(state: EN_CONSTRUCTION) } - scope :state_en_instruction, -> { where(state: EN_INSTRUCTION) } - scope :state_a_instruire, -> { where(state: A_INSTRUIRE) } - scope :state_termine, -> { where(state: TERMINE) } + scope :state_brouillon, -> { where(state: BROUILLON) } + scope :state_not_brouillon, -> { where.not(state: BROUILLON) } + scope :state_nouveaux, -> { where(state: NOUVEAUX) } + scope :state_en_construction, -> { where(state: EN_CONSTRUCTION) } + scope :state_en_instruction, -> { where(state: EN_INSTRUCTION) } + scope :state_en_construction_ou_instruction, -> { where(state: EN_CONSTRUCTION_OU_INSTRUCTION) } + scope :state_termine, -> { where(state: TERMINE) } scope :archived, -> { where(archived: true) } scope :not_archived, -> { where(archived: false) } scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } - scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) } - scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) } - scope :ouvert, -> { not_archived.state_ouvert.order_by_updated_at(:asc) } - scope :waiting_for_gestionnaire, -> { not_archived.state_waiting_for_gestionnaire.order_by_updated_at(:asc) } - scope :waiting_for_user, -> { not_archived.state_waiting_for_user.order_by_updated_at(:asc) } - scope :a_instruire, -> { not_archived.state_a_instruire.order_by_updated_at(:asc) } - scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) } - scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) } + scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) } + scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) } + scope :en_instruction, -> { not_archived.state_en_instruction.order_by_updated_at(:asc) } + scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) } + scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) } + scope :en_cours, -> { not_archived.state_en_construction_ou_instruction.order_by_updated_at(:asc) } + scope :without_followers, -> { includes(:follows).where(follows: { id: nil }) } + scope :with_unread_notifications, -> { where(notifications: { already_read: false }) } accepts_nested_attributes_for :individual @@ -96,6 +91,17 @@ class Dossier < ActiveRecord::Base pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0 end + def notifications_summary + unread_notifications = notifications.unread + + { + demande: unread_notifications.select(&:demande?).present?, + avis: unread_notifications.select(&:avis?).present?, + messagerie: unread_notifications.select(&:messagerie?).present?, + annotations_privees: unread_notifications.select(&:annotations_privees?).present? + } + end + def retrieve_last_piece_justificative_by_type(type) pieces_justificatives.where(type_de_piece_justificative_id: type).last end @@ -152,27 +158,9 @@ class Dossier < ActiveRecord::Base if draft? initiated! end - when 'update' - if replied? - updated! - end - when 'comment' - if replied? - updated! - end end when 'gestionnaire' case action - when 'comment' - if updated? - replied! - elsif initiated? - replied! - end - when 'follow' - if initiated? - updated! - end when 'close' if received? self.attestation = build_attestation @@ -213,6 +201,22 @@ class Dossier < ActiveRecord::Base BROUILLON.include?(state) end + def en_construction? + EN_CONSTRUCTION.include?(state) + end + + def en_instruction? + EN_INSTRUCTION.include?(state) + end + + def en_construction_ou_instruction? + EN_CONSTRUCTION_OU_INSTRUCTION.include?(state) + end + + def termine? + TERMINE.include?(state) + end + def cerfa_available? procedure.cerfa_flag? && cerfa.size != 0 end @@ -314,6 +318,18 @@ class Dossier < ActiveRecord::Base parts.join end + def avis_for(gestionnaire) + if gestionnaire.dossiers.include?(self) + avis.order(created_at: :asc) + else + avis + .where(confidentiel: false) + .or(avis.where(claimant: gestionnaire)) + .or(avis.where(gestionnaire: gestionnaire)) + .order(created_at: :asc) + end + end + private def build_attestation diff --git a/app/models/drop_down_list.rb b/app/models/drop_down_list.rb index 979112f4c..150bebb73 100644 --- a/app/models/drop_down_list.rb +++ b/app/models/drop_down_list.rb @@ -14,6 +14,10 @@ class DropDownList < ActiveRecord::Base champ.object.value.blank? ? [] : multiple ? JSON.parse(champ.object.value) : [champ.object.value] end + def selected_options_without_decorator(champ) + champ.value.blank? ? [] : multiple ? JSON.parse(champ.value) : [champ.value] + end + def multiple type_de_champ.type_champ == 'multiple_drop_down_list' end diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index cbaa3a6de..931ab09d2 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -7,7 +7,7 @@ class Gestionnaire < ActiveRecord::Base has_one :preference_smart_listing_page, dependent: :destroy has_many :assign_to, dependent: :destroy - has_many :procedures, through: :assign_to + has_many :procedures, -> { publiees_ou_archivees }, through: :assign_to has_many :dossiers, -> { state_not_brouillon }, through: :procedures has_many :followed_dossiers, through: :follows, source: :dossier has_many :follows @@ -34,21 +34,14 @@ class Gestionnaire < ActiveRecord::Base dossiers.where(id: dossier_id).any? end - def toggle_follow_dossier dossier_id - dossier = dossier_id - dossier = Dossier.find(dossier_id) unless dossier_id.class == Dossier + def follow(dossier) + return if follow?(dossier) - Follow.create!(dossier: dossier, gestionnaire: self) - rescue ActiveRecord::RecordInvalid - Follow.where(dossier: dossier, gestionnaire: self).delete_all - rescue ActiveRecord::RecordNotFound - nil + followed_dossiers << dossier end - def follow? dossier_id - dossier_id = dossier_id.id if dossier_id.class == Dossier - - Follow.where(gestionnaire_id: id, dossier_id: dossier_id).any? + def follow?(dossier) + followed_dossiers.include?(dossier) end def assigned_on_procedure?(procedure_id) @@ -100,6 +93,14 @@ class Gestionnaire < ActiveRecord::Base Notification.unread.where(dossier_id: followed_dossiers_id).select(:dossier_id).distinct(:dossier_id).count end + def notifications_count_per_procedure + followed_dossiers + .joins(:notifications) + .where(notifications: { already_read: false }) + .group('procedure_id') + .count + end + def dossiers_with_notifications_count notifications.pluck(:dossier_id).uniq.count end diff --git a/app/models/notification.rb b/app/models/notification.rb index 53d8af02d..a9fb5b7dc 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -5,10 +5,37 @@ class Notification < ActiveRecord::Base piece_justificative: 'piece_justificative', champs: 'champs', submitted: 'submitted', - avis: 'avis' + avis: 'avis', + annotations_privees: 'annotations_privees' } + DEMANDE = %w(cerfa piece_justificative champs submitted) + AVIS = %w(avis) + MESSAGERIE = %w(commentaire) + ANNOTATIONS_PRIVEES = %w(annotations_privees) + belongs_to :dossier - scope :unread, -> { where(already_read: false) } + scope :unread, -> { where(already_read: false) } + scope :demande, -> { where(type_notif: DEMANDE) } + scope :avis, -> { where(type_notif: AVIS) } + scope :messagerie, -> { where(type_notif: MESSAGERIE) } + scope :annotations_privees, -> { where(type_notif: ANNOTATIONS_PRIVEES) } + scope :mark_as_read, -> { update_all(already_read: true) } + + def demande? + Notification::DEMANDE.include?(type_notif) + end + + def avis? + Notification::AVIS.include?(type_notif) + end + + def messagerie? + Notification::MESSAGERIE.include?(type_notif) + end + + def annotations_privees? + Notification::ANNOTATIONS_PRIVEES.include?(type_notif) + end end diff --git a/app/services/dossiers_list_gestionnaire_service.rb b/app/services/dossiers_list_gestionnaire_service.rb index 2a7058456..3f5135164 100644 --- a/app/services/dossiers_list_gestionnaire_service.rb +++ b/app/services/dossiers_list_gestionnaire_service.rb @@ -8,7 +8,7 @@ class DossiersListGestionnaireService def dossiers_to_display @dossiers_to_display ||= {'nouveaux' => nouveaux, - 'a_traiter' => ouvert, + 'a_traiter' => nouveaux, 'a_instruire' => a_instruire, 'termine' => termine, 'archive' => archive, @@ -31,12 +31,8 @@ class DossiersListGestionnaireService @nouveaux ||= filter_dossiers.nouveaux end - def ouvert - @ouvert ||= filter_dossiers.ouvert - end - def a_instruire - @a_instruire ||= filter_dossiers.a_instruire + @a_instruire ||= filter_dossiers.en_instruction end def archive diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml index 5827186b6..8d564cb22 100644 --- a/app/views/admin/procedures/show.html.haml +++ b/app/views/admin/procedures/show.html.haml @@ -98,14 +98,6 @@ %div = @facade.dossiers_total - %h4.text-danger Attente Accompagnateur - %div - = @facade.dossiers_waiting_gestionnaire_total - - %h4.text-info Attente Usager - %div - = @facade.dossiers_waiting_user_total - %h4.text-success Terminé %div = @facade.dossiers_termine_total diff --git a/app/views/backoffice/dossiers/_list.html.haml b/app/views/backoffice/dossiers/_list.html.haml index 509684096..5b284ffbd 100644 --- a/app/views/backoffice/dossiers/_list.html.haml +++ b/app/views/backoffice/dossiers/_list.html.haml @@ -52,10 +52,10 @@ = value %td.center - - if current_gestionnaire.follow?(dossier.id) - = link_to('Quitter', backoffice_dossier_follow_path(dossier_id: dossier.id), 'data-method' => :put, class: 'btn-sm btn-danger', id: "suivre_dossier_#{dossier.id}") + - if current_gestionnaire.follow?(dossier) + = link_to('Quitter', unfollow_dossier_path(dossier.procedure, dossier), method: :patch, class: 'btn-sm btn-danger', id: "suivre_dossier_#{dossier.id}") - else - = link_to('Suivre', backoffice_dossier_follow_path(dossier_id: dossier.id), 'data-method' => :put, class: 'btn-sm btn-primary', id: "suivre_dossier_#{dossier.id}") + = link_to('Suivre', follow_dossier_path(dossier.procedure, dossier), method: :patch, class: 'btn-sm btn-primary', id: "suivre_dossier_#{dossier.id}") %td.center{ style: "color: #{dossier.total_follow == 0 ? 'red' : ''}" } = dossier.total_follow diff --git a/app/views/dossiers/_dossier_show.html.haml b/app/views/dossiers/_dossier_show.html.haml index afb5a5c77..09f09cd99 100644 --- a/app/views/dossiers/_dossier_show.html.haml +++ b/app/views/dossiers/_dossier_show.html.haml @@ -13,7 +13,7 @@ .col-xs-8.title-no-expanse .carret-right INFORMATIONS DU DEMANDEUR - - if !@current_gestionnaire && ["draft", "updated", "replied", "initiated"].include?(@facade.dossier.state) + - if !@current_gestionnaire && ["draft", "initiated"].include?(@facade.dossier.state) %a#add_siret.action{ href: users_dossier_add_siret_path(dossier_id: @facade.dossier.id) } .col-lg-4.col-md-4.col-sm-4.col-xs-4.action Renseigner un SIRET diff --git a/app/views/dossiers/_infos_dossier.html.haml b/app/views/dossiers/_infos_dossier.html.haml index 4d286fec4..d53fe4e59 100644 --- a/app/views/dossiers/_infos_dossier.html.haml +++ b/app/views/dossiers/_infos_dossier.html.haml @@ -46,11 +46,6 @@ = dossier.text_summary - else Pas de dossier associé - - elsif champ.type_champ == 'yes_no' - - if champ.decorate.value == 'true' - Oui - - elsif champ.decorate.value == 'false' - Non - else = sanitize(champ.decorate.value) diff --git a/app/views/layouts/_crisp.html.haml b/app/views/layouts/_crisp.html.haml index d16217cd0..4100bd050 100644 --- a/app/views/layouts/_crisp.html.haml +++ b/app/views/layouts/_crisp.html.haml @@ -1,15 +1,14 @@ -- if request.path == root_path - :javascript - window.$crisp=[]; - window.CRISP_WEBSITE_ID="779b5050-4cc1-4172-8dd0-bde55716a289"; - (function(){ - d=document; - s=d.createElement("script"); - s.src="https://client.crisp.im/l.js"; - s.async=1; - d.getElementsByTagName("head")[0].appendChild(s); - })(); - window.$crisp.push(["do", "chat:show"]); -- else - :javascript - window.$crisp && window.$crisp.push(["do", "chat:hide"]); +:javascript + window.$crisp=[]; + window.CRISP_WEBSITE_ID="779b5050-4cc1-4172-8dd0-bde55716a289"; + (function(){ + d=document; + s=d.createElement("script"); + s.src="https://client.crisp.im/l.js"; + s.async=1; + d.getElementsByTagName("head")[0].appendChild(s); + })(); + window.$crisp.push(["do", "chat:hide"]); + window.$crisp.push(["on", "chat:closed", function () { + window.$crisp.push(["do", "chat:hide"]); + }]); diff --git a/app/views/layouts/_new_footer.html.haml b/app/views/layouts/_new_footer.html.haml index 4bcc09bc8..266f73065 100644 --- a/app/views/layouts/_new_footer.html.haml +++ b/app/views/layouts/_new_footer.html.haml @@ -1,5 +1,5 @@ -.footer - .footer-inner-content +%footer + .container %ul.footer-columns %li.footer-column %ul.footer-logos diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index e6919dc50..27b381fa1 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -1,14 +1,33 @@ .new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" } .header-inner-content - = link_to root_path do - %img{ src: image_url("header/logo-tps.svg") } + + .flex.align-center + -# TODO delete new_ui when old UI is no longer used + = link_to root_path(new_ui: 1), class: "header-logo" do + %img{ src: image_url("header/logo-tps.svg") } + + - if gestionnaire_signed_in? + - current_url = request.path_info + %ul.header-tabs + - if current_gestionnaire.procedures.count > 0 + %li + = link_to "Procédures", procedures_path, class: (controller_name != 'avis') ? "active" : nil + - if current_gestionnaire.avis.count > 0 + %li + = link_to avis_index_path, class: (controller_name == 'avis') ? "active" : nil do + Avis + - avis_counter = current_gestionnaire.avis.without_answer.count + - if avis_counter > 0 + %span.badge.warning= avis_counter + %li + = link_to "Ancienne interface", backoffice_dossiers_path %ul.header-right-content - if gestionnaire_signed_in? %li .header-search - = form_tag backoffice_dossiers_search_url, method: :get, class: "form" do - = text_field_tag "q", "#{@search_terms unless @search_terms.nil?}", placeholder: "Rechercher" + = form_tag recherche_path, method: :get, class: "form" do + = text_field_tag "q", "#{@search_terms unless @search_terms.nil?}", placeholder: "Rechercher un dossier" %button{ title: "Rechercher" } = image_tag "icons/search-blue.svg" %li @@ -18,7 +37,11 @@ %li .menu-item{ title: current_email } = current_email - + - if administration_signed_in? + %li + = link_to administrations_path, class: "menu-item menu-link" do + = image_tag "icons/super-admin.svg" + Passer en super-admin - if SwitchDeviseProfileService.new(warden).multiple_devise_profile_connect? - if user_signed_in? %li @@ -27,7 +50,7 @@ Passer en usager - if gestionnaire_signed_in? %li - = link_to backoffice_dossiers_path, class: "menu-item menu-link" do + = link_to procedures_path, class: "menu-item menu-link" do = image_tag "icons/switch-profile.svg" Passer en accompagnateur - if administrateur_signed_in? diff --git a/app/views/layouts/left_panels/_search_area.html.haml b/app/views/layouts/left_panels/_search_area.html.haml index c957bb425..7126593f8 100644 --- a/app/views/layouts/left_panels/_search_area.html.haml +++ b/app/views/layouts/left_panels/_search_area.html.haml @@ -1,3 +1,6 @@ +- if gestionnaire_signed_in? + = link_to 'nouvelle interface', procedures_path, class: 'new-design-button' + #search-area = form_tag(backoffice_dossiers_search_url, method: :get) do .input-group diff --git a/app/views/layouts/navbars/_navbar_backoffice_dossierscontroller_show.html.haml b/app/views/layouts/navbars/_navbar_backoffice_dossierscontroller_show.html.haml index 6604a98c1..378fe9421 100644 --- a/app/views/layouts/navbars/_navbar_backoffice_dossierscontroller_show.html.haml +++ b/app/views/layouts/navbars/_navbar_backoffice_dossierscontroller_show.html.haml @@ -4,12 +4,12 @@ - if current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id) .row .col-xs-12 - - if current_gestionnaire.follow?(@facade.dossier.id) - = link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), "data-method" => :put, class: "button-navbar-action", id: "suivre_dossier_#{@facade.dossier.id}" do + - if current_gestionnaire.follow?(@facade.dossier) + = link_to unfollow_dossier_path(@facade.dossier.procedure, @facade.dossier), method: :patch, class: "button-navbar-action", id: "suivre_dossier_#{@facade.dossier.id}" do %i.fa.fa-user-times Ne plus suivre - else - = link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), 'data-method' => :put, class: 'button-navbar-action', id: "suivre_dossier_#{@facade.dossier.id}" do + = link_to follow_dossier_path(@facade.dossier.procedure, @facade.dossier), method: :patch, class: 'button-navbar-action', id: "suivre_dossier_#{@facade.dossier.id}" do %i.fa.fa-user-plus Suivre le dossier .row diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index 4db03ec7d..3da4de62d 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -18,25 +18,26 @@ = stylesheet_link_tag "print", media: "print", "data-turbolinks-track": true %body - = render partial: "layouts/support_navigator_banner" - = render partial: "layouts/ie_lt_10" - #beta{ class:(Rails.env == "production" ? "" : "beta_staging") } - - if Rails.env == "production" - Beta - - else - Env Test + .page-wrapper + = render partial: "layouts/support_navigator_banner" + = render partial: "layouts/ie_lt_10" + #beta{ class:(Rails.env == "production" ? "" : "beta_staging") } + - if Rails.env == "production" + Beta + - else + Env Test - = render partial: "layouts/new_header" - = render partial: "layouts/flash_messages" - = yield + = render partial: "layouts/new_header" + = render partial: "layouts/flash_messages" + = yield - = render partial: "layouts/new_footer" - = render partial: "layouts/google_analytics" - = render partial: "layouts/mailjet_newsletter" - = render partial: "layouts/crisp" + = render partial: "layouts/new_footer" + = render partial: "layouts/google_analytics" + = render partial: "layouts/mailjet_newsletter" + = render partial: "layouts/crisp" - = javascript_include_tag "new_design/application", "data-turbolinks-eval": false - = yield :charts_js - - if Rails.env == "test" - %script{ type: "text/javascript" } - (typeof jQuery !== "undefined") && (jQuery.fx.off = true); + = javascript_include_tag "new_design/application", "data-turbolinks-eval": false + = yield :charts_js + - if Rails.env == "test" + %script{ type: "text/javascript" } + (typeof jQuery !== "undefined") && (jQuery.fx.off = true); diff --git a/app/views/new_gestionnaire/avis/_avis_list.html.haml b/app/views/new_gestionnaire/avis/_avis_list.html.haml new file mode 100644 index 000000000..a389fd5e5 --- /dev/null +++ b/app/views/new_gestionnaire/avis/_avis_list.html.haml @@ -0,0 +1,30 @@ +- if avis.present? + %section.list-avis + %h1.title + Avis des invités + %span.count= avis.count + + %ul + - avis.each do |avis| + %li.one-avis.flex.align-start + .width-100 + %h2.claimant + Demandeur : + %span.email= (avis.claimant.email == current_gestionnaire.email) ? 'Vous' : avis.claimant.email + - if avis.confidentiel? + %span.confidentiel + confidentiel + %i.lock{ title: "Cet avis n'est pas affiché avec les autres experts consultés" } + %span.date Demande d'avis envoyée le #{I18n.l(avis.created_at.localtime, format: '%d/%m/%y')} + %p= avis.introduction + + .answer.flex.align-start + %i.bubble.avis-icon + .width-100 + %h2.gestionnaire + = (avis.email_to_display == current_gestionnaire.email) ? 'Vous' : avis.email_to_display + - if avis.answer.present? + %span.date Réponse donnée le #{I18n.l(avis.updated_at.localtime, format: '%d/%m/%y')} + - else + %span.waiting En attente de réponse + %p= avis.answer diff --git a/app/views/new_gestionnaire/avis/_header.html.haml b/app/views/new_gestionnaire/avis/_header.html.haml new file mode 100644 index 000000000..1933d5a71 --- /dev/null +++ b/app/views/new_gestionnaire/avis/_header.html.haml @@ -0,0 +1,13 @@ +.backoffice-header + .container + %ul.breadcrumbs + %li= link_to('Avis', avis_index_path) + %li= "#{dossier.procedure.libelle}, dossier nº #{dossier.id}" + + %ul.tabs + %li{ class: current_page?(avis_path(avis)) ? 'active' : nil } + = link_to 'Demande', avis_path(avis) + %li{ class: current_page?(instruction_avis_path(avis)) ? 'active' : nil } + = link_to 'Instruction', instruction_avis_path(avis) + %li{ class: current_page?(messagerie_avis_path(avis)) ? 'active' : nil } + = link_to 'Messagerie', messagerie_avis_path(avis) diff --git a/app/views/new_gestionnaire/avis/index.html.haml b/app/views/new_gestionnaire/avis/index.html.haml new file mode 100644 index 000000000..984db4f24 --- /dev/null +++ b/app/views/new_gestionnaire/avis/index.html.haml @@ -0,0 +1,38 @@ +#avis-index + .backoffice-header + .container.flex + .width-100 + %h1 Avis + %ul.tabs + %li{ class: (@statut == NewGestionnaire::AvisController::A_DONNER_STATUS) ? 'active' : nil }> + = link_to(avis_index_path(statut: NewGestionnaire::AvisController::A_DONNER_STATUS)) do + avis à donner + %span.badge= @avis_a_donner.count + - if @avis_a_donner.any? + %span.notifications + + %li{ class: (@statut == NewGestionnaire::AvisController::DONNES_STATUS) ? 'active' : nil }> + = link_to(avis_index_path(statut: NewGestionnaire::AvisController::DONNES_STATUS)) do + avis #{'donné'.pluralize(@avis_donnes.count)} + %span.badge= @avis_donnes.count + + .container + - if @avis.present? + %table.table.dossiers-table.hoverable + %thead + %tr + %th.number-col Nº dossier + %th Demandeur + %th Procédure + %tbody + - @avis.each do |avis| + %tr + %td.number-col + = link_to(avis_path(avis), class: 'cell-link') do + %i.folder + #{avis.dossier.id} + %td= link_to(avis.dossier.user.email, avis_path(avis), class: 'cell-link') + %td= link_to(avis.dossier.procedure.libelle, avis_path(avis), class: 'cell-link') + = paginate(@avis) + - else + %h2.empty-text Aucun avis diff --git a/app/views/new_gestionnaire/avis/instruction.html.haml b/app/views/new_gestionnaire/avis/instruction.html.haml new file mode 100644 index 000000000..53c4dd8e7 --- /dev/null +++ b/app/views/new_gestionnaire/avis/instruction.html.haml @@ -0,0 +1,43 @@ +#avis-show + = render partial: 'header', locals: { avis: @avis, dossier: @dossier } + +.container + %section.give-avis + %h1 Donner votre avis + %h2.claimant + Demandeur : + %span.email= @avis.claimant.email + %span.date Demande d'avis envoyée le #{I18n.l(@avis.created_at.localtime, format: '%d/%m/%y')} + %p.introduction= @avis.introduction + + = form_for @avis, url: avis_path(@avis), html: { class: 'form' } do |f| + = f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true + .flex.justify-between.align-baseline + %p.confidentiel + %i.lock + Cet avis est confidentiel et n'est pas affiché aux autres experts consultés + .send-wrapper + = f.submit 'Envoyer votre avis', class: 'button send' + + %section.ask-avis + %h1 Inviter une personne à donner son avis + %p.avis-notice L'invité pourra consulter, donner un avis sur le dossier et contribuer au fil de messagerie, mais il ne pourra le modifier. + + = form_for Avis.new, url: avis_avis_path(@avis), html: { class: 'form' } do |f| + = f.email_field :email, placeholder: 'Adresse email', required: true + = f.text_area :introduction, rows: 3, value: 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true + .flex.justify-between.align-baseline + - if @avis.confidentiel? + %p.confidentiel + %i.lock + Cet avis est confidentiel et n'est pas affiché aux autres experts consultés + .send-wrapper + = f.submit 'Demander un avis', class: 'button send' + - else + .confidentiel-wrapper + = f.label :confidentiel, 'Cet avis est' + = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]] + .send-wrapper + = f.submit 'Demander un avis', class: 'button send' + + = render partial: 'avis_list', locals: { avis: @dossier.avis_for(current_gestionnaire) } diff --git a/app/views/new_gestionnaire/avis/messagerie.html.haml b/app/views/new_gestionnaire/avis/messagerie.html.haml new file mode 100644 index 000000000..4365459e8 --- /dev/null +++ b/app/views/new_gestionnaire/avis/messagerie.html.haml @@ -0,0 +1,21 @@ += render partial: 'header', locals: { avis: @avis, dossier: @dossier } + +.messagerie.container + %ul + - @dossier.commentaires.ordered.each do |commentaire| + %li + = render partial: 'new_gestionnaire/dossiers/commentaire_icon', locals: { commentaire: commentaire, current_gestionnaire: current_gestionnaire } + + .width-100 + %h2 + %span.mail + = render partial: 'new_gestionnaire/dossiers/commentaire_issuer', locals: { commentaire: commentaire, current_gestionnaire: current_gestionnaire } + - if ![current_gestionnaire.email, @dossier.user.email, 'contact@tps.apientreprise.fr'].include?(commentaire.email) + %span.guest Invité + %span.date= I18n.l(commentaire.created_at.localtime, format: '%H:%M le %d/%m/%Y') + %p= sanitize(commentaire.body) + + = form_for(Commentaire.new, url: commentaire_avis_path(@avis), html: { class: 'form' }) do |f| + = f.text_area :body, rows: 5, placeholder: 'Répondre ici', required: true + .send-wrapper + = f.submit 'Envoyer', class: 'button send' diff --git a/app/views/new_gestionnaire/avis/show.html.haml b/app/views/new_gestionnaire/avis/show.html.haml new file mode 100644 index 000000000..e88d364f3 --- /dev/null +++ b/app/views/new_gestionnaire/avis/show.html.haml @@ -0,0 +1,47 @@ +#avis-show + = render partial: 'header', locals: { avis: @avis, dossier: @dossier } + + .container + .card + .card-title Identité du demandeur + - if @dossier.entreprise.present? + = render partial: 'new_gestionnaire/dossiers/identite_entreprise', locals: { entreprise: @dossier.entreprise } + + - if @dossier.individual.present? + = render partial: 'new_gestionnaire/dossiers/identite_individual', locals: { individual: @dossier.individual } + + .backoffice-title Formulaire + + - champs = @dossier.ordered_champs.decorate + - if champs.any? + .card.featured + = render partial: 'new_gestionnaire/dossiers/champs', locals: { champs: champs } + + - if @dossier.procedure.use_api_carto + = render partial: 'new_gestionnaire/dossiers/map', locals: { dossier: @dossier } + + - if @dossier.procedure.cerfa_flag? || @dossier.types_de_piece_justificative.any? + .card.featured + .card-title Pièces jointes + + %table.table.vertical + %tbody + - if @dossier.procedure.cerfa_flag? + %tr + %th Formulaire : + %td + - if @dossier.cerfa_available? + = link_to 'Télécharger', @dossier.cerfa.last.content_url, class: 'button', target: :blank + - else + Pièce non fournie + + - @dossier.procedure.types_de_piece_justificative.each do |type_de_piece_justificative| + %tr + %th= "#{type_de_piece_justificative.libelle} :" + %td + - pj = @dossier.retrieve_last_piece_justificative_by_type(type_de_piece_justificative.id) + - if pj.present? + Pièce fournie - + = link_to "Consulter", pj.content_url, class: "link", target: :blank + - else + Pièce non fournie diff --git a/app/views/new_gestionnaire/dossiers/_champs.html.haml b/app/views/new_gestionnaire/dossiers/_champs.html.haml new file mode 100644 index 000000000..b7c125c92 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_champs.html.haml @@ -0,0 +1,12 @@ +%table.table.vertical + %tbody + - champs.each do |c| + %tr + - if c.type_champ == "header_section" + %th.header-section{ colspan: 2 } + = c.libelle + - else + %th + = "#{c.libelle} :" + %td + = c.value diff --git a/app/views/new_gestionnaire/dossiers/_commentaire_icon.html.haml b/app/views/new_gestionnaire/dossiers/_commentaire_icon.html.haml new file mode 100644 index 000000000..c4574b676 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_commentaire_icon.html.haml @@ -0,0 +1,7 @@ +- case commentaire.email +- when current_gestionnaire.email + = image_tag('icons/account-circle.svg', class: 'person-icon') +- when 'contact@tps.apientreprise.fr' + = image_tag('icons/mail.svg', class: 'person-icon') +- else + = image_tag('icons/blue-person.svg', class: 'person-icon') diff --git a/app/views/new_gestionnaire/dossiers/_commentaire_issuer.html.haml b/app/views/new_gestionnaire/dossiers/_commentaire_issuer.html.haml new file mode 100644 index 000000000..7c16510bd --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_commentaire_issuer.html.haml @@ -0,0 +1,7 @@ +- case commentaire.email +- when current_gestionnaire.email + Vous +- when 'contact@tps.apientreprise.fr' + Email automatique +- else + = commentaire.email diff --git a/app/views/new_gestionnaire/dossiers/_header.html.haml b/app/views/new_gestionnaire/dossiers/_header.html.haml new file mode 100644 index 000000000..2321f44aa --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_header.html.haml @@ -0,0 +1,29 @@ +.backoffice-header + .container + .flex.justify-between + %ul.breadcrumbs + %li + = link_to dossier.procedure.libelle.truncate_words(10), procedure_path(dossier.procedure), title: dossier.procedure.libelle + %li + = "Dossier nº #{dossier.id}" + %div + = render partial: "state_button", locals: { dossier: dossier } + = render partial: "new_gestionnaire/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_gestionnaire&.follow?(dossier) } + %ul.tabs + - notifications_summary = dossier.notifications_summary + %li{ class: current_page?(dossier_path(dossier.procedure, dossier)) ? 'active' : nil } + - if notifications_summary[:demande] + %span.notifications{ 'aria-label': 'notifications' } + = link_to "Demande", dossier_path(dossier.procedure, dossier) + %li{ class: current_page?(annotations_privees_dossier_path(dossier.procedure, dossier)) ? 'active' : nil } + - if notifications_summary[:annotations_privees] + %span.notifications{ 'aria-label': 'notifications' } + = link_to "Annotations Privées", annotations_privees_dossier_path(dossier.procedure, dossier) + %li{ class: current_page?(avis_dossier_path(dossier.procedure, dossier)) ? 'active' : nil } + - if notifications_summary[:avis] + %span.notifications{ 'aria-label': 'notifications' } + = link_to "Avis Externes", avis_dossier_path(dossier.procedure, dossier) + %li{ class: current_page?(messagerie_dossier_path(dossier.procedure, dossier)) ? 'active' : nil } + - if notifications_summary[:messagerie] + %span.notifications{ 'aria-label': 'notifications' } + = link_to "Messagerie", messagerie_dossier_path(dossier.procedure, dossier) diff --git a/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml b/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml new file mode 100644 index 000000000..bbb5ba9d9 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_identite_entreprise.html.haml @@ -0,0 +1,68 @@ +- entreprise = entreprise.decorate +- etablissement = entreprise.etablissement +%h4= entreprise.raison_sociale_or_name + +%table.table.vertical + %tbody + %tr + %th SIRET : + %td= entreprise.siret_siege_social + %tr + %th Forme juridique : + %td= entreprise.forme_juridique + - if etablissement.present? + %tr + %th Libellé NAF : + %td= etablissement.libelle_naf + %tr + %th Code NAF : + %td= etablissement.naf + %tr + %th Date de création : + %td= Time.at(entreprise.date_creation).localtime.strftime("%d/%m/%Y") + %tr + %th Effectif de l'organisation : + %td= entreprise.effectif + %tr + %th Code effectif : + %td= entreprise.code_effectif_entreprise + %tr + %th Numéro de TVA intracommunautaire : + %td= entreprise.numero_tva_intracommunautaire + - if etablissement.present? + %tr + %th Adresse : + %td + - etablissement.adresse.split("\n").each do |line| + = line + %br + %tr + %th Capital social : + %td= entreprise.pretty_capital_social + - if etablissement.present? + %tr + %th Exercices : + %td + - etablissement.exercices.each_with_index do |exercice, index| + = "#{exercice.dateFinExercice.year} : " + = number_to_currency(exercice.ca) + %br + - if entreprise.rna_information.present? + %tr + %th Numéro RNA : + %td= entreprise.rna_information.association_id + %tr + %th Titre : + %td= entreprise.rna_information.titre + %tr + %th Objet : + %td= entreprise.rna_information.objet + %tr + %th Date de création : + %td= entreprise.rna_information.date_creation + %tr + %th Date de publication : + %td= entreprise.rna_information.date_publication + %tr + %th Date de déclaration : + %td= entreprise.rna_information.date_declaration diff --git a/app/views/new_gestionnaire/dossiers/_identite_individual.html.haml b/app/views/new_gestionnaire/dossiers/_identite_individual.html.haml new file mode 100644 index 000000000..1f41ecd17 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_identite_individual.html.haml @@ -0,0 +1,14 @@ +%table.table.vertical + %tbody + %tr + %th Civilité : + %td= individual.gender + %tr + %th Prénom : + %td= individual.prenom + %tr + %th Nom : + %td= individual.nom + %tr + %th Date de naissance : + %td= individual.birthdate diff --git a/app/views/new_gestionnaire/dossiers/_map.html.haml b/app/views/new_gestionnaire/dossiers/_map.html.haml new file mode 100644 index 000000000..47becb044 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_map.html.haml @@ -0,0 +1,22 @@ +.card.featured + .card-title Cartographie + #map + + - if dossier.quartier_prioritaires.any? + .card-title Quartiers prioritaires + %ul + - dossier.quartier_prioritaires.each do |q| + %li= q.nom + + - if dossier.cadastres.any? + .card-title Parcelles cadastrales + %ul + - dossier.cadastres.each do |p| + %li + = "Parcelle n° #{p.numero} - Feuille #{p.code_arr} #{p.section} #{p.feuille}" + +:javascript + var getPositionUrl = "#{position_dossier_path(dossier.procedure, dossier)}"; + var dossierJsonLatLngs = #{dossier.json_latlngs}; + var dossierCadastres = #{dossier.cadastres.to_json}; + var dossierQuartiersPrioritaires = #{dossier.quartier_prioritaires.to_json}; diff --git a/app/views/new_gestionnaire/dossiers/_state_button.html.haml b/app/views/new_gestionnaire/dossiers/_state_button.html.haml new file mode 100644 index 000000000..7584cd95d --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/_state_button.html.haml @@ -0,0 +1,83 @@ +- if dossier.en_construction? || dossier.en_instruction? + %span.button.primary.dropdown + = dossier.decorate.display_state + .dropdown-content.fade-in-down + %ul.dropdown-items + - if dossier.en_construction? + %li.selected + %i.edit + .description + %h4 En construction + Vous permettez à l'usager de modifier ses réponses au formulaire + %li + = link_to backoffice_dossier_receive_path(dossier, new_ui: true), method: :post, data: { confirm: "Confirmer vous le passage en instruction de ce dossier ?" } do + %i.in-progress + .description + %h4 Passer en instruction + L'usager ne pourra plus modifier le formulaire + + - if dossier.en_instruction? + %li + = link_to backoffice_dossier_reopen_path(dossier, new_ui: true), method: :post, data: { confirm: "Confirmer vous la réouverture de ce dossier ?" } do + %i.edit + .description + %h4 Repasser en construction + Vous permettrez à l'usager de modifier ses réponses au formulaire + %li.selected + %i.in-progress + .description + %h4 En instruction + L'usager ne peut modifer son dossier pendant l'instruction + %li{ onclick: "TPS.acceptDossier();" } + %i.accept + .description + %h4 Accepter + L'usager sera notifié que son dossier a été accepté + %li + = link_to backoffice_dossier_process_dossier_path(dossier, process_action: "without_continuation", new_ui: true), method: :post, data: { confirm: "Confirmer vous le classement sans suite de ce dossier ?" } do + %i.without-continuation + .description + %h4 Classer sans suite + L'usager ne recevra aucune notification + %li + = link_to backoffice_dossier_process_dossier_path(dossier, process_action: "refuse", new_ui: true), method: :post, data: { confirm: "Confirmer vous le refus de ce dossier ?" } do + %i.close + .description + %h4 Refuser + L'usager sera notifié que son dossier a été refusé + .motivation + %h3 + %i.accept + Accepter le dossier + = "nº #{dossier.id}" + + = form_tag(backoffice_dossier_process_dossier_url(dossier.id, new_ui: true), method: :post, class: "form") do + = text_area :dossier, :motivation, class: "motivation-text-area", placeholder: "Rédigez votre motivation ici (facultative)" + %p.help + L'acceptation du dossier envoie automatiquement une attestation à l'usager. + .text-right + %span.button{ onclick: "TPS.motivationCancel();" } Annuler + = button_tag "Valider la décision", name: :process_action, value: "close", class: 'button primary', title: 'Accepter', data: { confirm: "Accepter ce dossier ?" } + +- else + %span.button.dropdown{ class: dossier.closed? ? 'success' : nil } + - if dossier.closed? + accepté + - elsif dossier.without_continuation? + classé sans suite + - elsif dossier.refused? + refusé + .dropdown-content.fade-in-down.terminated + %h4 + - if dossier.closed? + Dossier nº #{dossier.id} accepté + - elsif dossier.without_continuation? + Dossier nº #{dossier.id} classé sans suite + - elsif dossier.refused? + Dossier nº #{dossier.id} refusé + + %p.dossier-motivation= dossier.motivation.present? ? dossier.motivation : "aucune motivation n'a été fournie" + + - if dossier.attestation.present? + %p.attestation L'acceptation du dossier a envoyé automatiquement une attestation au demandeur + = link_to "Voir l'attestation", attestation_dossier_path(dossier.procedure, dossier), target: '_blank', class: 'button' diff --git a/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml b/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml new file mode 100644 index 000000000..6aaece0df --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/annotations_privees.html.haml @@ -0,0 +1,18 @@ += render partial: "header", locals: { dossier: @dossier } + +#dossier-annotations-privees.container + - if @dossier.ordered_champs_private.present? + %section + %h1.private-annotations Annotations privées + .card.featured + = form_for @dossier, url: annotations_dossier_path(@dossier.procedure, @dossier), html: { class: 'form' } do |f| + = f.fields_for :champs_private, f.object.ordered_champs_private do |champ_form| + - champ = champ_form.object + = render partial: "new_gestionnaire/dossiers/champs/#{champ.type_champ}", + locals: { champ: champ, form: champ_form } + + .send-wrapper + = f.submit 'Sauvegarder', class: 'button send' + + - else + %h2.empty-text Aucune annotation privée diff --git a/app/views/new_gestionnaire/dossiers/avis.html.haml b/app/views/new_gestionnaire/dossiers/avis.html.haml new file mode 100644 index 000000000..5484a0b29 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/avis.html.haml @@ -0,0 +1,18 @@ += render partial: "header", locals: { dossier: @dossier } + +.container + %section.ask-avis + %h1 Inviter une personne à donner son avis + %p.avis-notice L'invité pourra consulter, donner un avis sur le dossier et contribuer au fil de messagerie, mais il ne pourra le modifier. + + = form_for Avis.new, url: avis_dossier_path(@dossier.procedure, @dossier), html: { class: 'form' } do |f| + = f.email_field :email, placeholder: 'Adresse email', required: true + = f.text_area :introduction, rows: 3, value: 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true + .flex.justify-between.align-baseline + .confidentiel-wrapper + = f.label :confidentiel, 'Cet avis est' + = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]] + .send-wrapper + = f.submit 'Demander un avis', class: 'button send' + + = render partial: 'new_gestionnaire/avis/avis_list', locals: { avis: @dossier.avis } diff --git a/app/views/new_gestionnaire/dossiers/champs/_address.html.haml b/app/views/new_gestionnaire/dossiers/champs/_address.html.haml new file mode 100644 index 000000000..c0707410f --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_address.html.haml @@ -0,0 +1,6 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.text_field :value, + 'data-address': 'true', + placeholder: champ.libelle, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_champ_label.html.haml b/app/views/new_gestionnaire/dossiers/champs/_champ_label.html.haml new file mode 100644 index 000000000..000439a3a --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_champ_label.html.haml @@ -0,0 +1,6 @@ += form.label :value do + #{champ.libelle} + - if champ.mandatory + %span.mandatory * + - if champ.description.present? + %span.notice= champ.description diff --git a/app/views/new_gestionnaire/dossiers/champs/_checkbox.html.haml b/app/views/new_gestionnaire/dossiers/champs/_checkbox.html.haml new file mode 100644 index 000000000..2668405ba --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_checkbox.html.haml @@ -0,0 +1,8 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.check_box :value, + { required: champ.mandatory }, + 'on', + 'off' + +%br diff --git a/app/views/new_gestionnaire/dossiers/champs/_civilite.html.haml b/app/views/new_gestionnaire/dossiers/champs/_civilite.html.haml new file mode 100644 index 000000000..01952292d --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_civilite.html.haml @@ -0,0 +1,10 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +%div + %label + = form.radio_button :value, 'M.' + Monsieur + + %label + = form.radio_button :value, 'Mme.' + Madame diff --git a/app/views/new_gestionnaire/dossiers/champs/_date.html.haml b/app/views/new_gestionnaire/dossiers/champs/_date.html.haml new file mode 100644 index 000000000..5bdd4ad45 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_date.html.haml @@ -0,0 +1,6 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.date_field :value, + value: champ.value, + placeholder: 'JJ/MM/AAAA', + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_datetime.html.haml b/app/views/new_gestionnaire/dossiers/champs/_datetime.html.haml new file mode 100644 index 000000000..4dabb6705 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_datetime.html.haml @@ -0,0 +1,6 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +- parsed_value = champ.value.present? ? DateTime.parse(champ.value) : DateTime.now + +.datetime + = form.datetime_select(:value, selected: parsed_value, start_year: 1950, end_year: 2100, minute_step: 5) diff --git a/app/views/new_gestionnaire/dossiers/champs/_departements.html.haml b/app/views/new_gestionnaire/dossiers/champs/_departements.html.haml new file mode 100644 index 000000000..c16358e58 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_departements.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.select :value, + Champ.departements, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_dossier_link.html.haml b/app/views/new_gestionnaire/dossiers/champs/_dossier_link.html.haml new file mode 100644 index 000000000..54c9d9b12 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_dossier_link.html.haml @@ -0,0 +1,20 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +- dossier = Dossier.find_by(id: champ.value) +- show_text_summary = dossier.present? +- show_warning = !show_text_summary && champ.value.present? +- text_summary = dossier.try(:text_summary) + +.dossier-link + = form.number_field :value, + placeholder: "Numéro de dossier", + autocomplete: 'off', + 'data-type': 'dossier-link', + required: champ.mandatory + + .help-block + %p.text-info{ style: show_text_summary ? nil : 'display: none;' } + %span.dossier-text-summary= text_summary + + %p.text-warning{ style: show_warning ? nil : 'display: none;' } + Ce dossier est inconnu diff --git a/app/views/new_gestionnaire/dossiers/champs/_drop_down_list.html.haml b/app/views/new_gestionnaire/dossiers/champs/_drop_down_list.html.haml new file mode 100644 index 000000000..ee46b4b26 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_drop_down_list.html.haml @@ -0,0 +1,7 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +- if champ.drop_down_list && champ.drop_down_list.options.any? + = form.select :value, + champ.drop_down_list.options, + disabled: champ.drop_down_list.disabled_options, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_email.html.haml b/app/views/new_gestionnaire/dossiers/champs/_email.html.haml new file mode 100644 index 000000000..c9819de88 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_email.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.email_field :value, + placeholder: champ.libelle, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_engagement.html.haml b/app/views/new_gestionnaire/dossiers/champs/_engagement.html.haml new file mode 100644 index 000000000..2668405ba --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_engagement.html.haml @@ -0,0 +1,8 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.check_box :value, + { required: champ.mandatory }, + 'on', + 'off' + +%br diff --git a/app/views/new_gestionnaire/dossiers/champs/_explication.html.haml b/app/views/new_gestionnaire/dossiers/champs/_explication.html.haml new file mode 100644 index 000000000..0baedbbae --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_explication.html.haml @@ -0,0 +1,2 @@ +%h2.explication-libelle= champ.libelle +%p.explication= champ.value diff --git a/app/views/new_gestionnaire/dossiers/champs/_header_section.html.haml b/app/views/new_gestionnaire/dossiers/champs/_header_section.html.haml new file mode 100644 index 000000000..16c6c6c9f --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_header_section.html.haml @@ -0,0 +1,2 @@ +%h2.header-section + = champ.libelle diff --git a/app/views/new_gestionnaire/dossiers/champs/_multiple_drop_down_list.html.haml b/app/views/new_gestionnaire/dossiers/champs/_multiple_drop_down_list.html.haml new file mode 100644 index 000000000..5f0fb9439 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_multiple_drop_down_list.html.haml @@ -0,0 +1,9 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +- if champ.drop_down_list && champ.drop_down_list.options.any? + = form.select :value, + champ.drop_down_list.options, + { selected: champ.drop_down_list.selected_options_without_decorator(champ), + disabled: champ.drop_down_list.disabled_options }, + multiple: true, + class: 'select2' diff --git a/app/views/new_gestionnaire/dossiers/champs/_number.html.haml b/app/views/new_gestionnaire/dossiers/champs/_number.html.haml new file mode 100644 index 000000000..c9bcb8eae --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_number.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.number_field :value, + placeholder: champ.libelle, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_pays.html.haml b/app/views/new_gestionnaire/dossiers/champs/_pays.html.haml new file mode 100644 index 000000000..cad18df07 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_pays.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.select :value, + Champ.pays, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_phone.html.haml b/app/views/new_gestionnaire/dossiers/champs/_phone.html.haml new file mode 100644 index 000000000..e42dbfad1 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_phone.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.phone_field :value, + placeholder: champ.libelle, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_regions.html.haml b/app/views/new_gestionnaire/dossiers/champs/_regions.html.haml new file mode 100644 index 000000000..eb9ffaf8d --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_regions.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.select :value, + Champ.regions, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_text.html.haml b/app/views/new_gestionnaire/dossiers/champs/_text.html.haml new file mode 100644 index 000000000..2e34ffa6c --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_text.html.haml @@ -0,0 +1,5 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.text_field :value, + placeholder: champ.libelle, + required: champ.mandatory diff --git a/app/views/new_gestionnaire/dossiers/champs/_textarea.html.haml b/app/views/new_gestionnaire/dossiers/champs/_textarea.html.haml new file mode 100644 index 000000000..88c5803aa --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_textarea.html.haml @@ -0,0 +1,7 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + += form.text_area :value, + row: 6, + placeholder: champ.description, + required: champ.mandatory, + value: sanitize(champ.value) diff --git a/app/views/new_gestionnaire/dossiers/champs/_yes_no.html.haml b/app/views/new_gestionnaire/dossiers/champs/_yes_no.html.haml new file mode 100644 index 000000000..9e9207f86 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/champs/_yes_no.html.haml @@ -0,0 +1,10 @@ += render partial: 'new_gestionnaire/dossiers/champs/champ_label', locals: { form: form, champ: champ } + +%div + %label + = form.radio_button :value, true + Oui + + %label + = form.radio_button :value, false + Non diff --git a/app/views/new_gestionnaire/dossiers/messagerie.html.haml b/app/views/new_gestionnaire/dossiers/messagerie.html.haml new file mode 100644 index 000000000..96699c81c --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/messagerie.html.haml @@ -0,0 +1,26 @@ += render partial: "header", locals: { dossier: @dossier } + +.messagerie.container + %ul + - @dossier.commentaires.ordered.each do |commentaire| + %li + = render partial: 'commentaire_icon', locals: { commentaire: commentaire, current_gestionnaire: current_gestionnaire } + + .width-100 + %h2 + %span.mail + = render partial: 'commentaire_issuer', locals: { commentaire: commentaire, current_gestionnaire: current_gestionnaire } + - if ![current_gestionnaire.email, @dossier.user.email, 'contact@tps.apientreprise.fr'].include?(commentaire.email) + %span.guest Invité + %span.date= I18n.l(commentaire.created_at.localtime, format: '%H:%M le %d/%m/%Y') + %p= sanitize(commentaire.body) + - if file = commentaire.piece_justificative + .attachment-link + = link_to file.content_url, class: "button", target: "_blank", title: "Télécharger" do + %i.attachment + = file.original_filename + + = form_for(Commentaire.new, url: commentaire_dossier_path(@dossier.procedure, @dossier), html: { class: 'form' }) do |f| + = f.text_area :body, rows: 5, placeholder: 'Répondre ici', required: true + .send-wrapper + = f.submit 'Envoyer', class: 'button send', data: { disable_with: "Envoi..." } diff --git a/app/views/new_gestionnaire/dossiers/show.html.haml b/app/views/new_gestionnaire/dossiers/show.html.haml new file mode 100644 index 000000000..516d2c520 --- /dev/null +++ b/app/views/new_gestionnaire/dossiers/show.html.haml @@ -0,0 +1,47 @@ += render partial: "header", locals: { dossier: @dossier } + +.container + .card + .card-title Identité du demandeur + - if @dossier.entreprise.present? + = render partial: "identite_entreprise", locals: { entreprise: @dossier.entreprise } + + - if @dossier.individual.present? + = render partial: "identite_individual", locals: { individual: @dossier.individual } + + .backoffice-title Formulaire + + - champs = @dossier.ordered_champs.decorate + - if champs.any? + .card.featured + = render partial: "champs", locals: { champs: champs } + + - if @dossier.procedure.use_api_carto + = render partial: "map", locals: { dossier: @dossier } + + - if @dossier.procedure.cerfa_flag? || @dossier.types_de_piece_justificative.any? + .card.featured + .card-title Pièces jointes + + %table.table.vertical + %tbody + - if @dossier.procedure.cerfa_flag? + %tr + %th Formulaire : + %td + - if @dossier.cerfa_available? + Pièce fournie - + = link_to "Consulter", @dossier.cerfa.last.content_url, class: "link", target: :blank + - else + Pièce non fournie + + - @dossier.procedure.types_de_piece_justificative.each do |type_de_piece_justificative| + %tr + %th= "#{type_de_piece_justificative.libelle} :" + %td + - pj = @dossier.retrieve_last_piece_justificative_by_type(type_de_piece_justificative.id) + - if pj.present? + Pièce fournie - + = link_to "Consulter", pj.content_url, class: "link", target: :blank + - else + Pièce non fournie diff --git a/app/views/new_gestionnaire/procedures/_dossier_actions.html.haml b/app/views/new_gestionnaire/procedures/_dossier_actions.html.haml new file mode 100644 index 000000000..d61410fce --- /dev/null +++ b/app/views/new_gestionnaire/procedures/_dossier_actions.html.haml @@ -0,0 +1,19 @@ +- if dossier.en_construction_ou_instruction? + - if dossier_is_followed + = link_to unfollow_dossier_path(procedure, dossier), method: :patch, class: 'button' do + %i.unfollow> + ne plus suivre + - else + = link_to follow_dossier_path(procedure, dossier), method: :patch, class: 'button' do + %i.follow> + suivre le dossier + +- elsif dossier.termine? + - if dossier.archived + = link_to unarchive_dossier_path(procedure, dossier), method: :patch, class: 'button' do + %i.unarchive> + désarchiver le dossier + - else + = link_to archive_dossier_path(procedure, dossier), method: :patch, class: 'button' do + %i.archive> + archiver le dossier diff --git a/app/views/new_gestionnaire/procedures/_status.html.haml b/app/views/new_gestionnaire/procedures/_status.html.haml new file mode 100644 index 000000000..b6fbd6df9 --- /dev/null +++ b/app/views/new_gestionnaire/procedures/_status.html.haml @@ -0,0 +1,10 @@ +- if dossier.en_instruction? + %span.label.instruction en instruction +- elsif dossier.en_construction? + %span.label.construction en construction +- elsif dossier.closed? + %span.label.closed accepté +- elsif dossier.refused? + %span.label.refused refusé +- elsif dossier.without_continuation? + %span.label.without-continuation sans suite diff --git a/app/views/new_gestionnaire/procedures/index.html.haml b/app/views/new_gestionnaire/procedures/index.html.haml new file mode 100644 index 000000000..ab074836d --- /dev/null +++ b/app/views/new_gestionnaire/procedures/index.html.haml @@ -0,0 +1,54 @@ +.container + %h1.backoffice-title Procédures + + %ul.procedure-list + - @procedures.each do |p| + %li.procedure-item.flex.align-start + = link_to(procedure_path(p)) do + .flex + + .procedure-logo + - if p.logo.present? + = image_tag p.logo, alt: "Logo de la procédure" + + .procedure-details + %p.procedure-title + = p.libelle + + %ul.procedure-stats.flex + %li + - a_suivre_count = @dossiers_a_suivre_count_per_procedure[p.id] || 0 + .stats-number + = a_suivre_count + .stats-legend + à suivre + %li + - if @notifications_count_per_procedure[p.id].present? + %span.notifications{ 'aria-label': "notifications" } + - followed_count = @followed_dossiers_count_per_procedure[p.id] || 0 + .stats-number + = followed_count + .stats-legend + = t('pluralize.followed', count: followed_count) + %li + - termines_count = @dossiers_termines_count_per_procedure[p.id] || 0 + .stats-number + = termines_count + .stats-legend + = t('pluralize.processed', count: termines_count) + %li + - dossier_count = @dossiers_count_per_procedure[p.id] || 0 + .stats-number + = dossier_count + .stats-legend + = t('pluralize.case', count: dossier_count) + %li + - archived_count = @dossiers_archived_count_per_procedure[p.id] || 0 + .stats-number + = archived_count + .stats-legend + = t('pluralize.archived', count: archived_count) + + - if p.archivee? + .procedure-status + %span.label Archivée diff --git a/app/views/new_gestionnaire/procedures/show.html.haml b/app/views/new_gestionnaire/procedures/show.html.haml new file mode 100644 index 000000000..f2498d451 --- /dev/null +++ b/app/views/new_gestionnaire/procedures/show.html.haml @@ -0,0 +1,78 @@ +#procedure-show + .backoffice-header + .container.flex + + .procedure-logo + - if @procedure.logo.present? + = image_tag @procedure.logo, alt: "Logo de la procédure" + + .procedure-header + %h1= @procedure.libelle + %ul.tabs + %li{ class: (@statut == 'a-suivre') ? 'active' : nil }> + = link_to(procedure_path(@procedure, statut: 'a-suivre')) do + à suivre + %span.badge= @a_suivre_dossiers.count + + %li{ class: (@statut == 'suivis') ? 'active' : nil }> + - if @followed_dossiers.with_unread_notifications.present? + %span.notifications{ 'aria-label': 'notifications' } + = link_to(procedure_path(@procedure, statut: 'suivis')) do + = t('pluralize.followed', count: @followed_dossiers.count) + %span.badge= @followed_dossiers.count + + %li{ class: (@statut == 'traites') ? 'active' : nil }> + = link_to(procedure_path(@procedure, statut: 'traites')) do + = t('pluralize.processed', count: @termines_dossiers.count) + %span.badge= @termines_dossiers.count + + %li{ class: (@statut == 'tous') ? 'active' : nil }> + - if @followed_dossiers.with_unread_notifications.present? + %span.notifications{ 'aria-label': 'notifications' } + = link_to(procedure_path(@procedure, statut: 'tous')) do + tous les dossiers + %span.badge= @all_state_dossiers.count + + %li{ class: (@statut == 'archives') ? 'active' : nil }> + = link_to(procedure_path(@procedure, statut: 'archives')) do + = t('pluralize.archived', count: @archived_dossiers.count) + %span.badge= @archived_dossiers.count + + .procedure-actions + %span.button.dropdown + Télécharger tous les dossiers + .dropdown-content.fade-in-down + %ul.dropdown-items + %li + = link_to "Au format .csv", backoffice_download_dossiers_tps_path(format: :csv, procedure_id: @procedure.id), target: "_blank" + %li + = link_to "Au format .xls", backoffice_download_dossiers_tps_path(format: :xls, procedure_id: @procedure.id), target: "_blank" + %li + = link_to "Au format .ods", backoffice_download_dossiers_tps_path(format: :ods, procedure_id: @procedure.id), target: "_blank" + + .container + - if @dossiers.present? + %table.table.dossiers-table.hoverable + %thead + %tr + %th.number-col Nº dossier + %th Demandeur + %th.status-col Statut + %th.follow-col + %tbody + - @dossiers.each do |dossier| + %tr + %td.number-col + = link_to(dossier_path(@procedure, dossier), class: 'cell-link') do + %i.folder + - if @followed_dossiers.with_unread_notifications.include?(dossier) + %span.notifications{ 'aria-label': 'notifications' } + = dossier.id + %td= link_to(dossier.user.email, dossier_path(@procedure, dossier), class: 'cell-link') + %td.status-col + = link_to(dossier_path(@procedure, dossier), class: 'cell-link') do + = render partial: 'status', locals: { dossier: dossier } + %td.follow-col= render partial: 'dossier_actions', locals: { procedure: @procedure, dossier: dossier, dossier_is_followed: @followed_dossiers_id.include?(dossier.id) } + = paginate @dossiers + - else + %h2.empty-text Aucun dossier diff --git a/app/views/new_gestionnaire/recherches/index.html.haml b/app/views/new_gestionnaire/recherches/index.html.haml new file mode 100644 index 000000000..0c665ac1d --- /dev/null +++ b/app/views/new_gestionnaire/recherches/index.html.haml @@ -0,0 +1,27 @@ +.container + .backoffice-title + Résultat de la recherche : + = pluralize(@dossiers.count, "dossier trouvé", "dossiers trouvés") + + - if @dossiers.present? + %table.table.dossiers-table.hoverable + %thead + %tr + %th Nº + %th Procédure + %th Demandeur + %th Statut + %tbody + - @dossiers.each do |dossier| + %tr + %td.number-col + = link_to(dossier_path(dossier.procedure, dossier), class: 'cell-link') do + %i.folder> + = dossier.id + %td= link_to(dossier.procedure.libelle, dossier_path(dossier.procedure, dossier), class: 'cell-link') + %td= link_to(dossier.user.email, dossier_path(dossier.procedure, dossier), class: 'cell-link') + %td.status-col + = link_to(dossier_path(dossier.procedure, dossier), class: 'cell-link') do + = render partial: 'new_gestionnaire/procedures/status', locals: { dossier: dossier } + - else + %h2 Aucun dossier correspondant à votre recherche n'a été trouvé diff --git a/app/views/root/landing.html.haml b/app/views/root/landing.html.haml index 2801ddbdb..c85bf7b80 100644 --- a/app/views/root/landing.html.haml +++ b/app/views/root/landing.html.haml @@ -1,6 +1,6 @@ .landing .landing-panel.hero-panel - .landing-panel-inner-content + .container .hero-wrapper .hero-text %p.hero-tagline @@ -8,16 +8,16 @@ %br vos procédures administratives en quelques minutes - %a.hero-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:open'])" } Demander une démo + %a.hero-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:show']); $crisp.push(['do', 'chat:open']);" } Demander une démo %p.hero-phone-cta - ou nous appeler au 01 40 15 68 49 + ou nous appeler au 09 72 62 57 12 .hero-illustration %img{ :src => image_url("landing/hero/dematerialiser.svg") } .landing-panel.features-panel - .landing-panel-inner-content + .container %h2.landing-panel-title.features-panel-title Un outil dédié aux organismes publics %ul.features @@ -47,7 +47,7 @@ à plusieurs .landing-panel - .landing-panel-inner-content + .container %h2.landing-panel-title Ce que les utilisateurs pensent du service %ul.quotes @@ -75,7 +75,7 @@ - cache "numbers-panel", :expires_in => 3.hours do .landing-panel.numbers-panel - .landing-panel-inner-content + .container %h2.landing-panel-title TPS en chiffres %ul.numbers %li.number @@ -101,7 +101,7 @@ des délais de traitement .landing-panel.users-panel - .landing-panel-inner-content + .container %h2.landing-panel-title Ils nous font confiance %ul.users @@ -122,12 +122,12 @@ %img.user-image{ :src => image_url("landing/users/driea-idf.jpg") } .landing-panel.cta-panel - .landing-panel-inner-content + .container .cta-panel-wrapper %div %h1.cta-panel-title Commencez à dématerialiser vos procédures %p.cta-panel-explanation Nous vous accompagnons dans la prise en main de l’outil %div - %a.cta-panel-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:open'])" } Demander une démo + %a.cta-panel-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:show']); $crisp.push(['do', 'chat:open']);" } Demander une démo %p.cta-panel-phone-cta - ou nous appeler au 01 40 15 68 49 + ou nous appeler au 09 72 62 57 12 diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index 6d2a06f92..38c478083 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -1,19 +1,36 @@ .patron - .patron-container - %h1 Patron + .container + %h1 Icones - %h2 Formulaires + %i.follow + %i.unfollow + %i.archive + %i.unarchive + %i.folder + %i.accept + %i.close + %i.without-continuation + %i.edit + %i.in-progress + %i.bubble + %i.attachment + %i.lock + + %h1 Formulaires %form.form - %label Nom - %input{ type: "text" } - %label Prénom - %input{ type: "text", placeholder: "ex : Ivan" } - %label Mot de passe - %input{ type: "password", value: "12345678" } - %input.button{ type: "submit", value: "Envoyer" } + = form_for @dossier, url: '', html: { class: 'form' } do |f| + = f.fields_for :champs do |champ_form| + - champ = champ_form.object + = render partial: "new_gestionnaire/dossiers/champs/#{champ.type_champ}", + locals: { champ: champ, form: champ_form } - %h2 Boutons + %input{ type: "password", value: "12345678" } + .send-wrapper + = f.submit 'Enregistrer un brouillon (formnovalidate)', formnovalidate: true, class: 'button send' + = f.submit 'Envoyer', class: 'button send' + + %h1 Boutons %p = link_to ".button", "#", class: "button" @@ -22,14 +39,125 @@ = link_to ".button.secondary", "#", class: "button secondary" + = link_to ".button.success", "#", class: "button success" + = link_to ".button.large", "#", class: "button large" = link_to ".button.large.primary", "#", class: "button large primary" + %p + = link_to "#", class: "button" do + %i.follow + = "%i.follow" + = link_to "#", class: "button" do + %i.unfollow + = "%i.unfollow" + = link_to "#", class: "button" do + %i.archive + = "%i.archive" + = link_to "#", class: "button" do + %i.unarchive + = "%i.unarchive" %p = link_to ".button.primary.expand", "#", class: "button primary expand" - %h2 Layout deux colonnes + %h1 Labels + + %span.label .label + %span.label.instruction .label.instruction + %span.label.construction .label.construction + %span.label.closed .label.closed + %span.label.refused .label.refused + %span.label.without-continuation .label.without-continuation + + %h1 Badges + + %span.badge 1 + %span.badge.warning 1 + + %h1 Cards + + .card + .card-title + Titre de la carte + Et voici le contenu de la carte + + .card.featured + .card-title + Titre de la carte mise en avant + Et voici le contenu de la carte + + %h1 Table + + %table.table + %thead + %tr + %th Header 1 + %th Header 2 + %tbody + %tr + %td Table Data 1 + %td Table Data 2 + %tr + %td Table Data 3 + %td Table Data 4 + + %h2 Hoverable (.table.hoverable) + + %table.table.hoverable + %thead + %tr + %th Header 1 + %th Header 2 + %tbody + %tr + %td Table Data 1 + %td Table Data 2 + %tr + %td Table Data 3 + %td Table Data 4 + + %h2 Vertical layout (.table.vertical) + + %table.table.vertical + %tbody + %tr + %th Header 1 + %td Table Data 1 + %tr + %th Header 2 + %td Table Data 2 + %tr + %th.header-section{ colspan: 2 } Header section + %tr + %th Header 3 + %td Table Data 3 + + %h1 Header + .backoffice-header + .container + Titre + %ul.tabs + %li.active + = link_to "Onglet actif", "#" + %li + = link_to "Onglet inactif", "#" + %li + = link_to "#" do + Onglet avec badge + %span.badge 2 + + .container + %h1 Breadcrumbs + + %ul.breadcrumbs + %li + = link_to "Procédure 123", "#" + %li + = "Dossier n° 38" + + + %h1 Layout deux colonnes .two-columns .columns-container @@ -37,3 +165,37 @@ Insérer ici le contenu de la colonne 1 .column Insérer ici le contenu de la colonne 2 + + .container + %section.ask-avis + %h1 Inviter une personne à donner son avis + %p.avis-notice L'invité pourra consulter, donner un avis sur le dossier et contribuer au fil de messagerie, mais il ne pourra le modifier. + + = form_for Avis.new, url: '/', html: { class: 'form' } do |f| + = f.email_field :email, placeholder: 'Adresse email', required: true + = f.text_area :introduction, rows: 3, value: 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true + .send-wrapper + = f.submit 'Demander un avis', class: 'button send' + + %section.list-avis + %h1.title + Avis des invités + %span.count 1 + + %ul + %li.one-avis + %h2.claimant + Vous + %span.date Demande d'avis envoyée le 12/01/2012 + %p Bonjour, merci de me donner votre avis sur ce dossier. + + .answer.flex.align-start + %i.bubble.avis-icon + .width-100 + %h2.gestionnaire + gestionnnaire@tps.com + %span.date Réponse donnée le 13/01/2012 + %p Je donne un avis favorable + + .container + %h2.empty-text Aucun dossier diff --git a/app/views/users/description/champs/_date.html.haml b/app/views/users/description/champs/_date.html.haml index f04b581a6..74088b67c 100644 --- a/app/views/users/description/champs/_date.html.haml +++ b/app/views/users/description/champs/_date.html.haml @@ -1,5 +1,5 @@ %input.form-control{ name: "champs['#{champ.id}']", placeholder: "JJ/MM/AAAA", id: "champs_#{champ.id}", - value: champ.value, + value: champ.value ? champ.object.value : champ.value, type: "date" } diff --git a/app/views/users/description/champs/_yes_no.html.haml b/app/views/users/description/champs/_yes_no.html.haml index e914d1bb3..83d4622dc 100644 --- a/app/views/users/description/champs/_yes_no.html.haml +++ b/app/views/users/description/champs/_yes_no.html.haml @@ -1,8 +1,8 @@ %div %label.radio-inline - = radio_button_tag "champs['#{champ.id}']", "true", champ.value == 'true' + = radio_button_tag "champs['#{champ.id}']", "true", champ.object.value == 'true' Oui %label.radio-inline - = radio_button_tag "champs['#{champ.id}']", "false", champ.value == 'false' + = radio_button_tag "champs['#{champ.id}']", "false", champ.object.value == 'false' Non diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 2c66cc6af..e140cf563 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -38,7 +38,7 @@ - if devise_mapping.rememberable? = f.check_box :remember_me, as: :boolean - = f.label :remember_me, "Se souvenir de moi" + = f.label :remember_me, "Se souvenir de moi", class: 'remember-me' = f.submit "Se connecter", class: "button large primary expand" diff --git a/app/workers/auto_archive_procedure_worker.rb b/app/workers/auto_archive_procedure_worker.rb index d9715b843..d8c6df0a1 100644 --- a/app/workers/auto_archive_procedure_worker.rb +++ b/app/workers/auto_archive_procedure_worker.rb @@ -1,7 +1,6 @@ class AutoArchiveProcedureWorker - include Sidekiq::Worker - def perform(*args) + Rails.logger.info("AutoArchiveProcedureWorker started at #{Time.now}") Procedure.publiees.where("auto_archive_on <= ?", Date.today).each do |procedure| procedure.dossiers.state_en_construction.each do |dossier| dossier.received! @@ -9,5 +8,12 @@ class AutoArchiveProcedureWorker procedure.archive end + Rails.logger.info("AutoArchiveProcedureWorker ended at #{Time.now}") end + + def queue_name + "cron" + end + + handle_asynchronously :perform end diff --git a/app/workers/weekly_overview_worker.rb b/app/workers/weekly_overview_worker.rb index 8f15194a7..2d3cf9260 100644 --- a/app/workers/weekly_overview_worker.rb +++ b/app/workers/weekly_overview_worker.rb @@ -1,7 +1,6 @@ class WeeklyOverviewWorker - include Sidekiq::Worker - def perform(*args) + Rails.logger.info("WeeklyOverviewWorker started at #{Time.now}") # Feature flipped to avoid mails in staging due to unprocessed dossier if Features.weekly_overview Gestionnaire.all @@ -9,5 +8,12 @@ class WeeklyOverviewWorker .reject { |_, overview| overview.nil? } .each { |gestionnaire, overview| GestionnaireMailer.last_week_overview(gestionnaire, overview).deliver_now } end + Rails.logger.info("WeeklyOverviewWorker ended at #{Time.now}") end + + def queue_name + "cron" + end + + handle_asynchronously :perform end diff --git a/bin/delayed_job b/bin/delayed_job new file mode 100755 index 000000000..edf195985 --- /dev/null +++ b/bin/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/config/deploy.rb b/config/deploy.rb index c6c264d9c..9f7cbbdec 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -58,7 +58,6 @@ set :shared_paths, [ "config/fog_credentials.yml", 'config/initializers/secret_token.rb', 'config/initializers/features.yml', - 'config/initializers/sidekiq.rb', "config/environments/#{rails_env}.rb", "config/initializers/token.rb", "config/initializers/urls.rb", @@ -131,7 +130,7 @@ desc "Deploys the current version to the server." task :deploy => :environment do queue 'export PATH=$PATH:/usr/local/rbenv/bin:/usr/local/rbenv/shims' deploy do - queue %[sudo stop sidekiq_#{user!} || true] + queue %[sudo service delayed_job_#{user!} stop || true] # Put things that will set up an empty directory into a fully set-up # instance of your project. invoke :'git:clone' @@ -142,7 +141,7 @@ task :deploy => :environment do to :launch do queue "/etc/init.d/#{user} upgrade " - queue! %[sudo start sidekiq_#{user!}] + queue! %[sudo service delayed_job_#{user!} start] queue "cd #{deploy_to}/#{current_path}/" queue "bundle exec rake db:seed RAILS_ENV=#{rails_env}" diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 864ba4221..fc49561ab 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -18,12 +18,28 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.irregular('avis', 'avis') end -# These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' -# end +# From https://github.com/davidcelis/inflections ActiveSupport::Inflector.inflections(:fr) do |inflect| + inflect.clear + inflect.plural(/$/, 's') - inflect.plural(/(hib|ch|bij|caill|p|gen|jouj)ou$/i, '\1oux') - inflect.irregular('avis', 'avis') + inflect.singular(/s$/, '') + + inflect.plural(/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)$/, '\1x') + inflect.singular(/(bijou|caillou|chou|genou|hibou|joujou|pou|au|eu|eau)x$/, '\1') + + inflect.plural(/(bleu|émeu|landau|lieu|pneu|sarrau)$/, '\1s') + inflect.plural(/al$/, 'aux') + inflect.plural(/ail$/, 'ails') + inflect.singular(/(journ|chev)aux$/, '\1al') + inflect.singular(/ails$/, 'ail') + + inflect.plural(/(b|cor|ém|gemm|soupir|trav|vant|vitr)ail$/, '\1aux') + inflect.singular(/(b|cor|ém|gemm|soupir|trav|vant|vitr)aux$/, '\1ail') + + inflect.plural(/(s|x|z)$/, '\1') + + inflect.irregular('monsieur', 'messieurs') + inflect.irregular('madame', 'mesdames') + inflect.irregular('mademoiselle', 'mesdemoiselles') end diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb deleted file mode 100644 index a61ed5e57..000000000 --- a/config/initializers/sidekiq.rb +++ /dev/null @@ -1,6 +0,0 @@ -Sidekiq.configure_server do |config| - Sidekiq::Logging.logger = Rails.logger - - schedule_file = "config/schedule.yml" - Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) -end diff --git a/config/locales/fr.yml b/config/locales/fr.yml index b94806305..2162eec59 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -285,3 +285,24 @@ fr: x_seconds: one: 1 seconde other: "%{count} secondes" + pluralize: + case: + zero: dossier + one: dossier + other: dossiers + processed: + zero: traité + one: traité + other: traités + new: + zero: nouveau + one: nouveau + other: nouveaux + followed: + zero: suivi + one: suivi + other: suivis + archived: + zero: archivé + one: archivé + other: archivés diff --git a/config/locales/models/dossier/fr.yml b/config/locales/models/dossier/fr.yml index 562adc110..c24c200b1 100644 --- a/config/locales/models/dossier/fr.yml +++ b/config/locales/models/dossier/fr.yml @@ -10,8 +10,6 @@ fr: state: draft: "Brouillon" initiated: "En construction" - replied: "En construction" - updated: "En construction" received: "En instruction" closed: "Accepté" refused: "Refusé" diff --git a/config/routes.rb b/config/routes.rb index b93f66f2b..dbaa51e86 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -47,9 +47,7 @@ Rails.application.routes.draw do authenticate :administration do resources :administrations, only: [:index, :create] namespace :administrations do - require 'sidekiq/web' - require 'sidekiq/cron/web' - mount Sidekiq::Web => '/sidekiq' + match "/delayed_job" => DelayedJobWeb, :anchor => false, :via => [:get, :post] end end @@ -190,7 +188,6 @@ Rails.application.routes.draw do post 'unarchive' end post 'reopen' => 'dossiers#reopen' - put 'follow' => 'dossiers#follow' resources :commentaires, only: [:index] resources :avis, only: [:create, :update] end @@ -238,11 +235,37 @@ Rails.application.routes.draw do end scope module: 'new_gestionnaire' do - resources :procedures, only: [] do - resources :dossiers, only: [] do - get 'attestation' + resources :procedures, only: [:index, :show], param: :procedure_id do + member do + resources :dossiers, only: [:show], param: :dossier_id do + member do + get 'attestation' + get 'messagerie' + get 'annotations-privees' => 'dossiers#annotations_privees' + get 'avis' + patch 'follow' + patch 'unfollow' + patch 'archive' + patch 'unarchive' + patch 'annotations' => 'dossiers#update_annotations' + post 'commentaire' => 'dossiers#create_commentaire' + scope :carte do + get 'position' + end + post 'avis' => 'dossiers#create_avis' + end + end end end + resources :avis, only: [:index, :show, :update] do + member do + get 'instruction' + get 'messagerie' + post 'commentaire' => 'avis#create_commentaire' + post 'avis' => 'avis#create_avis' + end + end + get "recherche" => "recherches#index" end apipie diff --git a/config/sidekiq.yml b/config/sidekiq.yml deleted file mode 100644 index 0fe0c4360..000000000 --- a/config/sidekiq.yml +++ /dev/null @@ -1,7 +0,0 @@ -:concurrency: 5 -staging: - :concurrency: 2 -production: - :concurrency: 2 -:queues: - - default diff --git a/db/migrate/20170908094900_add_confidentiel_field_to_avis.rb b/db/migrate/20170908094900_add_confidentiel_field_to_avis.rb new file mode 100644 index 000000000..eed554510 --- /dev/null +++ b/db/migrate/20170908094900_add_confidentiel_field_to_avis.rb @@ -0,0 +1,5 @@ +class AddConfidentielFieldToAvis < ActiveRecord::Migration[5.0] + def change + add_column :avis, :confidentiel, :boolean, default: false, null: false + end +end diff --git a/db/migrate/20170926083816_create_delayed_jobs.rb b/db/migrate/20170926083816_create_delayed_jobs.rb new file mode 100644 index 000000000..130a8d570 --- /dev/null +++ b/db/migrate/20170926083816_create_delayed_jobs.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration[5.0] + def self.up + create_table :delayed_jobs, force: true do |table| + table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue + table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. + table.text :handler, null: false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps null: true + end + + add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/db/migrate/20170926092716_add_cron_to_delayed_jobs.rb b/db/migrate/20170926092716_add_cron_to_delayed_jobs.rb new file mode 100644 index 000000000..33e89d190 --- /dev/null +++ b/db/migrate/20170926092716_add_cron_to_delayed_jobs.rb @@ -0,0 +1,9 @@ +class AddCronToDelayedJobs < ActiveRecord::Migration[5.0] + def self.up + add_column :delayed_jobs, :cron, :string + end + + def self.down + remove_column :delayed_jobs, :cron + end +end diff --git a/db/schema.rb b/db/schema.rb index 04ae79e32..e2bd74e20 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: 20170801083632) do +ActiveRecord::Schema.define(version: 20170926092716) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -99,9 +99,10 @@ ActiveRecord::Schema.define(version: 20170801083632) do t.text "answer" t.integer "gestionnaire_id" t.integer "dossier_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "claimant_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "claimant_id", null: false + t.boolean "confidentiel", default: false, null: false t.index ["claimant_id"], name: "index_avis_on_claimant_id", using: :btree t.index ["dossier_id"], name: "index_avis_on_dossier_id", using: :btree t.index ["gestionnaire_id"], name: "index_avis_on_gestionnaire_id", using: :btree @@ -161,6 +162,22 @@ ActiveRecord::Schema.define(version: 20170801083632) do t.index ["dossier_id"], name: "index_commentaires_on_dossier_id", using: :btree end + create_table "delayed_jobs", force: :cascade do |t| + t.integer "priority", default: 0, null: false + t.integer "attempts", default: 0, null: false + t.text "handler", null: false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at" + t.datetime "updated_at" + t.string "cron" + t.index ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree + end + create_table "dossiers", force: :cascade do |t| t.boolean "autorisation_donnees" t.integer "procedure_id" diff --git a/doc/apipie_examples.json b/doc/apipie_examples.json index 3b5809195..e88b2a84c 100644 --- a/doc/apipie_examples.json +++ b/doc/apipie_examples.json @@ -45,7 +45,7 @@ "updated_at": "2008-09-01T08:05:00.000Z", "archived": false, "mandataire_social": false, - "state": "updated", + "state": "initiated", "simplified_state": "En construction", "initiated_at": "2017-04-11T12:00:12.000Z", "received_at": null, diff --git a/lib/tasks/2017_09_19_set_confidentialite_to_old_avis.rake b/lib/tasks/2017_09_19_set_confidentialite_to_old_avis.rake new file mode 100644 index 000000000..9579811da --- /dev/null +++ b/lib/tasks/2017_09_19_set_confidentialite_to_old_avis.rake @@ -0,0 +1,5 @@ +namespace :'2017_09_19_set_confidentialite_to_old_avis' do + task set: :environment do + Avis.unscoped.update_all(confidentiel: true) + end +end diff --git a/lib/tasks/2017_09_22_set_dossier_updated_replied_to_initiated.rake b/lib/tasks/2017_09_22_set_dossier_updated_replied_to_initiated.rake new file mode 100644 index 000000000..001672458 --- /dev/null +++ b/lib/tasks/2017_09_22_set_dossier_updated_replied_to_initiated.rake @@ -0,0 +1,5 @@ +namespace :'2017_09_22_set_dossier_updated_replied_to_initiated' do + task set: :environment do + Dossier.unscoped.where(state: [:updated, :replied]).update_all(state: :initiated) + end +end diff --git a/spec/controllers/backoffice/avis_controller_spec.rb b/spec/controllers/backoffice/avis_controller_spec.rb index 950ad57b9..c059ba7b8 100644 --- a/spec/controllers/backoffice/avis_controller_spec.rb +++ b/spec/controllers/backoffice/avis_controller_spec.rb @@ -34,6 +34,7 @@ describe Backoffice::AvisController, type: :controller do it { expect(created_avis.dossier_id).to eq(dossier.id) } it { expect(created_avis.gestionnaire).to eq(gestionnaire) } it { expect(created_avis.claimant).to eq(claimant) } + it { expect(created_avis.confidentiel).to be(true) } end end diff --git a/spec/controllers/backoffice/commentaires_controller_spec.rb b/spec/controllers/backoffice/commentaires_controller_spec.rb index a491df621..27e3d5486 100644 --- a/spec/controllers/backoffice/commentaires_controller_spec.rb +++ b/spec/controllers/backoffice/commentaires_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Backoffice::CommentairesController, type: :controller do - let(:dossier) { create(:dossier, :replied) } + let(:dossier) { create(:dossier, :initiated) } let(:dossier_id) { dossier.id } let(:email_commentaire) { 'test@test.com' } let(:texte_commentaire) { 'Commentaire de test' } @@ -113,19 +113,15 @@ describe Backoffice::CommentairesController, type: :controller do describe 'change dossier state after post a comment' do context 'gestionnaire is connected' do - context 'when dossier is at state updated' do + context 'when dossier is at state initiated' do before do sign_in gestionnaire - dossier.updated! + dossier.initiated! post :create, params: {dossier_id: dossier_id, texte_commentaire: texte_commentaire} dossier.reload end - subject { dossier.state } - - it { is_expected.to eq('replied') } - it 'Notification email is send' do expect(NotificationMailer).to receive(:new_answer).and_return(NotificationMailer) expect(NotificationMailer).to receive(:deliver_now!) diff --git a/spec/controllers/backoffice/dossiers/procedure_controller_spec.rb b/spec/controllers/backoffice/dossiers/procedure_controller_spec.rb index 411e53121..6c1da0b3e 100644 --- a/spec/controllers/backoffice/dossiers/procedure_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers/procedure_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Backoffice::Dossiers::ProcedureController, type: :controller do let(:gestionnaire) { create :gestionnaire } - let(:procedure) { create :procedure } + let(:procedure) { create :procedure, :published } let(:archived) { false } let(:dossier) { create :dossier, procedure: procedure, archived: archived, state: 'initiated'} diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 37c1249bf..d5837a5ae 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -4,8 +4,8 @@ describe Backoffice::DossiersController, type: :controller do before do @request.env['HTTP_REFERER'] = TPS::Application::URL end - let(:procedure) { create :procedure } - let(:procedure2) { create :procedure } + let(:procedure) { create :procedure, :published } + let(:procedure2) { create :procedure, :published } let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: :initiated) } let(:dossier2) { create(:dossier, :with_entreprise, procedure: procedure2, state: :initiated) } @@ -38,8 +38,8 @@ describe Backoffice::DossiersController, type: :controller do context 'when gestionnaire is assign to many proceudure' do before do - create :assign_to, procedure: create(:procedure), gestionnaire: gestionnaire - create :assign_to, procedure: create(:procedure), gestionnaire: gestionnaire + create :assign_to, procedure: create(:procedure, :published), gestionnaire: gestionnaire + create :assign_to, procedure: create(:procedure, :published), gestionnaire: gestionnaire end it { expect(gestionnaire.procedures.count).to eq 3 } @@ -212,8 +212,8 @@ describe Backoffice::DossiersController, type: :controller do expect(response).to have_http_status(200) end - it 'returns nothing' do - expect(assigns(:dossiers).count).to eq(0) + it 'does not return the dossier' do + expect(assigns(:dossiers).pluck(:id)).not_to include(dossier2_id) end end end @@ -224,30 +224,13 @@ describe Backoffice::DossiersController, type: :controller do before do dossier.initiated! sign_in gestionnaire + post :receive, params: { dossier_id: dossier_id } + dossier.reload end - subject { post :receive, params: {dossier_id: dossier_id} } - - context 'when it post a receive instruction' do - before do - subject - dossier.reload - end - - it 'change state to received' do - expect(dossier.state).to eq('received') - end - end - - it 'Notification email is send' do - expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::ReceivedMail)).and_return(NotificationMailer) - expect(NotificationMailer).to receive(:deliver_now!) - - subject - end - - it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } + it { expect(dossier.state).to eq('received') } + it { is_expected.to redirect_to backoffice_dossier_path(dossier) } + it { expect(gestionnaire.follow?(dossier)).to be true } end describe 'POST #process_dossier' do @@ -369,46 +352,6 @@ describe Backoffice::DossiersController, type: :controller do end end - describe 'PUT #toggle_follow' do - before do - sign_in gestionnaire - end - - subject { put :follow, params: {dossier_id: dossier_id} } - - it { expect(subject.status).to eq 302 } - - context 'when dossier is at state initiated' do - let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } - - before do - subject - dossier.reload - end - - it 'change state for updated' do - expect(dossier.state).to eq 'updated' - end - end - - describe 'flash alert' do - context 'when dossier is not follow by gestionnaire' do - before do - subject - end - it { expect(flash[:notice]).to have_content 'Dossier suivi' } - end - - context 'when dossier is follow by gestionnaire' do - before do - create :follow, gestionnaire_id: gestionnaire.id, dossier_id: dossier.id - subject - end - it { expect(flash[:notice]).to have_content 'Dossier relaché' } - end - end - end - describe 'POST #reopen' do before do dossier.received! @@ -417,14 +360,14 @@ describe Backoffice::DossiersController, type: :controller do subject { post :reopen, params: {dossier_id: dossier_id} } - it 'change state to replied' do + it 'change state to initiated' do subject dossier.reload - expect(dossier.state).to eq('replied') + expect(dossier.state).to eq('initiated') end - it { is_expected.to redirect_to backoffice_dossiers_path } + it { is_expected.to redirect_to backoffice_dossier_path(id: dossier_id) } end describe 'POST #archive' do diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 952e10e42..c2c0441b9 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe InvitesController, type: :controller do - let(:dossier) { create(:dossier, :replied) } + let(:dossier) { create(:dossier, :initiated) } let(:email) { 'plop@octo.com' } describe '#POST create' do diff --git a/spec/controllers/new_gestionnaire/avis_controller_spec.rb b/spec/controllers/new_gestionnaire/avis_controller_spec.rb new file mode 100644 index 000000000..1a8b27a6a --- /dev/null +++ b/spec/controllers/new_gestionnaire/avis_controller_spec.rb @@ -0,0 +1,115 @@ +require 'spec_helper' + +describe NewGestionnaire::AvisController, type: :controller do + render_views + + let(:claimant) { create(:gestionnaire) } + let(:gestionnaire) { create(:gestionnaire) } + let(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) } + let(:dossier) { create(:dossier, :initiated, procedure: procedure) } + let!(:avis_without_answer) { Avis.create(dossier: dossier, claimant: claimant, gestionnaire: gestionnaire) } + let!(:avis_with_answer) { Avis.create(dossier: dossier, claimant: claimant, gestionnaire: gestionnaire, answer: 'yop') } + + before { sign_in(gestionnaire) } + + describe '#index' do + before { get :index } + + it { expect(response).to have_http_status(:success) } + it { expect(assigns(:avis_a_donner)).to match([avis_without_answer]) } + it { expect(assigns(:avis_donnes)).to match([avis_with_answer]) } + it { expect(assigns(:statut)).to eq('a-donner') } + + context 'with a statut equal to donnes' do + before { get :index, statut: 'donnes' } + + it { expect(assigns(:statut)).to eq('donnes') } + end + end + + describe '#show' do + before { get :show, { id: avis_without_answer.id } } + + it { expect(response).to have_http_status(:success) } + it { expect(assigns(:avis)).to eq(avis_without_answer) } + it { expect(assigns(:dossier)).to eq(dossier) } + end + + describe '#instruction' do + before { get :instruction, { id: avis_without_answer.id } } + + it { expect(response).to have_http_status(:success) } + it { expect(assigns(:avis)).to eq(avis_without_answer) } + it { expect(assigns(:dossier)).to eq(dossier) } + end + + describe '#messagerie' do + before { get :messagerie, { id: avis_without_answer.id } } + + it { expect(response).to have_http_status(:success) } + it { expect(assigns(:avis)).to eq(avis_without_answer) } + it { expect(assigns(:dossier)).to eq(dossier) } + end + + describe '#update' do + before do + patch :update, { id: avis_without_answer.id, avis: { answer: 'answer' } } + avis_without_answer.reload + end + + it { expect(response).to redirect_to(instruction_avis_path(avis_without_answer)) } + it { expect(avis_without_answer.answer).to eq('answer') } + it { expect(flash.notice).to eq('Votre réponse est enregistrée.') } + end + + describe '#create_commentaire' do + before do + post :create_commentaire, { id: avis_without_answer.id, commentaire: { body: 'commentaire body' } } + end + + it { expect(response).to redirect_to(messagerie_avis_path(avis_without_answer)) } + it { expect(dossier.commentaires.map(&:body)).to match(['commentaire body']) } + end + + describe '#create_avis' do + let!(:previous_avis) { Avis.create(dossier: dossier, claimant: claimant, gestionnaire: gestionnaire, confidentiel: previous_avis_confidentiel) } + let(:email) { 'a@b.com' } + let(:intro) { 'introduction' } + let(:created_avis) { Avis.last } + + before do + post :create_avis, { id: previous_avis.id, avis: { email: email, introduction: intro, confidentiel: asked_confidentiel } } + end + + context 'when the previous avis is public' do + let(:previous_avis_confidentiel) { false } + + context 'when the user asked for a public avis' do + let(:asked_confidentiel) { false } + + it { expect(created_avis.confidentiel).to be(false) } + it { expect(created_avis.email).to eq(email) } + it { expect(created_avis.introduction).to eq(intro) } + it { expect(created_avis.dossier).to eq(previous_avis.dossier) } + it { expect(created_avis.claimant).to eq(gestionnaire) } + it { expect(response).to redirect_to(instruction_avis_path(previous_avis)) } + end + + context 'when the user asked for a confidentiel avis' do + let(:asked_confidentiel) { true } + + it { expect(created_avis.confidentiel).to be(true) } + end + end + + context 'when the preivous avis is confidentiel' do + let(:previous_avis_confidentiel) { true } + + context 'when the user asked for a public avis' do + let(:asked_confidentiel) { false } + + it { expect(created_avis.confidentiel).to be(true) } + end + end + end +end diff --git a/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb b/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb index 0ec7b4bda..6610ae4c9 100644 --- a/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb +++ b/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb @@ -1,14 +1,20 @@ require 'spec_helper' describe NewGestionnaire::DossiersController, type: :controller do + render_views + let(:gestionnaire) { create(:gestionnaire) } + let(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) } + let(:dossier) { create(:dossier, :initiated, procedure: procedure) } + before { sign_in(gestionnaire) } - describe 'attestation' do + describe '#attestation' do context 'when a dossier has an attestation' do let(:fake_pdf) { double(read: 'pdf content') } - let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire]) } - let!(:dossier) { create(:dossier, attestation: Attestation.new, procedure: procedure) } + let!(:dossier) { create(:dossier, :initiated, attestation: Attestation.new, procedure: procedure) } + let!(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) } + let!(:dossier) { create(:dossier, :initiated, attestation: Attestation.new, procedure: procedure) } it 'returns the attestation pdf' do allow_any_instance_of(Attestation).to receive(:pdf).and_return(fake_pdf) @@ -23,4 +29,162 @@ describe NewGestionnaire::DossiersController, type: :controller do end end end + + describe '#follow' do + before do + expect_any_instance_of(Dossier).to receive(:next_step!).with('gestionnaire', 'follow') + patch :follow, params: { procedure_id: procedure.id, dossier_id: dossier.id } + end + + it { expect(gestionnaire.followed_dossiers).to match([dossier]) } + it { expect(flash.notice).to eq('Dossier suivi') } + it { expect(response).to redirect_to(procedures_url) } + end + + describe '#unfollow' do + before do + gestionnaire.followed_dossiers << dossier + patch :unfollow, params: { procedure_id: procedure.id, dossier_id: dossier.id } + gestionnaire.reload + end + + it { expect(gestionnaire.followed_dossiers).to match([]) } + it { expect(flash.notice).to eq("Vous ne suivez plus le dossier nº #{dossier.id}") } + it { expect(response).to redirect_to(procedures_url) } + end + + describe '#archive' do + before do + patch :archive, params: { procedure_id: procedure.id, dossier_id: dossier.id } + dossier.reload + end + + it { expect(dossier.archived).to be true } + it { expect(response).to redirect_to(procedures_url) } + end + + describe '#unarchive' do + before do + dossier.update_attributes(archived: true) + patch :unarchive, params: { procedure_id: procedure.id, dossier_id: dossier.id } + dossier.reload + end + + it { expect(dossier.archived).to be false } + it { expect(response).to redirect_to(procedures_url) } + end + + describe '#show #messagerie #annotations_privees #avis' do + before do + dossier.notifications = %w(champs annotations_privees avis commentaire).map{ |type| Notification.create!(type_notif: type) } + get method, params: { procedure_id: procedure.id, dossier_id: dossier.id } + dossier.notifications.each(&:reload) + end + + context '#show' do + let(:method) { :show } + it { expect(dossier.notifications.map(&:already_read)).to match([true, false, false, false]) } + it { expect(response).to have_http_status(:success) } + end + + context '#annotations_privees' do + let(:method) { :annotations_privees } + it { expect(dossier.notifications.map(&:already_read)).to match([false, true, false, false]) } + it { expect(response).to have_http_status(:success) } + end + + context '#avis' do + let(:method) { :avis } + it { expect(dossier.notifications.map(&:already_read)).to match([false, false, true, false]) } + it { expect(response).to have_http_status(:success) } + end + + context '#messagerie' do + let(:method) { :messagerie } + it { expect(dossier.notifications.map(&:already_read)).to match([false, false, false, true]) } + it { expect(response).to have_http_status(:success) } + end + end + + describe "#create_commentaire" do + let(:saved_commentaire) { dossier.commentaires.first } + + before do + post :create_commentaire, params: { + procedure_id: procedure.id, + dossier_id: dossier.id, + commentaire: { body: 'body' } + } + end + + it { expect(saved_commentaire.body).to eq('body') } + it { expect(saved_commentaire.email).to eq(gestionnaire.email) } + it { expect(saved_commentaire.dossier).to eq(dossier) } + it { expect(response).to redirect_to(messagerie_dossier_path(dossier.procedure, dossier)) } + end + + describe "#create_avis" do + let(:saved_avis) { dossier.avis.first } + + before do + post :create_avis, params: { + procedure_id: procedure.id, + dossier_id: dossier.id, + avis: { email: 'email@a.com', introduction: 'intro', confidentiel: true } + } + end + + it { expect(saved_avis.email).to eq('email@a.com') } + it { expect(saved_avis.introduction).to eq('intro') } + it { expect(saved_avis.confidentiel).to eq(true) } + it { expect(saved_avis.dossier).to eq(dossier) } + it { expect(saved_avis.claimant).to eq(gestionnaire) } + it { expect(response).to redirect_to(avis_dossier_path(dossier.procedure, dossier)) } + end + + describe "#update_annotations" do + let(:champ_multiple_drop_down_list) do + type_de_champ = TypeDeChamp.create(type_champ: 'multiple_drop_down_list', libelle: 'libelle') + ChampPrivate.create(type_de_champ: type_de_champ) + end + + let(:champ_datetime) do + type_de_champ = TypeDeChamp.create(type_champ: 'datetime', libelle: 'libelle') + ChampPrivate.create(type_de_champ: type_de_champ) + end + + let(:dossier) do + create(:dossier, :initiated, procedure: procedure, champs_private: [champ_multiple_drop_down_list, champ_datetime]) + end + + before do + patch :update_annotations, params: { + procedure_id: procedure.id, + dossier_id: dossier.id, + dossier: { + champs_private_attributes: { + '0': { + id: champ_multiple_drop_down_list.id, + value: ['', 'un', 'deux'] + }, + '1': { + id: champ_datetime.id, + 'value(1i)': 2019, + 'value(2i)': 12, + 'value(3i)': 21, + 'value(4i)': 13, + 'value(5i)': 17 + } + } + } + } + + champ_multiple_drop_down_list.reload + champ_datetime.reload + end + + it { expect(champ_multiple_drop_down_list.value).to eq('["un", "deux"]') } + it { expect(champ_datetime.value).to eq('21/12/2019 13:17') } + it { expect(response).to redirect_to(annotations_privees_dossier_path(dossier.procedure, dossier)) } + end end diff --git a/spec/controllers/new_gestionnaire/procedures_controller_spec.rb b/spec/controllers/new_gestionnaire/procedures_controller_spec.rb index fc7623488..99ed9d86c 100644 --- a/spec/controllers/new_gestionnaire/procedures_controller_spec.rb +++ b/spec/controllers/new_gestionnaire/procedures_controller_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe NewGestionnaire::ProceduresController, type: :controller do - describe 'before_action: ensure_ownership!' do - it 'is present' do + describe "before_action: ensure_ownership!" do + it "is present" do before_actions = NewGestionnaire::ProceduresController ._process_action_callbacks .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} @@ -12,7 +12,7 @@ describe NewGestionnaire::ProceduresController, type: :controller do end end - describe 'ensure_ownership!' do + describe "ensure_ownership!" do let(:gestionnaire) { create(:gestionnaire) } before do @@ -23,22 +23,265 @@ describe NewGestionnaire::ProceduresController, type: :controller do @controller.send(:ensure_ownership!) end - context 'when a gestionnaire asks for its procedure' do + context "when a gestionnaire asks for its procedure" do let(:asked_procedure) { create(:procedure, gestionnaires: [gestionnaire]) } - it 'does not redirects nor flash' do + it "does not redirects nor flash" do expect(@controller).not_to have_received(:redirect_to) expect(flash.alert).to eq(nil) end end - context 'when a gestionnaire asks for another procedure' do + context "when a gestionnaire asks for another procedure" do let(:asked_procedure) { create(:procedure) } - it 'redirects and flash' do + it "redirects and flash" do expect(@controller).to have_received(:redirect_to).with(root_path) expect(flash.alert).to eq("Vous n'avez pas accès à cette procédure") end end end + + describe "before_action: redirect_to_avis_if_needed" do + it "is present" do + before_actions = NewGestionnaire::ProceduresController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:redirect_to_avis_if_needed) + end + end + + describe "redirect_to_avis_if_needed" do + let(:gestionnaire) { create(:gestionnaire) } + + before do + expect(@controller).to receive(:current_gestionnaire).at_least(:once).and_return(gestionnaire) + allow(@controller).to receive(:redirect_to) + end + + context "when a gestionnaire has some procedures" do + let!(:some_procedure) { create(:procedure, gestionnaires: [gestionnaire]) } + + before { @controller.send(:redirect_to_avis_if_needed) } + + it "does not redirects nor flash" do + expect(@controller).not_to have_received(:redirect_to) + end + end + + context "when a gestionnaire has no procedure and some avis" do + before do + Avis.create!(dossier: create(:dossier), claimant: create(:gestionnaire), gestionnaire: gestionnaire) + @controller.send(:redirect_to_avis_if_needed) + end + + it "redirects avis" do + expect(@controller).to have_received(:redirect_to).with(avis_index_path) + end + end + end + + describe "#index" do + let(:gestionnaire) { create(:gestionnaire) } + subject { get :index } + + context "when not logged" do + before { subject } + it { expect(response).to redirect_to(new_user_session_path)} + end + + context "when logged in" do + before { sign_in(gestionnaire) } + + it { expect(response).to have_http_status(:ok) } + + context "with procedures assigned" do + let(:procedure1) { create(:procedure, :published) } + let(:procedure2) { create(:procedure, :published, :archived) } + let(:procedure3) { create(:procedure) } + + before do + gestionnaire.procedures << procedure1 + gestionnaire.procedures << procedure2 + gestionnaire.procedures << procedure3 + subject + end + + it { expect(assigns(:procedures)).to include(procedure1, procedure2) } + end + + context "with dossiers" do + let(:procedure) { create(:procedure, :published) } + let(:dossier) { create(:dossier, state: state, procedure: procedure) } + + before do + gestionnaire.procedures << procedure + dossier + end + + context "with draft state" do + let(:state) { "draft" } + before { subject } + + it { expect(assigns(:dossiers_count_per_procedure)[procedure.id]).to eq(nil) } + it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure.id]).to eq(nil) } + it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(nil) } + it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) } + it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(nil) } + end + + context "with not draft state on multiple procedures" do + let(:procedure2) { create(:procedure, :published) } + let(:state) { "initiated" } + + before do + create(:dossier, procedure: procedure, state: "initiated") + create(:dossier, procedure: procedure, state: "received") + create(:dossier, procedure: procedure, state: "without_continuation", archived: true) + + gestionnaire.procedures << procedure2 + create(:dossier, :followed, procedure: procedure2, state: "initiated") + create(:dossier, procedure: procedure2, state: "closed") + gestionnaire.followed_dossiers << create(:dossier, procedure: procedure2, state: "received") + + subject + end + + it { expect(assigns(:dossiers_count_per_procedure)[procedure.id]).to eq(3) } + it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure.id]).to eq(3) } + it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure.id]).to eq(nil) } + it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure.id]).to eq(1) } + it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure.id]).to eq(nil) } + + it { expect(assigns(:dossiers_count_per_procedure)[procedure2.id]).to eq(3) } + it { expect(assigns(:dossiers_a_suivre_count_per_procedure)[procedure2.id]).to eq(nil) } + it { expect(assigns(:followed_dossiers_count_per_procedure)[procedure2.id]).to eq(1) } + it { expect(assigns(:dossiers_archived_count_per_procedure)[procedure2.id]).to eq(nil) } + it { expect(assigns(:dossiers_termines_count_per_procedure)[procedure2.id]).to eq(1) } + end + end + end + end + + describe "#show" do + let(:gestionnaire) { create(:gestionnaire) } + let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire]) } + + context "when logged in" do + before do + sign_in(gestionnaire) + get :show, params: { procedure_id: procedure.id } + end + + it { expect(response).to have_http_status(:ok) } + it { expect(assigns(:procedure)).to eq(procedure) } + + context 'with a new draft dossier' do + let!(:draft_dossier) { create(:dossier, procedure: procedure, state: 'draft') } + + it { expect(assigns(:a_suivre_dossiers)).to be_empty } + it { expect(assigns(:followed_dossiers)).to be_empty } + it { expect(assigns(:termines_dossiers)).to be_empty } + it { expect(assigns(:all_state_dossiers)).to be_empty } + it { expect(assigns(:archived_dossiers)).to be_empty } + end + + context 'with a new dossier without follower' do + let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: 'received') } + + it { expect(assigns(:a_suivre_dossiers)).to match([new_unfollow_dossier]) } + it { expect(assigns(:followed_dossiers)).to be_empty } + it { expect(assigns(:termines_dossiers)).to be_empty } + it { expect(assigns(:all_state_dossiers)).to match([new_unfollow_dossier]) } + it { expect(assigns(:archived_dossiers)).to be_empty } + end + + context 'with a new dossier with a follower' do + let!(:new_followed_dossier) { create(:dossier, procedure: procedure, state: 'received') } + + before { gestionnaire.followed_dossiers << new_followed_dossier } + + it { expect(assigns(:a_suivre_dossiers)).to be_empty } + it { expect(assigns(:followed_dossiers)).to match([new_followed_dossier]) } + it { expect(assigns(:termines_dossiers)).to be_empty } + it { expect(assigns(:all_state_dossiers)).to match([new_followed_dossier]) } + it { expect(assigns(:archived_dossiers)).to be_empty } + end + + context 'with a termine dossier with a follower' do + let!(:termine_dossier) { create(:dossier, procedure: procedure, state: 'closed') } + + it { expect(assigns(:a_suivre_dossiers)).to be_empty } + it { expect(assigns(:followed_dossiers)).to be_empty } + it { expect(assigns(:termines_dossiers)).to match([termine_dossier]) } + it { expect(assigns(:all_state_dossiers)).to match([termine_dossier]) } + it { expect(assigns(:archived_dossiers)).to be_empty } + end + + context 'with an archived dossier' do + let!(:archived_dossier) { create(:dossier, procedure: procedure, state: 'received', archived: true) } + + it { expect(assigns(:a_suivre_dossiers)).to be_empty } + it { expect(assigns(:followed_dossiers)).to be_empty } + it { expect(assigns(:termines_dossiers)).to be_empty } + it { expect(assigns(:all_state_dossiers)).to be_empty } + it { expect(assigns(:archived_dossiers)).to match([archived_dossier]) } + end + + describe 'statut' do + let!(:a_suivre__dossier) { Timecop.freeze(1.day.ago){ create(:dossier, procedure: procedure, state: 'received') } } + let!(:new_followed_dossier) { Timecop.freeze(2.day.ago){ create(:dossier, procedure: procedure, state: 'received') } } + let!(:termine_dossier) { Timecop.freeze(3.day.ago){ create(:dossier, procedure: procedure, state: 'closed') } } + let!(:archived_dossier) { Timecop.freeze(4.day.ago){ create(:dossier, procedure: procedure, state: 'received', archived: true) } } + before do + gestionnaire.followed_dossiers << new_followed_dossier + get :show, params: { procedure_id: procedure.id, statut: statut } + end + + context 'when statut is empty' do + let(:statut) { nil } + + it { expect(assigns(:dossiers)).to match([a_suivre__dossier]) } + it { expect(assigns(:statut)).to eq('a-suivre') } + end + + context 'when statut is a-suivre' do + let(:statut) { 'a-suivre' } + + it { expect(assigns(:statut)).to eq('a-suivre') } + it { expect(assigns(:dossiers)).to match([a_suivre__dossier]) } + end + + context 'when statut is suivis' do + let(:statut) { 'suivis' } + + it { expect(assigns(:statut)).to eq('suivis') } + it { expect(assigns(:dossiers)).to match([new_followed_dossier]) } + end + + context 'when statut is traites' do + let(:statut) { 'traites' } + + it { expect(assigns(:statut)).to eq('traites') } + it { expect(assigns(:dossiers)).to match([termine_dossier]) } + end + + context 'when statut is tous' do + let(:statut) { 'tous' } + + it { expect(assigns(:statut)).to eq('tous') } + it { expect(assigns(:dossiers)).to match([a_suivre__dossier, new_followed_dossier, termine_dossier].sort_by(&:updated_at)) } + end + + context 'when statut is archives' do + let(:statut) { 'archives' } + + it { expect(assigns(:statut)).to eq('archives') } + it { expect(assigns(:dossiers)).to match([archived_dossier]) } + end + end + end + end end diff --git a/spec/controllers/new_gestionnaire/recherches_controller_spec.rb b/spec/controllers/new_gestionnaire/recherches_controller_spec.rb new file mode 100644 index 000000000..06a3f557a --- /dev/null +++ b/spec/controllers/new_gestionnaire/recherches_controller_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe NewGestionnaire::RecherchesController, type: :controller do + let(:dossier) { create(:dossier, :initiated) } + let(:dossier2) { create(:dossier, :initiated, procedure: dossier.procedure) } + let(:gestionnaire) { create(:gestionnaire) } + + before { gestionnaire.procedures << dossier2.procedure } + + describe 'GET #index' do + before { sign_in gestionnaire } + + subject { get :index, params: { q: query } } + + describe 'by id' do + context 'when gestionnaire own the dossier' do + let(:query) { dossier.id } + + it { is_expected.to have_http_status(200) } + + it 'returns the expected dossier' do + subject + expect(assigns(:dossiers).count).to eq(1) + expect(assigns(:dossiers).first.id).to eq(dossier.id) + end + end + + context 'when gestionnaire do not own the dossier' do + let(:dossier3) { create(:dossier, :initiated) } + let(:query) { dossier3.id } + + it { is_expected.to have_http_status(200) } + + it 'does not return the dossier' do + subject + expect(assigns(:dossiers).count).to eq(0) + end + end + end + end +end diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 4ea5e837b..f39c348db 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -20,7 +20,7 @@ describe RootController, type: :controller do context 'when gestionnaire is affect to a procedure' do before do - create :assign_to, procedure: (create :procedure), gestionnaire: gestionnaire + create :assign_to, procedure: (create :procedure, :published), gestionnaire: gestionnaire end it { expect(subject).to redirect_to(backoffice_dossiers_procedure_path(id: Procedure.all.first.id)) } diff --git a/spec/controllers/users/commentaires_controller_spec.rb b/spec/controllers/users/commentaires_controller_spec.rb index 1be0021cf..1b05ff5ac 100644 --- a/spec/controllers/users/commentaires_controller_spec.rb +++ b/spec/controllers/users/commentaires_controller_spec.rb @@ -102,23 +102,5 @@ describe Users::CommentairesController, type: :controller do end end end - - describe 'change dossier state after post a comment' do - context 'when user is connected' do - context 'when dossier is at state replied' do - before do - sign_in dossier.user - dossier.replied! - - post :create, params: { dossier_id: dossier_id, texte_commentaire: texte_commentaire } - dossier.reload - end - - subject { dossier.state } - - it { is_expected.to eq('updated') } - end - end - end end end diff --git a/spec/controllers/users/description_controller_shared_example.rb b/spec/controllers/users/description_controller_shared_example.rb index f61a12773..90d101cef 100644 --- a/spec/controllers/users/description_controller_shared_example.rb +++ b/spec/controllers/users/description_controller_shared_example.rb @@ -223,38 +223,44 @@ shared_examples 'description_controller_spec' do describe 'Sauvegarde des champs' do let(:champs_dossier) { dossier.champs } - let(:dossier_champs_first) { 'test value' } + let(:dossier_text_value) { 'test value' } let(:dossier_date_value) { '23/06/2016' } let(:dossier_hour_value) { '17' } let(:dossier_minute_value) { '00' } + let(:dossier_datetime_champ_id) { dossier.champs.find { |c| c.type_champ == "datetime" }.id } + let(:dossier_text_champ_id) { dossier.champs.find { |c| c.type_champ == "text" }.id } + let(:params) { + { + dossier_id: dossier_id, + champs: { + "'#{dossier_text_champ_id}'" => dossier_text_value, # PARFOIS ce putain de champ est associé à un type datetime, et en plus parfois l'ordre n'est pas le bon + "'#{dossier_datetime_champ_id}'" => dossier_date_value + }, + time_hour: { + "'#{dossier_datetime_champ_id}'" => dossier_hour_value, + }, + time_minute: { + "'#{dossier_datetime_champ_id}'" => dossier_minute_value, + } + } + } before do - post :update, params: {dossier_id: dossier_id, - champs: { - "'#{dossier.champs.first.id}'" => dossier_champs_first, - "'#{dossier.champs.second.id}'" => dossier_date_value - }, - time_hour: { - "'#{dossier.champs.second.id}'" => dossier_hour_value, - }, - time_minute: { - "'#{dossier.champs.second.id}'" => dossier_minute_value, - } - } + post :update, params: params dossier.reload end - it { expect(dossier.champs.first.value).to eq(dossier_champs_first) } + it { expect(dossier.champs.find(dossier_text_champ_id).value).to eq(dossier_text_value) } it { expect(response).to redirect_to users_dossier_recapitulatif_path } context 'when champs is type_de_champ datetime' do - it { expect(dossier.champs.second.value).to eq(dossier_date_value + ' ' + dossier_hour_value + ':' + dossier_minute_value) } + it { expect(dossier.champs.find(dossier_datetime_champ_id).value).to eq(dossier_date_value + ' ' + dossier_hour_value + ':' + dossier_minute_value) } end context 'when champs value is empty' do - let(:dossier_champs_first) { '' } + let(:dossier_text_value) { '' } - it { expect(dossier.champs.first.value).to eq(dossier_champs_first) } + it { expect(dossier.champs.find(dossier_text_champ_id).value).to eq(dossier_text_value) } it { expect(response).to redirect_to users_dossier_recapitulatif_path } context 'when champs is mandatory' do diff --git a/spec/controllers/users/dossiers/commentaires_controller_spec.rb b/spec/controllers/users/dossiers/commentaires_controller_spec.rb index 1fd067919..18908f468 100644 --- a/spec/controllers/users/dossiers/commentaires_controller_spec.rb +++ b/spec/controllers/users/dossiers/commentaires_controller_spec.rb @@ -15,13 +15,11 @@ describe Users::Dossiers::CommentairesController, type: :controller do before do sign_in invite.user - dossier.replied! end it do subject is_expected.to redirect_to users_dossiers_invite_path(invite.id) - expect(dossier.state).to eq 'replied' end it 'should notify user' do diff --git a/spec/decorators/champ_decorator_spec.rb b/spec/decorators/champ_decorator_spec.rb index c0817e4b6..2dc572ec5 100644 --- a/spec/decorators/champ_decorator_spec.rb +++ b/spec/decorators/champ_decorator_spec.rb @@ -20,6 +20,19 @@ describe ChampDecorator do end end + describe 'for a engagement' do + let(:type_champ) { :engagement } + + context 'when value is on' do + before { champ.update value: 'on' } + it { is_expected.to eq 'Oui' } + end + + context 'when value is other' do + it { is_expected.to eq 'Non' } + end + end + describe 'for a multiple_drop_down_list' do let(:type_champ) { :multiple_drop_down_list } diff --git a/spec/decorators/dossier_decorator_spec.rb b/spec/decorators/dossier_decorator_spec.rb index 9920a89b4..058d72563 100644 --- a/spec/decorators/dossier_decorator_spec.rb +++ b/spec/decorators/dossier_decorator_spec.rb @@ -27,16 +27,6 @@ describe DossierDecorator do expect(subject).to eq('En construction') end - it 'replied is repondu' do - dossier.replied! - expect(subject).to eq('En construction') - end - - it 'updated is mis à jour' do - dossier.updated! - expect(subject).to eq('En construction') - end - it 'closed is traité' do dossier.closed! expect(subject).to eq('Accepté') @@ -79,7 +69,7 @@ describe DossierDecorator do context "when the dossier is not in brouillon state" do before do - dossier.state = 'updated' + dossier.state = 'initiated' dossier.save end diff --git a/spec/facades/admin_procedures_show_facades_spec.rb b/spec/facades/admin_procedures_show_facades_spec.rb index 1ea3a1a1e..5e1641aca 100644 --- a/spec/facades/admin_procedures_show_facades_spec.rb +++ b/spec/facades/admin_procedures_show_facades_spec.rb @@ -5,9 +5,8 @@ describe AdminProceduresShowFacades do let!(:dossier_0) { create(:dossier, procedure: procedure, state: 'draft') } let!(:dossier_1) { create(:dossier, procedure: procedure, state: 'initiated') } - let!(:dossier_2) { create(:dossier, procedure: procedure, state: 'replied') } + let!(:dossier_2) { create(:dossier, procedure: procedure, state: 'initiated') } let!(:dossier_6) { create(:dossier, procedure: procedure, archived: true, state: 'initiated') } - let!(:dossier_7) { create(:dossier, procedure: procedure, state: 'updated') } subject { AdminProceduresShowFacades.new procedure } @@ -20,13 +19,13 @@ describe AdminProceduresShowFacades do describe '.dossiers' do subject { super().dossiers } - it { expect(subject.size).to eq(4) } + it { expect(subject.size).to eq(3) } end describe '.dossiers_for_pie_highchart' do subject { super().dossiers_for_pie_highchart } - it { expect(subject).to eq({ 'En construction' => 3 }) } + it { expect(subject).to eq({ 'En construction' => 2 }) } end describe '.dossiers_archived_by_state_total' do @@ -47,19 +46,7 @@ describe AdminProceduresShowFacades do describe 'dossiers_total' do subject { super().dossiers_total } - it { is_expected.to eq(4) } - end - - describe 'dossiers_waiting_gestionnaire_total' do - subject { super().dossiers_waiting_gestionnaire_total } - - it { is_expected.to eq(1) } - end - - describe 'dossiers_waiting_user_total' do - subject { super().dossiers_waiting_user_total } - - it { is_expected.to eq(1) } + it { is_expected.to eq(3) } end describe 'dossiers_termine_total' do diff --git a/spec/facades/dossiers_list_facades_spec.rb b/spec/facades/dossiers_list_facades_spec.rb index 08ca0c878..98f19254f 100644 --- a/spec/facades/dossiers_list_facades_spec.rb +++ b/spec/facades/dossiers_list_facades_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe DossiersListFacades do let(:gestionnaire) { create :gestionnaire } - let(:procedure) { create :procedure, libelle: 'Ma procédure' } - let(:procedure_2) { create :procedure, libelle: 'Ma seconde procédure' } + let(:procedure) { create :procedure, :published, libelle: 'Ma procédure' } + let(:procedure_2) { create :procedure, :published, libelle: 'Ma seconde procédure' } let!(:preference) { create :preference_list_dossier, gestionnaire: gestionnaire, diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 630e787ba..07cf128e9 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -1,5 +1,13 @@ FactoryGirl.define do factory :champ do type_de_champ { FactoryGirl.create(:type_de_champ_public) } + + trait :checkbox do + type_de_champ { FactoryGirl.create(:type_de_champ_public, :checkbox) } + end + + trait :header_section do + type_de_champ { FactoryGirl.create(:type_de_champ_public, :header_section) } + end end end diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index f53eeb0f6..036d498ca 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -5,7 +5,7 @@ FactoryGirl.define do before(:create) do |dossier, _evaluator| unless dossier.procedure - procedure = create(:procedure, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_type_de_champ_private) + procedure = create(:procedure, :published, :with_two_type_de_piece_justificative, :with_type_de_champ, :with_type_de_champ_private) dossier.procedure = procedure end end @@ -65,8 +65,15 @@ FactoryGirl.define do end end - trait :replied do - state 'replied' + trait :followed do + after(:create) do |dossier, _evaluator| + g = create(:gestionnaire) + g.followed_dossiers << dossier + end + end + + trait :initiated do + state 'initiated' end end end diff --git a/spec/factories/entreprise.rb b/spec/factories/entreprise.rb index a90e8cb28..95a2ae53c 100644 --- a/spec/factories/entreprise.rb +++ b/spec/factories/entreprise.rb @@ -10,5 +10,11 @@ FactoryGirl.define do siret_siege_social '44011762001530' code_effectif_entreprise '51' date_creation Time.at(1453976189).to_datetime + + trait :is_association do + after(:create) do |entreprise, _evaluator| + create(:rna_information, entreprise: entreprise) + end + end end end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 558741a97..e258bd329 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -98,5 +98,11 @@ FactoryGirl.define do procedure.publish!(generate(:published_path)) end end + + trait :archived do + after(:build) do |procedure, _evaluator| + procedure.archived_at = Time.now + end + end end end diff --git a/spec/factories/type_de_champ_public.rb b/spec/factories/type_de_champ_public.rb index 3e1acf1e8..76511640a 100644 --- a/spec/factories/type_de_champ_public.rb +++ b/spec/factories/type_de_champ_public.rb @@ -10,6 +10,10 @@ FactoryGirl.define do type_champ 'checkbox' end + trait :header_section do + type_champ 'header_section' + end + trait :type_dossier_link do libelle 'Référence autre dossier' type_champ 'dossier_link' diff --git a/spec/features/backoffice/add_commentaire_spec.rb b/spec/features/backoffice/add_commentaire_spec.rb index 4a02c93c8..7bc6d1769 100644 --- a/spec/features/backoffice/add_commentaire_spec.rb +++ b/spec/features/backoffice/add_commentaire_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' feature 'add commentaire on backoffice' do - let(:procedure) { create(:procedure) } - let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') } + let(:procedure) { create(:procedure, :published) } + let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } let(:dossier_id) { dossier.id } let!(:commentaire) { create(:commentaire, dossier: dossier, email: 'toto@toto.com') } let(:email_commentaire) { 'test@test.com' } diff --git a/spec/features/backoffice/connection_spec.rb b/spec/features/backoffice/connection_spec.rb index 10252b367..65a20af26 100644 --- a/spec/features/backoffice/connection_spec.rb +++ b/spec/features/backoffice/connection_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'when gestionnaire come to /backoffice and is not authenticated' do - let(:procedure) { create(:procedure) } + let(:procedure) { create(:procedure, :published) } let!(:dossier) { create(:dossier, procedure: procedure) } before do visit backoffice_path diff --git a/spec/features/backoffice/flux_de_commentaires_spec.rb b/spec/features/backoffice/flux_de_commentaires_spec.rb index f2eef84f0..8ff622b4d 100644 --- a/spec/features/backoffice/flux_de_commentaires_spec.rb +++ b/spec/features/backoffice/flux_de_commentaires_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' feature 'backoffice: flux de commentaires' do - let(:procedure) { create(:procedure) } + let(:procedure) { create(:procedure, :published) } let(:gestionnaire) { create(:gestionnaire) } - let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') } + let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } let(:dossier_id) { dossier.id } let(:champ1) { create(:champ, dossier: dossier, type_de_champ: create(:type_de_champ_public, libelle: "subtitle1")) } diff --git a/spec/features/backoffice/index_show_procedure_spec.rb b/spec/features/backoffice/index_show_procedure_spec.rb index ef0eade1a..fc6e60d47 100644 --- a/spec/features/backoffice/index_show_procedure_spec.rb +++ b/spec/features/backoffice/index_show_procedure_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' feature 'As an Accompagnateur I can navigate and use each functionnality around procedures and their dossiers' do let(:user) { create(:user) } let(:gestionnaire) { create(:gestionnaire) } - let(:procedure_1) { create(:procedure, :with_type_de_champ, libelle: 'procedure 1') } - let(:procedure_2) { create(:procedure, :with_type_de_champ, libelle: 'procedure 2') } + let(:procedure_1) { create(:procedure, :published, :with_type_de_champ, libelle: 'procedure 1') } + let(:procedure_2) { create(:procedure, :published, :with_type_de_champ, libelle: 'procedure 2') } before 'Assign procedures to Accompagnateur and generating dossiers for each' do create :assign_to, gestionnaire: gestionnaire, procedure: procedure_1 diff --git a/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb b/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb index 9e7ff7289..7e7f57772 100644 --- a/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb +++ b/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' feature 'usage of pref list dossier lateral panel', js: true do let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } - let(:procedure) { create(:procedure, administrateur: administrateur) } + let(:procedure) { create(:procedure, :published, administrateur: administrateur) } before do - create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') + create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') create :assign_to, procedure: procedure, gestionnaire: gestionnaire login_as gestionnaire, scope: :gestionnaire diff --git a/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb index 79dd816ac..9b9dd4432 100644 --- a/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb +++ b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' feature 'usage of pref list dossier lateral panel by procedure', js: true do let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } - let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: administrateur) } + let(:procedure) { create(:procedure, :published, :with_type_de_champ, administrateur: administrateur) } before do - create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') + create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') create :assign_to, procedure: procedure, gestionnaire: gestionnaire login_as gestionnaire, scope: :gestionnaire diff --git a/spec/features/backoffice/navigate_to_dossier_spec.rb b/spec/features/backoffice/navigate_to_dossier_spec.rb index 8dcd90533..f2e80f9f2 100644 --- a/spec/features/backoffice/navigate_to_dossier_spec.rb +++ b/spec/features/backoffice/navigate_to_dossier_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' feature 'on backoffice page', js: true do let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } - let(:procedure) { create(:procedure, administrateur: administrateur) } - let(:procedure_individual) { create :procedure, libelle: 'procedure individual', administrateur: administrateur, for_individual: true } + let(:procedure) { create(:procedure, :published, administrateur: administrateur) } + let(:procedure_individual) { create :procedure, :published, libelle: 'procedure individual', administrateur: administrateur, for_individual: true } - let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') } - let!(:dossier_individual) { create :dossier, procedure: procedure_individual, state: 'updated' } + let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } + let!(:dossier_individual) { create :dossier, procedure: procedure_individual, state: 'initiated' } before do create :assign_to, gestionnaire: gestionnaire, procedure: procedure @@ -34,7 +34,7 @@ feature 'on backoffice page', js: true do end context "and goes to the page of a dossier he hasn't access to" do - let!(:unauthorized_dossier) { create(:dossier, :with_entreprise, state: 'updated') } + let!(:unauthorized_dossier) { create(:dossier, :with_entreprise, state: 'initiated') } before do visit backoffice_dossier_path(unauthorized_dossier) diff --git a/spec/features/backoffice/search_file_spec.rb b/spec/features/backoffice/search_file_spec.rb index 42ecf5655..d284671cd 100644 --- a/spec/features/backoffice/search_file_spec.rb +++ b/spec/features/backoffice/search_file_spec.rb @@ -11,7 +11,7 @@ feature 'search file on gestionnaire backoffice' do context 'when gestionnaire is logged in' do context 'when he click on search button' do let(:terms) { '' } - let!(:procedure) { create(:procedure, administrateur: administrateur) } + let!(:procedure) { create(:procedure, :published, administrateur: administrateur) } before do create :assign_to, gestionnaire: gestionnaire, procedure: procedure diff --git a/spec/features/users/dossier_edition_spec.rb b/spec/features/users/dossier_edition_spec.rb index 238a7c3d9..698cb0dd8 100644 --- a/spec/features/users/dossier_edition_spec.rb +++ b/spec/features/users/dossier_edition_spec.rb @@ -39,10 +39,11 @@ feature 'As a User I want to edit a dossier I own' do page.find_by_id('maj_infos').trigger('click') expect(page).to have_current_path(users_dossier_description_path(dossier.id.to_s), only_path: true) - fill_in "champs_#{dossier.champs.order(:id).first.id.to_s}", with: 'Contenu du champ 1' + champ_id = dossier.champs.find { |t| t.type_champ == "text" }.id + fill_in "champs_#{champ_id.to_s}", with: 'Contenu du champ 1' page.find_by_id('modification_terminee').click expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier.id.to_s), only_path: true) - expect(page.find("#champ-#{dossier.champs.order(:id).first.id}-value").text).to eq('Contenu du champ 1') + expect(page.find("#champ-#{champ_id}-value").text).to eq('Contenu du champ 1') end end end diff --git a/spec/features/users/flux_de_commentaires_spec.rb b/spec/features/users/flux_de_commentaires_spec.rb index 4ce44cd3e..734db0d78 100644 --- a/spec/features/users/flux_de_commentaires_spec.rb +++ b/spec/features/users/flux_de_commentaires_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' feature 'users: flux de commentaires' do let(:user) { create(:user) } - let(:dossier) { create(:dossier, :with_entreprise, user: user, state: "replied") } + let(:dossier) { create(:dossier, :with_entreprise, user: user, state: "initiated") } let(:dossier_id) { dossier.id } let(:champ1) { dossier.champs.first } diff --git a/spec/features/users/list_dossiers_spec.rb b/spec/features/users/list_dossiers_spec.rb index 6acdbfb50..64a55d0b2 100644 --- a/spec/features/users/list_dossiers_spec.rb +++ b/spec/features/users/list_dossiers_spec.rb @@ -2,10 +2,10 @@ require 'spec_helper' describe 'user access to the list of his dossier' do let(:user) { create(:user) } - let!(:last_updated_dossier) { create(:dossier, :with_entreprise, user: user, state: 'replied')} - let!(:dossier1) { create(:dossier, :with_entreprise, user: user, state: 'replied') } + let!(:last_updated_dossier) { create(:dossier, :with_entreprise, user: user, state: 'initiated')} + let!(:dossier1) { create(:dossier, :with_entreprise, user: user, state: 'initiated') } let!(:dossier2) { create(:dossier, :with_entreprise) } - let!(:dossier_archived) { create(:dossier, :with_entreprise, user: user, state: 'replied') } + let!(:dossier_archived) { create(:dossier, :with_entreprise, user: user, state: 'initiated') } before do last_updated_dossier.update_column(:updated_at, "19/07/2052 15:35".to_time) diff --git a/spec/features/users/onglets_link_spec.rb b/spec/features/users/onglets_link_spec.rb index a3fef2dc3..5c9232c86 100644 --- a/spec/features/users/onglets_link_spec.rb +++ b/spec/features/users/onglets_link_spec.rb @@ -7,8 +7,6 @@ feature 'on click on tabs button' do before do create(:dossier, :with_entreprise, user: user, state: 'initiated') - create(:dossier, :with_entreprise, user: user, state: 'replied') - create(:dossier, :with_entreprise, user: user, state: 'updated') create(:dossier, :with_entreprise, user: user, state: 'received') create(:dossier, :with_entreprise, user: user, state: 'closed') create(:dossier, :with_entreprise, user: user, state: 'refused') @@ -23,7 +21,7 @@ feature 'on click on tabs button' do context 'when he click on tabs en construction' do before do visit users_dossiers_url(liste: :a_traiter) - page.click_on 'En construction 3' + page.click_on 'En construction 1' end scenario 'it redirect to users dossier termine' do diff --git a/spec/models/champ_spec.rb b/spec/models/champ_spec.rb index 868001afc..4769a6e7f 100644 --- a/spec/models/champ_spec.rb +++ b/spec/models/champ_spec.rb @@ -4,4 +4,63 @@ describe Champ do require 'models/champ_shared_example.rb' it_should_behave_like "champ_spec" + + describe '#serialize_datetime_if_needed' do + let(:type_de_champ) { TypeDeChamp.new(type_champ: 'datetime') } + let(:champ) { Champ.new(type_de_champ: type_de_champ, value: value) } + + before { champ.save } + + # when using the old form, and the ChampsService Class + # TODO: to remove + context 'when the value is already serialized' do + let(:value) { '12/01/2017 10:23' } + + it { expect(champ.value).to eq(value) } + end + + context 'when the value is not already serialized' do + let(:value) { '{ 1=>2017, 2=>01, 3=>12, 4=>10, 5=>23 }' } + + it { expect(champ.value).to eq('12/01/2017 10:23') } + end + end + + describe '#multiple_select_to_string' do + let(:type_de_champ) { TypeDeChamp.new(type_champ: 'multiple_drop_down_list') } + let(:champ) { Champ.new(type_de_champ: type_de_champ, value: value) } + + before { champ.save } + + # when using the old form, and the ChampsService Class + # TODO: to remove + context 'when the value is already deserialized' do + let(:value) { '["1", "2"]' } + + it { expect(champ.value).to eq(value) } + + context 'when the value is nil' do + let(:value) { nil } + + it { expect(champ.value).to eq(value) } + end + end + + # for explanation for the "" entry, see + # https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select + # GOTCHA + context 'when the value is not already deserialized' do + context 'when a choice is selected' do + let(:value) { '["", "1", "2"]' } + + it { expect(champ.value).to eq('["1", "2"]') } + end + + context 'when all choices are removed' do + let(:value) { '[""]' } + + it { expect(champ.value).to eq(nil) } + end + end + end end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index acba3c1ad..60418ad84 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -182,83 +182,13 @@ describe Dossier do context 'when is post a comment' do let(:action) { 'comment' } - it { is_expected.to eq('replied') } + it { is_expected.to eq('initiated') } end context 'when is follow' do let(:action) { 'follow' } - it { is_expected.to eq 'updated' } - end - end - end - - context 'when dossier is at state replied' do - before do - dossier.replied! - end - - context 'when user is connect' do - let(:role) { 'user' } - - context 'when is post a comment' do - let(:action) { 'comment' } - - it { is_expected.to eq('updated') } - end - - context 'when is updated dossier informations' do - let(:action) { 'update' } - - it { is_expected.to eq('updated') } - end - end - - context 'when gestionnaire is connect' do - let(:role) { 'gestionnaire' } - - context 'when is post a comment' do - let(:action) { 'comment' } - - it { is_expected.to eq('replied') } - end - - context 'when is follow' do - let(:action) { 'follow' } - - it { is_expected.to eq 'replied' } - end - end - end - - context 'when dossier is at state updated' do - before do - dossier.updated! - end - - context 'when user is connect' do - let(:role) { 'user' } - - context 'when is post a comment' do - let(:action) { 'comment' } - - it { is_expected.to eq('updated') } - end - - context 'when is updated dossier informations' do - let(:action) { 'update' } - - it { is_expected.to eq('updated') } - end - end - - context 'when gestionnaire is connect' do - let(:role) { 'gestionnaire' } - - context 'when is post a comment' do - let(:action) { 'comment' } - - it { is_expected.to eq('replied') } + it { is_expected.to eq 'initiated' } end end end @@ -695,6 +625,56 @@ describe Dossier do end end + describe '#avis_for' do + let!(:procedure) { create(:procedure, :published) } + let!(:dossier) { create(:dossier, procedure: procedure, state: :initiated) } + + let!(:gestionnaire) { create(:gestionnaire, procedures: [procedure]) } + let!(:expert_1) { create(:gestionnaire) } + let!(:expert_2) { create(:gestionnaire) } + + context 'when there is a public advice asked from the dossiers gestionnaire' do + let!(:avis) { Avis.create(dossier: dossier, claimant: gestionnaire, gestionnaire: expert_1, confidentiel: false) } + + it { expect(dossier.avis_for(gestionnaire)).to match([avis]) } + it { expect(dossier.avis_for(expert_1)).to match([avis]) } + it { expect(dossier.avis_for(expert_2)).to match([avis]) } + end + + context 'when there is a private advice asked from the dossiers gestionnaire' do + let!(:avis) { Avis.create(dossier: dossier, claimant: gestionnaire, gestionnaire: expert_1, confidentiel: true) } + + it { expect(dossier.avis_for(gestionnaire)).to match([avis]) } + it { expect(dossier.avis_for(expert_1)).to match([avis]) } + it { expect(dossier.avis_for(expert_2)).to match([]) } + end + + context 'when there is a public advice asked from one expert to another' do + let!(:avis) { Avis.create(dossier: dossier, claimant: expert_1, gestionnaire: expert_2, confidentiel: false) } + + it { expect(dossier.avis_for(gestionnaire)).to match([avis]) } + it { expect(dossier.avis_for(expert_1)).to match([avis]) } + it { expect(dossier.avis_for(expert_2)).to match([avis]) } + end + + context 'when there is a private advice asked from one expert to another' do + let!(:avis) { Avis.create(dossier: dossier, claimant: expert_1, gestionnaire: expert_2, confidentiel: true) } + + it { expect(dossier.avis_for(gestionnaire)).to match([avis]) } + it { expect(dossier.avis_for(expert_1)).to match([avis]) } + it { expect(dossier.avis_for(expert_2)).to match([avis]) } + end + + context 'when they are a lot of advice' do + let!(:avis_1) { Avis.create(dossier: dossier, claimant: expert_1, gestionnaire: expert_2, confidentiel: false, created_at: DateTime.parse('10/01/2010')) } + let!(:avis_2) { Avis.create(dossier: dossier, claimant: expert_1, gestionnaire: expert_2, confidentiel: false, created_at: DateTime.parse('9/01/2010')) } + let!(:avis_3) { Avis.create(dossier: dossier, claimant: expert_1, gestionnaire: expert_2, confidentiel: false, created_at: DateTime.parse('11/01/2010')) } + + it { expect(dossier.avis_for(gestionnaire)).to match([avis_2, avis_1, avis_3]) } + it { expect(dossier.avis_for(expert_1)).to match([avis_2, avis_1, avis_3]) } + end + end + describe '#update_state_dates' do let(:state) { 'draft' } let(:dossier) { create(:dossier, state: state) } diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index 18b8461d9..25d490fd1 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe Gestionnaire, type: :model do let(:admin) { create :administrateur } - let!(:procedure) { create :procedure, administrateur: admin } - let!(:procedure_2) { create :procedure, administrateur: admin } - let!(:procedure_3) { create :procedure, administrateur: admin } + let!(:procedure) { create :procedure, :published, administrateur: admin } + let!(:procedure_2) { create :procedure, :published, administrateur: admin } + let!(:procedure_3) { create :procedure, :published, administrateur: admin } let(:gestionnaire) { create :gestionnaire, procedure_filter: procedure_filter, administrateurs: [admin] } let(:procedure_filter) { nil } let!(:procedure_assign) { create :assign_to, gestionnaire: gestionnaire, procedure: procedure } @@ -13,70 +13,29 @@ describe Gestionnaire, type: :model do create :assign_to, gestionnaire: gestionnaire, procedure: procedure_2 end - describe '#toggle_follow_dossier' do - let!(:dossier) { create :dossier, procedure: procedure } + describe 'follow' do + let(:dossier) { create :dossier } + let(:already_followed_dossier) { create :dossier } - subject { gestionnaire.toggle_follow_dossier dossier_id } + before { gestionnaire.followed_dossiers << already_followed_dossier } - context 'when dossier id not valid' do - let(:dossier_id) { 0 } + context 'when a gestionnaire follow a dossier for the first time' do + before { gestionnaire.follow(dossier) } - it { expect(subject).to eq nil } + it { expect(gestionnaire.follow?(dossier)).to be true } end - context 'when dossier id is valid' do - let(:dossier_id) { dossier.id } + context 'when a gestionnaire follows a dossier already followed' do + before { gestionnaire.follow(already_followed_dossier) } - context 'when dossier is not follow by gestionnaire' do - it 'value change in database' do - expect { subject }.to change(Follow, :count).by(1) - end - - it { expect(subject).to be_an_instance_of Follow } - end - - context 'when dossier is follow by gestionnaire' do - before do - create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id - end - - it 'value change in database' do - expect { subject }.to change(Follow, :count).by(-1) - end - - it { expect(subject).to eq 1 } - end - end - - context 'when dossier instance is past' do - let(:dossier_id) { dossier } - - context 'when dossier is not follow by gestionnaire' do - it 'value change in database' do - expect { subject }.to change(Follow, :count).by(1) - end - - it { expect(subject).to be_an_instance_of Follow } - end - - context 'when dossier is follow by gestionnaire' do - before do - create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id - end - - it 'value change in database' do - expect { subject }.to change(Follow, :count).by(-1) - end - - it { expect(subject).to eq 1 } - end + it { expect(gestionnaire.follow?(already_followed_dossier)).to be true } end end describe '#follow?' do let!(:dossier) { create :dossier, procedure: procedure } - subject { gestionnaire.follow? dossier.id } + subject { gestionnaire.follow?(dossier) } context 'when gestionnaire follow a dossier' do before do @@ -360,7 +319,7 @@ describe Gestionnaire, type: :model do expect_any_instance_of(Procedure).to receive(:procedure_overview).and_return(procedure_overview) end - it { expect(gestionnaire.last_week_overview[:procedure_overviews]).to match([procedure_overview]) } + it { expect(gestionnaire2.last_week_overview[:procedure_overviews]).to match([procedure_overview]) } end context 'when a procedure not published was active with no notifications' do @@ -397,4 +356,30 @@ describe Gestionnaire, type: :model do it { expect(subject).to be false } end end + + describe '#notifications_count_per_procedure' do + subject { gestionnaire.notifications_count_per_procedure } + + let(:dossier_with_unread_notification) do + create(:dossier, notifications: [Notification.create(type_notif: 'champs', already_read: false)]) + end + + let(:dossier_with_no_unread_notification) do + create(:dossier, notifications: [Notification.create(type_notif: 'champs', already_read: true)]) + end + + before { gestionnaire.followed_dossiers << followed_dossier } + + context 'when a followed dossier has unread notification' do + let(:followed_dossier) { dossier_with_unread_notification } + + it { is_expected.to eq({ dossier_with_unread_notification.procedure.id => 1 }) } + end + + context 'when a followed dossier has unread notification' do + let(:followed_dossier) { dossier_with_no_unread_notification } + + it { is_expected.to eq({ }) } + end + end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 249b21931..4a0244e23 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -309,7 +309,7 @@ describe Procedure do before do create :dossier, procedure: procedure, state: :initiated create :dossier, procedure: procedure, state: :draft - create :dossier, procedure: procedure, state: :replied + create :dossier, procedure: procedure, state: :initiated end subject { procedure.total_dossier } diff --git a/spec/models/search_spec.rb b/spec/models/search_spec.rb index ba760cdd6..dd46ea750 100644 --- a/spec/models/search_spec.rb +++ b/spec/models/search_spec.rb @@ -19,8 +19,8 @@ describe Search do create :assign_to, gestionnaire: gestionnaire_2, procedure: procedure_2 end - let(:procedure_1) { create(:procedure, administrateur: administrateur_1) } - let(:procedure_2) { create(:procedure, administrateur: administrateur_2) } + let(:procedure_1) { create(:procedure, :published, administrateur: administrateur_1) } + let(:procedure_2) { create(:procedure, :published, administrateur: administrateur_2) } let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) } let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) } diff --git a/spec/services/accompagnateur_service_spec.rb b/spec/services/accompagnateur_service_spec.rb index ae6fbb016..e8a550949 100644 --- a/spec/services/accompagnateur_service_spec.rb +++ b/spec/services/accompagnateur_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe AccompagnateurService do - let(:procedure) { create :procedure } + let(:procedure) { create :procedure, :published } let(:accompagnateur) { create :gestionnaire } let(:accompagnateur_service) { AccompagnateurService.new accompagnateur, procedure, to} diff --git a/spec/services/dossiers_list_gestionnaire_service_spec.rb b/spec/services/dossiers_list_gestionnaire_service_spec.rb index 3af0796b9..2e1e8d164 100644 --- a/spec/services/dossiers_list_gestionnaire_service_spec.rb +++ b/spec/services/dossiers_list_gestionnaire_service_spec.rb @@ -348,8 +348,6 @@ describe DossiersListGestionnaireService do let!(:dossier) { create(:dossier, procedure: procedure, state: 'draft') } let!(:dossier2) { create(:dossier, procedure: procedure, state: 'initiated') } #nouveaux let!(:dossier3) { create(:dossier, procedure: procedure, state: 'initiated') } #nouveaux - let!(:dossier4) { create(:dossier, procedure: procedure, state: 'replied') } #en_attente - let!(:dossier5) { create(:dossier, procedure: procedure, state: 'updated') } #a_traiter let!(:dossier6) { create(:dossier, procedure: procedure, state: 'received') } #a_instruire let!(:dossier7) { create(:dossier, procedure: procedure, state: 'received') } #a_instruire let!(:dossier8) { create(:dossier, procedure: procedure, state: 'closed') } #termine @@ -357,7 +355,6 @@ describe DossiersListGestionnaireService do let!(:dossier10) { create(:dossier, procedure: procedure, state: 'without_continuation') } #termine let!(:dossier11) { create(:dossier, procedure: procedure, state: 'closed') } #termine let!(:dossier12) { create(:dossier, procedure: procedure, state: 'initiated', archived: true) } #a_traiter #archived - let!(:dossier13) { create(:dossier, procedure: procedure, state: 'replied', archived: true) } #en_attente #archived let!(:dossier14) { create(:dossier, procedure: procedure, state: 'closed', archived: true) } #termine #archived describe '#termine' do diff --git a/spec/services/user_routes_authorization_service_spec.rb b/spec/services/user_routes_authorization_service_spec.rb index 4bbb7d825..03a5b805b 100644 --- a/spec/services/user_routes_authorization_service_spec.rb +++ b/spec/services/user_routes_authorization_service_spec.rb @@ -23,16 +23,6 @@ describe UserRoutesAuthorizationService do it { is_expected.to be_falsey } end - describe 'replied' do - let(:state) { 'replied' } - it { is_expected.to be_falsey } - end - - describe 'updated' do - let(:state) { 'updated' } - it { is_expected.to be_falsey } - end - describe 'closed' do let(:state) { 'closed' } it { is_expected.to be_falsey } @@ -53,16 +43,6 @@ describe UserRoutesAuthorizationService do it { is_expected.to be_falsey } end - describe 'replied' do - let(:state) { 'replied' } - it { is_expected.to be_falsey } - end - - describe 'updated' do - let(:state) { 'updated' } - it { is_expected.to be_falsey } - end - describe 'closed' do let(:state) { 'closed' } it { is_expected.to be_falsey } @@ -82,16 +62,6 @@ describe UserRoutesAuthorizationService do it { is_expected.to be_truthy } end - describe 'replied' do - let(:state) { 'replied' } - it { is_expected.to be_truthy } - end - - describe 'updated' do - let(:state) { 'updated' } - it { is_expected.to be_truthy } - end - describe 'closed' do let(:state) { 'closed' } it { is_expected.to be_falsey } @@ -112,16 +82,6 @@ describe UserRoutesAuthorizationService do it { is_expected.to be_truthy } end - describe 'replied' do - let(:state) { 'replied' } - it { is_expected.to be_truthy } - end - - describe 'updated' do - let(:state) { 'updated' } - it { is_expected.to be_truthy } - end - describe 'closed' do let(:state) { 'closed' } it { is_expected.to be_falsey } @@ -141,16 +101,6 @@ describe UserRoutesAuthorizationService do it { is_expected.to be_truthy } end - describe 'replied' do - let(:state) { 'replied' } - it { is_expected.to be_truthy } - end - - describe 'updated' do - let(:state) { 'updated' } - it { is_expected.to be_truthy } - end - describe 'closed' do let(:state) { 'closed' } it { is_expected.to be_truthy } diff --git a/spec/views/backoffice/dossiers/index_html.haml_spec.rb b/spec/views/backoffice/dossiers/index_html.haml_spec.rb index 307fa0113..bfade3965 100644 --- a/spec/views/backoffice/dossiers/index_html.haml_spec.rb +++ b/spec/views/backoffice/dossiers/index_html.haml_spec.rb @@ -4,11 +4,9 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } - let!(:procedure) { create(:procedure, administrateur: administrateur) } + let!(:procedure) { create(:procedure, :published, administrateur: administrateur) } let!(:decorate_dossier_initiated) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated').decorate } - let!(:decorate_dossier_replied) { create(:dossier, :with_entreprise, procedure: procedure, state: 'replied').decorate } - let!(:decorate_dossier_updated) { create(:dossier, :with_entreprise, procedure: procedure, state: 'updated').decorate } let!(:decorate_dossier_received) { create(:dossier, :with_entreprise, procedure: procedure, state: 'received').decorate } let!(:decorate_dossier_closed) { create(:dossier, :with_entreprise, procedure: procedure, state: 'closed').decorate } let!(:decorate_dossier_refused) { create(:dossier, :with_entreprise, procedure: procedure, state: 'refused').decorate } @@ -21,8 +19,6 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do let(:all_state_dossiers_list) { dossiers_list_facade.service.all_state } before do - decorate_dossier_replied.entreprise.update_column(:raison_sociale, 'plap') - decorate_dossier_updated.entreprise.update_column(:raison_sociale, 'plep') decorate_dossier_received.entreprise.update_column(:raison_sociale, 'plup') decorate_dossier_closed.entreprise.update_column(:raison_sociale, 'plyp') decorate_dossier_refused.entreprise.update_column(:raison_sociale, 'plzp') @@ -79,15 +75,13 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do it { is_expected.to have_content('Nouveaux dossiers 1 dossier') } it { is_expected.to have_content('Dossiers suivis 0 dossiers') } - it { is_expected.to have_content('Tous les dossiers 7 dossiers') } + it { is_expected.to have_content('Tous les dossiers 5 dossiers') } it { is_expected.to have_content('État') } it { is_expected.to have_content('Libellé procédure') } it { is_expected.to have_content('Raison sociale') } it { is_expected.to have_content('Mise à jour le') } - it { is_expected.to have_content('plap') } - it { is_expected.to have_content('plep') } it { is_expected.to have_content('plup') } it { is_expected.to have_content('plyp') } end diff --git a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb index 4682bb331..17009aa9c 100644 --- a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb +++ b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb @@ -40,30 +40,6 @@ describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.htm include_examples 'button Passer en instruction is present' end - context 'when dossier have state replied' do - let(:state) { 'replied' } - - before do - render - end - - it { expect(rendered).to have_content('En construction') } - - include_examples 'button Passer en instruction is present' - end - - context 'when dossier have state update' do - let(:state) { 'updated' } - - before do - render - end - - it { expect(rendered).to have_content('En construction') } - - include_examples 'button Passer en instruction is present' - end - context 'when dossier have state received' do let(:state) { 'received' } diff --git a/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb b/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb index f580715a9..831f61295 100644 --- a/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb +++ b/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb @@ -20,26 +20,6 @@ describe 'layouts/left_panels/_left_panel_users_recapitulatifcontroller_show.htm it { expect(rendered).to have_content('En construction') } end - context 'when dossier state is replied' do - let(:state) { 'replied' } - - before do - render - end - - it { expect(rendered).to have_content('En construction') } - end - - context 'when dossier state is updated' do - let(:state) { 'updated' } - - before do - render - end - - it { expect(rendered).to have_content('En construction') } - end - context 'when dossier state is closed' do let(:state) { 'closed' } diff --git a/spec/views/new_gestionnaire/dossiers/_champs.html.haml_spec.rb b/spec/views/new_gestionnaire/dossiers/_champs.html.haml_spec.rb new file mode 100644 index 000000000..3a7ea2c2e --- /dev/null +++ b/spec/views/new_gestionnaire/dossiers/_champs.html.haml_spec.rb @@ -0,0 +1,15 @@ +describe 'new_gestionnaire/dossiers/champs.html.haml', type: :view do + before { render 'new_gestionnaire/dossiers/champs.html.haml', champs: champs } + + context "there is some champs" do + let(:champ1) { create(:champ, :checkbox, value: "true") } + let(:champ2) { create(:champ, :header_section, value: "Section") } + let(:champs) { [champ1, champ2] } + + it { expect(rendered).to include(champ1.libelle) } + it { expect(rendered).to include(champ1.value) } + + it { expect(rendered).to have_css(".header-section") } + it { expect(rendered).to include(champ2.libelle) } + end +end diff --git a/spec/views/new_gestionnaire/dossiers/show.html.haml_spec.rb b/spec/views/new_gestionnaire/dossiers/show.html.haml_spec.rb new file mode 100644 index 000000000..33a02b377 --- /dev/null +++ b/spec/views/new_gestionnaire/dossiers/show.html.haml_spec.rb @@ -0,0 +1,35 @@ +describe 'new_gestionnaire/dossiers/show.html.haml', type: :view do + let(:individual) { nil } + let(:entreprise) { nil } + let(:dossier) { create(:dossier, :initiated, entreprise: entreprise, individual: individual) } + + before do + assign(:dossier, dossier) + render + end + + context "when dossier was created by an entreprise" do + let(:entreprise) { create(:entreprise) } + + it { expect(rendered).to include(entreprise.decorate.raison_sociale_or_name) } + it { expect(rendered).to include(entreprise.decorate.siret_siege_social) } + it { expect(rendered).to include(entreprise.decorate.forme_juridique) } + + context "and entreprise is an association" do + let(:entreprise) { create(:entreprise, :is_association) } + + it { expect(rendered).to include(entreprise.rna_information.association_id) } + it { expect(rendered).to include(entreprise.rna_information.titre) } + it { expect(rendered).to include(entreprise.rna_information.objet) } + end + end + + context "when dossier was created by an individual" do + let(:individual) { create(:individual) } + + it { expect(rendered).to include(individual.gender) } + it { expect(rendered).to include(individual.nom) } + it { expect(rendered).to include(individual.prenom) } + it { expect(rendered).to include(individual.birthdate) } + end +end diff --git a/spec/views/users/description/champs/_date.html.haml_spec.rb b/spec/views/users/description/champs/_date.html.haml_spec.rb new file mode 100644 index 000000000..5e0e797e7 --- /dev/null +++ b/spec/views/users/description/champs/_date.html.haml_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'users/description/champs/date.html.haml', type: :view do + let(:type_champ) { create(:type_de_champ_public, type_champ: :date) } + + before do + render 'users/description/champs/date.html.haml', champ: champ + end + + let!(:champ) { create(:champ, type_de_champ: type_champ, value: "2017-09-19").decorate } + + it 'should render an input for the dossier link' do + expect(rendered).to have_css("input[value='2017-09-19']") + end +end diff --git a/spec/views/users/description/champs/_yes_no.html.haml_spec.rb b/spec/views/users/description/champs/_yes_no.html.haml_spec.rb new file mode 100644 index 000000000..9d2af680c --- /dev/null +++ b/spec/views/users/description/champs/_yes_no.html.haml_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'users/description/champs/yes_no.html.haml', type: :view do + let(:type_champ) { create(:type_de_champ_public, type_champ: :yes_no) } + + before do + render 'users/description/champs/yes_no.html.haml', champ: champ + end + + context "when the value is Oui" do + let!(:champ) { create(:champ, type_de_champ: type_champ, value: "true").decorate } + + it 'should select the Oui radio button' do + expect(rendered).to have_selector("input[value='true'][checked]") + end + end + + context "when the value is Non" do + let!(:champ) { create(:champ, type_de_champ: type_champ, value: "false").decorate } + + it 'should select the Non radio button' do + expect(rendered).to have_selector("input[value='false'][checked]") + end + end +end diff --git a/spec/views/users/dossiers/index_html.haml_spec.rb b/spec/views/users/dossiers/index_html.haml_spec.rb index dc6721b41..a8c6e9e11 100644 --- a/spec/views/users/dossiers/index_html.haml_spec.rb +++ b/spec/views/users/dossiers/index_html.haml_spec.rb @@ -4,8 +4,6 @@ describe 'users/dossiers/index.html.haml', type: :view do let(:user) { create(:user) } let!(:decorate_dossier_initiated) { create(:dossier, :with_entreprise, user: user, state: 'initiated').decorate } - let!(:decorate_dossier_replied) { create(:dossier, :with_entreprise, user: user, state: 'replied').decorate } - let!(:decorate_dossier_updated) { create(:dossier, :with_entreprise, user: user, state: 'updated').decorate } let!(:decorate_dossier_received) { create(:dossier, :with_entreprise, user: user, state: 'received').decorate } let!(:decorate_dossier_closed) { create(:dossier, :with_entreprise, user: user, state: 'closed').decorate } let!(:decorate_dossier_refused) { create(:dossier, :with_entreprise, user: user, state: 'refused').decorate } @@ -41,7 +39,7 @@ describe 'users/dossiers/index.html.haml', type: :view do end describe 'on tab en construction' do - let(:total_dossiers) { 3 } + let(:total_dossiers) { 1 } let(:active_class) { '.active .text-danger' } let(:dossiers_to_display) { user.dossiers.state_en_construction } let(:liste) { 'a_traiter' } @@ -49,14 +47,6 @@ describe 'users/dossiers/index.html.haml', type: :view do it_behaves_like 'check_tab_content' do let(:decorate_dossier_at_check) { decorate_dossier_initiated } end - - it_behaves_like 'check_tab_content' do - let(:decorate_dossier_at_check) { decorate_dossier_replied } - end - - it_behaves_like 'check_tab_content' do - let(:decorate_dossier_at_check) { decorate_dossier_updated } - end end describe 'on tab etude en examen' do diff --git a/spec/workers/auto_archive_procedure_worker_spec.rb b/spec/workers/auto_archive_procedure_worker_spec.rb index 9b4c57d81..af3fe73fe 100644 --- a/spec/workers/auto_archive_procedure_worker_spec.rb +++ b/spec/workers/auto_archive_procedure_worker_spec.rb @@ -1,6 +1,9 @@ require 'rails_helper' RSpec.describe AutoArchiveProcedureWorker, type: :worker do + before { Delayed::Worker.delay_jobs = false } + after { Delayed::Worker.delay_jobs = true } + let!(:procedure) { create(:procedure, published_at: Time.now, archived_at: nil, auto_archive_on: nil )} let!(:procedure_hier) { create(:procedure, published_at: Time.now, archived_at: nil, auto_archive_on: 1.day.ago )} let!(:procedure_aujourdhui) { create(:procedure, published_at: Time.now, archived_at: nil, auto_archive_on: Date.today )} @@ -20,8 +23,8 @@ RSpec.describe AutoArchiveProcedureWorker, type: :worker do context "when procedures have auto_archive_on set on yesterday or today" do let!(:dossier1) { create(:dossier, procedure: procedure_hier, state: 'draft', archived: false)} let!(:dossier2) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} - let!(:dossier3) { create(:dossier, procedure: procedure_hier, state: 'replied', archived: false)} - let!(:dossier4) { create(:dossier, procedure: procedure_hier, state: 'updated', archived: false)} + let!(:dossier3) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} + let!(:dossier4) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} let!(:dossier5) { create(:dossier, procedure: procedure_hier, state: 'received', archived: false)} let!(:dossier6) { create(:dossier, procedure: procedure_hier, state: 'closed', archived: false)} let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: 'refused', archived: false)} diff --git a/spec/workers/weekly_overview_worker_spec.rb b/spec/workers/weekly_overview_worker_spec.rb index e2bff3e80..9568e1509 100644 --- a/spec/workers/weekly_overview_worker_spec.rb +++ b/spec/workers/weekly_overview_worker_spec.rb @@ -1,6 +1,9 @@ require 'rails_helper' RSpec.describe WeeklyOverviewWorker, type: :worker do + before { Delayed::Worker.delay_jobs = false } + after { Delayed::Worker.delay_jobs = true } + describe 'perform' do let!(:gestionnaire) { create(:gestionnaire) } let(:overview) { double('overview') } diff --git a/vendor/assets/javascripts/leaflet.1.1.0.js b/vendor/assets/javascripts/leaflet.1.1.0.js new file mode 100644 index 000000000..4c4b8a63d --- /dev/null +++ b/vendor/assets/javascripts/leaflet.1.1.0.js @@ -0,0 +1,6 @@ +!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L=t.L||{})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e=0}function I(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function A(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(ue,n,!1):"touchmove"===i?t.removeEventListener(le,n,!1):"touchend"===i&&(t.removeEventListener(ce,n,!1),t.removeEventListener(_e,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(de.indexOf(t.target.tagName)<0))return;$(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(ue,o,!1),me||(document.documentElement.addEventListener(ue,R,!0),document.documentElement.addEventListener(le,D,!0),document.documentElement.addEventListener(ce,N,!0),document.documentElement.addEventListener(_e,N,!0),me=!0)}function R(t){pe[t.pointerId]=t,fe++}function D(t){pe[t.pointerId]&&(pe[t.pointerId]=t)}function N(t){delete pe[t.pointerId],fe--}function j(t,i){t.touches=[];for(var e in pe)t.touches.push(pe[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(le,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ce,n,!1),t.addEventListener(_e,n,!1)}function F(t,i,e){function n(t){var i;if(te){if(!Ai||"mouse"===t.pointerType)return;i=fe}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(te){if(!Ai||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[ye+ge+e]=n,t[ye+ve+e]=o,t[ye+"dblclick"+e]=i,t.addEventListener(ge,n,!1),t.addEventListener(ve,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[ye+ge+i],n=t[ye+ve+i],o=t[ye+"dblclick"+i];return t.removeEventListener(ge,e,!1),t.removeEventListener(ve,n,!1),Ai||t.removeEventListener("dblclick",o,!1),this}function V(t,i,e,n){if("object"==typeof i)for(var o in i)q(t,o,i[o],e);else{i=u(i);for(var s=0,r=i.length;s100&&n<500||t.target._simulatedClick&&!t._simulated?void Q(t):(zi=e,void i(t))}function rt(t){return"string"==typeof t?document.getElementById(t):t}function at(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function ht(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ut(t){var i=t.parentNode;i&&i.removeChild(t)}function lt(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ct(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function _t(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function dt(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=gt(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function pt(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;nh&&(s=r,h=a);h>e&&(i[s]=1,Bt(t,i,e,n,s),Bt(t,i,e,s,o))}function It(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;ni&&(e.push(t[n]),o=n);return oi.max.x&&(e|=2),t.yi.max.y&&(e|=8),e}function Dt(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Nt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&(o=((t.x-s)*a+(t.y-r)*h)/u,o>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function jt(t){return!mi(t[0])||"object"!=typeof t[0][0]&&"undefined"!=typeof t[0][0]}function Wt(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.xi.y&&n.y=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lati.lng&&n.lng1,se=function(){return!!document.createElement("canvas").getContext}(),re=!(!document.createElementNS||!S("svg").createSVGRect),ae=!re&&function(){try{var t=document.createElement("div");t.innerHTML='';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),he=(Object.freeze||Object)({ie:Bi,ielt9:Ii,edge:Ai,webkit:Oi,android:Ri,android23:Di,opera:Ni,chrome:ji,gecko:Wi,safari:Hi,phantom:Fi,opera12:Ui,win:Vi,ie3d:Gi,webkit3d:qi,gecko3d:Ki,any3d:Yi,mobile:Xi,mobileWebkit:Ji,mobileWebkit3d:$i,msPointer:Qi,pointer:te,touch:ie,mobileOpera:ee,mobileGecko:ne,retina:oe,canvas:se,svg:re,vml:ae}),ue=Qi?"MSPointerDown":"pointerdown",le=Qi?"MSPointerMove":"pointermove",ce=Qi?"MSPointerUp":"pointerup",_e=Qi?"MSPointerCancel":"pointercancel",de=["INPUT","SELECT","OPTION"],pe={},me=!1,fe=0,ge=Qi?"MSPointerDown":te?"pointerdown":"touchstart",ve=Qi?"MSPointerUp":te?"pointerup":"touchend",ye="_leaflet_",xe="_leaflet_events",we=Vi&&ji?2*window.devicePixelRatio:Wi?window.devicePixelRatio:1,Le={},Pe=(Object.freeze||Object)({on:V,off:G,stopPropagation:Y,disableScrollPropagation:X,disableClickPropagation:J,preventDefault:$,stop:Q,getMousePosition:tt,getWheelDelta:it,fakeStop:et,skipped:nt,isExternalTarget:ot,addListener:V,removeListener:G}),be=xt(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),Te=xt(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),ze="webkitTransition"===Te||"OTransition"===Te?Te+"End":"transitionend";if("onselectstart"in document)Mi=function(){V(window,"selectstart",$)},Ci=function(){G(window,"selectstart",$)};else{var Me=xt(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);Mi=function(){if(Me){var t=document.documentElement.style;Zi=t[Me],t[Me]="none"}},Ci=function(){Me&&(document.documentElement.style[Me]=Zi,Zi=void 0)}}var Ce,Ze,Ee=(Object.freeze||Object)({TRANSFORM:be,TRANSITION:Te,TRANSITION_END:ze,get:rt,getStyle:at,create:ht,remove:ut,empty:lt,toFront:ct,toBack:_t,hasClass:dt,addClass:pt,removeClass:mt,setClass:ft,getClass:gt,setOpacity:vt,testProp:xt,setTransform:wt,setPosition:Lt,getPosition:Pt,disableTextSelection:Mi,enableTextSelection:Ci,disableImageDrag:bt,enableImageDrag:Tt,preventOutline:zt,restoreOutline:Mt}),Se=Li.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pt(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;ithis.options.maxZoom?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},t===!0?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=t.coords.latitude,e=t.coords.longitude,n=new M(i,e),o=n.toBounds(t.coords.accuracy),s=this._locateOptions;if(s.setView){var r=this.getBoundsZoom(o);this.setView(n,s.maxZoom?Math.min(r,s.maxZoom):r)}var a={latlng:n,bounds:o,timestamp:t.timestamp};for(var h in t.coords)"number"==typeof t.coords[h]&&(a[h]=t.coords[h]);this.fire("locationfound",a)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}ut(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)ut(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e="leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),n=ht("div",e,i||this._mapPane);return t&&(this._panes[t]=n),n},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds(),i=this.unproject(t.getBottomLeft()),e=this.unproject(t.getTopRight());return new T(i,e)},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=Yi?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){var i=this.project(C(t))._round();return i._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return tt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=rt(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");V(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Yi,pt(t,"leaflet-container"+(ie?" leaflet-touch":"")+(oe?" leaflet-retina":"")+(Ii?" leaflet-oldie":"")+(Hi?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=at(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Lt(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(pt(t.markerPane,"leaflet-zoom-hide"),pt(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){Lt(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t){return t&&this.fire("zoomstart"),this.fire("movestart")},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Lt(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?G:V;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Yi&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if(e=this._targets[n(r)],e&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!ot(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!ot(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!nt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||zt(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e)),n.length)){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&$(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.options&&"icon"in s.options;r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=Yi?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){mt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._floor();return!((i&&i.animate)!==!0&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=ht("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=be,e=this._proxy.style[i];wt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();wt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){ut(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||e.animate===!1||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(e.animate!==!0&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pt(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250)},_onZoomTransitionEnd:function(){this._animatingZoom&&(mt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Be=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pt(i,"leaflet-control"),e.indexOf("bottom")!==-1?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(ut(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),Ie=function(t){return new Be(t)};ke.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=ht("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=ht("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)ut(this._controlCorners[t]);ut(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Ae=Be.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?(i=document.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),V(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");e.appendChild(r),r.appendChild(i),r.appendChild(s);var a=t.overlay?this._overlaysList:this._baseLayersList;return a.appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e,n=this._layerControlInputs,o=[],s=[];this._handlingClick=!0;for(var r=n.length-1;r>=0;r--)t=n[r],i=this._getLayer(t.layerId).layer,e=this._map.hasLayer(i),t.checked&&!e?o.push(i):!t.checked&&e&&s.push(i);for(r=0;r=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&ni.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),Oe=function(t,i,e){return new Ae(t,i,e)},Re=Be.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"−",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ht("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoomthis._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ht("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),J(s),V(s,"click",Q),V(s,"click",o,this),V(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mt(this._zoomInButton,i),mt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&pt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&pt(this._zoomInButton,i)}});ke.mergeOptions({zoomControl:!0}),ke.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Re,this.addControl(this.zoomControl))});var De=function(t){return new Re(t)},Ne=Be.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i="leaflet-control-scale",e=ht("div",i),n=this.options;return this._addScales(n,i+"-line",e),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),e},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ht("div",i,e)),t.imperial&&(this._iScale=ht("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),je=function(t){return new Ne(t)},We=Be.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=ht("div","leaflet-control-attribution"),J(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});ke.mergeOptions({attributionControl:!0}),ke.addInitHook(function(){this.options.attributionControl&&(new We).addTo(this)});var He=function(t){return new We(t)};Be.Layers=Ae,Be.Zoom=Re,Be.Scale=Ne,Be.Attribution=We,Ie.layers=Oe,Ie.zoom=De,Ie.scale=je,Ie.attribution=He;var Fe,Ue=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}}),Ve={Events:wi},Ge=!1,qe=ie?"touchstart mousedown":"mousedown",Ke={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Ye={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Xe=Li.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(V(this._dragStartTarget,qe,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(L.Draggable._dragging===this&&this.finishDrag(),G(this._dragStartTarget,qe,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!dt(this._element,"leaflet-zoom-anim")&&!(Ge||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Ge=this,this._preventOutline&&zt(this._element),bt(),Mi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new x(i.clientX,i.clientY),V(document,Ye[t.type],this._onMove,this),V(document,Ke[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled){if(t.touches&&t.touches.length>1)return void(this._moved=!0);var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY),n=e.subtract(this._startPoint);(n.x||n.y)&&(Math.abs(n.x)+Math.abs(n.y)1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),u=Math.PI/2-2*Math.atan(r*i)-a,a+=u;return new M(a*e,t.x*e/n)}},en=(Object.freeze||Object)({LonLat:Qe,Mercator:tn,SphericalMercator:Ti}),nn=i({},bi,{code:"EPSG:3395",projection:tn,transformation:function(){var t=.5/(Math.PI*tn.R);return E(t,.5,-t,.5)}()}),on=i({},bi,{code:"EPSG:4326",projection:Qe,transformation:E(1/180,1,-1/180,.5)}),sn=i({},Pi,{projection:Qe,transformation:E(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});Pi.Earth=bi,Pi.EPSG3395=nn,Pi.EPSG3857=Ei,Pi.EPSG900913=Si,Pi.EPSG4326=on,Pi.Simple=sn;var rn=Li.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){ +return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});ke.include({addLayer:function(t){var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){t=t?mi(t)?t:[t]:[];for(var i=0,e=t.length;ithis._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return jt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=jt(t),n=0,o=t.length;n=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){vn.prototype._setLatLngs.call(this,t),jt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return jt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t)){if(this.options.noClip)return void(this._parts=this._rings);for(var n,o=0,s=this._rings.length;ot.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||vn.prototype._containsPoint.call(this,t,!0)}}),xn=un.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=mi(t)?t:t.features;if(o){for(i=0,e=o.length;io?(i.height=o+"px",pt(t,s)):mt(t,s),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Lt(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(at(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(Pt(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Q(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}}),Cn=function(t,i){return new Mn(t,i)};ke.mergeOptions({closePopupOnClick:!0}),ke.include({openPopup:function(t,i,e){return t instanceof Mn||(t=new Mn(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),rn.include({bindPopup:function(t,i){return t instanceof Mn?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new Mn(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof rn||(i=t,t=this),t instanceof un)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;if(this._popup&&this._map)return Q(t),i instanceof mn?void this.openPopup(t.layer||t.target,t.latlng):void(this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var Zn=zn.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){zn.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){zn.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=zn.prototype.getEvents.call(this);return ie&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip",i=t+" "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ht("div",i)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.xthis.options.maxZoom||en&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1this.options.maxZoom||void 0!==this.options.minZoom&&o1)return void this._setView(t,e);for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);d.z=this._tileZoom,this._isValidTile(d)&&(this._tiles[this._tileCoordsToKey(d)]||r.push(d))}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var p=document.createDocumentFragment();for(_=0;_e.max.x)||!i.wrapLat&&(t.ye.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToBounds:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e),s=i.unproject(n,t.z),r=i.unproject(o,t.z),a=new T(s,r);return this.options.noWrap||i.wrapLatLngBounds(a),a},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(ut(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pt(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Ii&&this.options.opacity<1&&vt(t,this.options.opacity),Ri&&!Di&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),Lt(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){if(this._map){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);n=this._tiles[o],n&&(n.loaded=+new Date,this._map._fadeAnimated?(vt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(pt(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Ii||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),Bn=kn.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,i=l(this,i),i.detectRetina&&oe&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),Ri||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return V(n,"load",e(this._tileOnLoad,this,i,n)),V(n,"error",e(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:oe?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Ii?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.src!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&(i=this._tiles[t].el,i.onload=r,i.onerror=r,i.complete||(i.src=fi,ut(i)))}}),In=Bn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);e=l(this,e),n.width=n.height=e.tileSize*(e.detectRetina&&oe?2:1),this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,Bn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToBounds(t),e=this._crs.project(i.getNorthWest()),n=this._crs.project(i.getSouthEast()),o=(this._wmsVersion>=1.3&&this._crs===on?[n.y,e.x,e.y,n.x]:[e.x,n.y,n.x,e.y]).join(","),s=Bn.prototype.getTileUrl.call(this,t);return s+c(this.wmsParams,s,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+o},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});Bn.WMS=In,si.wms=ri;var An=rn.extend({options:{padding:.1},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&pt(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=Pt(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i),a=r.subtract(s),h=o.multiplyBy(-e).add(n).add(o).subtract(a);Yi?wt(this._container,h,e):Lt(this._container,h)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),On=An.extend({getEvents:function(){var t=An.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){An.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");V(t,"mousemove",o(this._onMouseMove,32,this),this),V(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),V(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){delete this._ctx,ut(this._container),G(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){var t;this._redrawBounds=null;for(var i in this._layers)t=this._layers[i],t._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},An.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=oe?2:1;Lt(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",oe&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){An.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,n=i.prev;e?e.prev=n:this._drawLast=n,n?n.next=e:this._drawFirst=e,delete t._order,delete this._layers[L.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var i,e=t.options.dashArray.split(","),n=[];for(i=0;i')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),Dn={_initContainer:function(){this._container=ht("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(An.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=Rn("shape");pt(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=Rn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ut(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=Rn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=mi(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=Rn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ct(t._container)},_bringToBack:function(t){_t(t._container)}},Nn=ae?Rn:S,jn=An.extend({getEvents:function(){var t=An.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=Nn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=Nn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ut(this._container),G(this._container),delete this._container,delete this._rootGroup},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){An.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Lt(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=Nn("path");t.options.className&&pt(i,t.options.className),t.options.interactive&&pt(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ut(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=t._radius,n=t._radiusY||e,o="a"+e+","+n+" 0 1,0 ",s=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+o+2*e+",0 "+o+2*-e+",0 ";this._setPath(t,s)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ct(t._path)},_bringToBack:function(t){_t(t._path)}});ae&&jn.include(Dn),ke.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this.options.preferCanvas&&ai()||hi()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=jn&&hi({pane:t})||On&&ai({pane:t}),this._paneRenderers[t]=i),i}});var Wn=yn.extend({initialize:function(t,i){yn.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});jn.create=Nn,jn.pointsToPath=k,xn.geometryToLayer=Kt,xn.coordsToLatLng=Yt,xn.coordsToLatLngs=Xt,xn.latLngToCoords=Jt,xn.latLngsToCoords=$t,xn.getFeature=Qt,xn.asFeature=ti,ke.mergeOptions({boxZoom:!0});var Hn=Ue.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){V(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){G(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ut(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){return!(!t.shiftKey||1!==t.which&&1!==t.button)&&(this._clearDeferredResetState(),this._resetState(),Mi(),bt(),this._startPoint=this._map.mouseEventToContainerPoint(t),void V(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this))},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ht("div","leaflet-zoom-box",this._container),pt(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();Lt(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ut(this._box),mt(this._container,"leaflet-crosshair")),Ci(),Tt(),G(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});ke.addInitHook("addHandler","boxZoom",Hn),ke.mergeOptions({doubleClickZoom:!0});var Fn=Ue.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});ke.addInitHook("addHandler","doubleClickZoom",Fn),ke.mergeOptions({dragging:!0,inertia:!Di,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var Un=Ue.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Xe(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pt(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mt(this._map._container,"leaflet-grab"),mt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),i-this._times[0]>50&&(this._positions.shift(),this._times.shift())}this._map.fire("move",t).fire("drag",t)},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.xi.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});ke.addInitHook("addHandler","scrollWheelZoom",Gn),ke.mergeOptions({tap:!0,tapTolerance:15});var qn=Ue.extend({addHooks:function(){V(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){G(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if($(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&pt(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),V(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),G(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&mt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});ie&&!te&&ke.addInitHook("addHandler","tap",qn),ke.mergeOptions({touchZoom:ie&&!Di,bounceAtZoomLimits:!0});var Kn=Ue.extend({addHooks:function(){pt(this._map._container,"leaflet-touch-zoom"),V(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){mt(this._map._container,"leaflet-touch-zoom"),G(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),V(document,"touchmove",this._onTouchMove,this),V(document,"touchend",this._onTouchEnd,this),$(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoomi.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),$(t)}},_onTouchEnd:function(){return this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),G(document,"touchmove",this._onTouchMove),G(document,"touchend",this._onTouchEnd),void(this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom)))):void(this._zooming=!1)}});ke.addInitHook("addHandler","touchZoom",Kn),ke.BoxZoom=Hn,ke.DoubleClickZoom=Fn,ke.Drag=Un,ke.Keyboard=Vn,ke.ScrollWheelZoom=Gn,ke.Tap=qn,ke.TouchZoom=Kn;var Yn=window.L;window.L=t,t.version=ci,t.noConflict=li,t.Control=Be,t.control=Ie,t.Browser=he,t.Evented=Li,t.Mixin=Ve,t.Util=xi,t.Class=v,t.Handler=Ue,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=Pe,t.DomUtil=Ee,t.PosAnimation=Se,t.Draggable=Xe,t.LineUtil=Je,t.PolyUtil=$e,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=Z,t.transformation=E,t.Projection=en,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=Pi,t.GeoJSON=xn,t.geoJSON=ii,t.geoJson=Ln,t.Layer=rn,t.LayerGroup=an,t.layerGroup=hn,t.FeatureGroup=un,t.featureGroup=ln,t.ImageOverlay=Pn,t.imageOverlay=bn,t.VideoOverlay=Tn,t.videoOverlay=ei,t.DivOverlay=zn,t.Popup=Mn,t.popup=Cn,t.Tooltip=Zn,t.tooltip=En,t.Icon=cn,t.icon=Ht,t.DivIcon=Sn,t.divIcon=ni,t.Marker=pn,t.marker=Ft,t.TileLayer=Bn,t.tileLayer=si,t.GridLayer=kn,t.gridLayer=oi,t.SVG=jn,t.svg=hi,t.Renderer=An,t.Canvas=On,t.canvas=ai,t.Path=mn,t.CircleMarker=fn,t.circleMarker=Ut,t.Circle=gn,t.circle=Vt,t.Polyline=vn,t.polyline=Gt,t.Polygon=yn,t.polygon=qt,t.Rectangle=Wn,t.rectangle=ui,t.Map=ke,t.map=Ct}); +//# sourceMappingURL=leaflet.js.map diff --git a/vendor/assets/stylesheets/leaflet.1.1.0.css b/vendor/assets/stylesheets/leaflet.1.1.0.css new file mode 100644 index 000000000..41126abde --- /dev/null +++ b/vendor/assets/stylesheets/leaflet.1.1.0.css @@ -0,0 +1,631 @@ +/* required styles */ + +.leaflet-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-container, +.leaflet-pane > svg, +.leaflet-pane > canvas, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +/* Safari renders non-retina tile on retina better with this, but Chrome is worse */ +.leaflet-safari .leaflet-tile { + image-rendering: -webkit-optimize-contrast; + } +/* hack that prevents hw layers "stretching" when loading new tiles */ +.leaflet-safari .leaflet-tile-container { + width: 1600px; + height: 1600px; + -webkit-transform-origin: 0 0; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ +/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container .leaflet-overlay-pane svg, +.leaflet-container .leaflet-marker-pane img, +.leaflet-container .leaflet-shadow-pane img, +.leaflet-container .leaflet-tile-pane img, +.leaflet-container img.leaflet-image-layer { + max-width: none !important; + } + +.leaflet-container.leaflet-touch-zoom { + -ms-touch-action: pan-x pan-y; + touch-action: pan-x pan-y; + } +.leaflet-container.leaflet-touch-drag { + -ms-touch-action: pinch-zoom; + } +.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { + -ms-touch-action: none; + touch-action: none; +} +.leaflet-container { + -webkit-tap-highlight-color: transparent; +} +.leaflet-container a { + -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4); +} +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + -moz-box-sizing: border-box; + box-sizing: border-box; + z-index: 800; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-pane { z-index: 400; } + +.leaflet-tile-pane { z-index: 200; } +.leaflet-overlay-pane { z-index: 400; } +.leaflet-shadow-pane { z-index: 500; } +.leaflet-marker-pane { z-index: 600; } +.leaflet-tooltip-pane { z-index: 650; } +.leaflet-popup-pane { z-index: 700; } + +.leaflet-map-pane canvas { z-index: 100; } +.leaflet-map-pane svg { z-index: 200; } + +.leaflet-vml-shape { + width: 1px; + height: 1px; + } +.lvml { + behavior: url(#default#VML); + display: inline-block; + position: absolute; + } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 800; + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile { + will-change: opacity; + } +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } +.leaflet-zoom-animated { + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + transform-origin: 0 0; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + will-change: transform; + } +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-interactive { + cursor: pointer; + } +.leaflet-grab { + cursor: -webkit-grab; + cursor: -moz-grab; + } +.leaflet-crosshair, +.leaflet-crosshair .leaflet-interactive { + cursor: crosshair; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging .leaflet-grab, +.leaflet-dragging .leaflet-grab .leaflet-interactive, +.leaflet-dragging .leaflet-marker-draggable { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + +/* marker & overlays interactivity */ +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-image-layer, +.leaflet-pane > svg path, +.leaflet-tile-container { + pointer-events: none; + } + +.leaflet-marker-icon.leaflet-interactive, +.leaflet-image-layer.leaflet-interactive, +.leaflet-pane > svg path.leaflet-interactive { + pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ + pointer-events: auto; + } + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #38f; + background: rgba(255,255,255,0.5); + } + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 5px rgba(0,0,0,0.65); + border-radius: 4px; + } +.leaflet-bar a, +.leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + line-height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + border-top-left-radius: 2px; + border-top-right-radius: 2px; + } +.leaflet-touch .leaflet-bar a:last-child { + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; + } + +/* zoom control */ + +.leaflet-control-zoom-in, +.leaflet-control-zoom-out { + font: bold 18px 'Lucida Console', Monaco, monospace; + text-indent: 1px; + } + +.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out { + font-size: 22px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + background: #fff; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + padding-right: 5px; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + +/* Default icon URLs */ +.leaflet-default-icon-path { + background-image: url(images/marker-icon.png); + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background: #fff; + background: rgba(255, 255, 255, 0.7); + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + } +.leaflet-control-attribution a { + text-decoration: none; + } +.leaflet-control-attribution a:hover { + text-decoration: underline; + } +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font-size: 11px; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + white-space: nowrap; + overflow: hidden; + -moz-box-sizing: border-box; + box-sizing: border-box; + + background: #fff; + background: rgba(255, 255, 255, 0.5); + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 2px solid rgba(0,0,0,0.2); + background-clip: padding-box; + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + margin-bottom: 20px; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 19px; + line-height: 1.4; + } +.leaflet-popup-content p { + margin: 18px 0; + } +.leaflet-popup-tip-container { + width: 40px; + height: 20px; + position: absolute; + left: 50%; + margin-left: -20px; + overflow: hidden; + pointer-events: none; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, +.leaflet-popup-tip { + background: white; + color: #333; + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 4px 0 0; + border: none; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; + } +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; + } + +.leaflet-oldie .leaflet-popup-content-wrapper { + zoom: 1; + } +.leaflet-oldie .leaflet-popup-tip { + width: 24px; + margin: 0 auto; + + -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; + filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); + } +.leaflet-oldie .leaflet-popup-tip-container { + margin-top: -1px; + } + +.leaflet-oldie .leaflet-control-zoom, +.leaflet-oldie .leaflet-control-layers, +.leaflet-oldie .leaflet-popup-content-wrapper, +.leaflet-oldie .leaflet-popup-tip { + border: 1px solid #999; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } + + +/* Tooltip */ +/* Base styles for the element that has a tooltip */ +.leaflet-tooltip { + position: absolute; + padding: 6px; + background-color: #fff; + border: 1px solid #fff; + border-radius: 3px; + color: #222; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + pointer-events: none; + box-shadow: 0 1px 3px rgba(0,0,0,0.4); + } +.leaflet-tooltip.leaflet-clickable { + cursor: pointer; + pointer-events: auto; + } +.leaflet-tooltip-top:before, +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + position: absolute; + pointer-events: none; + border: 6px solid transparent; + background: transparent; + content: ""; + } + +/* Directions */ + +.leaflet-tooltip-bottom { + margin-top: 6px; +} +.leaflet-tooltip-top { + margin-top: -6px; +} +.leaflet-tooltip-bottom:before, +.leaflet-tooltip-top:before { + left: 50%; + margin-left: -6px; + } +.leaflet-tooltip-top:before { + bottom: 0; + margin-bottom: -12px; + border-top-color: #fff; + } +.leaflet-tooltip-bottom:before { + top: 0; + margin-top: -12px; + margin-left: -6px; + border-bottom-color: #fff; + } +.leaflet-tooltip-left { + margin-left: -6px; +} +.leaflet-tooltip-right { + margin-left: 6px; +} +.leaflet-tooltip-left:before, +.leaflet-tooltip-right:before { + top: 50%; + margin-top: -6px; + } +.leaflet-tooltip-left:before { + right: 0; + margin-right: -12px; + border-left-color: #fff; + } +.leaflet-tooltip-right:before { + left: 0; + margin-left: -12px; + border-right-color: #fff; + }