Skip to content
Merged
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
6 changes: 5 additions & 1 deletion app/controllers/idv/in_person/address_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ def redirect_to_next_page

def confirm_in_person_state_id_step_complete
return if pii_from_user&.has_key?(:identity_doc_address1)
redirect_to idv_in_person_step_url(step: :state_id)
if IdentityConfig.store.in_person_state_id_controller_enabled
redirect_to idv_in_person_proofing_state_id_url
else
redirect_to idv_in_person_step_url(step: :state_id)
end
end

def confirm_in_person_address_step_needed
Expand Down
91 changes: 91 additions & 0 deletions app/controllers/idv/in_person/state_id_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# frozen_string_literal: true

module Idv
module InPerson
class StateIdController < ApplicationController
include Idv::AvailabilityConcern
include IdvStepConcern

before_action :render_404_if_controller_not_enabled
before_action :redirect_unless_enrollment # confirm previous step is complete

def show
flow_session[:pii_from_user] ||= {}
analytics.idv_in_person_proofing_state_id_visited(**analytics_arguments)

render :show, locals: extra_view_variables
end

def extra_view_variables
{
form:,
pii:,
parsed_dob:,
updating_state_id: updating_state_id?,
}
end

private

def render_404_if_controller_not_enabled
render_not_found unless
IdentityConfig.store.in_person_state_id_controller_enabled
end

def redirect_unless_enrollment
redirect_to idv_document_capture_url unless current_user.establishing_in_person_enrollment
end

def flow_session
user_session.fetch('idv/in_person', {})
end

def analytics_arguments
{
flow_path: idv_session.flow_path,
step: 'state_id',
analytics_id: 'In Person Proofing',
irs_reproofing: irs_reproofing?,
}.merge(ab_test_analytics_buckets).
merge(extra_analytics_properties)
end

def updating_state_id?
flow_session[:pii_from_user].has_key?(:first_name)
end

def parsed_dob
form_dob = pii[:dob]
if form_dob.instance_of?(String)
dob_str = form_dob
elsif form_dob.instance_of?(Hash)
dob_str = MemorableDateComponent.extract_date_param(form_dob)
end
Date.parse(dob_str) unless dob_str.nil?
rescue StandardError
# Catch date parsing errors
end

def pii
data = flow_session[:pii_from_user]
data = data.merge(flow_params) if params.has_key?(:state_id)
data.deep_symbolize_keys
end

def flow_params
params.require(:state_id).permit(
*Idv::StateIdForm::ATTRIBUTES,
dob: [
:month,
:day,
:year,
],
)
end

def form
@form ||= Idv::StateIdForm.new(current_user)
end
end
end
end
10 changes: 9 additions & 1 deletion app/services/flow/flow_state_machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,15 @@ def flow_finish

def redirect_to_step(step)
flow_finish and return unless next_step
redirect_to send(@step_url, step: step)
redirect_url(step)
end

def redirect_url(step)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense. Basically if someone is in the FSM, and we have the state_id_controller_enabled, then bump the user over to the controller based page. I think the thing that is throwing me off is that since this is the last step that's using the FSM, how would a user get into the FSM if the state id controller is enabled?
Just thinking out loud, not saying anything about removing this code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good question. I think the InPersonController allows us to enter the fsm?

if IdentityConfig.store.in_person_state_id_controller_enabled
redirect_to idv_in_person_proofing_state_id_url
else
redirect_to send(@step_url, step: step)
end
end

def analytics_properties
Expand Down
244 changes: 244 additions & 0 deletions app/views/idv/in_person/state_id/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
<% content_for(:pre_flash_content) do %>
<%= render StepIndicatorComponent.new(
steps: Idv::Flows::InPersonFlow::STEP_INDICATOR_STEPS,
current_step: :verify_info,
locale_scope: 'idv',
class: 'margin-x-neg-2 margin-top-neg-4 tablet:margin-x-neg-6 tablet:margin-top-neg-4',
) %>
<% end %>
<% if updating_state_id %>
<% self.title = t('in_person_proofing.headings.update_state_id') %>
<%= render PageHeadingComponent.new.with_content(t('in_person_proofing.headings.update_state_id')) %>
<% else %>
<% self.title = t('in_person_proofing.headings.state_id_milestone_2') %>
<%= render PageHeadingComponent.new.with_content(t('in_person_proofing.headings.state_id_milestone_2')) %>
<% end %>

<p>
<%= t('in_person_proofing.body.state_id.info_html') %>
</p>

<%= render AlertComponent.new(
type: :info,
class: 'margin-bottom-4',
text_tag: 'div',
) do %>
<strong><%= t('in_person_proofing.body.state_id.alert_message') %></strong>
<ul class="margin-bottom-0">
<% t('in_person_proofing.body.state_id.id_types').each do | id_type | %>
<li>
<%= id_type %>
</li>
<% end %>
</ul>
<p>
<%= t('in_person_proofing.body.state_id.questions') %>
<%= link_to(
MarketingSite.help_center_article_url(
category: 'verify-your-identity',
article: 'accepted-state-issued-identification',
),
class: 'display-inline',
) do %>
<%= t('in_person_proofing.body.state_id.learn_more_link') %>
<% end %>
</p>
<% end %>
<%= simple_form_for form,
url: url_for,
method: 'put',
html: { autocomplete: 'off', class: 'margin-y-5' } do |f| %>

<div class="margin-bottom-4">
<%= render ValidatedFieldComponent.new(
name: :first_name,
form: f,
input_html: { value: pii[:first_name] },
label: t('in_person_proofing.form.state_id.first_name'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: true,
) %>
</div>

<div class="margin-bottom-4">
<%= render ValidatedFieldComponent.new(
name: :last_name,
form: f,
input_html: { value: pii[:last_name] },
label: t('in_person_proofing.form.state_id.last_name'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: true,
) %>
</div>

<div class="margin-bottom-4">
<%= render MemorableDateComponent.new(
content_tag: 'memorable-date',
name: :dob,
day: parsed_dob&.day,
month: parsed_dob&.month,
year: parsed_dob&.year,
required: true,
min: '1900-01-01',
max: Time.zone.today,
hint: t('in_person_proofing.form.state_id.dob_hint'),
label: t('in_person_proofing.form.state_id.dob'),
form: f,
error_messages: {
missing_month_day_year: t('in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.missing_month_day_year'),
range_overflow: t('in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_overflow'),
},
range_errors: [
{
max: Time.zone.today - 13.years,
message: t(
'in_person_proofing.form.state_id.memorable_date.errors.date_of_birth.range_min_age',
app_name: APP_NAME,
),
},
],
)
%>
</div>

<div class="margin-bottom-4">
<%= render ValidatedFieldComponent.new(
name: :state_id_jurisdiction,
collection: us_states_territories,
form: f,
hint: t('in_person_proofing.form.state_id.state_id_jurisdiction_hint'),
input_html: { class: 'jurisdiction-state-selector' },
label: t('in_person_proofing.form.state_id.state_id_jurisdiction'),
label_html: { class: 'usa-label' },
prompt: t('in_person_proofing.form.state_id.state_id_jurisdiction_prompt'),
required: true,
selected: pii[:state_id_jurisdiction],
) %>
</div>
<div class="margin-bottom-5">
<% state_id_number_hint_default = capture do %>
<%= t('in_person_proofing.form.state_id.state_id_number_hint') %>
<% [
[t('in_person_proofing.form.state_id.state_id_number_hint_spaces'), ' '],
[t('in_person_proofing.form.state_id.state_id_number_hint_forward_slashes'), '/'],
[t('in_person_proofing.form.state_id.state_id_number_hint_asterisks'), '*'],
[t('in_person_proofing.form.state_id.state_id_number_hint_dashes'), '-', true],
].each do |text, symbol, last| %>
<span class="usa-sr-only"><%= text %><%= ',' if !last %></span>
<span aria-hidden="true"><%= symbol %></span>
<% end %>
<% end %>
<% state_id_number_hint = capture do %>
<% [
[:default, state_id_number_hint_default],
['FL', t('in_person_proofing.form.state_id.state_id_number_florida_hint_html')],
['TX', t('in_person_proofing.form.state_id.state_id_number_texas_hint')],
].each do |state, hint| %>
<%= content_tag(
:span,
hint,
class: state == :default ? nil : 'display-none',
data: { state: },
) %>
<% end %>
<% end %>
<%= render ValidatedFieldComponent.new(
name: :state_id_number,
form: f,
hint: state_id_number_hint,
hint_html: { class: ['tablet:grid-col-10', 'jurisdiction-extras'] },
input_html: { value: pii[:state_id_number] },
label: t('in_person_proofing.form.state_id.state_id_number'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: true,
) %>
</div>

<h2> <%= t('in_person_proofing.headings.id_address') %> </h2>
<%= render ValidatedFieldComponent.new(
name: :identity_doc_address_state,
collection: us_states_territories,
form: f,
input_html: { class: 'address-state-selector' },
label: t('in_person_proofing.form.state_id.identity_doc_address_state'),
label_html: { class: 'usa-label' },
prompt: t('in_person_proofing.form.state_id.identity_doc_address_state_prompt'),
required: true,
selected: pii[:identity_doc_address_state],
) %>
<%= render ValidatedFieldComponent.new(
name: :identity_doc_address1,
form: f,
hint_html: { class: ['display-none', 'puerto-rico-extras'] },
hint: t('in_person_proofing.form.state_id.address1_hint'),
input_html: { value: pii[:identity_doc_address1] },
label: t('in_person_proofing.form.state_id.address1'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: true,
) %>
<%= render ValidatedFieldComponent.new(
name: :identity_doc_address2,
form: f,
hint: t('in_person_proofing.form.state_id.address2_hint'),
hint_html: { class: ['display-none', 'puerto-rico-extras'] },
input_html: { value: pii[:identity_doc_address2] },
label: t('in_person_proofing.form.state_id.address2'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: false,
) %>
<%= render ValidatedFieldComponent.new(
name: :identity_doc_city,
form: f,
input_html: { value: pii[:identity_doc_city] },
label: t('in_person_proofing.form.state_id.city'),
label_html: { class: 'usa-label' },
maxlength: 255,
required: true,
) %>
<div class="tablet:grid-col-8 margin-bottom-5">
<%# using :tel for mobile numeric keypad %>
<%= render ValidatedFieldComponent.new(
as: :tel,
error_messages: { patternMismatch: t('idv.errors.pattern_mismatch.zipcode') },
form: f,
input_html: { value: pii[:identity_doc_zipcode], class: 'zipcode' },
label: t('in_person_proofing.form.state_id.zipcode'),
label_html: { class: 'usa-label' },
name: :identity_doc_zipcode,
pattern: '\d{5}([\-]\d{4})?',
required: true,
) %>
</div>
<%= render ValidatedFieldComponent.new(
as: :radio_buttons,
checked: pii[:same_address_as_id],
collection: [
[t('in_person_proofing.form.state_id.same_address_as_id_yes'), true],
[t('in_person_proofing.form.state_id.same_address_as_id_no'), false],
],
form: f,
label: t('in_person_proofing.form.state_id.same_address_as_id'),
legend_html: { class: 'h2' },
name: :same_address_as_id,
required: true,
wrapper: :uswds_radio_buttons,
) %>
<%= f.submit do %>
<% if updating_state_id %>
<%= t('forms.buttons.submit.update') %>
<% else %>
<%= t('forms.buttons.continue') %>
<% end %>
<% end %>
<% end %>
<% if updating_state_id %>
<%= render 'idv/shared/back', action: 'cancel_update_state_id' %>
<% else %>
<%= render 'idv/doc_auth/cancel', step: 'state_id' %>
<% end %>
<%= javascript_packs_tag_once('formatted-fields', 'state-guidance') %>
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@
# during the deprecation process.
get '/in_person_proofing/address' => redirect('/verify/in_person/address', status: 307)
put '/in_person_proofing/address' => redirect('/verify/in_person/address', status: 307)
get '/in_person_proofing/state_id' => 'in_person/state_id#show'

get '/in_person' => 'in_person#index'
get '/in_person/ready_to_verify' => 'in_person/ready_to_verify#show',
Expand Down
Loading