Merge pull request #691 from sgmap/notifications

Notifications
This commit is contained in:
LeSim 2017-08-31 15:52:33 +02:00 committed by Mathieu Magnin
commit d7f4860256
14 changed files with 146 additions and 15 deletions

View file

@ -52,5 +52,10 @@
color: rgba(0, 0, 0, 0.6); color: rgba(0, 0, 0, 0.6);
font-size: 14px; font-size: 14px;
} }
.notifications {
top: 3px;
right: 3px;
}
} }
} }

View file

@ -14,6 +14,12 @@
i.folder { i.folder {
margin-right: $default-spacer; margin-right: $default-spacer;
position: relative;
.notifications {
top: 0px;
right: -10px;
}
} }
.number-col, .number-col,

View file

@ -0,0 +1,9 @@
@import "colors";
span.notifications {
position: absolute;
width: 8px;
height: 8px;
border-radius: 4px;
background-color: $orange;
}

View file

@ -40,6 +40,7 @@
min-height: 36px; min-height: 36px;
border-left: 1px solid $border-grey; border-left: 1px solid $border-grey;
width: 90px; width: 90px;
position: relative;
&:last-child { &:last-child {
border-right: 1px solid $border-grey; border-right: 1px solid $border-grey;
@ -60,5 +61,10 @@
color: $grey; color: $grey;
} }
} }
.notifications {
top: 3px;
right: 18px;
}
} }
} }

View file

@ -6,14 +6,17 @@ module NewGestionnaire
def show def show
@dossier = dossier @dossier = dossier
dossier.notifications.demande.mark_as_read
end end
def messagerie def messagerie
@dossier = dossier @dossier = dossier
dossier.notifications.messagerie.mark_as_read
end end
def instruction def instruction
@dossier = dossier @dossier = dossier
dossier.notifications.instruction.mark_as_read
end end
def follow def follow

View file

@ -18,6 +18,8 @@ module NewGestionnaire
.group(:procedure_id) .group(:procedure_id)
.reorder(nil) .reorder(nil)
.count .count
@notifications_count_per_procedure = current_gestionnaire.notifications_count_per_procedure
end end
def show def show
@ -31,7 +33,7 @@ module NewGestionnaire
@followed_dossiers = current_gestionnaire @followed_dossiers = current_gestionnaire
.followed_dossiers .followed_dossiers
.includes(:user) .includes(:user, :notifications)
.where(procedure: @procedure) .where(procedure: @procedure)
.en_cours .en_cours

View file

@ -64,16 +64,17 @@ class Dossier < ActiveRecord::Base
scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) }
scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) } scope :all_state, -> { not_archived.state_not_brouillon.order_by_updated_at(:asc) }
scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) } scope :nouveaux, -> { not_archived.state_nouveaux.order_by_updated_at(:asc) }
scope :ouvert, -> { not_archived.state_ouvert.order_by_updated_at(:asc) } scope :ouvert, -> { not_archived.state_ouvert.order_by_updated_at(:asc) }
scope :waiting_for_gestionnaire, -> { not_archived.state_waiting_for_gestionnaire.order_by_updated_at(:asc) } scope :waiting_for_gestionnaire, -> { not_archived.state_waiting_for_gestionnaire.order_by_updated_at(:asc) }
scope :waiting_for_user, -> { not_archived.state_waiting_for_user.order_by_updated_at(:asc) } scope :waiting_for_user, -> { not_archived.state_waiting_for_user.order_by_updated_at(:asc) }
scope :a_instruire, -> { not_archived.state_a_instruire.order_by_updated_at(:asc) } scope :a_instruire, -> { not_archived.state_a_instruire.order_by_updated_at(:asc) }
scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) } scope :termine, -> { not_archived.state_termine.order_by_updated_at(:asc) }
scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) } scope :downloadable, -> { state_not_brouillon.order_by_updated_at(:asc) }
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction.order_by_updated_at(:asc) } scope :en_cours, -> { not_archived.state_en_construction_ou_instruction.order_by_updated_at(:asc) }
scope :without_followers, -> { includes(:follows).where(follows: { id: nil }) } scope :without_followers, -> { includes(:follows).where(follows: { id: nil }) }
scope :with_unread_notifications, -> { where(notifications: { already_read: false }) }
accepts_nested_attributes_for :individual accepts_nested_attributes_for :individual
@ -103,6 +104,16 @@ class Dossier < ActiveRecord::Base
pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0 pieces_justificatives.where(type_de_piece_justificative_id: type_id).count > 0
end end
def notifications_summary
unread_notifications = notifications.unread
{
demande: unread_notifications.select(&:demande?).present?,
instruction: unread_notifications.select(&:instruction?).present?,
messagerie: unread_notifications.select(&:messagerie?).present?
}
end
def retrieve_last_piece_justificative_by_type(type) def retrieve_last_piece_justificative_by_type(type)
pieces_justificatives.where(type_de_piece_justificative_id: type).last pieces_justificatives.where(type_de_piece_justificative_id: type).last
end end

View file

@ -93,6 +93,14 @@ class Gestionnaire < ActiveRecord::Base
Notification.unread.where(dossier_id: followed_dossiers_id).select(:dossier_id).distinct(:dossier_id).count Notification.unread.where(dossier_id: followed_dossiers_id).select(:dossier_id).distinct(:dossier_id).count
end end
def notifications_count_per_procedure
followed_dossiers
.joins(:notifications)
.where(notifications: { already_read: false })
.group('procedure_id')
.count
end
def dossiers_with_notifications_count def dossiers_with_notifications_count
notifications.pluck(:dossier_id).uniq.count notifications.pluck(:dossier_id).uniq.count
end end

View file

@ -8,7 +8,27 @@ class Notification < ActiveRecord::Base
avis: 'avis' avis: 'avis'
} }
DEMANDE = %w(cerfa piece_justificative champs submitted)
INSTRUCTION = %w(avis)
MESSAGERIE = %w(commentaire)
belongs_to :dossier belongs_to :dossier
scope :unread, -> { where(already_read: false) } scope :unread, -> { where(already_read: false) }
scope :demande, -> { where(type_notif: DEMANDE) }
scope :instruction, -> { where(type_notif: INSTRUCTION) }
scope :messagerie, -> { where(type_notif: MESSAGERIE) }
scope :mark_as_read, -> { update_all(already_read: true) }
def demande?
Notification::DEMANDE.include?(type_notif)
end
def instruction?
Notification::INSTRUCTION.include?(type_notif)
end
def messagerie?
Notification::MESSAGERIE.include?(type_notif)
end
end end

View file

@ -6,11 +6,18 @@
%li %li
= "Dossier n° #{dossier.id}" = "Dossier n° #{dossier.id}"
%ul.tabs %ul.tabs
- notifications_summary = dossier.notifications_summary
%li{ class: current_page?(dossier_path(dossier.procedure, dossier)) ? 'active' : nil } %li{ class: current_page?(dossier_path(dossier.procedure, dossier)) ? 'active' : nil }
- if notifications_summary[:demande]
%span.notifications{ 'aria-label': 'notifications' }
= link_to "Demande", dossier_path(dossier.procedure, dossier) = link_to "Demande", dossier_path(dossier.procedure, dossier)
%li{ class: current_page?(instruction_dossier_path(dossier.procedure, dossier)) ? 'active' : nil } %li{ class: current_page?(instruction_dossier_path(dossier.procedure, dossier)) ? 'active' : nil }
- if notifications_summary[:instruction]
%span.notifications{ 'aria-label': 'notifications' }
= link_to "Instruction", instruction_dossier_path(dossier.procedure, dossier) = link_to "Instruction", instruction_dossier_path(dossier.procedure, dossier)
%li{ class: current_page?(messagerie_dossier_path(dossier.procedure, dossier)) ? 'active' : nil } %li{ class: current_page?(messagerie_dossier_path(dossier.procedure, dossier)) ? 'active' : nil }
- if notifications_summary[:messagerie]
%span.notifications{ 'aria-label': 'notifications' }
= link_to "Messagerie", messagerie_dossier_path(dossier.procedure, dossier) = link_to "Messagerie", messagerie_dossier_path(dossier.procedure, dossier)
%li %li
= link_to "Historique", "#" = link_to "Historique", "#"

View file

@ -19,6 +19,8 @@
.stats-legend .stats-legend
à suivre à suivre
%li %li
- if @notifications_count_per_procedure[p.id].present?
%span.notifications{ 'aria-label': "notifications" }
- followed_count = @followed_dossiers_count_per_procedure[p.id] || 0 - followed_count = @followed_dossiers_count_per_procedure[p.id] || 0
.stats-number .stats-number
= followed_count = followed_count

View file

@ -13,6 +13,8 @@
%span.badge= @a_suivre_dossiers.count %span.badge= @a_suivre_dossiers.count
%li{ class: (@statut == 'suivis') ? 'active' : nil }> %li{ class: (@statut == 'suivis') ? 'active' : nil }>
- if @followed_dossiers.with_unread_notifications.present?
%span.notifications{ 'aria-label': 'notifications' }
= link_to(procedure_path(@procedure, statut: 'suivis')) do = link_to(procedure_path(@procedure, statut: 'suivis')) do
= t('pluralize.followed', count: @followed_dossiers.count) = t('pluralize.followed', count: @followed_dossiers.count)
%span.badge= @followed_dossiers.count %span.badge= @followed_dossiers.count
@ -23,6 +25,8 @@
%span.badge= @termines_dossiers.count %span.badge= @termines_dossiers.count
%li{ class: (@statut == 'tous') ? 'active' : nil }> %li{ class: (@statut == 'tous') ? 'active' : nil }>
- if @followed_dossiers.with_unread_notifications.present?
%span.notifications{ 'aria-label': 'notifications' }
= link_to(procedure_path(@procedure, statut: 'tous')) do = link_to(procedure_path(@procedure, statut: 'tous')) do
tous les dossiers tous les dossiers
%span.badge= @all_state_dossiers.count %span.badge= @all_state_dossiers.count
@ -47,6 +51,8 @@
%td.number-col %td.number-col
= link_to(dossier_path(@procedure, dossier), class: 'cell-link') do = link_to(dossier_path(@procedure, dossier), class: 'cell-link') do
%i.folder %i.folder
- if @followed_dossiers.with_unread_notifications.include?(dossier)
%span.notifications{ 'aria-label': 'notifications' }
= dossier.id = dossier.id
%td= link_to(dossier.user.email, dossier_path(@procedure, dossier), class: 'cell-link') %td= link_to(dossier.user.email, dossier_path(@procedure, dossier), class: 'cell-link')
%td.status-col %td.status-col

View file

@ -72,10 +72,30 @@ describe NewGestionnaire::DossiersController, type: :controller do
it { expect(response).to redirect_to(procedures_url) } it { expect(response).to redirect_to(procedures_url) }
end end
describe "#show" do describe '#show #messagerie #instruction' do
before { get :show, params: { procedure_id: procedure.id, dossier_id: dossier.id } } before do
dossier.notifications = %w(champs avis commentaire).map{ |type| Notification.create!(type_notif: type) }
get method, params: { procedure_id: procedure.id, dossier_id: dossier.id }
dossier.notifications.each(&:reload)
end
it { expect(response).to have_http_status(:success) } context '#show' do
let(:method) { :show }
it { expect(dossier.notifications.map(&:already_read)).to match([true, false, false]) }
it { expect(response).to have_http_status(:success) }
end
context '#instruction' do
let(:method) { :instruction }
it { expect(dossier.notifications.map(&:already_read)).to match([false, true, false]) }
it { expect(response).to have_http_status(:success) }
end
context '#messagerie' do
let(:method) { :messagerie }
it { expect(dossier.notifications.map(&:already_read)).to match([false, false, true]) }
it { expect(response).to have_http_status(:success) }
end
end end
describe "#create_commentaire" do describe "#create_commentaire" do

View file

@ -356,4 +356,30 @@ describe Gestionnaire, type: :model do
it { expect(subject).to be false } it { expect(subject).to be false }
end end
end end
describe '#notifications_count_per_procedure' do
subject { gestionnaire.notifications_count_per_procedure }
let(:dossier_with_unread_notification) do
create(:dossier, notifications: [Notification.create(type_notif: 'champs', already_read: false)])
end
let(:dossier_with_no_unread_notification) do
create(:dossier, notifications: [Notification.create(type_notif: 'champs', already_read: true)])
end
before { gestionnaire.followed_dossiers << followed_dossier }
context 'when a followed dossier has unread notification' do
let(:followed_dossier) { dossier_with_unread_notification }
it { is_expected.to eq({ dossier_with_unread_notification.procedure.id => 1 }) }
end
context 'when a followed dossier has unread notification' do
let(:followed_dossier) { dossier_with_no_unread_notification }
it { is_expected.to eq({ }) }
end
end
end end