Merge pull request #6151 from betagouv/main

2021-04-29-01
This commit is contained in:
LeSim 2021-04-29 12:19:24 +02:00 committed by GitHub
commit 3f507e3929
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 229 additions and 298 deletions

View file

@ -3,5 +3,4 @@
// = require ./common // = require ./common
// = require ./utils // = require ./utils
// = require ./fonts // = require ./fonts
// = require leaflet
// = require_tree . // = require_tree .

View file

@ -85,10 +85,10 @@ module Instructeurs
@archived_dossiers @archived_dossiers
end end
@has_en_cours_notifications = current_instructeur.notifications_for_procedure(@procedure, :en_cours).exists? notifications = current_instructeur.notifications_for_groupe_instructeurs(groupe_instructeur_ids)
@has_termine_notifications = current_instructeur.notifications_for_procedure(@procedure, :termine).exists? @has_en_cours_notifications = notifications[:en_cours].present?
@has_termine_notifications = notifications[:termines].present?
@not_archived_notifications_dossier_ids = current_instructeur.notifications_for_procedure(@procedure, :not_archived).pluck(:id) @not_archived_notifications_dossier_ids = notifications[:en_cours] + notifications[:termines]
sorted_ids = procedure_presentation.sorted_ids(@dossiers, current_instructeur) sorted_ids = procedure_presentation.sorted_ids(@dossiers, current_instructeur)
@ -101,18 +101,12 @@ module Instructeurs
page = params[:page].presence || 1 page = params[:page].presence || 1
filtered_sorted_paginated_ids = Kaminari @filtered_sorted_paginated_ids = Kaminari
.paginate_array(filtered_sorted_ids) .paginate_array(filtered_sorted_ids)
.page(page) .page(page)
.per(ITEMS_PER_PAGE) .per(ITEMS_PER_PAGE)
@dossiers = @dossiers.where(id: filtered_sorted_paginated_ids) @projected_dossiers = DossierProjectionService.project(@filtered_sorted_paginated_ids, procedure_presentation.displayed_fields)
@dossiers = @dossiers.sort_by { |d| filtered_sorted_paginated_ids.index(d.id) }
@projected_dossiers = DossierProjectionService.project(filtered_sorted_paginated_ids, procedure_presentation.displayed_fields)
kaminarize(page, filtered_sorted_ids.count)
assign_exports assign_exports
end end
@ -156,6 +150,11 @@ module Instructeurs
.groupe_instructeurs .groupe_instructeurs
.where(procedure: procedure) .where(procedure: procedure)
@dossier_count = current_instructeur
.dossiers_count_summary(groupe_instructeur_ids)
.fetch_values('tous', 'archives')
.sum
export = Export.find_or_create_export(export_format, groupe_instructeurs) export = Export.find_or_create_export(export_format, groupe_instructeurs)
if export.ready? && export.old? && params[:force_export] if export.ready? && export.old? && params[:force_export]
@ -220,10 +219,7 @@ module Instructeurs
end end
def assign_exports def assign_exports
groupe_instructeurs_for_procedure = current_instructeur.groupe_instructeurs.where(procedure: procedure) @xlsx_export, @csv_export, @ods_export = Export.find_for_groupe_instructeurs(groupe_instructeur_ids)
@xlsx_export = Export.find_for_format_and_groupe_instructeurs(:xlsx, groupe_instructeurs_for_procedure)
@csv_export = Export.find_for_format_and_groupe_instructeurs(:csv, groupe_instructeurs_for_procedure)
@ods_export = Export.find_for_format_and_groupe_instructeurs(:ods, groupe_instructeurs_for_procedure)
end end
def assign_to def assign_to
@ -251,7 +247,9 @@ module Instructeurs
end end
def procedure def procedure
Procedure.find(procedure_id) Procedure
.with_attached_logo
.find(procedure_id)
end end
def ensure_ownership! def ensure_ownership!
@ -282,25 +280,5 @@ module Instructeurs
def current_filters def current_filters
@current_filters ||= procedure_presentation.filters[statut] @current_filters ||= procedure_presentation.filters[statut]
end end
def kaminarize(current_page, total)
@dossiers.instance_eval <<-EVAL
def current_page
#{current_page}
end
def total_pages
(#{total} / #{ITEMS_PER_PAGE}.to_f).ceil
end
def limit_value
#{ITEMS_PER_PAGE}
end
def first_page?
current_page == 1
end
def last_page?
current_page == total_pages
end
EVAL
end
end end
end end

View file

@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import { GeoJSONLayer, ZoomControl } from 'react-mapbox-gl'; import { GeoJSONLayer, ZoomControl } from 'react-mapbox-gl';
import DrawControl from 'react-mapbox-gl-draw'; import DrawControl from 'react-mapbox-gl-draw';
import 'mapbox-gl/dist/mapbox-gl.css';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import { getJSON, ajax, fire } from '@utils'; import { getJSON, ajax, fire } from '@utils';

View file

@ -21,4 +21,17 @@ class Expert < ApplicationRecord
def self.by_email(email) def self.by_email(email)
Expert.eager_load(:user).find_by(users: { email: email }) Expert.eager_load(:user).find_by(users: { email: email })
end end
def avis_summary
if @avis_summary.present?
@avis_summary
else
query = <<~EOF
COUNT(*) FILTER (where answer IS NULL) AS unanswered,
COUNT(*) AS total
EOF
result = avis.select(query)[0]
@avis_summary = { unanswered: result.unanswered, total: result.total }
end
end
end end

View file

@ -51,15 +51,18 @@ class Export < ApplicationRecord
def self.find_or_create_export(format, groupe_instructeurs) def self.find_or_create_export(format, groupe_instructeurs)
create_with(groupe_instructeurs: groupe_instructeurs) create_with(groupe_instructeurs: groupe_instructeurs)
.create_or_find_by(format: format, key: generate_cache_key(groupe_instructeurs)) .create_or_find_by(format: format, key: generate_cache_key(groupe_instructeurs.map(&:id)))
end end
def self.find_for_format_and_groupe_instructeurs(format, groupe_instructeurs) def self.find_for_groupe_instructeurs(groupe_instructeurs_ids)
find_by(format: format, key: generate_cache_key(groupe_instructeurs)) exports = where(key: generate_cache_key(groupe_instructeurs_ids))
['xlsx', 'csv', 'ods']
.map { |format| exports.find { |export| export.format == format } }
end end
def self.generate_cache_key(groupe_instructeurs) def self.generate_cache_key(groupe_instructeurs_ids)
groupe_instructeurs.map(&:id).sort.join('-') groupe_instructeurs_ids.sort.join('-')
end end
private private

View file

@ -134,14 +134,21 @@ class Instructeur < ApplicationRecord
end end
end end
def notifications_for_procedure(procedure, scope) def notifications_for_groupe_instructeurs(groupe_instructeurs)
target_groupes = groupe_instructeurs.where(procedure: procedure)
Dossier Dossier
.where(groupe_instructeur: target_groupes) .not_archived
.send(scope) # :en_cours or :termine or :not_archived (or any other Dossier scope) .where(groupe_instructeur: groupe_instructeurs)
.merge(followed_dossiers) .merge(followed_dossiers)
.with_notifications .with_notifications
.pluck(:state, :id)
.reduce({ termines: [], en_cours: [] }) do |acc, e|
if Dossier::TERMINE.include?(e[0])
acc[:termines] << e[1]
elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(e[0])
acc[:en_cours] << e[1]
end
acc
end
end end
def procedure_ids_with_notifications(scope) def procedure_ids_with_notifications(scope)
@ -165,11 +172,14 @@ class Instructeur < ApplicationRecord
.reduce([]) do |acc, groupe| .reduce([]) do |acc, groupe|
procedure = groupe.procedure procedure = groupe.procedure
notifications = notifications_for_groupe_instructeurs([groupe.id])
nb_notification = notifications[:en_cours].count + notifications[:termines].count
h = { h = {
nb_en_construction: groupe.dossiers.en_construction.count, nb_en_construction: groupe.dossiers.en_construction.count,
nb_en_instruction: groupe.dossiers.en_instruction.count, nb_en_instruction: groupe.dossiers.en_instruction.count,
nb_accepted: Traitement.where(dossier: groupe.dossiers.accepte, processed_at: Time.zone.yesterday.beginning_of_day..Time.zone.yesterday.end_of_day).count, nb_accepted: Traitement.where(dossier: groupe.dossiers.accepte, processed_at: Time.zone.yesterday.beginning_of_day..Time.zone.yesterday.end_of_day).count,
nb_notification: notifications_for_procedure(procedure, :not_archived).count nb_notification: nb_notification
} }
if h[:nb_en_construction] > 0 || h[:nb_notification] > 0 if h[:nb_en_construction] > 0 || h[:nb_notification] > 0

View file

@ -1,5 +1,5 @@
class DossierProjectionService class DossierProjectionService
class DossierProjection < Struct.new(:dossier, :columns) class DossierProjection < Struct.new(:dossier_id, :state, :archived, :columns)
end end
TABLE = 'table' TABLE = 'table'
@ -18,57 +18,64 @@ class DossierProjectionService
# - the order of the intermediary query results are unknown # - the order of the intermediary query results are unknown
# - some values can be missing (if a revision added or removed them) # - some values can be missing (if a revision added or removed them)
def self.project(dossiers_ids, fields) def self.project(dossiers_ids, fields)
champ_fields, other_fields = fields state_field = { TABLE => 'self', COLUMN => 'state' }
.partition { |f| ['type_de_champ', 'type_de_champ_private'].include?(f[TABLE]) } archived_field = { TABLE => 'self', COLUMN => 'archived' }
if champ_fields.present? ([state_field, archived_field] + fields) # the view needs state and archived dossier attributes
Champ .each { |f| f[:id_value_h] = {} }
.includes(:type_de_champ) .group_by { |f| f[TABLE] } # one query per table
.where( .each do |table, fields|
# as querying the champs table is costly case table
# we fetch all the requested champs at once when 'type_de_champ', 'type_de_champ_private'
types_de_champ: { stable_id: champ_fields.map { |f| f[COLUMN] } }, Champ
dossier_id: dossiers_ids .includes(:type_de_champ)
) .where(
.select(:dossier_id, :value, :type_de_champ_id, :stable_id) # we cannot pluck :value, as we need the champ.to_s method types_de_champ: { stable_id: fields.map { |f| f[COLUMN] } },
.group_by(&:stable_id) # the champs are redispatched to their respective fields dossier_id: dossiers_ids
.map do |stable_id, champs| )
field = champ_fields.find { |f| f[COLUMN] == stable_id.to_s } .select(:dossier_id, :value, :type_de_champ_id, :stable_id) # we cannot pluck :value, as we need the champ.to_s method
field[:id_value_h] = champs.to_h { |c| [c.dossier_id, c.to_s] } .group_by(&:stable_id) # the champs are redispatched to their respective fields
end .map do |stable_id, champs|
end field = fields.find { |f| f[COLUMN] == stable_id.to_s }
field[:id_value_h] = champs.to_h { |c| [c.dossier_id, c.to_s] }
other_fields.each do |field| end
field[:id_value_h] = case field[TABLE]
when 'self' when 'self'
Dossier Dossier
.where(id: dossiers_ids) .where(id: dossiers_ids)
.pluck(:id, field[COLUMN].to_sym) .pluck(:id, *fields.map { |f| f[COLUMN].to_sym })
.to_h { |id, col| [id, col&.strftime('%d/%m/%Y')] } .each do |id, *columns|
fields.zip(columns).each do |field, value|
if [state_field, archived_field].include?(field)
field[:id_value_h][id] = value
else
field[:id_value_h][id] = value&.strftime('%d/%m/%Y') # other fields are datetime
end
end
end
when 'individual'
Individual
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, *fields.map { |f| f[COLUMN].to_sym })
.each { |id, *columns| fields.zip(columns).each { |field, value| field[:id_value_h][id] = value } }
when 'etablissement'
Etablissement
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, *fields.map { |f| f[COLUMN].to_sym })
.each { |id, *columns| fields.zip(columns).each { |field, value| field[:id_value_h][id] = value } }
when 'user' when 'user'
Dossier fields[0][:id_value_h] = Dossier # there is only one field available for user table
.joins(:user) .joins(:user)
.where(id: dossiers_ids) .where(id: dossiers_ids)
.pluck('dossiers.id, users.email') .pluck('dossiers.id, users.email')
.to_h .to_h
when 'individual'
Individual
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, field[COLUMN].to_sym)
.to_h
when 'etablissement'
Etablissement
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, field[COLUMN].to_sym)
.to_h
when 'groupe_instructeur' when 'groupe_instructeur'
Dossier fields[0][:id_value_h] = Dossier
.joins(:groupe_instructeur) .joins(:groupe_instructeur)
.where(id: dossiers_ids) .where(id: dossiers_ids)
.pluck('dossiers.id, groupe_instructeurs.label') .pluck('dossiers.id, groupe_instructeurs.label')
.to_h .to_h
when 'followers_instructeurs' when 'followers_instructeurs'
Follow fields[0][:id_value_h] = Follow
.active .active
.joins(instructeur: :user) .joins(instructeur: :user)
.where(dossier_id: dossiers_ids) .where(dossier_id: dossiers_ids)
@ -78,11 +85,13 @@ class DossierProjectionService
end end
end end
Dossier dossiers_ids.map do |dossier_id|
.select(:id, :state, :archived) # the dossier object is needed in the view DossierProjection.new(
.find(dossiers_ids) # keeps dossiers_ids order and raise exception if one is missing dossier_id,
.map do |dossier| state_field[:id_value_h][dossier_id],
DossierProjection.new(dossier, fields.map { |f| f[:id_value_h][dossier.id] }) archived_field[:id_value_h][dossier_id],
fields.map { |f| f[:id_value_h][dossier_id] }
)
end end
end end
end end

