Merge pull request #8630 from demarches-simplifiees/harmonize-actions-for-instructeurs

[instructeurs] Uniformiser les actions pour les instructeurs sur la page tableau et dossier
This commit is contained in:
Lisa Durand 2023-03-02 13:28:23 +00:00 committed by GitHub
commit 54f17ad3c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 399 additions and 381 deletions

View file

@ -132,7 +132,7 @@ GEM
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.9.3) bootsnap (1.9.3)
msgpack (~> 1.0) msgpack (~> 1.0)
brakeman (5.2.2) brakeman (5.4.1)
browser (5.3.1) browser (5.3.1)
builder (3.2.4) builder (3.2.4)
byebug (11.1.3) byebug (11.1.3)

View file

@ -139,6 +139,10 @@
overflow: visible; overflow: visible;
} }
.super {
vertical-align: super;
}
// generate spacer utility like bootstrap my-2 -> margin-left/right: 2 * $default-spacer // generate spacer utility like bootstrap my-2 -> margin-left/right: 2 * $default-spacer
// using $direction.key as css modifier, $direction.values to set css properties // using $direction.key as css modifier, $direction.values to set css properties
// scale it using $steps // scale it using $steps

View file

@ -140,7 +140,7 @@
height: 25px; height: 25px;
} }
.state-button { .instruction-button {
display: inline-block; display: inline-block;
} }

View file

@ -78,7 +78,7 @@
} }
.follow-col { .follow-col {
width: 200px; width: 450px;
.fr-btn { .fr-btn {
margin-bottom: 0; margin-bottom: 0;

View file

@ -56,3 +56,13 @@ fieldset {
} }
} }
// dans le DSFR il est possible d'avoir un bouton seulement avec une icone mais j'ai du surcharger ici pour eviter d'avoir des marges de l'icone. Je n'ai pas bien compris pourquoi
.fr-btns-group--sm.fr-btns-group--icon-right .fr-btn[class*=" fr-icon-"].icon-only::after {
margin-left: 0;
margin-right: 0;
}
.fr-btn.fr-btn--secondary.danger {
color: $light-red;
box-shadow: 0px 0px 0px 1px $light-red;
}

View file

@ -134,9 +134,17 @@ module Instructeurs
end end
@dossier = dossier @dossier = dossier
respond_to do |format|
format.turbo_stream do
render :change_state render :change_state
end end
format.html do
redirect_back(fallback_location: instructeur_procedure_path(procedure))
end
end
end
def repasser_en_construction def repasser_en_construction
begin begin
dossier.repasser_en_construction!(instructeur: current_instructeur) dossier.repasser_en_construction!(instructeur: current_instructeur)
@ -146,9 +154,17 @@ module Instructeurs
end end
@dossier = dossier @dossier = dossier
respond_to do |format|
format.turbo_stream do
render :change_state render :change_state
end end
format.html do
redirect_back(fallback_location: instructeur_procedure_path(procedure))
end
end
end
def repasser_en_instruction def repasser_en_instruction
begin begin
flash.notice = "Le dossier #{dossier.id} a été repassé en instruction." flash.notice = "Le dossier #{dossier.id} a été repassé en instruction."
@ -158,9 +174,17 @@ module Instructeurs
end end
@dossier = dossier @dossier = dossier
respond_to do |format|
format.turbo_stream do
render :change_state render :change_state
end end
format.html do
redirect_back(fallback_location: instructeur_procedure_path(procedure))
end
end
end
def terminer def terminer
motivation = params[:dossier] && params[:dossier][:motivation] motivation = params[:dossier] && params[:dossier][:motivation]
justificatif = params[:dossier] && params[:dossier][:justificatif_motivation] justificatif = params[:dossier] && params[:dossier][:justificatif_motivation]

View file

@ -60,10 +60,10 @@ module DossierHelper
end end
end end
def status_badge(state) def status_badge(state, alignment_class = '')
status_text = dossier_display_state(state, lower: true) status_text = dossier_display_state(state, lower: true)
status_class = state.tr('_', '-') status_class = state.tr('_', '-')
tag.span(status_text, class: "label #{status_class} ", role: 'status') tag.span(status_text, class: "label #{status_class} #{alignment_class}", role: 'status')
end end
def deletion_reason_badge(reason) def deletion_reason_badge(reason)

View file

@ -19,7 +19,7 @@ import {
showMotivation, showMotivation,
motivationCancel, motivationCancel,
showImportJustificatif showImportJustificatif
} from '../new_design/state-button'; } from '../new_design/instruction-button';
import { showFusion, showNewAccount } from '../new_design/fc-fusion'; import { showFusion, showNewAccount } from '../new_design/fc-fusion';
const application = Application.start(); const application = Application.start();

