diff --git a/Gemfile b/Gemfile index 4a650a02d..aae99d51e 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,7 @@ gem "auto_strip_attributes" # Use sidekiq for background processing gem "sidekiq" gem "sidekiq-cron" +gem "unread" group :development, :test do # Check gems for known vulnerabilities diff --git a/Gemfile.lock b/Gemfile.lock index b7fdb4059..808f2c24a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -408,6 +408,8 @@ GEM concurrent-ruby (~> 1.0) uk_postcode (2.1.8) unicode-display_width (2.4.2) + unread (0.13.0) + activerecord (>= 6.1) view_component (3.9.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) @@ -493,6 +495,7 @@ DEPENDENCIES timecop (~> 0.9.4) tzinfo-data uk_postcode + unread view_component (~> 3.9) web-console (>= 4.1.0) webmock diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 307e79f15..d2d6a6dd1 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -24,7 +24,7 @@ private if resource.need_two_factor_authentication?(request) user_two_factor_authentication_path else - params.dig("user", "start").present? ? lettings_logs_path : super + params.dig("user", "start").present? ? root_path : super end end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb new file mode 100644 index 000000000..517199e93 --- /dev/null +++ b/app/controllers/notifications_controller.rb @@ -0,0 +1,19 @@ +class NotificationsController < ApplicationController + def dismiss + if current_user.blank? + redirect_to root_path + else + current_user.newest_active_unread_notification.mark_as_read! for: current_user + redirect_back(fallback_location: root_path) + end + end + + def show + @notification = current_user&.newest_active_unread_notification || Notification.newest_active_unauthenticated_notification + if @notification&.page_content + render "show" + else + redirect_back(fallback_location: root_path) + end + end +end diff --git a/app/controllers/start_controller.rb b/app/controllers/start_controller.rb index f2b96f557..318e7cace 100644 --- a/app/controllers/start_controller.rb +++ b/app/controllers/start_controller.rb @@ -1,7 +1,7 @@ class StartController < ApplicationController def index if current_user - redirect_to(lettings_logs_path) + render "home/index" end end diff --git a/app/frontend/styles/_data_box.scss b/app/frontend/styles/_data_box.scss new file mode 100644 index 000000000..0844ffa51 --- /dev/null +++ b/app/frontend/styles/_data_box.scss @@ -0,0 +1,66 @@ +.app-data-box-group { + @include govuk-font($size: 19); + font-weight: bold; + white-space: nowrap; +} + +@media (min-width: 54.0625em) { + .app-data-box-group-one-third { + display: flex; + justify-content: space-between; + column-gap: govuk-spacing(4); + width: 100%; + min-width: 733.33px; + } +} +@media (min-width: 54.0625em) { + .app-data-box-one-third { + width: 33.3333%; + float: left; + } +} +@media (min-width: 54.0625em) { + .app-data-box__underline { + min-width: 733.33px; + } +} +@media (min-width: 54.0625em) { + .app-data-box-group-one-half { + display: flex; + justify-content: space-between; + column-gap: govuk-spacing(4); + width: 100%; + min-width: 733.33px; + } +} +@media (min-width: 54.0625em) { + .app-data-box-one-half { + width: 50%; + float: left; + } +} +@media (min-width: 54.0625em) { + .app-data-box-one-half__underline { + min-width: 733.33px; + } +} + +.app-data-box__upper { + @include govuk-responsive-margin(2, "bottom"); + @include govuk-responsive-padding(4); + + background-color: govuk-colour("light-grey"); + color: govuk-colour("blue"); +} + +.app-data-box__lower { + @include govuk-responsive-margin(4, "bottom"); + @include govuk-responsive-padding(4); + + background-color: govuk-colour("blue"); +} + +.app-data-box__count { + font-size: 48px; + color: govuk-colour("blue"); +} diff --git a/app/frontend/styles/_document-list.scss b/app/frontend/styles/_document-list.scss index 47936a365..e8cef0625 100644 --- a/app/frontend/styles/_document-list.scss +++ b/app/frontend/styles/_document-list.scss @@ -12,7 +12,7 @@ } .app-document-list__item-title { - @include govuk-font($size: 16, $weight: "bold"); + @include govuk-font($size: 16); margin: 0 0 govuk-spacing(1); } diff --git a/app/frontend/styles/_header.scss b/app/frontend/styles/_header.scss index 12cfd4e54..924276d5f 100644 --- a/app/frontend/styles/_header.scss +++ b/app/frontend/styles/_header.scss @@ -26,3 +26,7 @@ .app-header--orange .govuk-header__container { border-bottom-color: govuk-colour("orange"); } + +.app-header__no-border-bottom { + border-bottom: 0; +} diff --git a/app/frontend/styles/_unread-notification.scss b/app/frontend/styles/_unread-notification.scss new file mode 100644 index 000000000..d76b36fa2 --- /dev/null +++ b/app/frontend/styles/_unread-notification.scss @@ -0,0 +1,7 @@ +.app-unread-notification { + background-color: govuk-colour("blue"); +} + +.app-unread-notification p { + color: govuk-colour("white"); +} diff --git a/app/frontend/styles/application.scss b/app/frontend/styles/application.scss index ddf368807..6309425de 100644 --- a/app/frontend/styles/application.scss +++ b/app/frontend/styles/application.scss @@ -25,7 +25,10 @@ $govuk-breakpoints: ( @import "accessible-autocomplete"; @import "button"; @import "card"; +@import "data_box"; +@import "delete-logs-table"; @import "document-list"; +@import "errors"; @import "feedback"; @import "filter"; @import "filter-layout"; @@ -43,8 +46,7 @@ $govuk-breakpoints: ( @import "primary-navigation"; @import "search"; @import "sub-navigation"; -@import "errors"; -@import "delete-logs-table"; +@import "unread-notification"; // App utilities .app-\!-colour-muted { diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 971dd68d9..01f7734c2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,21 +10,27 @@ module ApplicationHelper end def govuk_header_classes(current_user) - if current_user && current_user.support? + if current_user&.support? "app-header app-header--orange" + elsif ((current_user.blank? && Notification.active_unauthenticated_notifications.present?) || current_user&.active_unread_notifications.present?) && !current_page?(notifications_path) + "app-header app-header__no-border-bottom" else "app-header" end end def govuk_phase_banner_tag(current_user) - if current_user && current_user.support? + if current_user&.support? { colour: "orange", text: "Support beta" } else { text: "Beta" } end end + def notifications_to_display? + !current_page?(notifications_path) && (authenticated_user_has_notifications? || unauthenticated_user_has_notifications?) + end + private def paginated_title(title, pagy) @@ -33,4 +39,12 @@ private title + " (page #{pagy.page} of #{pagy.pages})" end + + def authenticated_user_has_notifications? + current_user&.active_unread_notifications.present? + end + + def unauthenticated_user_has_notifications? + current_user.blank? && Notification.active_unauthenticated_notifications.present? + end end diff --git a/app/helpers/collection_time_helper.rb b/app/helpers/collection_time_helper.rb index ea7601c70..6f8ef62fc 100644 --- a/app/helpers/collection_time_helper.rb +++ b/app/helpers/collection_time_helper.rb @@ -45,4 +45,24 @@ module CollectionTimeHelper def previous_collection_start_date current_collection_start_date - 1.year end + + def quarter_for_date(date: Time.zone.now) + quarters = [ + { quarter: "Q3", cutoff_date: Time.zone.local(2024, 1, 12), start_date: Time.zone.local(2023, 10, 1), end_date: Time.zone.local(2023, 12, 31) }, + { quarter: "Q1", cutoff_date: Time.zone.local(2024, 7, 12), start_date: Time.zone.local(2024, 4, 1), end_date: Time.zone.local(2024, 6, 30) }, + { quarter: "Q2", cutoff_date: Time.zone.local(2024, 10, 11), start_date: Time.zone.local(2024, 7, 1), end_date: Time.zone.local(2024, 9, 30) }, + { quarter: "Q3", cutoff_date: Time.zone.local(2025, 1, 10), start_date: Time.zone.local(2024, 10, 1), end_date: Time.zone.local(2024, 12, 31) }, + ] + + quarter = quarters.find { |q| date.between?(q[:start_date], q[:cutoff_date] + 1.day) } + + return unless quarter + + OpenStruct.new( + quarter: quarter[:quarter], + cutoff_date: quarter[:cutoff_date], + quarter_start_date: quarter[:start_date], + quarter_end_date: quarter[:end_date], + ) + end end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 000000000..5806b0b00 --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,71 @@ +module HomeHelper + def data_count(user, type) + if user.data_provider? + case type + when "lettings" then user.lettings_logs.in_progress.where(created_by: user).count + when "sales" then user.sales_logs.in_progress.where(created_by: user).count + when "misc" then user.lettings_logs.completed.where(created_by: user).count + end + else + case type + when "lettings" then user.lettings_logs.in_progress.count + when "sales" then user.sales_logs.in_progress.count + when "schemes" then user.schemes.incomplete.count + end + end + end + + def heading_for_user_role(user) + ROLE_HEADINGS[user.role] + end + + def data_subheading(user, type) + case type + when "schemes" + "Incomplete schemes" + when "misc" + "Your completed lettings" + else + "#{user.role == 'data_provider' ? :"Your " : nil}#{type} in progress".capitalize + end + end + + def data_path(user, type) + if user.data_provider? + case type + when "lettings" then lettings_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all") + when "sales" then sales_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all") + when "misc" then lettings_logs_path(status: [:completed], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all") + end + else + case type + when "lettings" then lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all") + when "sales" then sales_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all") + when "schemes" then schemes_path(status: [:incomplete], owning_organisation_select: "all") + end + end + end + + def view_all_path(type) + case type + when "lettings" then clear_filters_path(filter_type: "lettings_logs") + when "sales" then clear_filters_path(filter_type: "sales_logs") + when "schemes" then clear_filters_path(filter_type: "schemes") + when "misc" then clear_filters_path(filter_type: "schemes") + end + end + + def view_all_text(type) + if type == "misc" + "View all schemes" + else + "View all #{type}" + end + end + + ROLE_HEADINGS = { + "data_provider" => "Complete your logs", + "data_coordinator" => "Manage your data", + "support" => "Manage all data", + }.freeze +end diff --git a/app/helpers/navigation_items_helper.rb b/app/helpers/navigation_items_helper.rb index 10ffc273d..1cecb97ec 100644 --- a/app/helpers/navigation_items_helper.rb +++ b/app/helpers/navigation_items_helper.rb @@ -4,6 +4,7 @@ module NavigationItemsHelper def primary_items(path, current_user) if current_user.support? [ + NavigationItem.new("Home", root_path, home_current?(path)), NavigationItem.new("Organisations", organisations_path, organisations_current?(path)), NavigationItem.new("Users", users_path, users_current?(path)), NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)), @@ -12,6 +13,7 @@ module NavigationItemsHelper ].compact else [ + NavigationItem.new("Home", root_path, home_current?(path)), NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)), NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)), (NavigationItem.new("Schemes", schemes_path, non_support_supported_housing_schemes_current?(path)) if current_user.organisation.holds_own_stock? || current_user.organisation.stock_owners.present?), @@ -44,6 +46,10 @@ module NavigationItemsHelper private + def home_current?(path) + path == root_path || path == notifications_path + end + def lettings_logs_current?(path) path.starts_with?(lettings_logs_path) end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb new file mode 100644 index 000000000..ab4c8ec21 --- /dev/null +++ b/app/helpers/notifications_helper.rb @@ -0,0 +1,17 @@ +module NotificationsHelper + def notification_count + if current_user.present? + current_user.active_unread_notifications.count + else + Notification.active_unauthenticated_notifications.count + end + end + + def notification + if current_user.present? + current_user.newest_active_unread_notification + else + Notification.newest_active_unauthenticated_notification + end + end +end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 83bfb0f8a..d9a3d4a35 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -28,6 +28,10 @@ class FormHandler forms["current_lettings"] end + def previous_lettings_form + forms["previous_lettings"] + end + def current_sales_form forms["current_sales"] end diff --git a/app/models/notification.rb b/app/models/notification.rb new file mode 100644 index 000000000..86978ebea --- /dev/null +++ b/app/models/notification.rb @@ -0,0 +1,14 @@ +class Notification < ApplicationRecord + acts_as_readable + + scope :active, -> { where("start_date <= ? AND end_date >= ?", Time.zone.now, Time.zone.now) } + scope :unauthenticated, -> { where(show_on_unauthenticated_pages: true) } + + def self.active_unauthenticated_notifications + active.unauthenticated + end + + def self.newest_active_unauthenticated_notification + active_unauthenticated_notifications.last + end +end diff --git a/app/models/user.rb b/app/models/user.rb index bd04ba2b2..aba948425 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ApplicationRecord + acts_as_reader + # Include default devise modules. Others available are: # :omniauthable devise :database_authenticatable, :recoverable, :rememberable, @@ -227,6 +229,14 @@ class User < ApplicationRecord sales_logs.after_date(FormHandler.instance.sales_earliest_open_for_editing_collection_start_date).duplicate_sets(id).map { |array_str| array_str ? array_str.map(&:to_i) : [] } end + def active_unread_notifications + Notification.active.unread_by(self) + end + + def newest_active_unread_notification + active_unread_notifications.last + end + protected # Checks whether a password is needed or not. For validations only. diff --git a/app/views/home/_data_box.html.erb b/app/views/home/_data_box.html.erb new file mode 100644 index 000000000..f1d04fb2f --- /dev/null +++ b/app/views/home/_data_box.html.erb @@ -0,0 +1,9 @@ +
+
+ <%= govuk_link_to data_count(@current_user, type), data_path(@current_user, type), class: "govuk-link--no-visited-state govuk-link--no-underline" %> +
+ <%= govuk_link_to data_subheading(@current_user, type), data_path(@current_user, type), class: "govuk-link--no-visited-state" %> +
+
+ <%= govuk_link_to view_all_text(type), view_all_path(type), class: "govuk-link--inverse" %> +
diff --git a/app/views/home/_upcoming_deadlines.html.erb b/app/views/home/_upcoming_deadlines.html.erb new file mode 100644 index 000000000..ffed67ca1 --- /dev/null +++ b/app/views/home/_upcoming_deadlines.html.erb @@ -0,0 +1,35 @@ +

