Skip to content

Commit

Permalink
[EASI-3123] Issue LCID (#2186)
Browse files Browse the repository at this point in the history
* Update field keys to match schema

* Fix undefined IT Investment bug

* Issue LCID mutation

* Add LCID fields to form

* Initial field setup

* Add mutation to onSubmit

* Add useExistingLcid field

* LCID field

* Default lcid value

* Field validation schema

* Implement validation schema & error messages

* Fix LCID error messages

* Fix date picker validation schema

* Refetch intake on mutation success

* Add field errors summary to <ActionForm>

* Issue LCID field errors summary

* Success message text

* Add lcid to query

* Success message

Made <ActionForm> `successMessage` prop optional so that it can be set from individual form if needed

* Update success message text

* Refactor resolutions title

- Refactored resolutions form layout to fix server error alert placement
- Created <ResolutionTitleBox> component

* Refactor Manage LCID title box

* Remove unused <TitleBox> component

* Manage LCID - remove references to resolutions

* ActionForm loading state

* Resolutions title box unit test

* Manage LCID title box unit test

* Add lcid info to GetSystemIntakesWithLCIDS query

* Convert LCID text input to dropdown

- Add GetSystemIntakesWithLCIDS query
- Populate LCID dropdown with existing LCID options
- When LCID is selected, populate fields

* Update LCID error message

* Issue LCID unit test

* Fix fields set from selecting LCID

* Confirmation modal

* Confirmation modal unit test

* Fix actions unit tests

* Fix LCID default value bug

* Fix success message

* Add missing fields to test mock data

* Reset lcid fields

Resets LCID fields to default when "Generate new LCID" is selected

* Fix <DatePickerFormatted> bug

- Fixes bug where <DatePickerFormatted> value cannot be updated dynamically
- Wrapped component in useCallback to force re-render

* Update unit test

- Fixed field label capitalization
- Added test for dynamic expiration date field
  • Loading branch information
aterstriep authored Sep 20, 2023
1 parent 4e90488 commit 7aca6cc
Show file tree
Hide file tree
Showing 30 changed files with 1,369 additions and 278 deletions.
25 changes: 23 additions & 2 deletions src/components/shared/DatePickerFormatted/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback, useMemo } from 'react';
import { DatePicker } from '@trussworks/react-uswds';
// eslint-disable-next-line import/no-unresolved
import { DatePickerProps } from '@trussworks/react-uswds/lib/components/forms/DatePicker/DatePicker';
Expand All @@ -20,8 +20,29 @@ const DatePickerFormatted = ({
}: DatePickerProps & { format?: (dt: DateTime) => string }) => {
const dtFormat = format || defaultFormat;

/** Memoized current field value */
const value = useMemo(() => {
if (typeof props.value === 'string' && props.value.length > 0) {
return props.value;
}

return props.defaultValue;
}, [props.defaultValue, props.value]);

/**
* Fix for bug where <DatePicker> does not rerender to show updated value when set dynamically
*
* Forces re-render of component when props.value or props.defaultValue is updated
*/
const FieldCallback = useCallback(
(fieldProps: DatePickerProps) => {
return <DatePicker {...fieldProps} defaultValue={value} />;
},
[value]
);

return (
<DatePicker
<FieldCallback
{...props}
onChange={val => {
if (typeof onChange === 'function') {
Expand Down
92 changes: 91 additions & 1 deletion src/data/mock/systemIntake.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { CMSOffice } from 'constants/enums/cmsDivisionsAndOffices';
import GetGovernanceTaskListQuery from 'queries/GetGovernanceTaskListQuery';
import GetSystemIntakeQuery from 'queries/GetSystemIntakeQuery';
import GetSystemIntakesWithLCIDS from 'queries/GetSystemIntakesWithLCIDS';
import { GetSystemIntakeContactsQuery } from 'queries/SystemIntakeContactsQueries';
import {
GetGovernanceTaskList,
GetGovernanceTaskListVariables
} from 'queries/types/GetGovernanceTaskList';
import {
GetSystemIntake,
GetSystemIntake_systemIntake as SystemIntake,
Expand All @@ -10,15 +16,27 @@ import {
GetSystemIntakeContacts,
GetSystemIntakeContactsVariables
} from 'queries/types/GetSystemIntakeContacts';
import {
GetSystemIntakesWithLCIDS as GetSystemIntakesWithLCIDSType,
GetSystemIntakesWithLCIDS_systemIntakesWithLcids as SystemIntakeWithLcid
} from 'queries/types/GetSystemIntakesWithLCIDS';
import { SystemIntakeContact } from 'queries/types/SystemIntakeContact';
import { SystemIntakeDocument } from 'queries/types/SystemIntakeDocument';
import {
ITGovDecisionStatus,
ITGovDraftBusinessCaseStatus,
ITGovFeedbackStatus,
ITGovFinalBusinessCaseStatus,
ITGovGRBStatus,
ITGovGRTStatus,
ITGovIntakeFormStatus,
SystemIntakeDecisionState,
SystemIntakeDocumentCommonType,
SystemIntakeDocumentStatus,
SystemIntakeRequestType,
SystemIntakeState,
SystemIntakeStatus
SystemIntakeStatus,
SystemIntakeTRBFollowUp
} from 'types/graphql-global-types';
import { MockedQuery } from 'types/util';

Expand Down Expand Up @@ -238,6 +256,43 @@ export const getSystemIntakeQuery: MockedQuery<
}
};

export const systemIntakeWithLcid: SystemIntakeWithLcid = {
__typename: 'SystemIntake',
id: '8be3f86d-a4d6-446b-8a56-dc9da77ed326',
lcid: '123456',
requestName: 'Test request name',
lcidExpiresAt: '2024-09-21T05:00:00.000Z',
lcidScope: 'Test scope',
decisionNextSteps: 'Test next steps',
trbFollowUpRecommendation: SystemIntakeTRBFollowUp.NOT_RECOMMENDED,
lcidCostBaseline: 'Text cost baseline'
};

export const getSystemIntakesWithLcidsQuery: MockedQuery<GetSystemIntakesWithLCIDSType> = {
request: {
query: GetSystemIntakesWithLCIDS,
variables: {}
},
result: {
data: {
systemIntakesWithLcids: [
systemIntakeWithLcid,
{
__typename: 'SystemIntake',
id: '8be3f86d-a4d6-446b-8a56-dc9da77ed326',
lcid: '654321',
requestName: 'Test request name 2',
lcidExpiresAt: null,
lcidScope: null,
decisionNextSteps: null,
trbFollowUpRecommendation: null,
lcidCostBaseline: null
}
]
}
}
};

export const getSystemIntakeContactsQuery: MockedQuery<
GetSystemIntakeContacts,
GetSystemIntakeContactsVariables
Expand All @@ -257,3 +312,38 @@ export const getSystemIntakeContactsQuery: MockedQuery<
}
}
};

export const getGovernanceTaskListQuery: MockedQuery<
GetGovernanceTaskList,
GetGovernanceTaskListVariables
> = {
request: {
query: GetGovernanceTaskListQuery,
variables: {
id: systemIntakeId
}
},
result: {
data: {
systemIntake: {
__typename: 'SystemIntake',
id: systemIntakeId,
itGovTaskStatuses: {
__typename: 'ITGovTaskStatuses',
intakeFormStatus: ITGovIntakeFormStatus.READY,
feedbackFromInitialReviewStatus: ITGovFeedbackStatus.CANT_START,
bizCaseDraftStatus: ITGovDraftBusinessCaseStatus.CANT_START,
grtMeetingStatus: ITGovGRTStatus.CANT_START,
bizCaseFinalStatus: ITGovFinalBusinessCaseStatus.CANT_START,
grbMeetingStatus: ITGovGRBStatus.CANT_START,
decisionAndNextStepsStatus: ITGovDecisionStatus.CANT_START
},
governanceRequestFeedbacks: [],
submittedAt: null,
updatedAt: null,
grtDate: null,
grbDate: null
}
}
}
};
54 changes: 43 additions & 11 deletions src/i18n/en-US/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,18 @@ const action = {
'Use this action if you want to modify a retired Life Cycle ID in any way. You will have the option to maintain its retired status or reinstate it.'
}
},
errorLabels: {
useExistingLcid: 'Life Cycle ID',
lcid: 'Life Cycle ID',
expiresAt: 'Expiration date',
scope: 'Scope of Life Cycle ID',
nextSteps: 'Next steps',
trbFollowUp: 'TRB follow-up'
},
manageLcid: {
title: 'Manage a Life Cycle ID (LCID)',
breadcrumb: 'Manage a Life Cycle ID',
label: 'Action',
description:
'Update the status or details of a previously-issued Life Cycle ID by expiring it, retiring it, or updating it (Change the expiration date, scope, next steps, or project cost baseline).',
description_EXPIRED:
Expand All @@ -84,7 +93,9 @@ const action = {
expire: 'Expire',
expireDescription:
'LCIDs will automatically expire when they reach their expiration date. Manually expiring an LCID will update the expiration date and set the status to “Expired”. Expired LCIDs are usually unplanned or unintended, and result in the project being added to the CIO risk register. This project team will continue to receive automatic notifications about their Life Cycle ID.'
}
},
success:
'Life Cycle ID {{lcid}} is issued for this request. This request is now closed. You may continue to manage this Life Cycle ID using the "Manage a Life Cycle ID" action.'
},
titleBox: {
selected: 'Selected {{type}}',
Expand Down Expand Up @@ -195,30 +206,51 @@ const action = {
subheading: 'How to proceed?',
lifecycleId: 'Lifecycle ID',
feedbackLabel: 'This email will be sent to recipients',
nextStepsLabel: 'Next Steps',
nextStepsLabel: 'Next steps',
nextStepsHelpText:
'Provide the requester with some recommendations on how to continue their process. For example, “begin your ATO” or “update your System Profile”. This will help the requester stay on track after they receive their Lifecycle ID.',
scopeLabel: 'Scope of Lifecycle ID',
'Provide the team with recommendations on how best to continue the process and stay on track with their project. For example “start your ATO” or “complete acquisition planning”.',
scopeLabel: 'Scope of Life Cycle ID',
scopeHelpText:
'Tell the requester what is covered by this Lifecycle ID and what work it limits the project team to.',
costBaselineLabel: 'Project Cost Baseline (Optional)',
costBaselineLabel: 'Project cost baseline',
costBaselineHelpText:
'Enter the current cost baseline for the project for the first two planned fiscal years of the project',
'Include the cost baseline for the first two planned fiscal years of the project.',
lcid: {
new: 'Generate a new Lifecycle ID',
new: 'Generate a new Life Cycle ID',
helpText:
'If you choose to generate a new Lifecycle ID, one will be generated when you submit this page',
existing: 'Use an existing Lifecycle ID',
label: 'Lifecycle ID'
'If you choose to generate a new Life Cycle ID, it will be generated when you complete this action.',
existing: 'Use an existing Life Cycle ID',
label: 'Life Cycle ID'
},
select: {
label: 'Select the Life Cycle ID (LCID) for this request.',
helpText:
'Select an existing LCID from EASi. Selecting a LCID will pre-populate its information below, allowing you to edit it as needed.'
},
expirationDate: {
label: 'Expiration Date',
label: 'Expiration date',
helpText: 'For example 08 02 1776',
month: 'Month',
day: 'Day',
year: 'Year'
},
trbFollowup: {
label:
'Should this team consult with the Technical Review Board (TRB) as a part of their next steps?',
stronglyRecommended: 'Yes, strongly recommend',
recommendedNotCritical:
'Yes, it’s not critical but the TRB could provide useful feedback',
notRecommended: 'No, they may if they wish but it’s not necessary'
}
},
decisionModal: {
title: 'Are you sure you want to complete this action?',
content:
'You previously requested that the team make changes to their {{action}}. Completing this decision action will remove the “Edits requested” status from that form, and the requester will no longer be able to make any changes.',
intakeRequest: 'intake request form',
draftBusinessCase: 'draft business case',
finalBusinessCase: 'final business case'
},
extendLcid: {
back: 'Change',
heading: 'Actions on request',
Expand Down
14 changes: 14 additions & 0 deletions src/queries/CreateSystemIntakeActionIssueLcidQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { gql } from '@apollo/client';

export default gql`
mutation CreateSystemIntakeActionIssueLcid(
$input: SystemIntakeIssueLCIDInput!
) {
createSystemIntakeActionIssueLCID(input: $input) {
systemIntake {
id
lcid
}
}
}
`;
5 changes: 5 additions & 0 deletions src/queries/GetSystemIntakesWithLCIDS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export default gql`
id
lcid
requestName
lcidExpiresAt
lcidScope
decisionNextSteps
trbFollowUpRecommendation
lcidCostBaseline
}
}
`;
29 changes: 29 additions & 0 deletions src/queries/types/CreateSystemIntakeActionIssueLcid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.

import { SystemIntakeIssueLCIDInput } from "./../../types/graphql-global-types";

// ====================================================
// GraphQL mutation operation: CreateSystemIntakeActionIssueLcid
// ====================================================

export interface CreateSystemIntakeActionIssueLcid_createSystemIntakeActionIssueLCID_systemIntake {
__typename: "SystemIntake";
id: UUID;
lcid: string | null;
}

export interface CreateSystemIntakeActionIssueLcid_createSystemIntakeActionIssueLCID {
__typename: "UpdateSystemIntakePayload";
systemIntake: CreateSystemIntakeActionIssueLcid_createSystemIntakeActionIssueLCID_systemIntake | null;
}

export interface CreateSystemIntakeActionIssueLcid {
createSystemIntakeActionIssueLCID: CreateSystemIntakeActionIssueLcid_createSystemIntakeActionIssueLCID | null;
}

export interface CreateSystemIntakeActionIssueLcidVariables {
input: SystemIntakeIssueLCIDInput;
}
7 changes: 7 additions & 0 deletions src/queries/types/GetSystemIntakesWithLCIDS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// @generated
// This file was automatically generated and should not be edited.

import { SystemIntakeTRBFollowUp } from "./../../types/graphql-global-types";

// ====================================================
// GraphQL query operation: GetSystemIntakesWithLCIDS
// ====================================================
Expand All @@ -12,6 +14,11 @@ export interface GetSystemIntakesWithLCIDS_systemIntakesWithLcids {
id: UUID;
lcid: string | null;
requestName: string | null;
lcidExpiresAt: Time | null;
lcidScope: HTML | null;
decisionNextSteps: HTML | null;
trbFollowUpRecommendation: SystemIntakeTRBFollowUp | null;
lcidCostBaseline: string | null;
}

export interface GetSystemIntakesWithLCIDS {
Expand Down
25 changes: 25 additions & 0 deletions src/types/graphql-global-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ export enum SystemIntakeStatus {
WITHDRAWN = "WITHDRAWN",
}

/**
* Different options for whether the Governance team believes a requester's team should consult with the TRB
*/
export enum SystemIntakeTRBFollowUp {
NOT_RECOMMENDED = "NOT_RECOMMENDED",
RECOMMENDED_BUT_NOT_CRITICAL = "RECOMMENDED_BUT_NOT_CRITICAL",
STRONGLY_RECOMMENDED = "STRONGLY_RECOMMENDED",
}

/**
* Represents the category of a single TRB admin note
*/
Expand Down Expand Up @@ -844,6 +853,22 @@ export interface SystemIntakeISSOInput {
name?: string | null;
}

/**
* Input for setting an intake's decision to issuing an LCID in IT Gov v2
*/
export interface SystemIntakeIssueLCIDInput {
systemIntakeID: UUID;
lcid?: string | null;
expiresAt: Time;
scope: HTML;
nextSteps: HTML;
trbFollowUp: SystemIntakeTRBFollowUp;
costBaseline?: string | null;
additionalInfo?: HTML | null;
notificationRecipients?: EmailNotificationRecipients | null;
adminNote?: HTML | null;
}

/**
* The input data used to set the CMS product manager/lead of a system
*/
Expand Down
Loading

0 comments on commit 7aca6cc

Please sign in to comment.