View file

@ -1,7 +1,6 @@
-# small expires mention -# small expires mention
- if dossier.expirable? - if dossier.expirable?
%p.expires_at.mb-2 %p.expires_at
%small %small
= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months) = t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months)
- if dossier.conservation_extension.positive? - if dossier.conservation_extension.positive?
@ -25,4 +24,3 @@
- elsif dossier.en_instruction? && dossier.procedure.procedure_expires_when_termine_enabled - elsif dossier.en_instruction? && dossier.procedure.procedure_expires_when_termine_enabled
%p.expires_at_en_instruction %p.expires_at_en_instruction
%small= t("shared.dossiers.header.expires_at.en_instruction") %small= t("shared.dossiers.header.expires_at.en_instruction")

View file

@ -6,42 +6,8 @@
pour voir si l'opération est finie. pour voir si l'opération est finie.
.sub-header .sub-header
.container = render partial: 'instructeurs/dossiers/header_top', locals: { dossier: }
.flex.justify-between = render partial: 'instructeurs/dossiers/header_bottom', locals: { dossier: }
%ul.breadcrumbs
%li
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
= procedure_badge(dossier.procedure)
%li
= "Dossier nº #{dossier.id}"
.header-actions
= render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: dossier }
= render(partial: 'instructeurs/dossiers/expiration_banner', locals: {dossier: dossier})
%nav.tabs
%ul
- notifications_summary = current_instructeur.notifications_for_dossier(dossier)
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.request'),
instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:demande])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.private_annotations'),
annotations_privees_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:annotations_privees])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.external_opinion'),
avis_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:avis])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.messaging'),
messagerie_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:messagerie])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.involved_persons'),
personnes_impliquees_instructeur_dossier_path(dossier.procedure, dossier))
.container .container
.print-header .print-header

View file

@ -1,33 +1,4 @@
%ul.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline-md.fr-btns-group--icon-right %ul.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline-md.fr-btns-group--icon-right.flex.justify-end.fr-mb-1w
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-menu'}, button_options: { class: ['fr-btn--tertiary', 'fr-icon-printer-line']}) do |menu|
- menu.with_button_inner_html do
Imprimer
- menu.with_item do
= link_to print_instructeur_dossier_path(dossier.procedure, dossier), role: 'menuitem', target: "_blank", rel: "noopener", class: "menu-item menu-link" do
Tout le dossier
- menu.with_item do
= link_to '#', role: 'menuitem', onclick: "window.print()", class: "menu-item menu-link" do
Uniquement cet onglet
- menu.with_item do
= link_to instructeur_dossier_path(dossier.procedure, dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export PDF
- if dossier.geo_data?
- menu.with_item do
= link_to geo_data_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export GeoJSON
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-pj-menu'}, button_options: { class: ['fr-btn--tertiary', 'fr-icon-download-line']}) do |menu|
- menu.with_button_inner_html do
Télécharger
- menu.with_item do
= link_to telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Télécharger le dossier et toutes ses pièces jointes
%li
= render partial: "instructeurs/procedures/dossier_actions", = render partial: "instructeurs/procedures/dossier_actions",
locals: { procedure_id: dossier.procedure.id, locals: { procedure_id: dossier.procedure.id,
dossier_id: dossier.id, dossier_id: dossier.id,
@ -35,7 +6,8 @@
archived: dossier.archived, archived: dossier.archived,
dossier_is_followed: current_instructeur&.follow?(dossier), dossier_is_followed: current_instructeur&.follow?(dossier),
close_to_expiration: dossier.close_to_expiration?, close_to_expiration: dossier.close_to_expiration?,
hidden_by_administration: dossier.hidden_by_administration? } hidden_by_administration: dossier.hidden_by_administration?,
post_method: {data: { turbo_method: :post }}}
%li.state-button %li.instruction-button
= render partial: "state_button", locals: { dossier: dossier } = render partial: "instruction_button", locals: { dossier: dossier }

View file

@ -0,0 +1,23 @@
#header-bottom.container
%nav.tabs
%ul
- notifications_summary = current_instructeur.notifications_for_dossier(dossier)
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.request'),
instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:demande])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.private_annotations'),
annotations_privees_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:annotations_privees])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.external_opinion'),
avis_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:avis])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.messaging'),
messagerie_instructeur_dossier_path(dossier.procedure, dossier),
notification: notifications_summary[:messagerie])
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.involved_persons'),
personnes_impliquees_instructeur_dossier_path(dossier.procedure, dossier))

