Merge pull request #10797 from demarches-simplifiees/gallery_v2
ETQ instructeur je vois la provenance des pièces jointes ainsi que leur date d'ajout ou de mise à jour
This commit is contained in:
commit
ba330bfa98
32 changed files with 377 additions and 98 deletions
|
@ -52,7 +52,15 @@
|
|||
flex-wrap: wrap;
|
||||
|
||||
.gallery-item {
|
||||
margin: 0 2rem 1.5rem 0;
|
||||
margin: 0 2rem 3rem 0;
|
||||
|
||||
.fr-download {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.fr-text--sm {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
86
app/components/attachment/gallery_item_component.rb
Normal file
86
app/components/attachment/gallery_item_component.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Attachment::GalleryItemComponent < ApplicationComponent
|
||||
include GalleryHelper
|
||||
attr_reader :attachment, :seen_at
|
||||
|
||||
def initialize(attachment:, gallery_demande: false, seen_at: nil)
|
||||
@attachment = attachment
|
||||
@gallery_demande = gallery_demande
|
||||
@seen_at = seen_at
|
||||
end
|
||||
|
||||
def blob
|
||||
attachment.blob
|
||||
end
|
||||
|
||||
def gallery_demande? = @gallery_demande
|
||||
|
||||
def libelle
|
||||
from_dossier? ? attachment.record.libelle : 'Pièce jointe au message'
|
||||
end
|
||||
|
||||
def origin
|
||||
case
|
||||
when from_dossier?
|
||||
'Dossier usager'
|
||||
when from_messagerie_instructeur?
|
||||
'Messagerie (instructeur)'
|
||||
when from_messagerie_usager?
|
||||
'Messagerie (usager)'
|
||||
end
|
||||
end
|
||||
|
||||
def title
|
||||
"#{libelle} -- #{sanitize(blob.filename.to_s)}"
|
||||
end
|
||||
|
||||
def gallery_link(blob, &block)
|
||||
if displayable_image?(blob)
|
||||
link_to image_url(blob_url(attachment)), title: title, data: { src: blob.url }, class: 'gallery-link' do
|
||||
yield
|
||||
end
|
||||
elsif displayable_pdf?(blob)
|
||||
link_to blob.url, id: blob.id, data: { iframe: true, src: blob.url }, class: 'gallery-link', type: blob.content_type, title: title do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def created_at
|
||||
attachment.record.created_at
|
||||
end
|
||||
|
||||
def updated?
|
||||
from_dossier? && updated_at > attachment.record.dossier.depose_at
|
||||
end
|
||||
|
||||
def updated_at
|
||||
blob.created_at
|
||||
end
|
||||
|
||||
def badge_updated_class
|
||||
class_names(
|
||||
"fr-badge fr-badge--sm" => true,
|
||||
"highlighted" => seen_at.present? && updated_at&.>(seen_at)
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def from_dossier?
|
||||
attachment.record.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp])
|
||||
end
|
||||
|
||||
def from_messagerie?
|
||||
attachment.record.is_a?(Commentaire)
|
||||
end
|
||||
|
||||
def from_messagerie_instructeur?
|
||||
from_messagerie? && attachment.record.instructeur.present?
|
||||
end
|
||||
|
||||
def from_messagerie_usager?
|
||||
from_messagerie? && attachment.record.instructeur.nil?
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
en:
|
||||
created_at: "Added on %{datetime}"
|
||||
updated_at: "Updated on %{datetime}"
|
|
@ -0,0 +1,3 @@
|
|||
fr:
|
||||
created_at: "Ajoutée le %{datetime}"
|
||||
updated_at: "Modifiée le %{datetime}"
|
|
@ -0,0 +1,26 @@
|
|||
.gallery-item
|
||||
- if !gallery_demande?
|
||||
%p.fr-tag.fr-tag--sm.fr-mb-3v= origin
|
||||
- if displayable_pdf?(blob) || displayable_image?(blob)
|
||||
= gallery_link(blob) do
|
||||
.thumbnail
|
||||
= image_tag(representation_url_for(attachment), loading: :lazy)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
Visualiser
|
||||
- if !gallery_demande?
|
||||
.fr-text--sm.fr-mt-2v.fr-mb-1v
|
||||
= libelle.truncate(25)
|
||||
= render Attachment::ShowComponent.new(attachment:, truncate: true, new_tab: gallery_demande?)
|
||||
- if !gallery_demande?
|
||||
.fr-mt-2v.fr-mb-2v{ class: badge_updated_class }
|
||||
= t(updated? ? '.updated_at' : '.created_at', datetime: helpers.try_format_datetime(updated_at, format: :veryshort))
|
||||
- else
|
||||
.thumbnail
|
||||
= image_tag('apercu-indisponible.png')
|
||||
- if !gallery_demande?
|
||||
.fr-text--sm.fr-mt-2v.fr-mb-1v
|
||||
= libelle.truncate(25)
|
||||
= render Attachment::ShowComponent.new(attachment:, truncate: true, new_tab: gallery_demande?)
|
||||
- if !gallery_demande?
|
||||
.fr-mt-2v.fr-mb-2v{ class: badge_updated_class }
|
||||
= t(updated? ? '.updated_at' : '.created_at', datetime: helpers.try_format_datetime(updated_at, format: :veryshort))
|
|
@ -13,11 +13,13 @@ module Instructeurs
|
|||
|
||||
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 :set_gallery_attachments, only: [:show, :pieces_jointes, :annotations_privees, :avis, :messagerie, :personnes_impliquees, :reaffectation]
|
||||
after_action :mark_demande_as_read, only: :show
|
||||
|
||||
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_annotations_privees_as_read, only: [:annotations_privees, :update_annotations]
|
||||
after_action :mark_pieces_jointes_as_read, only: [:pieces_jointes]
|
||||
|
||||
def extend_conservation
|
||||
dossier.extend_conservation(1.month)
|
||||
|
@ -371,25 +373,8 @@ module Instructeurs
|
|||
end
|
||||
|
||||
def pieces_jointes
|
||||
@dossier = current_instructeur.dossiers.find(params[:dossier_id])
|
||||
|
||||
champs_attachments_and_libelles = @dossier
|
||||
.champs
|
||||
.filter { _1.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp]) }
|
||||
.flat_map do |c|
|
||||
c.piece_justificative_file.map do |attachment|
|
||||
[attachment, c.libelle]
|
||||
end
|
||||
end
|
||||
|
||||
commentaires_attachments_and_libelles = @dossier
|
||||
.commentaires
|
||||
.map(&:piece_jointe)
|
||||
.map(&:attachments)
|
||||
.flatten
|
||||
.map { [_1, 'Messagerie'] }
|
||||
|
||||
@attachments_and_libelles = champs_attachments_and_libelles + commentaires_attachments_and_libelles
|
||||
@dossier = dossier
|
||||
@pieces_jointes_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.pieces_jointes_seen_at
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -471,6 +456,10 @@ module Instructeurs
|
|||
current_instructeur.mark_tab_as_seen(dossier, :annotations_privees)
|
||||
end
|
||||
|
||||
def mark_pieces_jointes_as_read
|
||||
current_instructeur.mark_tab_as_seen(dossier, :pieces_jointes)
|
||||
end
|
||||
|
||||
def aasm_error_message(exception, target_state:)
|
||||
if exception.originating_state == target_state
|
||||
"Le dossier est déjà #{dossier_display_state(target_state, lower: true)}."
|
||||
|
@ -498,5 +487,26 @@ module Instructeurs
|
|||
redirect_back(fallback_location: instructeur_dossier_path(procedure, dossier_in_batch))
|
||||
end
|
||||
end
|
||||
|
||||
def set_gallery_attachments
|
||||
gallery_attachments_ids = Rails.cache.fetch([dossier, "gallery_attachments"], expires_in: 10.minutes) do
|
||||
champs_attachments_ids = dossier
|
||||
.champs
|
||||
.where(type: [Champs::PieceJustificativeChamp.name, Champs::TitreIdentiteChamp.name])
|
||||
.flat_map(&:piece_justificative_file)
|
||||
.map(&:id)
|
||||
|
||||
commentaires_attachments_ids = dossier
|
||||
.commentaires
|
||||
.includes(piece_jointe_attachments: :blob)
|
||||
.map(&:piece_jointe)
|
||||
.map(&:attachments)
|
||||
.flatten
|
||||
.map(&:id)
|
||||
|
||||
champs_attachments_ids + commentaires_attachments_ids
|
||||
end
|
||||
@gallery_attachments = ActiveStorage::Attachment.where(id: gallery_attachments_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -341,7 +341,10 @@ module Users
|
|||
@commentaire = CommentaireService.create(current_user, dossier, commentaire_params)
|
||||
|
||||
if @commentaire.errors.empty?
|
||||
@commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
|
||||
timestamps = [:last_commentaire_updated_at, :updated_at]
|
||||
timestamps << :last_commentaire_piece_jointe_updated_at if @commentaire.piece_jointe.attached?
|
||||
|
||||
@commentaire.dossier.touch(*timestamps)
|
||||
|
||||
flash.notice = t('.message_send')
|
||||
redirect_to messagerie_dossier_path(dossier)
|
||||
|
|
|
@ -9,6 +9,12 @@ module GalleryHelper
|
|||
blob.variable? && blob.content_type.in?(AUTHORIZED_IMAGE_TYPES)
|
||||
end
|
||||
|
||||
def representation_url_for(attachment)
|
||||
return variant_url_for(attachment) if displayable_image?(attachment.blob)
|
||||
|
||||
preview_url_for(attachment) if displayable_pdf?(attachment.blob)
|
||||
end
|
||||
|
||||
def preview_url_for(attachment)
|
||||
preview = attachment.preview(resize_to_limit: [400, 400])
|
||||
preview.image.attached? ? preview.processed.url : 'pdf-placeholder.png'
|
||||
|
|
|
@ -71,6 +71,7 @@ module DossierCloneConcern
|
|||
diff = make_diff(editing_fork)
|
||||
apply_diff(diff)
|
||||
touch(:last_champ_updated_at)
|
||||
touch(:last_champ_piece_jointe_updated_at) if diff[:updated].any? { |c| c.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp]) }
|
||||
end
|
||||
reload
|
||||
index_search_terms_later
|
||||
|
|
|
@ -377,7 +377,9 @@ class Dossier < ApplicationRecord
|
|||
' OR groupe_instructeur_updated_at > follows.demande_seen_at' \
|
||||
' OR last_champ_private_updated_at > follows.annotations_privees_seen_at' \
|
||||
' OR last_avis_updated_at > follows.avis_seen_at' \
|
||||
' OR last_commentaire_updated_at > follows.messagerie_seen_at')
|
||||
' OR last_commentaire_updated_at > follows.messagerie_seen_at' \
|
||||
' OR last_commentaire_piece_jointe_updated_at > follows.pieces_jointes_seen_at' \
|
||||
' OR last_champ_piece_jointe_updated_at > follows.pieces_jointes_seen_at')
|
||||
.distinct
|
||||
end
|
||||
|
||||
|
|
|
@ -18,5 +18,6 @@ class Follow < ApplicationRecord
|
|||
self.annotations_privees_seen_at ||= Time.zone.now
|
||||
self.avis_seen_at ||= Time.zone.now
|
||||
self.messagerie_seen_at ||= Time.zone.now
|
||||
self.pieces_jointes_seen_at ||= Time.zone.now
|
||||
end
|
||||
end
|
||||
|
|
|
@ -125,10 +125,11 @@ class Instructeur < ApplicationRecord
|
|||
annotations_privees = dossier.last_champ_private_updated_at&.>(follow.annotations_privees_seen_at) || false
|
||||
avis_notif = dossier.last_avis_updated_at&.>(follow.avis_seen_at) || false
|
||||
messagerie = dossier.last_commentaire_updated_at&.>(follow.messagerie_seen_at) || false
|
||||
pieces_jointes = dossier.last_champ_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || dossier.last_commentaire_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || false
|
||||
|
||||
annotations_hash(demande, annotations_privees, avis_notif, messagerie)
|
||||
annotations_hash(demande, annotations_privees, avis_notif, messagerie, pieces_jointes)
|
||||
else
|
||||
annotations_hash(false, false, false, false)
|
||||
annotations_hash(false, false, false, false, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -314,12 +315,13 @@ class Instructeur < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def annotations_hash(demande, annotations_privees, avis, messagerie)
|
||||
def annotations_hash(demande, annotations_privees, avis, messagerie, pieces_jointes)
|
||||
{
|
||||
demande: demande,
|
||||
annotations_privees: annotations_privees,
|
||||
avis: avis,
|
||||
messagerie: messagerie
|
||||
messagerie: messagerie,
|
||||
pieces_jointes: pieces_jointes
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
.sub-header
|
||||
= render partial: 'instructeurs/dossiers/header_top', locals: { dossier: }
|
||||
= render partial: 'instructeurs/dossiers/header_bottom', locals: { dossier: }
|
||||
= render partial: 'instructeurs/dossiers/header_bottom', locals: { dossier:, gallery_attachments: }
|
||||
|
||||
.fr-container
|
||||
.print-header
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
instructeur_dossier_path(dossier.procedure, dossier),
|
||||
notification: notifications_summary[:demande])
|
||||
|
||||
- if dossier.champs.map(&:piece_justificative_file).flatten.any?
|
||||
- if gallery_attachments.present?
|
||||
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.attachments'),
|
||||
pieces_jointes_instructeur_dossier_path(dossier.procedure, dossier))
|
||||
pieces_jointes_instructeur_dossier_path(dossier.procedure, dossier),
|
||||
notification: notifications_summary[:pieces_jointes])
|
||||
|
||||
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.private_annotations'),
|
||||
annotations_privees_instructeur_dossier_path(dossier.procedure, dossier),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Annotations privées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
#dossier-annotations-privees
|
||||
.fr-container
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
.container
|
||||
.fr-grid-row
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Avis · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
.container
|
||||
.fr-grid-row
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- content_for(:title, "Messagerie · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
= 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) }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Personnes impliquées · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
.personnes-impliquees.container
|
||||
= render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients }
|
||||
|
|
|
@ -1,35 +1,8 @@
|
|||
- content_for(:title, "Pièces jointes")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
.fr-container
|
||||
.gallery.gallery-pieces-jointes{ "data-controller": "lightbox" }
|
||||
- @attachments_and_libelles.each do |attachment, libelle|
|
||||
.gallery-item
|
||||
- blob = attachment.blob
|
||||
- if displayable_pdf?(blob)
|
||||
= link_to blob.url, id: blob.id, data: { iframe: true, src: blob.url }, class: 'gallery-link', type: blob.content_type, title: "#{libelle} -- #{sanitize(blob.filename.to_s)}" do
|
||||
.thumbnail
|
||||
= image_tag(preview_url_for(attachment), loading: :lazy)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
Visualiser
|
||||
.champ-libelle
|
||||
= libelle.truncate(25)
|
||||
= render Attachment::ShowComponent.new(attachment: attachment, truncate: true)
|
||||
|
||||
- elsif displayable_image?(blob)
|
||||
= link_to image_url(blob_url(attachment)), title: "#{libelle} -- #{sanitize(blob.filename.to_s)}", data: { src: blob.url }, class: 'gallery-link' do
|
||||
.thumbnail
|
||||
= image_tag(variant_url_for(attachment), loading: :lazy)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
Visualiser
|
||||
.champ-libelle
|
||||
= libelle.truncate(25)
|
||||
= render Attachment::ShowComponent.new(attachment: attachment, truncate: true)
|
||||
|
||||
- else
|
||||
.thumbnail
|
||||
= image_tag('apercu-indisponible.png')
|
||||
.champ-libelle
|
||||
= libelle.truncate(25)
|
||||
= render Attachment::ShowComponent.new(attachment: attachment, truncate: true)
|
||||
- @gallery_attachments.each do |attachment|
|
||||
= render Attachment::GalleryItemComponent.new(attachment:, seen_at: @pieces_jointes_seen_at)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Réaffectation · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
.container.groupe-instructeur
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for(:title, "Demande · Dossier nº #{@dossier.id} (#{@dossier.owner_name})")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
= render partial: "header", locals: { dossier: @dossier, gallery_attachments: @gallery_attachments }
|
||||
|
||||
|
||||
- if @dossier.etablissement&.as_degraded_mode?
|
||||
|
|
|
@ -2,25 +2,7 @@
|
|||
- if profile == 'instructeur'
|
||||
.gallery-items-list
|
||||
- champ.piece_justificative_file.attachments.with_all_variant_records.each do |attachment|
|
||||
.gallery-item
|
||||
- blob = attachment.blob
|
||||
- if displayable_pdf?(blob)
|
||||
= link_to blob.url, id: blob.id, data: { iframe: true, src: blob.url }, class: 'gallery-link', type: blob.content_type, title: "#{champ.libelle} -- #{sanitize(blob.filename.to_s)}" do
|
||||
.thumbnail
|
||||
= image_tag(preview_url_for(attachment), loading: :lazy)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
= 'Visualiser'
|
||||
|
||||
- elsif displayable_image?(blob)
|
||||
= link_to image_url(blob_url(attachment)), title: "#{champ.libelle} -- #{sanitize(blob.filename.to_s)}", data: { src: blob.url }, class: 'gallery-link' do
|
||||
.thumbnail
|
||||
= image_tag(variant_url_for(attachment), loading: :lazy)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
= 'Visualiser'
|
||||
- else
|
||||
.thumbnail
|
||||
= image_tag('apercu-indisponible.png')
|
||||
= render Attachment::ShowComponent.new(attachment:, new_tab: true, truncate: true)
|
||||
= render Attachment::GalleryItemComponent.new(attachment:, gallery_demande: true)
|
||||
- else
|
||||
%ul
|
||||
- champ.piece_justificative_file.attachments.each do |attachment|
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPiecesJointesUpdatesToDossiers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :dossiers, :last_champ_piece_jointe_updated_at, :datetime
|
||||
add_column :dossiers, :last_commentaire_piece_jointe_updated_at, :datetime
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPiecesJointesSeenAtToFollows < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
add_column :follows, :pieces_jointes_seen_at, :datetime
|
||||
change_column_default :follows, :pieces_jointes_seen_at, from: nil, to: 'CURRENT_TIMESTAMP'
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :follows, :pieces_jointes_seen_at
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class BackfillFollowsWithPiecesJointesSeenAt < ActiveRecord::Migration[7.0]
|
||||
disable_ddl_transaction!
|
||||
def up
|
||||
Follow.in_batches do |relation|
|
||||
relation.update_all pieces_jointes_seen_at: Time.zone.now
|
||||
sleep(0.001) # throttle
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
|
@ -488,8 +488,10 @@ ActiveRecord::Schema[7.0].define(version: 2024_09_23_125619) do
|
|||
t.datetime "hidden_by_user_at", precision: nil
|
||||
t.datetime "identity_updated_at", precision: nil
|
||||
t.datetime "last_avis_updated_at", precision: nil
|
||||
t.datetime "last_champ_piece_jointe_updated_at"
|
||||
t.datetime "last_champ_private_updated_at", precision: nil
|
||||
t.datetime "last_champ_updated_at", precision: nil
|
||||
t.datetime "last_commentaire_piece_jointe_updated_at"
|
||||
t.datetime "last_commentaire_updated_at", precision: nil
|
||||
t.string "mandataire_first_name"
|
||||
t.string "mandataire_last_name"
|
||||
|
@ -689,6 +691,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_09_23_125619) do
|
|||
t.integer "dossier_id", null: false
|
||||
t.integer "instructeur_id", null: false
|
||||
t.datetime "messagerie_seen_at", precision: nil, null: false
|
||||
t.datetime "pieces_jointes_seen_at"
|
||||
t.datetime "unfollowed_at", precision: nil
|
||||
t.datetime "updated_at", precision: nil
|
||||
t.index ["dossier_id"], name: "index_follows_on_dossier_id"
|
||||
|
|
104
spec/components/attachment/gallery_item_component_spec.rb
Normal file
104
spec/components/attachment/gallery_item_component_spec.rb
Normal file
|
@ -0,0 +1,104 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Attachment::GalleryItemComponent, type: :component do
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
|
||||
let(:dossier) { create(:dossier, :with_populated_champs, :en_construction, procedure:) }
|
||||
let(:filename) { attachment.blob.filename.to_s }
|
||||
let(:gallery_demande) { false }
|
||||
let(:seen_at) { nil }
|
||||
let(:now) { Time.zone.now }
|
||||
|
||||
let(:component) { described_class.new(attachment: attachment, gallery_demande:, seen_at: seen_at) }
|
||||
|
||||
subject { render_inline(component).to_html }
|
||||
|
||||
context "when attachment is from a piece justificative champ" do
|
||||
let(:champ) { dossier.champs.first }
|
||||
let(:libelle) { champ.libelle }
|
||||
let(:attachment) { champ.piece_justificative_file.attachments.first }
|
||||
|
||||
# Correspond au cas standard où le blob est créé avant le dépôt du dossier
|
||||
before { dossier.touch(:depose_at) }
|
||||
|
||||
it "displays libelle, link, tag and renders title" do
|
||||
expect(subject).to have_text(libelle)
|
||||
expect(subject).not_to have_text('Pièce jointe au message')
|
||||
expect(subject).to have_link(filename)
|
||||
expect(subject).to have_text('Dossier usager')
|
||||
expect(component.title).to eq("#{libelle} -- #{filename}")
|
||||
end
|
||||
|
||||
it "displays when gallery item has been added" do
|
||||
expect(subject).to have_text('Ajoutée le')
|
||||
expect(subject).not_to have_css('.highlighted')
|
||||
expect(subject).to have_text(component.helpers.try_format_datetime(attachment.record.created_at, format: :veryshort))
|
||||
end
|
||||
|
||||
context "when gallery item has been updated" do
|
||||
# un nouveau blob est créé après modification d'un champ pièce justificative
|
||||
before { attachment.blob.touch(:created_at) }
|
||||
|
||||
it 'displays the right text' do
|
||||
expect(subject).to have_text('Modifiée le')
|
||||
end
|
||||
end
|
||||
|
||||
context "when gallery item is in page Demande" do
|
||||
let(:gallery_demande) { true }
|
||||
|
||||
it "does not display libelle" do
|
||||
expect(subject).not_to have_text(libelle)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when attachment is from a commentaire" do
|
||||
let(:commentaire) { create(:commentaire, :with_file, dossier: dossier) }
|
||||
let(:attachment) { commentaire.piece_jointe.first }
|
||||
|
||||
context 'from an usager' do
|
||||
it "displays a generic libelle, link, tag and renders title" do
|
||||
expect(subject).to have_text('Pièce jointe au message')
|
||||
expect(subject).to have_link(filename)
|
||||
expect(subject).to have_text('Messagerie (usager)')
|
||||
expect(component.title).to eq("Pièce jointe au message -- #{filename}")
|
||||
end
|
||||
|
||||
context "when instructeur has not seen it yet" do
|
||||
let(:seen_at) { now - 1.day }
|
||||
|
||||
before do
|
||||
attachment.blob.update(created_at: now)
|
||||
end
|
||||
|
||||
it 'displays datetime in the right style' do
|
||||
expect(subject).to have_css('.highlighted')
|
||||
end
|
||||
end
|
||||
|
||||
context "when instructeur has already seen it" do
|
||||
let!(:seen_at) { now }
|
||||
|
||||
before do
|
||||
freeze_time
|
||||
attachment.blob.touch(:created_at)
|
||||
end
|
||||
|
||||
it 'displays datetime in the right style' do
|
||||
expect(subject).not_to have_css('.highlighted')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'from an instructeur' do
|
||||
before { commentaire.update!(instructeur:) }
|
||||
it "displays the right tag" do
|
||||
expect(subject).to have_text('Messagerie (instructeur)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1513,7 +1513,9 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
expect(response.body).to include('Télécharger le fichier logo_test_procedure.png')
|
||||
expect(response.body).to include('Télécharger le fichier RIB.pdf')
|
||||
expect(response.body).to include('Visualiser')
|
||||
expect(assigns(:attachments_and_libelles).count).to eq 3
|
||||
expect(assigns(:gallery_attachments).count).to eq 3
|
||||
expect(assigns(:gallery_attachments)).to all(be_a(ActiveStorage::Attachment))
|
||||
expect([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp, Commentaire]).to include(*assigns(:gallery_attachments).map { _1.record.class })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,4 +74,20 @@ RSpec.describe GalleryHelper, type: :helper do
|
|||
it { is_expected.to eq("pdf-placeholder.png") }
|
||||
end
|
||||
end
|
||||
|
||||
describe ".representation_url_for" do
|
||||
subject { representation_url_for(attachment) }
|
||||
|
||||
context "when attachment is an image with no variant" do
|
||||
let(:file) { fixture_file_upload('spec/fixtures/files/logo_test_procedure.png', 'image/png') }
|
||||
|
||||
it { is_expected.to eq("apercu-indisponible.png") }
|
||||
end
|
||||
|
||||
context "when attachment is a pdf with no preview" do
|
||||
let(:file) { fixture_file_upload('spec/fixtures/files/RIB.pdf', 'application/pdf') }
|
||||
|
||||
it { is_expected.to eq("pdf-placeholder.png") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,7 +44,9 @@ RSpec.describe DossierCloneConcern do
|
|||
expect(new_dossier.last_avis_updated_at).to be_nil
|
||||
expect(new_dossier.last_champ_private_updated_at).to be_nil
|
||||
expect(new_dossier.last_champ_updated_at).to be_nil
|
||||
expect(new_dossier.last_champ_piece_jointe_updated_at).to be_nil
|
||||
expect(new_dossier.last_commentaire_updated_at).to be_nil
|
||||
expect(new_dossier.last_commentaire_piece_jointe_updated_at).to be_nil
|
||||
expect(new_dossier.motivation).to be_nil
|
||||
expect(new_dossier.processed_at).to be_nil
|
||||
end
|
||||
|
|
|
@ -196,7 +196,7 @@ describe Instructeur, type: :model do
|
|||
subject { instructeur.notifications_for_dossier(dossier) }
|
||||
|
||||
context 'when the instructeur has just followed the dossier' do
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on public champs' do
|
||||
|
@ -205,20 +205,20 @@ describe Instructeur, type: :model do
|
|||
dossier.update(last_champ_updated_at: Time.zone.now)
|
||||
}
|
||||
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on identity' do
|
||||
before { dossier.update(identity_updated_at: Time.zone.now) }
|
||||
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on groupe instructeur' do
|
||||
let(:groupe_instructeur) { create(:groupe_instructeur, instructeurs: [instructeur], procedure: dossier.procedure) }
|
||||
before { dossier.assign_to_groupe_instructeur(groupe_instructeur, DossierAssignment.modes.fetch(:auto)) }
|
||||
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: true, annotations_privees: false, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on private champs' do
|
||||
|
@ -227,7 +227,7 @@ describe Instructeur, type: :model do
|
|||
dossier.update(last_champ_private_updated_at: Time.zone.now)
|
||||
}
|
||||
|
||||
it { is_expected.to match({ demande: false, annotations_privees: true, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: false, annotations_privees: true, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'when there is a modification on avis' do
|
||||
|
@ -236,23 +236,34 @@ describe Instructeur, type: :model do
|
|||
dossier.update(last_avis_updated_at: Time.zone.now)
|
||||
}
|
||||
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: true, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: true, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'messagerie' do
|
||||
context 'when there is a new commentaire' do
|
||||
before {
|
||||
create(:commentaire, dossier: dossier, email: 'a@b.com')
|
||||
dossier.update(last_commentaire_updated_at: Time.zone.now)
|
||||
}
|
||||
context 'without a file' do
|
||||
before {
|
||||
create(:commentaire, dossier: dossier, email: 'a@b.com')
|
||||
dossier.update(last_commentaire_updated_at: Time.zone.now)
|
||||
}
|
||||
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: true }) }
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: true, pieces_jointes: false }) }
|
||||
end
|
||||
|
||||
context 'with a file' do
|
||||
before {
|
||||
create(:commentaire, :with_file, dossier: dossier, email: 'a@b.com')
|
||||
dossier.update(last_commentaire_updated_at: Time.zone.now, last_commentaire_piece_jointe_updated_at: Time.zone.now)
|
||||
}
|
||||
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: true, pieces_jointes: true }) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a new commentaire issued by tps' do
|
||||
before { create(:commentaire, dossier: dossier, email: CONTACT_EMAIL) }
|
||||
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false }) }
|
||||
it { is_expected.to match({ demande: false, annotations_privees: false, avis: false, messagerie: false, pieces_jointes: false }) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue