Browse Source

Cldc 1669 add location redesign (#1034)

* feat: wip postcode page

* feat: wip add new flow

* feat: add check answers page, continue updating flow

* feat: add back behaviour

* feat: more flow work

* feat: further flow work and flash notice, name tidying

* feat: add check_answers referrer linking

* feat: add controller and details linking and lint

* refactor: erblinting

* feat: add local_authority page (currenlty separate from edit_local_authority to avoid conflict with add location to newly created scheme path, to be fixed later). also copy and routing updates

* feat: add validations to controller (could later be added to model)

* fix: correct date order

* feat: validate on model (except startdate)

* feat: copy update

* refactor: railsification

* feat: add date model validation

* feat: add updated availability text in check_answers

* feat: more updated dynamic text and linking

* refactor: erblinting

* refactor: cleanup of redundant code

* refactor: remove redundant create

* feat: use rails route

* test: wip tests

* test: wip tests

* feat: add new back behaviour for new scheme path and tests wip

* feat: add more back behaviour for new scheme path and fix local auth validation

* feat: further linking behaviour with routes and referrers, don't add new location automatically to new schemes

* feat: if location with no details added, route to normal path rather than check answers

* test: update model and helper location tests

* test: more test updates

* test: more test updates and remove redundant files

* test: remove redundant tests, add postcode test

* test: add new #new tests

* test: add test for all new controller methods

* test: add more startdate tests

* refactor: erblinting

* test: fix failing tests

* test: update scheme test

* feat: respond to PR comments

* feat: make postcode clear only if changed

* feat: add _update methods

* refactor: simplify validation and location_edit_path method

* refactor: add helper method for action text

* refactor: remove redundant logic

* refactor: simplify routing

* refactor: reintroduce location_params

* refactor: use presence validation for postcode

* refactor: use presence validation for la

* feat: validate confirmed

* tests: make tests pass with new startdate validation

* WIP

* refactor: simplify display attributes helpers and update tests

* refactor: linting

* feat: design change to save behaviour

* test: fix failing tests

* feat: location code copy tweak

* refactor: remove redundant function

* refactor: remove add_another_location and new location page

* feat: simplify startdate validations to move to model, use scheme available_from date for in range validation

* test: fix failing tests

* refactor: linting

* feat: remove name validation as a result of PO review

* test: update tests

* refactor: remove redundant context

Co-authored-by: James Rose <james@jbpr.net>
pull/1096/head
natdeanlewissoftwire 2 years ago committed by GitHub
parent
commit
02c41333f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 231
      app/controllers/locations_controller.rb
  2. 2
      app/controllers/schemes_controller.rb
  3. 12
      app/helpers/check_answers_helper.rb
  4. 38
      app/helpers/locations_helper.rb
  5. 13
      app/helpers/tab_nav_helper.rb
  6. 40
      app/models/location.rb
  7. 33
      app/views/locations/availability.erb
  8. 34
      app/views/locations/check_answers.html.erb
  9. 56
      app/views/locations/edit.html.erb
  10. 31
      app/views/locations/edit_local_authority.html.erb
  11. 26
      app/views/locations/edit_name.html.erb
  12. 18
      app/views/locations/index.html.erb
  13. 40
      app/views/locations/local_authority.html.erb
  14. 35
      app/views/locations/mobility_standards.html.erb
  15. 42
      app/views/locations/name.html.erb
  16. 61
      app/views/locations/new.html.erb
  17. 37
      app/views/locations/postcode.html.erb
  18. 4
      app/views/locations/show.html.erb
  19. 34
      app/views/locations/type_of_unit.html.erb
  20. 34
      app/views/locations/units.html.erb
  21. 47
      app/views/schemes/check_answers.html.erb
  22. 23
      config/locales/en.yml
  23. 19
      config/routes.rb
  24. 2
      spec/factories/location.rb
  25. 39
      spec/features/schemes_helpers.rb
  26. 159
      spec/features/schemes_spec.rb
  27. 24
      spec/helpers/locations_helper_spec.rb
  28. 56
      spec/models/location_spec.rb
  29. 6
      spec/models/validations/date_validations_spec.rb
  30. 8
      spec/models/validations/setup_validations_spec.rb
  31. 1424
      spec/requests/locations_controller_spec.rb
  32. 10
      spec/requests/schemes_controller_spec.rb

231
app/controllers/locations_controller.rb

@ -2,9 +2,10 @@ class LocationsController < ApplicationController
include Pagy::Backend
before_action :authenticate_user!
before_action :authenticate_scope!
before_action :find_location, except: %i[new create index]
before_action :find_location, except: %i[create index]
before_action :find_scheme
before_action :authenticate_action!
before_action :scheme_and_location_present, except: %i[create index]
include Modules::SearchFilter
@ -14,8 +15,127 @@ class LocationsController < ApplicationController
@searched = search_term.presence
end
def new
@location = Location.new
def create
@location = @scheme.locations.create!
redirect_to scheme_location_postcode_path(@scheme, @location, route: params[:route])
end
def postcode; end
def update_postcode
@location.postcode = location_params[:postcode]
if @location.save(context: :postcode)
if @location.location_code.blank? || @location.location_admin_district.blank?
redirect_to scheme_location_local_authority_path(@scheme, @location, route: params[:route], referrer: params[:referrer])
elsif return_to_check_your_answers?
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
redirect_to scheme_location_name_path(@scheme, @location, route: params[:route])
end
else
render :postcode, status: :unprocessable_entity
end
end
def local_authority; end
def update_local_authority
@location.location_admin_district = location_params[:location_admin_district]
@location.location_code = Location.local_authorities.key(location_params[:location_admin_district])
if @location.save(context: :location_admin_district)
if return_to_check_your_answers? || params[:referrer] == "check_local_authority"
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
redirect_to scheme_location_name_path(@scheme, @location, route: params[:route])
end
else
render :local_authority, status: :unprocessable_entity
end
end
def name; end
def update_name
@location.name = location_params[:name]
if @location.save(context: :name)
if return_to_check_your_answers?
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
elsif params[:referrer] == "details"
redirect_to scheme_location_path(@scheme, @location)
else
redirect_to scheme_location_units_path(@scheme, @location, route: params[:route])
end
else
render :name, status: :unprocessable_entity
end
end
def units; end
def update_units
@location.units = location_params[:units]
if @location.save(context: :units)
if return_to_check_your_answers?
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
redirect_to scheme_location_type_of_unit_path(@scheme, @location, route: params[:route])
end
else
render :units, status: :unprocessable_entity
end
end
def type_of_unit; end
def update_type_of_unit
@location.type_of_unit = location_params[:type_of_unit]
if @location.save(context: :type_of_unit)
if return_to_check_your_answers?
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
redirect_to scheme_location_mobility_standards_path(@scheme, @location, route: params[:route])
end
else
render :type_of_unit, status: :unprocessable_entity
end
end
def mobility_standards; end
def update_mobility_standards
@location.mobility_type = location_params[:mobility_type]
if @location.save(context: :mobility_type)
if return_to_check_your_answers?
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
redirect_to scheme_location_availability_path(@scheme, @location, route: params[:route])
end
else
render :mobility_standards, status: :unprocessable_entity
end
end
def availability; end
def update_availability
day = location_params["startdate(3i)"]
month = location_params["startdate(2i)"]
year = location_params["startdate(1i)"]
@location.startdate = if [day, month, year].none?(&:blank?) && Date.valid_date?(year.to_i, month.to_i, day.to_i)
Time.zone.local(year.to_i, month.to_i, day.to_i)
end
if @location.save(context: :startdate)
redirect_to scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
render :availability, status: :unprocessable_entity
end
end
def check_answers; end
def confirm
flash[:notice] = "#{@location.postcode} #{@location.startdate.blank? || @location.startdate < Time.zone.now ? 'has been' : 'will be'} added to this scheme"
redirect_to scheme_locations_path(@scheme)
end
def show; end
@ -82,99 +202,14 @@ class LocationsController < ApplicationController
end
end
def create
if date_params_missing?(location_params) || valid_date_params?(location_params)
@location = Location.new(location_params)
if @location.save
if @location.location_admin_district.nil?
redirect_to(scheme_location_edit_local_authority_path(scheme_id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
elsif location_params[:add_another_location] == "Yes"
redirect_to new_scheme_location_path(@scheme)
else
check_answers_path = @scheme.confirmed? ? scheme_check_answers_path(@scheme, anchor: "locations") : scheme_check_answers_path(@scheme)
redirect_to check_answers_path
end
else
render :new, status: :unprocessable_entity
end
else
@location = Location.new(location_params.except("startdate(3i)", "startdate(2i)", "startdate(1i)"))
@location.valid?
@location.errors.add(:startdate)
render :new, status: :unprocessable_entity
end
end
def edit
render_not_found and return unless @location && @scheme
end
def edit_name
render_not_found and return unless @location && @scheme
end
def edit_local_authority
render_not_found and return unless @location && @scheme
end
def update
render_not_found and return unless @location && @scheme
page = params[:location][:page]
if page == "edit-local-authority" && !valid_location_admin_district?(location_params)
error_message = I18n.t("validations.location_admin_district")
@location.errors.add :location_admin_district, error_message
render :edit_local_authority, status: :unprocessable_entity
else
if page == "edit-local-authority"
params[:location][:location_code] = Location.local_authorities.key(params[:location][:location_admin_district])
end
if @location.update(location_params)
case page
when "edit"
if @location.location_admin_district.nil?
redirect_to(scheme_location_edit_local_authority_path(scheme_id: @scheme.id, location_id: @location.id, add_another_location: location_params[:add_another_location]))
elsif location_params[:add_another_location] == "Yes"
redirect_to(new_scheme_location_path(@location.scheme))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
when "edit-name"
if @scheme.locations.count == @scheme.locations.active.count
redirect_to(scheme_location_path(@scheme, @location))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
when "edit-local-authority"
if params[:add_another_location] == "Yes"
redirect_to(new_scheme_location_path(@location.scheme))
else
redirect_to(scheme_check_answers_path(@scheme, anchor: "locations"))
end
end
else
render :edit, status: :unprocessable_entity
end
end
end
private
def valid_date_params?(location_params)
is_integer?(location_params["startdate(1i)"]) && is_integer?(location_params["startdate(2i)"]) && is_integer?(location_params["startdate(3i)"]) &&
Date.valid_date?(location_params["startdate(1i)"].to_i, location_params["startdate(2i)"].to_i, location_params["startdate(3i)"].to_i)
end
def date_params_missing?(location_params)
location_params["startdate(1i)"].blank? || location_params["startdate(2i)"].blank? || location_params["startdate(3i)"].blank?
end
def is_integer?(string)
string.sub(/^0+/, "").to_i.to_s == string.sub(/^0+/, "")
def scheme_and_location_present
render_not_found and return unless @location && @scheme
end
def find_scheme
@scheme = if %w[new create index edit_name].include?(action_name)
@scheme = if %w[create index].include?(action_name)
Scheme.find(params[:scheme_id])
else
@location&.scheme
@ -190,14 +225,15 @@ private
end
def authenticate_action!
if %w[new edit update create index edit_name edit_local_authority new_deactivation deactivate_confirm deactivate].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
if %w[create update index new_deactivation deactivate_confirm deactivate postcode local_authority name units type_of_unit mobility_standards availability check_answers].include?(action_name) && !((current_user.organisation == @scheme&.owning_organisation) || current_user.support?)
render_not_found and return
end
end
def location_params
required_params = params.require(:location).permit(:postcode, :name, :units, :type_of_unit, :add_another_location, :startdate, :mobility_type, :location_admin_district, :location_code).merge(scheme_id: @scheme.id)
required_params = params.require(:location).permit(:postcode, :location_admin_district, :location_code, :name, :units, :type_of_unit, :mobility_type, "startdate(1i)", "startdate(2i)", "startdate(3i)").merge(scheme_id: @scheme.id)
required_params[:postcode] = PostcodeService.clean(required_params[:postcode]) if required_params[:postcode]
required_params[:location_admin_district] = nil if required_params[:location_admin_district] == "Select an option"
required_params
end
@ -205,10 +241,6 @@ private
params["search"]
end
def valid_location_admin_district?(location_params)
location_params["location_admin_district"] != "Select an option"
end
def deactivate_success_notice
case @location.status
when :deactivated
@ -249,4 +281,9 @@ private
Time.zone.local(year.to_i, month.to_i, day.to_i) if Date.valid_date?(year.to_i, month.to_i, day.to_i)
end
def return_to_check_your_answers?
params[:referrer] == "check_answers"
end
helper_method :return_to_check_your_answers?
end

2
app/controllers/schemes_controller.rb

@ -237,7 +237,7 @@ private
when "secondary-client-group"
scheme_support_path(@scheme)
when "support"
new_scheme_location_path(@scheme)
scheme_check_answers_path(@scheme)
when "details"
if @scheme.arrangement_type_before_type_cast == "D"
scheme_primary_client_group_path(@scheme)

12
app/helpers/check_answers_helper.rb

@ -16,18 +16,6 @@ module CheckAnswersHelper
!scheme.confirmed? || editable_attributes.include?(attribute_name)
end
def get_location_change_link_href_postcode(scheme, location)
if location.confirmed?
scheme_location_path(scheme_id: scheme.id, id: location.id)
else
edit_scheme_location_path(scheme_id: scheme.id, id: location.id)
end
end
def get_location_change_link_href_location_admin_district(scheme, location)
scheme_location_edit_local_authority_path(scheme_id: scheme.id, location_id: location.id)
end
def any_questions_have_summary_card_number?(subsection, lettings_log)
subsection.applicable_questions(lettings_log).map(&:check_answers_card_number).compact.length.positive?
end

38
app/helpers/locations_helper.rb

@ -25,23 +25,35 @@ module LocationsHelper
def display_location_attributes(location)
base_attributes = [
{ name: "Postcode", value: location.postcode },
{ name: "Location name", value: location.name, edit: true },
{ name: "Local authority", value: location.location_admin_district },
{ name: "Total number of units at this location", value: location.units },
{ name: "Common type of unit", value: location.type_of_unit },
{ name: "Mobility type", value: location.mobility_type },
{ name: "Location code", value: location.location_code },
{ name: "Availability", value: location_availability(location) },
{ name: "Postcode", value: location.postcode, attribute: "postcode" },
{ name: "Location name", value: location.name, attribute: "name" },
{ name: "Local authority", value: location.location_admin_district, attribute: "local_authority" },
{ name: "Number of units", value: location.units, attribute: "units" },
{ name: "Most common unit", value: location.type_of_unit, attribute: "type_of_unit" },
{ name: "Mobility standards", value: location.mobility_type, attribute: "mobility_standards" },
{ name: "Location code", value: location.location_code, attribute: "location_code" },
{ name: "Availability", value: location_availability(location), attribute: "availability" },
]
if FeatureToggle.location_toggle_enabled?
base_attributes.append({ name: "Status", value: location.status })
base_attributes.append({ name: "Status", value: location.status, attribute: "status" })
end
base_attributes
end
def display_location_attributes_for_check_answers(location)
[
{ name: "Postcode", value: location.postcode, attribute: "postcode" },
{ name: "Location name", value: location.name, attribute: "name" },
{ name: "Local authority", value: location.location_admin_district, attribute: "local_authority" },
{ name: "Number of units", value: location.units, attribute: "units" },
{ name: "Most common unit", value: location.type_of_unit, attribute: "type_of_unit" },
{ name: "Mobility standards", value: location.mobility_type, attribute: "mobility_standards" },
{ name: "Availability", value: location&.startdate&.to_formatted_s(:govuk_date), attribute: "availability" },
]
end
def location_availability(location)
availability = ""
location_active_periods(location).each do |period|
@ -53,6 +65,14 @@ module LocationsHelper
availability.strip
end
def location_edit_path(location, attribute)
send("scheme_location_#{attribute}_path", location.scheme, location, referrer: "check_answers", route: params[:route])
end
def action_text_helper(attr, location)
attr[:value].blank? || (attr[:attribute] == "availability" && location.startdate.blank?) ? "Answer" : "Change"
end
def toggle_location_link(location)
return govuk_button_link_to "Deactivate this location", scheme_location_new_deactivation_path(location.scheme, location), warning: true if location.active?
return govuk_button_link_to "Reactivate this location", scheme_location_new_reactivation_path(location.scheme, location) if location.deactivated?

13
app/helpers/tab_nav_helper.rb

@ -7,21 +7,10 @@ module TabNavHelper
end
def location_cell_postcode(location, link)
link_text = location.postcode
link_text = location.postcode || "Add postcode"
[govuk_link_to(link_text, link, method: :patch), "<span class=\"govuk-visually-hidden\">Location </span><span class=\"govuk-!-font-weight-regular app-!-colour-muted\">#{location.name}</span>"].join("\n")
end
def location_cell_location_admin_district(location, link)
la = location.location_admin_district
if location.confirmed?
la
elsif la
govuk_link_to(la, link, method: :patch)
else
govuk_link_to("Select local authority", link, method: :patch)
end
end
def scheme_cell(scheme)
link_text = scheme.service_name
link = scheme.confirmed? ? scheme : scheme_check_answers_path(scheme)

40
app/models/location.rb

@ -1,6 +1,13 @@
class Location < ApplicationRecord
validate :validate_postcode
validates :units, :type_of_unit, :mobility_type, presence: true
validates :postcode, on: :postcode, presence: { message: I18n.t("validations.location.postcode_blank") }
validate :validate_postcode, on: :postcode, if: proc { |model| model.postcode.presence }
validates :location_admin_district, on: :location_admin_district, presence: { message: I18n.t("validations.location_admin_district") }
validates :units, on: :units, presence: { message: I18n.t("validations.location.units") }
validates :type_of_unit, on: :type_of_unit, presence: { message: I18n.t("validations.location.type_of_unit") }
validates :mobility_type, on: :mobility_type, presence: { message: I18n.t("validations.location.mobility_standards") }
validates :startdate, on: :startdate, presence: { message: I18n.t("validations.location.startdate_invalid") }
validate :validate_startdate, on: :startdate, if: proc { |model| model.startdate.presence }
validate :validate_confirmed
belongs_to :scheme
has_many :lettings_logs, class_name: "LettingsLog"
has_many :location_deactivation_periods, class_name: "LocationDeactivationPeriod"
@ -11,8 +18,6 @@ class Location < ApplicationRecord
auto_strip_attributes :name
attr_accessor :add_another_location
scope :search_by_postcode, ->(postcode) { where("REPLACE(postcode, ' ', '') ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param).or(search_by_postcode(param)) }
@ -409,17 +414,34 @@ class Location < ApplicationRecord
status == :reactivating_soon
end
private
PIO = PostcodeService.new
def validate_postcode
if postcode.nil? || !postcode&.match(POSTCODE_REGEXP)
if !postcode&.match(POSTCODE_REGEXP)
error_message = I18n.t("validations.postcode")
errors.add :postcode, error_message
else
self.postcode = PostcodeService.clean(postcode)
if postcode_changed?
self.location_admin_district = nil
self.location_code = nil
end
end
end
def validate_startdate
unless startdate.between?(scheme.available_from, Time.zone.local(2200, 1, 1))
error_message = I18n.t("validations.location.startdate_out_of_range", date: scheme.available_from.to_formatted_s(:govuk_date))
errors.add :startdate, error_message
end
end
def validate_confirmed
self.confirmed = [postcode, location_admin_district, location_code, units, type_of_unit, mobility_type].all?(&:present?)
end
private
PIO = PostcodeService.new
def lookup_postcode!
result = PIO.lookup(postcode)
if result

33
app/views/locations/availability.erb

@ -0,0 +1,33 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: params[:referrer] == "check_answers" ? scheme_location_check_answers_path(@scheme, @location, route: params[:route]) : scheme_location_mobility_standards_path(@scheme, @location, route: params[:route]),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_availability_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.startdate"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_date_field :startdate,
hint: { text: I18n.t("hints.location.startdate") },
legend: nil,
width: 20 %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_check_answers_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

34
app/views/locations/check_answers.html.erb

@ -0,0 +1,34 @@
<% title = @location.name %>
<% content_for :title, title %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: case params[:route]
when "locations"
scheme_locations_path(@scheme)
else
:back
end,
) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "Check your answers", sub: "Add a location to #{@scheme.service_name}" } %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<%= govuk_summary_list do |summary_list| %>
<% display_location_attributes_for_check_answers(@location).each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name] } %>
<% row.value { details_html(attr) } %>
<% row.action(text: action_text_helper(attr, @location), href: location_edit_path(@location, attr[:attribute])) %>
<% end %>
<% end %>
<% end %>
</div>
</div>
<div class="govuk-button-group">
<%= govuk_button_to "Save and return to locations", scheme_location_confirm_path(@scheme, @location, route: params[:route]), method: :patch %>
<%= govuk_button_link_to "Cancel", scheme_locations_path(@scheme), secondary: true %>
</div>

56
app/views/locations/edit.html.erb

@ -1,56 +0,0 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_check_answers_path(@scheme, anchor: "locations"),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id)) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: "Add a location to this scheme", sub: @scheme.service_name } %>
<%= f.govuk_text_field :postcode,
label: { size: "m" },
hint: { text: I18n.t("hints.location.postcode") },
width: 5 %>
<%= f.govuk_text_field :name,
label: { text: I18n.t("questions.location.name"), size: "m" },
hint: { text: I18n.t("hints.location.name") } %>
<%= f.govuk_number_field :units,
label: { text: I18n.t("questions.location.units"), size: "m" },
width: 2,
hint: { text: I18n.t("hints.location.units") },
autofocus: true %>
<%= f.govuk_collection_radio_buttons :type_of_unit,
type_of_units_selection,
:id,
:name,
legend: { text: I18n.t("questions.location.type_of_unit"), size: "m" } %>
<%= f.govuk_collection_radio_buttons :mobility_type,
mobility_type_selection,
:id,
:name,
:description,
legend: { text: I18n.t("questions.location.mobility_type"), size: "m" } %>
<%= f.govuk_date_field :startdate,
legend: { text: I18n.t("questions.location.startdate"), size: "m" },
width: 20 %>
<%= govuk_section_break(visible: true, size: "m") %>
<%= f.hidden_field :page, value: "edit" %>
<%= f.govuk_submit "Save and continue" %>
</div>
</div>
<% end %>