View file

@ -23,7 +23,13 @@
- else - else
%p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}. %p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}.
= render partial: "instructeurs/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_instructeur&.follow?(dossier) } = render partial: "instructeurs/procedures/dossier_actions",
locals: { procedure_id: dossier.procedure.id,
dossier_id: dossier.id,
state: dossier.state,
archived: dossier.archived,
dossier_is_followed: current_instructeur&.follow?(dossier) }
.state-button .state-button
= render partial: "state_button", locals: { dossier: dossier } = render partial: "state_button", locals: { dossier: dossier }

View file

@ -1,19 +1,19 @@
- if dossier.en_construction_ou_instruction? - if Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
- if dossier_is_followed - if dossier_is_followed
= link_to unfollow_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do = link_to unfollow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.unfollow> %span.icon.unfollow>
Ne plus suivre Ne plus suivre
- else - else
= link_to follow_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do = link_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.follow> %span.icon.follow>
Suivre le dossier Suivre le dossier
- elsif dossier.termine? - elsif Dossier::TERMINE.include?(state)
- if dossier.archived - if archived
= link_to unarchive_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do = link_to unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.unarchive> %span.icon.unarchive>
Désarchiver le dossier Désarchiver le dossier
- else - else
= link_to archive_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do = link_to archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.archive> %span.icon.archive>
Archiver le dossier Archiver le dossier

