Merge pull request #8743 from demarches-simplifiees/ask-question-in-avis

[instructeurs] Je peux poser une question dans une demande d'avis
This commit is contained in:
Lisa Durand 2023-03-17 15:10:51 +00:00 committed by GitHub
commit 893ee5801e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 207 additions and 116 deletions

View file

@ -65,13 +65,6 @@
.lock { .lock {
margin-right: $default-spacer; margin-right: $default-spacer;
} }
.confidentiel-explanation {
font-size: 14px;
color: $dark-grey;
margin-top: - $default-padding;
margin-bottom: 2 * $default-padding;
}
} }
.list-avis { .list-avis {

View file

@ -4,6 +4,16 @@ module CreateAvisConcern
private private
def create_avis_from_params(dossier, instructeur_or_expert, confidentiel = false) def create_avis_from_params(dossier, instructeur_or_expert, confidentiel = false)
if create_avis_params[:emails].empty?
avis = Avis.new(create_avis_params)
errors = avis.errors
errors.add(:emails, :blank)
flash.alert = errors.full_message(:emails, errors[:emails].first)
return avis
end
confidentiel ||= create_avis_params[:confidentiel] confidentiel ||= create_avis_params[:confidentiel]
# Because of a limitation of the email_field rails helper, # Because of a limitation of the email_field rails helper,
# the :emails parameter is a 1-element array. # the :emails parameter is a 1-element array.
@ -33,7 +43,8 @@ module CreateAvisConcern
claimant: instructeur_or_expert, claimant: instructeur_or_expert,
dossier: dossier, dossier: dossier,
confidentiel: confidentiel, confidentiel: confidentiel,
experts_procedure: experts_procedure experts_procedure: experts_procedure,
question_label: create_avis_params[:question_label]
} }
end end
end end
@ -71,6 +82,6 @@ module CreateAvisConcern
end end
def create_avis_params def create_avis_params
params.require(:avis).permit(:introduction_file, :introduction, :confidentiel, :invite_linked_dossiers, :emails) params.require(:avis).permit(:introduction_file, :introduction, :confidentiel, :invite_linked_dossiers, :emails, :question_label)
end end
end end

View file

@ -179,7 +179,7 @@ module Experts
end end
def avis_params def avis_params
params.require(:avis).permit(:answer, :piece_justificative_file) params.require(:avis).permit(:answer, :piece_justificative_file, :question_answer)
end end
def commentaire_params def commentaire_params

View file

@ -177,6 +177,8 @@ type Avis {
id: ID! id: ID!
instructeur: Profile! @deprecated(reason: "Utilisez le champ `claimant` à la place.") instructeur: Profile! @deprecated(reason: "Utilisez le champ `claimant` à la place.")
question: String! question: String!
questionAnswer: Boolean
questionLabel: String
reponse: String reponse: String
} }

View file

@ -4,6 +4,8 @@ module Types
field :question, String, null: false, method: :introduction field :question, String, null: false, method: :introduction
field :reponse, String, null: true, method: :answer field :reponse, String, null: true, method: :answer
field :question_label, String, null: true
field :question_answer, Boolean, null: true
field :date_question, GraphQL::Types::ISO8601DateTime, null: false, method: :created_at field :date_question, GraphQL::Types::ISO8601DateTime, null: false, method: :created_at
field :date_reponse, GraphQL::Types::ISO8601DateTime, null: true, method: :updated_at field :date_reponse, GraphQL::Types::ISO8601DateTime, null: true, method: :updated_at

View file

@ -9,6 +9,7 @@
/* Verify README of each component to insert them in the expected order. */ /* Verify README of each component to insert them in the expected order. */
@import '@gouvfr/dsfr/dist/component/alert/alert.css'; @import '@gouvfr/dsfr/dist/component/alert/alert.css';
@import '@gouvfr/dsfr/dist/component/radio/radio.css'; @import '@gouvfr/dsfr/dist/component/radio/radio.css';
@import '@gouvfr/dsfr/dist/component/select/select.css';
@import '@gouvfr/dsfr/dist/component/toggle/toggle.css'; @import '@gouvfr/dsfr/dist/component/toggle/toggle.css';
@import '@gouvfr/dsfr/dist/component/badge/badge.css'; @import '@gouvfr/dsfr/dist/component/badge/badge.css';
@import '@gouvfr/dsfr/dist/component/breadcrumb/breadcrumb.css'; @import '@gouvfr/dsfr/dist/component/breadcrumb/breadcrumb.css';

