Merge pull request #10898 from mfo/US/navigation-accros-dossiers
ETQ instructeur, je peux passer au dossiers suivant ou au dossier précédent lorsque je visualise un dossiers d'un des onglet (a-suivre, tous, ...)
This commit is contained in:
commit
6e5aa90bc7
31 changed files with 434 additions and 30 deletions
|
@ -397,3 +397,7 @@ ul.dropdown-items {
|
||||||
content: none !important;
|
content: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
line-height: 1.75rem;
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ class Instructeurs::BackButtonComponent < ApplicationComponent
|
||||||
end
|
end
|
||||||
|
|
||||||
def call
|
def call
|
||||||
link_to "", @to, class: 'back-btn fr-btn fr-btn--secondary fr-btn--sm fr-mr-2w fr-icon-arrow-left-line', title: t('.back')
|
link_to @to, class: 'back-btn fr-btn fr-btn--secondary fr-btn--sm fr-mr-2w fr-px-1v', title: t('.back') do
|
||||||
|
dsfr_icon("fr-icon-arrow-left-line")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
45
app/components/instructeurs/dossiers_navigation_component.rb
Normal file
45
app/components/instructeurs/dossiers_navigation_component.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Instructeurs::DossiersNavigationComponent < ApplicationComponent
|
||||||
|
attr_reader :dossier, :statut
|
||||||
|
|
||||||
|
def initialize(dossier:, procedure_presentation:, statut:)
|
||||||
|
@dossier = dossier
|
||||||
|
@cache = Cache::ProcedureDossierPagination.new(procedure_presentation: procedure_presentation, statut:)
|
||||||
|
@statut = statut
|
||||||
|
end
|
||||||
|
|
||||||
|
def back_url_options
|
||||||
|
options = { statut: }
|
||||||
|
options = options.merge(page: @cache.incoming_page) if @cache.incoming_page
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_next
|
||||||
|
if has_next?
|
||||||
|
html_tag = :a
|
||||||
|
options = { class: "fr-link no-wrap fr-ml-3w", href: next_instructeur_dossier_path(dossier:, statut:) }
|
||||||
|
else
|
||||||
|
html_tag = :span
|
||||||
|
options = { class: "fr-link no-wrap fr-ml-3w fr-text-mention--grey" }
|
||||||
|
end
|
||||||
|
|
||||||
|
tag.send(html_tag, t('.next').html_safe + tag.span(class: 'fr-icon-arrow-right-line fr-ml-1w'), **options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_previous
|
||||||
|
if has_previous?
|
||||||
|
html_tag = :a
|
||||||
|
options = { class: "fr-link no-wrap", href: previous_instructeur_dossier_path(dossier:, statut:) }
|
||||||
|
else
|
||||||
|
html_tag = :span
|
||||||
|
options = { class: "fr-link no-wrap fr-text-mention--grey" }
|
||||||
|
end
|
||||||
|
|
||||||
|
tag.send(html_tag, tag.span(class: 'fr-icon-arrow-left-line fr-mr-1w') + t('.previous'), **options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_next? = @has_next ||= @cache.next_dossier_id(from_id: dossier.id).present?
|
||||||
|
|
||||||
|
def has_previous? = @has_previous ||= @cache.previous_dossier_id(from_id: dossier.id).present?
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
en:
|
||||||
|
next: Next file
|
||||||
|
previous: Previous file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
fr:
|
||||||
|
next: Dossier suivant
|
||||||
|
previous: Dossier précédent
|
|
@ -0,0 +1,11 @@
|
||||||
|
.flex.fr-mb-1w.align-start
|
||||||
|
= render Instructeurs::BackButtonComponent.new(to: instructeur_procedure_path(dossier.procedure, **back_url_options))
|
||||||
|
|
||||||
|
%h1.fr-h3.fr-mb-0
|
||||||
|
= t('show_dossier', scope: [:layouts, :breadcrumb], dossier_id: dossier.id, owner_name: dossier.owner_name)
|
||||||
|
|
||||||
|
.fr.ml-auto.align-center.flex.fr-mt-1v
|
||||||
|
= link_previous
|
||||||
|
= link_next
|
||||||
|
|
||||||
|
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
|
11
app/controllers/concerns/instructeur_concern.rb
Normal file
11
app/controllers/concerns/instructeur_concern.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module InstructeurConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
def retrieve_procedure_presentation
|
||||||
|
@procedure_presentation ||= current_instructeur.procedure_presentation_for_procedure_id(params[:procedure_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
module Instructeurs
|
module Instructeurs
|
||||||
class CommentairesController < ApplicationController
|
class CommentairesController < ApplicationController
|
||||||
|
include InstructeurConcern
|
||||||
before_action :authenticate_instructeur_or_expert!
|
before_action :authenticate_instructeur_or_expert!
|
||||||
after_action :mark_messagerie_as_read
|
after_action :mark_messagerie_as_read
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
retrieve_procedure_presentation if current_instructeur
|
||||||
if commentaire.sent_by?(current_instructeur) || commentaire.sent_by?(current_expert)
|
if commentaire.sent_by?(current_instructeur) || commentaire.sent_by?(current_expert)
|
||||||
commentaire.soft_delete!
|
commentaire.soft_delete!
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,16 @@ module Instructeurs
|
||||||
include CreateAvisConcern
|
include CreateAvisConcern
|
||||||
include DossierHelper
|
include DossierHelper
|
||||||
include TurboChampsConcern
|
include TurboChampsConcern
|
||||||
|
include InstructeurConcern
|
||||||
include ActionController::Streaming
|
include ActionController::Streaming
|
||||||
include Zipline
|
include Zipline
|
||||||
|
|
||||||
before_action :redirect_on_dossier_not_found, only: :show
|
before_action :redirect_on_dossier_not_found, only: :show
|
||||||
before_action :redirect_on_dossier_in_batch_operation, only: [:archive, :unarchive, :follow, :unfollow, :passer_en_instruction, :repasser_en_construction, :repasser_en_instruction, :terminer, :restore, :destroy, :extend_conservation]
|
before_action :redirect_on_dossier_in_batch_operation, only: [:archive, :unarchive, :follow, :unfollow, :passer_en_instruction, :repasser_en_construction, :repasser_en_instruction, :terminer, :restore, :destroy, :extend_conservation]
|
||||||
before_action :set_gallery_attachments, only: [:show, :pieces_jointes, :annotations_privees, :avis, :messagerie, :personnes_impliquees, :reaffectation]
|
before_action :set_gallery_attachments, only: [:show, :pieces_jointes, :annotations_privees, :avis, :messagerie, :personnes_impliquees, :reaffectation]
|
||||||
after_action :mark_demande_as_read, only: :show
|
before_action :retrieve_procedure_presentation, only: [:annotations_privees, :avis_new, :avis, :messagerie, :personnes_impliquees, :pieces_jointes, :reaffectation, :show, :dossier_labels, :passer_en_instruction, :repasser_en_construction, :repasser_en_instruction, :terminer, :pending_correction, :create_avis, :create_commentaire]
|
||||||
|
|
||||||
|
after_action :mark_demande_as_read, only: :show
|
||||||
after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire, :pending_correction]
|
after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire, :pending_correction]
|
||||||
after_action :mark_avis_as_read, only: [:avis, :create_avis]
|
after_action :mark_avis_as_read, only: [:avis, :create_avis]
|
||||||
after_action :mark_annotations_privees_as_read, only: [:annotations_privees, :update_annotations]
|
after_action :mark_annotations_privees_as_read, only: [:annotations_privees, :update_annotations]
|
||||||
|
@ -390,8 +391,40 @@ module Instructeurs
|
||||||
@pieces_jointes_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.pieces_jointes_seen_at
|
@pieces_jointes_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.pieces_jointes_seen_at
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
navigate_through_dossiers_list do |cache|
|
||||||
|
cache.next_dossier_id(from_id: params[:dossier_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def previous
|
||||||
|
navigate_through_dossiers_list do |cache|
|
||||||
|
cache.previous_dossier_id(from_id: params[:dossier_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def navigate_through_dossiers_list
|
||||||
|
dossier = dossier_scope.find(params[:dossier_id])
|
||||||
|
procedure_presentation = current_instructeur.procedure_presentation_for_procedure_id(dossier.procedure.id)
|
||||||
|
cache = Cache::ProcedureDossierPagination.new(procedure_presentation:, statut: params[:statut])
|
||||||
|
|
||||||
|
next_or_previous_dossier_id = yield(cache)
|
||||||
|
|
||||||
|
if next_or_previous_dossier_id
|
||||||
|
redirect_to instructeur_dossier_path(procedure_id: procedure.id, dossier_id: next_or_previous_dossier_id, statut: params[:statut])
|
||||||
|
else
|
||||||
|
redirect_back fallback_location: instructeur_dossier_path(procedure_id: procedure.id, dossier_id: dossier.id, statut: params[:statut]), alert: "Une erreur est survenue"
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
Sentry.capture_message(
|
||||||
|
"Navigation through dossier failed => ActiveRecord::RecordNotFound",
|
||||||
|
extra: { dossier_id: params[:dossier_id] }
|
||||||
|
)
|
||||||
|
redirect_to instructeur_procedure_path(procedure_id: procedure.id), alert: "Une erreur est survenue"
|
||||||
|
end
|
||||||
|
|
||||||
def dossier_scope
|
def dossier_scope
|
||||||
if action_name == 'update_annotations'
|
if action_name == 'update_annotations'
|
||||||
Dossier
|
Dossier
|
||||||
|
|
|
@ -122,6 +122,8 @@ module Instructeurs
|
||||||
.where(groupe_instructeurs: current_instructeur.groupe_instructeurs.where(procedure_id: @procedure.id))
|
.where(groupe_instructeurs: current_instructeur.groupe_instructeurs.where(procedure_id: @procedure.id))
|
||||||
.where(seen_at: nil)
|
.where(seen_at: nil)
|
||||||
.distinct
|
.distinct
|
||||||
|
|
||||||
|
cache_show_procedure_state # don't move in callback, inherited by Instructeurs::DossiersController
|
||||||
end
|
end
|
||||||
|
|
||||||
def deleted_dossiers
|
def deleted_dossiers
|
||||||
|
@ -392,5 +394,11 @@ module Instructeurs
|
||||||
def ordered_procedure_ids_params
|
def ordered_procedure_ids_params
|
||||||
params.require(:ordered_procedure_ids)
|
params.require(:ordered_procedure_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cache_show_procedure_state
|
||||||
|
cache = Cache::ProcedureDossierPagination.new(procedure_presentation:, statut:)
|
||||||
|
|
||||||
|
cache.save_context(ids: @filtered_sorted_paginated_ids, incoming_page: params[:page])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
102
app/models/cache/procedure_dossier_pagination.rb
vendored
Normal file
102
app/models/cache/procedure_dossier_pagination.rb
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Cache::ProcedureDossierPagination
|
||||||
|
HALF_WINDOW = 50
|
||||||
|
TRESHOLD_BEFORE_REFRESH = 1
|
||||||
|
CACHE_EXPIRACY = 8.hours
|
||||||
|
|
||||||
|
attr_reader :procedure_presentation, :statut, :cache
|
||||||
|
delegate :procedure, :instructeur, to: :procedure_presentation
|
||||||
|
|
||||||
|
def initialize(procedure_presentation:, statut:)
|
||||||
|
@procedure_presentation = procedure_presentation
|
||||||
|
@statut = statut
|
||||||
|
@cache = Kredis.json(cache_key, expires_in: CACHE_EXPIRACY)
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_context(ids:, incoming_page:)
|
||||||
|
value = { ids: }
|
||||||
|
value[:incoming_page] = incoming_page if incoming_page
|
||||||
|
write_cache(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_dossier_id(from_id:)
|
||||||
|
index = ids&.index(from_id.to_i)
|
||||||
|
|
||||||
|
return nil if index.nil? # not found
|
||||||
|
|
||||||
|
if refresh_cache_after?(from_id:)
|
||||||
|
renew_ids(from_id:)
|
||||||
|
index = ids.index(from_id.to_i)
|
||||||
|
end
|
||||||
|
return nil if index.blank?
|
||||||
|
return nil if index + 1 > ids.size # out of bound end
|
||||||
|
|
||||||
|
ids[index + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def previous_dossier_id(from_id:)
|
||||||
|
index = ids&.index(from_id.to_i)
|
||||||
|
|
||||||
|
return nil if index.nil? # not found
|
||||||
|
|
||||||
|
if refresh_cache_before?(from_id:)
|
||||||
|
renew_ids(from_id:)
|
||||||
|
index = ids.index(from_id.to_i)
|
||||||
|
end
|
||||||
|
return nil if index.blank?
|
||||||
|
return nil if index - 1 < 0 # out of bound start
|
||||||
|
|
||||||
|
ids[index - 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def incoming_page
|
||||||
|
read_cache[:incoming_page]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def cache_key
|
||||||
|
[procedure.id, instructeur.id, statut].join(":")
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_cache(value)
|
||||||
|
cache.value = value
|
||||||
|
@read_cache = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_cache
|
||||||
|
@read_cache ||= Hash(cache.value).with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
def ids = read_cache[:ids]
|
||||||
|
|
||||||
|
def refresh_cache_after?(from_id:) = from_id.in?(ids.last(TRESHOLD_BEFORE_REFRESH))
|
||||||
|
|
||||||
|
def refresh_cache_before?(from_id:) = from_id.in?(ids.first(TRESHOLD_BEFORE_REFRESH))
|
||||||
|
|
||||||
|
def renew_ids(from_id:)
|
||||||
|
value = read_cache
|
||||||
|
value[:ids] = fetch_ids_around(from_id:)
|
||||||
|
|
||||||
|
write_cache(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_all_ids
|
||||||
|
dossiers = Dossier.where(groupe_instructeur_id: GroupeInstructeur.joins(:instructeurs, :procedure).where(procedure: procedure, instructeurs: [instructeur]).select(:id))
|
||||||
|
DossierFilterService.filtered_sorted_ids(dossiers, statut, procedure_presentation.filters_for(statut), procedure_presentation.sorted_column, instructeur, count: 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_ids_around(from_id:)
|
||||||
|
all_ids = fetch_all_ids
|
||||||
|
from_id_at = all_ids.index(from_id)
|
||||||
|
|
||||||
|
if from_id_at.present?
|
||||||
|
new_page_starts_at = [0, from_id_at - HALF_WINDOW].max # avoid index below 0
|
||||||
|
new_page_ends_at = [from_id_at + HALF_WINDOW, all_ids.size].min # avoid index above all_ids.size
|
||||||
|
all_ids.slice(new_page_starts_at, new_page_ends_at)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -127,12 +127,14 @@ class Instructeur < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def procedure_presentation_for_procedure_id(procedure_id)
|
||||||
|
assign_to = assign_to_for_procedure_id(procedure_id)
|
||||||
|
assign_to.procedure_presentation || assign_to.create_procedure_presentation!
|
||||||
|
end
|
||||||
|
|
||||||
def procedure_presentation_and_errors_for_procedure_id(procedure_id)
|
def procedure_presentation_and_errors_for_procedure_id(procedure_id)
|
||||||
assign_to
|
assign_to = assign_to_for_procedure_id(procedure_id)
|
||||||
.joins(:groupe_instructeur)
|
assign_to.procedure_presentation_or_default_and_errors
|
||||||
.includes(:instructeur, :procedure)
|
|
||||||
.find_by(groupe_instructeurs: { procedure_id: procedure_id })
|
|
||||||
.procedure_presentation_or_default_and_errors
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications_for_dossier(dossier)
|
def notifications_for_dossier(dossier)
|
||||||
|
@ -355,4 +357,11 @@ class Instructeur < ApplicationRecord
|
||||||
.merge(followed_dossiers)
|
.merge(followed_dossiers)
|
||||||
.with_notifications
|
.with_notifications
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assign_to_for_procedure_id(procedure_id)
|
||||||
|
assign_to
|
||||||
|
.joins(:groupe_instructeur)
|
||||||
|
.includes(:instructeur, :procedure)
|
||||||
|
.find_by(groupe_instructeurs: { procedure_id: procedure_id })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
= render Dossiers::MessageComponent.new(commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert)
|
= render Dossiers::MessageComponent.new(commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert)
|
||||||
|
|
||||||
- if current_user.instructeur? && @commentaire.dossier_correction.present?
|
- if current_user.instructeur? && @commentaire.dossier_correction.present?
|
||||||
= turbo_stream.replace 'header-top', partial: 'instructeurs/dossiers/header_top', locals: { dossier: @commentaire.dossier }
|
= turbo_stream.replace 'header-top', partial: 'instructeurs/dossiers/header_top', locals: { dossier: @commentaire.dossier, procedure_presentation: @procedure_presentation }
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
locals: { steps: [[t('show_procedure', scope: [:layouts, :breadcrumb], libelle: dossier.procedure.libelle.truncate(22)), instructeur_procedure_path(dossier.procedure)],
|
locals: { steps: [[t('show_procedure', scope: [:layouts, :breadcrumb], libelle: dossier.procedure.libelle.truncate(22)), instructeur_procedure_path(dossier.procedure)],
|
||||||
[t('show_dossier', scope: [:layouts, :breadcrumb], dossier_id: dossier.id, owner_name: dossier.owner_name)]] }
|
[t('show_dossier', scope: [:layouts, :breadcrumb], dossier_id: dossier.id, owner_name: dossier.owner_name)]] }
|
||||||
|
|
||||||
= render partial: 'instructeurs/dossiers/header_top', locals: { dossier: }
|
= render partial: 'instructeurs/dossiers/header_top', locals: { dossier:, procedure_presentation: }
|
||||||
= render partial: 'instructeurs/dossiers/header_bottom', locals: { dossier:, gallery_attachments: }
|
= render partial: 'instructeurs/dossiers/header_bottom', locals: { dossier:, gallery_attachments: }
|
||||||
|
|
||||||
.fr-container
|
.fr-container
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
#header-top.fr-container
|
#header-top.fr-container
|
||||||
.flex
|
= render Instructeurs::DossiersNavigationComponent.new(dossier:, procedure_presentation:, statut: params[:statut])
|
||||||
|
|
||||||
|
.flex.fr-mb-3w
|
||||||
%div
|
%div
|
||||||
.flex.fr-mb-1w
|
.fr-mt-2w.badge-group
|
||||||
= render Instructeurs::BackButtonComponent.new(to: instructeur_procedure_path(dossier.procedure, statut: params[:statut]))
|
|
||||||
%h1.fr-h3.fr-mb-1w
|
|
||||||
= t('show_dossier', scope: [:layouts, :breadcrumb], dossier_id: dossier.id, owner_name: dossier.owner_name)
|
|
||||||
|
|
||||||
|
|
||||||
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
|
|
||||||
.fr-mt-2w.fr-badge-group
|
|
||||||
= procedure_badge(dossier.procedure)
|
= procedure_badge(dossier.procedure)
|
||||||
|
|
||||||
= status_badge(dossier.state)
|
= status_badge(dossier.state)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Annotations privées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Annotations privées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
#dossier-annotations-privees
|
#dossier-annotations-privees
|
||||||
.fr-container
|
.fr-container
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
.container
|
.container
|
||||||
.fr-grid-row
|
.fr-grid-row
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
.container
|
.container
|
||||||
.fr-grid-row
|
.fr-grid-row
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
= turbo_stream.replace 'header-top', partial: 'header_top', locals: { dossier: @dossier }
|
= turbo_stream.replace 'header-top', partial: 'header_top', locals: { dossier: @dossier, procedure_presentation: @procedure_presentation }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- content_for(:title, "Messagerie · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Messagerie · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
= render partial: "shared/dossiers/messagerie", locals: { dossier: @dossier, connected_user: current_instructeur, messagerie_seen_at: @messagerie_seen_at , new_commentaire: @commentaire, form_url: commentaire_instructeur_dossier_path(@dossier.procedure, @dossier, statut: params[:statut]) }
|
= render partial: "shared/dossiers/messagerie", locals: { dossier: @dossier, connected_user: current_instructeur, messagerie_seen_at: @messagerie_seen_at , new_commentaire: @commentaire, form_url: commentaire_instructeur_dossier_path(@dossier.procedure, @dossier, statut: params[:statut]) }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Personnes impliquées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Personnes impliquées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
.personnes-impliquees.container
|
.personnes-impliquees.container
|
||||||
= render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients }
|
= render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Pièces jointes")
|
- content_for(:title, "Pièces jointes")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
.fr-container
|
.fr-container
|
||||||
.gallery.gallery-pieces-jointes{ "data-controller": "lightbox" }
|
.gallery.gallery-pieces-jointes{ "data-controller": "lightbox" }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Réaffectation · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Réaffectation · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
.container.groupe-instructeur
|
.container.groupe-instructeur
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for(:title, "Demande · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
- content_for(:title, "Demande · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||||
|
|
||||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments, procedure_presentation: @procedure_presentation }
|
||||||
|
|
||||||
|
|
||||||
- if @dossier.etablissement&.as_degraded_mode?
|
- if @dossier.etablissement&.as_degraded_mode?
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.procedure-header
|
.procedure-header
|
||||||
.clipboard-container.flex
|
.align-start.flex
|
||||||
= render Instructeurs::BackButtonComponent.new(to: instructeur_procedures_path)
|
= render Instructeurs::BackButtonComponent.new(to: instructeur_procedures_path)
|
||||||
%h1.fr-h3.fr-mb-0
|
%h1.fr-h3.fr-mb-0
|
||||||
= "#{procedure_libelle procedure} (n°#{procedure.id})"
|
= "#{procedure_libelle procedure} (n°#{procedure.id})"
|
||||||
|
|
|
@ -485,6 +485,8 @@ Rails.application.routes.draw do
|
||||||
resources :dossiers, only: [:show, :destroy], param: :dossier_id, path: "(:statut)/dossiers", defaults: { statut: 'a-suivre' } do
|
resources :dossiers, only: [:show, :destroy], param: :dossier_id, path: "(:statut)/dossiers", defaults: { statut: 'a-suivre' } do
|
||||||
member do
|
member do
|
||||||
resources :commentaires, only: [:destroy]
|
resources :commentaires, only: [:destroy]
|
||||||
|
get 'next'
|
||||||
|
get 'previous'
|
||||||
post 'repousser-expiration' => 'dossiers#extend_conservation'
|
post 'repousser-expiration' => 'dossiers#extend_conservation'
|
||||||
post 'repousser-expiration-and-restore' => 'dossiers#extend_conservation_and_restore'
|
post 'repousser-expiration-and-restore' => 'dossiers#extend_conservation_and_restore'
|
||||||
post 'dossier_labels' => 'dossiers#dossier_labels'
|
post 'dossier_labels' => 'dossiers#dossier_labels'
|
||||||
|
|
|
@ -984,6 +984,68 @@ describe Instructeurs::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'navigation accross next/prev dossiers' do
|
||||||
|
let(:dossier_id) { dossier.id }
|
||||||
|
let(:statut) { 'a-suivre' }
|
||||||
|
let(:previous_dossier) { create(:dossier, :en_construction, procedure:) }
|
||||||
|
let(:next_dossier) { create(:dossier, :en_construction, procedure:) }
|
||||||
|
let(:cached_ids) { [previous_dossier, dossier, next_dossier].map(&:id) }
|
||||||
|
before do
|
||||||
|
cache = Cache::ProcedureDossierPagination.new(procedure_presentation: double(procedure:, instructeur:), statut:)
|
||||||
|
cache.save_context(incoming_page: 1, ids: cached_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when nexting' do
|
||||||
|
subject { get :next, params: { procedure_id: procedure.id, dossier_id: from_id, statut: } }
|
||||||
|
|
||||||
|
context 'when their is a next id' do
|
||||||
|
let(:from_id) { dossier.id }
|
||||||
|
it { is_expected.to redirect_to(instructeur_dossier_path(procedure_id: procedure.id, dossier_id: next_dossier.id)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when their is not next id (en of list)' do
|
||||||
|
let(:from_id) { cached_ids.last }
|
||||||
|
it 'redirect on fallback location being current dossier and flashes an error' do
|
||||||
|
expect(subject).to redirect_to(instructeur_dossier_path(procedure_id: procedure.id, dossier_id: from_id))
|
||||||
|
expect(flash.alert).to eq("Une erreur est survenue")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when id does not exists' do
|
||||||
|
let(:from_id) { 'kthxbye' }
|
||||||
|
it 'redirect on fallback location being current dossier and flashes an error' do
|
||||||
|
expect(subject).to redirect_to(instructeur_procedure_path(procedure_id: procedure.id))
|
||||||
|
expect(flash.alert).to eq("Une erreur est survenue")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when previousing' do
|
||||||
|
subject { get :previous, params: { procedure_id: procedure.id, dossier_id: from_id, statut: } }
|
||||||
|
|
||||||
|
context 'when their is a previous id' do
|
||||||
|
let(:from_id) { dossier.id }
|
||||||
|
it { is_expected.to redirect_to(instructeur_dossier_path(procedure_id: procedure.id, dossier_id: previous_dossier.id)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when their is not previous id (before list)' do
|
||||||
|
let(:from_id) { cached_ids.first }
|
||||||
|
it 'redirect on fallback location being current dossier and flashes an error' do
|
||||||
|
expect(subject).to redirect_to(instructeur_dossier_path(procedure_id: procedure.id, dossier_id: from_id))
|
||||||
|
expect(flash.alert).to eq("Une erreur est survenue")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when id does not exists' do
|
||||||
|
let(:from_id) { 'kthxbye' }
|
||||||
|
it 'redirect on fallback location being current dossier and flashes an error' do
|
||||||
|
expect(subject).to redirect_to(instructeur_procedure_path(procedure_id: procedure.id))
|
||||||
|
expect(flash.alert).to eq("Une erreur est survenue")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "#update_annotations" do
|
describe "#update_annotations" do
|
||||||
let(:procedure) do
|
let(:procedure) do
|
||||||
create(:procedure, :published, types_de_champ_public:, types_de_champ_private:, instructeurs: instructeurs)
|
create(:procedure, :published, types_de_champ_public:, types_de_champ_private:, instructeurs: instructeurs)
|
||||||
|
|
|
@ -670,6 +670,18 @@ describe Instructeurs::ProceduresController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'caches statut and page query param' do
|
||||||
|
let(:statut) { 'tous' }
|
||||||
|
let(:page) { '1' }
|
||||||
|
let!(:dossier) { create(:dossier, :accepte, procedure:) }
|
||||||
|
before { sign_in(instructeur.user) }
|
||||||
|
subject { get :show, params: { procedure_id: procedure.id, statut:, page: } }
|
||||||
|
it 'changes cached value' do
|
||||||
|
expect { subject }.to change { Cache::ProcedureDossierPagination.new(statut:, procedure_presentation: double(procedure:, instructeur:)).send(:read_cache) }
|
||||||
|
.from({}).to(ids: [dossier.id], incoming_page: page)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#deleted_dossiers' do
|
describe '#deleted_dossiers' do
|
||||||
|
|
86
spec/models/cache/procedure_dossier_pagination_spec.rb
vendored
Normal file
86
spec/models/cache/procedure_dossier_pagination_spec.rb
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe Cache::ProcedureDossierPagination do
|
||||||
|
let(:instructeur) { double(id: 1) }
|
||||||
|
let(:procedure) { double(id: 1) }
|
||||||
|
let(:procedure_presentation) { double(instructeur:, procedure:) }
|
||||||
|
let(:instance) { described_class.new(procedure_presentation:, statut: 'a-suivre') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
instance.save_context(ids: cached_ids, incoming_page: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'next_dossier_id' do
|
||||||
|
context 'when procedure.dossiers.by_statut has only one page' do
|
||||||
|
let(:cached_ids) { [3, 4] }
|
||||||
|
before do
|
||||||
|
allow(instance).to receive(:fetch_all_ids).and_return(cached_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'find next until the end' do
|
||||||
|
expect(instance.next_dossier_id(from_id: cached_ids.last)).to eq(nil)
|
||||||
|
expect(instance.next_dossier_id(from_id: cached_ids.first)).to eq(cached_ids.last)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when procedure.dossiers.by_statut has more than one page' do
|
||||||
|
let(:cached_ids) { [2, 3, 4] }
|
||||||
|
let(:next_page_ids) { (0..10).to_a }
|
||||||
|
|
||||||
|
subject { instance.next_dossier_id(from_id: cached_ids.last) }
|
||||||
|
before do
|
||||||
|
allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'refreshes paginated_ids' do
|
||||||
|
expect { subject }.to change { instance.send(:ids) }.from(cached_ids).to(next_page_ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when procedure.dossiers.by_statut does not include searched dossiers anymore' do
|
||||||
|
let(:cached_ids) { [] }
|
||||||
|
let(:next_page_ids) { [] }
|
||||||
|
before { allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids) }
|
||||||
|
|
||||||
|
it 'works' do
|
||||||
|
expect(instance.next_dossier_id(from_id: 50)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'previous_dossier_id' do
|
||||||
|
context 'when procedure.dossiers.by_statut has only one page' do
|
||||||
|
let(:cached_ids) { [3, 4] }
|
||||||
|
before do
|
||||||
|
allow(instance).to receive(:fetch_all_ids).and_return(cached_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'find next until the end' do
|
||||||
|
expect(instance.previous_dossier_id(from_id: cached_ids.last)).to eq(cached_ids.first)
|
||||||
|
expect(instance.previous_dossier_id(from_id: cached_ids.first)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when procedure.dossiers.by_statut has more than one page' do
|
||||||
|
let(:cached_ids) { [11, 12, 13] }
|
||||||
|
subject { instance.previous_dossier_id(from_id: cached_ids.first) }
|
||||||
|
let(:next_page_ids) { (11..20).to_a }
|
||||||
|
before do
|
||||||
|
allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works' do
|
||||||
|
expect { subject }.to change { instance.send(:ids) }.from(cached_ids).to(next_page_ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when procedure.dossiers.by_statut does not include searched dossiers anymore' do
|
||||||
|
let(:cached_ids) { [] }
|
||||||
|
before { allow(instance).to receive(:fetch_all_ids).and_return([]) }
|
||||||
|
|
||||||
|
it 'works' do
|
||||||
|
expect(instance.previous_dossier_id(from_id: 50)).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,12 +3,15 @@
|
||||||
describe 'instructeurs/dossiers/annotations_privees', type: :view do
|
describe 'instructeurs/dossiers/annotations_privees', type: :view do
|
||||||
let(:current_instructeur) { create(:instructeur) }
|
let(:current_instructeur) { create(:instructeur) }
|
||||||
let(:dossier) { create(:dossier, :en_construction) }
|
let(:dossier) { create(:dossier, :en_construction) }
|
||||||
|
let(:procedure_presentation) { double(instructeur: current_instructeur, procedure: dossier.procedure) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
sign_in(current_instructeur.user)
|
sign_in(current_instructeur.user)
|
||||||
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
|
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
|
||||||
|
|
||||||
allow(controller).to receive(:params).and_return({ statut: 'a-suivre' })
|
allow(controller).to receive(:params).and_return({ statut: 'a-suivre' })
|
||||||
assign(:dossier, dossier)
|
assign(:dossier, dossier)
|
||||||
|
assign(:procedure_presentation, procedure_presentation)
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { render }
|
subject { render }
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
describe 'instructeurs/dossiers/show', type: :view do
|
describe 'instructeurs/dossiers/show', type: :view do
|
||||||
let(:current_instructeur) { create(:instructeur) }
|
let(:current_instructeur) { create(:instructeur) }
|
||||||
let(:dossier) { create(:dossier, :en_construction) }
|
let(:dossier) { create(:dossier, :en_construction) }
|
||||||
|
let(:statut) { { statut: 'tous' } }
|
||||||
|
let(:procedure_presentation) { double(instructeur: current_instructeur, procedure: dossier.procedure) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
sign_in(current_instructeur.user)
|
sign_in(current_instructeur.user)
|
||||||
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
|
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
|
||||||
allow(controller).to receive(:params).and_return(statut: 'a-suivre')
|
allow(controller).to receive(:params).and_return(statut:)
|
||||||
assign(:dossier, dossier)
|
assign(:dossier, dossier)
|
||||||
|
assign(:procedure_presentation, procedure_presentation)
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { render }
|
subject { render }
|
||||||
|
@ -17,6 +20,12 @@ describe 'instructeurs/dossiers/show', type: :view do
|
||||||
expect(subject).to have_text("Dossier nº #{dossier.id}")
|
expect(subject).to have_text("Dossier nº #{dossier.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when procedure statut / page was saved in session' do
|
||||||
|
it 'renders back button with saved state' do
|
||||||
|
expect(subject).to have_selector("a[href=\"#{instructeur_procedure_path(dossier.procedure, statut: statut)}\"]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'renders the dossier infos' do
|
it 'renders the dossier infos' do
|
||||||
expect(subject).to have_text('Identité')
|
expect(subject).to have_text('Identité')
|
||||||
expect(subject).to have_text('Demande')
|
expect(subject).to have_text('Demande')
|
||||||
|
|
Loading…
Reference in a new issue