View file

@ -1,4 +1,4 @@
- if procedure.dossiers.state_not_brouillon.any? - if dossier_count > 0
%span.dropdown %span.dropdown
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'download-menu' } %button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'download-menu' }
Télécharger tous les dossiers Télécharger tous les dossiers

View file

@ -3,6 +3,6 @@
= field['label'] = field['label']
- if @procedure_presentation.sort['table'] == field['table'] && @procedure_presentation.sort['column'] == field['column'] - if @procedure_presentation.sort['table'] == field['table'] && @procedure_presentation.sort['column'] == field['column']
- if @procedure_presentation.sort['order'] == 'asc' - if @procedure_presentation.sort['order'] == 'asc'
%img.caret-icon{ src: image_url("table/up_caret.svg") } %img.caret-icon{ src: image_url("table/up_caret.svg"), width: 10, height: 6, loading: 'lazy' }
- else - else
%img.caret-icon{ src: image_url("table/down_caret.svg") } %img.caret-icon{ src: image_url("table/down_caret.svg"), width: 10, height: 6, loading: 'lazy' }

View file

@ -1,5 +1,5 @@
<%= render_to_element('.procedure-actions', partial: "download_dossiers", <%= render_to_element('.procedure-actions', partial: "download_dossiers",
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export }) %> locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export, dossier_count: @dossier_count }) %>
<% [[@xlsx_export, :xlsx], [@csv_export, :csv], [@ods_export, :ods]].each do |(export, format)| %> <% [[@xlsx_export, :xlsx], [@csv_export, :csv], [@ods_export, :ods]].each do |(export, format)| %>
<% if export && !export.ready? %> <% if export && !export.ready? %>

View file

