diff --git a/app/assets/images/header/logo-ds-narrow.svg b/app/assets/images/header/logo-ds-narrow.svg index eda8aa5eb..437c271ee 100644 --- a/app/assets/images/header/logo-ds-narrow.svg +++ b/app/assets/images/header/logo-ds-narrow.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/assets/images/header/logo-ds-wide.svg b/app/assets/images/header/logo-ds-wide.svg new file mode 100644 index 000000000..3fb67e18a --- /dev/null +++ b/app/assets/images/header/logo-ds-wide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/header/logo-ds.svg b/app/assets/images/header/logo-ds.svg index 3fb67e18a..eda8aa5eb 100644 --- a/app/assets/images/header/logo-ds.svg +++ b/app/assets/images/header/logo-ds.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/assets/images/icons/clock.svg b/app/assets/images/icons/clock.svg new file mode 100644 index 000000000..b705ba219 --- /dev/null +++ b/app/assets/images/icons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/help.svg b/app/assets/images/icons/help.svg new file mode 100644 index 000000000..1248f48c1 --- /dev/null +++ b/app/assets/images/icons/help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/phone.svg b/app/assets/images/icons/phone.svg new file mode 100644 index 000000000..7ffb850f1 --- /dev/null +++ b/app/assets/images/icons/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/trash.svg b/app/assets/images/icons/trash.svg index ea3e1ca31..1c152d84f 100644 --- a/app/assets/images/icons/trash.svg +++ b/app/assets/images/icons/trash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index 8597e720a..b228b8cc6 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -56,13 +56,13 @@ border-color: $border-grey; background-color: #FFFFFF; - &:hover { + &:hover:not(:disabled) { color: #FFFFFF; border-color: $medium-red; background-color: $medium-red; > .icon { - filter: brightness(100); + filter: contrast(0) brightness(100); } } } @@ -232,7 +232,7 @@ flex-shrink: 0; } - div { + .dropdown-description { padding-left: $default-spacer; } } diff --git a/app/assets/stylesheets/new_design/help_dropdown.scss b/app/assets/stylesheets/new_design/help_dropdown.scss new file mode 100644 index 000000000..01d8bd70c --- /dev/null +++ b/app/assets/stylesheets/new_design/help_dropdown.scss @@ -0,0 +1,51 @@ +@import "colors"; +@import "constants"; + +.help-dropdown { + .dropdown-content { + width: 340px; + } + + .dropdown-description { + font-size: 14px; + } +} + +h4.help-dropdown-title { + font-size: 16px; + color: $blue; +} + +.dropdown-items li.help-dropdown-service { + cursor: default; + + &:hover { + background: inherit; + } + + a { + display: inline; + color: $blue; + } +} + +.help-dropdown-service-action { + margin-top: $default-padding; + margin-bottom: $default-spacer; +} + +.help-dropdown-service-item { + margin-top: $default-spacer; + line-height: 18px; + + .icon { + vertical-align: middle; + margin-right: 5px; + + &.clock { + filter: contrast(0) brightness(120%); + vertical-align: -4px; + } + } +} + diff --git a/app/assets/stylesheets/new_design/icons.scss b/app/assets/stylesheets/new_design/icons.scss index fd3b8497c..d5623b221 100644 --- a/app/assets/stylesheets/new_design/icons.scss +++ b/app/assets/stylesheets/new_design/icons.scss @@ -5,6 +5,12 @@ background-size: 24px 24px; vertical-align: bottom; + &.small { + width: 16px; + height: 16px; + background-size: 16px 16px; + } + &.follow { background-image: image-url("icons/follow-folder.svg"); } @@ -94,6 +100,18 @@ object-fit: contain; } + &.help { + background-image: image-url("icons/help.svg"); + } + + &.phone { + background-image: image-url("icons/phone.svg"); + } + + &.clock { + background-image: image-url("icons/clock.svg"); + } + &.smile { background-image: image-url("icons/smile-regular.svg"); } diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index 59378c36f..87ee0446b 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -3,7 +3,8 @@ @import "constants"; @import "mixins"; -$landing-breakpoint: 1040px; +$header-landing-breakpoint: 1040px; +$header-mobile-breakpoint: 550px; // FIXME: Rename when the header is generalized .new-header { @@ -23,24 +24,28 @@ $landing-breakpoint: 1040px; .header-logo { display: inline-block; + height: 100%; + background-size: contain; + background-position: center; + background-repeat: no-repeat; - .header-logo-wide { - margin-right: 4 * $default-spacer; + // Logo large + background-image: url("/assets/header/logo-ds-wide.svg"); + width: 360px; + margin-right: 4 * $default-spacer; + + // Logo normal + @media (max-width: $header-landing-breakpoint) { + background-image: url("/assets/header/logo-ds.svg"); + width: 132px; + margin-right: $default-spacer; } - .header-logo-narrow { - display: none; - } - - @media (max-width: $landing-breakpoint) { - .header-logo-wide { - display: none; - } - - .header-logo-narrow { - display: inline; - margin-right: 0; - } + // Logo narrow + @media (max-width: $header-mobile-breakpoint) { + background-image: url("/assets/header/logo-ds-narrow.svg"); + width: 32px; + margin-right: 0; } } @@ -173,6 +178,8 @@ $landing-breakpoint: 1040px; .header-menu-button { border: none; padding: 0; + line-height: 14px; + vertical-align: middle; &:hover { background: none; diff --git a/app/controllers/new_user/dossiers_controller.rb b/app/controllers/new_user/dossiers_controller.rb index b4bb54a99..44091e710 100644 --- a/app/controllers/new_user/dossiers_controller.rb +++ b/app/controllers/new_user/dossiers_controller.rb @@ -240,6 +240,11 @@ module NewUser flash.notice = 'La pièce jointe a bien été supprimée.' end + def dossier_for_help + dossier_id = params[:id] || params[:dossier_id] + @dossier || (dossier_id.present? && Dossier.find_by(id: dossier_id.to_i)) + end + private def store_user_location! diff --git a/app/controllers/webhook_controller.rb b/app/controllers/webhook_controller.rb index 8ee719820..6548b68de 100644 --- a/app/controllers/webhook_controller.rb +++ b/app/controllers/webhook_controller.rb @@ -2,7 +2,7 @@ class WebhookController < ActionController::Base before_action :verify_signature!, only: :helpscout def helpscout - email = params[:customer][:email] + email = params[:customer][:email].downcase user = User.find_by(email: email) gestionnaire = Gestionnaire.find_by(email: email) administrateur = Administrateur.find_by(email: email) diff --git a/app/helpers/service_helper.rb b/app/helpers/service_helper.rb new file mode 100644 index 000000000..8ae646698 --- /dev/null +++ b/app/helpers/service_helper.rb @@ -0,0 +1,5 @@ +module ServiceHelper + def formatted_horaires(horaires) + horaires.sub(/\S/, &:downcase) + end +end diff --git a/app/models/champs/piece_justificative_champ.rb b/app/models/champs/piece_justificative_champ.rb index 8f140c8e4..1ec10d2ab 100644 --- a/app/models/champs/piece_justificative_champ.rb +++ b/app/models/champs/piece_justificative_champ.rb @@ -49,7 +49,7 @@ class Champs::PieceJustificativeChamp < Champ end def for_api - if piece_justificative_file.attached? && virus_scan&.safe? + if piece_justificative_file.attached? && (virus_scan&.safe? || virus_scan&.pending?) Rails.application.routes.url_helpers.url_for(piece_justificative_file) end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index c47c5c935..1b8516399 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -170,6 +170,10 @@ class Dossier < ApplicationRecord brouillon? || en_construction? end + def messagerie_available? + !brouillon? && !archived + end + def retention_end_date if instruction_commencee? en_instruction_at + procedure.duree_conservation_dossiers_dans_ds.months diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index a01812504..f996d6565 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -1,13 +1,12 @@ -/ We can't use &. because the controller may not implement #nav_bar_profile +-# We can't use &. because the controller may not implement #nav_bar_profile - nav_bar_profile = controller.try(:nav_bar_profile) +- dossier = controller.try(:dossier_for_help) .new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" } .header-inner-content .flex.align-center - = link_to root_path_for_profile(nav_bar_profile), class: "header-logo" do - = image_tag "header/logo-ds.svg", alt: "demarches-simplifiees.fr", class: "header-logo-wide" - = image_tag "header/logo-ds-narrow.svg", alt: "demarches-simplifiees.fr", class: "header-logo-narrow" + = link_to '', root_path_for_profile(nav_bar_profile), class: "header-logo", title: "Revenir à l’accueil" - if nav_bar_profile == :gestionnaire && gestionnaire_signed_in? - current_url = request.path_info @@ -22,12 +21,6 @@ - avis_counter = current_gestionnaire.avis.without_answer.count - if avis_counter > 0 %span.badge.warning= avis_counter - %li - .tab-link.contact-link - Aide - .contact-details - Besoin d’aide technique ? Contactez-nous - = contact_link("par email") - if nav_bar_profile == :user %ul.header-tabs @@ -92,3 +85,12 @@ Vous avez déjà un compte ? %li = link_to "Connexion", new_user_session_path, class: "button secondary" + + %li + .header-help + - if nav_bar_profile == :user && dossier.present? + = render partial: 'new_user/dossier_help_dropdown', locals: { dossier: dossier } + - elsif nav_bar_profile == :gestionnaire + = render partial: 'new_gestionnaire/help_dropdown' + - else + = link_to 'Aide', FAQ_URL, class: "button primary" diff --git a/app/views/new_gestionnaire/_help_dropdown.html.haml b/app/views/new_gestionnaire/_help_dropdown.html.haml new file mode 100644 index 000000000..1e4d4f7e1 --- /dev/null +++ b/app/views/new_gestionnaire/_help_dropdown.html.haml @@ -0,0 +1,20 @@ +.dropdown.help-dropdown + .button.primary.dropdown-button Aide + .dropdown-content.fade-in-down + %ul.dropdown-items + + -# Use the help website + %li + = link_to FAQ_URL, target: "_blank", rel: "noopener" do + %span.icon.help + .dropdown-description + %h4.help-dropdown-title Un problème avec le site ? + %p Trouvez votre réponse dans l’aide en ligne. + + -# Technical contact + %li + = mail_to CONTACT_EMAIL do + %span.icon.mail + .dropdown-description + %h4.help-dropdown-title Contact technique + %p Envoyez nous un message à #{CONTACT_EMAIL}. diff --git a/app/views/new_gestionnaire/dossiers/_state_button.html.haml b/app/views/new_gestionnaire/dossiers/_state_button.html.haml index 3ca3b92f9..c28254ca0 100644 --- a/app/views/new_gestionnaire/dossiers/_state_button.html.haml +++ b/app/views/new_gestionnaire/dossiers/_state_button.html.haml @@ -7,13 +7,13 @@ - if dossier.en_construction? %li.selected %span.icon.edit - .description + .dropdown-description %h4 En construction Vous permettez à l'usager de modifier ses réponses au formulaire %li = link_to passer_en_instruction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote: true, confirm: "Confirmer vous le passage en instruction de ce dossier ?" } do %span.icon.in-progress - .description + .dropdown-description %h4 Passer en instruction L'usager ne pourra plus modifier le formulaire @@ -21,30 +21,30 @@ %li = link_to repasser_en_construction_gestionnaire_dossier_path(dossier.procedure, dossier), method: :post, data: { remote:true, confirm: "Confirmer vous le passage en construction de ce dossier ?" } do %span.icon.edit - .description + .dropdown-description %h4 Repasser en construction Vous permettrez à l'usager de modifier ses réponses au formulaire %li.selected %span.icon.in-progress - .description + .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 - .description + .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 - .description + .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 - .description + .dropdown-description %h4 Refuser L'usager sera notifié que son dossier a été refusé = render partial: 'new_gestionnaire/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 ?" } diff --git a/app/views/new_user/_dossier_help_dropdown.html.haml b/app/views/new_user/_dossier_help_dropdown.html.haml new file mode 100644 index 000000000..275b7e1ac --- /dev/null +++ b/app/views/new_user/_dossier_help_dropdown.html.haml @@ -0,0 +1,42 @@ +.dropdown.help-dropdown + .button.primary.dropdown-button Aide + .dropdown-content.fade-in-down + %ul.dropdown-items + + - title = dossier.brouillon? ? "Besoin d’aide pour remplir votre dossier ?" : "Une question sur votre dossier ?" + + - if dossier.messagerie_available? + -# Contact the administration using the messagerie + %li + = link_to messagerie_dossier_path(dossier) do + %span.icon.mail + .dropdown-description + %h4.help-dropdown-title= title + %p Envoyez directement un message à l’instructeur. + + - elsif dossier.procedure.service.present? + - service = dossier.procedure.service + -# Contact the administration using email or phone + %li.help-dropdown-service + %span.icon.person + .dropdown-description + %h4.help-dropdown-title= title + .help-dropdown-service-action + %p Contactez directement l’administration : + %p.help-dropdown-service-item + %span.icon.small.mail + = link_to service.email, "mailto:#{service.email}" + %p.help-dropdown-service-item + %span.icon.small.phone + = link_to service.telephone, "tel:#{service.telephone}" + %p.help-dropdown-service-item + %span.icon.small.clock + = service.horaires + + -# Use the help website + %li + = link_to FAQ_URL, target: "_blank", rel: "noopener" do + %span.icon.help + .dropdown-description + %h4.help-dropdown-title Un problème avec le site ? + %p Trouvez votre réponse dans l’aide en ligne. diff --git a/app/views/new_user/_procedure_footer.html.haml b/app/views/new_user/_procedure_footer.html.haml index 2ac523da8..81e5c7ccb 100644 --- a/app/views/new_user/_procedure_footer.html.haml +++ b/app/views/new_user/_procedure_footer.html.haml @@ -15,7 +15,7 @@ %li.footer-column %h3.footer-header Poser une question sur votre dossier : %p - - if dossier.present? && !dossier.brouillon? + - if dossier.present? && dossier.messagerie_available? Directement = link_to "par la messagerie", messagerie_dossier_path(dossier) - else @@ -27,7 +27,7 @@ %a{ href: "tel:#{service.telephone}" }= service.telephone %p - Horaires : #{ service.horaires.sub(/\S/, &:downcase) } + Horaires : #{formatted_horaires(service.horaires)} - politiques = politiques_conservation_de_donnees(procedure) - if politiques.present? diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index 871899932..06e7e45b1 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -23,6 +23,10 @@ %span.icon.search %span.icon.sign-out %span.icon.info + %span.icon.delete + %span.icon.help + %span.icon.phone + %span.icon.clock %span.icon.download %span.icon.frown %span.icon.meh @@ -91,6 +95,39 @@ .patron-section = link_to ".button.primary.expand", "#", class: "button primary expand" + %h1 Dropdown + + .dropdown + .button.primary.dropdown-button .button.primary.dropdown-button + .dropdown-content.fade-in-down + %ul.dropdown-items + %li .dropdown-content ul.dropdown-items li + %li .dropdown-content ul.dropdown-items li + + .dropdown + .button.dropdown-button .button.dropdown-button + .dropdown-content.fade-in-down + %ul.dropdown-items + %li + %a{ href: '#' } + %span.icon.mail + .dropdown-description + %h4 Action + %p Explanation + %li + %a{ href: '#' } + %span.icon.edit + .dropdown-description + %h4 Other action + %p Explanation + + .dropdown + .button.dropdown-button .button.dropdown-button (left) + .dropdown-content.fade-in-down.left-aligned + %ul.dropdown-items + %li .dropdown-content.left-aligned ul.dropdown-items li + %li .dropdown-content.left-aligned ul.dropdown-items li + %h1 Labels %span.label .label diff --git a/spec/controllers/new_user/dossiers_controller_spec.rb b/spec/controllers/new_user/dossiers_controller_spec.rb index a981b85b9..6d133d62b 100644 --- a/spec/controllers/new_user/dossiers_controller_spec.rb +++ b/spec/controllers/new_user/dossiers_controller_spec.rb @@ -952,4 +952,30 @@ describe NewUser::DossiersController, type: :controller do end end end + + describe "#dossier_for_help" do + before do + sign_in(user) + controller.params[:dossier_id] = dossier_id.to_s + end + + subject { controller.dossier_for_help } + + context 'when the id matches an existing dossier' do + let(:dossier) { create(:dossier) } + let(:dossier_id) { dossier.id } + + it { is_expected.to eq dossier } + end + + context 'when the id doesn’t match an existing dossier' do + let(:dossier_id) { 9999999 } + it { is_expected.to be nil } + end + + context 'when the id is empty' do + let(:dossier_id) { nil } + it { is_expected.to be_falsy } + end + end end diff --git a/spec/features/help_spec.rb b/spec/features/help_spec.rb new file mode 100644 index 000000000..6293bddad --- /dev/null +++ b/spec/features/help_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' + +feature 'Getting help:' do + scenario 'a Help button is visible on public pages' do + visit '/' + within('.new-header') do + expect(page).to have_help_button + end + end + + context 'as a signed-in user' do + let(:user) { create(:user) } + let(:procedure) { create(:procedure, :with_service) } + + before do + login_as user, scope: :user + end + + scenario 'a Help button is visible on signed-in pages' do + visit dossiers_path + within('.new-header') do + expect(page).to have_help_button + end + end + + context 'on a page related to a draft dossier' do + let(:dossier) { create(:dossier, user: user, procedure: procedure) } + + scenario 'a Help menu provides administration contacts and a link to the FAQ' do + visit dossier_path(dossier) + + within('.new-header') do + expect(page).to have_help_menu + end + + within('.help-dropdown') do + expect(page).to have_content(dossier.procedure.service.email) + expect(page).to have_content(dossier.procedure.service.telephone) + expect(page).to have_link(nil, href: FAQ_URL) + end + end + end + + context 'on a page related to a submitted dossier' do + let(:dossier) { create(:dossier, :en_construction, user: user, procedure: procedure) } + + scenario 'a Help menu provides links to the Messagerie and to the FAQ' do + visit dossier_path(dossier) + + within('.new-header') do + expect(page).to have_help_menu + end + + within('.help-dropdown') do + expect(page).to have_link(nil, href: messagerie_dossier_path(dossier)) + expect(page).to have_link(nil, href: FAQ_URL) + end + end + end + end + + context 'as a gestionnaire' do + let(:gestionnaire) { create(:gestionnaire) } + + before do + login_as gestionnaire, scope: :gestionnaire + end + + scenario 'a Help menu is visible on signed-in pages' do + visit gestionnaire_procedures_path + within('.new-header') do + expect(page).to have_help_menu + end + end + end + + def have_help_button + have_link('Aide', href: FAQ_URL) + end + + def have_help_menu + have_selector('.help-dropdown') + end +end diff --git a/spec/features/new_gestionnaire/gestionnaire_spec.rb b/spec/features/new_gestionnaire/gestionnaire_spec.rb index e19676b0d..45b740660 100644 --- a/spec/features/new_gestionnaire/gestionnaire_spec.rb +++ b/spec/features/new_gestionnaire/gestionnaire_spec.rb @@ -54,7 +54,7 @@ feature 'The gestionnaire part' do click_on 'En instruction' - within('.dropdown-items') do + within('.state-button') do click_on 'Accepter' end diff --git a/spec/models/champs/piece_justificative_champ_spec.rb b/spec/models/champs/piece_justificative_champ_spec.rb index 3c44c791b..4c709090d 100644 --- a/spec/models/champs/piece_justificative_champ_spec.rb +++ b/spec/models/champs/piece_justificative_champ_spec.rb @@ -13,7 +13,7 @@ describe Champs::PieceJustificativeChamp do context 'when file is not scanned' do let(:status) { 'pending' } - it { is_expected.to be_nil } + it { is_expected.to include("/rails/active_storage/blobs/") } end context 'when file is infected' do diff --git a/spec/views/layouts/_new_header_spec.rb b/spec/views/layouts/_new_header_spec.rb index 17980f07e..c47bd3ae2 100644 --- a/spec/views/layouts/_new_header_spec.rb +++ b/spec/views/layouts/_new_header_spec.rb @@ -16,6 +16,10 @@ describe 'layouts/_new_header.html.haml', type: :view do it { is_expected.to have_css("a.header-logo[href=\"#{dossiers_path}\"]") } it { is_expected.to have_link("Dossiers", href: dossiers_path) } + + it 'displays the Help button' do + expect(subject).to have_link("Aide", href: FAQ_URL) + end end context 'when rendering for gestionnaire' do @@ -24,9 +28,8 @@ describe 'layouts/_new_header.html.haml', type: :view do it { is_expected.to have_css("a.header-logo[href=\"#{gestionnaire_procedures_path}\"]") } - it "displays the contact infos" do - expect(rendered).to have_text("Contact") - expect(rendered).to have_link("par email", href: contact_url) + it 'displays the Help dropdown menu' do + expect(rendered).to have_css(".help-dropdown") end end end