Upcoming deadlines

+ +<% current_lettings_form = FormHandler.instance.in_crossover_period? ? FormHandler.instance.previous_lettings_form : FormHandler.instance.current_lettings_form %> +<% formatted_deadline = "#{current_lettings_form.submission_deadline.strftime('%A')} #{current_lettings_form.submission_deadline.to_formatted_s(:govuk_date)}" %> +<% if FormHandler.instance.in_crossover_period? %> +

End of year deadline - <%= formatted_deadline %>: Deadline to submit logs for tenancies starting between <%= collection_start_date(Time.zone.now).to_formatted_s(:govuk_date) %> to <%= collection_end_date(Time.zone.now).to_formatted_s(:govuk_date) %>

+<% end %> + +<% current_quarter = quarter_for_date(date: Time.zone.now) %> +<% if current_quarter.present? %> +

<%= "#{current_quarter.quarter} - #{current_quarter.cutoff_date.strftime('%A')} #{current_quarter.cutoff_date.to_formatted_s(:govuk_date)}" %>: Quarterly cut off date for tenancies and sales starting between <%= current_quarter.quarter_start_date.to_formatted_s(:govuk_date) %> and <%= current_quarter.quarter_end_date.to_formatted_s(:govuk_date) %>.

+<% end %> + +<% if !FormHandler.instance.in_crossover_period? %> +