View file

@ -8,6 +8,8 @@
# confidentiel :boolean default(FALSE), not null # confidentiel :boolean default(FALSE), not null
# email :string # email :string
# introduction :text # introduction :text
# question_answer :boolean
# question_label :string
# reminded_at :datetime # reminded_at :datetime
# revoked_at :datetime # revoked_at :datetime
# created_at :datetime not null # created_at :datetime not null
@ -41,6 +43,7 @@ class Avis < ApplicationRecord
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true
validates :claimant, presence: true validates :claimant, presence: true
validates :question_answer, presence: { on: :update, if: -> { question_label.present? } }
validates :piece_justificative_file, size: { less_than: FILE_MAX_SIZE } validates :piece_justificative_file, size: { less_than: FILE_MAX_SIZE }
validates :introduction_file, size: { less_than: FILE_MAX_SIZE } validates :introduction_file, size: { less_than: FILE_MAX_SIZE }
before_validation -> { sanitize_email(:email) } before_validation -> { sanitize_email(:email) }
@ -67,8 +70,10 @@ class Avis < ApplicationRecord
def spreadsheet_columns def spreadsheet_columns
[ [
['Dossier ID', dossier_id.to_s], ['Dossier ID', dossier_id.to_s],
['Question / Introduction', :introduction], ['Introduction', :introduction],
['Réponse', :answer], ['Réponse', :answer],
['Question', :question_label],
['Réponse oui/non', :question_answer],
['Créé le', :created_at], ['Créé le', :created_at],
['Répondu le', :updated_at], ['Répondu le', :updated_at],
['Instructeur', claimant&.email], ['Instructeur', claimant&.email],

View file

@ -1,6 +1,8 @@
class AvisSerializer < ActiveModel::Serializer class AvisSerializer < ActiveModel::Serializer
attributes :answer, attributes :answer,
:introduction, :introduction,
:question_label,
:question_answer,
:created_at, :created_at,
:answered_at :answered_at

View file

@ -211,6 +211,10 @@ end
def add_avis(pdf, avis) def add_avis(pdf, avis)
format_in_2_lines(pdf, "Avis de #{avis.email_to_display}#{avis.confidentiel? ? ' (confidentiel)' : ''}", format_in_2_lines(pdf, "Avis de #{avis.email_to_display}#{avis.confidentiel? ? ' (confidentiel)' : ''}",
avis.answer || 'En attente de réponse') avis.answer || 'En attente de réponse')
if avis.question_answer.present?
format_in_2_columns(pdf, "Réponse oui/non ", t("question_answer.#{avis.question_answer}", scope: 'helpers.label'))
end
end end
def add_etat_dossier(pdf, dossier) def add_etat_dossier(pdf, dossier)

View file

@ -15,8 +15,27 @@
= render Attachment::ShowComponent.new(attachment: @avis.introduction_file.attachment) = render Attachment::ShowComponent.new(attachment: @avis.introduction_file.attachment)
%br/ %br/
= render Attachment::DeleteFormComponent.new = render Attachment::DeleteFormComponent.new
= form_for @avis, url: expert_avis_path(@avis.procedure, @avis), html: { class: 'form', data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@avis) }, multipart: true } do |f| = form_for @avis, url: expert_avis_path(@avis.procedure, @avis), html: { data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@avis) }, multipart: true } do |f|
= f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true
- if @avis.question_label
.fr-form-group
%fieldset.fr-fieldset.fr-fieldset--inline
%legend#radio-inline-legend.fr-fieldset__legend.fr-text--regular
= @avis.question_label
.fr-fieldset__content
.fr-radio-group
= f.radio_button :question_answer, true
= f.label :question_answer, 'oui', value: true, class: 'fr-label'
.fr-radio-group
= f.radio_button :question_answer, false
= f.label :question_answer, 'non', value: false, class: 'fr-label'
.fr-select-group
= f.text_area :answer, rows: 3, class: 'fr-input', placeholder: 'Votre avis', required: true
= render Attachment::EditComponent.new(attached_file: @avis.piece_justificative_file, view_as: :download) = render Attachment::EditComponent.new(attached_file: @avis.piece_justificative_file, view_as: :download)
.flex.justify-between.align-baseline .flex.justify-between.align-baseline
@ -32,7 +51,7 @@
= f.submit 'Envoyer votre avis', class: 'fr-btn' = f.submit 'Envoyer votre avis', class: 'fr-btn'
- if !@dossier.termine? - if !@dossier.termine?
= render partial: "experts/shared/avis/form", locals: { url: avis_expert_avis_path(@avis.procedure, @avis), linked_dossiers: @dossier.linked_dossiers_for(current_expert), must_be_confidentiel: @avis.confidentiel?, avis: @new_avis } = render partial: "experts/avis/shared/form", locals: { url: avis_expert_avis_path(@avis.procedure, @avis), linked_dossiers: @dossier.linked_dossiers_for(current_expert), must_be_confidentiel: @avis.confidentiel?, avis: @new_avis }
- if @dossier.avis_for_expert(current_expert).present? - if @dossier.avis_for_expert(current_expert).present?
= render partial: 'experts/shared/avis/list', locals: { avis: @dossier.avis_for_expert(current_expert), avis_seen_at: nil } = render partial: 'experts/avis/shared/list', locals: { avis: @dossier.avis_for_expert(current_expert), avis_seen_at: nil }

