Skip to content

Commit

Permalink
Merge pull request #3133 from DFE-Digital/new-reminders
Browse files Browse the repository at this point in the history
[LUPEYALPHA-704] FE reminders
  • Loading branch information
asmega authored Aug 30, 2024
2 parents d552237 + 3e2a0af commit 5ab77f2
Show file tree
Hide file tree
Showing 13 changed files with 430 additions and 4 deletions.
76 changes: 76 additions & 0 deletions app/controllers/reminders_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class RemindersController < BasePublicController
before_action :create_journey_session_if_no_session

def show
@form = form_from_slug

render view_file
end

def update
@form = form_from_slug

if @form.valid?
redirect_to independent_reminder_path(
journey: journey::ROUTING_NAME,
slug: navigator.next_slug
)

@form.save!
else
render view_file
end
end

private

def create_journey_session_if_no_session
if session[journey_session_key].blank?
create_journey_session!
end
end

def create_journey_session!
journey_session = journey::SessionForm.create!(params)

session[journey_session_key] = journey_session.id

journey_session
end

def journey_session_key
:"#{journey::ROUTING_NAME}_journeys_session_id"
end

def view_file
params[:slug].underscore
end

def navigator
@navigator ||= Journeys::Reminders::SlugSequence::Navigator.new(
current_slug: params[:slug]
)
end
helper_method :navigator

def form_class_from_slug
case params[:slug]
when "personal-details"
Reminders::PersonalDetailsForm
when "email-verification"
Reminders::EmailVerificationForm
when "confirmation"
Reminders::ConfirmationForm
else
raise "form not found"
end
end

def form_from_slug
form_class_from_slug.new(
journey_session:,
journey:,
params:
)
end
end
18 changes: 18 additions & 0 deletions app/forms/reminders/confirmation_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Reminders
class ConfirmationForm < Form
def reminder
@reminder ||= Reminder.find_by(
full_name: journey_session.answers.reminder_full_name,
email_address: journey_session.answers.reminder_email_address,
email_verified: true,
itt_academic_year: next_academic_year.to_s
)
end

private

def next_academic_year
AcademicYear.next
end
end
end
45 changes: 45 additions & 0 deletions app/forms/reminders/email_verification_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Reminders
class EmailVerificationForm < Form
attribute :one_time_password, :string

validate :validate_otp_correct

def save!
return false if invalid?

journey_session.answers.assign_attributes(
reminder_otp_confirmed: true
)
journey_session.save!

reminder = Reminder.find_or_create_by(
full_name: journey_session.answers.reminder_full_name,
email_address: journey_session.answers.reminder_email_address,
email_verified: true,
itt_academic_year: next_academic_year.to_s
)

ReminderMailer.reminder_set(reminder).deliver_now
end

private

def next_academic_year
AcademicYear.next
end

def validate_otp_correct
if !validator.valid?
errors.add(:one_time_password, validator.warning)
end
end

def validator
@validator ||= OneTimePassword::Validator.new(
one_time_password,
nil,
secret: journey_session.answers.reminder_otp_secret
)
end
end
end
62 changes: 62 additions & 0 deletions app/forms/reminders/personal_details_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module Reminders
class PersonalDetailsForm < Form
attribute :reminder_full_name, :string
attribute :reminder_email_address, :string

validates :reminder_full_name,
presence: {
message: i18n_error_message("full_name.blank")
},
length: {
maximum: 100,
message: i18n_error_message("full_name.length")
}

validates :reminder_email_address,
presence: {
message: i18n_error_message("email_address.blank")
}

validates :reminder_email_address,
if: -> { reminder_email_address.present? },
format: {
with: Rails.application.config.email_regexp,
message: i18n_error_message("email_address.invalid")
},
length: {
maximum: 256,
message: i18n_error_message("email_address.length")
}

def save!
return false if invalid?

journey_session.answers.assign_attributes(
reminder_full_name:,
reminder_email_address:,
reminder_otp_secret:
)

ReminderMailer.email_verification(reminder, otp_code).deliver_now

journey_session.save!
end

private

def reminder
@reminder ||= Reminder.new(
full_name: reminder_full_name,
email_address: reminder_email_address
)
end

def reminder_otp_secret
@reminder_otp_secret ||= ROTP::Base32.random
end

def otp_code
@opt_code ||= OneTimePassword::Generator.new(secret: reminder_otp_secret).code
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ class SessionAnswers < Journeys::SessionAnswers
attribute :half_teaching_hours, :boolean
attribute :award_amount, :decimal

# TODO: extract
attribute :reminder_full_name, :string
attribute :reminder_email_address, :string
attribute :reminder_otp_secret, :string
attribute :reminder_otp_confirmed, :boolean, default: false # whether or not they have confirmed email via otp

def policy
Policies::FurtherEducationPayments
end
Expand Down
39 changes: 39 additions & 0 deletions app/models/journeys/reminders/slug_sequence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Journeys
module Reminders
class SlugSequence
SLUGS = [
"personal-details",
"email-verification",
"confirmation"
].freeze

def slugs
SLUGS
end

class Navigator
attr_reader :current_slug

def initialize(current_slug:)
@current_slug = current_slug
end

def next_slug
SLUGS[current_index + 1]
end

def previous_slug
return if current_index == 0

SLUGS[current_index - 1]
end

private

def current_index
SLUGS.index(current_slug)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@
You can set a reminder so you know when you are able to apply next year. We cannot issue payments unless you apply.
</p>

<%= govuk_button_link_to "Set reminder", "#" %>
<%= govuk_button_link_to "Set reminder", independent_reminder_path(journey: journey::ROUTING_NAME, slug: "personal-details") %>
</div>
</div>
36 changes: 36 additions & 0 deletions app/views/reminders/confirmation.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<% content_for(:page_title, page_title("We have set your reminders", journey: current_journey_routing_name)) %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<div class="govuk-panel govuk-panel--confirmation">
<h1 class="govuk-panel__title" id="reminders-title">We have set your reminder</h1>
</div>

<h2 class="govuk-heading-m">What happens next</h2>

<p class="govuk-body">
We will send you a verification email now. If you don’t receive one,
contact us on <%= mail_to t("support_email_address", scope: journey::I18N_NAMESPACE), t("support_email_address", scope: journey::I18N_NAMESPACE), class: "govuk-link" %>.
</p>

<h3 class="govuk-heading-s">When will your reminder be sent?</h3>

<p class="govuk-body">
We will send you a reminder by email in September <%= @form.reminder.send_year -%>. You will be able to check your eligibility and apply at this time.
</p>

<p class="govuk-body">
Each application window will be open for 5 months from September in the year stated. If any of your employment circumstances change it might affect your eligibility.
</p>

<p class="govuk-inset-text">
It is your responsibility to apply for each payment you are eligible for. The Department for Education will not
issue payments unless you apply, also we will not issue payments for claims made after the window has closed.
</p>

<p class="govuk-body">
<%= link_to "What do you think of this service?", done_page_url, class: "govuk-link" %>
(takes 30 seconds)
</p>
</div>
</div>
44 changes: 44 additions & 0 deletions app/views/reminders/email_verification.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<% content_for(:page_title, page_title(t("one_time_password.title"), journey: current_journey_routing_name, show_error: @form.errors.any?)) %>

<% @backlink_path = independent_reminder_path(current_journey_routing_name, navigator.previous_slug) %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_with model: @form,
url: independent_reminder_path(current_journey_routing_name),
builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
<%= f.govuk_error_summary %>

<%= f.govuk_text_field :one_time_password,
autocomplete: "off",
width: 5,
label: {
text: t("one_time_password.title"),
size: "l",
tag: "h1"
},
caption: {
text: "Email verification",
size: "xl"
},
hint: -> do %>
<p>
<%= t("one_time_password.hint1_html", email_or_mobile_message: "an email", email_or_mobile_value: journey_session.answers.reminder_email_address) %>
</p>

<p>
<%= t("one_time_password.validity_duration", duration_valid: one_time_password_validity_duration) %>
</p>
<% end %>

<div class="govuk-body govuk-!-margin-bottom-6">
<%= govuk_link_to "Resend passcode (you will be sent back to the email address page)", independent_reminder_path(journey: journey::ROUTING_NAME, slug: "personal-details"), no_visited_state: true %>
</div>

<div class="govuk-button-group">
<%= f.govuk_submit "Confirm" %>
<%= govuk_button_link_to "Change email address", independent_reminder_path(journey: journey::ROUTING_NAME, slug: "personal-details"), secondary: true %>
</div>
<% end %>
</div>
</div>
51 changes: 51 additions & 0 deletions app/views/reminders/personal_details.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<% content_for(:page_title, page_title(t("questions.personal_details"), journey: current_journey_routing_name, show_error: @form.errors.any?)) %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= form_with model: @form,
url: independent_reminder_path(current_journey_routing_name),
builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
<%= f.govuk_error_summary %>

<h1 class="govuk-heading-xl">
<%= t("questions.personal_details") %>
</h1>

<%= f.govuk_text_field :reminder_full_name,
label: {
text: t("additional_payments.reminders.full_name"),
size: "l"
},
spellcheck: "false",
autocomplete: "name" %>

<%= f.govuk_text_field :reminder_email_address,
label: {
text: t("questions.email_address"),
size: "l"
},
spellcheck: "false",
autocomplete: "email",
hint: -> do %>
<p>
Tell us the email you want us to send reminders to.
We recommend you use a non-work email address in case your circumstances change.
</p>

<p>
To verify your email address we will send you an email with a 6-digit passcode.
You can enter the passcode on the next screen.
</p>
<% end %>

<%= govuk_details(summary_text: "Get help with access codes") do %>
<p class="govuk-body">
If you have any issues with the passcode, email us at:
<%= govuk_mail_to support_email_address, support_email_address -%>.
</p>
<% end %>

<%= f.govuk_submit "Continue" %>
<% end %>
</div>
</div>
Loading

0 comments on commit 5ab77f2

Please sign in to comment.