Try to complete your logs for each quarter by the cut-off date.

+

You can still create logs for a previous quarter after its cut-off date, as long as you complete them by the end-of-year deadline: <%= formatted_deadline %>.

+<% end %> + +<% if FormHandler.instance.in_crossover_period? %> +<% previous_lettings_form = FormHandler.instance.previous_lettings_form %> +

Prioritise completing logs for the closing collection year. You must complete all <%= previous_lettings_form.start_date.year %> to <%= previous_lettings_form.submission_deadline.year %> logs must by the end-of-year deadline. You can still create <%= current_lettings_form.start_date.year %> to <%= current_lettings_form.submission_deadline.year %> logs for this quarter after the quarterly cut-off date.

+<% end %> + +<%= govuk_details(summary_text: "Quarterly cut-off dates for 2023 to 2024") do %> +

The 2023 to 2024 quarterly cut-off dates are:

+ +

It is important that you meet these cut-off dates because we submit data to the Office for National Statistics quarterly, helping them create essential inflation statistics.

+

Meeting these cut-off dates also gives you more accurate data for your own analysis, and reduces the burden at the end of the year.

+

If you are not able to meet these quarterly dates, submit your logs as soon as you can so that they can be included in the annual data.

+<% end %> diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 000000000..aa8f7135f --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1,58 @@ +
+
+
+
+ <%= "Welcome back, #{@current_user.name}" %> +

<%= heading_for_user_role(@current_user) %>

+
+
+
+ <% if @current_user.support? || (@current_user.data_coordinator? && @current_user.sales_logs.present?) %> +
+
+ <%= render partial: "home/data_box", locals: { type: "lettings" } %> +
+
+ <%= render partial: "home/data_box", locals: { type: "sales" } %> +
+
+ <%= render partial: "home/data_box", locals: { type: "schemes" } %> +
+
+ <% else %> +
+
+ <%= render partial: "home/data_box", locals: { type: "lettings" } %> +
+ <% if @current_user.data_coordinator? %> +
+ <%= render partial: "home/data_box", locals: { type: "schemes" } %> +
+ <% elsif @current_user.sales_logs.present? %> +
+ <%= render partial: "home/data_box", locals: { type: "sales" } %> +
+ <% else %> +
+ <%= render partial: "home/data_box", locals: { type: "misc" } %> +
+ <% end %> +
+ <% end %> +
+
+
+
+
+
+
+
+ <%= render partial: "layouts/collection_resources" %> +
+ <%= render partial: "home/upcoming_deadlines" %> +
+ <%= render partial: "layouts/about_this_service" %> +
+
+
+
diff --git a/app/views/layouts/_about_this_service.html.erb b/app/views/layouts/_about_this_service.html.erb new file mode 100644 index 000000000..cdb2ceb2b --- /dev/null +++ b/app/views/layouts/_about_this_service.html.erb @@ -0,0 +1,7 @@ +<% if current_user.present? %> +

About this service

+<% else %> +

About this service

+<% end %> +

Submit social housing lettings and sales data (CORE) is a service that collects information about every new social housing letting and sale in England. The data you submit is used to make decisions about funding, regulations, and policies.

+

<%= govuk_link_to "Learn more about statistics on social housing lettings (opens in a new tab)", "https://www.gov.uk/government/collections/rents-lettings-and-tenancies", target: "_blank" %>

diff --git a/app/views/layouts/_collection_resources.html.erb b/app/views/layouts/_collection_resources.html.erb index e2d56e4a5..0c084596e 100644 --- a/app/views/layouts/_collection_resources.html.erb +++ b/app/views/layouts/_collection_resources.html.erb @@ -1,94 +1,59 @@ -
+<% if current_user %> +

Collection resources

+

<%= govuk_link_to "Guidance for submitting social housing lettings and sales data (CORE)", guidance_path %>

+<% else %>

Collection resources

-

For lettings starting during 1 April 2023 to 31 March 2024 and sales completing during the same period, use the 2023/24 forms.

- +<% end %> +

Use the 2023 to 2024 forms for lettings that start and sales that complete between 1 April 2023 and 31 March 2024.

+<%= govuk_tabs(title: "Collection resources") do |c| %> <% if FormHandler.instance.lettings_form_for_start_year(2023) && FormHandler.instance.lettings_form_for_start_year(2023).edit_end_date > Time.zone.today %> -

Lettings 2023/24

- <%= render DocumentListComponent.new(items: [ + <% c.with_tab(label: "Lettings 2023/24") do %> + <%= render DocumentListComponent.new(items: [ { - name: "Lettings log for tenants (2023/24)", + name: "Download the lettings log for tenants (2023 to 2024)", href: download_23_24_lettings_form_path, metadata: file_type_size_and_pages("2023_24_lettings_paper_form.pdf", number_of_pages: 8), }, { - name: "Lettings bulk upload template (2023/24) – New question ordering", + name: "Download the lettings bulk upload template (2023 to 2024) – New question ordering", href: download_23_24_lettings_bulk_upload_template_path, metadata: file_type_size_and_pages("bulk-upload-lettings-template-2023-24.xlsx"), }, { - name: "Lettings bulk upload template (2023/24)", + name: "Download the lettings bulk upload template (2023 to 2024) – Legacy version", href: download_23_24_lettings_bulk_upload_legacy_template_path, metadata: file_type_size_and_pages("bulk-upload-lettings-legacy-template-2023-24.xlsx"), }, { - name: "Lettings bulk upload specification (2023/24)", + name: "Download the lettings bulk upload specification (2023 to 2024)", href: download_23_24_lettings_bulk_upload_specification_path, metadata: file_type_size_and_pages("bulk-upload-lettings-specification-2023-24.xlsx"), }, - ]) %> - -

Sales 2023/24

- <%= render DocumentListComponent.new(items: [ - { - name: "Sales log for buyers (2023/24)", - href: download_23_24_sales_form_path, - metadata: file_type_size_and_pages("2023_24_sales_paper_form.pdf", number_of_pages: 8), - }, - { - name: "Sales bulk upload template (2023/24) – New question ordering", - href: download_23_24_sales_bulk_upload_template_path, - metadata: file_type_size_and_pages("bulk-upload-sales-template-2023-24.xlsx"), - }, - { - name: "Sales bulk upload template (2023/24)", - href: download_23_24_sales_bulk_upload_legacy_template_path, - metadata: file_type_size_and_pages("bulk-upload-sales-legacy-template-2023-24.xlsx"), - }, - { - name: "Sales bulk upload specification (2023/24)", - href: download_23_24_sales_bulk_upload_specification_path, - metadata: file_type_size_and_pages("bulk-upload-sales-specification-2023-24.xlsx"), - }, - ]) %> - <% end %> - - <% if FormHandler.instance.lettings_form_for_start_year(2022) && FormHandler.instance.lettings_form_for_start_year(2022).edit_end_date > Time.zone.today %> -

Lettings 2022/23

- <%= render DocumentListComponent.new(items: [ - { - name: "Lettings log for tenants (2022/23)", - href: download_22_23_lettings_form_path, - metadata: file_type_size_and_pages("2022_23_lettings_paper_form.pdf", number_of_pages: 4), - }, - { - name: "Lettings bulk upload template (2022/23)", - href: download_22_23_lettings_bulk_upload_template_path, - metadata: file_type_size_and_pages("bulk-upload-lettings-template-2022-23.xlsx"), - }, - { - name: "Lettings bulk upload specification (2022/23)", - href: download_22_23_lettings_bulk_upload_specification_path, - metadata: file_type_size_and_pages("bulk-upload-lettings-specification-2022-23.xlsx"), - }, - ]) %> - -

Sales 2022/23

- <%= render DocumentListComponent.new(items: [ - { - name: "Sales log for buyers (2022/23)", - href: download_22_23_sales_form_path, - metadata: file_type_size_and_pages("2022_23_sales_paper_form.pdf", number_of_pages: 5), - }, - { - name: "Sales bulk upload template (2022/23)", - href: download_22_23_sales_bulk_upload_template_path, - metadata: file_type_size_and_pages("bulk-upload-sales-template-2022-23.xlsx"), - }, - { - name: "Sales bulk upload specification (2022/23)", - href: download_22_23_sales_bulk_upload_specification_path, - metadata: file_type_size_and_pages("bulk-upload-sales-template-2022-23.xlsx"), - }, - ]) %> + ]) %> + <% end %> + <% c.with_tab(label: "Sales 2023/24") do %> + <%= render DocumentListComponent.new(items: [ + { + name: "Download the sales log for buyers (2023 to 2024)", + href: download_23_24_sales_form_path, + metadata: file_type_size_and_pages("2023_24_sales_paper_form.pdf", number_of_pages: 8), + }, + { + name: "Download the sales bulk upload template (2023 to 2024) – New question ordering", + href: download_23_24_sales_bulk_upload_template_path, + metadata: file_type_size_and_pages("bulk-upload-sales-template-2023-24.xlsx"), + }, + { + name: "Download the sales bulk upload template (2023 to 2024) – Legacy version", + href: download_23_24_sales_bulk_upload_legacy_template_path, + metadata: file_type_size_and_pages("bulk-upload-sales-legacy-template-2023-24.xlsx"), + }, + { + name: "Download the sales bulk upload specification (2023 to 2024)", + href: download_23_24_sales_bulk_upload_specification_path, + metadata: file_type_size_and_pages("bulk-upload-sales-specification-2023-24.xlsx"), + }, + ]) %> + <% end %> <% end %> -
+<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 35d3c23a8..1ed5014c3 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -99,6 +99,10 @@ end end %> + <% if notifications_to_display? %> + <%= render "notifications/notification_banner" %> + <% end %> + <% feedback_link = govuk_link_to "giving us your feedback (opens in a new tab)", t("feedback_form"), rel: "noreferrer noopener", target: "_blank" %> <%= govuk_phase_banner( @@ -107,7 +111,7 @@ text: "This is a new service – help us improve it by #{feedback_link}".html_safe, ) %> - <% if !current_user.nil? %> + <% if current_user.present? %> <%= render PrimaryNavigationComponent.new( items: primary_items(request.path, current_user), ) %> @@ -122,7 +126,7 @@ <%= govuk_notification_banner( title_text: "Success", success: true, title_heading_level: 3, - title_id: "swanky-notifications" + title_id: "flash-notice" ) do |notification_banner| notification_banner.with_heading(text: flash.notice.html_safe) if flash[:notification_banner_body] diff --git a/app/views/notifications/_notification_banner.html.erb b/app/views/notifications/_notification_banner.html.erb new file mode 100644 index 000000000..230fe458a --- /dev/null +++ b/app/views/notifications/_notification_banner.html.erb @@ -0,0 +1,23 @@ +
+
+
+
+
+ <% if notification_count > 1 && current_user.present? %> +