@ -50,7 +50,7 @@
.procedure-actions .procedure-actions
= render partial: "download_dossiers", = render partial: "download_dossiers",
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export } locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export, dossier_count: @tous_count + @archives_count }
.container .container
- if @statut == 'a-suivre' - if @statut == 'a-suivre'
@ -75,8 +75,9 @@
%span.icon.delete %span.icon.delete
Afficher les dossiers supprimés Afficher les dossiers supprimés
- if @dossiers.present? || @current_filters.count > 0 - if @filtered_sorted_paginated_ids.present? || @current_filters.count > 0
= paginate @dossiers - pagination = paginate @filtered_sorted_paginated_ids
= pagination
%span.dropdown %span.dropdown
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'filter-menu' } %button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'filter-menu' }
Filtrer Filtrer
@ -130,27 +131,32 @@
%tbody %tbody
- @projected_dossiers.each do |p| - @projected_dossiers.each do |p|
- dossier = p.dossier - path = instructeur_dossier_path(@procedure, p.dossier_id)
- path = instructeur_dossier_path(@procedure, dossier.id)
%tr %tr
%td.folder-col %td.folder-col
%a.cell-link{ href: path } %a.cell-link{ href: path }
%span.icon.folder %span.icon.folder
- if @not_archived_notifications_dossier_ids.include?(dossier.id) - if @not_archived_notifications_dossier_ids.include?(p.dossier_id)
%span.notifications{ 'aria-label': 'notifications' } %span.notifications{ 'aria-label': 'notifications' }
%td.number-col %td.number-col
%a.cell-link{ href: path }= dossier.id %a.cell-link{ href: path }= p.dossier_id
- p.columns.each do |column| - p.columns.each do |column|
%td %td
%a.cell-link{ href: path }= column %a.cell-link{ href: path }= column
%td.status-col %td.status-col
%a.cell-link{ href: path }= status_badge(dossier.state) %a.cell-link{ href: path }= status_badge(p.state)
%td.action-col.follow-col= render partial: 'dossier_actions', locals: { procedure: @procedure, dossier: dossier, dossier_is_followed: @followed_dossiers_id.include?(dossier.id) } %td.action-col.follow-col= render partial: 'dossier_actions',
= paginate @dossiers locals: { procedure_id: @procedure.id,
dossier_id: p.dossier_id,
state: p.state,
archived: p.archived,
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id) }
= pagination
- else - else
%h2.empty-text Aucun dossier %h2.empty-text Aucun dossier

View file

@ -31,6 +31,12 @@
%td.status-col %td.status-col
= link_to(dossier_linked_path(current_instructeur, dossier), class: 'cell-link') do = link_to(dossier_linked_path(current_instructeur, dossier), class: 'cell-link') do
= status_badge(dossier.state) = status_badge(dossier.state)
%td.action-col.follow-col= render partial: 'instructeurs/procedures/dossier_actions', locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: @followed_dossiers_id.include?(dossier.id) } %td.action-col.follow-col= render partial: "instructeurs/procedures/dossier_actions",
locals: { procedure_id: dossier.procedure.id,
dossier_id: dossier.id,
state: dossier.state,
archived: dossier.archived,
dossier_is_followed: @followed_dossiers_id.include?(dossier.id) }
- else - else
%h2 Aucun dossier correspondant à votre recherche n'a été trouvé %h2 Aucun dossier correspondant à votre recherche n'a été trouvé

View file

@ -1,7 +1,7 @@
.dropdown.header-menu-opener .dropdown.header-menu-opener
%button.button.dropdown-button.icon-only.header-menu-button{ title: "Mon compte", 'aria-expanded' => 'false', 'aria-controls' => 'mon_compte_menu' } %button.button.dropdown-button.icon-only.header-menu-button{ title: "Mon compte", 'aria-expanded' => 'false', 'aria-controls' => 'mon_compte_menu' }
.hidden Mon compte .hidden Mon compte
= image_tag "icons/account-circle.svg", alt: 'Mon compte' = image_tag "icons/account-circle.svg", alt: 'Mon compte', width: 24, height: 24, loading: 'lazy'
%ul.header-menu.dropdown-content#mon_compte_menu %ul.header-menu.dropdown-content#mon_compte_menu
%li %li
.menu-item{ title: current_email } .menu-item{ title: current_email }

View file

@ -21,30 +21,28 @@
- if nav_bar_profile == :instructeur && instructeur_signed_in? - if nav_bar_profile == :instructeur && instructeur_signed_in?
- current_url = request.path_info - current_url = request.path_info
%ul.header-tabs %ul.header-tabs
- if current_instructeur.procedures.count > 0 - if current_instructeur.procedures.any?
%li %li
= active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link' = active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link'
- if current_instructeur.user.expert && current_expert.avis.count > 0 - if current_instructeur.user.expert && current_expert.avis_summary[:total] > 0
%li %li
= active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do = active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do
Avis Avis
- avis_counter = current_expert.avis.without_answer.count - if current_expert.avis_summary[:unanswered] > 0
- if avis_counter > 0 %span.badge.warning= current_expert.avis_summary[:unanswered]
%span.badge.warning= avis_counter
- if nav_bar_profile == :expert && expert_signed_in? - if nav_bar_profile == :expert && expert_signed_in?
%ul.header-tabs %ul.header-tabs
- if current_expert.user.instructeur && current_instructeur.procedures.count > 0 - if current_expert.user.instructeur && current_instructeur.procedures.any?
%li %li
= active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link' = active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link'
- if current_expert.avis.count > 0 - if current_expert.avis_summary[:total] > 0
%li %li
= active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do = active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do
Avis Avis
- avis_counter = current_expert.avis.without_answer.count - if current_expert.avis_summary[:unanswered] > 0
- if avis_counter > 0 %span.badge.warning= current_expert.avis_summary[:unanswered]
%span.badge.warning= avis_counter
- if nav_bar_profile == :user - if nav_bar_profile == :user
%ul.header-tabs %ul.header-tabs

View file

@ -3,4 +3,4 @@
= label_tag :q, "Numéro de dossier", class: 'hidden' = label_tag :q, "Numéro de dossier", class: 'hidden'
= text_field_tag "q", "#{@search_terms if @search_terms.present?}", placeholder: "Rechercher un dossier" = text_field_tag "q", "#{@search_terms if @search_terms.present?}", placeholder: "Rechercher un dossier"
%button{ title: "Rechercher" } %button{ title: "Rechercher" }
= image_tag "icons/search-blue.svg", alt: 'Rechercher', 'aria-hidden':'true' = image_tag "icons/search-blue.svg", alt: 'Rechercher', 'aria-hidden':'true', width: 24, height: 24, loading: 'lazy'

View file

