feat(announces): show Nouveautés link in main navigation

This commit is contained in:
Colin Darie 2023-10-31 10:50:28 +01:00
parent 352ed3969c
commit 49cd10458c
14 changed files with 224 additions and 11 deletions

View file

@ -13,3 +13,14 @@ span.notifications {
top: 5px;
right: 8px;
}
.fr-nav {
&__notifiable {
position: relative;
}
.notifications {
top: 1rem;
right: 0.25rem;
}
}

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
class MainNavigation::AnnouncesLinkComponent < ApplicationComponent
def render?
# also see app/controllers/release_notes_controller.rb#ensure_access_allowed!
return false if !helpers.instructeur_signed_in? && !helpers.administrateur_signed_in? && !helpers.expert_signed_in?
@most_recent_released_on = load_most_recent_released_on
@most_recent_released_on.present?
end
def something_new?
return true if current_user.announces_seen_at.nil?
@most_recent_released_on.after? current_user.announces_seen_at
end
def load_most_recent_released_on
categories = helpers.infer_default_announce_categories
ReleaseNote.most_recent_announce_date_for_categories(categories)
end
end

View file

@ -0,0 +1,4 @@
---
en:
news: News
something_new: New informations about the website may be of interest to you.

View file

@ -0,0 +1,4 @@
---
fr:
news: Nouveautés
something_new: De nouvelles informations à propos du site pourraient vous intéresser.

View file

@ -0,0 +1,4 @@
%li.fr-nav__item.fr-nav__notifiable
= link_to t('.news'), release_notes_path, class: "fr-nav__link",'aria-current': current_page?(release_notes_path) ? 'page' : nil
- if something_new?
%span.notifications{ 'aria-label': t('.something_new') }

View file

@ -10,3 +10,5 @@
= Avis.model_name.human(count: 10)
- if helpers.current_expert.avis_summary[:unanswered] > 0
%span.badge.warning= helpers.current_expert.avis_summary[:unanswered]
= render MainNavigation::AnnouncesLinkComponent.new

View file

@ -1,8 +1,9 @@
class ReleaseNotesController < ApplicationController
before_action :ensure_access_allowed!
after_action :touch_default_categories_seen_at
def index
@categories = params[:categories].presence || infer_default_categories
@categories = params[:categories].presence || helpers.infer_default_announce_categories
# Paginate per group of dates, then show all announces for theses dates
@paginated_groups = ReleaseNote.published
@ -20,18 +21,26 @@ class ReleaseNotesController < ApplicationController
render "scrollable_list" if params[:page].present?
end
def nav_bar_profile
# detect context from referer, simple (no detection when refreshing the page)
params = Rails.application.routes.recognize_path(request&.referer)
controller_class = "#{params[:controller].camelize}Controller".safe_constantize
return if controller_class.nil?
controller_instance = controller_class.new
controller_instance.try(:nav_bar_profile)
end
private
def infer_default_categories
if administrateur_signed_in?
['administrateur', 'usager', current_administrateur.api_tokens.exists? ? 'api' : nil]
elsif instructeur_signed_in?
['instructeur', 'expert']
elsif expert_signed_in?
['expert']
else
['usager']
end
def touch_default_categories_seen_at
return if params[:categories].present? || params[:page].present?
return if current_user.blank?
return if current_user.announces_seen_at&.after?(@announces.max_by(&:released_on).released_on)
current_user.touch(:announces_seen_at)
end
def ensure_access_allowed!

View file

@ -15,4 +15,16 @@ module ReleaseNotesHelper
content_tag(:span, ReleaseNote.human_attribute_name("categories.#{category}"), class: "fr-badge #{color_class}")
end
def infer_default_announce_categories
if administrateur_signed_in?
ReleaseNote.default_categories_for_role(:administrateur, current_administrateur)
elsif instructeur_signed_in?
ReleaseNote.default_categories_for_role(:instructeur, current_instructeur)
elsif expert_signed_in?
ReleaseNote.default_categories_for_role(:expert, current_expert)
else
ReleaseNote.default_categories_for_role(:usager)
end
end
end

View file

@ -14,4 +14,21 @@ class ReleaseNote < ApplicationRecord
scope :published, -> { where(published: true, released_on: ..Date.current) }
scope :for_categories, -> (categories) { where("categories && ARRAY[?]::varchar[]", categories) }
def self.default_categories_for_role(role, instance = nil)
case role
when :administrateur
['administrateur', 'usager', instance.api_tokens.exists? ? 'api' : nil]
when :instructeur
['instructeur', instance.user.expert? ? 'expert' : nil]
when :expert
['expert', instance.user.instructeur? ? 'instructeur' : nil]
else
['usager']
end
end
def self.most_recent_announce_date_for_categories(categories)
published.for_categories(categories).maximum(:released_on)
end
end