Notification 1 of <%= notification_count %>

+ <% end %> +

<%= notification.title %>

+ <% if notification.page_content.present? %> +
+ <%= govuk_link_to notification.link_text, notifications_path, class: "govuk-link--inverse govuk-!-font-weight-bold" %> +
+ <% end %> +
+ <% if current_user.present? %> +

+ <%= govuk_link_to "Dismiss", dismiss_notifications_path, class: "govuk-link--inverse" %> +

+ <% end %> +
+
+
diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb new file mode 100644 index 000000000..abdc77044 --- /dev/null +++ b/app/views/notifications/show.html.erb @@ -0,0 +1,17 @@ +<% content_for :title, "Notification" %> +<% content_for :before_content do %> + <%= govuk_back_link(href: :back) %> +<% end %> + +
+
+

<%= @notification.title %>

+

+ <%= sanitize @notification.page_content %> +

+
+
+
+
+ <%= govuk_button_link_to "Back to #{current_user.present? ? 'Home' : 'Start'}", root_path %> +
diff --git a/app/views/organisations/show.html.erb b/app/views/organisations/show.html.erb index 4fbe0e805..21f722920 100644 --- a/app/views/organisations/show.html.erb +++ b/app/views/organisations/show.html.erb @@ -41,8 +41,4 @@ <% end %> <%= render partial: "organisations/merged_organisation_details" %> - -
- <%= render partial: "layouts/collection_resources" %> -
diff --git a/app/views/start/guidance.html.erb b/app/views/start/guidance.html.erb new file mode 100644 index 000000000..127b56b3b --- /dev/null +++ b/app/views/start/guidance.html.erb @@ -0,0 +1,68 @@ +

+ Guidance for submitting social housing lettings and sales data +

+ +
+
+

This page includes details of when a CORE log is and is not required, what to do if a tenant or buyer is reluctant to answer questions in a log, and other information about submitting logs using CORE.

+ <%= govuk_accordion do |accordion| %> + <%= accordion.with_section(heading_text: "How to create logs", expanded: true) do %> +

There are 2 ways to create logs on CORE.

+

You can create logs one at a time by answering questions using the online form. Click the “Create a new log” button on the logs page to create logs this way.

+

You can also create many logs at once by uploading a CSV file. This might be faster than creating logs individually if your organisation has its own database and a way to export the data. Click the “Upload logs in bulk” button on the logs page to create logs this way. For more information, <%= govuk_link_to "read the full guidance on bulk upload", bulk_upload_lettings_log_path(id: "guidance", form: { year: current_collection_start_year }) %>.

+

Once you have created and completed a log, there is nothing more you need to do to submit the data.

+ <% end %> + + <%= accordion.with_section(heading_text: "What scenarios require a new log?") do %> +

For general needs, you should complete a log for each new tenancy intended to last 2 years or more if it is social rent or affordable rent, or of any length if it is intermediate rent.

+

For supported housing, you should complete a log for each new letting of any length.

+

If a new tenancy agreement is signed, create a new log.

+ <% end %> + + <%= accordion.with_section(heading_text: "Types of lettings you should create logs for") do %> +

You’ll need to create a log for:

+ + <% end %> + + <%= accordion.with_section(heading_text: "Types of lettings you should not create logs for") do %> +

You don’t need to create a log for:

+ + <% end %> + + <%= accordion.with_section(heading_text: "What if someone is reluctant to answer any questions?") do %> +

If a tenant or buyer is reluctant to answer questions as part of a log, you should explain that:

+ +

If a tenant or buyer is still unwilling or unable to answer questions, select the ‘Don’t know’ or ‘Tenant/person prefers not to say’ options.

+ <% end %> + <% end %> +
+
diff --git a/app/views/start/index.html.erb b/app/views/start/index.html.erb index 3c10232d2..79abb5df7 100644 --- a/app/views/start/index.html.erb +++ b/app/views/start/index.html.erb @@ -16,13 +16,16 @@ href: start_path, ) %> +
+

Before you start

Use your account details to sign in.

If you need to set up a new account, speak to your organisation’s CORE data coordinator. If you don’t know who that is, <%= govuk_link_to("contact the helpdesk", GlobalConstants::HELPDESK_URL) %>.

You can <%= govuk_mail_to("dluhc.digital-services@levellingup.gov.uk", "request an account", subject: "CORE: Request a new account") %> if your organisation doesn’t have one.

- - -
+

<%= govuk_link_to guidance_path do %>Guidance for submitting social housing lettings and sales data (CORE)<% end %>

+