View file

@ -3,15 +3,17 @@
%h1.tab-title Inviter des personnes à donner leur avis %h1.tab-title Inviter des personnes à donner leur avis
%p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier. %p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier.
= render Attachment::DeleteFormComponent.new = render Attachment::DeleteFormComponent.new
= form_for avis, url: url, html: { class: 'form', multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@avis.dossier, :avis_by_expert) } } do |f| = form_for avis, url: url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@avis.dossier, :avis_by_expert) } } do |f|
= hidden_field_tag 'avis[emails]', nil = hidden_field_tag 'avis[emails]', nil
.fr-input-group
= react_component("ComboMultiple", = react_component("ComboMultiple",
options: [], selected: [], disabled: [], options: [], selected: [], disabled: [],
group: '.ask-avis', group: '.ask-avis',
name: 'emails', name: 'emails',
label: 'Emails', label: 'Emails',
acceptNewValues: true) acceptNewValues: true)
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true .fr-input-group
= f.text_area :introduction, rows: 3, class: 'fr-input', value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
%p.tab-title Ajouter une pièce jointe %p.tab-title Ajouter une pièce jointe
.form-group .form-group
= render Attachment::EditComponent.new(attached_file: avis.introduction_file) = render Attachment::EditComponent.new(attached_file: avis.introduction_file)
@ -29,9 +31,9 @@
- else - else
.confidentiel-wrapper .confidentiel-wrapper
= f.label :confidentiel, 'Cet avis sera ' = f.label :confidentiel, 'Cet avis sera ', class: 'fr-label'
= f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);" = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);", class: 'fr-input'
.confidentiel-explanation.hidden .confidentiel-explanation.hidden
Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs. Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
= f.submit 'Demander un avis', class: 'button primary send' = f.submit 'Demander un avis', class: 'fr-btn fr-mt-2w'

View file

@ -17,6 +17,8 @@
%span.date.fr-text--xs.fr-text-mention--grey{ class: highlight_if_unseen_class(avis_seen_at, avis.created_at) } %span.date.fr-text--xs.fr-text-mention--grey{ class: highlight_if_unseen_class(avis_seen_at, avis.created_at) }
= t('demande_envoyee_le', scope: 'views.shared.avis', date: l(avis.created_at, format: '%d/%m/%y à %H:%M')) = t('demande_envoyee_le', scope: 'views.shared.avis', date: l(avis.created_at, format: '%d/%m/%y à %H:%M'))
%p= avis.introduction %p= avis.introduction
- if avis.question_label
%p= avis.question_label
.answer.flex.align-start .answer.flex.align-start
%span.icon.bubble.avis-icon %span.icon.bubble.avis-icon
@ -35,4 +37,6 @@
- if avis.piece_justificative_file.attached? - if avis.piece_justificative_file.attached?
= render Attachment::ShowComponent.new(attachment: avis.piece_justificative_file.attachment) = render Attachment::ShowComponent.new(attachment: avis.piece_justificative_file.attachment)
.answer-body .answer-body
- if avis.question_answer
%p= t("question_answer.#{avis.question_answer}", scope: 'helpers.label')
= render SimpleFormatComponent.new(avis.answer, allow_a: false) = render SimpleFormatComponent.new(avis.answer, allow_a: false)

View file

@ -1,30 +0,0 @@
%section.ask-avis
%h1.tab-title Inviter des personnes à donner leur avis
%p.avis-notice Les invités pourront consulter le dossier, donner un avis et contribuer au fil de messagerie. Ils ne pourront pas modifier le dossier.
= form_for avis, url: url, html: { class: 'form' } do |f|
= f.email_field :emails, placeholder: 'Adresses email, séparées par des virgules', required: true, multiple: true, data: { controller: 'format', format: 'list' }
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
%p.tab-title Ajouter une pièce jointe
.form-group
= render Attachment::EditComponent.new(attached_file: avis.introduction_file)
- if linked_dossiers.present?
= f.check_box :invite_linked_dossiers, {}, true, false
= f.label :invite_linked_dossiers, t('helpers.label.invite_linked_dossiers', count: linked_dossiers.length, ids: linked_dossiers.map(&:id).to_sentence)
.flex.justify-between.align-baseline
- if must_be_confidentiel
%p.confidentiel.flex
%span.icon.lock
%span
Cet avis sera confidentiel : il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
- else
.confidentiel-wrapper
= f.label :confidentiel, 'Cet avis sera '
= f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);"
.confidentiel-explanation.hidden
Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
= f.submit 'Demander un avis', class: 'button primary send'

View file

@ -1,39 +0,0 @@
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
= render partial: 'header', locals: { avis: @avis, dossier: @dossier }
.container
%section.give-avis
%h1.tab-title Donner votre avis
%h2.claimant
Demandeur :
%span.email= @avis.claimant.email
%span.date Demande davis envoyée le #{l(@avis.created_at, format: '%d/%m/%y')}
%p.introduction= @avis.introduction
- if @avis.introduction_file.attached?
= render Attachment::ShowComponent.new(attachment: @avis.introduction_file.attachment)
%br/
= render Attachment::DeleteFormComponent.new
= form_for @avis, url: instructeur_avis_path(@avis.procedure, @avis), html: { class: 'form' } do |f|
= f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true
= render Attachment::EditComponent.new(attached_file: @avis.piece_justificative_file)
.flex.justify-between.align-baseline
%p.confidentiel.flex
- if @avis.confidentiel?
%span.icon.lock
%span
Cet avis est confidentiel et nest pas affiché aux autres experts consultés
- else
%span
Cet avis est partagé avec les autres experts
.send-wrapper
= f.submit 'Envoyer votre avis', class: 'fr-btn'
- if !@dossier.termine?
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_avis_path(@avis.procedure, @avis), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: @avis.confidentiel?, avis: @new_avis }
- if @dossier.avis_for(current_instructeur).present?
= render partial: 'instructeurs/shared/avis/list', locals: { avis: @dossier.avis_for(current_instructeur), avis_seen_at: nil }

View file

@ -9,8 +9,9 @@
Entrez les adresses email des experts à qui vous souhaitez demander un avis Entrez les adresses email des experts à qui vous souhaitez demander un avis
= render Attachment::DeleteFormComponent.new = render Attachment::DeleteFormComponent.new
= form_for avis, url: url, html: { class: 'form', multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@dossier, :avis_by_instructeur) } } do |f| = form_for avis, url: url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@dossier, :avis_by_instructeur) } } do |f|
= hidden_field_tag 'avis[emails]', nil = hidden_field_tag 'avis[emails]', nil
.fr-input-group
= react_component("ComboMultiple", = react_component("ComboMultiple",
options: @dossier.procedure.experts_require_administrateur_invitation ? @experts_emails : [], options: @dossier.procedure.experts_require_administrateur_invitation ? @experts_emails : [],
selected: [], disabled: [], selected: [], disabled: [],
@ -19,7 +20,17 @@
name: 'emails', name: 'emails',
describedby: 'avis-emails-description', describedby: 'avis-emails-description',
acceptNewValues: !@dossier.procedure.experts_require_administrateur_invitation) acceptNewValues: !@dossier.procedure.experts_require_administrateur_invitation)
= f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
.fr-input-group
= f.label :introduction, t('helpers.label.introduction'), class: 'fr-label'
= f.text_area :introduction, rows: 3, class: 'fr-input', value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true
.fr-input-group
= f.label :question_label, class: 'fr-label' do
= t('helpers.label.question_label')
%span.fr-hint-text
= t('helpers.label.question_label_hint')
= f.text_area :question_label, label: "question", class: 'fr-input'
%p.tab-title Ajouter une pièce jointe %p.tab-title Ajouter une pièce jointe
.form-group .form-group
= render Attachment::EditComponent.new(attached_file: avis.introduction_file) = render Attachment::EditComponent.new(attached_file: avis.introduction_file)
@ -37,9 +48,10 @@
- else - else
.confidentiel-wrapper .confidentiel-wrapper
= f.label :confidentiel, 'Cet avis sera ' = f.label :confidentiel, 'Cet avis sera ', class: 'fr-label'
= f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);" = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);", class: 'fr-select'
.confidentiel-explanation.hidden .confidentiel-explanation.hidden
%small
Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs. Il ne sera pas affiché aux autres experts consultés, mais sera visible par les instructeurs.
= f.submit 'Demander un avis', class: 'fr-btn' = f.submit 'Demander un avis', class: 'fr-btn fr-mt-2w'

View file

@ -17,6 +17,8 @@
%span.date.fr-text--xs.fr-text-mention--grey{ class: highlight_if_unseen_class(avis_seen_at, avis.created_at) } %span.date.fr-text--xs.fr-text-mention--grey{ class: highlight_if_unseen_class(avis_seen_at, avis.created_at) }
= t('demande_envoyee_le', scope: 'views.shared.avis', date: l(avis.created_at, format: '%d/%m/%y à %H:%M')) = t('demande_envoyee_le', scope: 'views.shared.avis', date: l(avis.created_at, format: '%d/%m/%y à %H:%M'))
%p= avis.introduction %p= avis.introduction
- if avis.question_label
%p= avis.question_label
.answer.flex.align-start .answer.flex.align-start
%span.icon.bubble.avis-icon %span.icon.bubble.avis-icon
@ -51,4 +53,7 @@
- if avis.piece_justificative_file.attached? - if avis.piece_justificative_file.attached?
= render Attachment::ShowComponent.new(attachment: avis.piece_justificative_file.attachment) = render Attachment::ShowComponent.new(attachment: avis.piece_justificative_file.attachment)
.answer-body .answer-body
- if avis.question_answer
%p= t("question_answer.#{avis.question_answer}", scope: 'helpers.label')
= render SimpleFormatComponent.new(avis.answer, allow_a: false) = render SimpleFormatComponent.new(avis.answer, allow_a: false)

View file