31
app/views/locations/edit_local_authority.html.erb

@ -1,31 +0,0 @@
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: "/schemes/#{@scheme.id}/locations",
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id, add_another_location: params[:add_another_location])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: "What is the local authority of #{@location.postcode}?", sub: @scheme.service_name } %>
<%= f.govuk_collection_select :location_admin_district,
local_authorities_selection,
:name,
:name,
label: { hidden: true },
"data-controller": %w[conditional-filter accessible-autocomplete] %>
<%= f.hidden_field :page, value: "edit-local-authority" %>
<div class="govuk-button-group">
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", "/schemes/#{@scheme.id}/check-answers#locations" %>
</div>
</div>
</div>
<% end %>

26
app/views/locations/edit_name.html.erb

@ -1,26 +0,0 @@
<% content_for :title, "Location name for #{@location.postcode}" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: scheme_location_path(@scheme, @location),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_path(scheme_id: @scheme.id, id: @location.id)) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: "Location name for #{@location.postcode}", sub: @scheme.service_name } %>
<%= f.govuk_text_field :name,
label: { hidden: true },
hint: { text: "This is how you refer to this location within your organisation" } %>
<%= f.hidden_field :page, value: "edit-name" %>
<%= f.govuk_submit "Save and continue" %>
</div>
</div>
<% end %>

