Browse Source

Merge branch 'main' into CLDC-1171-update-design-for-inviting-and-editing-users

pull/787/head
J G 3 years ago committed by GitHub
parent
commit
b1fb144938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 67
      app/components/log_summary_component.html.erb
  2. 13
      app/components/log_summary_component.rb
  3. 12
      app/components/search_result_caption_component.html.erb
  4. 42
      app/controllers/organisations_controller.rb
  5. 23
      app/frontend/styles/_log.scss
  6. 23
      app/frontend/styles/_metadata.scss
  7. 2
      app/frontend/styles/application.scss
  8. 2
      app/models/form.rb
  9. 14
      app/models/form/question.rb
  10. 2
      app/models/form/setup/questions/scheme_id.rb
  11. 9
      app/models/scheme.rb
  12. 76
      app/views/case_logs/_log_list.html.erb
  13. 4
      app/views/form/_checkbox_question.html.erb
  14. 4
      app/views/form/_date_question.html.erb
  15. 4
      app/views/form/_numeric_output_question.html.erb
  16. 4
      app/views/form/_numeric_question.html.erb
  17. 4
      app/views/form/_radio_question.html.erb
  18. 4
      app/views/form/_select_question.html.erb
  19. 4
      app/views/form/_text_question.html.erb
  20. 4
      app/views/form/_textarea_question.html.erb
  21. 5
      app/views/form/guidance/_scheme_selection.html.erb
  22. 2
      app/views/locations/index.html.erb
  23. 2
      app/views/organisations/index.html.erb
  24. 11
      app/views/organisations/logs.html.erb
  25. 11
      app/views/organisations/schemes.html.erb
  26. 3
      app/views/organisations/show.html.erb
  27. 11
      app/views/organisations/users.html.erb
  28. 130
      app/views/schemes/check_answers.html.erb
  29. 4
      app/views/schemes/index.html.erb
  30. 20
      app/views/schemes/show.html.erb
  31. 2
      app/views/users/_user_list.html.erb
  32. 4
      app/views/users/index.html.erb
  33. 90
      config/locales/en.yml
  34. 34
      spec/components/log_summary_component_spec.rb
  35. 4
      spec/components/search_result_caption_component_spec.rb
  36. 4
      spec/features/schemes_spec.rb
  37. 4
      spec/models/form_spec.rb
  38. 6
      spec/models/location_spec.rb
  39. 14
      spec/models/scheme_spec.rb
  40. 8
      spec/requests/case_logs_controller_spec.rb
  41. 3
      spec/requests/locations_controller_spec.rb
  42. 68
      spec/requests/organisations_controller_spec.rb
  43. 13
      spec/requests/schemes_controller_spec.rb

67
app/components/log_summary_component.html.erb

@ -0,0 +1,67 @@
<article class="app-log-summary">
<div class="govuk-grid-row">
<div class="govuk-grid-column-one-half">
<header class="app-log-summary__header">
<h2 class="app-log-summary__title">
<%= govuk_link_to case_log_path(log) do %>
<span class="govuk-visually-hidden">Log </span><%= log.id %>
<% end %>
</h2>
<% if log.tenancycode? or log.propcode? %>
<dl class="app-metadata app-metadata--inline">
<% if log.tenancycode? %>
<div class="app-metadata__item">
<dt class="app-metadata__term">Tenant</dt>
<dd class="app-metadata__definition"><%= log.tenancycode %></dd>
</div>
<% end %>
<% if log.propcode? %>
<div class="app-metadata__item">
<dt class="app-metadata__term">Property</dt>
<dd class="app-metadata__definition"><%= log.propcode %></dd>
</div>
<% end %>
</dl>
<% end %>
</header>
<% if log.needstype? or log.startdate? %>
<p class="govuk-body govuk-!-margin-bottom-2">
<% if log.needstype? %>
<%= log.is_general_needs? ? "General needs" : "Supported housing" %><br>
<% end %>
<% if log.startdate? %>
Tenancy starts <time datetime="<%= log.startdate.iso8601 %>"><%= log.startdate.to_formatted_s(:govuk_date) %></time>
<% end %>
</p>
<% end %>
<% if current_user.support? %>
<% if log.owning_organisation or log.managing_organisation %>
<dl class="app-metadata">
<div class="app-metadata__item">
<dt class="app-metadata__term">Owned by</dt>
<dd class="app-metadata__definition"><%= log.owning_organisation&.name %></dd>
</div>
<div class="app-metadata__item">
<dt class="app-metadata__term">Managed by</dt>
<dd class="app-metadata__definition"><%= log.managing_organisation&.name %></dd>
</div>
</dl>
<% end %>
<% end %>
</div>
<footer class="govuk-grid-column-one-half app-log-summary__footer">
<p class="govuk-body govuk-!-margin-bottom-2">
<%= log_status %>
</p>
<p class="govuk-body">
Created <time datetime="<%= log.created_at.iso8601 %>"><%= log.created_at.to_formatted_s(:govuk_date) %></time>
<% if log.created_by %>
<span class="app-log-summary__footer--actor">by <%= log.created_by.name || log.created_by.email %></span>
<% end %>
</p>
</footer>
</div>
</article>

13
app/components/log_summary_component.rb

@ -0,0 +1,13 @@
class LogSummaryComponent < ViewComponent::Base
attr_reader :current_user, :log
def initialize(current_user:, log:)
@current_user = current_user
@log = log
super
end
def log_status
helpers.status_tag(log.status)
end
end

12
app/components/search_result_caption_component.html.erb

@ -1,7 +1,7 @@
<span class="govuk-!-margin-right-4"> <span class="govuk-!-margin-right-4">
<% if searched.present? %> <% if searched.present? %>
<strong><%= count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total <%= item %>. <%= govuk_link_to("Clear search", path) %> <strong><%= count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total <%= item %>. <%= govuk_link_to("Clear search", path) %>
<% else %> <% else %>
<strong><%= count %></strong> total <%= item %>. <strong><%= count %></strong> total <%= item %>
<% end %> <% end %>
</span> </span>

42
app/controllers/organisations_controller.rb

@ -29,13 +29,24 @@ class OrganisationsController < ApplicationController
end end
def users def users
@pagy, @users = pagy(filtered_users(@organisation.users.sorted_by_organisation_and_role, search_term)) organisation_users = @organisation.users.sorted_by_organisation_and_role
@searched = search_term.presence unpaginated_filtered_users = filtered_collection(organisation_users, search_term)
@total_count = @organisation.users.size
if current_user.support? respond_to do |format|
render "users", layout: "application" format.html do
else @pagy, @users = pagy(unpaginated_filtered_users)
render "users/index" @searched = search_term.presence
@total_count = @organisation.users.size
if current_user.support?
render "users", layout: "application"
else
render "users/index"
end
end
format.csv do
send_data unpaginated_filtered_users.to_csv, filename: "users-#{@organisation.name}-#{Time.zone.now}.csv"
end
end end
end end
@ -83,11 +94,18 @@ class OrganisationsController < ApplicationController
organisation_logs = CaseLog.all.where(owning_organisation_id: @organisation.id) organisation_logs = CaseLog.all.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_case_logs(filtered_collection(organisation_logs, search_term)) unpaginated_filtered_logs = filtered_case_logs(filtered_collection(organisation_logs, search_term))
@pagy, @case_logs = pagy(unpaginated_filtered_logs) respond_to do |format|
@searched = search_term.presence format.html do
@total_count = organisation_logs.size @pagy, @case_logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
render "logs", layout: "application" @total_count = organisation_logs.size
render "logs", layout: "application"
end
format.csv do
send_data unpaginated_filtered_logs.to_csv, filename: "logs-#{@organisation.name}-#{Time.zone.now}.csv"
end
end
else else
redirect_to(case_logs_path) redirect_to(case_logs_path)
end end

23
app/frontend/styles/_log.scss

@ -0,0 +1,23 @@
.app-log-summary {
border-top: 1px solid $govuk-border-colour;
padding-top: govuk-spacing(3);
}
.app-log-summary__header {
align-items: baseline;
display: flex;
}
.app-log-summary__title {
margin: 0 govuk-spacing(3) govuk-spacing(2) 0;
}
@include govuk-media-query(tablet) {
.app-log-summary__footer {
text-align: right;
}
.app-log-summary__footer--actor {
display: block;
}
}

23
app/frontend/styles/_metadata.scss

@ -0,0 +1,23 @@
.app-metadata {
@include govuk-font($size: 16, $tabular: true);
color: $govuk-secondary-text-colour;
margin-top: 0;
&--inline {
display: flex;
gap: govuk-spacing(3);
margin: 0;
}
&__item {
display: flex;
}
&__term {
margin-right: govuk-spacing(1);
}
&__definition {
margin-left: 0;
}
}

2
app/frontend/styles/application.scss

@ -30,6 +30,8 @@ $govuk-breakpoints: (
@import "filter-layout"; @import "filter-layout";
@import "header"; @import "header";
@import "input"; @import "input";
@import "log";
@import "metadata";
@import "related-navigation"; @import "related-navigation";
@import "section-skip-link"; @import "section-skip-link";
@import "table-group"; @import "table-group";

2
app/models/form.rb

@ -79,7 +79,7 @@ class Form
when :in_progress when :in_progress
"#{next_subsection.id}/check_answers".dasherize "#{next_subsection.id}/check_answers".dasherize
when :not_started when :not_started
first_question_in_subsection = next_subsection.pages.first.id first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(case_log, nil) }.id
first_question_in_subsection.to_s.dasherize first_question_in_subsection.to_s.dasherize
else else
"error" "error"

14
app/models/form/question.rb

@ -5,6 +5,11 @@ class Form::Question
:inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value, :inferred_answers, :hidden_in_check_answers, :inferred_check_answers_value,
:guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived :guidance_partial, :prefix, :suffix, :requires_js, :fields_added, :derived
module GuidancePosition
TOP = 1
BOTTOM = 2
end
def initialize(id, hsh, page) def initialize(id, hsh, page)
@id = id @id = id
@page = page @page = page
@ -12,6 +17,7 @@ class Form::Question
@check_answer_label = hsh["check_answer_label"] @check_answer_label = hsh["check_answer_label"]
@header = hsh["header"] @header = hsh["header"]
@guidance_partial = hsh["guidance_partial"] @guidance_partial = hsh["guidance_partial"]
@guidance_position = GuidancePosition::TOP
@hint_text = hsh["hint_text"] @hint_text = hsh["hint_text"]
@type = hsh["type"] @type = hsh["type"]
@min = hsh["min"] @min = hsh["min"]
@ -238,6 +244,14 @@ class Form::Question
case_log[id].to_s == answer.id.to_s case_log[id].to_s == answer.id.to_s
end end
def top_guidance?
@guidance_partial && @guidance_position == GuidancePosition::TOP
end
def bottom_guidance?
@guidance_partial && @guidance_position == GuidancePosition::BOTTOM
end
private private
def selected_answer_option_is_derived?(case_log) def selected_answer_option_is_derived?(case_log)

2
app/models/form/setup/questions/scheme_id.rb

@ -7,6 +7,8 @@ class Form::Setup::Questions::SchemeId < ::Form::Question
@type = "select" @type = "select"
@answer_options = answer_options @answer_options = answer_options
@derived = true unless FeatureToggle.supported_housing_schemes_enabled? @derived = true unless FeatureToggle.supported_housing_schemes_enabled?
@guidance_position = GuidancePosition::BOTTOM
@guidance_partial = "scheme_selection"
end end
def answer_options def answer_options

9
app/models/scheme.rb

@ -7,7 +7,13 @@ class Scheme < ApplicationRecord
scope :filter_by_id, ->(id) { where(id: (id.start_with?("S") ? id[1..] : id)) } scope :filter_by_id, ->(id) { where(id: (id.start_with?("S") ? id[1..] : id)) }
scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") } scope :search_by_service_name, ->(name) { where("service_name ILIKE ?", "%#{name}%") }
scope :search_by_postcode, ->(postcode) { joins("LEFT JOIN locations ON locations.scheme_id = schemes.id").where("locations.postcode ILIKE ?", "%#{postcode.delete(' ')}%") } scope :search_by_postcode, ->(postcode) { joins("LEFT JOIN locations ON locations.scheme_id = schemes.id").where("locations.postcode ILIKE ?", "%#{postcode.delete(' ')}%") }
scope :search_by, ->(param) { search_by_postcode(param).or(search_by_service_name(param)).or(filter_by_id(param)).distinct } scope :search_by_location_name, ->(name) { joins("LEFT JOIN locations ON locations.scheme_id = schemes.id").where("locations.name ILIKE ?", "%#{name}%") }
scope :search_by, lambda { |param|
search_by_postcode(param)
.or(search_by_service_name(param))
.or(search_by_location_name(param))
.or(filter_by_id(param)).distinct
}
validate :validate_confirmed validate :validate_confirmed
@ -214,7 +220,6 @@ class Scheme < ApplicationRecord
if confirmed == true if confirmed == true
required_attributes.any? do |attribute| required_attributes.any? do |attribute|
if self[attribute].blank? if self[attribute].blank?
errors.add :base
errors.add attribute.to_sym errors.add attribute.to_sym
self.confirmed = false self.confirmed = false
end end

76
app/views/case_logs/_log_list.html.erb

@ -1,68 +1,8 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>"> <h2 class="govuk-body">
<%= govuk_table do |table| %> <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular], id: title.dasherize) do |caption| %> <%= govuk_link_to "Download (CSV)", "#{request.path}.csv", type: "text/csv" %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> </h2>
<%= govuk_link_to "Download (CSV)", "/logs.csv", type: "text/csv" %>
<% end %> <% case_logs.map do |log| %>
<%= table.head do |head| %> <%= render(LogSummaryComponent.new(current_user:, log:)) %>
<%= head.row do |row| %> <% end %>
<% row.cell(header: true, text: "Log", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Tenant", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Property", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Tenancy starts", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Log created", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Log status", html_attributes: {
scope: "col",
}) %>
<% if current_user.support? %>
<% row.cell(header: true, text: "Owning organisation", html_attributes: {
scope: "col",
}) %>
<% row.cell(header: true, text: "Managing organisation", html_attributes: {
scope: "col",
}) %>
<% end %>
<% end %>
<% end %>
<%= table.body do |body| %>
<% case_logs.map do |log| %>
<%= body.row do |row| %>
<% row.cell(header: true, html_attributes: {
scope: "row",
}) do %>
<%= govuk_link_to case_log_path(log) do %>
<span class="govuk-visually-hidden">Log </span><%= log.id %>
<% end %>
<% end %>
<% row.cell(
text: log.tenancycode? ? log.tenancycode : "–",
classes: "app-!-font-tabular",
) %>
<% row.cell(
text: log.propcode? ? log.propcode : "–",
classes: "app-!-font-tabular",
) %>
<% row.cell(text: log.startdate.present? ? log.startdate.to_formatted_s(:govuk_date) : "–") %>
<% row.cell(text: log.created_at.to_formatted_s(:govuk_date)) %>
<% row.cell do %>
<%= status_tag(log.status) %>
<% end %>
<% if current_user.support? %>
<% row.cell(text: log.owning_organisation&.name) %>
<% row.cell(text: log.managing_organisation&.name) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
</section>

4
app/views/form/_checkbox_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_check_boxes_fieldset question.id.to_sym, <%= f.govuk_check_boxes_fieldset question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -20,3 +20,5 @@
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_date_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_date_field question.id.to_sym, <%= f.govuk_date_field question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -6,3 +6,5 @@
hint: { text: question.hint_text&.html_safe || "For example, 1 9 2022." }, hint: { text: question.hint_text&.html_safe || "For example, 1 9 2022." },
width: 20, width: 20,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_numeric_output_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<div class="govuk-form-group"> <div class="govuk-form-group">
<label class="govuk-label govuk-label--<%= label_size(page_header, conditional) %>" for="case-log-<%= question.id %>-field"> <label class="govuk-label govuk-label--<%= label_size(page_header, conditional) %>" for="case-log-<%= question.id %>-field">
@ -20,3 +20,5 @@
<span class="govuk-input__suffix"><%= question.suffix_label(case_log) %></span> <span class="govuk-input__suffix"><%= question.suffix_label(case_log) %></span>
</div> </div>
</div> </div>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_numeric_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_number_field question.id.to_sym, <%= f.govuk_number_field question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -10,3 +10,5 @@
prefix_text: question.prefix.to_s, prefix_text: question.prefix.to_s,
suffix_text: question.suffix_label(@case_log), suffix_text: question.suffix_label(@case_log),
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_radio_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_radio_buttons_fieldset question.id.to_sym, <%= f.govuk_radio_buttons_fieldset question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -34,3 +34,5 @@
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_select_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<% selected = @case_log.public_send(question.id) || "" %> <% selected = @case_log.public_send(question.id) || "" %>
<% answers = question.displayed_answer_options(@case_log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %> <% answers = question.displayed_answer_options(@case_log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %>
@ -16,3 +16,5 @@
<%= answer.id == "" ? "disabled" : "" %>><%= answer.name || answer.resource %></option> <%= answer.id == "" ? "disabled" : "" %>><%= answer.name || answer.resource %></option>
<% end %> <% end %>
<% end %> <% end %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_text_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_text_field question.id.to_sym, <%= f.govuk_text_field question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -6,3 +6,5 @@
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
width: question.width || nil, width: question.width || nil,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

4
app/views/form/_textarea_question.html.erb

@ -1,4 +1,4 @@
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.guidance_partial %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %>
<%= f.govuk_text_area question.id.to_sym, <%= f.govuk_text_area question.id.to_sym,
caption: caption(caption_text, page_header, conditional), caption: caption(caption_text, page_header, conditional),
@ -6,3 +6,5 @@
hint: { text: question.hint_text&.html_safe }, hint: { text: question.hint_text&.html_safe },
width: question.width || nil, width: question.width || nil,
**stimulus_html_attributes(question) %> **stimulus_html_attributes(question) %>
<%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %>

5
app/views/form/guidance/_scheme_selection.html.erb

@ -0,0 +1,5 @@
<% if current_user.data_provider? %>
<p class="govuk-body">If you can’t find the supported housing service you’re looking for or not sure which to choose, contact a data coordinator at <%= current_user.organisation.name %>.</p>
<% elsif current_user.data_coordinator? %>
<p class="govuk-body">or <%= govuk_link_to "create a new supported housing service", new_scheme_path %></p>
<% end %>

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

@ -13,6 +13,8 @@
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<h2 class="govuk-visually-hidden">Locations</h2>
<%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %> <%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %>
<%= govuk_section_break(visible: true, size: "m") %> <%= govuk_section_break(visible: true, size: "m") %>

2
app/views/organisations/index.html.erb

@ -5,8 +5,6 @@
<%= render partial: "organisations/headings", locals: request.path == "/organisations" ? { main: "Organisations", sub: nil } : { main: @organisation.name, sub: "Organisations" } %> <%= render partial: "organisations/headings", locals: request.path == "/organisations" ? { main: "Organisations", sub: nil } : { main: @organisation.name, sub: "Organisations" } %>
<h2 class="govuk-visually-hidden">Organisations</h2>
<% if current_user.support? %> <% if current_user.support? %>
<%= govuk_button_link_to "Create a new organisation", new_organisation_path, html: { method: :get } %> <%= govuk_button_link_to "Create a new organisation", new_organisation_path, html: { method: :get } %>
<% end %> <% end %>

11
app/views/organisations/logs.html.erb

@ -5,11 +5,12 @@
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new( <% if current_user.support? %>
items: secondary_items(request.path, @organisation.id), <%= render SubNavigationComponent.new(
) %> items: secondary_items(request.path, @organisation.id),
) %>
<h2 class="govuk-visually-hidden">Logs</h2> <h2 class="govuk-visually-hidden">Logs</h2>
<% end %>
<div class="app-filter-layout" data-controller="filter-layout"> <div class="app-filter-layout" data-controller="filter-layout">
<div class="govuk-button-group app-filter-toggle"> <div class="govuk-button-group app-filter-toggle">

11
app/views/organisations/schemes.html.erb

@ -3,19 +3,24 @@
<% content_for :title, title %> <% content_for :title, title %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: @organisation.name, sub: nil } : { main: "Schemes", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: current_user.support? ? { main: @organisation.name, sub: nil } : { main: "Supported housing schemes", sub: current_user.organisation.name } %>
<% if current_user.support? %> <% if current_user.support? %>
<%= render SubNavigationComponent.new( <%= render SubNavigationComponent.new(
items: secondary_items(request.path, @organisation.id), items: secondary_items(request.path, @organisation.id),
) %> ) %>
<h2 class="govuk-visually-hidden">Supported housing schemes</h2>
<% end %> <% end %>
<%= govuk_button_link_to "Create a new supported housing scheme", new_scheme_path, html: { method: :post } %> <%= govuk_button_link_to "Create a new supported housing scheme", new_scheme_path, html: { method: :post } %>
<h2 class="govuk-visually-hidden">Supported housing schemes</h2> <%= govuk_details(
classes: "govuk-!-width-two-thirds",
summary_text: "What is a supported housing scheme?",
text: "A supported housing scheme (also known as a ‘supported housing service’) provides shared or self-contained housing for a particular client group, for example younger or vulnerable people. A single scheme can contain multiple units, for example bedrooms in shared houses or a bungalow with 3 bedrooms.",
) %>
<%= render SearchComponent.new(current_user:, search_label: "Search by scheme name, code or postcode", value: @searched) %> <%= render SearchComponent.new(current_user:, search_label: "Search by scheme name, code, postcode or location name", value: @searched) %>
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m"> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">

3
app/views/organisations/show.html.erb

@ -8,10 +8,9 @@
<%= render SubNavigationComponent.new( <%= render SubNavigationComponent.new(
items: secondary_items(request.path, @organisation.id), items: secondary_items(request.path, @organisation.id),
) %> ) %>
<h2 class="govuk-visually-hidden">About this organisation</h2>
<% end %> <% end %>
<h2 class="govuk-visually-hidden">About this organisation</h2>
<div class="govuk-grid-row"> <div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop"> <div class="govuk-grid-column-two-thirds-from-desktop">
<%= govuk_summary_list do |summary_list| %> <%= govuk_summary_list do |summary_list| %>

11
app/views/organisations/users.html.erb

@ -5,11 +5,12 @@
<%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %> <%= render partial: "organisations/headings", locals: { main: @organisation.name, sub: nil } %>
<%= render SubNavigationComponent.new( <% if current_user.support? %>
items: secondary_items(request.path, @organisation.id), <%= render SubNavigationComponent.new(
) %> items: secondary_items(request.path, @organisation.id),
) %>
<h2 class="govuk-visually-hidden">Users</h2> <h2 class="govuk-visually-hidden">Users</h2>
<% end %>
<% if current_user.data_coordinator? || current_user.support? %> <% if current_user.data_coordinator? || current_user.support? %>
<%= govuk_button_link_to "Invite user", new_user_path(organisation_id: @organisation.id), html: { method: :get } %> <%= govuk_button_link_to "Invite user", new_user_path(organisation_id: @organisation.id), html: { method: :get } %>

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

@ -2,76 +2,76 @@
<%= render partial: "organisations/headings", locals: { main: "Check your changes before creating this scheme", sub: @scheme.service_name } %> <%= render partial: "organisations/headings", locals: { main: "Check your changes before creating this scheme", sub: @scheme.service_name } %>
<%= form_for(@scheme, as: :scheme, method: :patch) do |f| %> <%= form_for(@scheme, as: :scheme, method: :patch) do |f| %>
<div class="govuk-grid-row"> <%= f.govuk_error_summary %>
<%= f.govuk_error_summary %> <%= govuk_tabs(title: "Check your answers before creating this scheme") do |component| %>
<%= govuk_tabs(title: "Check your answers before creating this scheme") do |component| %> <% component.tab(label: "Scheme") do %>
<% component.tab(label: "Scheme") do %> <h2 class="govuk-visually-hidden">Scheme</h2>
<dl class="govuk-summary-list"> <dl class="govuk-summary-list">
<% @scheme.check_details_attributes.each do |attr| %> <% @scheme.check_details_attributes.each do |attr| %>
<% next if current_user.data_coordinator? && attr[:name] == ("owned by") %> <% next if current_user.data_coordinator? && attr[:name] == ("owned by") %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_details_path(@scheme, check_answers: true) } %> <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_details_path(@scheme, check_answers: true) } %>
<% end %>
<% if !@scheme.arrangement_type_same? %>
<% @scheme.check_support_services_provider_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_services_provider_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% @scheme.check_primary_client_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_primary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% @scheme.check_secondary_client_confirmation_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_confirm_secondary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% if @scheme.has_other_client_group == "Yes" %>
<% @scheme.check_secondary_client_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_secondary_client_group_path(@scheme, check_answers: true) } %>
<% end %>
<% end %>
<% @scheme.check_support_attributes.each do |attr| %>
<%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_path(@scheme, check_answers: true) } %>
<% end %>
</dl>
<% end %> <% end %>
<% component.tab(label: "Locations") do %> <% if !@scheme.arrangement_type_same? %>
<%= govuk_table do |table| %> <% @scheme.check_support_services_provider_attributes.each do |attr| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_services_provider_path(@scheme, check_answers: true) } %>
<strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %> <% end %>
<% end %> <% end %>
<%= table.head do |head| %> <% @scheme.check_primary_client_attributes.each do |attr| %>
<%= head.row do |row| %> <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_primary_client_group_path(@scheme, check_answers: true) } %>
<% row.cell(header: true, text: "Code", html_attributes: { <% end %>
scope: "col", <% @scheme.check_secondary_client_confirmation_attributes.each do |attr| %>
}) %> <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_confirm_secondary_client_group_path(@scheme, check_answers: true) } %>
<% row.cell(header: true, text: "Postcode", html_attributes: { <% end %>
scope: "col", <% if @scheme.has_other_client_group == "Yes" %>
}) %> <% @scheme.check_secondary_client_attributes.each do |attr| %>
<% row.cell(header: true, text: "Units", html_attributes: { <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_secondary_client_group_path(@scheme, check_answers: true) } %>
scope: "col", <% end %>
}) %> <% end %>
<% row.cell(header: true, text: "Common unit type", html_attributes: { <% @scheme.check_support_attributes.each do |attr| %>
scope: "col", <%= render partial: "scheme_summary_list_row", locals: { scheme: @scheme, attribute: attr, change_link: scheme_support_path(@scheme, check_answers: true) } %>
}) %> <% end %>
<% row.cell(header: true, text: "Available from", html_attributes: { </dl>
scope: "col", <% end %>
}) %> <% component.tab(label: "Locations") do %>
<% end %> <h2 class="govuk-visually-hidden">Locations</h2>
<% end %> <%= govuk_table do |table| %>
<% @scheme.locations.each do |location| %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<%= table.body do |body| %> <strong><%= @scheme.locations.count %></strong> <%= @scheme.locations.count.eql?(1) ? "location" : "locations" %>
<%= body.row do |row| %> <% end %>
<% row.cell(text: location.id) %> <%= table.head do |head| %>
<% row.cell(text: simple_format(location_cell(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit"), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> <%= head.row do |row| %>
<% row.cell(text: location.units) %> <% row.cell(header: true, text: "Code", html_attributes: {
<% row.cell(text: simple_format("<span>#{location.type_of_unit}</span>")) %> scope: "col",
<% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %> }) %>
<% end %> <% 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: "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(location, "/schemes/#{@scheme.id}/locations/#{location.id}/edit"), { 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.startdate&.to_formatted_s(:govuk_date)) %>
<% end %> <% end %>
<% end %>
<% end %> <% end %>
<%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<% end %> <% end %>
<% end %> <% end %>
</div> <%= govuk_button_link_to "Add a location", new_location_path(id: @scheme.id), secondary: true %>
<% end %>
<% end %>
<%= f.hidden_field :page, value: "check-answers" %> <%= f.hidden_field :page, value: "check-answers" %>
<%= f.hidden_field :confirmed, value: "true" %> <%= f.hidden_field :confirmed, value: "true" %>
<% button_label = @scheme.confirmed? ? "Save" : "Create scheme" %> <% button_label = @scheme.confirmed? ? "Save" : "Create scheme" %>

4
app/views/schemes/index.html.erb

@ -5,11 +5,9 @@
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Supported housing schemes", sub: nil } : { main: "Supported housing schemes", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Supported housing schemes", sub: nil } : { main: "Supported housing schemes", sub: current_user.organisation.name } %>
<h2 class="govuk-visually-hidden">Supported housing schemes</h2>
<%= govuk_button_link_to "Create a new supported housing scheme", new_scheme_path, html: { method: :post } %> <%= govuk_button_link_to "Create a new supported housing scheme", new_scheme_path, html: { method: :post } %>
<%= render SearchComponent.new(current_user:, search_label: "Search by scheme name, code or postcode", value: @searched) %> <%= render SearchComponent.new(current_user:, search_label: "Search by scheme name, code, postcode or location name", value: @searched) %>
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m"> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">

20
app/views/schemes/show.html.erb

@ -12,15 +12,15 @@
<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %>
<div class="govuk-grid-row"> <h2 class="govuk-visually-hidden">Scheme</h2>
<%= govuk_summary_list do |summary_list| %>
<% @scheme.display_attributes.each do |attr| %> <%= govuk_summary_list do |summary_list| %>
<% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %> <% @scheme.display_attributes.each do |attr| %>
<%= summary_list.row do |row| %> <% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %>
<% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %> <%= summary_list.row do |row| %>
<% row.value { details_html(attr) } %> <% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %>
<% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %> <% row.value { details_html(attr) } %>
<% end %> <% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %>
<% end %> <% end %>
<% end %> <% end %>
</div> <% end %>

2
app/views/users/_user_list.html.erb

@ -4,7 +4,7 @@
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "users", path: request.path)) %> <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "users", path: request.path)) %>
<% if current_user.support? %> <% if current_user.support? %>
<% query = searched.present? ? "?search=#{searched}" : nil %> <% query = searched.present? ? "?search=#{searched}" : nil %>
<%= govuk_link_to "Download (CSV)", "/users.csv#{query}", type: "text/csv" %> <%= govuk_link_to "Download (CSV)", "#{request.path}.csv#{query}", type: "text/csv" %>
<% end %> <% end %>
<% end %> <% end %>
<%= table.head do |head| %> <%= table.head do |head| %>

4
app/views/users/index.html.erb

@ -3,9 +3,7 @@
<% content_for :title, title %> <% content_for :title, title %>
<%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Users", sub: nil } : { main: "User", sub: current_user.organisation.name } %> <%= render partial: "organisations/headings", locals: current_user.support? ? { main: "Users", sub: nil } : { main: "Users", sub: current_user.organisation.name } %>
<h2 class="govuk-visually-hidden">Users</h2>
<% if current_user.data_coordinator? || current_user.support? %> <% if current_user.data_coordinator? || current_user.support? %>
<%= govuk_button_link_to "Invite user", new_user_path, html: { method: :get } %> <%= govuk_button_link_to "Invite user", new_user_path, html: { method: :get } %>

90
config/locales/en.yml

@ -44,35 +44,33 @@ en:
scheme: scheme:
attributes: attributes:
owning_organisation_id: owning_organisation_id:
invalid: "Enter the existing organisation’s name" invalid: "Enter the name of the organisation that owns the housing stock"
managing_organisation_id: managing_organisation_id:
invalid: "Enter the existing organisation’s name" invalid: "Enter the name of the organisation that manages the housing stock"
service_name: service_name:
invalid: "Enter the scheme’s name" invalid: "Enter the name of the scheme"
scheme_type: scheme_type:
invalid: "Select the scheme’s type" invalid: "Select the type of scheme"
registered_under_care_act: registered_under_care_act:
invalid: "Select if this scheme is registered under the Care Standards Act 2000" invalid: "Select if this scheme is registered under the Care Standards Act 2000"
primary_client_group: primary_client_group:
invalid: "Select what client group is this scheme intended for" invalid: "Select what client group this scheme is intended for"
secondary_client_group: secondary_client_group:
invalid: "Select what is the other client group" invalid: "Select the other client group for this scheme"
support_type: support_type:
invalid: "Select level of support given" invalid: "Select the level of support provided by this scheme"
intended_stay: intended_stay:
invalid: "Select intended length of stay" invalid: "Select the intended length of stay"
has_other_client_group: has_other_client_group:
invalid: "Select if this scheme provides for another client group" invalid: "Select if this scheme provides for another client group"
arrangement_type: arrangement_type:
invalid: "Select who provides the support services used by this scheme" invalid: "Select who provides the support services used by this scheme"
base:
invalid: "You must answer all the required questions for this scheme"
location: location:
attributes: attributes:
startdate: startdate:
invalid: "Enter a date in the correct format, for example 1 9 2022" invalid: "Enter a date in the correct format, for example 31 1 2022"
units: units:
blank: "Enter total number of units at this location" blank: "Enter the total number of units at this location"
type_of_unit: type_of_unit:
blank: "Select the most common type of unit at this location" blank: "Select the most common type of unit at this location"
mobility_type: mobility_type:
@ -94,8 +92,8 @@ en:
validations: validations:
organisation: organisation:
name_missing: "Enter the organisation’s name" name_missing: "Enter the name of the organisation"
provider_type_missing: "Select the organisation’s type" provider_type_missing: "Select the organisation type"
not_answered: "You must answer %{question}" not_answered: "You must answer %{question}"
other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided" other_field_missing: "If %{main_field_label} is other then %{other_field_label} must be provided"
@ -103,8 +101,8 @@ en:
numeric: numeric:
valid: "%{field} must be between %{min} and %{max}" valid: "%{field} must be between %{min} and %{max}"
date: date:
invalid_date: "Enter a date in the correct format, for example 1 9 2022" invalid_date: "Enter a date in the correct format, for example 31 1 2022"
outside_collection_window: "Date must be within the current collection windows" outside_collection_window: "Enter a date within the current collection windows"
postcode: "Enter a postcode in the correct format, for example AA1 1AA" postcode: "Enter a postcode in the correct format, for example AA1 1AA"
email: email:
taken: "Enter an email address that hasn’t already been used to sign up" taken: "Enter an email address that hasn’t already been used to sign up"
@ -122,20 +120,20 @@ en:
property: property:
mrcdate: mrcdate:
before_tenancy_start: "Major repairs date must be before the tenancy start date" before_tenancy_start: "Enter a major repairs date that is before the tenancy start date"
not_first_let: "Major repairs date must not be completed if the tenancy is a first let" not_first_let: "Major repairs date must not be completed if the tenancy is a first let"
730_days_before_tenancy_start: "The major repairs completion date should be no more than 730 days before the tenancy start date" 730_days_before_tenancy_start: "Enter a major repairs completion date that is no more than 730 days before the tenancy start date"
void_date: void_date:
ten_years_before_tenancy_start: "The void date must be no more than 10 years before the tenancy start date" ten_years_before_tenancy_start: "Enter a void date must no more than 10 years before the tenancy start date"
before_tenancy_start: "Void date must be before the tenancy start date" before_tenancy_start: "Enter a void date must that is before the tenancy start date"
after_mrcdate: "Void date must be before the major repairs date if provided" after_mrcdate: "Void date must be before the major repairs date if provided"
offered: offered:
relet_number: "Number of times the property has been re-let must be between 0 and 20" relet_number: "Enter a number between 0 and 20 for the amount of times the property has been re-let"
la: la:
la_invalid_for_org: "%{org_name} does not operate in %{la_name}" la_invalid_for_org: "%{org_name} does not operate in %{la_name}"
postcode_invalid_for_org: "Postcode must be in an area covered by %{org_name}" postcode_invalid_for_org: "Enter a postcode in an area covered by %{org_name}"
rsnvac: rsnvac:
first_let_not_social: "Reason for vacancy cannot be first let if unit has been previously let as social housing" first_let_not_social: "Enter a reason for vacancy that is not 'first let' if unit has been previously let as social housing"
first_let_social: "Reason for vacancy must be first let if unit has been previously let as social housing" first_let_social: "Reason for vacancy must be first let if unit has been previously let as social housing"
previous_let_social: "Property cannot have a previous let type if being let as social housing for the first time" previous_let_social: "Property cannot have a previous let type if being let as social housing for the first time"
non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as you already told us this accommodation is not temporary" non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as you already told us this accommodation is not temporary"
@ -150,9 +148,9 @@ en:
financial: financial:
tshortfall: tshortfall:
outstanding_amount_not_required: "You must not answer the outstanding amount question if you don’t have outstanding rent or charges" outstanding_amount_not_required: "You cannot answer the outstanding amount question if you don’t have outstanding rent or charges"
more_than_rent: "Answer must be less than the basic rent amount" more_than_rent: "Enter a value less less than the basic rent amount"
must_be_positive: "Answer must be more than £0.01 as you told us there is an outstanding amount" must_be_positive: "Enter a value over £0.01 as you told us there is an outstanding amount"
hbrentshortfall: hbrentshortfall:
outstanding_no_benefits: "Answer cannot be ‘yes’ to outstanding amount for basic rent or charges if tenant does not receive housing benefit or Universal Credit or you‘re not sure" outstanding_no_benefits: "Answer cannot be ‘yes’ to outstanding amount for basic rent or charges if tenant does not receive housing benefit or Universal Credit or you‘re not sure"
benefits: benefits:
@ -164,28 +162,28 @@ en:
earnings_missing: "Enter how much income the household has in total" earnings_missing: "Enter how much income the household has in total"
negative_currency: "Enter an amount above 0" negative_currency: "Enter an amount above 0"
rent: rent:
less_than_shortfall: "Answer must be more than the shortfall in basic rent" less_than_shortfall: "Enter an amount that is more than the shortfall in basic rent"
scharge: scharge:
private_registered_provider: private_registered_provider:
general_needs: "Service charge must be between £0 and £55 per week if the landlord is a private registered provider and it is a general needs letting" general_needs: "Enter a value for the service charge between £0 and £55 per week if the landlord is a private registered provider and it is a general needs letting"
supported_housing: "Service charge must be between £0 and £280 per week if the landlord is a private registered provider and it is a supported housing letting" supported_housing: "Enter a value for the service charge between £0 and £280 per week if the landlord is a private registered provider and it is a supported housing letting"
local_authority: local_authority:
general_needs: "Service charge must be between £0 and £45 per week if the landlord is a local authority and it is a general needs letting" general_needs: "Enter a value for the service charge between £0 and £45 per week if the landlord is a local authority and it is a general needs letting"
supported_housing: "Service charge must be between £0 and £165 per week if the landlord is a local authority and it is a supported housing letting" supported_housing: "Enter a value for the service charge between £0 and £165 per week if the landlord is a local authority and it is a supported housing letting"
pscharge: pscharge:
private_registered_provider: private_registered_provider:
general_needs: "Personal service charge must be between £0 and £30 per week if the landlord is a private registered provider and it is a general needs letting" general_needs: "Enter a value for the personal service charge between £0 and £30 per week if the landlord is a private registered provider and it is a general needs letting"
supported_housing: "Personal service charge must be between £0 and £200 per week if the landlord is a private registered provider and it is a supported housing letting" supported_housing: "Enter a value for the personal service charge between £0 and £200 per week if the landlord is a private registered provider and it is a supported housing letting"
local_authority: local_authority:
general_needs: "Personal service charge must be between £0 and £35 per week if the landlord is a local authority and it is a general needs letting" general_needs: "Enter a value for the personal service charge between £0 and £35 per week if the landlord is a local authority and it is a general needs letting"
supported_housing: "Personal service charge must be between £0 and £75 per week if the landlord is a local authority and it is a supported housing letting" supported_housing: "Enter a value for the personal service charge between £0 and £75 per week if the landlord is a local authority and it is a supported housing letting"
supcharg: supcharg:
private_registered_provider: private_registered_provider:
general_needs: "Support charge must be between £0 and £40 per week if the landlord is a private registered provider and it is a general needs letting" general_needs: "Enter a value for the support charge between £0 and £40 per week if the landlord is a private registered provider and it is a general needs letting"
supported_housing: "Support charge must be between £0 and £465 per week if the landlord is a private registered provider and it is a supported housing letting" supported_housing: "Enter a value for the support charge between £0 and £465 per week if the landlord is a private registered provider and it is a supported housing letting"
local_authority: local_authority:
general_needs: "Support charge must be between £0 and £60 per week if the landlord is a local authority and it is a general needs letting" general_needs: "Enter a value for the support charge between £0 and £60 per week if the landlord is a local authority and it is a general needs letting"
supported_housing: "Support charge must be between £0 and £120 per week if the landlord is a local authority and it is a supported housing letting" supported_housing: "Enter a value for the support charge between £0 and £120 per week if the landlord is a local authority and it is a supported housing letting"
brent: brent:
not_in_range: "Basic rent is outside of the expected range based on the lettings type, local authority and number of bedrooms" not_in_range: "Basic rent is outside of the expected range based on the lettings type, local authority and number of bedrooms"
la: la:
@ -201,7 +199,7 @@ en:
charges: charges:
complete_1_of_3: "Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’" complete_1_of_3: "Answer either the ‘household rent and charges’ question or ‘is this accommodation a care home‘, or select ‘no’ for ‘does the household pay rent or charges for the accommodation?’"
tcharge: tcharge:
under_10: "Total charge must be at least £10 per week" under_10: "Enter a total charge that is at least £10 per week"
rent_period: rent_period:
invalid_for_org: "%{org_name} does not charge rent %{rent_period}" invalid_for_org: "%{org_name} does not charge rent %{rent_period}"
carehome: carehome:
@ -211,18 +209,18 @@ en:
reasonpref: reasonpref:
not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as you already told us the tenant was not homeless immediately prior to this letting" not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as you already told us the tenant was not homeless immediately prior to this letting"
reasonable_preference_reason: reasonable_preference_reason:
reason_required: "If reasonable preference is ‘yes’, a reason must be given" reason_required: "Enter a reason if you've answered 'yes' to reasonable preference"
reason_not_required: "If reasonable preference is ‘no’, no reason should be given" reason_not_required: "Do not enter a reason if you've answered 'no' to reasonable preference"
underoccupation_benefitcap: underoccupation_benefitcap:
dont_know_required: "Answer must be ‘don’t know’ as you told us you don’t know the tenant’s main reason for leaving" dont_know_required: "Answer must be ‘don’t know’ as you told us you don’t know the tenant’s main reason for leaving"
reservist: reservist:
injury_required: "You must answer whether the person was seriously injured or ill as a result of serving in the UK armed forces" injury_required: "Tell us whether the person was seriously injured or ill as a result of serving in the UK armed forces"
injury_not_required: "You cannot answer this question as you told us the person has not served in the UK armed forces or prefers not to say" injury_not_required: "You cannot answer this question as you told us the person has not served in the UK armed forces or prefers not to say"
leftreg: leftreg:
question_required: "You must answer whether the person is still serving in the UK armed forces as you told us they’re a current or former regular" question_required: "Tell us whether the person is still serving in the UK armed forces as you told us they’re a current or former regular"
question_not_required: "You cannot answer whether the person is still serving in the UK armed forces as you told us they’re not a current or former regular" question_not_required: "You cannot answer whether the person is still serving in the UK armed forces as you told us they’re not a current or former regular"
preg_occ: preg_occ:
no_female: "You must answer ‘no’ as there are no female tenants aged 11-65 in the household" no_female: "Enter ‘no’ as there are no female tenants aged 11-65 in the household"
age: age:
retired_male: "Male tenant who is retired must be 65 or over" retired_male: "Male tenant who is retired must be 65 or over"
retired_female: "Female tenant who is retired must be 60 or over" retired_female: "Female tenant who is retired must be 60 or over"
@ -338,7 +336,7 @@ en:
name: "Location name (optional)" name: "Location name (optional)"
units: "Total number of units at this location" units: "Total number of units at this location"
type_of_unit: "What is the most common type of unit 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?" 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?" add_another_location: "Do you want to add another location?"
mobility_type: "What are the mobility standards for the majority of units in this location?" mobility_type: "What are the mobility standards for the majority of units in this location?"
descriptions: descriptions:

34
spec/components/log_summary_component_spec.rb

@ -0,0 +1,34 @@
require "rails_helper"
RSpec.describe LogSummaryComponent, type: :component do
let(:support_user) { FactoryBot.create(:user, :support) }
let(:coordinator_user) { FactoryBot.create(:user) }
let(:propcode) { "P3647" }
let(:tenancycode) { "T62863" }
let(:log) { FactoryBot.create(:case_log, needstype: 1, startdate: Time.utc(2022, 1, 1), tenancycode:, propcode:) }
context "when rendering log for a support user" do
it "show the log summary with organisational relationships" do
result = render_inline(described_class.new(current_user: support_user, log:))
expect(result).to have_link(log.id.to_s)
expect(result).to have_text(log.tenancycode)
expect(result).to have_text(log.propcode)
expect(result).to have_text("General needs")
expect(result).to have_text("Tenancy starts 1 January 2022")
expect(result).to have_text("Created 8 February 2022")
expect(result).to have_text("by Danny Rojas")
expect(result).to have_content("Owned by\n DLUHC")
expect(result).to have_content("Managed by\n DLUHC")
end
end
context "when rendering log for a data coordinator user" do
it "show the log summary" do
result = render_inline(described_class.new(current_user: coordinator_user, log:))
expect(result).not_to have_content("Owned by\n DLUHC")
expect(result).not_to have_content("Managed by\n DLUHC")
end
end
end

4
spec/components/search_result_caption_component_spec.rb

@ -10,7 +10,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
it "renders table caption including the search results and total" do it "renders table caption including the search results and total" do
result = render_inline(described_class.new(searched:, count:, item_label:, total_count:, item:, path:)) result = render_inline(described_class.new(searched:, count:, item_label:, total_count:, item:, path:))
expect(result.to_html).to eq(" <span class=\"govuk-!-margin-right-4\">\n <strong>#{count}</strong> #{item_label} found matching ‘#{searched}’ of <strong>#{total_count}</strong> total #{item}. <a class=\"govuk-link\" href=\"path\">Clear search</a>\n</span>\n") expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>#{count}</strong> #{item_label} found matching ‘#{searched}’ of <strong>#{total_count}</strong> total #{item}. <a class=\"govuk-link\" href=\"path\">Clear search</a>\n</span>\n")
end end
context "when no search results are found" do context "when no search results are found" do
@ -19,7 +19,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
it "renders table caption with total count only" do it "renders table caption with total count only" do
result = render_inline(described_class.new(searched:, count:, item_label:, total_count:, item:, path:)) result = render_inline(described_class.new(searched:, count:, item_label:, total_count:, item:, path:))
expect(result.to_html).to eq(" <span class=\"govuk-!-margin-right-4\">\n <strong>#{count}</strong> total #{item}.\n</span>\n") expect(result.to_html).to eq("<span class=\"govuk-!-margin-right-4\">\n <strong>#{count}</strong> total #{item}\n</span>\n")
end end
end end
end end

4
spec/features/schemes_spec.rb

@ -34,7 +34,7 @@ RSpec.describe "Schemes scheme Features" do
context "when I search for a specific scheme" do context "when I search for a specific scheme" do
it "there is a search bar with a message and search button for schemes" do it "there is a search bar with a message and search button for schemes" do
expect(page).to have_field("search") expect(page).to have_field("search")
expect(page).to have_content("Search by scheme name, code or postcode") expect(page).to have_content("Search by scheme name, code, postcode or location name")
expect(page).to have_button("Search") expect(page).to have_button("Search")
end end
@ -109,7 +109,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays a search bar" do it "displays a search bar" do
expect(page).to have_field("search") expect(page).to have_field("search")
expect(page).to have_content("Search by scheme name, code or postcode") expect(page).to have_content("Search by scheme name, code, postcode or location name")
expect(page).to have_button("Search") expect(page).to have_button("Search")
end end

4
spec/models/form_spec.rb

@ -149,9 +149,9 @@ RSpec.describe Form, type: :model do
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("household-needs/check-answers") expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("household-needs/check-answers")
end end
it "returns the first page of the next incomplete subsection (skipping completed subsections)" do it "returns the first page of the next incomplete subsection (skipping completed subsections, and pages that are not routed to)" do
answer_household_needs(case_log) answer_household_needs(case_log)
expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("accessible-select-too") expect(form.next_incomplete_section_redirect_path(subsection, case_log)).to eq("property-postcode")
end end
it "returns the declaration section for a completed case log" do it "returns the declaration section for a completed case log" do

6
spec/models/location_spec.rb

@ -32,7 +32,7 @@ RSpec.describe Location, type: :model do
it "does add an error when the postcode is invalid" do it "does add an error when the postcode is invalid" do
location.postcode = "invalid" location.postcode = "invalid"
expect { location.save! } expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode Enter a postcode in the correct format, for example AA1 1AA") .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode #{I18n.t('validations.postcode')}")
end end
end end
@ -42,7 +42,7 @@ RSpec.describe Location, type: :model do
it "does add an error when the postcode is invalid" do it "does add an error when the postcode is invalid" do
location.units = nil location.units = nil
expect { location.save! } expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Units Enter total number of units at this location") .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Units #{I18n.t('activerecord.errors.models.location.attributes.units.blank')}")
end end
end end
@ -52,7 +52,7 @@ RSpec.describe Location, type: :model do
it "does add an error when the postcode is invalid" do it "does add an error when the postcode is invalid" do
location.type_of_unit = nil location.type_of_unit = nil
expect { location.save! } expect { location.save! }
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Type of unit Select the most common type of unit at this location") .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Type of unit #{I18n.t('activerecord.errors.models.location.attributes.type_of_unit.blank')}")
end end
end end

14
spec/models/scheme_spec.rb

@ -45,6 +45,17 @@ RSpec.describe Scheme, type: :model do
end end
end end
context "when searching by location name" do
it "returns case insensitive matching records" do
expect(described_class.search_by_location_name(location.name.upcase).count).to eq(1)
expect(described_class.search_by_location_name(location.name.downcase).count).to eq(1)
expect(described_class.search_by_location_name(location.name.downcase).first.locations.first.name).to eq(location.name)
expect(described_class.search_by_location_name(location_2.name.upcase).count).to eq(1)
expect(described_class.search_by_location_name(location_2.name.downcase).count).to eq(1)
expect(described_class.search_by_location_name(location_2.name.downcase).first.locations.first.name).to eq(location_2.name)
end
end
context "when searching by all searchable fields" do context "when searching by all searchable fields" do
before do before do
location_2.update!(postcode: location_2.postcode.gsub(scheme_1.id.to_s, "0")) location_2.update!(postcode: location_2.postcode.gsub(scheme_1.id.to_s, "0"))
@ -59,6 +70,9 @@ RSpec.describe Scheme, type: :model do
expect(described_class.search_by(location.postcode.upcase).count).to eq(1) expect(described_class.search_by(location.postcode.upcase).count).to eq(1)
expect(described_class.search_by(location.postcode.downcase).count).to eq(1) expect(described_class.search_by(location.postcode.downcase).count).to eq(1)
expect(described_class.search_by(location.postcode.downcase).first.locations.first.postcode).to eq(location.postcode) expect(described_class.search_by(location.postcode.downcase).first.locations.first.postcode).to eq(location.postcode)
expect(described_class.search_by(location.name.upcase).count).to eq(1)
expect(described_class.search_by(location.name.downcase).count).to eq(1)
expect(described_class.search_by(location.name.downcase).first.locations.first.name).to eq(location.name)
end end
end end
end end

8
spec/requests/case_logs_controller_spec.rb

@ -172,10 +172,10 @@ RSpec.describe CaseLogsController, type: :request do
sign_in user sign_in user
end end
it "does have organisation columns" do it "does have organisation values" do
get "/logs", headers: headers, params: {} get "/logs", headers: headers, params: {}
expect(page).to have_content("Owning organisation") expect(page).to have_content("Owned by")
expect(page).to have_content("Managing organisation") expect(page).to have_content("Managed by")
end end
it "shows case logs for all organisations" do it "shows case logs for all organisations" do
@ -449,7 +449,7 @@ RSpec.describe CaseLogsController, type: :request do
end end
it "shows a table of logs" do it "shows a table of logs" do
expect(CGI.unescape_html(response.body)).to match(/<table class="govuk-table">/) expect(CGI.unescape_html(response.body)).to match(/<article class="app-log-summary">/)
expect(CGI.unescape_html(response.body)).to match(/logs/) expect(CGI.unescape_html(response.body)).to match(/logs/)
end end

3
spec/requests/locations_controller_spec.rb

@ -885,7 +885,8 @@ RSpec.describe LocationsController, type: :request do
end end
it "has search in the title" do it "has search in the title" do
expect(page).to have_title("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK") expected_title = CGI.escapeHTML("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title(expected_title)
end end
end end
end end

68
spec/requests/organisations_controller_spec.rb

@ -116,11 +116,6 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search") expect(page).to have_field("search", type: "search")
end end
it "has hidden accessibility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field)
end
it "shows only schemes belonging to the same organisation" do it "shows only schemes belonging to the same organisation" do
expect(page).to have_content(same_org_scheme.id_to_display) expect(page).to have_content(same_org_scheme.id_to_display)
schemes.each do |scheme| schemes.each do |scheme|
@ -897,7 +892,7 @@ RSpec.describe OrganisationsController, type: :request do
end end
it "shows the total organisations count" do it "shows the total organisations count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_organisations_count}</strong> total organisations.") expect(CGI.unescape_html(response.body)).to match("<strong>#{total_organisations_count}</strong> total organisations")
end end
it "has pagination links" do it "has pagination links" do
@ -1025,4 +1020,65 @@ RSpec.describe OrganisationsController, type: :request do
end end
end end
end end
context "when the user is a support user" do
let(:user) { FactoryBot.create(:user, :support) }
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
context "when they view the logs tab" do
before do
get "/organisations/#{organisation.id}/logs"
end
it "has a CSV download button with the correct path" do
expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/logs.csv")
end
context "when you download the CSV" do
let(:headers) { { "Accept" => "text/csv" } }
let(:other_organisation) { FactoryBot.create(:organisation) }
before do
FactoryBot.create_list(:case_log, 3, owning_organisation: organisation)
FactoryBot.create_list(:case_log, 2, owning_organisation: other_organisation)
end
it "only includes logs from that organisation" do
get "/organisations/#{organisation.id}/logs", headers:, params: {}
csv = CSV.parse(response.body)
expect(csv.count).to eq(4)
end
end
end
context "when they view the users tab" do
before do
get "/organisations/#{organisation.id}/users"
end
it "has a CSV download button with the correct path" do
expect(page).to have_link("Download (CSV)", href: "/organisations/#{organisation.id}/users.csv")
end
context "when you download the CSV" do
let(:headers) { { "Accept" => "text/csv" } }
let(:other_organisation) { FactoryBot.create(:organisation) }
before do
FactoryBot.create_list(:user, 3, organisation:)
FactoryBot.create_list(:user, 2, organisation: other_organisation)
end
it "only includes users from that organisation" do
get "/organisations/#{other_organisation.id}/users", headers:, params: {}
csv = CSV.parse(response.body)
expect(csv.count).to eq(3)
end
end
end
end
end end

13
spec/requests/schemes_controller_spec.rb

@ -90,12 +90,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "shows the total organisations count" do it "shows the total organisations count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{schemes.count}</strong> total schemes.") expect(CGI.unescape_html(response.body)).to match("<strong>#{schemes.count}</strong> total schemes")
end
it "has hidden accebility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field)
end end
context "when params scheme_id is present" do context "when params scheme_id is present" do
@ -118,7 +113,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "shows the total schemes count" do it "shows the total schemes count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes.") expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes")
end end
it "shows which schemes are being shown on the current page" do it "shows which schemes are being shown on the current page" do
@ -143,7 +138,7 @@ RSpec.describe SchemesController, type: :request do
end end
it "shows the total schemes count" do it "shows the total schemes count" do
expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes.") expect(CGI.unescape_html(response.body)).to match("<strong>#{total_schemes_count}</strong> total schemes")
end end
it "has pagination links" do it "has pagination links" do
@ -591,7 +586,7 @@ RSpec.describe SchemesController, type: :request do
it "does not allow the scheme to be confirmed" do it "does not allow the scheme to be confirmed" do
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("activerecord.errors.models.scheme.attributes.base.invalid")) expect(page).to have_content(I18n.t("activerecord.errors.models.scheme.attributes.managing_organisation_id.invalid"))
end end
end end

Loading…
Cancel
Save