View file

@ -0,0 +1,19 @@
#header-top.container
.flex.fr-mb-3w
%div
%h1.fr-h2.fr-mb-1w
= "Dossier nº #{dossier.id}"
= status_badge(dossier.state, 'super')
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
= procedure_badge(dossier.procedure)
.header-actions.fr-ml-auto
= render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: }
= render partial: 'instructeurs/dossiers/print_and_export_actions', locals: { dossier: }
= render partial: 'instructeurs/dossiers/expiration_banner', locals: { dossier: }
- if dossier.user_deleted?
%p.fr-mb-1w
%small Lusager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.

View file

@ -0,0 +1,36 @@
- if dossier.en_instruction?
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: {'turbo-force': true} }, button_options: { class: [button_or_label_class(dossier)] }) do |menu|
- menu.with_button_inner_html do
Instruire le dossier
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do
%span.icon.accept
.dropdown-description
%h4 Accepter
Lusager sera notifié que son dossier a été accepté
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do
%span.icon.without-continuation
.dropdown-description
%h4 Classer sans suite
Lusager sera notifié que son dossier a été classé sans suite
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do
%span.icon.refuse
.dropdown-description
%h4 Refuser
Lusager sera notifié que son dossier a été refusé
- menu.with_form do
= render partial: 'instructeurs/dossiers/instruction_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 ?" }
- menu.with_form do
= render partial: 'instructeurs/dossiers/instruction_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 ?' }
- menu.with_form do
= render partial: 'instructeurs/dossiers/instruction_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 ?' }

View file

