diff --git a/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb b/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb new file mode 100644 index 0000000000..27f12d2f4d --- /dev/null +++ b/app/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form.rb @@ -0,0 +1,27 @@ +module Journeys + module EarlyYearsPayment + module Provider + module Authenticated + class ClaimantNameForm < Form + attribute :first_name + attribute :surname + + validates :first_name, presence: {message: i18n_error_message("first_name.presence")} + validates :first_name, length: {maximum: 100, message: i18n_error_message("first_name.length")}, if: -> { first_name.present? } + validates :first_name, name_format: {message: i18n_error_message("first_name.format")} + + validates :surname, presence: {message: i18n_error_message("last_name.presence")} + validates :surname, length: {maximum: 100, message: i18n_error_message("last_name.length")}, if: -> { surname.present? } + validates :surname, name_format: {message: i18n_error_message("last_name.format")} + + def save + return false if invalid? + + journey_session.answers.assign_attributes(first_name:, surname:) + journey_session.save! + end + end + end + end + end +end diff --git a/app/forms/personal_details_form.rb b/app/forms/personal_details_form.rb index cd255e293d..2999451e78 100644 --- a/app/forms/personal_details_form.rb +++ b/app/forms/personal_details_form.rb @@ -5,7 +5,6 @@ def future? end end - NAME_REGEX_FILTER = /\A[^"=$%#&*+\/\\()@?!<>0-9]*\z/ NINO_REGEX_FILTER = /\A[A-Z]{2}[0-9]{6}[A-D]{1}\Z/ DOB_PARAM_CONVERSION = { "date_of_birth(3i)" => "day", @@ -23,19 +22,16 @@ def future? attribute :national_insurance_number validates :first_name, presence: {message: "Enter your first name"} - validates :first_name, - length: {maximum: 100, message: "First name must be less than 100 characters"}, - format: {with: NAME_REGEX_FILTER, message: "First name cannot contain special characters"}, - if: -> { first_name.present? } - validates :middle_name, - length: {maximum: 61, message: "Middle names must be less than 61 characters"}, - format: {with: NAME_REGEX_FILTER, message: "Middle names cannot contain special characters"}, - if: -> { middle_name.present? } + validates :first_name, length: {maximum: 100, message: "First name must be less than 100 characters"}, if: -> { first_name.present? } + validates :first_name, name_format: {message: "First name cannot contain special characters"} + + validates :middle_name, length: {maximum: 61, message: "Middle names must be less than 61 characters"}, if: -> { middle_name.present? } + validates :middle_name, name_format: {message: "Middle names cannot contain special characters"} + validates :surname, presence: {message: "Enter your last name"} - validates :surname, - length: {maximum: 100, message: "Last name must be less than 100 characters"}, - format: {with: NAME_REGEX_FILTER, message: "Last name cannot contain special characters"}, - if: -> { surname.present? } + validates :surname, length: {maximum: 100, message: "Last name must be less than 100 characters"}, if: -> { surname.present? } + validates :surname, name_format: {message: "Last name cannot contain special characters"} + validate :date_of_birth_criteria validates :national_insurance_number, presence: {message: "Enter a National Insurance number in the correct format"} validate :ni_number_is_correct_format diff --git a/app/models/journeys/early_years_payment/provider/authenticated.rb b/app/models/journeys/early_years_payment/provider/authenticated.rb index 3d4aca0ac2..9bac7f3cbc 100644 --- a/app/models/journeys/early_years_payment/provider/authenticated.rb +++ b/app/models/journeys/early_years_payment/provider/authenticated.rb @@ -12,7 +12,8 @@ module Authenticated FORMS = { "claims" => { "consent" => ConsentForm, - "current-nursery" => CurrentNurseryForm + "current-nursery" => CurrentNurseryForm, + "claimant-name" => ClaimantNameForm } } START_WITH_MAGIC_LINK = true diff --git a/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb b/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb index 2a28e3cc9b..69b725ac84 100644 --- a/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb +++ b/app/models/journeys/early_years_payment/provider/authenticated/slug_sequence.rb @@ -7,6 +7,7 @@ class SlugSequence consent current-nursery claimant-name + start-date ineligible ].freeze diff --git a/app/models/name_format_validator.rb b/app/models/name_format_validator.rb new file mode 100644 index 0000000000..1fb21104e0 --- /dev/null +++ b/app/models/name_format_validator.rb @@ -0,0 +1,11 @@ +class NameFormatValidator < ActiveModel::EachValidator + NAME_REGEX_FILTER = /\A[^\[\]\^"=$%#&*+\/\\()@?!<>_`|{}~0-9]*\z/ + + def validate_each(record, attribute, value) + return unless value.present? + + unless NAME_REGEX_FILTER.match?(value) + record.errors.add(attribute, options[:message]) + end + end +end diff --git a/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb b/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb index e69de29bb2..6520031c1a 100644 --- a/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb +++ b/app/views/early_years_payment/provider/authenticated/claims/claimant_name.html.erb @@ -0,0 +1,23 @@ +<% content_for(:page_title, page_title(@form.t(:question), journey: current_journey_routing_name, show_error: @form.errors.any?)) %> + +
+
+ <%= form_with model: @form, url: claim_path(current_journey_routing_name), method: :patch, builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %> + <%= f.govuk_error_summary %> + +

+ <%= @form.t(:question) %> +

+ +
+ <%= @form.t(:hint) %> +
+ + <%= f.govuk_text_field :first_name, label: { text: "First name" }, spellcheck: "false", autocomplete: "name" %> + + <%= f.govuk_text_field :surname, label: { text: "Last name" }, spellcheck: "false", autocomplete: "name" %> + + <%= f.govuk_submit "Continue" %> + <% end %> +
+
diff --git a/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb b/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb new file mode 100644 index 0000000000..136d06384a --- /dev/null +++ b/app/views/early_years_payment/provider/authenticated/claims/start_date.html.erb @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index fb713aaf31..ffb5604294 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -865,7 +865,9 @@ en: min_length: Enter a college name or postcode that is at least 3 characters long select_provision: heading: Select where you are employed - hint: Select the further education provider you are employed by. If you work for a college or sixth-form group, you should select the group name. + hint: + Select the further education provider you are employed by. If you work for a college or sixth-form group, you + should select the group name. errors: blank: Select where you are employed contract_type: @@ -940,7 +942,8 @@ en: question: Which building and construction courses do you teach? question_check_your_answers: Building and construction courses options: - level3_buildingconstruction_approved: Qualifications approved for funding at level 3 and below in the %{link} sector subject area + level3_buildingconstruction_approved: + Qualifications approved for funding at level 3 and below in the %{link} sector subject area tlevel_building: T Level in building services engineering for construction tlevel_onsiteconstruction: T Level in onsite construction tlevel_design_surveying: T Level in design, surveying and planning for construction @@ -953,7 +956,9 @@ en: options: alevel_chemistry: A or AS level chemistry gcse_chemistry: GCSE chemistry - ibo_level_3_chemistry: IBO level 3 SL and HL chemistry, taught as part of a diploma or career related programme or as a standalone certificate + ibo_level_3_chemistry: + IBO level 3 SL and HL chemistry, taught as part of a diploma or career related programme or as a standalone + certificate ibo_level_1_2_myp_chemistry: IBO level 1 / level 2 MYP chemistry none: I do not teach any of these courses computing_courses: @@ -986,9 +991,12 @@ en: question: Which engineering and manufacturing courses do you teach? question_check_your_answers: Engineering and manufacturing courses options: - approved_level_321_engineering: Qualifications approved for funding at level 3 and below in the %{link} sector subject area - approved_level_321_manufacturing: Qualifications approved for funding at level 3 and below in the %{link} sector subject area - approved_level_321_transportation: Qualifications approved for funding at level 3 and below in the %{link} sector subject area + approved_level_321_engineering: + Qualifications approved for funding at level 3 and below in the %{link} sector subject area + approved_level_321_manufacturing: + Qualifications approved for funding at level 3 and below in the %{link} sector subject area + approved_level_321_transportation: + Qualifications approved for funding at level 3 and below in the %{link} sector subject area tlevel_design: T Level in design and development for engineering and manufacturing tlevel_maintenance: T Level in maintenance, installation and repair for engineering and manufacturing tlevel_engineering: T Level in engineering, manufacturing, processing and control @@ -999,9 +1007,11 @@ en: question: Which maths courses do you teach? question_check_your_answers: Maths courses options: - approved_level_321_maths: Qualifications approved for funding at level 3 and below in the %{link} sector subject area + approved_level_321_maths: + Qualifications approved for funding at level 3 and below in the %{link} sector subject area gcse_maths: - GCSE in maths, functional skills qualifications and %{link} approved for teaching to 16 to 19-year-olds who meet the condition of funding + GCSE in maths, functional skills qualifications and %{link} approved for teaching to 16 to 19-year-olds who + meet the condition of funding none: I do not teach any of these courses physics_courses: <<: *courses @@ -1011,7 +1021,9 @@ en: alevel_physics: A or AS level physics gcse_physics: GCSE physics ibo_level_1_2_myp_physics: IBO level 1 / level 2 MYP physics - ibo_level_3_physics: IBO level 3 in SL and HL physics, taught as part of a diploma or career related programme or as a standalone certificate + ibo_level_3_physics: + IBO level 3 in SL and HL physics, taught as part of a diploma or career related programme or as a standalone + certificate none: I do not teach any of these courses teaching_qualification: question: Do you have a teaching qualification? @@ -1105,6 +1117,20 @@ en: question: Confirm that your employee works at the location below errors: presence: Select the nursery where your employee works + claimant_name: + question: Who are you making this claim for? + hint: + Enter the employee’s full legal name as it appears on official identity documents such as passports or driving + licenses. + errors: + first_name: + presence: Enter employee’s first name + length: Employee’s first name must be less than 100 characters + format: Employee’s first name cannot contain special characters + last_name: + presence: Enter employee’s last name + length: Employee’s last name must be less than 100 characters + format: Employee’s last name cannot contain special characters early_years_payments: <<: *early_years_payment_provider_authenticated claim_subject: "Early Years Payment" diff --git a/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb b/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb index d8870aea02..711b0f476b 100644 --- a/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb +++ b/spec/features/early_years_payment/provider/authenticated/happy_path_spec.rb @@ -15,6 +15,7 @@ expect(journey_session.reload.answers.email_address).to eq email_address expect(journey_session.reload.answers.email_verified).to be true expect(page).to have_content("Declaration of Employee Consent") + check "I confirm that I have obtained consent from my employee and have provided them with the relevant privacy notice." click_button "Continue" expect(page.current_path).to eq "/early-years-payment-provider/current-nursery" @@ -22,6 +23,11 @@ choose nursery.nursery_name click_button "Continue" expect(page.current_path).to eq "/early-years-payment-provider/claimant-name" + + fill_in "First name", with: "Bobby" + fill_in "Last name", with: "Bobberson" + click_button "Continue" + expect(page.current_path).to eq "/early-years-payment-provider/start-date" end scenario "using magic link after having completed some of the journey" do diff --git a/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb b/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb new file mode 100644 index 0000000000..5e3823430e --- /dev/null +++ b/spec/forms/journeys/early_years_payment/provider/authenticated/claimant_name_form_spec.rb @@ -0,0 +1,50 @@ +require "rails_helper" + +RSpec.describe Journeys::EarlyYearsPayment::Provider::Authenticated::ClaimantNameForm, type: :model do + let(:journey) { Journeys::EarlyYearsPayment::Provider::Authenticated } + let(:journey_session) { create(:early_years_payment_provider_authenticated_session) } + let(:first_name) { nil } + let(:surname) { nil } + + let(:params) do + ActionController::Parameters.new( + claim: { + first_name:, + surname: + } + ) + end + + subject do + described_class.new(journey_session:, journey:, params:) + end + + describe "validations" do + # First name validations + it { should validate_presence_of(:first_name).with_message("Enter employee’s first name") } + it { should validate_length_of(:first_name).is_at_most(100).with_message("Employee’s first name must be less than 100 characters") } + it { should allow_value("O'Brian").for(:first_name) } + %w[* | { ^ /].each do |char| + it { should_not allow_value(char).for(:first_name).with_message("Employee’s first name cannot contain special characters") } + end + + # Surname validations + it { should validate_presence_of(:surname).with_message("Enter employee’s last name") } + it { should validate_length_of(:surname).is_at_most(100).with_message("Employee’s last name must be less than 100 characters") } + it { should_not allow_value("5").for(:surname).with_message("Employee’s last name cannot contain special characters") } + it { should allow_value("O'Brian").for(:surname) } + end + + describe "#save" do + let(:first_name) { "Bobby" } + let(:surname) { "Bobberson" } + + it "updates the journey session" do + expect { subject.save }.to( + change { journey_session.answers.first_name }.to(first_name).and( + change { journey_session.answers.surname }.to(surname) + ) + ) + end + end +end