18
app/views/locations/index.html.erb

@ -50,14 +50,18 @@
<% @locations.each do |location| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: simple_format(location_cell_postcode(location, scheme_location_path(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
scheme_location_path(@scheme, location)
else
location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.id) %>
<% row.cell(text: status_tag(location.status)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_scheme_location_path(@scheme), secondary: true %>
<%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %>
</div>
</div>
@ -95,19 +99,23 @@
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell_postcode(location, scheme_location_path(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: simple_format(location_cell_postcode(location, if location.confirmed
scheme_location_path(@scheme, location)
else
location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)
end), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell do %>
<span><%= simple_format(location.type_of_unit) %></span>
<% end %>
<% row.cell(text: location.mobility_type) %>
<% row.cell(text: simple_format(location_cell_location_admin_district(location, scheme_location_edit_local_authority_path(@scheme, location)), wrapper_tag: "div")) %>
<% row.cell(text: location.location_admin_district) %>
<% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_scheme_location_path(@scheme), secondary: true %>
<%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post", secondary: true %>
<% end %>

40
app/views/locations/local_authority.html.erb

@ -0,0 +1,40 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: case params[:referrer]
when "check_local_authority"
scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
scheme_location_postcode_path(@scheme, @location, route: params[:route], referrer: params[:referrer])
end,
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_local_authority_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: "What is the local authority of #{@location.postcode}?", sub: @scheme.service_name } %>
<%= f.govuk_collection_select :location_admin_district,
local_authorities_selection,
:name,
:name,
label: { text: "Start typing to search for your local authority" },
"data-controller": %w[conditional-filter accessible-autocomplete] %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" || params[:referrer] == "check_local_authority" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_name_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

35
app/views/locations/mobility_standards.html.erb

@ -0,0 +1,35 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: params[:referrer] == "check_answers" ? scheme_location_check_answers_path(@scheme, @location, route: params[:route]) : scheme_location_type_of_unit_path(@scheme, @location, route: params[:route]),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_mobility_standards_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.mobility_type"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_collection_radio_buttons :mobility_type,
mobility_type_selection,
:id,
:name,
:description,
legend: nil %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_availability_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

42
app/views/locations/name.html.erb

@ -0,0 +1,42 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: case params[:referrer]
when "check_answers"
scheme_location_check_answers_path(@scheme, @location, route: params[:route])
when "details"
scheme_location_path(@scheme, @location)
else
scheme_location_postcode_path(@scheme, @location, route: params[:route])
end,
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_name_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.name"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_text_field :name,
label: nil,
hint: { text: I18n.t("hints.location.name") } %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% elsif params[:referrer] == "details" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_path(@scheme, @location), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_units_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

61
app/views/locations/new.html.erb

@ -1,61 +0,0 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: "/schemes/#{@scheme.id}/support",
) %>
<% end %>
<%= form_for(@location, method: :post, url: scheme_locations_path) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: "Add a location to this scheme", sub: @scheme.service_name } %>
<%= f.govuk_text_field :postcode,
label: { size: "m" },
hint: { text: I18n.t("hints.location.postcode") },
width: 5 %>
<%= f.govuk_text_field :name,
label: { text: I18n.t("questions.location.name"), size: "m" },
hint: { text: I18n.t("hints.location.name") } %>
<%= f.govuk_number_field :units,
label: { text: I18n.t("questions.location.units"), size: "m" },
width: 2,
hint: { text: I18n.t("hints.location.units") },
autofocus: true %>
<%= f.govuk_collection_radio_buttons :type_of_unit,
type_of_units_selection,
:id,
:name,
legend: { text: I18n.t("questions.location.type_of_unit"), size: "m" } %>
<%= f.govuk_collection_radio_buttons :mobility_type,
mobility_type_selection,
:id,
:name,
:description,
legend: { text: I18n.t("questions.location.mobility_type"), size: "m" } %>
<%= f.govuk_date_field :startdate,
legend: { text: I18n.t("questions.location.startdate"), size: "m" },
width: 20 %>
<%= govuk_section_break(visible: true, size: "m") %>
<%= f.govuk_collection_radio_buttons :add_another_location,
another_location_selection,
:id,
:name,
inline: true,
legend: { text: I18n.t("questions.location.add_another_location"), size: "m" } %>
<%= f.govuk_submit "Save and continue" %>
</div>
</div>
<% end %>

37
app/views/locations/postcode.html.erb

@ -0,0 +1,37 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: if params[:referrer] == "check_answers"
scheme_location_check_answers_path(@scheme, @location, route: params[:route])
else
scheme_locations_path(@scheme)
end,
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_postcode_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.postcode"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_text_field :postcode,
label: nil,
hint: { text: I18n.t("hints.location.postcode") },
width: 5 %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_name_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

4
app/views/locations/show.html.erb

@ -16,8 +16,8 @@
<% display_location_attributes(@location).each do |attr| %>
<%= summary_list.row do |row| %>
<% row.key { attr[:name] } %>
<% row.value { attr[:name].eql?("Status") ? status_tag(attr[:value]) : details_html(attr) } %>
<% row.action(text: "Change", href: scheme_location_edit_name_path(scheme_id: @scheme.id, location_id: @location.id)) if attr[:edit] %>
<% row.value { attr[:attribute].eql?("status") ? status_tag(attr[:value]) : details_html(attr) } %>
<% row.action(text: "Change", href: scheme_location_name_path(@scheme, @location, referrer: "details")) if attr[:attribute] == "name" %>
<% end %>
<% end %>
<% end %>

34
app/views/locations/type_of_unit.html.erb

@ -0,0 +1,34 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: params[:referrer] == "check_answers" ? scheme_location_check_answers_path(@scheme, @location, route: params[:route]) : scheme_location_units_path(@scheme, @location, route: params[:route]),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_type_of_unit_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.type_of_unit"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_collection_radio_buttons :type_of_unit,
type_of_units_selection,
:id,
:name,
legend: nil %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_mobility_standards_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

34
app/views/locations/units.html.erb

@ -0,0 +1,34 @@
<% content_for :title, "Add a location to this scheme" %>
<% content_for :before_content do %>
<%= govuk_back_link(
text: "Back",
href: params[:referrer] == "check_answers" ? scheme_location_check_answers_path(@scheme, @location, route: params[:route]) : scheme_location_name_path(@scheme, @location, route: params[:route]),
) %>
<% end %>
<%= form_for(@location, method: :patch, url: scheme_location_units_path(@scheme, @location, route: params[:route], referrer: params[:referrer])) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= render partial: "organisations/headings", locals: { main: I18n.t("questions.location.units"), sub: "Add a location to #{@scheme.service_name}" } %>
<%= f.govuk_number_field :units,
label: nil,
width: 2,
hint: { text: I18n.t("hints.location.units") },
autofocus: true %>
<div class="govuk-button-group">
<% if params[:referrer] == "check_answers" %>
<%= f.govuk_submit "Save changes" %>
<%= govuk_link_to "Cancel", scheme_location_check_answers_path(@scheme, @location, route: params[:route]), secondary: true %>
<% else %>
<%= f.govuk_submit "Save and continue" %>
<%= govuk_link_to "Skip for now", scheme_location_type_of_unit_path(@scheme, @location), secondary: true %>
<% end %>
</div>
</div>
</div>
<% end %>

47
app/views/schemes/check_answers.html.erb

@ -33,53 +33,6 @@
<% end %>
</dl>
<% end %>
<% component.tab(label: "Locations") do %>
<h2 class="govuk-visually-hidden">Locations</h2>
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Code", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Postcode", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Units", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Common unit type", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Mobility type", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Local authority", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Available from", html_attributes: {
scope: "col",
}) %>
<% end %>
<% end %>
<% @scheme.locations.each do |location| %>
<%= table.body do |body| %>
<%= body.row do |row| %>
<% row.cell(text: location.id) %>
<% row.cell(text: simple_format(location_cell_postcode(location, get_location_change_link_href_postcode(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %>
<% row.cell(text: location.units) %>
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %>
<% row.cell(text: location.mobility_type) %>
<% row.cell(text: simple_format(location_cell_location_admin_district(location, get_location_change_link_href_location_admin_district(@scheme, location)), wrapper_tag: "div")) %>
<% row.cell(text: location_availability(location)) %>
<% end %>
<% end %>
<% end %>
<% end %>
<%= govuk_button_link_to "Add a location", new_scheme_location_path(scheme_id: @scheme.id), secondary: true %>
<% end %>
<% end %>
<%= f.hidden_field :page, value: "check-answers" %>
<%= f.hidden_field :confirmed, value: "true" %>

23
config/locales/en.yml

@ -372,6 +372,12 @@ en:
location:
postcode_blank: "Enter a postcode"
units: "The units at this location must be a number"
type_of_unit: "Select the most common type of unit at this location"
mobility_standards: "Select the mobility standard for the majority of the units at this location"
startdate_invalid: "Enter a valid day, month and year when the first property became available at this location"
startdate_out_of_range: "Availability date must be on or after the %{date}"
toggle_date:
not_selected: "Select one of the options"
invalid: "Enter a valid day, month and year"
@ -425,11 +431,11 @@ en:
questions:
location:
name: "Location name (optional)"
units: "Total number of units at this location"
postcode: "What is the postcode?"
name: "What is the name of this location?"
units: "How many units are at this location?"
type_of_unit: "What is the most common type of unit at this location?"
startdate: "When did the first property in this location become available under this scheme? (optional)"
add_another_location: "Do you want to add another location?"
startdate: "When did the first property in this location become available under this scheme?"
mobility_type: "What are the mobility standards for the majority of units in this location?"
toggle_active:
apply_from: "When should this change apply?"
@ -439,16 +445,17 @@ en:
descriptions:
location:
mobility_type:
W: "The majority of units are suitable for someone who uses a wheelchair and offer the full use of all rooms and facilities."
A: "For example, the majority of units have been fitted with stairlifts, ramps, level access showers or grab rails."
N: "The majority of units are not designed to wheelchair-user standards or fitted with any equipment and adaptations."
W: "Suitable for someone who uses a wheelchair and offers the full use of all rooms and facilities."
A: "Fitted with stairlifts, ramps, level access showers or grab rails."
N: "Not designed to wheelchair-user standards or fitted with any equipment or adaptations."
hints:
location:
postcode: "For example, SW1P 4DF."
name: "This is how you refer to this location within your organisation"
units: "A unit can be a bedroom in a shared house or flat, or a house with 4 bedrooms. Do not include bedrooms used for wardens, managers, volunteers or sleep-in staff."
units: "A unit is the space being let. For example, the property might be a block of flats and the unit would be the specific flat being let. A unit can also be a bedroom in a shared house or flat. Do not include spaces used for staff."
toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed."
startdate: "For example, 27 3 2021"
scheme:
toggle_active: "If the date is before %{date}, select ‘From the start of the current collection period’ because the previous period has now closed."

19
config/routes.rb

@ -58,12 +58,27 @@ Rails.application.routes.draw do
patch "reactivate", to: "schemes#reactivate"
resources :locations do
get "edit-name", to: "locations#edit_name"
get "edit-local-authority", to: "locations#edit_local_authority"
post "locations", to: "locations#create"
get "new-deactivation", to: "locations#new_deactivation"
get "deactivate-confirm", to: "locations#deactivate_confirm"
get "reactivate", to: "locations#reactivate"
get "new-reactivation", to: "locations#new_reactivation"
get "postcode", to: "locations#postcode"
patch "postcode", to: "locations#update_postcode"
get "local-authority", to: "locations#local_authority"
patch "local-authority", to: "locations#update_local_authority"
get "name", to: "locations#name"
patch "name", to: "locations#update_name"
get "units", to: "locations#units"
patch "units", to: "locations#update_units"
get "type-of-unit", to: "locations#type_of_unit"
patch "type-of-unit", to: "locations#update_type_of_unit"
get "mobility-standards", to: "locations#mobility_standards"
patch "mobility-standards", to: "locations#update_mobility_standards"
get "availability", to: "locations#availability"
patch "availability", to: "locations#update_availability"
get "check-answers", to: "locations#check_answers"
patch "confirm", to: "locations#confirm"
patch "new-deactivation", to: "locations#new_deactivation"
patch "deactivate", to: "locations#deactivate"
patch "reactivate", to: "locations#reactivate"

2
spec/factories/location.rb

@ -7,7 +7,7 @@ FactoryBot.define do
mobility_type { %w[A M N W X].sample }
location_code { "E09000033" }
location_admin_district { "Westminster" }
startdate { nil }
startdate { Time.zone.local(2022, 4, 1) }
confirmed { true }
scheme
trait :export do

39
spec/features/schemes_helpers.rb

@ -55,26 +55,43 @@ module SchemesHelpers
end
def fill_in_and_save_location
fill_in "Postcode", with: "AA11AA"
fill_in "Location name (optional)", with: "Some name"
fill_in "Total number of units at this location", with: 5
fill_in "Day", with: 2
fill_in "Month", with: 2
fill_in "Year", with: 2022
click_link "Locations"
click_button "Add a location"
fill_in with: "AA11AA"
click_button "Save and continue"
fill_in with: "Adur"
fill_in with: "Some name"
click_button "Save and continue"
fill_in with: 5
click_button "Save and continue"
choose "Self-contained house"
choose "location-add-another-location-no-field"
click_button "Save and continue"
choose "location-mobility-type-none-field"
click_button "Save and continue"
fill_in "Day", with: 2
fill_in "Month", with: 5
fill_in "Year", with: 2022
click_button "Save and continue"
end
def fill_in_and_save_second_location
fill_in "Postcode", with: "AA12AA"
fill_in "Location name (optional)", with: "Other name"
fill_in "Total number of units at this location", with: 2
click_link "Locations"
click_button "Add a location"
fill_in with: "AA12AA"
click_button "Save and continue"
fill_in with: "Adur"
fill_in with: "Other name"
click_button "Save and continue"
fill_in with: 2
click_button "Save and continue"
choose "Self-contained house"
choose "location-add-another-location-no-field"
click_button "Save and continue"
choose "location-mobility-type-none-field"
click_button "Save and continue"
fill_in "Day", with: 2
fill_in "Month", with: 5
fill_in "Year", with: 2022
click_button "Save and continue"
end
def create_and_save_a_scheme

159
spec/features/schemes_spec.rb

@ -270,59 +270,66 @@ RSpec.describe "Schemes scheme Features" do
context "when the user clicks add location" do
before do
click_link("Locations")
click_link("Add a location")
click_button("Add a location")
end
it "shows the new location form" do
expect(page).to have_content("Add a location to this scheme")
expect(page).to have_content("Add a location to #{scheme.service_name}")
end
context "when the user completes the new location form" do
let(:location_name) { "Area 42" }
before do
fill_in "Postcode", with: "NW1L 5DP"
fill_in "Location name (optional)", with: location_name
fill_in "Total number of units at this location", with: 1
choose "Bungalow"
fill_in with: "AA12AA"
click_button "Save and continue"
fill_in with: "Adur"
fill_in with: location_name
click_button "Save and continue"
fill_in with: 1
click_button "Save and continue"
choose "Self-contained house"
click_button "Save and continue"
choose "location-mobility-type-none-field"
choose "location-add-another-location-no-field"
click_button "Save and continue"
fill_in "Day", with: 2
fill_in "Month", with: 2
fill_in "Year", with: 2022
click_button "Save and continue"
end
it "shows the check answers page location tab" do
expect(page.current_url.split("/").last).to eq("check-answers#locations")
it "shows the location check answers page" do
expect(page.current_url.split("/").last).to eq("check-answers")
expect(page).to have_content(location_name)
end
it "has the correct action button text" do
expect(page).to have_button("Save")
expect(page).not_to have_button("Create scheme")
expect(page).to have_button("Save and return to locations")
end
it "allows you to edit the newly added location" do
click_link "Locations"
expect(page).to have_link(nil, href: /edit/)
expect(page).to have_link(href: /postcode/)
end
context "when you click save" do
before do
click_button "Save and return to locations"
end
it "displays a updated banner" do
click_button "Save"
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
expect(page).to have_content("has been updated")
expect(page).to have_content("has been added")
end
it "does not let you edit the saved location" do
click_link "Locations"
expect(page).to have_link(nil, href: /edit(?!-name)/)
click_button "Save"
click_link "Locations"
expect(page).not_to have_link(nil, href: /edit(?!-name)/)
click_link "AA1 2AA"
expect(page).not_to have_link(nil, href: /postcode/)
end
end
context "when you click to view the scheme details" do
before do
click_button "Save and return to locations"
click_link("Scheme")
end
@ -340,6 +347,7 @@ RSpec.describe "Schemes scheme Features" do
context "when creating a new scheme" do
let(:organisation_name) { "FooBar" }
let(:scheme) { Scheme.first }
let(:location) { Location.first }
before do
FactoryBot.create(:organisation, name: organisation_name)
@ -468,68 +476,56 @@ RSpec.describe "Schemes scheme Features" do
context "when adding a location" do
before do
create_and_save_a_scheme
click_button "Create scheme"
end
it "lets me add location" do
expect(page).to have_content "Add a location to this scheme"
end
it "lets me navigate back to support questions" do
click_link "Back"
expect(page).to have_current_path("/schemes/#{scheme.id}/support")
expect(page).to have_content "What support does this scheme provide?"
end
it "returns to the add location page after amending the support question" do
click_link "Back"
click_button "Save and continue"
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/new")
click_link "Locations"
expect(page).to have_content "Add a location"
end
it "lets me check my answers after adding a location" do
fill_in_and_save_location
expect(page).to have_current_path("/schemes/#{scheme.id}/check-answers")
expect(page).to have_content "Check your changes before creating this scheme"
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}/check-answers")
expect(page).to have_content "Check your answers"
end
it "lets me check my answers after adding a second location" do
fill_in_and_save_location
click_link "Add a location"
click_button "Save and return to locations"
fill_in_and_save_second_location
expect(page).to have_content "Check your changes before creating this scheme"
expect(page).to have_content "Check your answers"
end
end
context "when viewing locations" do
before do
create_and_save_a_scheme
click_button "Create scheme"
fill_in_and_save_location
click_button "Save and return to locations"
click_link "Locations"
end
it "displays information about a single location" do
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} location"
expect(page).to have_content "#{scheme.locations.count} total location"
end
it "displays information about the first created location" do
expect(page).to have_content "AA1 1AA"
expect(page).to have_content "Some name"
expect(page).to have_content "5"
expect(page).to have_content "Self-contained house"
expect(page).to have_content "2 February 2022"
expect(page).to have_content "Active"
end
it "displays information about another location" do
click_link "Add a location"
fill_in_and_save_second_location
click_button "Save and return to locations"
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} location"
expect(page).to have_content "#{scheme.locations.count} total location"
end
it "displays information about newly created location" do
click_link "Add a location"
fill_in_and_save_second_location
expect(page).to have_content "AA1 2AA"
expect(page).to have_content "Other name"
@ -540,25 +536,24 @@ RSpec.describe "Schemes scheme Features" do
context "when changing location details" do
before do
create_and_save_a_scheme
click_button "Create scheme"
fill_in_and_save_second_location
click_link "Locations"
click_button "Save and return to locations"
end
it "displays changed location" do
click_link "AA1 2AA"
fill_in "Postcode", with: "AA1 3AA"
choose "location-mobility-type-wheelchair-user-standard-field"
click_button "Save and continue"
expect(page).to have_content "Locations"
expect(page).to have_content "#{scheme.locations.count} location"
expect(page).to have_content "AA1 3AA"
click_link "Change"
fill_in with: "new name"
click_button "Save changes"
expect(page).to have_content "AA1 2AA"
expect(page).to have_content "new name"
end
end
context "when changing scheme answers" do
before do
create_and_save_a_scheme
fill_in_and_save_location
end
it "displays change links" do
@ -594,7 +589,6 @@ RSpec.describe "Schemes scheme Features" do
context "when selecting 'create a scheme'" do
before do
create_and_save_a_scheme
fill_in_and_save_location
click_button "Create scheme"
end
@ -650,7 +644,6 @@ RSpec.describe "Schemes scheme Features" do
fill_in_and_save_secondary_client_group_confirmation
fill_in_and_save_secondary_client_group
fill_in_and_save_support
fill_in_and_save_location
end
it "displays change links" do
@ -832,7 +825,7 @@ RSpec.describe "Schemes scheme Features" do
assert_selector "a", text: "Change", count: 1
click_link("Change")
expect(page).to have_content "Location name for #{location.postcode}"
expect(page).to have_content "What is the name of this location?"
end
it "allows to deactivate a location" do
@ -858,7 +851,7 @@ RSpec.describe "Schemes scheme Features" do
it "returns to locations check your answers page and shows the new name" do
fill_in "location-name-field", with: "NewName"
click_button "Save and continue"
click_button "Save changes"
expect(page).to have_content location.postcode
expect(page).to have_content "NewName"
expect(page).to have_current_path("/schemes/#{scheme.id}/locations/#{location.id}")
@ -920,42 +913,70 @@ RSpec.describe "Schemes scheme Features" do
context "when the user clicks add location" do
before do
click_link("Locations")
click_link("Add a location")
click_button("Add a location")
end
it "shows the new location form" do
expect(page).to have_content("Add a location to this scheme")
expect(page).to have_content("Add a location to #{scheme.service_name}")
end
context "when the user completes the new location form" do
let(:location_name) { "Area 42" }
before do
fill_in "Postcode", with: "NW1L 5DP"
fill_in "Location name (optional)", with: location_name
fill_in "Total number of units at this location", with: 1
choose "Bungalow"
fill_in with: "AA12AA"
click_button "Save and continue"
fill_in with: "Adur"
fill_in with: location_name
click_button "Save and continue"
fill_in with: 1
click_button "Save and continue"
choose "Self-contained house"
click_button "Save and continue"
choose "location-mobility-type-none-field"
choose "location-add-another-location-no-field"
click_button "Save and continue"
fill_in "Day", with: 2
fill_in "Month", with: 2
fill_in "Year", with: 2022
click_button "Save and continue"
end
it "shows the check answers page location tab" do
expect(page.current_url.split("/").last).to eq("check-answers#locations")
it "shows the location check answers page" do
expect(page.current_url.split("/").last).to eq("check-answers")
expect(page).to have_content(location_name)
end
it "has the correct action button text" do
expect(page).to have_button("Save")
expect(page).not_to have_button("Create scheme")
expect(page).to have_button("Save and return to locations")
end
it "allows you to edit the newly added location" do
expect(page).to have_link(href: /postcode/)
end
context "when you click save" do
before do
click_button "Save and return to locations"
end
it "displays a updated banner" do
expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
expect(page).to have_content("has been added")
end
it "does not let you edit the saved location" do
click_link "AA1 2AA"
expect(page).not_to have_link(nil, href: /postcode/)
end
end
context "when you click to view the scheme details" do
before do
click_button "Save and return to locations"
click_link("Scheme")
end
it "does not let you change details other than the name, Confidential information and Housing stock owned by" do
it "does not let you change details other than the name, confidential information and housing stock owner" do
assert_selector "a", text: "Change", count: 3
end
end

24
spec/helpers/locations_helper_spec.rb

@ -2,9 +2,9 @@ require "rails_helper"
RSpec.describe LocationsHelper do
describe "mobility type selection" do
expected_selection = [OpenStruct.new(id: "Wheelchair-user standard", name: "Wheelchair-user standard", description: "The majority of units are suitable for someone who uses a wheelchair and offer the full use of all rooms and facilities."),
OpenStruct.new(id: "Fitted with equipment and adaptations", name: "Fitted with equipment and adaptations", description: "For example, the majority of units have been fitted with stairlifts, ramps, level access showers or grab rails."),
OpenStruct.new(id: "None", name: "None", description: "The majority of units are not designed to wheelchair-user standards or fitted with any equipment and adaptations.")]
expected_selection = [OpenStruct.new(id: "Wheelchair-user standard", name: "Wheelchair-user standard", description: "Suitable for someone who uses a wheelchair and offers the full use of all rooms and facilities."),
OpenStruct.new(id: "Fitted with equipment and adaptations", name: "Fitted with equipment and adaptations", description: "Fitted with stairlifts, ramps, level access showers or grab rails."),
OpenStruct.new(id: "None", name: "None", description: "Not designed to wheelchair-user standards or fitted with any equipment or adaptations.")]
it "returns correct selection to display" do
expect(mobility_type_selection).to eq(expected_selection)
end
@ -138,15 +138,15 @@ RSpec.describe LocationsHelper do
it "returns correct display attributes" do
attributes = [
{ name: "Postcode", value: location.postcode },
{ name: "Location name", value: location.name, edit: true },
{ name: "Local authority", value: location.location_admin_district },
{ name: "Total number of units at this location", value: location.units },
{ name: "Common type of unit", value: location.type_of_unit },
{ name: "Mobility type", value: location.mobility_type },
{ name: "Location code", value: location.location_code },
{ name: "Availability", value: "Active from 1 April 2022" },
{ name: "Status", value: :active },
{ attribute: "postcode", name: "Postcode", value: location.postcode },
{ attribute: "name", name: "Location name", value: location.name },
{ attribute: "local_authority", name: "Local authority", value: location.location_admin_district },
{ attribute: "units", name: "Number of units", value: location.units },
{ attribute: "type_of_unit", name: "Most common unit", value: location.type_of_unit },
{ attribute: "mobility_standards", name: "Mobility standards", value: location.mobility_type },
{ attribute: "location_code", name: "Location code", value: location.location_code },
{ attribute: "availability", name: "Availability", value: "Active from 1 April 2022" },
{ attribute: "status", name: "Status", value: :active },
]
expect(display_location_attributes(location)).to eq(attributes)

56
spec/models/location_spec.rb

@ -20,7 +20,7 @@ RSpec.describe Location, type: :model do
end
end
describe "#validate_postcode" do
describe "#postcode" do
let(:location) { FactoryBot.build(:location) }
it "does not add an error if postcode is valid" do
@ -31,14 +31,34 @@ RSpec.describe Location, type: :model do
it "does add an error when the postcode is invalid" do
location.postcode = "invalid"
expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode #{I18n.t('validations.postcode')}")
location.valid?(:postcode)
expect(location.errors.count).to eq(1)
end
it "does add an error when the postcode is missing" do
location.postcode = nil
expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode #{I18n.t('validations.postcode')}")
location.valid?(:postcode)
expect(location.errors.count).to eq(1)
end
end
describe "#local_authority" do
let(:location) { FactoryBot.build(:location) }
it "does add an error when the local authority is invalid" do
location.location_admin_district = nil
location.valid?(:location_admin_district)
expect(location.errors.count).to eq(1)
end
end
describe "#name" do
let(:location) { FactoryBot.build(:location) }
it "does not add an error when the name is invalid" do
location.name = nil
location.valid?
expect(location.errors.count).to eq(0)
end
end
@ -47,8 +67,8 @@ RSpec.describe Location, type: :model do
it "does add an error when the number of units is invalid" do
location.units = nil
expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Units #{I18n.t('activerecord.errors.models.location.attributes.units.blank')}")
location.valid?(:units)
expect(location.errors.count).to eq(1)
end
end
@ -57,8 +77,8 @@ RSpec.describe Location, type: :model do
it "does add an error when the type of unit is invalid" do
location.type_of_unit = nil
expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Type of unit #{I18n.t('activerecord.errors.models.location.attributes.type_of_unit.blank')}")
location.valid?(:type_of_unit)
expect(location.errors.count).to eq(1)
end
end
@ -67,8 +87,18 @@ RSpec.describe Location, type: :model do
it "does add an error when the mobility type is invalid" do
location.mobility_type = nil
expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Mobility type #{I18n.t('activerecord.errors.models.location.attributes.mobility_type.blank')}")
location.valid?(:mobility_type)
expect(location.errors.count).to eq(1)
end
end
describe "#availability" do
let(:location) { FactoryBot.build(:location) }
it "does add an error when the availability is invalid" do
location.startdate = Time.zone.local(1, 1, 1)
location.valid?(:startdate)
expect(location.errors.count).to eq(1)
end
end
@ -90,7 +120,7 @@ RSpec.describe Location, type: :model do
before do
FactoryBot.create(:location, name: "ABC", postcode: "NW1 8RR", startdate: Time.zone.today)
FactoryBot.create(:location, name: "XYZ", postcode: "SE1 6HJ", startdate: Time.zone.today + 1.day)
FactoryBot.create(:location, name: "GHQ", postcode: "EW1 7JK", startdate: Time.zone.today - 1.day, confirmed: false)
FactoryBot.create(:location, name: "GHQ", postcode: "EW1 7JK", startdate: Time.zone.today - 1.day, units: nil)
FactoryBot.create(:location, name: "GHQ", postcode: "EW1 7JK", startdate: nil)
end
@ -122,7 +152,7 @@ RSpec.describe Location, type: :model do
end
context "when filtering by active locations" do
it "returns only locations that started today or earlier and have been confirmed" do
it "returns only locations that started today or earlier and are complete (and so confirmed)" do
expect(described_class.active.count).to eq(2)
end
end

6
spec/models/validations/date_validations_spec.rb

@ -86,7 +86,7 @@ RSpec.describe Validations::DateValidations do
context "with a deactivated location" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:)
@ -111,7 +111,7 @@ RSpec.describe Validations::DateValidations do
context "with a location that is reactivating soon" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:)
@ -136,7 +136,7 @@ RSpec.describe Validations::DateValidations do
context "with a location that has many reactivations soon" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:)