@ -0,0 +1,28 @@
%ul.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline-md.fr-btns-group--icon-right.flex.justify-end
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-menu'}, button_options: { class: ['fr-btn--tertiary-no-outline', 'fr-icon-printer-line']}) do |menu|
- menu.with_button_inner_html do
Imprimer
- menu.with_item do
= link_to print_instructeur_dossier_path(dossier.procedure, dossier), role: 'menuitem', target: "_blank", rel: "noopener", class: "menu-item menu-link" do
Tout le dossier
- menu.with_item do
= link_to '#', role: 'menuitem', onclick: "window.print()", class: "menu-item menu-link" do
Uniquement cet onglet
- menu.with_item do
= link_to instructeur_dossier_path(dossier.procedure, dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export PDF
- if dossier.geo_data?
- menu.with_item do
= link_to geo_data_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export GeoJSON
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-pj-menu'}, button_options: { class: ['fr-btn--tertiary-no-outline', 'fr-icon-download-line']}) do |menu|
- menu.with_button_inner_html do
Télécharger
- menu.with_item do
= link_to telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Télécharger le dossier et toutes ses pièces jointes

View file

@ -1,107 +0,0 @@
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: {'turbo-force': true} }, button_options: { class: [button_or_label_class(dossier)] }) do |menu|
- menu.with_button_inner_html do
= dossier_display_state(dossier)
- if dossier.en_construction?
- menu.with_item(aria: {disabled:"true"}, class: 'selected') do
%span.icon.edit
.dropdown-description
%h4 En construction
Vous permettez à l'usager de modifier ses réponses au formulaire
- menu.with_item('data-turbo': 'true') do
= link_to(passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en instruction de ce dossier ?", turbo: true }, role: 'menuitem') do
%span.icon.in-progress
.dropdown-description
%h4 Passer en instruction
Lusager ne pourra plus modifier le formulaire
- elsif dossier.en_instruction?
- menu.with_item('data-turbo': 'true') do
= link_to(repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en construction de ce dossier ?"}, role: 'menuitem') do
%span.icon.edit
.dropdown-description
%h4 Repasser en construction
Vous permettrez à l'usager de modifier ses réponses au formulaire
- menu.with_item(aria: {disabled:"true"}, class: 'selected') do
%span.icon.in-progress
.dropdown-description
%h4 En instruction
Lusager ne peut modifier son dossier pendant l'instruction
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do
%span.icon.accept
.dropdown-description
%h4 Accepter
Lusager sera notifié que son dossier a été accepté
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do
%span.icon.without-continuation
.dropdown-description
%h4 Classer sans suite
Lusager sera notifié que son dossier a été classé sans suite
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do
%span.icon.refuse
.dropdown-description
%h4 Refuser
Lusager sera notifié que son dossier a été refusé
- menu.with_form do
= 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 ?" }
- menu.with_form do
= 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 ?' }
- menu.with_form do
= 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?
- if dossier.motivation.present?
- menu.with_item(class: 'inactive') do
%span.icon.info
.dropdown-description
%h4 Motivation
%p « #{dossier.motivation} »
- if dossier.justificatif_motivation.attached?
- menu.with_item(class: 'inactive') do
%span.icon.justificatif
.dropdown-description
%h4 Justificatif
%p Ce justificatif joint par linstructeur a été envoyé au demandeur.
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.attestation.present?
- menu.with_item do
= link_to(attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', role: 'menuitem') do
%span.icon.preview
.dropdown-description
%h4 Voir lattestation
%p Cette attestation a été envoyée automatiquement au demandeur.
- if dossier.can_repasser_en_instruction?
- menu.with_item do
= link_to(repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?", turbo: true }, role: 'menuitem') do
%span.icon.in-progress
.dropdown-description
%h4 Repasser en instruction
Lusager sera notifié que son dossier est réexaminé.
- elsif dossier.user_deleted?
- menu.with_item do
%span.icon.info
.dropdown-description
%h4 En attente darchivage
Lusager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.
- menu.with_item do
= link_to(instructeur_dossier_path(dossier.procedure, dossier), method: :delete, role: 'menuitem') do
%span.icon.delete
.dropdown-description
%h4 Supprimer le dossier

View file

@ -1 +1 @@
= turbo_stream.update_all '.header-actions', partial: 'header_actions', locals: { dossier: @dossier } = turbo_stream.replace 'header-top', partial: 'header_top', locals: { dossier: @dossier }

View file

@ -1,42 +1,47 @@
- if hidden_by_administration - if hidden_by_administration
= link_to restore_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: "fr-btn fr-btn--secondary" do %li
= link_to restore_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: "fr-btn fr-icon-refresh-line" do
= t('views.instructeurs.dossiers.restore') = t('views.instructeurs.dossiers.restore')
- elsif close_to_expiration || Dossier::TERMINE.include?(state) - elsif close_to_expiration || Dossier::TERMINE.include?(state)
= render Dropdown::MenuComponent.new(wrapper: :li, button_options: { class: ['fr-mb-0']}, menu_options: { id: "dossier_#{dossier_id}_actions_menu", class: 'user-dossier-actions' }) do |menu| %li{ 'data-turbo': 'true' }
- menu.with_button_inner_html do
Actions
- if close_to_expiration - if close_to_expiration
- menu.with_item do = link_to repasser_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), { class: 'fr-btn fr-btn--secondary fr-icon-edit-line' }.deep_merge!(post_method) do
= link_to(repousser_expiration_instructeur_dossier_path(procedure_id, dossier_id), method: :post, role: 'menuitem') do Repasser en instruction
%span.icon.standby
%span.dropdown-description= t('instructeurs.dossiers.header.banner.button_delay_expiration')
- if archived = link_to(repousser_expiration_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: "fr-btn") do
- menu.with_item do = t('instructeurs.dossiers.header.banner.button_delay_expiration')
= link_to( unarchive_instructeur_dossier_path(procedure_id, dossier_id), role: 'menuitem', method: :patch) do
%span.icon.unarchive - elsif archived
%span.dropdown-description = link_to( unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: "fr-btn fr-icon-upload-2-line") do
Désarchiver le dossier Désarchiver le dossier
= link_to('', instructeur_dossier_path(procedure_id, dossier_id), method: :delete, class: 'fr-btn fr-btn--secondary fr-icon-delete-line icon-only danger', title: t('views.instructeurs.dossiers.delete_dossier'))
- else - else
- menu.with_item do = link_to repasser_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), { class: 'fr-btn fr-btn--secondary fr-icon-edit-line' }.deep_merge!(post_method) do
= link_to( archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, role: 'menuitem') do Repasser en instruction
%span.icon.archive
%span.dropdown-description = link_to( archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-icon-folder-2-line') do
Archiver le dossier Archiver le dossier
- menu.with_item(class: 'danger') do = link_to('', instructeur_dossier_path(procedure_id, dossier_id), method: :delete, class: 'fr-btn fr-btn--secondary fr-icon-delete-line icon-only danger', title: t('views.instructeurs.dossiers.delete_dossier'))
= link_to(instructeur_dossier_path(procedure_id, dossier_id), method: :delete, role: 'menuitem') do
%span.icon.delete
%span.dropdown-description
= t('views.instructeurs.dossiers.delete_dossier')
- elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state) - elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
- if Dossier.states[:en_construction] == state
%li{ 'data-turbo': 'true' }
= link_to passer_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), { class: 'fr-btn fr-btn--secondary fr-icon-edit-line' }.deep_merge!(post_method) do
Passer en instruction
- elsif Dossier.states[:en_instruction] == state
%li{ 'data-turbo': 'true' }
= link_to repasser_en_construction_instructeur_dossier_path(procedure_id, dossier_id), { class: 'fr-btn fr-btn--secondary fr-icon-draft-line' }.deep_merge!(post_method) do
Repasser en construction
- if dossier_is_followed - if dossier_is_followed
%li
= link_to unfollow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-btn--secondary fr-icon-star-fill' do = link_to unfollow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-btn--secondary fr-icon-star-fill' do
= t('views.instructeurs.dossiers.stop_follow') = t('views.instructeurs.dossiers.stop_follow')
- else - else
= link_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-btn--secondary fr-icon-star-line' do %li
= link_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-icon-star-line' do
= t('views.instructeurs.dossiers.follow_file') = t('views.instructeurs.dossiers.follow_file')

View file

@ -180,7 +180,8 @@
archived: p.archived, archived: p.archived,
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id), dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
close_to_expiration: @statut == 'expirant', close_to_expiration: @statut == 'expirant',
hidden_by_administration: @statut == 'supprimes_recemment' } hidden_by_administration: @statut == 'supprimes_recemment',
post_method: { method: :post } }
%tfoot %tfoot
%tr %tr
%td.force-table-100{ colspan: @procedure_presentation.displayed_fields_for_headers.size + 2 }= paginate @filtered_sorted_paginated_ids %td.force-table-100{ colspan: @procedure_presentation.displayed_fields_for_headers.size + 2 }= paginate @filtered_sorted_paginated_ids

