diff --git a/Gemfile.lock b/Gemfile.lock
index 0f3e99521c1..d6f072de731 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -197,7 +197,7 @@ GEM
debug_inspector (>= 0.0.1)
bootsnap (1.15.0)
msgpack (~> 1.2)
- brakeman (5.4.0)
+ brakeman (5.4.1)
browser (5.3.1)
builder (3.2.4)
bullet (7.0.7)
diff --git a/app/components/phone_input_component.html.erb b/app/components/phone_input_component.html.erb
index efe8510bb5c..24a495525de 100644
--- a/app/components/phone_input_component.html.erb
+++ b/app/components/phone_input_component.html.erb
@@ -36,12 +36,6 @@
:phone,
class: 'usa-label',
) { t('two_factor_authentication.phone_label') } %>
-
- <%= f.hint(capture do %>
- <%= t('forms.example') %>
-
- <% end) %>
-
<%= render ValidatedFieldComponent.new(
form: f,
name: :phone,
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ec78024eaef..9c17fec259f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -65,6 +65,7 @@ def analytics
sp: current_sp&.issuer,
session: session,
ahoy: ahoy,
+ irs_session_id: irs_attempts_api_session_id,
)
end
diff --git a/app/controllers/concerns/idv/verify_info_concern.rb b/app/controllers/concerns/idv/verify_info_concern.rb
index 0635f2c248f..9e1acd7dce2 100644
--- a/app/controllers/concerns/idv/verify_info_concern.rb
+++ b/app/controllers/concerns/idv/verify_info_concern.rb
@@ -97,7 +97,8 @@ def async_state_done(current_async_state)
extra: {
address_edited: !!flow_session['address_edited'],
address_line2_present: !pii[:address2].blank?,
- pii_like_keypaths: [[:errors, :ssn], [:response_body, :first_name]],
+ pii_like_keypaths: [[:errors, :ssn], [:response_body, :first_name],
+ [:state_id, :state_id_jurisdiction]],
},
)
log_idv_verification_submitted_event(
@@ -105,15 +106,14 @@ def async_state_done(current_async_state)
failure_reason: irs_attempts_api_tracker.parse_failure_reason(form_response),
)
- if form_response.success?
- response = check_ssn
- form_response = form_response.merge(response)
- end
+ form_response = form_response.merge(check_ssn) if form_response.success?
summarize_result_and_throttle_failures(form_response)
delete_async
if form_response.success?
+ move_applicant_to_idv_session
idv_session.mark_verify_info_step_complete!
+ idv_session.invalidate_steps_after_verify_info!
redirect_to idv_phone_url
else
idv_session.invalidate_verify_info_step!
@@ -196,27 +196,13 @@ def log_idv_verification_submitted_event(success: false, failure_reason: nil)
end
def check_ssn
- result = Idv::SsnForm.new(current_user).submit(ssn: pii[:ssn])
-
- if result.success?
- save_legacy_state
- delete_pii
- end
-
- result
+ Idv::SsnForm.new(current_user).submit(ssn: pii[:ssn])
end
- def save_legacy_state
- skip_legacy_steps
+ def move_applicant_to_idv_session
idv_session.applicant = pii
idv_session.applicant['uuid'] = current_user.uuid
- end
-
- def skip_legacy_steps
- idv_session.mark_verify_info_step_complete!
- idv_session.vendor_phone_confirmation = false
- idv_session.user_phone_confirmation = false
- idv_session.address_verification_mechanism = 'phone'
+ delete_pii
end
def add_proofing_costs(results)
diff --git a/app/controllers/idv/in_person/address_search_controller.rb b/app/controllers/idv/in_person/address_search_controller.rb
index e96e4208fee..725ab329e1d 100644
--- a/app/controllers/idv/in_person/address_search_controller.rb
+++ b/app/controllers/idv/in_person/address_search_controller.rb
@@ -1,10 +1,6 @@
module Idv
module InPerson
class AddressSearchController < ApplicationController
- include RenderConditionConcern
-
- check_or_render_not_found -> { IdentityConfig.store.arcgis_search_enabled }
-
def index
response = addresses(params[:address])
render(**response)
diff --git a/app/controllers/idv/in_person/usps_locations_controller.rb b/app/controllers/idv/in_person/usps_locations_controller.rb
index 6e62cb11ecf..ec7e9cc2a16 100644
--- a/app/controllers/idv/in_person/usps_locations_controller.rb
+++ b/app/controllers/idv/in_person/usps_locations_controller.rb
@@ -20,26 +20,21 @@ class UspsLocationsController < ApplicationController
# retrieve the list of nearby IPP Post Office locations with a POST request
def index
- response = []
- if IdentityConfig.store.arcgis_search_enabled
- candidate = UspsInPersonProofing::Applicant.new(
- address: search_params['street_address'],
- city: search_params['city'], state: search_params['state'],
- zip_code: search_params['zip_code']
+ candidate = UspsInPersonProofing::Applicant.new(
+ address: search_params['street_address'],
+ city: search_params['city'], state: search_params['state'],
+ zip_code: search_params['zip_code']
+ )
+ response = proofer.request_facilities(candidate)
+ if response.length > 0
+ analytics.idv_in_person_locations_searched(
+ success: true,
+ result_total: response.length,
)
- response = proofer.request_facilities(candidate)
- if response.length > 0
- analytics.idv_in_person_locations_searched(
- success: true,
- result_total: response.length,
- )
- else
- analytics.idv_in_person_locations_searched(
- success: false, errors: 'No USPS locations found',
- )
- end
else
- response = proofer.request_pilot_facilities
+ analytics.idv_in_person_locations_searched(
+ success: false, errors: 'No USPS locations found',
+ )
end
render json: response.to_json
end
diff --git a/app/controllers/redirect/policy_controller.rb b/app/controllers/redirect/policy_controller.rb
new file mode 100644
index 00000000000..9e75c5dfd79
--- /dev/null
+++ b/app/controllers/redirect/policy_controller.rb
@@ -0,0 +1,10 @@
+module Redirect
+ class PolicyController < RedirectController
+ def show
+ redirect_to_and_log(
+ MarketingSite.security_and_privacy_practices_url,
+ tracker_method: analytics.method(:policy_redirect),
+ )
+ end
+ end
+ end
diff --git a/app/javascript/packages/document-capture/components/document-capture.tsx b/app/javascript/packages/document-capture/components/document-capture.tsx
index 42e6f2bacc1..5dd591c2de5 100644
--- a/app/javascript/packages/document-capture/components/document-capture.tsx
+++ b/app/javascript/packages/document-capture/components/document-capture.tsx
@@ -9,7 +9,6 @@ import { getConfigValue } from '@18f/identity-config';
import { UploadFormEntriesError } from '../services/upload';
import DocumentsStep from './documents-step';
import InPersonPrepareStep from './in-person-prepare-step';
-import InPersonLocationStep from './in-person-location-step';
import InPersonLocationPostOfficeSearchStep from './in-person-location-post-office-search-step';
import InPersonSwitchBackStep from './in-person-switch-back-step';
import ReviewIssuesStep from './review-issues-step';
@@ -60,7 +59,7 @@ function DocumentCapture({ isAsyncForm = false, onStepChange = () => {} }: Docum
const { t } = useI18n();
const { flowPath } = useContext(UploadContext);
const { trackSubmitEvent, trackVisitEvent } = useContext(AnalyticsContext);
- const { inPersonURL, arcgisSearchEnabled } = useContext(InPersonContext);
+ const { inPersonURL } = useContext(InPersonContext);
const appName = getConfigValue('appName');
useDidUpdateEffect(onStepChange, [stepName]);
@@ -114,7 +113,7 @@ function DocumentCapture({ isAsyncForm = false, onStepChange = () => {} }: Docum
: ([
{
name: 'location',
- form: arcgisSearchEnabled ? InPersonLocationPostOfficeSearchStep : InPersonLocationStep,
+ form: InPersonLocationPostOfficeSearchStep,
title: t('in_person_proofing.headings.po_search.location'),
},
{
diff --git a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx
index 4df8ac8d242..e88a91c6e57 100644
--- a/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx
+++ b/app/javascript/packages/document-capture/components/in-person-location-post-office-search-step.spec.tsx
@@ -7,9 +7,7 @@ import type { SetupServerApi } from 'msw/node';
import { SWRConfig } from 'swr';
import { I18nContext } from '@18f/identity-react-i18n';
import { ComponentType } from 'react';
-import { LOCATIONS_URL } from './in-person-location-step';
-import { ADDRESS_SEARCH_URL } from './address-search';
-import InPersonContext from '../context/in-person';
+import { ADDRESS_SEARCH_URL, LOCATIONS_URL } from './address-search';
import InPersonLocationPostOfficeSearchStep from './in-person-location-post-office-search-step';
const DEFAULT_RESPONSE = [
@@ -58,11 +56,9 @@ const DEFAULT_PROPS = {
registerField() {},
};
-describe('InPersonLocationStep', () => {
+describe('InPersonPostOfficeSearchStep', () => {
const wrapper: ComponentType = ({ children }) => (
-
- new Map() }}>{children}
-
+ new Map() }}>{children}
);
let server: SetupServerApi;
@@ -90,9 +86,7 @@ describe('InPersonLocationStep', () => {
it('displays a try again error message', async () => {
const { findByText, findByLabelText } = render(
new Map() }}>
-
-
-
+
,
);
diff --git a/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx b/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx
deleted file mode 100644
index a5de88ed931..00000000000
--- a/app/javascript/packages/document-capture/components/in-person-location-step.spec.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import sinon from 'sinon';
-import { useContext } from 'react';
-import { render } from '@testing-library/react';
-import { getAllByRole } from '@testing-library/dom';
-import userEvent from '@testing-library/user-event';
-import { setupServer } from 'msw/node';
-import { rest } from 'msw';
-import type { SetupServerApi } from 'msw/node';
-import AnalyticsContext, { AnalyticsContextProvider } from '../context/analytics';
-import InPersonLocationStep, { LOCATIONS_URL } from './in-person-location-step';
-import { ADDRESS_SEARCH_URL } from './address-search';
-
-const DEFAULT_RESPONSE = [
- {
- address: '100 Main St E, Bronwood, Georgia, 39826',
- location: {
- latitude: 31.831686000000005,
- longitude: -84.363768,
- },
- street_address: '100 Main St E',
- city: 'Bronwood',
- state: 'GA',
- zip_code: '39826',
- },
-];
-
-const DEFAULT_PROPS = {
- toPreviousStep() {},
- onChange() {},
- value: {},
- registerField() {},
-};
-
-describe('InPersonLocationStep', () => {
- let server: SetupServerApi;
- before(() => {
- server = setupServer(
- rest.post(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ name: 'Baltimore' }]))),
- rest.post(ADDRESS_SEARCH_URL, (_req, res, ctx) => res(ctx.json(DEFAULT_RESPONSE))),
- rest.put(LOCATIONS_URL, (_req, res, ctx) => res(ctx.json([{ success: true }]))),
- );
- server.listen();
- });
-
- after(() => {
- server.close();
- });
-
- it('logs step submission with selected location', async () => {
- const trackEvent = sinon.stub();
- function MetadataValue() {
- return <>{JSON.stringify(useContext(AnalyticsContext).submitEventMetadata)}>;
- }
- const { findByText } = render(
-
-
-
- ,
- );
-
- const item = await findByText('Baltimore — in_person_proofing.body.location.post_office');
- const button = getAllByRole(item.closest('.location-collection-item')!, 'button')[0];
-
- await userEvent.click(button);
-
- await findByText('{"selected_location":"Baltimore","in_person_cta_variant":""}');
- });
-});
diff --git a/app/javascript/packages/document-capture/components/in-person-location-step.tsx b/app/javascript/packages/document-capture/components/in-person-location-step.tsx
deleted file mode 100644
index 6712e3b8ef1..00000000000
--- a/app/javascript/packages/document-capture/components/in-person-location-step.tsx
+++ /dev/null
@@ -1,201 +0,0 @@
-import { useState, useEffect, useCallback, useRef, useContext } from 'react';
-import { useI18n } from '@18f/identity-react-i18n';
-import { PageHeading, SpinnerDots } from '@18f/identity-components';
-import { request } from '@18f/identity-request';
-import BackButton from './back-button';
-import LocationCollection from './location-collection';
-import LocationCollectionItem from './location-collection-item';
-import AnalyticsContext from '../context/analytics';
-import { InPersonContext } from '../context';
-
-interface PostOffice {
- address: string;
- city: string;
- name: string;
- saturday_hours: string;
- state: string;
- sunday_hours: string;
- weekday_hours: string;
- zip_code_4: string;
- zip_code_5: string;
-}
-
-interface FormattedLocation {
- formattedCityStateZip: string;
- id: number;
- name: string;
- saturdayHours: string;
- streetAddress: string;
- sundayHours: string;
- weekdayHours: string;
-}
-interface LocationQuery {
- streetAddress: string;
- city: string;
- state: string;
- zipCode: string;
-}
-
-export const LOCATIONS_URL = '/verify/in_person/usps_locations';
-
-const getUspsLocations = (address) =>
- request(LOCATIONS_URL, {
- method: 'post',
- json: { address },
- });
-
-const formatLocation = (postOffices: PostOffice[]) => {
- const formattedLocations = [] as FormattedLocation[];
- postOffices.forEach((po: PostOffice, index) => {
- const location = {
- formattedCityStateZip: `${po.city}, ${po.state}, ${po.zip_code_5}-${po.zip_code_4}`,
- id: index,
- name: po.name,
- saturdayHours: po.saturday_hours,
- streetAddress: po.address,
- sundayHours: po.sunday_hours,
- weekdayHours: po.weekday_hours,
- } as FormattedLocation;
- formattedLocations.push(location);
- });
- return formattedLocations;
-};
-
-const snakeCase = (value: string) =>
- value
- .split(/(?=[A-Z])/)
- .join('_')
- .toLowerCase();
-
-// snake case the keys of the location
-const prepToSend = (location: object) => {
- const sendObject = {};
- Object.keys(location).forEach((key) => {
- sendObject[snakeCase(key)] = location[key];
- });
- return sendObject;
-};
-
-function InPersonLocationStep({ onChange, toPreviousStep }) {
- const { inPersonCtaVariantActive } = useContext(InPersonContext);
- const { t } = useI18n();
- const [locationData, setLocationData] = useState([] as FormattedLocation[]);
- const [foundAddress] = useState({} as LocationQuery);
- const [inProgress, setInProgress] = useState(false);
- const [autoSubmit, setAutoSubmit] = useState(false);
- const [isLoadingComplete, setIsLoadingComplete] = useState(false);
- const { setSubmitEventMetadata } = useContext(AnalyticsContext);
-
- // ref allows us to avoid a memory leak
- const mountedRef = useRef(false);
-
- useEffect(() => {
- mountedRef.current = true;
- return () => {
- mountedRef.current = false;
- };
- }, []);
-
- // useCallBack here prevents unnecessary rerenders due to changing function identity
- const handleLocationSelect = useCallback(
- async (e: any, id: number) => {
- const selectedLocation = locationData[id];
- const { name: selectedLocationName } = selectedLocation;
- setSubmitEventMetadata({
- selected_location: selectedLocationName,
- in_person_cta_variant: inPersonCtaVariantActive,
- });
- onChange({ selectedLocationName });
- if (autoSubmit) {
- return;
- }
- // prevent navigation from continuing
- e.preventDefault();
- if (inProgress) {
- return;
- }
- const selected = prepToSend(selectedLocation);
- setInProgress(true);
- await request(LOCATIONS_URL, {
- json: selected,
- method: 'PUT',
- })
- .then(() => {
- if (!mountedRef.current) {
- return;
- }
- setAutoSubmit(true);
- setImmediate(() => {
- // continue with navigation
- e.target.click();
- // allow process to be re-triggered in case submission did not work as expected
- setAutoSubmit(false);
- });
- })
- .finally(() => {
- if (!mountedRef.current) {
- return;
- }
- setInProgress(false);
- });
- },
- [locationData, inProgress],
- );
-
- useEffect(() => {
- let didCancel = false;
- (async () => {
- try {
- const fetchedLocations = await getUspsLocations(prepToSend(foundAddress));
-
- if (!didCancel) {
- const formattedLocations = formatLocation(fetchedLocations);
- setLocationData(formattedLocations);
- }
- } finally {
- if (!didCancel) {
- setIsLoadingComplete(true);
- }
- }
- })();
- return () => {
- didCancel = true;
- };
- }, [foundAddress]);
-
- let locationsContent: React.ReactNode;
- if (!isLoadingComplete) {
- locationsContent = ;
- } else if (locationData.length < 1) {
- locationsContent = {t('in_person_proofing.body.location.none_found')}
;
- } else {
- locationsContent = (
-
- {locationData.map((item, index) => (
-
- ))}
-
- );
- }
-
- return (
- <>
- {t('in_person_proofing.headings.location')}
- {t('in_person_proofing.body.location.location_step_about')}
- {locationsContent}
-
- >
- );
-}
-
-export default InPersonLocationStep;
diff --git a/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx b/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx
index 1043d3afdcc..91f5bbcf829 100644
--- a/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx
+++ b/app/javascript/packages/document-capture/components/in-person-prepare-step.tsx
@@ -20,8 +20,7 @@ function InPersonPrepareStep({ toPreviousStep, value }) {
const { flowPath } = useContext(UploadContext);
const { trackEvent } = useContext(AnalyticsContext);
const { securityAndPrivacyHowItWorksURL } = useContext(MarketingSiteContext);
- /* Remove selectedLocationName when request_pilot_facilities removed */
- const { selectedLocationName, selectedLocationAddress } = value;
+ const { selectedLocationAddress } = value;
const onContinue: MouseEventHandler = async (event) => {
event.preventDefault();
@@ -36,14 +35,6 @@ function InPersonPrepareStep({ toPreviousStep, value }) {
return (
<>
- {/* Remove selectedLocationName version of alert when request_pilot_facilities removed */}
- {selectedLocationName && (
-
- {t('in_person_proofing.body.prepare.alert_selected_po_name', {
- name: selectedLocationName,
- })}
-
- )}
{selectedLocationAddress && (
{t('in_person_proofing.body.prepare.alert_selected_post_office', {
diff --git a/app/javascript/packages/document-capture/context/in-person.ts b/app/javascript/packages/document-capture/context/in-person.ts
index 90e7fdd6ced..f8bb51ba8fd 100644
--- a/app/javascript/packages/document-capture/context/in-person.ts
+++ b/app/javascript/packages/document-capture/context/in-person.ts
@@ -1,11 +1,6 @@
import { createContext } from 'react';
export interface InPersonContextProps {
- /**
- * Feature flag for enabling address search
- */
- arcgisSearchEnabled?: boolean;
-
/**
* Whether or not A/B testing of the in-person proofing CTA is enabled.
*/
@@ -23,7 +18,6 @@ export interface InPersonContextProps {
}
const InPersonContext = createContext({
- arcgisSearchEnabled: false,
inPersonCtaVariantTestingEnabled: false,
inPersonCtaVariantActive: '',
});
diff --git a/app/javascript/packages/phone-input/index.spec.ts b/app/javascript/packages/phone-input/index.spec.ts
index c8c09d97ec5..75505c5fdb9 100644
--- a/app/javascript/packages/phone-input/index.spec.ts
+++ b/app/javascript/packages/phone-input/index.spec.ts
@@ -80,10 +80,6 @@ describe('PhoneInput', () => {
${!isSingleOption && !isNonUSSingleOption ? MULTIPLE_OPTIONS_HTML : ''}
-
- Example:
-
-