View file

@ -3,3 +3,5 @@
%li.fr-nav__item= link_to 'Mes démarches', admin_procedures_path, class:'fr-nav__link', 'aria-current': current_page?(controller: 'administrateurs/procedures', action: :index) ? 'true' : nil
- if Rails.application.config.ds_zonage_enabled
%li.fr-nav__item= link_to 'Toutes les démarches', all_admin_procedures_path(zone_ids: current_administrateur.zones), class:'fr-nav__link', 'aria-current': current_page?(all_admin_procedures_path) ? 'page' : nil
= render MainNavigation::AnnouncesLinkComponent.new

View file

@ -0,0 +1,87 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe MainNavigation::AnnouncesLinkComponent, type: :component do
let(:user) { build(:user) }
let!(:admin_release_note) { create(:release_note, released_on: Date.yesterday, categories: ["administrateur"]) }
let!(:instructeur_release_note) { create(:release_note, released_on: Date.yesterday, categories: ["instructeur"]) }
let(:not_published_release_note) { create(:release_note, published: false, released_on: Date.tomorrow) }
let(:as_administrateur) { false }
let(:as_instructeur) { false }
before do
if as_administrateur
user.build_administrateur
end
if as_instructeur
user.build_instructeur
end
allow(controller).to receive(:current_user).and_return(user)
end
subject { render_inline(described_class.new) }
context 'when signed as simple user' do
it 'does not render the announcements link if not signed in' do
expect(subject.to_html).to be_empty
end
end
context 'when no signed in' do
let(:current_user) { nil }
it 'does not render the announcements link if not signed in' do
expect(subject.to_html).to be_empty
end
end
context 'when instructeur signed in' do
let(:as_instructeur) { true }
it 'renders the announcements link' do
expect(subject).to have_link("Nouveautés")
end
context 'when there are new announcements' do
before do
user.announces_seen_at = 5.days.ago
end
it 'does not render the notification badge' do
expect(subject).to have_link("Nouveautés")
expect(subject).to have_css(".notifications")
end
end
context 'when there are no new announcements' do
before do
user.announces_seen_at = 1.minute.ago
end
it 'does not render the notification badge' do
expect(subject).to have_link("Nouveautés")
expect(subject).not_to have_css(".notifications")
end
end
context 'when there are no announcement at all' do
let(:instructeur_release_note) { nil }
it 'does not render anything' do
expect(subject.to_html).to be_empty
end
end
end
context 'when administrateur signed in' do
let(:as_administrateur) { true }
it 'renders the announcements link' do
expect(subject).to have_link("Nouveautés")
end
end
end

View file

@ -42,6 +42,14 @@ describe MainNavigation::InstructeurExpertNavigationComponent, type: :component
expect(subject).not_to have_selector('a[aria-current="page"]', text: 'Avis')
end
end
context 'when there are release notes' do
let!(:release_note) { create(:release_note, categories: ['instructeur']) }
it 'renders a link to Announces page' do
expect(subject).to have_link('Nouveautés')
end
end
end
describe 'when expert is signed in' do

View file

@ -47,5 +47,31 @@ RSpec.describe ReleaseNotesController, type: :controller do
it { is_expected.to be_redirection }
end
end
describe 'touch user announces_seen_at' do
let(:user) { create(:user, administrateur: build(:administrateur)) }
context 'when default categories' do
it 'touch announces_seen_at' do
expect { subject }.to change { user.reload.announces_seen_at }
end
context 'when current announces_seen_at is more recent than last announce' do
before { user.update(announces_seen_at: 1.second.ago) }
it 'does not touch announces_seen_at' do
expect { subject }.not_to change { user.reload.announces_seen_at }
end
end
end
context 'when specific categories' do
subject { get :index, params: { categories: ['administrateur', 'instructeur'] } }
it 'does not touch announces_seen_at' do
expect { subject }.not_to change { user.reload.announces_seen_at }
end
end
end
end
end

View file

@ -5,6 +5,8 @@ describe 'layouts/_header', type: :view do
allow(view).to receive(:multiple_devise_profile_connect?).and_return(false)
allow(view).to receive(:instructeur_signed_in?).and_return((profile == :instructeur))
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
allow(view).to receive(:administrateur_signed_in?).and_return(false)
allow(view).to receive(:expert_signed_in?).and_return(false)
allow(view).to receive(:localization_enabled?).and_return(false)
if user
@ -57,6 +59,7 @@ describe 'layouts/_header', type: :view do
let(:user) { instructeur.user }
let(:profile) { :instructeur }
let(:current_instructeur) { instructeur }
let!(:release_note) { create(:release_note, categories: ['instructeur']) }
it { is_expected.to have_css(".fr-header__logo") }
it { is_expected.to have_selector(:button, user.email, class: "account-btn") }