View file

@ -97,7 +97,8 @@
archived: p.archived, archived: p.archived,
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id), dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
close_to_expiration: nil, close_to_expiration: nil,
hidden_by_administration: nil } hidden_by_administration: nil,
post_method: { method: :post } }
- else - else
%td %td

View file

@ -3,9 +3,24 @@
%tr %tr
%td.libelle Déposé le : %td.libelle Déposé le :
%td= l(dossier.depose_at, format: '%d %B %Y') %td= l(dossier.depose_at, format: '%d %B %Y')
- if dossier.justificatif_motivation.attached? - if dossier.justificatif_motivation.attached?
%tr %tr
%td.libelle Justificatif : %td.libelle Justificatif :
%td %td
.action .action
= render Attachment::ShowComponent.new(attachment: dossier.justificatif_motivation.attachment) = render Attachment::ShowComponent.new(attachment: dossier.justificatif_motivation.attachment)
- if dossier.motivation.present?
%tr
%td.libelle Motivation :
%td
.action
= dossier.motivation
- if dossier.attestation.present?
%tr
%td.libelle Attestation :
%td
.action
= link_to('Voir lattestation', attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener')

View file

@ -1,6 +1,6 @@
-# small expires mention -# small expires mention
- if dossier.expirable? - if dossier.expirable?
%p.expires_at.mb-2 %p.expires_at
%small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months) %small= t("shared.dossiers.header.expires_at.#{dossier.state}", date: safe_expiration_date(dossier), duree_conservation_totale: dossier.duree_totale_conservation_in_months)
- if dossier.close_to_expiration? - if dossier.close_to_expiration?
@ -21,4 +21,3 @@
- elsif dossier.en_instruction? && dossier.procedure.procedure_expires_when_termine_enabled - elsif dossier.en_instruction? && dossier.procedure.procedure_expires_when_termine_enabled
%p.expires_at_en_instruction %p.expires_at_en_instruction
%small= t("shared.dossiers.header.expires_at.en_instruction") %small= t("shared.dossiers.header.expires_at.en_instruction")

View file

@ -154,7 +154,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) }
it { expect(instructeur.follow?(dossier)).to be true } it { expect(instructeur.follow?(dossier)).to be true }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.header-actions') } it { expect(response.body).to include('header-top') }
context 'when the dossier has already been put en_instruction' do context 'when the dossier has already been put en_instruction' do
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
@ -200,7 +200,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.header-actions') } it { expect(response.body).to include('header-top') }
context 'when the dossier has already been put en_construction' do context 'when the dossier has already been put en_construction' do
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
@ -236,7 +236,7 @@ describe Instructeurs::DossiersController, type: :controller do
context 'when the dossier is refuse' do context 'when the dossier is refuse' do
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.header-actions') } it { expect(response.body).to include('header-top') }
end end
context 'when the dossier has already been put en_instruction' do context 'when the dossier has already been put en_instruction' do
@ -313,7 +313,7 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.header-actions') } it { expect(subject.body).to include('header-top') }
end end
context 'with dossier in batch_operation' do context 'with dossier in batch_operation' do
@ -353,7 +353,7 @@ describe Instructeurs::DossiersController, type: :controller do
subject subject
end end
it { expect(subject.body).to include('.header-actions') } it { expect(subject.body).to include('header-top') }
end end
context 'with attachment' do context 'with attachment' do
@ -367,7 +367,7 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.header-actions') } it { expect(subject.body).to include('header-top') }
end end
end end
@ -409,14 +409,14 @@ describe Instructeurs::DossiersController, type: :controller do
end end
it 'The instructeur is sent back to the dossier page' do it 'The instructeur is sent back to the dossier page' do
expect(subject.body).to include('.header-actions') expect(subject.body).to include('header-top')
end end
context 'and the dossier has already an attestation' do context 'and the dossier has already an attestation' do
it 'should not crash' do it 'should not crash' do
dossier.attestation = Attestation.new dossier.attestation = Attestation.new
dossier.save dossier.save
expect(subject.body).to include('.header-actions') expect(subject.body).to include('header-top')
end end
end end
end end
@ -455,7 +455,7 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.header-actions') } it { expect(subject.body).to include('header-top') }
end end
end end

View file

@ -37,18 +37,18 @@ describe 'Instructing a dossier:', js: true do
click_on dossier.user.email click_on dossier.user.email
expect(page).to have_current_path(instructeur_dossier_path(procedure, dossier)) expect(page).to have_current_path(instructeur_dossier_path(procedure, dossier))
click_on 'En construction'
accept_confirm do
click_on 'Passer en instruction' click_on 'Passer en instruction'
end
expect(page).to have_text('En instruction') expect(page).to have_text('Dossier passé en instruction.')
expect(page).to have_text('Instruire le dossier')
expect(page).to have_selector('.label.en-instruction')
dossier.reload dossier.reload
expect(dossier.state).to eq(Dossier.states.fetch(:en_instruction)) expect(dossier.state).to eq(Dossier.states.fetch(:en_instruction))
click_on 'En instruction' click_on 'Instruire le dossier'
within('.state-button') do within('.instruction-button') do
click_on 'Accepter' click_on 'Accepter'
end end
@ -69,10 +69,10 @@ describe 'Instructing a dossier:', js: true do
click_on procedure.libelle click_on procedure.libelle
click_on 'traité' click_on 'traité'
click_on 'Actions' # destroy from list expect(page).to have_link('Repasser en instruction')
click_on 'Supprimer le dossier' page.click_link('', title: 'Supprimer le dossier')
click_on 'traité' click_on 'traité'
expect(page).not_to have_button('Actions') expect(page).not_to have_link('Repasser en instruction')
end end
scenario 'An instructeur can destroy a dossier from view' do scenario 'An instructeur can destroy a dossier from view' do
@ -81,10 +81,7 @@ describe 'Instructing a dossier:', js: true do
dossier.passer_en_instruction(instructeur: instructeur) dossier.passer_en_instruction(instructeur: instructeur)
dossier.accepter!(instructeur: instructeur) dossier.accepter!(instructeur: instructeur)
visit instructeur_dossier_path(procedure, dossier) visit instructeur_dossier_path(procedure, dossier)
click_on 'Actions' # destroy from view page.click_link('', title: 'Supprimer le dossier')
within '.user-dossier-actions' do
click_on 'Supprimer le dossier'
end
end end
scenario 'A instructeur can follow/unfollow a dossier' do scenario 'A instructeur can follow/unfollow a dossier' do

View file

