diff --git a/app/assets/images/icons/download.svg b/app/assets/images/icons/download.svg new file mode 100644 index 000000000..70b8ac752 --- /dev/null +++ b/app/assets/images/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/justificatif.svg b/app/assets/images/icons/justificatif.svg new file mode 100644 index 000000000..3569c189a --- /dev/null +++ b/app/assets/images/icons/justificatif.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/preview.svg b/app/assets/images/icons/preview.svg new file mode 100644 index 000000000..4eb692353 --- /dev/null +++ b/app/assets/images/icons/preview.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/new_design/avis.scss b/app/assets/stylesheets/new_design/avis.scss index 42e7c354d..fa8876b1d 100644 --- a/app/assets/stylesheets/new_design/avis.scss +++ b/app/assets/stylesheets/new_design/avis.scss @@ -33,6 +33,8 @@ } .ask-avis { + margin-bottom: 2 * $default-padding; + .avis-notice { font-size: 14px; color: $grey; @@ -67,7 +69,7 @@ .confidentiel-explanation { font-size: 14px; color: $grey; - margin-top: -$default-padding; + margin-top: - $default-padding; margin-bottom: 2 * $default-padding; } } diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index 6fe9cb876..a36b7a719 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -206,7 +206,6 @@ border-bottom: 1px solid $border-grey; font-size: 12px; min-width: 300px; - cursor: pointer; &.selected { cursor: default; @@ -217,8 +216,9 @@ } &.selected, - &:hover { + &:hover:not(.inactive) { background: $light-grey; + cursor: pointer; } &.danger { @@ -267,8 +267,9 @@ margin-bottom: $default-spacer; } - &.with-top-border { - border-top: 1px solid $grey; + p + h4, + p + p, { + margin-top: $default-spacer; } } diff --git a/app/assets/stylesheets/new_design/icons.scss b/app/assets/stylesheets/new_design/icons.scss index 20a290c7d..5155ee6d8 100644 --- a/app/assets/stylesheets/new_design/icons.scss +++ b/app/assets/stylesheets/new_design/icons.scss @@ -63,7 +63,15 @@ background-image: image-url("icons/attachment.svg"); } + &.preview { + background-image: image-url("icons/preview.svg"); + } + &.download { + background-image: image-url("icons/download.svg"); + } + + &.download-white { background-image: image-url("icons/download-white.svg"); } @@ -71,6 +79,11 @@ background-image: image-url("icons/lock.svg"); } + &.justificatif { + background-image: image-url("icons/justificatif.svg"); + } + + &.printer { background-image: image-url("icons/printer.svg"); } diff --git a/app/assets/stylesheets/new_design/patron.scss b/app/assets/stylesheets/new_design/patron.scss index ae160adab..cb337bce2 100644 --- a/app/assets/stylesheets/new_design/patron.scss +++ b/app/assets/stylesheets/new_design/patron.scss @@ -5,7 +5,7 @@ margin-bottom: 20px; } - .icon.download { + .icon.download-white { background-color: $blue; box-shadow: 0px 0px 1px 2px $blue; } diff --git a/app/views/instructeurs/dossiers/_state_button.html.haml b/app/views/instructeurs/dossiers/_state_button.html.haml index 9574d6987..cbf3c2251 100644 --- a/app/views/instructeurs/dossiers/_state_button.html.haml +++ b/app/views/instructeurs/dossiers/_state_button.html.haml @@ -1,88 +1,107 @@ -- if dossier.en_construction? || dossier.en_instruction? || dossier.accepte? - %span.dropdown - %button.button.primary.dropdown-button - = dossier_display_state dossier - .dropdown-content.fade-in-down - %ul.dropdown-items - - if dossier.en_construction? - %li.selected - %span.icon.edit - .dropdown-description - %h4 En construction - Vous permettez à l'usager de modifier ses réponses au formulaire - %li - = link_to passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmez-vous le passage en instruction de ce dossier ?" } do - %span.icon.in-progress - .dropdown-description - %h4 Passer en instruction - L'usager ne pourra plus modifier le formulaire +%span.dropdown + -# Dropdown button title + %button.button.primary.dropdown-button{ class: button_or_label_class(dossier) } + = dossier_display_state dossier - - if dossier.accepte? - %li - = link_to repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmez-vous le passage en instruction de ce dossier ?" } do - %span.icon.in-progress - .dropdown-description - %h4 Passer en instruction - L'usager ne pourra plus modifier le formulaire - - if dossier.en_instruction? - %li - = link_to repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Confirmez-vous le passage en construction de ce dossier ?" } do - %span.icon.edit - .dropdown-description - %h4 Repasser en construction - Vous permettrez à l'usager de modifier ses réponses au formulaire - %li.selected + -# Dropdown content + .dropdown-content.fade-in-down + + - if dossier.en_construction? + -# ------------------------------------------------------ + -# EN CONSTRUCTION + -# ------------------------------------------------------ + %ul.dropdown-items + + %li.selected + %span.icon.edit + .dropdown-description + %h4 En construction + Vous permettez à l'usager de modifier ses réponses au formulaire + + %li + = link_to passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmez-vous le passage en instruction de ce dossier ?" } do %span.icon.in-progress .dropdown-description - %h4 En instruction - L'usager ne peut modifier son dossier pendant l'instruction - %li - %a{ href: '#', onclick: "DS.showMotivation(event, 'accept');" } - %span.icon.accept - .dropdown-description - %h4 Accepter - L'usager sera notifié que son dossier a été accepté - %li - %a{ href: '#', onclick: "DS.showMotivation(event, 'without-continuation');" } - %span.icon.without-continuation - .dropdown-description - %h4 Classer sans suite - L'usager sera notifié que son dossier a été classé sans suite - %li - %a{ href: '#', onclick: "DS.showMotivation(event, 'refuse');" } - %span.icon.refuse - .dropdown-description - %h4 Refuser - L'usager sera notifié que son dossier a été refusé - = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Accepter le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } - = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Classer le dossier sans suite', placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } - = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Refuser le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + %h4 Passer en instruction + L'usager ne pourra plus modifier le formulaire -- else - - if dossier.motivation.present? || dossier.attestation.present? - %span.dropdown - %button.button.dropdown-button{ class: button_or_label_class(dossier) } - = dossier_display_state(dossier, lower: true) - .dropdown-content.fade-in-down.terminated + - elsif dossier.en_instruction? + -# ------------------------------------------------------ + -# EN INSTRUCTION + -# ------------------------------------------------------ + %ul.dropdown-items + + %li + = link_to repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Confirmez-vous le passage en construction de ce dossier ?" } do + %span.icon.edit + .dropdown-description + %h4 Repasser en construction + Vous permettrez à l'usager de modifier ses réponses au formulaire + + %li.selected + %span.icon.in-progress + .dropdown-description + %h4 En instruction + L'usager ne peut modifier son dossier pendant l'instruction + + %li + %a{ href: '#', onclick: "DS.showMotivation(event, 'accept');" } + %span.icon.accept + .dropdown-description + %h4 Accepter + L'usager sera notifié que son dossier a été accepté + + %li + %a{ href: '#', onclick: "DS.showMotivation(event, 'without-continuation');" } + %span.icon.without-continuation + .dropdown-description + %h4 Classer sans suite + L'usager sera notifié que son dossier a été classé sans suite + + %li + %a{ href: '#', onclick: "DS.showMotivation(event, 'refuse');" } + %span.icon.refuse + .dropdown-description + %h4 Refuser + L'usager sera notifié que son dossier a été refusé + + = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Accepter le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } + + = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Classer le dossier sans suite', placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } + + = render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Refuser le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + + - elsif dossier.termine? + -# --------------------------------------------------- + -# TERMINÉ + -# --------------------------------------------------- + %ul.dropdown-items - if dossier.motivation.present? - %h4.title Motivation - %p.dossier-motivation= dossier.motivation - = render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier } + %li.inactive + %span.icon.info + .dropdown-description + %h4 Motivation + %p « #{dossier.motivation} » + + - if dossier.justificatif_motivation.attached? + %li.inactive + %span.icon.justificatif + .dropdown-description + %h4 Justificatif + %p Ce justificatif joint par l’instructeur a été envoyé au demandeur. + = render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier } - if dossier.attestation.present? - %h4.title Attestation - %p.attestation L'acceptation du dossier a envoyé automatiquement une attestation au demandeur - = link_to "Voir l'attestation", attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', class: 'button' + %li + = link_to attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener' do + %span.icon.preview + .dropdown-description + %h4 Voir l’attestation + %p Cette attestation a été envoyée automatiquement au demandeur. - - - if dossier.refuse? || dossier.sans_suite? - %ul.dropdown-items.with-top-border - %li - = link_to repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?" } do - %span.icon.in-progress - .dropdown-description - %h4 Repasser en instruction - L’usager sera notifié que son dossier est réexaminé. - - else - %span.label{ class: button_or_label_class(dossier) } - = dossier_display_state(dossier, lower: true) + %li + = link_to repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?" } do + %span.icon.in-progress + .dropdown-description + %h4 Repasser en instruction + L’usager sera notifié que son dossier est réexaminé. diff --git a/app/views/instructeurs/dossiers/_state_button_motivation.html.haml b/app/views/instructeurs/dossiers/_state_button_motivation.html.haml index d071c05b2..7376dceca 100644 --- a/app/views/instructeurs/dossiers/_state_button_motivation.html.haml +++ b/app/views/instructeurs/dossiers/_state_button_motivation.html.haml @@ -6,8 +6,11 @@ = form_tag(terminer_instructeur_dossier_path(dossier.procedure, dossier), remote: true, method: :post, class: 'form') do - if title == 'Accepter' = text_area :dossier, :motivation, class: 'motivation-text-area', placeholder: placeholder, required: false - %p.help - L'acceptation du dossier envoie automatiquement une attestation à l'usager. + - if dossier.procedure.attestation_template&.activated? + %p.help + L'acceptation du dossier envoie automatiquement + = link_to 'une attestation', apercu_attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', title: "Voir l'attestation qui sera envoyée à l'usager" + à l'usager. - unspecified_attestation_champs = dossier.unspecified_attestation_champs - if unspecified_attestation_champs.present? @@ -33,7 +36,5 @@ .hidden{ id: "justificatif_motivation_import_#{popup_class}" } = file_field :dossier, :justificatif_motivation, direct_upload: true .text-right - - if title == 'Accepter' && dossier.procedure.attestation_template&.activated? - = link_to "Voir l'attestation", apercu_attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', class: 'button', title: "Voir l'attestation qui sera envoyée au demandeur" %span.button{ onclick: 'DS.motivationCancel();' } Annuler = button_tag 'Valider la décision', name: :process_action, value: process_action, class: 'button primary', title: title, data: { confirm: confirm } diff --git a/app/views/instructeurs/shared/avis/_form.html.haml b/app/views/instructeurs/shared/avis/_form.html.haml index 15f317f8f..a23a30ae2 100644 --- a/app/views/instructeurs/shared/avis/_form.html.haml +++ b/app/views/instructeurs/shared/avis/_form.html.haml @@ -1,23 +1,23 @@ %section.ask-avis %h1.tab-title Inviter des personnes à donner leur avis - %p.avis-notice Les invités pourront consulter, donner un avis sur le dossier et contribuer au fil de messagerie, mais ils ne pourront pas le modifier. + %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, onchange: "javascript:DS.replaceSemicolonByComma(event);" = f.text_area :introduction, rows: 3, value: avis.introduction || 'Bonjour, merci de me donner votre avis sur ce dossier.', required: true + .flex.justify-between.align-baseline - if must_be_confidentiel %p.confidentiel.flex %span.icon.lock %span - Cet avis est confidentiel et n'est pas affiché aux autres experts consultés mais est visible par tous les instructeurs - .send-wrapper - = f.submit 'Demander un avis', class: 'button send' + 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 est' + = 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 - .send-wrapper - = f.submit 'Demander un avis', class: 'button send' + 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' diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index 1e297d8cc..a55189955 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -16,6 +16,7 @@ %span.icon.bubble %span.icon.attachment %span.icon.lock + %span.icon.justificatif %span.icon.printer %span.icon.account %span.icon.person @@ -29,7 +30,9 @@ %span.icon.help %span.icon.phone %span.icon.clock + %span.icon.preview %span.icon.download + %span.icon.download-white %span.icon.move-handle %span.icon.frown %span.icon.meh diff --git a/app/views/users/dossiers/show/_status_overview.html.haml b/app/views/users/dossiers/show/_status_overview.html.haml index 945b18e63..50de64eda 100644 --- a/app/views/users/dossiers/show/_status_overview.html.haml +++ b/app/views/users/dossiers/show/_status_overview.html.haml @@ -62,7 +62,7 @@ - if dossier.attestation.present? .action = link_to attestation_dossier_path(dossier), target: '_blank', rel: 'noopener', class: 'button primary' do - %span.icon.download + %span.icon.download-white Télécharger l’attestation diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 98cf72929..163109eac 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -145,6 +145,15 @@ FactoryBot.define do end end + trait :with_justificatif do + after(:create) do |dossier, _evaluator| + dossier.justificatif_motivation.attach( + io: StringIO.new('Hello World'), + filename: 'hello.txt' + ) + end + end + trait :with_all_champs do after(:create) do |dossier, _evaluator| dossier.champs = dossier.procedure.types_de_champ.map do |type_de_champ| diff --git a/spec/views/instructeur/dossiers/_state_button.html.haml_spec.rb b/spec/views/instructeur/dossiers/_state_button.html.haml_spec.rb new file mode 100644 index 000000000..048b8b7bb --- /dev/null +++ b/spec/views/instructeur/dossiers/_state_button.html.haml_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe 'instructeurs/dossiers/state_button.html.haml', type: :view do + include DossierHelper + + subject! do + render('instructeurs/dossiers/state_button.html.haml', dossier: dossier) + end + + matcher :have_dropdown_title do |expected_title| + match do |rendered| + expect(rendered).to have_selector('.dropdown .dropdown-button', text: expected_title) + end + end + + matcher :have_dropdown_items do |options| + match do |rendered| + expected_count = options[:count] || 1 + expect(rendered).to have_selector('ul.dropdown-items li', count: expected_count) + end + end + + matcher :have_dropdown_item do |expected_title, options = {}| + match do |rendered| + expected_href = options[:href] + if (expected_href.present?) + expect(rendered).to have_selector("ul.dropdown-items li a[href='#{expected_href}']", text: expected_title) + else + expect(rendered).to have_selector('ul.dropdown-items li', text: expected_title) + end + end + end + + context 'brouillon' do + # Currently the state button is not supposed to be displayed for brouillons. + # But better have a sane fallback than crashing. + let(:dossier) { create(:dossier) } + + it 'renders a dropdown' do + expect(rendered).to have_dropdown_title('Brouillon') + expect(rendered).to have_dropdown_items(count: 0) + end + end + + context 'en_contruction' do + let(:dossier) { create(:dossier, :en_construction) } + + it 'renders a dropdown' do + expect(rendered).to have_dropdown_title('En construction') + expect(rendered).to have_dropdown_items(count: 2) + expect(rendered).to have_dropdown_item('En construction') + expect(rendered).to have_dropdown_item('Passer en instruction', href: passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier)) + end + end + + context 'en_instruction' do + let(:dossier) { create(:dossier, :en_instruction) } + + it 'renders a dropdown' do + expect(rendered).to have_dropdown_title('En instruction') + expect(rendered).to have_dropdown_items(count: 5) + expect(rendered).to have_dropdown_item('Repasser en construction', href: repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier)) + expect(rendered).to have_dropdown_item('En instruction') + expect(rendered).to have_dropdown_item('Accepter') + expect(rendered).to have_dropdown_item('Classer sans suite') + expect(rendered).to have_dropdown_item('Refuser') + end + end + + shared_examples 'a dropdown for a closed state' do |state| + let(:dossier) { create :dossier, state } + + it 'renders a dropdown' do + expect(rendered).to have_dropdown_title(dossier_display_state(dossier)) + expect(rendered).to have_dropdown_items(count: 1) + expect(rendered).to have_dropdown_item('Repasser en instruction', href: repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier)) + end + + context 'with a motivation' do + let(:dossier) { create :dossier, state, :with_motivation } + + it 'displays the motivation text' do + expect(rendered).to have_dropdown_item('Motivation') + expect(rendered).to have_content(dossier.motivation) + end + end + + context 'with an attestation' do + let(:dossier) { create :dossier, state, :with_attestation } + + it 'provides a link to the attestation' do + expect(rendered).to have_dropdown_item('Voir l’attestation') + expect(rendered).to have_link(href: attestation_instructeur_dossier_path(dossier.procedure, dossier)) + end + end + + context 'with a justificatif' do + let(:dossier) do + dossier = create(:dossier, state, :with_justificatif) + dossier.justificatif_motivation.blob.update(metadata: dossier.justificatif_motivation.blob.metadata.merge(virus_scan_result: ActiveStorage::VirusScanner::SAFE)) + dossier + end + + it 'allows to download the justificatif' do + expect(rendered).to have_dropdown_item('Justificatif') + expect(rendered).to have_link(href: url_for(dossier.justificatif_motivation.attachment.blob)) + end + end + end + + context 'accepte' do + it_behaves_like 'a dropdown for a closed state', :accepte + end + + context 'refuse' do + it_behaves_like 'a dropdown for a closed state', :refuse + end + + context 'sans_suite' do + it_behaves_like 'a dropdown for a closed state', :sans_suite + end +end diff --git a/spec/views/instructeur/dossiers/_state_button_motivation.html.haml_spec.rb b/spec/views/instructeur/dossiers/_state_button_motivation.html.haml_spec.rb index 368cf13cc..e02e37d14 100644 --- a/spec/views/instructeur/dossiers/_state_button_motivation.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/_state_button_motivation.html.haml_spec.rb @@ -16,10 +16,10 @@ describe 'instructeurs/dossiers/state_button_motivation.html.haml', type: :view context 'with an attestation preview' do let(:dossier) { create :dossier, :accepte, :with_attestation } - it { expect(rendered).to have_text("Voir l'attestation") } + it { expect(rendered).to have_link(href: apercu_attestation_instructeur_dossier_path(dossier.procedure, dossier)) } end context 'without an attestation preview' do - it { expect(rendered).not_to have_text("Voir l'attestation") } + it { expect(rendered).not_to have_link(href: apercu_attestation_instructeur_dossier_path(dossier.procedure, dossier)) } end end