Skip to content

Commit

Permalink
Type of financial institution (Phase 1.5) (#465)
Browse files Browse the repository at this point in the history
Closes #455 

## Changes

- Integrates Type of FI form into the workflow
- Updates Type of FI form content
- Prevents creation of a Filing for the institution until they've
clicked "Start filing"

Co-authored-by: shindigira <[email protected]>
  • Loading branch information
meissadia and shindigira authored May 15, 2024
1 parent 2e398a6 commit 4bfca21
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 61 deletions.
21 changes: 20 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ const PointOfContact = lazy(async () => import('pages/PointOfContact'));
const TypesFinancialInstitutions = lazy(
async () => import('pages/TypesFinancialInstitutions'),
);
const FilingCreate = lazy(
async () => import('pages/Filing/FilingApp/FilingCreate'),
);

// allow developers to toggle routing in development
const isRoutingEnabled = getIsRoutingEnabled();
Expand Down Expand Up @@ -200,6 +203,14 @@ export default function App(): ReactElement {
}
/>
) : null}
<Route
path='/filing/:year/:lei/create'
element={
<ProtectedRoute {...ProtectedRouteAuthorizations}>
<FilingCreate />
</ProtectedRoute>
}
/>
<Route
path='/filing/:year/:lei/upload'
element={
Expand Down Expand Up @@ -313,7 +324,15 @@ export default function App(): ReactElement {
}
/>
<Route
path='/types-financial-institutions'
path='/institution/:lei/type'
element={
<ProtectedRoute {...ProtectedRouteAuthorizations}>
<TypesFinancialInstitutions />
</ProtectedRoute>
}
/>
<Route
path='/institution/:lei/type/:year'
element={
<ProtectedRoute {...ProtectedRouteAuthorizations}>
<TypesFinancialInstitutions />
Expand Down
50 changes: 50 additions & 0 deletions src/pages/Filing/FilingApp/FilingCreate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AlertApiUnavailable from 'components/AlertApiUnavailable';
import { LoadingContent } from 'components/Loading';
import { Button } from 'design-system-react';
import { useNavigate, useParams } from 'react-router-dom';
import { FILING_STATUS_CODE_FILING_EXISTS } from 'utils/constants';
import useCreateFiling from 'utils/useCreateFiling';

export function FilingCreate(): JSX.Element | null | undefined {
const { lei, year } = useParams();
const navigate = useNavigate();

const { isLoading, error, data: filing } = useCreateFiling(lei, year);

/** Missing required param, cannot continue */
if (!lei || !year) {
navigate('/filing');
return null;
}

if (isLoading) return <LoadingContent message='Loading filing data...' />;

/** Filing exists */
if (
filing ??
Number(error?.response?.status) === FILING_STATUS_CODE_FILING_EXISTS
) {
// Note: React was complaining about setState during render. This setTimeout seems to resolve the issue.
setTimeout(() => navigate(`/filing/${year}/${lei}/upload`), 0);
return <LoadingContent message='Loading filing data...' />;
}

const onReturnToFiling = (): void => navigate('/filing');

if (error)
return (
<div className='u-mt45 u-mb45 mx-3'>
<AlertApiUnavailable
message={`Unable to create a ${year} Filing for ${lei}`}
/>
<div>
<Button
label='Return to Filing overview'
onClick={onReturnToFiling}
/>
</div>
</div>
);
}

export default FilingCreate;
9 changes: 5 additions & 4 deletions src/pages/Filing/FilingApp/FilingSubmit.helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,18 @@ export function PointOfContactConfirm({
<DisplayField label='First name' value={poc?.first_name} />
<DisplayField label='Last name' value={poc?.last_name} />
<DisplayField label='Email address' value={poc?.email} />
<DisplayField label='Phone number' value={poc?.phone} />
<DisplayField label='Phone number' value={poc?.phone_number} />
<DisplayField
label='Business address'
value={
poc ? (
<>
{poc.hq_address_street_1}
{poc.hq_address_street_1 ? <br /> : null}
<AddressStreetOptional street={poc.hq_address_street_1} />
<AddressStreetOptional street={poc.hq_address_street_2} />
<AddressStreetOptional street={poc.hq_address_street_3} />
<AddressStreetOptional street={poc.hq_address_street_4} />
{poc.hq_address_city ? <>{poc.hq_address_city},&nbsp;</> : null}
{poc.hq_address_state_code} {poc.hq_address_zip}
{poc.hq_address_state} {poc.hq_address_zip}
</>
) : null
}
Expand Down
29 changes: 24 additions & 5 deletions src/pages/Filing/FilingApp/InstitutionCard.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type StatusProperties = SecondaryButtonType & StatusCardType;
export function deriveCardContent({
status,
lei,
filingPeriod = '2024',
}: InstitutionDataType): StatusProperties {
let title = '';
let description = '';
Expand All @@ -38,13 +39,31 @@ export function deriveCardContent({

// TODO: Account for all states found at: https://github.com/cfpb/sbl-filing-api/blob/main/src/sbl_filing_api/entities/models/model_enums.py
switch (status) {
case FilingStatusAsString.TYPES_OF_INSTITUTION: {
title = 'Start the filing process (institution)'; // TODO: Remove "(institution)" bit
description =
'Our system will guide you through each step of the filing process from file upload and error and validation checks to providing identifying and contact information for your financial institution. You will be asked to verify the contents of your filing before you sign and certify';

mainButtonLabel = 'Start filing';
mainButtonDestination = `/institution/${lei}/type/${filingPeriod}`;
break;
}
case FilingStatusAsString.START_A_FILING: {
title = 'Start the filing process (filing)'; // TODO: Remove "(filing)" bit
description =
'Our system will guide you through each step of the filing process from file upload and error and validation checks to providing identifying and contact information for your financial institution. You will be asked to verify the contents of your filing before you sign and certify';

mainButtonLabel = 'Start filing';
mainButtonDestination = `/filing/${filingPeriod}/${lei}/create`;
break;
}
case FilingStatusAsString.VALIDATION_WITH_WARNINGS: {
title = 'Resolve warnings in your lending data';
description =
'If you need to upload a new small business lending file, the previously completed filing will not be overridden until all edits have been cleared and verified, and the new file has been submitted.';

mainButtonLabel = 'Resolve warnings';
mainButtonDestination = `/filing/2024/${lei}/warnings`;
mainButtonDestination = `/filing/${filingPeriod}/${lei}/warnings`;
break;
}
case FilingStatusAsString.VALIDATION_WITH_ERRORS: {
Expand All @@ -53,7 +72,7 @@ export function deriveCardContent({
'If you need to upload a new small business lending file, the previously completed filing will not be overridden until all edits have been cleared and verified, and the new file has been submitted.';

mainButtonLabel = 'Review errors';
mainButtonDestination = `/filing/2024/${lei}/errors`;
mainButtonDestination = `/filing/${filingPeriod}/${lei}/errors`;
break;
}
case POINT_OF_CONTACT: {
Expand All @@ -62,7 +81,7 @@ export function deriveCardContent({
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.';

mainButtonLabel = 'Provide point of contact';
mainButtonDestination = `/filing/2024/${lei}/contact`;
mainButtonDestination = `/filing/${filingPeriod}/${lei}/contact`;
break;
}
case SIGN_SUBMIT: {
Expand All @@ -71,7 +90,7 @@ export function deriveCardContent({
'If you need to upload a new small business lending file, the previously completed filing will not be overridden until all edits have been cleared and verified, and the new file has been submitted.';

mainButtonLabel = 'Sign and submit';
mainButtonDestination = `/filing/2024/${lei}/submit`;
mainButtonDestination = `/filing/${filingPeriod}/${lei}/submit`;
break;
}
case STATUS_PROVIDE_INSTITUTION: {
Expand All @@ -93,7 +112,7 @@ export function deriveCardContent({
'The filing period is open and available to accept small business lending data. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et.';

mainButtonLabel = 'Upload file';
mainButtonDestination = `/filing/2024/${lei}/upload`;
mainButtonDestination = `/filing/${filingPeriod}/${lei}/upload`;

secondaryButtonLabel = 'View your financial institution profile';
secondaryButtonDestination = `/institution/${lei}`;
Expand Down
115 changes: 88 additions & 27 deletions src/pages/Filing/FilingApp/InstitutionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import type { JSXElement } from 'design-system-react/dist/types/jsxElement';
import type { JSX } from 'react';
import { useNavigate } from 'react-router-dom';
import type { FilingType, SubmissionResponse } from 'types/filingTypes';
import { FilingStatusAsString } from 'types/filingTypes';
import type { InstitutionDetailsApiType } from 'types/formTypes';
import { useFilingAndSubmissionInfo } from 'utils/useFilingAndSubmissionInfo';
import useInstitutionDetails from 'utils/useInstitutionDetails';
import { getFilingSteps } from './FilingSteps.helpers';
import { UI_STEPS, deriveCardContent } from './InstitutionCard.helpers';
import type {
Expand All @@ -12,6 +15,12 @@ import type {
} from './InstitutionCard.types';
import InstitutionHeading from './InstitutionHeading';

const institutionHasTypes = (
institution: InstitutionDetailsApiType,
): boolean => {
return Number(institution.sbl_institution_types.length) > 0;
};

// Conditionally display a secondary action button
function SecondaryButton({
secondaryButtonLabel,
Expand All @@ -36,19 +45,28 @@ function SecondaryButton({
}

// Fetch and format the Institution filing status for a given filing period
function FilingStatus({
lei,
function NextStep({
filing,
submission,
}: InstitutionDataType & {
filing: FilingType;
institution,
}: {
// eslint-disable-next-line react/require-default-props
filing?: FilingType;
submission: SubmissionResponse;
institution: InstitutionDetailsApiType;
}): JSX.Element {
const navigate = useNavigate();
const { lei } = institution;
let status;

const { nextStepIndex } = getFilingSteps(submission, filing);

const status = UI_STEPS[nextStepIndex];
if (!institutionHasTypes(institution))
status = FilingStatusAsString.TYPES_OF_INSTITUTION;
else if (filing) {
const { nextStepIndex } = getFilingSteps(submission, filing);
status = UI_STEPS[nextStepIndex];
} else {
status = FilingStatusAsString.START_A_FILING;
}

const {
title,
Expand All @@ -59,7 +77,7 @@ function FilingStatus({
secondaryButtonLabel,
secondaryButtonDestination,
onClick,
} = deriveCardContent({ status, lei });
} = deriveCardContent({ status, lei, filingPeriod: '2024' }); // Post-MVP: Get filingPeriod value from a selector

const onButtonClick = (): void =>
onClick ? onClick() : navigate(mainButtonDestination);
Expand All @@ -81,6 +99,26 @@ function FilingStatus({
);
}

/**
* Helper component to share styling and structure
*/
function InstitutionContentWrapper({
children,
...others
}: {
children: JSX.Element;
lei: InstitutionDetailsApiType['lei'];
name: InstitutionDetailsApiType['name'];
filingPeriod: FilingType['filing_period'];
}): JSX.Element {
return (
<div className='mb-8 border-solid border-gray-300 p-6'>
<InstitutionHeading {...others} />
{children}
</div>
);
}

/**
* Fetch requisite data to determine Filing status.
*/
Expand All @@ -89,30 +127,53 @@ function InstitutionCardDataWrapper({
name,
filingPeriod,
}: InstitutionDataType): JSXElement {
const { error, filing, isLoading, submission } = useFilingAndSubmissionInfo({
const {
error: filingDataError,
filing,
isLoading: isSubmissionLoading,
submission,
} = useFilingAndSubmissionInfo({
lei,
filingPeriod,
});

return (
<div className='mb-8 border-solid border-gray-300 p-6'>
<InstitutionHeading {...{ lei, name, filingPeriod }} />
{error ? (
const {
isLoading: isInstitutionLoading,
data: institution,
error: institutionError,
} = useInstitutionDetails(lei);

const sharedContentProperties = { lei, name, filingPeriod };

const error = institutionError || filingDataError;

const isLoading = isInstitutionLoading || isSubmissionLoading;

if (error)
return (
<InstitutionContentWrapper {...sharedContentProperties}>
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */}
<Alert status='error' message={error.message} />
) : isLoading ? (
<div>
<Icon name='updating' /> Loading submission status...
</div>
) : (
<FilingStatus
{...{
lei,
filing,
submission,
}}
/>
)}
</div>
</InstitutionContentWrapper>
);

if (isLoading)
return (
<InstitutionContentWrapper {...sharedContentProperties}>
<Icon name='updating' /> Loading filing data...
</InstitutionContentWrapper>
);

return (
<InstitutionContentWrapper {...sharedContentProperties}>
<NextStep
{...{
filing,
submission,
institution,
}}
/>
</InstitutionContentWrapper>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import type {
UseFormWatch,
} from 'react-hook-form';
import type { InstitutionDetailsApiType } from 'types/formTypes';
import { SLB_INSTITUTION_TYPE_OTHER } from 'utils/constants';
import type { CheckboxOption, UpdateInstitutionType } from './types';
import { checkboxOptions } from './types';

const SLB_INSTITUTION_TYPE_OTHER = '13';
const OTHER_ID = `sbl_institution_types.${SLB_INSTITUTION_TYPE_OTHER}`;

interface TypesFinancialInstitutionSectionProperties {
Expand Down Expand Up @@ -89,7 +89,7 @@ function TypesFinancialInstitutionSection({
})}
</List>
<InputEntry
label='Other'
label=''
id='institutionTypeOther'
disabled={!isOtherChecked}
{...register('sbl_institution_types_other', {
Expand Down
Loading

0 comments on commit 4bfca21

Please sign in to comment.