@ -0,0 +1,43 @@
describe 'instructeurs/dossiers/instruction_button.html.haml', type: :view do
include DossierHelper
subject! do
render('instructeurs/dossiers/instruction_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 'en_instruction' do
let(:dossier) { create(:dossier, :en_instruction) }
it 'renders a dropdown' do
expect(rendered).to have_dropdown_title('Instruire le dossier')
expect(rendered).to have_dropdown_items(count: 3)
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
end

View file

@ -1,9 +1,9 @@
describe 'instructeurs/dossiers/state_button_motivation.html.haml', type: :view do describe 'instructeurs/dossiers/instruction_button_motivation.html.haml', type: :view do
let(:dossier) { create(:dossier, :en_instruction) } let(:dossier) { create(:dossier, :en_instruction) }
subject do subject do
render( render(
'instructeurs/dossiers/state_button_motivation.html.haml', 'instructeurs/dossiers/instruction_button_motivation.html.haml',
dossier: dossier, dossier: dossier,
popup_title: 'Accepter le dossier', popup_title: 'Accepter le dossier',
placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)',

View file

@ -1,120 +0,0 @@
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: 2)
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 lattestation')
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(virus_scan_result: ActiveStorage::VirusScanner::SAFE)
dossier
end
it 'allows to download the justificatif' do
expect(rendered).to have_dropdown_item('Justificatif')
expect(response).to have_css("a[href*='/rails/active_storage/blobs/']", text: dossier.justificatif_motivation.attachment.filename.to_s)
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

View file

@ -8,15 +8,119 @@ describe 'instructeurs/dossiers/show.html.haml', type: :view do
assign(:dossier, dossier) assign(:dossier, dossier)
end end
subject! { render } subject { render }
it 'renders the header' do it 'renders the header' do
expect(rendered).to have_text("Dossier nº #{dossier.id}") expect(subject).to have_text("Dossier nº #{dossier.id}")
end end
it 'renders the dossier infos' do it 'renders the dossier infos' do
expect(rendered).to have_text('Identité') expect(subject).to have_text('Identité')
expect(rendered).to have_text('Demande') expect(subject).to have_text('Demande')
end
it 'renders the correct dossier state' do
expect(subject).to have_text('en construction')
end
context 'with a motivation' do
let(:dossier) { create :dossier, :accepte, :with_motivation }
it 'displays the motivation text' do
expect(subject).to have_content(dossier.motivation)
end
end
context 'with an attestation' do
let(:dossier) { create :dossier, :accepte, :with_attestation }
it 'provides a link to the attestation' do
expect(subject).to have_text('Attestation')
expect(subject).to have_link(href: attestation_instructeur_dossier_path(dossier.procedure, dossier))
end
end
context 'with a justificatif' do
let(:dossier) do
dossier = create(:dossier, :accepte, :with_justificatif)
dossier.justificatif_motivation.blob.update(virus_scan_result: ActiveStorage::VirusScanner::SAFE)
dossier
end
it 'allows to download the justificatif' do
expect(subject).to have_css("a[href*='/rails/active_storage/blobs/']", text: dossier.justificatif_motivation.attachment.filename.to_s)
end
end
context 'en_contruction' do
let(:dossier) { create(:dossier, :en_construction) }
it 'displays the correct actions' do
expect(subject).to have_link('Passer en instruction', href: passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_link('Suivre le dossier', href: follow_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 2)
end
end
context 'en_instruction' do
let(:dossier) { create(:dossier, :en_instruction) }
before do
current_instructeur.followed_dossiers << dossier
render
end
it 'displays the correct actions' do
expect(subject).to have_link('Repasser en construction', href: repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_link('Ne plus suivre', href: unfollow_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_button('Instruire le dossier')
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 3)
end
end
context 'accepte' do
let(:dossier) { create(:dossier, :accepte) }
it 'displays the correct actions' do
expect(subject).to have_link('Repasser en instruction', href: repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_link('Archiver le dossier', href: archive_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_selector('[title^="Supprimer le dossier"]')
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 3)
end
end
context 'supprime' do
let(:dossier) { create(:dossier, :accepte) }
before do
dossier.hide_and_keep_track!(current_instructeur, :instructeur_request)
render
end
it 'displays the correct actions' do
expect(subject).to have_link('Restaurer', href: restore_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 1)
end
end
context 'expirant' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6, procedure_expires_when_termine_enabled: true) }
let!(:dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 175.days.ago) }
it 'displays the correct actions' do
expect(subject).to have_text('Conserver un mois de plus')
expect(subject).to have_link('Repasser en instruction', href: repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 2)
end
end
context 'archived' do
let(:dossier) { create(:dossier, :accepte, :archived) }
it 'displays the correct actions' do
expect(subject).to have_link('Désarchiver le dossier', href: unarchive_instructeur_dossier_path(dossier.procedure, dossier))
expect(subject).to have_selector('[title^="Supprimer le dossier"]')
expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 2)
end
end end
context 'when the user is logged in with france connect' do context 'when the user is logged in with france connect' do
@ -39,7 +143,7 @@ describe 'instructeurs/dossiers/show.html.haml', type: :view do
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, as_degraded_mode: false) } let(:dossier) { create(:dossier, :en_construction, :with_entreprise, as_degraded_mode: false) }
it 'contains no warning' do it 'contains no warning' do
expect(rendered).not_to have_text("Les services de lINSEE sont indisponibles") expect(subject).not_to have_text("Les services de lINSEE sont indisponibles")
end end
end end
@ -47,7 +151,7 @@ describe 'instructeurs/dossiers/show.html.haml', type: :view do
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, as_degraded_mode: true) } let(:dossier) { create(:dossier, :en_construction, :with_entreprise, as_degraded_mode: true) }
it 'warns the instructeur' do it 'warns the instructeur' do
expect(rendered).to have_text("Les services de lINSEE sont indisponibles") expect(subject).to have_text("Les services de lINSEE sont indisponibles")
end end
end end
end end