@ -5,12 +5,12 @@
.container .container
- if !@dossier.termine? - if !@dossier.termine?
- if @dossier.procedure.allow_expert_review - if @dossier.procedure.allow_expert_review
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: false, avis: @avis } = render partial: "instructeurs/avis/shared/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: false, avis: @avis }
- else - else
%p Cette démarche nautorise pas la demande davis à un expert. Veuillez contacter votre administrateur %p Cette démarche nautorise pas la demande davis à un expert. Veuillez contacter votre administrateur
- if @dossier.avis.present? - if @dossier.avis.present?
= render partial: 'instructeurs/shared/avis/list', locals: { avis: @dossier.avis, avis_seen_at: @avis_seen_at } = render partial: 'instructeurs/avis/shared/list', locals: { avis: @dossier.avis, avis_seen_at: @avis_seen_at }
- if @dossier.termine? && !@dossier.avis.present? - if @dossier.termine? && !@dossier.avis.present?
.blank-tab .blank-tab

View file

@ -0,0 +1,28 @@
en:
activerecord:
models:
avis: 'opinion'
attributes:
avis:
answer: "Answer"
claimant: Claimant
confidentiel: Confidential
question_answer: "Answer yes/no"
helpers:
label:
invite_linked_dossiers:
one: Invite also the expert on this linked file n° %{ids}
other: Invite also the expert on theses linked files n° %{ids}
revoke: Revoke opinion request
remind: Remind the expert
question_label: Ask a question to the expert
question_label_hint: (optional) the expert could answer by yes/no
introduction: Introduction message
question_answer:
true: 'yes'
false: 'no'
hint:
confidentiel: "This advice is not displayed to the others consulted experts"
confirmation:
revoke: "Would you like to revoke the opinion request to %{email} ?"
remind: "Would you like to remind %{email} ?"

View file

@ -7,6 +7,7 @@ fr:
answer: "Réponse" answer: "Réponse"
claimant: Demandeur claimant: Demandeur
confidentiel: confidentiel confidentiel: confidentiel
question_answer: "Réponse oui/non"
helpers: helpers:
label: label:
invite_linked_dossiers: invite_linked_dossiers:
@ -14,6 +15,12 @@ fr:
other: Inviter aussi lexpert sur les dossiers liés n° %{ids} other: Inviter aussi lexpert sur les dossiers liés n° %{ids}
revoke: Révoquer la demande davis revoke: Révoquer la demande davis
remind: Relancer lexpert remind: Relancer lexpert
question_label: Posez une question à l'expert
question_label_hint: (facultatif) l'expert pourra répondre par oui/non
introduction: Message d'introduction
question_answer:
true: oui
false: non
hint: hint:
confidentiel: "Cet avis nest pas affiché avec les autres experts consultés" confidentiel: "Cet avis nest pas affiché avec les autres experts consultés"
confirmation: confirmation:

View file

@ -0,0 +1,6 @@
class AddQuestionColumnsToAvis < ActiveRecord::Migration[6.1]
def change
add_column :avis, :question_label, :string
add_column :avis, :question_answer, :boolean
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2023_02_18_094119) do ActiveRecord::Schema.define(version: 2023_03_03_094613) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto" enable_extension "pgcrypto"
@ -164,6 +164,8 @@ ActiveRecord::Schema.define(version: 2023_02_18_094119) do
t.string "email" t.string "email"
t.bigint "experts_procedure_id" t.bigint "experts_procedure_id"
t.text "introduction" t.text "introduction"
t.boolean "question_answer"
t.string "question_label"
t.datetime "reminded_at" t.datetime "reminded_at"
t.datetime "revoked_at" t.datetime "revoked_at"
t.datetime "updated_at", null: false t.datetime "updated_at", null: false

View file

