diff --git a/app/controllers/api/v2/graphql_controller.rb b/app/controllers/api/v2/graphql_controller.rb index f25bf041d..34b564012 100644 --- a/app/controllers/api/v2/graphql_controller.rb +++ b/app/controllers/api/v2/graphql_controller.rb @@ -8,11 +8,11 @@ class API::V2::GraphqlController < API::V2::BaseController operation_name: params[:operationName]) render json: result - rescue => e + rescue => exception if Rails.env.development? - handle_error_in_development e + handle_error_in_development(exception) else - raise e + handle_error_in_production(exception) end end @@ -21,7 +21,12 @@ class API::V2::GraphqlController < API::V2::BaseController def process_action(*args) super rescue ActionDispatch::Http::Parameters::ParseError => exception - render status: 400, json: { errors: [{ message: exception.cause.message }] } + render json: { + errors: [ + { message: exception.cause.message } + ], + data: nil + }, status: 400 end # Handle form data, JSON body, or a blank value @@ -42,10 +47,32 @@ class API::V2::GraphqlController < API::V2::BaseController end end - def handle_error_in_development(e) - logger.error e.message - logger.error e.backtrace.join("\n") + def handle_error_in_development(exception) + logger.error exception.message + logger.error exception.backtrace.join("\n") - render json: { error: { message: e.message, backtrace: e.backtrace }, data: {} }, status: 500 + render json: { + errors: [ + { message: exception.message, backtrace: exception.backtrace } + ], + data: nil + }, status: 500 + end + + def handle_error_in_production(exception) + id = SecureRandom.uuid + Raven.capture_exception(exception, extra: { exception_id: id }) + + render json: { + errors: [ + { + message: "Internal Server Error", + extensions: { + exception: { id: id } + } + } + ], + data: nil + }, status: 500 end end diff --git a/app/controllers/new_administrateur/procedures_controller.rb b/app/controllers/new_administrateur/procedures_controller.rb index 29cf33abc..2f250ae2e 100644 --- a/app/controllers/new_administrateur/procedures_controller.rb +++ b/app/controllers/new_administrateur/procedures_controller.rb @@ -1,6 +1,6 @@ module NewAdministrateur class ProceduresController < AdministrateurController - before_action :retrieve_procedure, only: [:champs, :annotations, :edit, :monavis, :update_monavis, :jeton, :update_jeton, :publication, :publish, :transfert, :allow_expert_review] + before_action :retrieve_procedure, only: [:champs, :annotations, :edit, :monavis, :update_monavis, :jeton, :update_jeton, :publication, :publish, :transfert, :allow_expert_review, :invited_expert_list] before_action :procedure_locked?, only: [:champs, :annotations] ITEMS_PER_PAGE = 25 @@ -185,6 +185,10 @@ module NewAdministrateur end end + def invited_expert_list + @invited_expert_emails = Avis.invited_expert_emails(@procedure) + end + private def apercu_tab diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 4ed32c7ab..34ba29f5b 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -19,7 +19,7 @@ module Types end def dossier(number:) - Dossier.for_api_v2.find(number) + Dossier.state_not_brouillon.for_api_v2.find(number) rescue => e raise GraphQL::ExecutionError.new(e.message, extensions: { code: :not_found }) end diff --git a/app/models/avis.rb b/app/models/avis.rb index ca532fefd..51da59ce1 100644 --- a/app/models/avis.rb +++ b/app/models/avis.rb @@ -56,6 +56,16 @@ class Avis < ApplicationRecord Avis.find_by(id: avis_id)&.email == email end + def self.invited_expert_emails(procedure) + Avis + .joins(dossier: :revision) + .left_joins(instructeur: :user) + .where(procedure_revisions: { procedure_id: procedure }) + .map(&:email_to_display) + .uniq + .sort + end + def spreadsheet_columns [ ['Dossier ID', dossier_id.to_s], diff --git a/app/views/new_administrateur/procedures/invited_expert_list.html.haml b/app/views/new_administrateur/procedures/invited_expert_list.html.haml new file mode 100644 index 000000000..6c1b82e64 --- /dev/null +++ b/app/views/new_administrateur/procedures/invited_expert_list.html.haml @@ -0,0 +1,18 @@ +.container + %h1.mt-2 Experts invités sur #{@procedure.libelle} + - if @invited_expert_emails.present? + %table.table.mt-2 + %thead + %tr + %th Liste des experts + %tbody + - @invited_expert_emails.each do |expert| + %tr + %td + %span.icon.person + = expert + - else + .blank-tab + %h2.empty-text Aucun expert invité pour le moment. + %p.empty-text-details Les instructeurs de cette démarche n'ont pas encore fait appel aux experts. + diff --git a/app/views/new_administrateur/procedures/show.html.haml b/app/views/new_administrateur/procedures/show.html.haml index 21ae54a9d..834fe6adf 100644 --- a/app/views/new_administrateur/procedures/show.html.haml +++ b/app/views/new_administrateur/procedures/show.html.haml @@ -156,6 +156,20 @@ .card-admin-action = link_to "#{@procedure.allow_expert_review? ? 'Désactiver' : 'Activer'}", allow_expert_review_admin_procedure_path(@procedure), method: :put, class: 'button' + - if @procedure.allow_expert_review? + .card-admin + %div + %span.icon.preview + %p.card-admin-status-todo À voir + + %div + %p.card-admin-title Liste des experts + %p.card-admin-subtitle Liste des experts invités par les instructeurs + + .card-admin-action + = link_to "Voir", invited_expert_list_admin_procedure_path(@procedure), class: 'button' + + .card-admin %div %span.icon.clock diff --git a/config/routes.rb b/config/routes.rb index cfa066dc7..f9a9f8131 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -379,6 +379,7 @@ Rails.application.routes.draw do get 'jeton' patch 'update_jeton' put :allow_expert_review + get 'invited_expert_list' end get 'publication' => 'procedures#publication', as: :publication diff --git a/spec/models/avis_spec.rb b/spec/models/avis_spec.rb index 7f04bcf58..37c156cc5 100644 --- a/spec/models/avis_spec.rb +++ b/spec/models/avis_spec.rb @@ -200,4 +200,46 @@ RSpec.describe Avis, type: :model do end end end + + describe '#invited_expert_emails' do + let!(:procedure) { create(:procedure, :published) } + + subject { Avis.invited_expert_emails(procedure) } + + context 'when there is one dossier' do + let!(:dossier) { create(:dossier, procedure: procedure) } + + context 'when a procedure has one avis and unknown instructeur' do + let!(:avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') } + + it { is_expected.to eq(['expert@expert.com']) } + end + + context 'when a procedure has one avis and known instructeur' do + let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: 'expert@expert.com')) } + + it { is_expected.to eq(['expert@expert.com']) } + end + + context 'when a dossier has 2 avis from the same expert' do + let!(:avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') } + let!(:avis2) { create(:avis, dossier: dossier, email: 'expert@expert.com') } + + it { is_expected.to eq(['expert@expert.com']) } + end + end + + context 'when there are two dossiers' do + let!(:dossier) { create(:dossier, procedure: procedure) } + let!(:dossier2) { create(:dossier, procedure: procedure) } + + context 'and each one has an avis from 3 different experts' do + let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '2_expert@expert.com')) } + let!(:unaffected_avis) { create(:avis, dossier: dossier2, email: '3_expert@expert.com') } + let!(:unaffected_avis2) { create(:avis, dossier: dossier2, email: '1_expert@expert.com') } + + it { is_expected.to eq(['1_expert@expert.com', '2_expert@expert.com', '3_expert@expert.com']) } + end + end + end end diff --git a/spec/views/new_administrateur/procedures/invited_expert_list.html.haml_spec.rb b/spec/views/new_administrateur/procedures/invited_expert_list.html.haml_spec.rb new file mode 100644 index 000000000..ff575ed94 --- /dev/null +++ b/spec/views/new_administrateur/procedures/invited_expert_list.html.haml_spec.rb @@ -0,0 +1,41 @@ +describe 'new_administrateur/procedures/invited_expert_list.html.haml', type: :view do + let!(:procedure) { create(:procedure, :published) } + + before do + assign(:procedure, procedure) + assign(:procedure_lien, commencer_url(path: procedure.path)) + allow(view).to receive(:current_administrateur).and_return(procedure.administrateurs.first) + end + + subject { render } + + context 'when the procedure has 0 avis' do + let!(:dossier) { create(:dossier, procedure: procedure) } + before do + @invited_expert_emails = Avis.invited_expert_emails(procedure) + subject + end + + it 'has 0 experts into the page' do + expect(@invited_expert_emails.count).to eq(0) + expect(@invited_expert_emails).to eq([]) + end + end + + context 'when the procedure has 3 avis from 2 experts and 1 unasigned' do + let!(:dossier) { create(:dossier, procedure: procedure) } + let!(:avis) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '1_expert@expert.com')) } + let!(:avis2) { create(:avis, dossier: dossier, instructeur: create(:instructeur, email: '2_expert@expert.com')) } + let!(:unasigned_avis) { create(:avis, dossier: dossier, email: 'expert@expert.com') } + + before do + @invited_expert_emails = Avis.invited_expert_emails(procedure) + subject + end + + it 'has 3 experts and match array' do + expect(@invited_expert_emails.count).to eq(3) + expect(@invited_expert_emails).to eq(['1_expert@expert.com', '2_expert@expert.com', 'expert@expert.com']) + end + end +end diff --git a/spec/views/new_administrateur/procedures/show.html.haml_spec.rb b/spec/views/new_administrateur/procedures/show.html.haml_spec.rb index f321f6e3b..2f8dd2c8e 100644 --- a/spec/views/new_administrateur/procedures/show.html.haml_spec.rb +++ b/spec/views/new_administrateur/procedures/show.html.haml_spec.rb @@ -44,13 +44,35 @@ describe 'new_administrateur/procedures/show.html.haml', type: :view do procedure.publish! procedure.close! procedure.reload - render end describe 'Re-enable button is visible' do + before do + render + end + it { expect(rendered).not_to have_css('#close-procedure-link') } it { expect(rendered).to have_css('#publish-procedure-link') } it { expect(rendered).to have_content('Réactiver') } end + + describe 'When procedure.allow_expert_review is true, the expert list card must be visible' do + before do + render + end + it { expect(procedure.allow_expert_review).to be_truthy } + it { expect(rendered).to have_content('Liste des experts invités par les instructeurs') } + end + + describe 'When procedure.allow_expert_review is false, the expert list card must not be visible' do + before do + procedure.update!(allow_expert_review: false) + procedure.reload + render + end + + it { expect(procedure.allow_expert_review).to be_falsy } + it { expect(rendered).not_to have_content('Liste des experts invités par les instructeurs') } + end end end