Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .simplecov
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ if ENV.fetch("COVERAGE", 0).to_i.positive?

# However (possibly due to some residual random behaviour in test factories)
# the line coverage needs to be set 0.02 below the reported value.
# Normally this value needs to be 0.01 below the reported value due to rounding issues.
minimum_coverage line: 97.6, branch: 87.38
# Values from test run Fri 6th March 2026
# 97.7% (12705 / 13004) -> 308 + 53 = 361 lines uncovered
# 87.47% (2828 / 3233) -> 179 + 233 = 412 branches uncovered
# Nornmally this value needs to be 0.01 below the reported value due to rounding issues.
minimum_coverage line: 97.14, branch: 87.13
# Values from test run Fri 13th February 2026
# 97.46% (12553 / 12880) -> 327 lines uncovered
# 87.18% (2808 / 3221) -> 192 + 221 = 411 branches uncovered
end
end
82 changes: 56 additions & 26 deletions app/controllers/jobseekers/profiles/personal_details_controller.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
require "multistep/controller"

class Jobseekers::Profiles::PersonalDetailsController < Jobseekers::ProfilesController
include Multistep::Controller

multistep_form Jobseekers::Profile::PersonalDetailsForm, key: :personal_details_form

escape_path { jobseekers_profile_path }

def complete
render "review"
end

private

def store_form!
personal_details_record.assign_attributes(form.attributes.compact)
personal_details_record.save!
end

def personal_details_record
@personal_details_record ||= @profile.personal_details || @profile.build_personal_details
end

def attributes_from_store
personal_details_record.attributes.slice(*self.class.multistep_form.attribute_names)
module Jobseekers::Profiles
class PersonalDetailsController < Jobseekers::BaseController
include Wicked::Wizard

steps(*PersonalDetailsForm::FORMS.keys)

helper_method :escape_path, :back_url

before_action :set_personal_details_record

def show
@form = form_class.new(@personal_details_record.slice(form_class.fields))
render_wizard nil, params: { back_to_review: params[:back_to_review] }
end

def update
@form = form_class.new(params.fetch(form_key, {}).permit(*form_class.fields))
if @form.valid?
@personal_details_record.update!(@form.params_to_save.merge(completed_steps: @personal_details_record.completed_steps.merge(step => :completed)))
if params[:back_to_review]
redirect_to review_jobseekers_profile_personal_details_steps_path
elsif next_step == Wicked::FINISH_STEP
redirect_to finish_wizard_path
else
redirect_to next_wizard_path
end
else
render_wizard
end
end

def finish_wizard_path
review_jobseekers_profile_personal_details_steps_path
end

private

def back_url
previous_wizard_path
end

def form_key
ActiveModel::Naming.param_key(form_class)
end

def escape_path
jobseekers_profile_path
end

def form_class
PersonalDetailsForm::FORMS.fetch(step)
end

def set_personal_details_record
@personal_details_record = current_jobseeker.jobseeker_profile.personal_details || current_jobseeker.jobseeker_profile.build_personal_details
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/jobseekers/profiles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Jobseekers::ProfilesController < Jobseekers::BaseController
display_summary: ->(profile) { profile.personal_details&.completed_steps.present? },
key: "personal_details",
link_text: "Add personal details",
page_path: -> { personal_details_jobseekers_profile_path },
page_path: -> { jobseekers_profile_personal_details_step_path(Wicked::FIRST_STEP) },
},
{
title: "Job preferences",
Expand Down
30 changes: 0 additions & 30 deletions app/form_models/jobseekers/profile/personal_details_form.rb

This file was deleted.

90 changes: 90 additions & 0 deletions app/form_models/jobseekers/profiles/personal_details_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
module Jobseekers::Profiles
class PersonalDetailsForm
STEPS = { name: %i[first_name last_name],
phone_number: %i[phone_number_provided phone_number],
work: [:has_right_to_work_in_uk] }.freeze

class << self
def from_record(record)
new record
end

# TODO: - simplify view so this method can be deleted
def delegated_attributes
STEPS.invert.map { |kl, v| kl.map { |k| { k => v } }.reduce(&:merge) }.reduce(&:merge)
end
end

def initialize(record)
@personal_details = record
end

def next_invalid_step
FORMS.drop_while { |step, form_class|
form_class.new(@personal_details.slice(STEPS.fetch(step))).valid?
}.first.first
end

class PersonalDetailsForm < ::BaseForm
include ActiveModel::Model
include ActiveModel::Attributes
end

class NamesForm < PersonalDetailsForm
attribute :first_name
attribute :last_name

validates :first_name, :last_name, presence: true

class << self
def fields
%i[first_name last_name]
end
end

def params_to_save
{ first_name: first_name, last_name: last_name }
end
end

class PhoneNumberForm < PersonalDetailsForm
attribute :phone_number_provided
attribute :phone_number

validates :phone_number_provided, presence: true
validates :phone_number, presence: true, format: { with: /\A\+?(?:\d\s?){10,13}\z/ }, if: -> { phone_number_provided == "true" }

class << self
def fields
%i[phone_number_provided phone_number]
end
end

def params_to_save
{ phone_number: phone_number, phone_number_provided: phone_number_provided }
end
end

class WorkForm < PersonalDetailsForm
attribute :has_right_to_work_in_uk, :boolean

validates :has_right_to_work_in_uk, inclusion: { in: [true, false] }

class << self
def fields
[:has_right_to_work_in_uk]
end
end

def params_to_save
{ has_right_to_work_in_uk: has_right_to_work_in_uk }
end
end

FORMS = {
name: NamesForm,
phone_number: PhoneNumberForm,
work: WorkForm,
}.freeze
end
end
23 changes: 12 additions & 11 deletions app/views/jobseekers/profiles/personal_details/_summary.html.slim
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
ruby:
form = Jobseekers::Profile::PersonalDetailsForm.from_record(@profile.personal_details)
model = profile.personal_details
form = Jobseekers::Profiles::PersonalDetailsForm.from_record(model)

attributes = {
phone_number_provided: { map_values: ->(value) { value.nil? ? "" : t("helpers.label.personal_details_form.phone_number_provided_options.#{value}") } },
phone_number_provided: { map_values: ->(value) { value.nil? ? "" : t("helpers.label.jobseekers_profiles_personal_details_form_phone_number_form.phone_number_provided_options.#{value}") } },
phone_number: nil,
has_right_to_work_in_uk: { map_values: ->(value) { value.nil? ? "" : t("jobseekers.profiles.personal_details.work.options.#{value}") } },
}

summary = govuk_summary_list(html_attributes: { id: "personal_details" }) do |summary_list|
summary_list.with_row do |row|
row.with_key text: "Name"
row.with_value text: "#{form.first_name} #{form.last_name}"
row.with_action(text: t("buttons.change"), href: edit_personal_details_jobseekers_profile_path(step: "name"), visually_hidden_text: " name")
row.with_value text: "#{model.first_name} #{model.last_name}"
row.with_action(text: t("buttons.change"), href: jobseekers_profile_personal_details_step_path("name", back_to_review: true), visually_hidden_text: " name")
end

attributes.each do |attribute, options|
step_name = form.class.delegated_attributes[attribute.to_s]
value = form.public_send(attribute)
step_name = form.class.delegated_attributes[attribute]
value = model.public_send(attribute)

if options && options[:map_values]
value = options[:map_values].call(value)
Expand All @@ -25,25 +26,25 @@ ruby:
summary_list.with_row do |row|
row.with_key text: t("jobseekers.profiles.personal_details.summary.#{attribute}")
row.with_value text: value
row.with_action(text: t("buttons.change"), href: edit_personal_details_jobseekers_profile_path(step: step_name), visually_hidden_text: attribute.to_s.humanize)
row.with_action(text: t("buttons.change"), href: jobseekers_profile_personal_details_step_path(step_name, back_to_review: true), visually_hidden_text: attribute.to_s.humanize)
end
end

summary_list.with_row do |row|
row.with_key text: t("jobseekers.profiles.personal_details.summary.email")
row.with_value text: (current_jobseeker.email +
govuk_inset_text(text: t("helpers.label.personal_details_form.change_email_html",
link: govuk_link_to(t("helpers.label.personal_details_form.change_email_link_text"),
govuk_inset_text(text: t("helpers.label.jobseekers_profiles_personal_details_form_names_form.change_email_html",
link: govuk_link_to(t("helpers.label.jobseekers_profiles_personal_details_form_names_form.change_email_link_text"),
jobseekers_account_path)),
classes: "govuk-!-margin-top-3")).html_safe
end
end

- if form.completed?
- if model.complete?
= summary
- else
.govuk-inset-text.govuk-inset-text--incomplete
= summary
p.govuk-body-m.govuk-inset-text--header
strong = t("jobseekers.profiles.personal_details.incomplete_message")
= govuk_link_to t("buttons.complete_personal_details"), edit_personal_details_jobseekers_profile_path(step: form.next_step), class: "govuk-button"
= govuk_link_to t("buttons.complete_personal_details"), jobseekers_profile_personal_details_step_path(form.next_invalid_step), class: "govuk-button"
26 changes: 12 additions & 14 deletions app/views/jobseekers/profiles/personal_details/name.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
- content_for :breadcrumbs do
= govuk_back_link text: t("buttons.back"), href: escape_path, html_attributes: { "aria-label" => "Back navigation" }

.govuk-grid-row
.govuk-grid-column-two-thirds
= form_for @step, url: { action: :update }, method: :post, as: :personal_details_form do |f|
= f.govuk_error_summary
= form_for @form, url: wizard_path(step, back_to_review: params[:back_to_review]), method: :patch do |f|
= f.govuk_error_summary

= f.govuk_fieldset legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
= f.govuk_text_field :first_name,
label: { size: "m" },
autocomplete: "given-name"
= f.govuk_fieldset legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
= f.govuk_text_field :first_name,
label: { size: "m" },
autocomplete: "given-name"

= f.govuk_text_field :last_name,
label: { size: "m" },
autocomplete: "family-name"
= f.govuk_text_field :last_name,
label: { size: "m" },
autocomplete: "family-name"

= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"
= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"

.govuk-button-group
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path
.govuk-button-group
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@
- content_for :breadcrumbs do
= govuk_back_link text: t("buttons.back"), href: back_url, html_attributes: { "aria-label" => "Back navigation" }

.govuk-grid-row
.govuk-grid-column-two-thirds
= form_for @step, url: { action: :update }, method: :post, as: :personal_details_form do |f|
= f.govuk_error_summary
= form_for @form, url: wizard_path(step, back_to_review: params[:back_to_review]), method: :patch do |f|
= f.govuk_error_summary

= f.govuk_radio_buttons_fieldset :phone_number_provided, legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
= f.govuk_radio_button :phone_number_provided, "true", link_errors: true do
= f.govuk_text_field :phone_number,
label: { class: "govuk-label govuk-label--s" },
autocomplete: "tel"
= f.govuk_radio_buttons_fieldset :phone_number_provided, legend: { text: t(".heading"), tag: "h1", size: "l" }, caption: { text: t(".caption"), size: "l" } do
= f.govuk_radio_button :phone_number_provided, "true", link_errors: true do
= f.govuk_text_field :phone_number,
label: { class: "govuk-label govuk-label--s" },
autocomplete: "tel"

= f.govuk_radio_button :phone_number_provided, "false"
= f.govuk_radio_button :phone_number_provided, "false"

= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"
= f.govuk_submit t("buttons.save_and_continue"), class: "govuk-!-margin-bottom-5"

.govuk-button-group
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path
.govuk-button-group
= govuk_link_to t("buttons.cancel"), jobseekers_profile_path
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
- content_for :page_title_prefix, t(".page_title")

.govuk-grid-row
.govuk-grid-column-two-thirds
h1.govuk-heading-l = t(".heading")
h1.govuk-heading-l = t(".heading")

= render "summary"
= render "summary", profile: @personal_details_record.jobseeker_profile

= govuk_button_link_to t("buttons.return_to_profile"), jobseekers_profile_path, classes: "govuk-!-margin-bottom-5"
= govuk_button_link_to t("buttons.return_to_profile"), jobseekers_profile_path, classes: "govuk-!-margin-bottom-5"
Loading
Loading