@ -5,14 +5,14 @@
"@fortawesome/free-solid-svg-icons": "^5.15.2", "@fortawesome/free-solid-svg-icons": "^5.15.2",
"@fortawesome/react-fontawesome": "^0.1.14", "@fortawesome/react-fontawesome": "^0.1.14",
"@heroicons/react": "^1.0.1", "@heroicons/react": "^1.0.1",
"@mapbox/mapbox-gl-draw": "^1.2.0", "@mapbox/mapbox-gl-draw": "^1.2.2",
"@rails/actiontext": "^6.0.3", "@rails/actiontext": "^6.0.3",
"@rails/activestorage": "^6.0.3", "@rails/activestorage": "^6.0.3",
"@rails/ujs": "^6.0.3", "@rails/ujs": "^6.0.3",
"@rails/webpacker": "5.1.1", "@rails/webpacker": "5.1.1",
"@reach/combobox": "^0.13.0", "@reach/combobox": "^0.13.0",
"@sentry/browser": "^5.15.5", "@sentry/browser": "^5.15.5",
"@tmcw/togeojson": "^4.0.0", "@tmcw/togeojson": "^4.3.0",
"babel-plugin-macros": "^2.8.0", "babel-plugin-macros": "^2.8.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"chartkick": "^3.2.0", "chartkick": "^3.2.0",
@ -22,13 +22,13 @@
"email-butler": "^1.0.13", "email-butler": "^1.0.13",
"highcharts": "^8.1.1", "highcharts": "^8.1.1",
"intersection-observer": "^0.12.0", "intersection-observer": "^0.12.0",
"mapbox-gl": "^1.11.1", "mapbox-gl": "^1.3.0",
"match-sorter": "^6.2.0", "match-sorter": "^6.2.0",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react": "^17.0.1", "react": "^17.0.1",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-intersection-observer": "^8.31.0", "react-intersection-observer": "^8.31.0",
"react-mapbox-gl": "^4.8.6", "react-mapbox-gl": "^5.1.1",
"react-mapbox-gl-draw": "^2.0.4", "react-mapbox-gl-draw": "^2.0.4",
"react-query": "^3.9.7", "react-query": "^3.9.7",
"react-sortable-hoc": "^1.11.0", "react-sortable-hoc": "^1.11.0",

View file

@ -4,7 +4,7 @@ FactoryBot.define do
groupe_instructeurs { [association(:groupe_instructeur)] } groupe_instructeurs { [association(:groupe_instructeur)] }
after(:build) do |export, _evaluator| after(:build) do |export, _evaluator|
export.key = Export.generate_cache_key(export.groupe_instructeurs) export.key = Export.generate_cache_key(export.groupe_instructeurs.map(&:id))
end end
end end
end end

View file

@ -3,6 +3,10 @@ feature 'wcag rules for usager', js: true do
let(:password) { 'a very complicated password' } let(:password) { 'a very complicated password' }
let(:litteraire_user) { create(:user, password: password) } let(:litteraire_user) { create(:user, password: password) }
before do
procedure.types_de_champ.find { |tdc| tdc.type_champ == TypeDeChamp.type_champs.fetch(:carte) }.destroy
end
context 'pages without the need to be logged in' do context 'pages without the need to be logged in' do
scenario 'homepage' do scenario 'homepage' do
visit root_path visit root_path

View file

@ -48,9 +48,9 @@ RSpec.describe Export, type: :model do
context 'when an export is made for one groupe instructeur' do context 'when an export is made for one groupe instructeur' do
let!(:export) { create(:export, groupe_instructeurs: [gi_1, gi_2]) } let!(:export) { create(:export, groupe_instructeurs: [gi_1, gi_2]) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1])).to eq(nil) } it { expect(Export.find_for_groupe_instructeurs([gi_1.id])[1]).to eq(nil) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_2, gi_1])).to eq(export) } it { expect(Export.find_for_groupe_instructeurs([gi_2.id, gi_1.id])[1]).to eq(export) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1, gi_2, gi_3])).to eq(nil) } it { expect(Export.find_for_groupe_instructeurs([gi_1.id, gi_2.id, gi_3.id])[1]).to eq(nil) }
end end
end end
end end

View file