8
spec/models/validations/setup_validations_spec.rb

@ -34,7 +34,7 @@ RSpec.describe Validations::SetupValidations do
describe "#validate_scheme" do
context "with a deactivated location" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:)
@ -59,7 +59,7 @@ RSpec.describe Validations::SetupValidations do
context "with a location that is reactivating soon" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:)
@ -156,7 +156,7 @@ RSpec.describe Validations::SetupValidations do
describe "#validate_location" do
context "with a deactivated location" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:)
@ -181,7 +181,7 @@ RSpec.describe Validations::SetupValidations do
context "with a location that is reactivating soon" do
let(:scheme) { create(:scheme) }
let(:location) { create(:location, scheme:, startdate: nil) }
let(:location) { create(:location, scheme:) }
before do
create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:)

1424
spec/requests/locations_controller_spec.rb

File diff suppressed because it is too large Load Diff

10
spec/requests/schemes_controller_spec.rb

@ -834,10 +834,10 @@ RSpec.describe SchemesController, type: :request do
context "when updating support" do
let(:params) { { scheme: { intended_stay: "Medium stay", support_type: "Low level", page: "support" } } }
it "renders add location to this scheme successful update" do
it "renders the check answers page" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("Add a location to this scheme")
expect(page).to have_content("Check your answers before creating this scheme")
end
it "updates a scheme with valid params" do
@ -1000,7 +1000,7 @@ RSpec.describe SchemesController, type: :request do
expect(scheme_to_update.reload.confirmed?).to eq(true)
end
it "marks all the scheme locations as confirmed" do
it "marks all the scheme locations as confirmed given they are complete" do
expect(scheme_to_update.locations.count > 0).to eq(true)
scheme_to_update.locations.each do |location|
expect(location.confirmed?).to eq(true)
@ -1117,10 +1117,10 @@ RSpec.describe SchemesController, type: :request do
context "when updating support" do
let(:params) { { scheme: { intended_stay: "Medium stay", support_type: "Low level", page: "support" } } }
it "renders confirm secondary group after successful update" do
it "renders scheme check your answers page after successful update" do
follow_redirect!
expect(response).to have_http_status(:ok)
expect(page).to have_content("Add a location to this scheme")
expect(page).to have_content("Check your answers before creating this scheme")
end
it "updates a scheme with valid params" do

Loading…
Cancel
Save