@ -344,10 +344,11 @@ describe Experts::AvisController, type: :controller do
let(:invite_linked_dossiers) { nil } let(:invite_linked_dossiers) { nil }
let(:introduction_file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') } let(:introduction_file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
let(:confidentiel) { false } let(:confidentiel) { false }
let(:question_label) { '' }
before do before do
Timecop.freeze(now) Timecop.freeze(now)
post :create_avis, params: { id: previous_avis.id, procedure_id:, avis: { emails:, introduction:, experts_procedure:, confidentiel:, invite_linked_dossiers:, introduction_file: } } post :create_avis, params: { id: previous_avis.id, procedure_id:, avis: { emails:, introduction:, experts_procedure:, confidentiel:, invite_linked_dossiers:, introduction_file:, question_label: } }
created_avis.reload created_avis.reload
end end
@ -373,6 +374,15 @@ describe Experts::AvisController, type: :controller do
end end
end end
context 'with a question' do
let(:question_label) { "question" }
it do
expect(created_avis.question_label).to eq('question')
expect(response).to redirect_to(instruction_expert_avis_path(previous_avis.procedure, previous_avis))
end
end
context 'ask review with attachment' do context 'ask review with attachment' do
let(:emails) { "[\"toto@totomail.com\"]" } let(:emails) { "[\"toto@totomail.com\"]" }

View file

@ -628,6 +628,17 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(dossier.last_avis_updated_at).to eq(nil) } it { expect(dossier.last_avis_updated_at).to eq(nil) }
end end
context "with no email" do
let(:emails) { "" }
before { subject }
it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq("Le champ « Emails » doit être rempli") }
it { expect { subject }.not_to change(Avis, :count) }
it { expect(dossier.last_avis_updated_at).to eq(nil) }
end
context 'with multiple emails' do context 'with multiple emails' do
let(:emails) { "[\"toto.fr\",\"titi@titimail.com\"]" } let(:emails) { "[\"toto.fr\",\"titi@titimail.com\"]" }

View file

@ -364,8 +364,10 @@ describe ProcedureExportService do
it 'should have headers' do it 'should have headers' do
expect(avis_sheet.headers).to eq([ expect(avis_sheet.headers).to eq([
"Dossier ID", "Dossier ID",
"Question / Introduction", "Introduction",
"Réponse", "Réponse",
"Question",
"Réponse oui/non",
"Créé le", "Créé le",
"Répondu le", "Répondu le",
"Instructeur", "Instructeur",

View file

@ -10,6 +10,7 @@ describe 'Inviting an expert:' do
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
let(:champ) { dossier.champs_public.first } let(:champ) { dossier.champs_public.first }
let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) } let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) }
let(:avis_with_question) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true, question_label: 'Question ?') }
context 'when I dont already have an account' do context 'when I dont already have an account' do
let(:password) { 'This is an expert password' } let(:password) { 'This is an expert password' }
@ -82,6 +83,37 @@ describe 'Inviting an expert:' do
expect(page).to have_text('1 avis donné') expect(page).to have_text('1 avis donné')
end end
scenario 'I can give a yes/no answer to a question' do
avis_with_question # create avis
login_as expert.user, scope: :user
visit expert_all_avis_path
expect(page).to have_text('1 avis à donner')
expect(page).to have_text('0 avis donnés')
click_on '1 avis à donner'
click_on avis.dossier.user.email
within('.tabs') { click_on 'Avis' }
expect(page).to have_text("Demandeur : #{avis.claimant.email}")
expect(page).to have_text('Question ?')
expect(page).to have_text('Cet avis est confidentiel')
# check validation
click_on 'Envoyer votre avis'
expect(page).to have_content('Le champ « Réponse oui/non » doit être rempli')
choose 'oui'
fill_in 'avis_answer', with: 'Ma réponse dexpert.'
click_on 'Envoyer votre avis'
expect(page).to have_content('Votre réponse est enregistrée')
expect(page).to have_content('Ma réponse dexpert.')
expect(page).to have_content('oui')
within('.breadcrumbs') { click_on 'Avis' }
expect(page).to have_text('1 avis donné')
end
# scenario 'I can invite other experts' do # scenario 'I can invite other experts' do
# end # end

View file

@ -1,7 +1,7 @@
describe 'instructeurs/shared/avis/_list.html.haml', type: :view do describe 'instructeurs/avis/shared/_list.html.haml', type: :view do
before { view.extend DossierHelper } before { view.extend DossierHelper }
subject { render 'instructeurs/shared/avis/list.html.haml', avis: avis, avis_seen_at: seen_at, current_instructeur: instructeur } subject { render 'instructeurs/avis/shared/list.html.haml', avis: avis, avis_seen_at: seen_at, current_instructeur: instructeur }
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:instructeur2) { create(:instructeur) } let(:instructeur2) { create(:instructeur) }