@ -255,29 +255,35 @@ describe Instructeur, type: :model do
end end
end end
describe '#notifications_for_procedure' do describe '#notifications_for_groupe_instructeurs' do
# one procedure, one group, 2 instructeurs
let(:procedure) { create(:simple_procedure, :routee, :with_type_de_champ_private) } let(:procedure) { create(:simple_procedure, :routee, :with_type_de_champ_private) }
let!(:dossier) { create(:dossier, :followed, groupe_instructeur: procedure.groupe_instructeurs.last, state: Dossier.states.fetch(:en_construction)) } let(:gi_p1) { procedure.groupe_instructeurs.last }
let!(:dossier) { create(:dossier, :followed, groupe_instructeur: gi_p1, state: Dossier.states.fetch(:en_construction)) }
let(:instructeur) { dossier.follows.first.instructeur } let(:instructeur) { dossier.follows.first.instructeur }
let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [procedure.groupe_instructeurs.last]) } let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [gi_p1]) }
# one other procedure, dossier followed by a third instructeur
let!(:dossier_on_procedure_2) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) } let!(:dossier_on_procedure_2) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) }
let!(:instructeur_on_procedure_2) { dossier_on_procedure_2.follows.first.instructeur } let!(:instructeur_on_procedure_2) { dossier_on_procedure_2.follows.first.instructeur }
let(:gi_p2) { dossier.groupe_instructeur }
let(:now) { Time.zone.parse("14/09/1867") } let(:now) { Time.zone.parse("14/09/1867") }
let(:follow) { instructeur.follows.find_by(dossier: dossier) } let(:follow) { instructeur.follows.find_by(dossier: dossier) }
let(:follow2) { instructeur_2.follows.find_by(dossier: dossier) } let(:follow2) { instructeur_2.follows.find_by(dossier: dossier) }
let(:seen_at_instructeur) { now - 1.hour } let(:seen_at_instructeur) { now - 1.hour }
let(:seen_at_instructeur2) { now - 1.hour } let(:seen_at_instructeur2) { now - 1.hour }
before do before do
procedure.groupe_instructeurs.last.instructeurs << instructeur gi_p1.instructeurs << instructeur
instructeur_2.followed_dossiers << dossier instructeur_2.followed_dossiers << dossier
Timecop.freeze(now) Timecop.freeze(now)
end end
after { Timecop.return } after { Timecop.return }
subject { instructeur.notifications_for_procedure(procedure, :en_cours) } subject { instructeur.notifications_for_groupe_instructeurs(gi_p1)[:en_cours] }
context 'when the instructeur has just followed the dossier' do context 'when the instructeur has just followed the dossier' do
it { is_expected.to match([]) } it { is_expected.to match([]) }
@ -290,14 +296,14 @@ describe Instructeur, type: :model do
follow2.update_attribute('demande_seen_at', seen_at_instructeur2) follow2.update_attribute('demande_seen_at', seen_at_instructeur2)
end end
it { is_expected.to match([dossier]) } it { is_expected.to match([dossier.id]) }
it { expect(instructeur_2.notifications_for_procedure(procedure, :en_cours)).to match([dossier]) } it { expect(instructeur_2.notifications_for_groupe_instructeurs(gi_p1)[:en_cours]).to match([dossier.id]) }
it { expect(instructeur_on_procedure_2.notifications_for_procedure(procedure, :en_cours)).to match([]) } it { expect(instructeur_on_procedure_2.notifications_for_groupe_instructeurs(gi_p2)[:en_cours]).to match([]) }
context 'and there is a modification on private champs' do context 'and there is a modification on private champs' do
before { dossier.champs_private.first.update_attribute('value', 'toto') } before { dossier.champs_private.first.update_attribute('value', 'toto') }
it { is_expected.to match([dossier]) } it { is_expected.to match([dossier.id]) }
end end
context 'when instructeur update it s public champs last seen' do context 'when instructeur update it s public champs last seen' do
@ -305,7 +311,7 @@ describe Instructeur, type: :model do
let(:seen_at_instructeur2) { now - 1.hour } let(:seen_at_instructeur2) { now - 1.hour }
it { is_expected.to match([]) } it { is_expected.to match([]) }
it { expect(instructeur_2.notifications_for_procedure(procedure, :en_cours)).to match([dossier]) } it { expect(instructeur_2.notifications_for_groupe_instructeurs(gi_p1)[:en_cours]).to match([dossier.id]) }
end end
end end
@ -321,7 +327,7 @@ describe Instructeur, type: :model do
follow.update_attribute('annotations_privees_seen_at', seen_at_instructeur) follow.update_attribute('annotations_privees_seen_at', seen_at_instructeur)
end end
it { is_expected.to match([dossier]) } it { is_expected.to match([dossier.id]) }
end end
context 'when there is a modification on avis' do context 'when there is a modification on avis' do
@ -330,7 +336,7 @@ describe Instructeur, type: :model do
follow.update_attribute('avis_seen_at', seen_at_instructeur) follow.update_attribute('avis_seen_at', seen_at_instructeur)
end end
it { is_expected.to match([dossier]) } it { is_expected.to match([dossier.id]) }
end end
context 'the messagerie' do context 'the messagerie' do
@ -340,7 +346,7 @@ describe Instructeur, type: :model do
follow.update_attribute('messagerie_seen_at', seen_at_instructeur) follow.update_attribute('messagerie_seen_at', seen_at_instructeur)
end end
it { is_expected.to match([dossier]) } it { is_expected.to match([dossier.id]) }
end end
context 'when there is a new commentaire issued by tps' do context 'when there is a new commentaire issued by tps' do
@ -431,9 +437,9 @@ describe Instructeur, type: :model do
context 'when a notification exists' do context 'when a notification exists' do
before do before do
allow(instructeur).to receive(:notifications_for_procedure) allow(instructeur).to receive(:notifications_for_groupe_instructeurs)
.with(procedure_to_assign, :not_archived) .with([procedure_to_assign.groupe_instructeurs.first.id])
.and_return([1, 2, 3]) .and_return(en_cours: [1, 2, 3], termines: [])
end end
it do it do

View file

@ -5,8 +5,8 @@ describe DossierProjectionService do
context 'with multiple dossier' do context 'with multiple dossier' do
let!(:procedure) { create(:procedure, :with_type_de_champ) } let!(:procedure) { create(:procedure, :with_type_de_champ) }
let!(:dossier_1) { create(:dossier, procedure: procedure) } let!(:dossier_1) { create(:dossier, procedure: procedure) }
let!(:dossier_2) { create(:dossier, procedure: procedure) } let!(:dossier_2) { create(:dossier, :en_construction, :archived, procedure: procedure) }
let!(:dossier_3) { create(:dossier, procedure: procedure) } let!(:dossier_3) { create(:dossier, :en_instruction, procedure: procedure) }
let(:dossiers_ids) { [dossier_3.id, dossier_1.id, dossier_2.id] } let(:dossiers_ids) { [dossier_3.id, dossier_1.id, dossier_2.id] }
let(:fields) do let(:fields) do
@ -26,11 +26,20 @@ describe DossierProjectionService do
let(:result) { subject } let(:result) { subject }
it 'respects the dossiers_ids order and returns nil for empty result' do it 'respects the dossiers_ids order, returns state, archived and nil for empty result' do
expect(result.length).to eq(3) expect(result.length).to eq(3)
expect(result[0].dossier.id).to eq(dossier_3.id)
expect(result[1].dossier.id).to eq(dossier_1.id) expect(result[0].dossier_id).to eq(dossier_3.id)
expect(result[2].dossier.id).to eq(dossier_2.id) expect(result[1].dossier_id).to eq(dossier_1.id)
expect(result[2].dossier_id).to eq(dossier_2.id)
expect(result[0].state).to eq('en_instruction')
expect(result[1].state).to eq('brouillon')
expect(result[2].state).to eq('en_construction')
expect(result[0].archived).to be false
expect(result[1].archived).to be false
expect(result[2].archived).to be true
expect(result[0].columns[0]).to be nil expect(result[0].columns[0]).to be nil
expect(result[1].columns[0]).to eq('champ_1') expect(result[1].columns[0]).to eq('champ_1')

