Merge pull request #7169 from tchak/fix-messagerie
Corrige l'exception lors de l'affichage de la messagerie
This commit is contained in:
commit
5007434ab0
35 changed files with 305 additions and 233 deletions
1
Gemfile
1
Gemfile
|
@ -86,6 +86,7 @@ gem 'spreadsheet_architect'
|
|||
gem 'strong_migrations' # lint database migrations
|
||||
gem 'turbo-rails'
|
||||
gem 'typhoeus'
|
||||
gem 'view_component'
|
||||
gem 'warden'
|
||||
gem 'webpacker'
|
||||
gem 'zipline'
|
||||
|
|
|
@ -741,6 +741,9 @@ GEM
|
|||
activemodel (>= 3.0.0)
|
||||
public_suffix
|
||||
vcr (6.0.0)
|
||||
view_component (2.53.0)
|
||||
activesupport (>= 5.0.0, < 8.0)
|
||||
method_source (~> 1.0)
|
||||
virtus (2.0.0)
|
||||
axiom-types (~> 0.1)
|
||||
coercible (~> 1.0)
|
||||
|
@ -904,6 +907,7 @@ DEPENDENCIES
|
|||
turbo-rails
|
||||
typhoeus
|
||||
vcr
|
||||
view_component
|
||||
warden
|
||||
web-console
|
||||
webdrivers (~> 4.0)
|
||||
|
|
3
app/components/application_component.rb
Normal file
3
app/components/application_component.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
class ApplicationComponent < ViewComponent::Base
|
||||
include ViewComponent::Translatable
|
||||
end
|
58
app/components/dossiers/message_component.rb
Normal file
58
app/components/dossiers/message_component.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
class Dossiers::MessageComponent < ApplicationComponent
|
||||
def initialize(commentaire:, connected_user:, messagerie_seen_at: nil, show_reply_button: false)
|
||||
@commentaire = commentaire
|
||||
@connected_user = connected_user
|
||||
@messagerie_seen_at = messagerie_seen_at
|
||||
@show_reply_button = show_reply_button
|
||||
end
|
||||
|
||||
attr_reader :commentaire, :connected_user, :messagerie_seen_at
|
||||
|
||||
private
|
||||
|
||||
def show_reply_button?
|
||||
@show_reply_button
|
||||
end
|
||||
|
||||
def highlight_if_unseen_class
|
||||
helpers.highlight_if_unseen_class(@messagerie_seen_at, commentaire.created_at)
|
||||
end
|
||||
|
||||
def icon_path
|
||||
if commentaire.sent_by_system?
|
||||
'icons/mail.svg'
|
||||
elsif commentaire.sent_by?(connected_user)
|
||||
'icons/account-circle.svg'
|
||||
else
|
||||
'icons/blue-person.svg'
|
||||
end
|
||||
end
|
||||
|
||||
def commentaire_issuer
|
||||
if commentaire.sent_by_system?
|
||||
t('.automatic_email')
|
||||
elsif commentaire.sent_by?(connected_user)
|
||||
t('.you')
|
||||
else
|
||||
commentaire.redacted_email
|
||||
end
|
||||
end
|
||||
|
||||
def commentaire_from_guest?
|
||||
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
|
||||
end
|
||||
|
||||
def commentaire_date
|
||||
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
|
||||
l(commentaire.created_at, format: is_current_year ? :message_date : :message_date_with_year)
|
||||
end
|
||||
|
||||
def commentaire_body
|
||||
if commentaire.discarded?
|
||||
t('.deleted_body')
|
||||
else
|
||||
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
|
||||
sanitize(body_formatted)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
en:
|
||||
reply: Reply
|
||||
guest: Guest
|
||||
delete_button: Delete this message
|
||||
confirm: Are you sure you want to delete this message ?
|
||||
automatic_email: Automatic email
|
||||
you: You
|
||||
deleted_body: Message deleted
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
fr:
|
||||
reply: Répondre
|
||||
guest: Invité
|
||||
delete_button: Supprimer le message
|
||||
confirm: Êtes-vous sûr de vouloir supprimer ce message ?
|
||||
automatic_email: Email automatique
|
||||
you: Vous
|
||||
deleted_body: Message supprimé
|
|
@ -0,0 +1,26 @@
|
|||
= image_tag(icon_path, class: 'person-icon', alt: '')
|
||||
|
||||
.width-100
|
||||
%h2
|
||||
%span.mail
|
||||
= commentaire_issuer
|
||||
- if commentaire_from_guest?
|
||||
%span.guest= t('.guest')
|
||||
%span.date{ class: highlight_if_unseen_class }
|
||||
= commentaire_date
|
||||
.rich-text= commentaire_body
|
||||
|
||||
.message-extras.flex.justify-start
|
||||
- if commentaire.soft_deletable?(connected_user)
|
||||
= button_to instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire), method: :delete, class: 'button danger', form: { data: { turbo: true, turbo_confirm: t('.confirm') } } do
|
||||
%span.icon.delete
|
||||
= t('.delete_button')
|
||||
|
||||
- if commentaire.piece_jointe.attached?
|
||||
.attachment-link
|
||||
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
|
||||
|
||||
- if show_reply_button?
|
||||
= button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do
|
||||
%span.icon.reply
|
||||
= t('.reply')
|
|
@ -113,22 +113,6 @@ module Experts
|
|||
end
|
||||
end
|
||||
|
||||
def delete_commentaire
|
||||
commentaire = avis.dossier.commentaires.find(params[:commentaire])
|
||||
if commentaire.sent_by?(current_expert)
|
||||
commentaire.piece_jointe.purge_later if commentaire.piece_jointe.attached?
|
||||
commentaire.discard!
|
||||
commentaire.update!(body: '')
|
||||
flash[:notice] = t('views.shared.commentaires.destroy.notice')
|
||||
else
|
||||
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.acl')
|
||||
end
|
||||
redirect_to(messagerie_expert_avis_path(avis.procedure, avis))
|
||||
rescue Discard::RecordNotDiscarded
|
||||
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded')
|
||||
redirect_to(messagerie_expert_avis_path(avis.procedure, avis))
|
||||
end
|
||||
|
||||
def bilans_bdf
|
||||
if avis.dossier.etablissement&.entreprise_bilans_bdf.present?
|
||||
extension = params[:format]
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Instructeurs
|
||||
class CommentairesController < ProceduresController
|
||||
after_action :mark_messagerie_as_read
|
||||
|
||||
def destroy
|
||||
commentaire = Dossier.find(params[:dossier_id]).commentaires.find(params[:id])
|
||||
if commentaire.sent_by?(current_instructeur)
|
||||
commentaire.piece_jointe.purge_later if commentaire.piece_jointe.attached?
|
||||
commentaire.discard!
|
||||
commentaire.update!(body: '')
|
||||
flash[:notice] = t('views.shared.commentaires.destroy.notice')
|
||||
if commentaire.sent_by?(current_instructeur) || commentaire.sent_by?(current_expert)
|
||||
commentaire.soft_delete!
|
||||
|
||||
flash.notice = t('.notice')
|
||||
else
|
||||
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.acl')
|
||||
flash.alert = t('.alert_acl')
|
||||
end
|
||||
redirect_to(messagerie_instructeur_dossier_path(params[:procedure_id], params[:dossier_id]))
|
||||
rescue Discard::RecordNotDiscarded
|
||||
flash[:alert] = I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded')
|
||||
redirect_to(messagerie_instructeur_dossier_path(params[:procedure_id], params[:dossier_id]))
|
||||
# i18n-tasks-use t('instructeurs.commentaires.destroy.alert_already_discarded')
|
||||
flash.alert = t('.alert_already_discarded')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mark_messagerie_as_read
|
||||
if commentaire.sent_by?(current_instructeur)
|
||||
current_instructeur.mark_tab_as_seen(commentaire.dossier, :messagerie)
|
||||
end
|
||||
end
|
||||
|
||||
def commentaire
|
||||
@commentaire ||= Dossier
|
||||
.find(params[:dossier_id])
|
||||
.commentaires
|
||||
.find(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,20 +12,4 @@ module CommentaireHelper
|
|||
I18n.t('helpers.commentaire.reply_in_mailbox')
|
||||
end
|
||||
end
|
||||
|
||||
def commentaire_is_from_guest(commentaire)
|
||||
commentaire.dossier.invites.map(&:email).include?(commentaire.email)
|
||||
end
|
||||
|
||||
def commentaire_date(commentaire)
|
||||
is_current_year = (commentaire.created_at.year == Time.zone.today.year)
|
||||
template = is_current_year ? :message_date : :message_date_with_year
|
||||
I18n.l(commentaire.created_at, format: template)
|
||||
end
|
||||
|
||||
def pretty_commentaire(commentaire)
|
||||
return t('views.shared.commentaires.destroy.deleted_body') if commentaire.discarded?
|
||||
body_formatted = commentaire.sent_by_system? ? commentaire.body : simple_format(commentaire.body)
|
||||
sanitize(body_formatted)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,8 +10,9 @@ import {
|
|||
removeClass
|
||||
} from '@utils';
|
||||
|
||||
const AUTOSAVE_DEBOUNCE_DELAY = gon.autosave.debounce_delay;
|
||||
const AUTOSAVE_STATUS_VISIBLE_DURATION = gon.autosave.status_visible_duration;
|
||||
const AUTOSAVE_DEBOUNCE_DELAY = window?.gon?.autosave?.debounce_delay;
|
||||
const AUTOSAVE_STATUS_VISIBLE_DURATION =
|
||||
window?.gon?.autosave?.status_visible_duration;
|
||||
|
||||
// Create a controller responsible for queuing autosave operations.
|
||||
const autoSaveController = new AutoSaveController();
|
||||
|
|
|
@ -89,6 +89,12 @@ class Commentaire < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def soft_delete!
|
||||
piece_jointe.purge_later if piece_jointe.attached?
|
||||
discard!
|
||||
update! body: ''
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notify
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
- if @commentaire.discarded?
|
||||
= turbo_stream.update @commentaire do
|
||||
= render Dossiers::MessageComponent.new(commentaire: @commentaire, connected_user: @commentaire.instructeur || @commentaire.expert)
|
|
@ -49,7 +49,7 @@
|
|||
%ul.messages-list
|
||||
- @dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
|
||||
%li
|
||||
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: current_instructeur, messagerie_seen_at: nil, show_reply_button: false }
|
||||
= render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: current_instructeur)
|
||||
|
||||
%script{ type: "text/javascript" }
|
||||
window.print();
|
||||
|
|
27
app/views/layouts/component_preview.html.haml
Normal file
27
app/views/layouts/component_preview.html.haml
Normal file
|
@ -0,0 +1,27 @@
|
|||
!!! 5
|
||||
%html{ lang: html_lang, class: yield(:root_class) }
|
||||
%head
|
||||
%meta{ "http-equiv": "Content-Type", content: "text/html; charset=UTF-8" }
|
||||
%meta{ "http-equiv": "X-UA-Compatible", content: "IE=edge" }
|
||||
%meta{ name: "viewport", content: "width=device-width, initial-scale=1" }
|
||||
= csrf_meta_tags
|
||||
|
||||
%title
|
||||
= content_for?(:title) ? "#{yield(:title)} · #{APPLICATION_NAME}" : APPLICATION_NAME
|
||||
|
||||
= favicon_link_tag(image_url("#{FAVICON_16PX_SRC}"), type: "image/png", sizes: "16x16")
|
||||
= favicon_link_tag(image_url("#{FAVICON_32PX_SRC}"), type: "image/png", sizes: "32x32")
|
||||
= favicon_link_tag(image_url("#{FAVICON_96PX_SRC}"), type: "image/png", sizes: "96x96")
|
||||
|
||||
= javascript_packs_with_chunks_tag 'application', defer: true
|
||||
|
||||
= preload_link_tag(asset_url("Muli-Regular.woff2"))
|
||||
= preload_link_tag(asset_url("Muli-Bold.woff2"))
|
||||
|
||||
= stylesheet_link_tag 'application', media: 'all'
|
||||
|
||||
%body{ class: browser.platform.ios? ? 'ios' : nil }
|
||||
.page-wrapper
|
||||
%main.m-6
|
||||
= content_for?(:content) ? yield(:content) : yield
|
||||
%turbo-events
|
|
@ -1,8 +1,8 @@
|
|||
.messagerie.container
|
||||
%ul.messages-list
|
||||
- dossier.commentaires.with_attached_piece_jointe.each do |commentaire|
|
||||
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user) }
|
||||
= render partial: "shared/dossiers/messages/message", locals: { commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user) }
|
||||
%li.message{ class: commentaire_is_from_me_class(commentaire, connected_user), id: dom_id(commentaire) }
|
||||
= render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user))
|
||||
|
||||
- if dossier.messagerie_available?
|
||||
= render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url, dossier: dossier }
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
= render partial: 'shared/dossiers/messages/message_icon', locals: { commentaire: commentaire, connected_user: connected_user }
|
||||
|
||||
.width-100
|
||||
%h2
|
||||
%span.mail
|
||||
= render partial: 'shared/dossiers/messages/message_issuer', locals: { commentaire: commentaire, connected_user: connected_user }
|
||||
- if commentaire_is_from_guest(commentaire)
|
||||
%span.guest= t('views.shared.dossiers.messages.message.guest')
|
||||
%span.date{ class: highlight_if_unseen_class(messagerie_seen_at, commentaire.created_at) }
|
||||
= commentaire_date(commentaire)
|
||||
.rich-text= pretty_commentaire(commentaire)
|
||||
|
||||
.message-extras.flex.justify-start
|
||||
- if commentaire.soft_deletable?(connected_user)
|
||||
- path = connected_user.is_a?(Instructeur) ? instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) : delete_commentaire_expert_avis_path(@avis.procedure, @avis, commentaire: commentaire)
|
||||
= button_to path, method: :delete, class: 'button danger', data: { confirm: t('views.shared.commentaires.destroy.confirm') } do
|
||||
%span.icon.delete
|
||||
= t('views.shared.commentaires.destroy.button')
|
||||
|
||||
- if commentaire.piece_jointe.attached?
|
||||
.attachment-link
|
||||
= render partial: "shared/attachment/show", locals: { attachment: commentaire.piece_jointe.attachment }
|
||||
|
||||
- if show_reply_button
|
||||
= button_tag type: 'button', class: 'button small message-answer-button', onclick: 'document.querySelector("#commentaire_body").focus()' do
|
||||
%span.icon.reply
|
||||
= t('views.shared.dossiers.messages.message.reply')
|
|
@ -1,7 +0,0 @@
|
|||
- if commentaire.sent_by_system?
|
||||
= image_tag('icons/mail.svg', class: 'person-icon', alt: '')
|
||||
- elsif commentaire.sent_by?(connected_user)
|
||||
= image_tag('icons/account-circle.svg', class: 'person-icon', alt: '')
|
||||
- else
|
||||
= image_tag('icons/blue-person.svg', class: 'person-icon', alt: '')
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
- if commentaire.sent_by_system?
|
||||
= t('views.shared.dossiers.messages.message_issuer.automatic_email')
|
||||
- elsif commentaire.sent_by?(connected_user)
|
||||
= t('views.shared.dossiers.messages.message_issuer.you')
|
||||
- else
|
||||
= commentaire.redacted_email
|
|
@ -4,7 +4,7 @@
|
|||
%h3.tab-title= t('views.users.dossiers.show.latest_message.latest_message')
|
||||
|
||||
.message.inverted-background
|
||||
= render partial: "shared/dossiers/messages/message", locals: { commentaire: latest_message, connected_user: current_user, messagerie_seen_at: nil, show_reply_button: false }
|
||||
= render Dossiers::MessageComponent.new(commentaire: latest_message, connected_user: current_user)
|
||||
|
||||
= link_to messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button send' do
|
||||
%span.icon.reply
|
||||
|
|
|
@ -81,5 +81,13 @@ module TPS
|
|||
# Custom Configuration
|
||||
# @see https://guides.rubyonrails.org/configuring.html#custom-configuration
|
||||
config.x.clamav.enabled = ENV.fetch("CLAMAV_ENABLED", "enabled") == "enabled"
|
||||
|
||||
config.view_component.generate_sidecar = true
|
||||
config.view_component.generate_locale = true
|
||||
config.view_component.generate_distinct_locale_files = true
|
||||
config.view_component.generate_preview = true
|
||||
config.view_component.show_previews_source = true
|
||||
config.view_component.default_preview_layout = 'component_preview'
|
||||
config.view_component.preview_paths << "#{Rails.root}/spec/components/previews"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,6 +68,7 @@ search:
|
|||
- app/assets/images
|
||||
- app/assets/fonts
|
||||
- app/assets/videos
|
||||
- app/components
|
||||
|
||||
## Alternatively, the only files or `File.fnmatch patterns` to search in `paths`:
|
||||
## If specified, this settings takes priority over `exclude`, but `exclude` still applies.
|
||||
|
|
|
@ -127,12 +127,6 @@ en:
|
|||
submit_dossier: Submit the file
|
||||
save_changes: Save the changes of the file
|
||||
messages:
|
||||
message_issuer:
|
||||
automatic_email: "Automatic email"
|
||||
you: "You"
|
||||
message:
|
||||
reply: "Reply"
|
||||
guest: "Guest"
|
||||
form:
|
||||
send_message: "Send message"
|
||||
attachment_size: "(attachment size max : 20 Mo)"
|
||||
|
|
|
@ -122,12 +122,6 @@ fr:
|
|||
submit_dossier: Déposer le dossier
|
||||
save_changes: Enregistrer les modifications du dossier
|
||||
messages:
|
||||
message_issuer:
|
||||
automatic_email: "Email automatique"
|
||||
you: "Vous"
|
||||
message:
|
||||
reply: "Répondre"
|
||||
guest: "Invité"
|
||||
form:
|
||||
send_message: "Envoyer le message"
|
||||
attachment_size: "(taille max : 20 Mo)"
|
||||
|
|
7
config/locales/views/instructeurs/commentaires/en.yml
Normal file
7
config/locales/views/instructeurs/commentaires/en.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
en:
|
||||
instructeurs:
|
||||
commentaires:
|
||||
destroy:
|
||||
notice: Your message had been deleted
|
||||
alert_acl: "Can not destroy message: it does not belong to you"
|
||||
alert_already_discarded: "Can not destroy message: it was already destroyed"
|
7
config/locales/views/instructeurs/commentaires/fr.yml
Normal file
7
config/locales/views/instructeurs/commentaires/fr.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
fr:
|
||||
instructeurs:
|
||||
commentaires:
|
||||
destroy:
|
||||
notice: Votre message a été supprimé
|
||||
alert_acl: Impossible de supprimer le message, celui ci ne vous appartient pas
|
||||
alert_already_discarded: Ce message a déjà été supprimé
|
|
@ -2,4 +2,4 @@ en:
|
|||
instructeurs:
|
||||
procedure:
|
||||
archive_pending_html: Archive creation pending<br>(requested %{created_period} ago)
|
||||
archive_ready_html: Download archive<br>(requested %{generated_period} ago)
|
||||
archive_ready_html: Download archive<br>(requested %{generated_period} ago)
|
||||
|
|
|
@ -20,12 +20,3 @@ en:
|
|||
already_user: "I already have an account"
|
||||
create: 'Create an account'
|
||||
signin: 'Sign in'
|
||||
commentaires:
|
||||
destroy:
|
||||
button: 'Destroy this message'
|
||||
confirm: "Are you sure you want to destroy this message ?"
|
||||
deleted_body: Message deleted
|
||||
notice: 'Your message had been deleted'
|
||||
alert_reasons:
|
||||
acl: "Can not destroy message: it does not belong to you"
|
||||
already_discarded: "Can not destroy message: it was already destroyed"
|
||||
|
|
|
@ -20,12 +20,3 @@ fr:
|
|||
already_user: 'J’ai déjà un compte'
|
||||
create: 'Créer un compte'
|
||||
signin: 'Connexion'
|
||||
commentaires:
|
||||
destroy:
|
||||
button: 'Supprimer le message'
|
||||
confirm: "Êtes-vous sûr de vouloir supprimer ce message ?"
|
||||
deleted_body: Message supprimé
|
||||
notice: 'Votre message a été supprimé'
|
||||
alert_reasons:
|
||||
acl: "Impossible de supprimer le message, celui ci ne vous appartient pas"
|
||||
already_discarded: "Ce message a déjà été supprimé"
|
||||
|
|
|
@ -310,7 +310,6 @@ Rails.application.routes.draw do
|
|||
get 'instruction'
|
||||
get 'messagerie'
|
||||
post 'commentaire' => 'avis#create_commentaire'
|
||||
delete 'delete_commentaire' => 'avis#delete_commentaire'
|
||||
post 'avis' => 'avis#create_avis'
|
||||
get 'bilans_bdf'
|
||||
get 'telecharger_pjs' => 'avis#telecharger_pjs'
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
describe 'shared/dossiers/messages/message.html.haml', type: :view do
|
||||
before { view.extend DossierHelper }
|
||||
|
||||
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: dossier.user, show_reply_button: true }
|
||||
|
||||
RSpec.describe Dossiers::MessageComponent, type: :component do
|
||||
let(:component) do
|
||||
described_class.new(
|
||||
commentaire: commentaire,
|
||||
connected_user: connected_user,
|
||||
messagerie_seen_at: seen_at,
|
||||
show_reply_button: true
|
||||
)
|
||||
end
|
||||
let(:dossier) { create(:dossier, :en_construction) }
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier) }
|
||||
let(:connected_user) { dossier.user }
|
||||
let(:seen_at) { commentaire.created_at + 1.hour }
|
||||
|
||||
subject { render_inline(component).to_html }
|
||||
|
||||
it { is_expected.to have_button("Répondre") }
|
||||
|
||||
context "with a seen_at after commentaire created_at" do
|
||||
context 'with a seen_at after commentaire created_at' do
|
||||
let(:seen_at) { commentaire.created_at + 1.hour }
|
||||
|
||||
it { is_expected.not_to have_css(".highlighted") }
|
||||
end
|
||||
|
||||
context "with a seen_at after commentaire created_at" do
|
||||
context 'with a seen_at after commentaire created_at' do
|
||||
let(:seen_at) { commentaire.created_at - 1.hour }
|
||||
|
||||
it { is_expected.to have_css(".highlighted") }
|
||||
|
@ -51,8 +58,8 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
|
|||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) }
|
||||
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: instructeur, show_reply_button: true }
|
||||
let(:form_url) { instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) }
|
||||
let(:connected_user) { instructeur }
|
||||
let(:form_url) { component.helpers.instructeur_commentaire_path(commentaire.dossier.procedure, commentaire.dossier, commentaire) }
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected instructeur' do
|
||||
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message') }
|
||||
|
@ -64,7 +71,7 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
|
|||
let(:commentaire) { create(:commentaire, instructeur: instructeur, body: 'Second message', discarded_at: 2.days.ago) }
|
||||
|
||||
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
|
||||
it { is_expected.not_to have_selector(".rich-text", text: I18n.t(t('views.shared.commentaires.destroy.deleted_body'))) }
|
||||
it { is_expected.to have_selector(".rich-text", text: component.t('.deleted_body')) }
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected an user' do
|
||||
|
@ -87,49 +94,48 @@ describe 'shared/dossiers/messages/message.html.haml', type: :view do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with an expert message' do
|
||||
describe 'delete message button for expert' do
|
||||
let(:expert) { create(:expert) }
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, commentaires: [commentaire], procedure: procedure) }
|
||||
let(:experts_procedure) { create(:experts_procedure, procedure: procedure, expert: expert) }
|
||||
let!(:avis) { create(:avis, email: nil, experts_procedure: experts_procedure) }
|
||||
subject { render 'shared/dossiers/messages/message.html.haml', commentaire: commentaire, messagerie_seen_at: seen_at, connected_user: expert, show_reply_button: true }
|
||||
let(:form_url) { delete_commentaire_expert_avis_path(avis.procedure, avis, commentaire: commentaire) }
|
||||
describe '#commentaire_from_guest?' do
|
||||
let!(:guest) { create(:invite, dossier: dossier) }
|
||||
|
||||
before do
|
||||
assign(:avis, avis)
|
||||
subject { component.send(:commentaire_from_guest?) }
|
||||
|
||||
context 'when the commentaire sender is not a guest' do
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when the commentaire sender is a guest on this dossier' do
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#commentaire_date' do
|
||||
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
|
||||
let(:creation_date) { present_date }
|
||||
let(:commentaire) do
|
||||
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
|
||||
end
|
||||
|
||||
subject do
|
||||
Timecop.freeze(present_date) { component.send(:commentaire_date) }
|
||||
end
|
||||
|
||||
it 'doesn’t include the creation year' do
|
||||
expect(subject).to eq 'le 2 septembre à 10 h 05'
|
||||
end
|
||||
|
||||
context 'when displaying a commentaire created on a previous year' do
|
||||
let(:creation_date) { present_date.prev_year }
|
||||
it 'includes the creation year' do
|
||||
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected expert' do
|
||||
let(:commentaire) { create(:commentaire, expert: expert, body: 'Second message') }
|
||||
|
||||
it { is_expected.to have_selector("form[action=\"#{form_url}\"]") }
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected expert and discarded' do
|
||||
let(:commentaire) { create(:commentaire, expert: expert, body: 'Second message', discarded_at: 2.days.ago) }
|
||||
|
||||
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
|
||||
it { is_expected.not_to have_selector(".rich-text", text: I18n.t(t('views.shared.commentaires.destroy.deleted_body'))) }
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected an user' do
|
||||
let(:commentaire) { create(:commentaire, email: create(:user).email, body: 'Second message') }
|
||||
|
||||
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written by connected an instructeur' do
|
||||
let(:commentaire) { create(:commentaire, instructeur: create(:instructeur), body: 'Second message') }
|
||||
|
||||
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
|
||||
end
|
||||
|
||||
context 'on a procedure where commentaire had been written another expert' do
|
||||
let(:commentaire) { create(:commentaire, expert: create(:expert), body: 'Second message') }
|
||||
|
||||
it { is_expected.not_to have_selector("form[action=\"#{form_url}\"]") }
|
||||
context 'when formatting the first day of the month' do
|
||||
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
|
||||
it 'includes the ordinal' do
|
||||
expect(subject).to eq 'le 1er septembre à 10 h 05'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
class Dossiers::MessageComponentPreview < ViewComponent::Preview
|
||||
def with_default_commentaire
|
||||
render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: user)
|
||||
end
|
||||
|
||||
def with_discarded_commentaire
|
||||
render Dossiers::MessageComponent.new(commentaire: discarded_commentaire, connected_user: user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user
|
||||
User.new email: "usager@example.com", locale: I18n.locale
|
||||
end
|
||||
|
||||
def commentaire
|
||||
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago
|
||||
end
|
||||
|
||||
def discarded_commentaire
|
||||
Commentaire.new body: 'Hello world!', email: user.email, dossier: dossier, created_at: 2.days.ago, discarded_at: 1.day.ago
|
||||
end
|
||||
|
||||
def dossier
|
||||
Dossier.new(id: 47882, state: :en_instruction, procedure: procedure, user: user)
|
||||
end
|
||||
|
||||
def procedure
|
||||
Procedure.new id: 1234, libelle: 'Dotation d’Équipement des Territoires Ruraux - Exercice 2019'
|
||||
end
|
||||
end
|
|
@ -4,33 +4,33 @@ describe Instructeurs::CommentairesController, type: :controller do
|
|||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, instructeurs: [instructeur]) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
|
||||
render_views
|
||||
|
||||
before { sign_in(instructeur.user) }
|
||||
|
||||
describe 'destroy' do
|
||||
render_views
|
||||
|
||||
context 'when it works' do
|
||||
let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier) }
|
||||
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id } }
|
||||
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id }, format: :turbo_stream }
|
||||
|
||||
it 'redirect to dossier' do
|
||||
expect(subject).to redirect_to(messagerie_instructeur_dossier_path(dossier.procedure, dossier))
|
||||
end
|
||||
it 'flash success' do
|
||||
subject
|
||||
expect(flash[:notice]).to eq(I18n.t('views.shared.commentaires.destroy.notice'))
|
||||
it 'respond with OK and flash' do
|
||||
expect(subject).to have_http_status(:ok)
|
||||
expect(subject.body).to include('Message supprimé')
|
||||
expect(subject.body).to include('alert-success')
|
||||
expect(subject.body).to include('Votre message a été supprimé')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dossier had been discarded' do
|
||||
let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier, discarded_at: 2.hours.ago) }
|
||||
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id } }
|
||||
subject { delete :destroy, params: { dossier_id: dossier.id, procedure_id: procedure.id, id: commentaire.id }, format: :turbo_stream }
|
||||
|
||||
it 'redirect to dossier' do
|
||||
expect(subject).to redirect_to(messagerie_instructeur_dossier_path(dossier.procedure, dossier))
|
||||
end
|
||||
it 'flash success' do
|
||||
subject
|
||||
expect(flash[:alert]).to eq(I18n.t('views.shared.commentaires.destroy.alert_reasons.already_discarded'))
|
||||
it 'respond with OK and flash' do
|
||||
expect(subject).to have_http_status(:ok)
|
||||
expect(subject.body).to include('alert-danger')
|
||||
expect(subject.body).to include('Ce message a déjà été supprimé')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,51 +28,4 @@ RSpec.describe CommentaireHelper, type: :helper do
|
|||
it { is_expected.to include('Répondre') }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.commentaire_is_from_guest' do
|
||||
let(:dossier) { create(:dossier, :en_instruction) }
|
||||
let!(:guest) { create(:invite, dossier: dossier) }
|
||||
|
||||
subject { commentaire_is_from_guest(commentaire) }
|
||||
|
||||
context 'when the commentaire sender is not a guest' do
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier, email: "michel@pref.fr") }
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when the commentaire sender is a guest on this dossier' do
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier, email: guest.email) }
|
||||
it { is_expected.to be true }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.commentaire_date' do
|
||||
let(:present_date) { Time.zone.local(2018, 9, 2, 10, 5, 0) }
|
||||
let(:creation_date) { present_date }
|
||||
let(:commentaire) do
|
||||
Timecop.freeze(creation_date) { create(:commentaire, email: "michel@pref.fr") }
|
||||
end
|
||||
|
||||
subject do
|
||||
Timecop.freeze(present_date) { commentaire_date(commentaire) }
|
||||
end
|
||||
|
||||
it 'doesn’t include the creation year' do
|
||||
expect(subject).to eq 'le 2 septembre à 10 h 05'
|
||||
end
|
||||
|
||||
context 'when displaying a commentaire created on a previous year' do
|
||||
let(:creation_date) { present_date.prev_year }
|
||||
it 'includes the creation year' do
|
||||
expect(subject).to eq 'le 2 septembre 2017 à 10 h 05'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when formatting the first day of the month' do
|
||||
let(:present_date) { Time.zone.local(2018, 9, 1, 10, 5, 0) }
|
||||
it 'includes the ordinal' do
|
||||
expect(subject).to eq 'le 1er septembre à 10 h 05'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,7 @@ require 'rspec/rails'
|
|||
require 'axe-rspec'
|
||||
require 'devise'
|
||||
require 'shoulda-matchers'
|
||||
require 'view_component/test_helpers'
|
||||
|
||||
# Requires supporting ruby files with custom matchers and macros, etc, in
|
||||
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
|
||||
|
@ -124,4 +125,6 @@ RSpec.configure do |config|
|
|||
config.include Devise::Test::ControllerHelpers, type: :controller
|
||||
config.include Devise::Test::ControllerHelpers, type: :view
|
||||
config.include Devise::Test::IntegrationHelpers, type: :system
|
||||
config.include ViewComponent::TestHelpers, type: :component
|
||||
config.include Capybara::RSpecMatchers, type: :component
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue