Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
22bb08e
Add a method for loading an Incomplete Submission into session memory
chao-xian Oct 21, 2026
483a5e0
Add a method for setting onto the session store the submission reference
chao-xian Oct 21, 2026
6fa23ff
Add LoadFormProgressService
chao-xian Oct 21, 2026
034ba82
Add routes for ResumeProgress controller
chao-xian Oct 23, 2026
79d0b74
Add routing for Forms::ResumeOptionsController
chao-xian Oct 23, 2026
29ac48e
Update base_controller to also handle redirecting for save and return
chao-xian Oct 28, 2026
b7cd429
Add spec that checks logging correctly occurs in BaseController
chao-xian Oct 28, 2026
36eb012
Add spec and stub files for ResumeOptions screen
chao-xian Oct 28, 2026
2ca2c7e
Update ResumeOptionsController handle default behaviour
chao-xian Oct 28, 2026
9047408
Add view for showing user options for resuming or starting a submission
chao-xian Oct 29, 2026
73c8626
Add ResumeOptionsInput input object and spec
chao-xian Oct 29, 2026
c0c375b
Update show view for ResumeOptions screen
chao-xian Oct 29, 2026
6a57288
Update ResumeOptionsController to send filler to chosen path
chao-xian Oct 29, 2026
2530b45
Rename ResumeProgressController to ContinueSavedFormController
chao-xian Oct 30, 2026
c16376e
Update ContinueSavedFormController and views
chao-xian Oct 30, 2026
ba76f5b
Update to handle continuing a saved form submission when user is not …
chao-xian Oct 30, 2026
0b62244
Update resuming journey after user logs in
chao-xian Oct 30, 2026
c97751b
Update content for parts of Save and return
chao-xian Oct 30, 2026
abfa9c0
Update view spec to test for a button to continue filling in a form
chao-xian Oct 30, 2026
1651da3
Add feature spec for continuing submitting a form
chao-xian Nov 2, 2026
865a6ad
Fix clearing session data when choosing Start a new form
chao-xian Nov 3, 2026
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
5 changes: 4 additions & 1 deletion app/controllers/forms/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ class BaseController < ApplicationController
around_action :set_locale

def redirect_to_friendly_url_start
redirect_to form_step_path(params.require(:form_id), @form.form_slug, @form.start_page)
LogEventService.log_form_start unless mode.preview?

return redirect_to resume_options_path(@form.id, @form.form_slug) if @form.save_and_return_enabled?

redirect_to form_step_path(params.require(:form_id), @form.form_slug, @form.start_page)
end

rescue_from ActiveResource::ResourceNotFound, RepeatableStep::AnswerIndexError do
Expand Down
43 changes: 43 additions & 0 deletions app/controllers/forms/continue_saved_form_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Forms
class ContinueSavedFormController < BaseController
before_action :redirect_if_save_and_return_disabled

def new
unless auth_service.logged_in?
auth_service.store_return_params(
form: current_context.form,
mode:,
locale: locale_param,
return_to: Store::ReturnFromOneLoginStore::RETURN_TO_VALUES[:continue_saved_form],
)
return
end

load_saved_progress_and_redirect
end

def signed_in
unless auth_service.logged_in? && auth_service.return_to == Store::ReturnFromOneLoginStore::RETURN_TO_VALUES[:continue_saved_form]
return redirect_to(continue_saved_form_path(current_context.form.id, current_context.form.form_slug))
end

auth_service.clear_return_from_one_login
load_saved_progress_and_redirect
end

private

def load_saved_progress_and_redirect
incomplete_submission = LoadFormProgressService.new(current_context:, user_id: auth_service.sub).load!
return render :not_found, status: :not_found unless incomplete_submission

redirect_to form_step_path(current_context.form.id, current_context.form.form_slug, current_context.form.start_page)
end

def redirect_if_save_and_return_disabled
return if current_context.form.save_and_return_enabled?

redirect_to form_step_path(current_context.form.id, current_context.form.form_slug, current_context.next_step_slug)
end
end
end
41 changes: 41 additions & 0 deletions app/controllers/forms/resume_options_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module Forms
class ResumeOptionsController < BaseController
before_action :back_to_start
def show
@resume_options_input = ResumeOptionsInput.new
end

def continue
@resume_options_input = ResumeOptionsInput.new(resume_options_params)

unless @resume_options_input.valid?
return render :show, status: :unprocessable_entity
end

return start_new_form unless @resume_options_input.continue_saved?

redirect_to continue_saved_form_path(form_id: @current_context.form.id, form_slug: @current_context.form.form_slug)
end

private

def start_new_form
current_context.clear
current_context.clear_submission_details

redirect_to form_step_path(current_context.form.id, current_context.form.form_slug, current_context.form.start_page)
end

def back_to_start
unless @current_context.form.save_and_return_enabled?
redirect_to form_step_path(@current_context.form.id,
@current_context.form.form_slug,
@current_context.form.start_page)
end
end

def resume_options_params
params.require(:resume_options_input).permit(:resume_options)
end
end
end
2 changes: 2 additions & 0 deletions app/controllers/users/omniauth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def logged_out
def redirect_after_sign_in
if auth_service.return_to == Store::ReturnFromOneLoginStore::RETURN_TO_VALUES[:save_and_return]
redirect_to save_progress_after_sign_in_path(**auth_service.form_path_params)
elsif auth_service.return_to == Store::ReturnFromOneLoginStore::RETURN_TO_VALUES[:continue_saved_form]
redirect_to continue_saved_form_signed_in_path(**auth_service.form_path_params)
else # "copy_of_answers" as the default to help avoid regression issues when deploying for current users
redirect_to check_your_answers_path(**auth_service.form_path_params)
end
Expand Down
18 changes: 18 additions & 0 deletions app/input_objects/resume_options_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class ResumeOptionsInput
include ActiveModel::Model
include ActiveModel::Validations

attr_accessor :resume_options

RADIO_OPTIONS = { start_new: "start_new", continue_saved: "continue_saved" }.freeze

validates :resume_options, presence: true, inclusion: { in: RADIO_OPTIONS.values }

def continue_saved?
resume_options == RADIO_OPTIONS[:continue_saved]
end

def values
RADIO_OPTIONS.keys
end
end
3 changes: 2 additions & 1 deletion app/lib/flow/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ def initialize(form:, form_document:, store:)

delegate :support_details, to: :form
delegate :step_by_id, :previous_step, :next_step_slug, :next_step, :can_visit?, :completed_steps, :all_steps, to: :journey
delegate :clear_stored_answer, :clear, :form_submitted?, :starting_new_form?, :answers, to: :answer_store
delegate :clear_stored_answer, :clear, :form_submitted?, :load_answers, :add_locale, :starting_new_form?, :answers, to: :answer_store
delegate :generate_submission_reference,
:get_submission_reference,
:set_submission_reference,
:save_requested_email_confirmation,
:requested_email_confirmation?,
:clear_submission_details,
Expand Down
1 change: 1 addition & 0 deletions app/lib/store/return_from_one_login_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def initialize(message = "Return from One Login parameters are missing from the
RETURN_TO_VALUES = {
copy_of_answers: "copy_of_answers",
save_and_return: "save_and_return",
continue_saved_form: "continue_saved_form",
}.freeze

def initialize(store)
Expand Down
4 changes: 4 additions & 0 deletions app/lib/store/session_answer_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def save_step(step, answer)
@store[ANSWERS_KEY][@form_key][page_key(step)] = answer
end

def load_answers(saved_answers)
@store[ANSWERS_KEY][@form_key] = saved_answers
end

def get_stored_answer(step)
@store.dig(ANSWERS_KEY, @form_key, page_key(step))
end
Expand Down
5 changes: 5 additions & 0 deletions app/lib/store/submission_details_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def get_submission_reference
@store.dig(CONFIRMATION_KEY, @form_key, SUBMISSION_REFERENCE_KEY.to_s)
end

def set_submission_reference(reference)
@store[CONFIRMATION_KEY][@form_key] ||= {}
@store[CONFIRMATION_KEY][@form_key][SUBMISSION_REFERENCE_KEY.to_s] = reference
end

def requested_email_confirmation?
@store.dig(CONFIRMATION_KEY, @form_key, REQUESTED_EMAIL_CONFIRMATION_KEY.to_s)
end
Expand Down
2 changes: 1 addition & 1 deletion app/mailers/progress_saved_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def progress_saved_email(
end

def resume_form_link(form, mode, locale: nil)
path = Rails.application.routes.url_helpers.form_path(mode:, form_id: form.id, form_slug: form.form_slug, locale:)
path = Rails.application.routes.url_helpers.continue_saved_form_path(mode:, form_id: form.id, form_slug: form.form_slug, locale:)
"#{Settings.forms_runner.base_url}#{path}"
end

Expand Down
17 changes: 17 additions & 0 deletions app/services/load_form_progress_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class LoadFormProgressService
def initialize(current_context:, user_id:)
@current_context = current_context
@user_id = user_id
end

def load!
incomplete_submission = IncompleteSubmission.find_by(user_id: @user_id, form_id: @current_context.form.id)
return nil unless incomplete_submission
Comment on lines +8 to +9

@current_context.load_answers(incomplete_submission.answers)
@current_context.set_submission_reference(incomplete_submission.submission_reference)
@current_context.add_locale(incomplete_submission.locale)
Comment on lines +11 to +13

incomplete_submission
end
end
16 changes: 16 additions & 0 deletions app/views/forms/continue_saved_form/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<% set_page_title(form_title(form_name: @current_context.form.name, page_name: t('.title'), mode: @mode)) %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l"><%= t('.title') %></h1>

<%= t('.body_html') %>

<%= govuk_button_to(
t('.continue_to_one_login_button'),
"/auth/govuk_one_login",
method: :post,
form: { data: { turbo: false } },
) %>
</div>
</div>
19 changes: 19 additions & 0 deletions app/views/forms/continue_saved_form/not_found.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<% set_page_title(form_title(form_name: @current_context.form.name, page_name: t('.title'), mode: @mode)) %>

<% content_for :back_link do %>
<%= link_to t("forms.back"), resume_options_path(@form.id, @form.form_slug), class: "govuk-back-link" %>
<% end %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l"><%= t('.title') %></h1>

<%= t('.body_html') %>

<%= govuk_button_link_to t('.start_new_form_button'), form_step_path(@form.id, @form.form_slug, @form.start_page) %>

<p class="govuk-body">
<%= link_to t('.sign_in_different_link'), "#", class: "govuk-link" %>
</p>
</div>
</div>
23 changes: 23 additions & 0 deletions app/views/forms/resume_options/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<% set_page_title(form_title(form_name: @current_context.form.name, page_name: t('.title'), mode: @mode)) %>


<%= form_with(model: @resume_options_input, url: resume_options_continue_path(form_id: @form.id, form_slug: @form.form_slug)) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<% if @resume_options_input&.errors&.any? %>
<%= f.govuk_error_summary(t("error_summary_title")) %>
<% end %>

<%= f.govuk_collection_radio_buttons :resume_options,
@resume_options_input.values,
->(option) { option },
->(option) { t("helpers.label.resume_options_input.options.#{option}") },
->(option) { t("helpers.hint.resume_options_input.options.#{option}", default: nil) },
legend: { text: t('.title'), tag: 'h1', size: 'l' },
bold_labels: false %>


<%= f.govuk_submit(t("continue")) %>
</div>
</div>
<% end %>
5 changes: 4 additions & 1 deletion app/views/forms/save_progress/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
<% banner.with_heading(text: t('.title')) %>
<% end %>

<%= t('.body_html', email: @one_login_email, deletion_date: @deletion_date, form_url: form_url(@current_context.form.id, @current_context.form.form_slug)) %>
<%= t('.body_html', email: @one_login_email, deletion_date: @deletion_date, form_url: continue_saved_form_url(@current_context.form.id, @current_context.form.form_slug)) %>

<%= govuk_button_link_to t('.continue_button'), form_step_path(@current_context.form.id, @current_context.form.form_slug, @current_context.next_step_slug) %>

<p class="govuk-body"><%= link_to t('.sign_out_link'), "#" %></p>
</div>
</div>
27 changes: 26 additions & 1 deletion config/locales/cy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ cy:
attributes:
remove:
blank: Rhaid i chi ddewis opsiwn
resume_options_input:
attributes:
resume_options:
blank: Select whether you want to start a new form or continue filling in a saved form
autocomplete:
assistive_hint: 'Pan fo canlyniadau awtolenwi ar gael, defnyddiwch y saethau i fyny ac i lawr i adolygu a defnyddiwch ‘enter’ i ddewis. Defnyddwyr dyfeisiau cyffwrdd: chwiliwch drwy gyffwrdd neu sweipio'
no_results: Dim canlyniadau wedi’u canfod
Expand Down Expand Up @@ -399,6 +403,16 @@ cy:
view_english_form: 'Gallwch lenwi’r ffurflen yn Saesneg o hyd:'
welsh_version_archived: Mae’r fersiwn Gymraeg o’r ffurflen hon wedi’i harchifo ac nid yw ar gael mwyach.
back: Yn ôl
continue_saved_form:
new:
body_html: "<p>We use GOV.UK One Login to keep your information secure.</p><p>Sign in to your GOV.UK One Login to continue filling in your saved form.</p>"
continue_to_one_login_button: Continue to GOV.UK One Login
title: Sign in to your GOV.UK One Login
not_found:
body_html: <p class="govuk-body">This could be because:</p><ul class="govuk-list govuk-list--bullet"><li>you did not save your progress for this form</li><li>you used a different GOV.UK One Login to save the form</li><li>it has been more than 30 days since you saved the form (we delete saved forms after 30 days)</li></ul>
sign_in_different_link: Sign in with a different GOV.UK One Login
start_new_form_button: Start a new form
title: We could not find your saved form
continue_to_one_login:
show:
body_html: "<p>Bydd yr e-bost gyda’ch atebion yn cael ei anfon i’r cyfeiriad e-bost sy’n gysylltiedig â’ch GOV.UK One Login.</p><p>Os ydych angen i’r atebion gael eu hanfon i gyfeiriad e-bost gwahanol - er enghraifft, eich e-bost gwaith - gallwch greu GOV.UK One Login newydd.</p>"
Expand All @@ -425,6 +439,9 @@ cy:
show:
radios_legend: Ydych chi’n siŵr yr hoffech chi dynnu’ch ffeil?
title: Tynnu’ch ffeil
resume_options:
show:
title: What do you want to do?
review_file:
show:
check_file: Gwiriwch y ffeil sydd wedi’i huwchlwytho
Expand All @@ -440,8 +457,9 @@ cy:
title: Use GOV.UK One Login to save your progress
show:
body_html: '<p>We’ve sent a link to %{email} so you can continue filling in the form when you are ready. This is the email address you use to sign in with GOV.UK One Login.</p><p>Use this link to continue filling in the form: <a href="%{form_url}" class="govuk-link">%{form_url}</a></p><p>We’ll delete your saved form on %{deletion_date}.</p>'
continue_button: Continue filling the form
sign_out_link: Sign out of GOV.UK One Login
title: Your form has been saved
title: Your progress has been saved
gov_uk_forms: GOV.UK Forms
govuk_components:
govuk_summary_list:
Expand All @@ -459,6 +477,9 @@ cy:
hint:
email_confirmation_input:
send_confirmation: Byddwn ond yn defnyddio’r cyfeiriad e-bost rydych yn ei ddarparu yma i anfon cadarnhad bod eich ffurflen wedi’i chyflwyno’n llwyddiannus. Ni fydd yn cynnwys copi o’ch atebion.
resume_options_input:
options:
continue_saved: You’ll need to sign in to your GOV.UK One Login
label:
add_another_answer_input:
options:
Expand All @@ -477,6 +498,10 @@ cy:
options:
'no': Na
'yes': Ydw
resume_options_input:
options:
continue_saved: Continue filling in a saved form
start_new: Start a new form
legend:
email_confirmation_input:
send_confirmation: Ydych chi eisiau derbyn e-bost yn cadarnhau bod eich ffurflen wedi’i chyflwyno?
Expand Down
Loading
Loading