View file

@ -92,8 +92,8 @@ describe NotificationService do
context 'when there is a notification on this procedure' do context 'when there is a notification on this procedure' do
before do before do
allow_any_instance_of(Instructeur).to receive(:notifications_for_procedure) allow_any_instance_of(Instructeur).to receive(:notifications_for_groupe_instructeurs)
.and_return([12]) .and_return(en_cours: [12], termines: [])
end end
it do it do

View file

@ -1,20 +1,16 @@
describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do
let(:current_instructeur) { create(:instructeur) } let(:current_instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
let(:dossier_count) { 0 }
subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, xlsx_export: nil, csv_export: nil, ods_export: nil } subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, dossier_count: dossier_count, xlsx_export: nil, csv_export: nil, ods_export: nil }
context "when procedure has 0 dossier" do context "when procedure has 0 dossier" do
it { is_expected.not_to include("Télécharger tous les dossiers") } it { is_expected.not_to include("Télécharger tous les dossiers") }
end end
context "when procedure has 1 dossier brouillon" do context "when procedure has at least 1 dossier" do
let!(:dossier) { create(:dossier, procedure: procedure) } let(:dossier_count) { 1 }
it { is_expected.not_to include("Télécharger tous les dossiers") }
end
context "when procedure has at least 1 dossier en construction" do
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
it { is_expected.to include("Télécharger tous les dossiers") } it { is_expected.to include("Télécharger tous les dossiers") }
context "With zip archive enabled" do context "With zip archive enabled" do

151
yarn.lock
View file

@ -1109,17 +1109,16 @@
resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234"
integrity sha1-zlblOfg1UrWNENZy6k1vya3HsjQ= integrity sha1-zlblOfg1UrWNENZy6k1vya3HsjQ=
"@mapbox/mapbox-gl-draw@^1.2.0": "@mapbox/mapbox-gl-draw@^1.2.2":
version "1.2.0" version "1.2.2"
resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-draw/-/mapbox-gl-draw-1.2.0.tgz#b6e5278afef65bd5d7d92366034997768e478ad9" resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-draw/-/mapbox-gl-draw-1.2.2.tgz#1293db9912464128daa58335fc989defa9f90eda"
integrity sha512-gMrP2zn8PzDtrs72FMJTPytCumX5vUn9R7IK38qBOVy9UfqbdWr56KYuNA/2X+jKn4FIOpmWf8CWkKpOaQkv7w== integrity sha512-CmMSe6siErQD/LOPoF6DNBsSvIEfLEFvSjocBcXbBLgbnevhWjwQd1BJ/DYt1y9bGlPiSjowQ4s4JBg9y2L8Gw==
dependencies: dependencies:
"@mapbox/geojson-area" "^0.2.1" "@mapbox/geojson-area" "^0.2.1"
"@mapbox/geojson-extent" "^0.3.2" "@mapbox/geojson-extent" "^0.3.2"
"@mapbox/geojson-normalize" "0.0.1" "@mapbox/geojson-normalize" "0.0.1"
"@mapbox/geojsonhint" "3.0.0" "@mapbox/geojsonhint" "3.0.0"
"@mapbox/point-geometry" "0.1.0" "@mapbox/point-geometry" "0.1.0"
eslint-plugin-import "^2.19.1"
hat "0.0.3" hat "0.0.3"
lodash.isequal "^4.2.0" lodash.isequal "^4.2.0"
xtend "^4.0.1" xtend "^4.0.1"
@ -1815,10 +1814,10 @@
dependencies: dependencies:
defer-to-connect "^1.0.1" defer-to-connect "^1.0.1"
"@tmcw/togeojson@^4.0.0": "@tmcw/togeojson@^4.3.0":
version "4.0.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-4.0.0.tgz#ee111e4e5b2b5d498d43e27a6c9bf546ce4041cc" resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-4.4.0.tgz#88d2dfd10ddecf204d180a344da88447ab1f6bec"
integrity sha512-JZXGC1myBPPYb/moq03cYPtErqZKzVR74Cv9C85IuqATHCxHCNOxw4D45vVcYHQnnxG2TQTIR+igzpbFiu/O6Q== integrity sha512-QA0Yk/i1D2tmbuQ/fPtB+opKxEvRagdNG0pfk6JbVd6oiI90XG0SZPQwuPlORRZ5SQFT7Kv6nQ9ptDJuTXC3kA==
"@turf/bbox@4.7.3": "@turf/bbox@4.7.3":
version "4.7.3" version "4.7.3"
@ -1905,11 +1904,6 @@
"@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*" "@types/istanbul-lib-report" "*"
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/minimatch@*": "@types/minimatch@*":
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -2502,14 +2496,6 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
array.prototype.flat@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.0-next.1"
array.prototype.flatmap@^1.2.3: array.prototype.flatmap@^1.2.3:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
@ -3796,11 +3782,6 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
contains-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
content-disposition@0.5.3, content-disposition@^0.5.2: content-disposition@0.5.3, content-disposition@^0.5.2:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
@ -4268,7 +4249,7 @@ debounce@^1.2.0:
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
version "2.6.9" version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@ -4635,14 +4616,6 @@ dns-txt@^2.0.2:
dependencies: dependencies:
buffer-indexof "^1.0.0" buffer-indexof "^1.0.0"
doctrine@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
dependencies:
esutils "^2.0.2"
isarray "^1.0.0"
doctrine@^2.1.0: doctrine@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@ -5012,41 +4985,6 @@ eslint-config-prettier@^6.11.0:
dependencies: dependencies:
get-stdin "^6.0.0" get-stdin "^6.0.0"
eslint-import-resolver-node@^0.3.3:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==
dependencies:
debug "^2.6.9"
resolve "^1.13.1"
eslint-module-utils@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==
dependencies:
debug "^2.6.9"
pkg-dir "^2.0.0"
eslint-plugin-import@^2.19.1:
version "2.22.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e"
integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==
dependencies:
array-includes "^3.1.1"
array.prototype.flat "^1.2.3"
contains-path "^0.1.0"
debug "^2.6.9"
doctrine "1.5.0"
eslint-import-resolver-node "^0.3.3"
eslint-module-utils "^2.6.0"
has "^1.0.3"
minimatch "^3.0.4"
object.values "^1.1.1"
read-pkg-up "^2.0.0"
resolve "^1.17.0"
tsconfig-paths "^3.9.0"
eslint-plugin-prettier@^3.1.3: eslint-plugin-prettier@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
@ -5635,7 +5573,7 @@ find-up@^1.0.0:
path-exists "^2.0.0" path-exists "^2.0.0"
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
find-up@^2.0.0, find-up@^2.1.0: find-up@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
@ -7633,16 +7571,6 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
strip-bom "^2.0.0" strip-bom "^2.0.0"
load-json-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
dependencies:
graceful-fs "^4.1.2"
parse-json "^2.2.0"
pify "^2.0.0"
strip-bom "^3.0.0"
load-json-file@^5.2.0: load-json-file@^5.2.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3"
@ -7996,10 +7924,10 @@ map-visit@^1.0.0:
dependencies: dependencies:
object-visit "^1.0.0" object-visit "^1.0.0"
mapbox-gl@^1.11.1: mapbox-gl@^1.3.0:
version "1.11.1" version "1.13.1"
resolved "https://registry.yarnpkg.com/mapbox-gl/-/mapbox-gl-1.11.1.tgz#063e72b591d506b6b1f483df563e3e48cd0a971b" resolved "https://registry.yarnpkg.com/mapbox-gl/-/mapbox-gl-1.13.1.tgz#322efe75ab4c764fc4c776da1506aad58d5a5b9d"
integrity sha512-UjXpPUTUzHTLfhl5dLefwV3Jgu7DN9phpn8RnnkQVe1sOXfVYMS5Vhjn225krhzRc7xnKIBHxLyu0rHZGyeXuQ== integrity sha512-GSyubcoSF5MyaP8z+DasLu5v7KmDK2pp4S5+VQ5WdVQUOaAqQY4jwl4JpcdNho3uWm2bIKs7x1l7q3ynGmW60g==
dependencies: dependencies:
"@mapbox/geojson-rewind" "^0.5.0" "@mapbox/geojson-rewind" "^0.5.0"
"@mapbox/geojson-types" "^1.0.2" "@mapbox/geojson-types" "^1.0.2"
@ -9458,13 +9386,6 @@ path-type@^1.0.0:
pify "^2.0.0" pify "^2.0.0"
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
dependencies:
pify "^2.0.0"
path-type@^3.0.0: path-type@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@ -9538,13 +9459,6 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
pkg-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
dependencies:
find-up "^2.1.0"
pkg-dir@^3.0.0: pkg-dir@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@ -10601,10 +10515,10 @@ react-mapbox-gl-draw@^2.0.4:
resolved "https://registry.yarnpkg.com/react-mapbox-gl-draw/-/react-mapbox-gl-draw-2.0.4.tgz#476d70a6efc07c329fa61c11022bcdab60ac4b91" resolved "https://registry.yarnpkg.com/react-mapbox-gl-draw/-/react-mapbox-gl-draw-2.0.4.tgz#476d70a6efc07c329fa61c11022bcdab60ac4b91"
integrity sha512-oaBdIlyu+g7PhLUvwnCsl/wvu+5tGB9I3RLjcrYLt6U1sUMzQJqplKtVxXRv9TZqRdNaAU5qNOP+dRs55QKjsA== integrity sha512-oaBdIlyu+g7PhLUvwnCsl/wvu+5tGB9I3RLjcrYLt6U1sUMzQJqplKtVxXRv9TZqRdNaAU5qNOP+dRs55QKjsA==
react-mapbox-gl@^4.8.6: react-mapbox-gl@^5.1.1:
version "4.8.6" version "5.1.1"
resolved "https://registry.yarnpkg.com/react-mapbox-gl/-/react-mapbox-gl-4.8.6.tgz#c3841bac882a297f60efce50cac4060e3a1c3f81" resolved "https://registry.yarnpkg.com/react-mapbox-gl/-/react-mapbox-gl-5.1.1.tgz#49e1ddf441c3ff9406d10ccd577ac5448d51584c"
integrity sha512-e6rJ4GFye2AIu10I0a0OfleIWYkigIMIysoSKCA4Wg5YHa52JRHq2F3x0c0cnhqfz1txnUhXUbkx2qqs8B6kKQ== integrity sha512-8vGldFQf7pW8T5ZV2OOhwXoaBvfigB2F7dnhzaZ/bD5/KJzP9zprMbn0xMX95W3eqbKzGGHnwyD5DyTTwR6wGw==
dependencies: dependencies:
"@turf/bbox" "4.7.3" "@turf/bbox" "4.7.3"
"@turf/helpers" "4.7.3" "@turf/helpers" "4.7.3"
@ -10659,14 +10573,6 @@ read-pkg-up@^1.0.1:
find-up "^1.0.0" find-up "^1.0.0"
read-pkg "^1.0.0" read-pkg "^1.0.0"
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
dependencies:
find-up "^2.0.0"
read-pkg "^2.0.0"
read-pkg-up@^7.0.1: read-pkg-up@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
@ -10685,15 +10591,6 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2" normalize-package-data "^2.3.2"
path-type "^1.0.0" path-type "^1.0.0"
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
dependencies:
load-json-file "^2.0.0"
normalize-package-data "^2.3.2"
path-type "^2.0.0"
read-pkg@^5.2.0: read-pkg@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
@ -10956,7 +10853,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1: resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.8.1:
version "1.17.0" version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@ -12360,16 +12257,6 @@ ts-pnp@^1.1.6:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
tsconfig-paths@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==
dependencies:
"@types/json5" "^0.0.29"
json5 "^1.0.1"
minimist "^1.2.0"
strip-bom "^3.0.0"
tslib@^1, tslib@^1.8.1: tslib@^1, tslib@^1.8.1:
version "1.13.0" version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"