<%= render partial: "layouts/collection_resources" %> +
+ <%= render partial: "layouts/about_this_service" %>
diff --git a/config/routes.rb b/config/routes.rb index af0b95d07..06cb4f8d3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,6 +30,7 @@ Rails.application.routes.draw do resource :cookies, only: %i[show update] root to: "start#index" + get "/guidance", to: "start#guidance" get "/logs", to: redirect("lettings-logs") get "/accessibility-statement", to: "content#accessibility_statement" @@ -127,6 +128,10 @@ Rails.application.routes.draw do end end + resource :notifications do + get "dismiss", to: "notifications#dismiss" + end + resources :organisations do get "duplicates", to: "duplicate_logs#index" diff --git a/db/migrate/20240108145545_create_notification.rb b/db/migrate/20240108145545_create_notification.rb new file mode 100644 index 000000000..dc69a8efe --- /dev/null +++ b/db/migrate/20240108145545_create_notification.rb @@ -0,0 +1,14 @@ +class CreateNotification < ActiveRecord::Migration[7.0] + def change + create_table :notifications do |t| + t.string :title + t.string :link_text + t.string :page_content + t.datetime :start_date + t.datetime :end_date + t.boolean :show_on_unauthenticated_pages + + t.timestamps + end + end +end diff --git a/db/migrate/20240108152935_unread_migration.rb b/db/migrate/20240108152935_unread_migration.rb new file mode 100644 index 000000000..25067b439 --- /dev/null +++ b/db/migrate/20240108152935_unread_migration.rb @@ -0,0 +1,25 @@ +class UnreadMigration < ActiveRecord::Migration[6.0] + def self.up + create_table ReadMark, force: true, options: create_options do |t| + t.references :readable, polymorphic: { null: false } + t.references :reader, polymorphic: { null: false } + t.datetime :timestamp, null: false + t.timestamps + end + + add_index ReadMark, %i[reader_id reader_type readable_type readable_id], name: "read_marks_reader_readable_index", unique: true + end + + def self.down + drop_table ReadMark + end + + def self.create_options + options = "" + if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) \ + && ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) + options = "DEFAULT CHARSET=latin1" + end + options + end +end diff --git a/db/schema.rb b/db/schema.rb index 22d67bb9f..e75f47e60 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_12_18_105226) do +ActiveRecord::Schema[7.0].define(version: 2024_01_08_152935) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -393,6 +393,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_12_18_105226) do t.string "new_organisation_telephone_number" end + create_table "notifications", force: :cascade do |t| + t.string "title" + t.string "link_text" + t.string "page_content" + t.datetime "start_date" + t.datetime "end_date" + t.boolean "show_on_unauthenticated_pages" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "organisation_relationships", force: :cascade do |t| t.integer "child_organisation_id" t.integer "parent_organisation_id" @@ -446,6 +457,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_12_18_105226) do t.index ["old_visible_id"], name: "index_organisations_on_old_visible_id", unique: true end + create_table "read_marks", force: :cascade do |t| + t.string "readable_type", null: false + t.bigint "readable_id" + t.string "reader_type", null: false + t.bigint "reader_id" + t.datetime "timestamp", precision: nil, null: false + t.index ["readable_type", "readable_id"], name: "index_read_marks_on_readable_type_and_readable_id" + t.index ["reader_id", "reader_type", "readable_type", "readable_id"], name: "read_marks_reader_readable_index", unique: true + t.index ["reader_type", "reader_id"], name: "index_read_marks_on_reader_type_and_reader_id" + end + create_table "sales_logs", force: :cascade do |t| t.integer "status", default: 0 t.datetime "saledate" diff --git a/spec/factories/notification.rb b/spec/factories/notification.rb new file mode 100644 index 000000000..05d4faa3f --- /dev/null +++ b/spec/factories/notification.rb @@ -0,0 +1,10 @@ +FactoryBot.define do + factory :notification do + title { "Notification title" } + link_text { "Link text" } + page_content { "Some html content" } + start_date { Time.zone.yesterday } + end_date { Time.zone.tomorrow } + show_on_unauthenticated_pages { false } + end +end diff --git a/spec/features/home_page_spec.rb b/spec/features/home_page_spec.rb new file mode 100644 index 000000000..871616b8e --- /dev/null +++ b/spec/features/home_page_spec.rb @@ -0,0 +1,276 @@ +require "rails_helper" +require_relative "form/helpers" + +RSpec.describe "Home Page Features" do + include Helpers + + context "when there are notifications" do + let!(:user) { FactoryBot.create(:user) } + + context "when the notifications are currently active" do + before do + create(:notification, title: "Notification title 1") + create(:notification, title: "Notification title 2") + create(:notification, title: "Notification title 3") + sign_in user + visit(root_path) + end + + it "shows the latest notification with count and dismiss link" do + expect(page).to have_content("Notification 1 of 3") + expect(page).to have_content("Notification title 3") + expect(page).to have_link("Dismiss") + expect(page).to have_link("Link text") + end + + context "when the user clicks a notification link" do + before do + click_link("Link text") + end + + it "takes them to the notification details page" do + expect(page).to have_current_path(notifications_path) + expect(page).to have_content("Notification title 3") + expect(page).to have_content("Some html content") + expect(page).to have_link("Back to Home") + end + + context "when they return" do + before do + click_link("Back to Home") + end + + it "the notification has not been dismissed" do + expect(page).to have_current_path(root_path) + expect(page).to have_content("Notification 1 of 3") + expect(page).to have_content("Notification title 3") + expect(page).to have_link("Dismiss") + expect(page).to have_link("Link text") + end + end + end + + context "when the user clicks a dismiss link" do + before do + click_link("Dismiss") + end + + it "dismisses the notification and takes them back" do + expect(page).to have_current_path(root_path) + expect(page).to have_content("Notification 1 of 2") + expect(page).to have_content("Notification title 2") + expect(page).to have_link("Dismiss") + expect(page).to have_link("Link text") + end + + context "when the user dismisses the penultimate notification" do + before do + click_link("Dismiss") + end + + it "no longer displays the count" do + expect(page).to have_current_path(root_path) + expect(page).not_to have_content("Notification 1 of") + expect(page).to have_content("Notification title 1") + end + + context "when the user dismisses the final notification" do + before do + click_link("Dismiss") + end + + it "no longer displays any notification" do + expect(page).to have_current_path(root_path) + expect(page).not_to have_content("Notification") + expect(page).not_to have_link("Dismiss") + expect(page).not_to have_link("Link_text") + end + end + end + end + + context "when another user has dismissed all their notifications" do + before do + other_user = create(:user) + Notification.mark_as_read! :all, for: other_user + visit(root_path) + end + + it "the first user can still see the notifications" do + expect(page).to have_content("Notification 1 of 3") + expect(page).to have_content("Notification title 3") + expect(page).to have_link("Dismiss") + expect(page).to have_link("Link text") + end + end + end + + context "when the notifications are not currently active" do + before do + create(:notification, end_date: Time.zone.yesterday, title: "Notification title 1") + create(:notification, start_date: Time.zone.tomorrow, title: "Notification title 2") + sign_in user + visit(root_path) + end + + it "does not show any notifications" do + expect(page).not_to have_content("Notification title") + expect(page).not_to have_content("Notification 1 of") + expect(page).not_to have_link("Dismiss") + expect(page).not_to have_link("Link text") + end + end + end + + context "when the user is a data provider" do + let(:user) { FactoryBot.create(:user, name: "Provider") } + + before do + create_list(:lettings_log, 6, :in_progress, owning_organisation: user.organisation, created_by: user) + create_list(:lettings_log, 2, :in_progress, owning_organisation: user.organisation) + create_list(:lettings_log, 4, :completed, owning_organisation: user.organisation, created_by: user) + create_list(:lettings_log, 2, :completed) + sign_in user + visit(root_path) + end + + it "displays the correct welcome text" do + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back, Provider") + expect(page).to have_content("Complete your logs") + end + + context "when their organisation has submitted sales logs" do + before do + create_list(:sales_log, 5, :in_progress, owning_organisation: user.organisation, created_by: user) + create_list(:sales_log, 3, :completed, owning_organisation: user.organisation, created_by: user) + visit(root_path) + end + + it "displays correct data boxes, counts and links" do + data_boxes = page.find_all(class: "app-data-box-one-half") + expect(data_boxes.count).to eq(2) + expect(data_boxes[0].all("a").map(&:text)).to eq(["6", "Your lettings in progress", "View all lettings"]) + expect(data_boxes[0].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "lettings_logs")]) + expect(data_boxes[1].all("a").map(&:text)).to eq(["5", "Your sales in progress", "View all sales"]) + expect(data_boxes[1].all("a").map { |line| line["href"] }).to eq([sales_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), sales_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "sales_logs")]) + end + end + + context "when their organisation has never submitted sales logs" do + before do + visit(root_path) + end + + it "displays correct data boxes, counts and links" do + data_boxes = page.find_all(class: "app-data-box-one-half") + expect(data_boxes.count).to eq(2) + expect(data_boxes[0].all("a").map(&:text)).to eq(["6", "Your lettings in progress", "View all lettings"]) + expect(data_boxes[0].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:in_progress], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "lettings_logs")]) + expect(data_boxes[1].all("a").map(&:text)).to eq(["4", "Your completed lettings", "View all schemes"]) + expect(data_boxes[1].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:completed], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:completed], assigned_to: "you", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "schemes")]) + end + end + end + + context "when the user is a data coordinator" do + before do + create_list(:lettings_log, 6, :in_progress, owning_organisation: user.organisation) + create_list(:lettings_log, 2, :in_progress, owning_organisation: user.organisation, created_by: user) + create_list(:lettings_log, 4, :completed, owning_organisation: user.organisation) + create_list(:lettings_log, 2, :completed) + create_list(:scheme, 1, :incomplete, owning_organisation: user.organisation) + sign_in user + visit(root_path) + end + + let(:user) { FactoryBot.create(:user, :data_coordinator, name: "Coordinator") } + + it "displays the correct welcome text" do + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back, Coordinator") + expect(page).to have_content("Manage your data") + end + + context "when their organisation has submitted sales logs" do + before do + create_list(:sales_log, 5, :in_progress, owning_organisation: user.organisation) + create_list(:sales_log, 3, :completed, owning_organisation: user.organisation) + visit(root_path) + end + + it "displays correct data boxes, counts and links" do + data_boxes = page.find_all(class: "app-data-box-one-third") + expect(data_boxes.count).to eq(3) + expect(data_boxes[0].all("a").map(&:text)).to eq(["8", "Lettings in progress", "View all lettings"]) + expect(data_boxes[0].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "lettings_logs")]) + expect(data_boxes[1].all("a").map(&:text)).to eq(["5", "Sales in progress", "View all sales"]) + expect(data_boxes[1].all("a").map { |line| line["href"] }).to eq([sales_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), sales_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "sales_logs")]) + expect(data_boxes[2].all("a").map(&:text)).to eq(["1", "Incomplete schemes", "View all schemes"]) + expect(data_boxes[2].all("a").map { |line| line["href"] }).to eq([schemes_path(status: [:incomplete], owning_organisation_select: "all"), schemes_path(status: [:incomplete], owning_organisation_select: "all"), clear_filters_path(filter_type: "schemes")]) + end + end + + context "when their organisation has never submitted sales logs" do + before do + visit(root_path) + end + + it "displays correct data boxes, counts and links" do + data_boxes = page.find_all(class: "app-data-box-one-half") + expect(data_boxes.count).to eq(2) + expect(data_boxes[0].all("a").map(&:text)).to eq(["8", "Lettings in progress", "View all lettings"]) + expect(data_boxes[0].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "lettings_logs")]) + expect(data_boxes[1].all("a").map(&:text)).to eq(["1", "Incomplete schemes", "View all schemes"]) + expect(data_boxes[1].all("a").map { |line| line["href"] }).to eq([schemes_path(status: [:incomplete], owning_organisation_select: "all"), schemes_path(status: [:incomplete], owning_organisation_select: "all"), clear_filters_path(filter_type: "schemes")]) + end + end + end + + context "when the user is a support user" do + let(:support_user) { FactoryBot.create(:user, :support, name: "Support") } + let(:notify_client) { instance_double(Notifications::Client) } + let(:confirmation_token) { "MCDH5y6Km-U7CFPgAMVS" } + let(:devise_notify_mailer) { DeviseNotifyMailer.new } + let(:otp) { "999111" } + + before do + create_list(:lettings_log, 2, :in_progress) + create_list(:lettings_log, 1, :completed) + create_list(:sales_log, 3, :in_progress) + create_list(:sales_log, 1, :completed) + create_list(:scheme, 1, :incomplete) + completed_scheme = create(:scheme) + create(:location, scheme: completed_scheme) + allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer) + allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client) + allow(Devise).to receive(:friendly_token).and_return(confirmation_token) + allow(notify_client).to receive(:send_email).and_return(true) + allow(SecureRandom).to receive(:random_number).and_return(otp) + visit("/lettings-logs") + fill_in("user[email]", with: support_user.email) + fill_in("user[password]", with: support_user.password) + click_button("Sign in") + fill_in("code", with: otp) + click_button("Submit") + visit(root_path) + end + + it "displays the correct welcome text" do + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back, Support") + expect(page).to have_content("Manage all data") + end + + it "displays correct data boxes, counts and links" do + data_boxes = page.find_all(class: "app-data-box-one-third") + expect(data_boxes.count).to eq(3) + expect(data_boxes[0].all("a").map(&:text)).to eq(["2", "Lettings in progress", "View all lettings"]) + expect(data_boxes[0].all("a").map { |line| line["href"] }).to eq([lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), lettings_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "lettings_logs")]) + expect(data_boxes[1].all("a").map(&:text)).to eq(["3", "Sales in progress", "View all sales"]) + expect(data_boxes[1].all("a").map { |line| line["href"] }).to eq([sales_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), sales_logs_path(status: [:in_progress], assigned_to: "all", years: [""], owning_organisation_select: "all", managing_organisation_select: "all"), clear_filters_path(filter_type: "sales_logs")]) + expect(data_boxes[2].all("a").map(&:text)).to eq(["1", "Incomplete schemes", "View all schemes"]) + expect(data_boxes[2].all("a").map { |line| line["href"] }).to eq([schemes_path(status: [:incomplete], owning_organisation_select: "all"), schemes_path(status: [:incomplete], owning_organisation_select: "all"), clear_filters_path(filter_type: "schemes")]) + end + end +end diff --git a/spec/features/notifications_page_spec.rb b/spec/features/notifications_page_spec.rb new file mode 100644 index 000000000..97bbeb7eb --- /dev/null +++ b/spec/features/notifications_page_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" +require_relative "form/helpers" + +RSpec.describe "Notifications Page Features" do + include Helpers + + context "when there are notifications" do + let!(:user) { FactoryBot.create(:user) } + + context "when the notifications are currently active" do + before do + create(:notification, title: "Notification title 1") + create(:notification, title: "Notification title 2") + create(:notification, title: "Notification title 3") + sign_in user + visit(notifications_path) + end + + it "does not show the notification banner" do + expect(page).not_to have_content("Notification 1 of") + expect(page).not_to have_link("Dismiss") + expect(page).not_to have_link("Link text") + end + end + + context "when the notifications are not currently active" do + before do + create(:notification, end_date: Time.zone.yesterday, title: "Notification title 1") + create(:notification, start_date: Time.zone.tomorrow, title: "Notification title 2") + sign_in user + visit(notifications_path) + end + + it "does not show the notifications banner" do + expect(page).not_to have_content("Notification 1 of") + expect(page).not_to have_link("Dismiss") + expect(page).not_to have_link("Link text") + end + end + end +end diff --git a/spec/features/start_page_spec.rb b/spec/features/start_page_spec.rb index 569ea4cfa..d90a0c1f0 100644 --- a/spec/features/start_page_spec.rb +++ b/spec/features/start_page_spec.rb @@ -10,21 +10,38 @@ RSpec.describe "Start Page Features" do sign_in user end - it "takes you to logs" do - visit("/") - expect(page).to have_current_path("/lettings-logs") + it "takes you to the home page" do + visit(root_path) + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back") end end context "when the user is not signed in" do - it "takes you to sign in and then to logs" do - visit("/") + it "takes you to sign in and then to the home page" do + visit(root_path) click_link("Start now") expect(page).to have_current_path("/account/sign-in?start=true") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") - expect(page).to have_current_path("/lettings-logs") + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back") + end + + context "when the unauthenticated user clicks a notification link" do + before do + create(:notification, show_on_unauthenticated_pages: true) + visit(root_path) + click_link("Link text") + end + + it "takes them to the notification details page" do + expect(page).to have_current_path(notifications_path) + expect(page).to have_content("Notification title") + expect(page).to have_content("Some html content") + expect(page).to have_link("Back to Start") + end end end end diff --git a/spec/features/test_spec.rb b/spec/features/test_spec.rb index ef54fa631..6dc977a9b 100644 --- a/spec/features/test_spec.rb +++ b/spec/features/test_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe "Test Features" do it "Displays the name of the app" do - visit("/") + visit(root_path) expect(page).to have_content("Submit social housing lettings and sales data (CORE)") end diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb index f298616f2..e898c1b0a 100644 --- a/spec/features/user_spec.rb +++ b/spec/features/user_spec.rb @@ -126,13 +126,14 @@ RSpec.describe "User Features" do end it "Can navigate and sign in page with sign in button" do - visit("/") + visit(root_path) expect(page).to have_link("Sign in") click_link("Sign in") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") - expect(page).to have_current_path("/lettings-logs") + expect(page).to have_current_path("/") + expect(page).to have_content("Welcome back") end it "tries to access account page, redirects to log in page" do diff --git a/spec/helpers/collection_time_helper_spec.rb b/spec/helpers/collection_time_helper_spec.rb index 3eef01b5e..859431c57 100644 --- a/spec/helpers/collection_time_helper_spec.rb +++ b/spec/helpers/collection_time_helper_spec.rb @@ -109,4 +109,34 @@ RSpec.describe CollectionTimeHelper do end end end + + describe "#quarter_for_date" do + it "returns correct cutoff date for curent quarter" do + quarter = quarter_for_date(date: Time.zone.local(2023, 10, 1)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 1, 12)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2023, 10, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2023, 12, 31)) + end + + it "returns correct cutoff date for the first quarter of 2024/25" do + quarter = quarter_for_date(date: Time.zone.local(2024, 4, 1)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 7, 12)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 4, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 6, 30)) + end + + it "returns correct cutoff date for the second quarter of 2024/25" do + quarter = quarter_for_date(date: Time.zone.local(2024, 9, 30)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2024, 10, 11)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 7, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 9, 30)) + end + + it "returns correct cutoff date for the third quarter of 2024/25" do + quarter = quarter_for_date(date: Time.zone.local(2024, 10, 25)) + expect(quarter.cutoff_date).to eq(Time.zone.local(2025, 1, 10)) + expect(quarter.quarter_start_date).to eq(Time.zone.local(2024, 10, 1)) + expect(quarter.quarter_end_date).to eq(Time.zone.local(2024, 12, 31)) + end + end end diff --git a/spec/helpers/navigation_items_helper_spec.rb b/spec/helpers/navigation_items_helper_spec.rb index 2538b10a8..13b634e2f 100644 --- a/spec/helpers/navigation_items_helper_spec.rb +++ b/spec/helpers/navigation_items_helper_spec.rb @@ -12,6 +12,7 @@ RSpec.describe NavigationItemsHelper do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), @@ -21,7 +22,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end @@ -34,6 +35,7 @@ RSpec.describe NavigationItemsHelper do let(:stock_owner) { create(:organisation) } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -44,15 +46,35 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end end end + context "when the user is on the home page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Home", "/", true), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}/details", false), + NavigationItemsHelper::NavigationItem.new("Stock owners", "/organisations/#{current_user.organisation.id}/stock-owners", false), + NavigationItemsHelper::NavigationItem.new("Managing agents", "/organisations/#{current_user.organisation.id}/managing-agents", false), + ] + end + + it "returns navigation items with the home item set as current" do + expect(primary_items("/", current_user)).to eq(expected_navigation_items) + end + end + context "when the user is on the lettings logs page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -63,7 +85,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end end @@ -71,6 +93,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the sales logs page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -81,7 +104,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the sales logs item set as current" do expect(primary_items("/sales-logs", current_user)).to eq(expected_navigation_items) end end @@ -89,6 +112,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the users page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -107,6 +131,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on their organisation details page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -117,7 +142,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the organisation item set as current" do expect(primary_items("/organisations/#{current_user.organisation.id}/details", current_user)).to eq(expected_navigation_items) end end @@ -125,6 +150,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the account page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -135,7 +161,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with no items set as current" do expect(primary_items("/account", current_user)).to eq(expected_navigation_items) end end @@ -143,6 +169,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the individual user's page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -161,6 +188,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the individual scheme's page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), @@ -171,7 +199,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with Schemes item set as current" do + it "returns navigation items with schemes item set as current" do expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) end end @@ -191,6 +219,7 @@ RSpec.describe NavigationItemsHelper do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), @@ -200,7 +229,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end @@ -213,6 +242,7 @@ RSpec.describe NavigationItemsHelper do let(:stock_owner) { create(:organisation) } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), @@ -223,7 +253,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end end @@ -233,9 +263,27 @@ RSpec.describe NavigationItemsHelper do context "when the user is a support user" do let(:current_user) { create(:user, :support) } + context "when the user is on the home page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Home", "/", true), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + it "returns navigation items with the home item set as current" do + expect(primary_items("/", current_user)).to eq(expected_navigation_items) + end + end + context "when the user is on the lettings logs page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), @@ -244,7 +292,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) end end @@ -252,6 +300,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the sales logs page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -260,7 +309,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the sales logs item set as current" do expect(primary_items("/sales-logs", current_user)).to eq(expected_navigation_items) end end @@ -268,6 +317,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the users page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", true), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -284,6 +334,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the account page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -292,7 +343,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the no items set as current" do expect(primary_items("/account", current_user)).to eq(expected_navigation_items) end end @@ -300,6 +351,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the Schemes page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -308,7 +360,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the users item set as current" do + it "returns navigation items with the schemes item set as current" do expect(primary_items("/schemes", current_user)).to eq(expected_navigation_items) end end @@ -316,6 +368,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the individual user's page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", true), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -332,6 +385,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the individual scheme's page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -347,7 +401,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with Schemes item set as current" do + it "returns navigation items with schemes item set as current" do expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) expect(scheme_items("/schemes/1", 1)).to eq(expected_scheme_items) end @@ -356,6 +410,7 @@ RSpec.describe NavigationItemsHelper do context "when the user is on the scheme locations page" do let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -371,7 +426,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with Schemes item set as current" do + it "returns navigation items with schemes item set as current" do expect(primary_items("/schemes/1/locations", current_user)).to eq(expected_navigation_items) expect(scheme_items("/schemes/1/locations", 1)).to eq(expected_scheme_items) end @@ -382,6 +437,7 @@ RSpec.describe NavigationItemsHelper do let(:required_sub_path) { "lettings-logs" } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -402,7 +458,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the logs item set as current" do + it "returns navigation items with the lettings logs item set as current" do expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) end @@ -412,6 +468,7 @@ RSpec.describe NavigationItemsHelper do let(:required_sub_path) { "users" } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -432,7 +489,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the logs item set as current" do + it "returns navigation items with the users item set as current" do expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) end @@ -442,6 +499,7 @@ RSpec.describe NavigationItemsHelper do let(:required_sub_path) { "schemes" } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -472,6 +530,7 @@ RSpec.describe NavigationItemsHelper do let(:required_sub_path) { "details" } let(:expected_navigation_items) do [ + NavigationItemsHelper::NavigationItem.new("Home", "/", false), NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), @@ -492,7 +551,7 @@ RSpec.describe NavigationItemsHelper do ] end - it "returns navigation items with the logs item set as current" do + it "returns navigation items with the organisation item set as current" do expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) end diff --git a/spec/requests/auth/passwords_controller_spec.rb b/spec/requests/auth/passwords_controller_spec.rb index 92c36608c..333985d9e 100644 --- a/spec/requests/auth/passwords_controller_spec.rb +++ b/spec/requests/auth/passwords_controller_spec.rb @@ -67,7 +67,6 @@ RSpec.describe Auth::PasswordsController, type: :request do put "/account/password", params: update_password_params # Devise redirects once after re-sign in with new password and then root redirects as well. follow_redirect! - follow_redirect! expect(page).to have_css("div", class: "govuk-notification-banner__heading", text: message) end end diff --git a/spec/requests/maintenance_controller_spec.rb b/spec/requests/maintenance_controller_spec.rb index 4d7f8ab8c..39e587302 100644 --- a/spec/requests/maintenance_controller_spec.rb +++ b/spec/requests/maintenance_controller_spec.rb @@ -153,7 +153,6 @@ RSpec.describe MaintenanceController, type: :request do end it "the cookie banner is visible" do - follow_redirect! follow_redirect! expect(page).to have_content("We’d like to use analytics cookies so we can understand how you use the service and make improvements.") end diff --git a/spec/requests/organisations_controller_spec.rb b/spec/requests/organisations_controller_spec.rb index 97665f8ca..68de93c51 100644 --- a/spec/requests/organisations_controller_spec.rb +++ b/spec/requests/organisations_controller_spec.rb @@ -310,42 +310,6 @@ RSpec.describe OrganisationsController, type: :request do it "redirects to details" do expect(response).to have_http_status(:redirect) end - - context "and 2022 collection window is open" do - let(:set_time) { allow(Time).to receive(:now).and_return(Time.zone.local(2023, 1, 1)) } - - it "displays correct resources for 2022/23 and 2023/24 collection years" do - follow_redirect! - expect(page).to have_content("Lettings 2023/24") - expect(page).to have_content("Sales 2023/24") - expect(page).to have_content("Lettings 2022/23") - expect(page).to have_content("Sales 2022/23") - end - end - - context "and 2022 collection window is closed for editing" do - let(:set_time) { allow(Time).to receive(:now).and_return(Time.zone.local(2024, 1, 1)) } - - it "displays correct resources for 2022/23 and 2023/24 collection years" do - follow_redirect! - expect(page).to have_content("Lettings 2023/24") - expect(page).to have_content("Sales 2023/24") - expect(page).not_to have_content("Lettings 2022/23") - expect(page).not_to have_content("Sales 2022/23") - end - end - - context "and 2023 collection window is closed for editing" do - let(:set_time) { allow(Time).to receive(:now).and_return(Time.zone.local(2025, 1, 1)) } - - it "displays correct resources for 2022/23 and 2023/24 collection years" do - follow_redirect! - expect(page).not_to have_content("Lettings 2023/24") - expect(page).not_to have_content("Sales 2023/24") - expect(page).not_to have_content("Lettings 2022/23") - expect(page).not_to have_content("Sales 2022/23") - end - end end context "with an organisation that are not in scope for the user, i.e. that they do not belong to" do diff --git a/spec/requests/start_controller_spec.rb b/spec/requests/start_controller_spec.rb new file mode 100644 index 000000000..edca4d7c4 --- /dev/null +++ b/spec/requests/start_controller_spec.rb @@ -0,0 +1,90 @@ +require "rails_helper" + +RSpec.describe StartController, type: :request do + let(:user) { create(:user) } + let(:headers) { { "Accept" => "text/html" } } + let(:page) { Capybara::Node::Simple.new(response.body) } + let(:notify_client) { instance_double(Notifications::Client) } + let(:devise_notify_mailer) { DeviseNotifyMailer.new } + + before do + allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer) + allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client) + allow(notify_client).to receive(:send_email).and_return(true) + end + + describe "GET" do + context "when the user is not signed in" do + it "routes user to the start page" do + get "/", headers: headers, params: {} + expect(path).to eq("/") + expect(page).to have_content("Start now") + end + end + + context "when the user is signed in" do + before do + sign_in user + end + + it "routes user to the home page" do + get "/", headers:, params: {} + expect(page).to have_content("Welcome back") + end + + context "and 2023 collection window is open for editing" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2024, 1, 1)) + end + + it "displays correct resources for 2022/23 and 2023/24 collection years" do + get "/", headers: headers, params: {} + expect(page).to have_content("Lettings 2023/24") + expect(page).to have_content("Sales 2023/24") + end + end + + context "and 2023 collection window is closed for editing" do + before do + allow(Time).to receive(:now).and_return(Time.zone.local(2025, 1, 1)) + end + + it "displays correct resources for 2022/23 and 2023/24 collection years" do + get "/", headers: headers, params: {} + expect(page).not_to have_content("Lettings 2023/24") + expect(page).not_to have_content("Sales 2023/24") + end + end + + it "shows guidance link" do + get "/", headers: headers, params: {} + expect(page).to have_content("Guidance for submitting social housing lettings and sales data (CORE)") + end + + it "displays About this service section" do + get "/", headers:, params: {} + expect(page).to have_content("About this service") + end + end + end + + describe "guidance page" do + context "when the user is not signed in" do + it "routes user to the guidance page" do + get "/guidance", headers:, params: {} + expect(page).to have_content("Guidance for submitting social housing lettings and sales data") + end + end + + context "when the user is signed in" do + before do + sign_in user + end + + it "routes user to the guidance page" do + get "/guidance", headers:, params: {} + expect(page).to have_content("Guidance for submitting social housing lettings and sales data") + end + end + end +end diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index 90b49c2be..48cc0f064 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -73,11 +73,11 @@ RSpec.describe UsersController, type: :request do end describe "title link" do - it "routes user to the /logs page" do + it "routes user to the home page" do sign_in user get "/", headers:, params: {} - follow_redirect! - expect(path).to include("/lettings-logs") + expect(path).to eq("/") + expect(page).to have_content("Welcome back") expected_link = "" expect(CGI.unescape_html(response.body)).to include(expected_link) end @@ -2025,10 +2025,10 @@ RSpec.describe UsersController, type: :request do sign_in user end - it "routes user to the /logs page" do + it "routes user to the home page" do get "/", headers:, params: {} - follow_redirect! - expect(path).to include("/lettings-logs") + expect(path).to eq("/") + expect(page).to have_content("Welcome back") expected_link = "" expect(CGI.unescape_html(response.body)).to include(expected_link) end diff --git a/spec/views/layouts/application_layout_spec.rb b/spec/views/layouts/application_layout_spec.rb index ac4a10a98..55e97bc20 100644 --- a/spec/views/layouts/application_layout_spec.rb +++ b/spec/views/layouts/application_layout_spec.rb @@ -54,4 +54,35 @@ RSpec.describe "layouts/application" do include_examples "analytics cookie elements", banner: false, scripts: false end + + context "with a notification present" do + context "when notification is shown on unauthenticated pages" do + before do + create(:notification, title: "Old notification title", show_on_unauthenticated_pages: true) + create(:notification, title: "New notification title", show_on_unauthenticated_pages: true) + render + end + + it "shows the most recent notification without dismiss link or count" do + expect(rendered).to have_content("New notification title") + expect(rendered).to have_link("Link text") + expect(rendered).not_to have_link("Dismiss") + expect(rendered).not_to have_content("Notification 1 of") + end + end + + context "when notification is not shown on unauthenticated pages" do + before do + create(:notification) + render + end + + it "does not show the notification banner" do + expect(rendered).not_to have_content("Notification title") + expect(rendered).not_to have_link("Link text") + expect(rendered).not_to have_link("Dismiss") + expect(rendered).not_to have_content("Notification 1 of") + end + end + end end