diff --git a/services/app-api/src/common-code/featureFlags/flags.ts b/services/app-api/src/common-code/featureFlags/flags.ts
index 32af51befc..61bac0122f 100644
--- a/services/app-api/src/common-code/featureFlags/flags.ts
+++ b/services/app-api/src/common-code/featureFlags/flags.ts
@@ -10,7 +10,7 @@ const featureFlags = {
flag: '438-attestation',
defaultValue: false,
},
- /**
+ /**
* Enables state and CMS rate edit, unlock, resubmit functionality
*/
RATE_EDIT_UNLOCK: {
@@ -22,7 +22,7 @@ const featureFlags = {
/**
Enables the modal that alerts the user to an expiring session
*/
- SESSION_EXPIRING_MODAL: {
+ SESSION_EXPIRING_MODAL: {
flag: 'session-expiring-modal',
defaultValue: true,
},
@@ -36,7 +36,7 @@ const featureFlags = {
/**
Toggles the site maintenance alert on the landing page
*/
- SITE_UNDER_MAINTENANCE_BANNER: {
+ SITE_UNDER_MAINTENANCE_BANNER: {
flag: 'site-under-maintenance-banner',
defaultValue: 'OFF',
},
diff --git a/services/app-web/src/common-code/ContractType.ts b/services/app-web/src/common-code/ContractType.ts
index 4cb3bc6278..8c65cf10bd 100644
--- a/services/app-web/src/common-code/ContractType.ts
+++ b/services/app-web/src/common-code/ContractType.ts
@@ -1,43 +1,43 @@
-import { Contract, ContractRevision } from '../gen/gqlClient'
+import { Contract, ContractRevision, UnlockedContract } from '../gen/gqlClient'
import { getLastContractSubmission } from '../gqlHelpers/contractsAndRates'
-const getContractRev = (contract: Contract): ContractRevision | undefined => {
+const getContractRev = (contract: Contract | UnlockedContract): ContractRevision | undefined => {
if (contract.draftRevision) {
return contract.draftRevision
} else {
return getLastContractSubmission(contract)?.contractRevision
}
}
-const isContractOnly = (contract: Contract): boolean => {
+const isContractOnly = (contract: Contract | UnlockedContract): boolean => {
const contractRev = getContractRev(contract)
return contractRev?.formData?.submissionType === 'CONTRACT_ONLY'
}
-const isBaseContract = (contract: Contract): boolean => {
+const isBaseContract = (contract: Contract | UnlockedContract): boolean => {
const contractRev = getContractRev(contract)
return contractRev?.formData?.contractType === 'BASE'
}
-const isContractAmendment = (contract: Contract): boolean => {
+const isContractAmendment = (contract: Contract | UnlockedContract): boolean => {
const contractRev = getContractRev(contract)
return contractRev?.formData?.contractType === 'AMENDMENT'
}
-const isCHIPOnly = (contract: Contract): boolean => {
+const isCHIPOnly = (contract: Contract | UnlockedContract): boolean => {
const contractRev = getContractRev(contract)
return contractRev?.formData?.populationCovered === 'CHIP'
}
-const isContractAndRates = (contract: Contract): boolean => {
+const isContractAndRates = (contract: Contract | UnlockedContract): boolean => {
const contractRev = getContractRev(contract)
return contractRev?.formData?.submissionType === 'CONTRACT_AND_RATES'
}
-const isContractWithProvisions = (contract: Contract): boolean =>
+const isContractWithProvisions = (contract: Contract | UnlockedContract): boolean =>
isContractAmendment(contract) || (isBaseContract(contract) && !isCHIPOnly(contract))
-const isSubmitted = (contract: Contract): boolean =>
+const isSubmitted = (contract: Contract | UnlockedContract): boolean =>
contract.status === 'SUBMITTED'
export {
diff --git a/services/app-web/src/common-code/ContractTypeProvisions.ts b/services/app-web/src/common-code/ContractTypeProvisions.ts
index bf618bced9..906f11c14d 100644
--- a/services/app-web/src/common-code/ContractTypeProvisions.ts
+++ b/services/app-web/src/common-code/ContractTypeProvisions.ts
@@ -3,7 +3,7 @@ import {
ModifiedProvisionsBaseContractRecord,
ModifiedProvisionsCHIPRecord,
} from '../constants/modifiedProvisions'
-import { Contract } from '../gen/gqlClient'
+import { Contract, UnlockedContract } from '../gen/gqlClient'
import {
CHIPProvisionType,
MedicaidBaseProvisionType,
@@ -38,7 +38,7 @@ import { getLastContractSubmission } from '../gqlHelpers/contractsAndRates'
// Returns the list of provision keys that apply for given submission variant
const generateApplicableProvisionsList = (
- draftSubmission: Contract
+ draftSubmission: Contract | UnlockedContract
):
| CHIPProvisionType[]
| MedicaidBaseProvisionType[]
@@ -56,7 +56,7 @@ const generateApplicableProvisionsList = (
// Returns user-friendly label text for the provision based on the given submission variant
const generateProvisionLabel = (
- draftSubmission: Contract,
+ draftSubmission: Contract | UnlockedContract,
provision: GeneralizedProvisionType
): string => {
if (isCHIPOnly(draftSubmission) && isCHIPProvision(provision)) {
@@ -83,7 +83,7 @@ const generateProvisionLabel = (
That functionality needed for unlocked contracts which can be edited in a non-linear fashion)
*/
const sortModifiedProvisions = (
- contract: Contract
+ contract: Contract | UnlockedContract
): [GeneralizedProvisionType[], GeneralizedProvisionType[]] => {
const contractFormData = contract.draftRevision?.formData || getLastContractSubmission(contract)?.contractRevision.formData
const initialProvisions = {
@@ -132,7 +132,7 @@ const sortModifiedProvisions = (
Returns boolean for whether a submission variant is missing required provisions
This is used to determine if we display the missing data warning on review and submit
*/
-const isMissingProvisions = (contract: Contract): boolean => {
+const isMissingProvisions = (contract: Contract | UnlockedContract): boolean => {
const requiredProvisions = generateApplicableProvisionsList(contract)
const [modifiedProvisions, unmodifiedProvisions] =
sortModifiedProvisions(contract)
@@ -147,7 +147,7 @@ const isMissingProvisions = (contract: Contract): boolean => {
Returns lang string dictionary for variant
*/
const getProvisionDictionary = (
- contract: Contract
+ contract: Contract | UnlockedContract
):
| typeof ModifiedProvisionsCHIPRecord
| typeof ModifiedProvisionsBaseContractRecord
diff --git a/services/app-web/src/components/Banner/UserAccountWarningBanner/UserAccountWarningBanner.tsx b/services/app-web/src/components/Banner/UserAccountWarningBanner/UserAccountWarningBanner.tsx
index 1bbb61a641..eb12ca83f2 100644
--- a/services/app-web/src/components/Banner/UserAccountWarningBanner/UserAccountWarningBanner.tsx
+++ b/services/app-web/src/components/Banner/UserAccountWarningBanner/UserAccountWarningBanner.tsx
@@ -22,7 +22,7 @@ const UserAccountWarningBanner = ({
type: 'warn',
extension: 'react-uswds',
})
- }, [logAlertImpressionEvent,message])
+ }, [logAlertImpressionEvent, message])
return (
diff --git a/services/app-web/src/components/ErrorAlert/ErrorAlert.tsx b/services/app-web/src/components/ErrorAlert/ErrorAlert.tsx
index 7e711b50d4..b3ad253c4b 100644
--- a/services/app-web/src/components/ErrorAlert/ErrorAlert.tsx
+++ b/services/app-web/src/components/ErrorAlert/ErrorAlert.tsx
@@ -28,7 +28,7 @@ export const ErrorAlert = ({
const showLink = appendLetUsKnow || !message // our default message includes the link
const defaultMessage =
"We're having trouble loading this page. Please refresh your browser and if you continue to experience an error,"
- const logErrorMessage = `${message ? extractText(message) : defaultMessage} email ${MAIL_TO_SUPPORT}`
+ const logErrorMessage = `${message ? extractText(message) : defaultMessage} email ${MAIL_TO_SUPPORT}`
useEffect(() => {
logAlertImpressionEvent({
@@ -37,7 +37,7 @@ export const ErrorAlert = ({
type: 'error',
extension: 'react-uswds',
})
- }, [logAlertImpressionEvent,logErrorMessage])
+ }, [logAlertImpressionEvent, logErrorMessage])
return (
{
// This effect should only fire on initial app load
useEffect(() => {
initializeTealium()
- // eslint-disable-next-line react-hooks/exhaustive-deps
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// This effect should only fire each time the url changes
diff --git a/services/app-web/src/gqlHelpers/contractsAndRates.ts b/services/app-web/src/gqlHelpers/contractsAndRates.ts
index 463b3c9808..04bd48dfe0 100644
--- a/services/app-web/src/gqlHelpers/contractsAndRates.ts
+++ b/services/app-web/src/gqlHelpers/contractsAndRates.ts
@@ -3,7 +3,7 @@ These helpers help you access nested data from the Contract and Rate Apollo Clie
If the data doesn't exist, returns undefined reliably
*/
-import { Contract, ContractFormData, ContractPackageSubmission, ContractRevision, Rate, RateRevision } from "../gen/gqlClient"
+import { Contract, ContractFormData, ContractPackageSubmission, ContractRevision, Rate, RateRevision, UnlockedContract } from "../gen/gqlClient"
import {ActuaryContact} from '../common-code/healthPlanFormDataType';
import {ActuaryFirmsRecord} from '../constants';
@@ -13,7 +13,7 @@ type RateRevisionWithIsLinked = {
// This function returns a revision with isLinked field as well manually calculated from parent rate
// There are cases where we need the revision itself to be able to track its own linked status, decontextualized from the parent rate or parent contract
-function getVisibleLatestRateRevisions(contract: Contract, isEditing: boolean): RateRevisionWithIsLinked[] | undefined {
+function getVisibleLatestRateRevisions(contract: Contract | UnlockedContract, isEditing: boolean): RateRevisionWithIsLinked[] | undefined {
if (isEditing) {
if (!contract.draftRates) {
console.error('Programming Error: on the rate details page with no draft rates')
@@ -67,7 +67,7 @@ function getVisibleLatestRateRevisions(contract: Contract, isEditing: boolean):
// returns draft form data for unlocked and draft, and last package submission data for submitted or resubmitted
// only state users get to see draft data.
-const getVisibleLatestContractFormData = (contract: Contract | ContractRevision, isStateUser: boolean): ContractFormData | undefined =>{
+const getVisibleLatestContractFormData = (contract: Contract | UnlockedContract| ContractRevision, isStateUser: boolean): ContractFormData | undefined =>{
if (contract.__typename === 'Contract') {
if (isStateUser) {
return contract.draftRevision?.formData ||
@@ -79,15 +79,15 @@ const getVisibleLatestContractFormData = (contract: Contract | ContractRevision,
}
}
-const getLastContractSubmission = (contract: Contract): ContractPackageSubmission | undefined => {
+const getLastContractSubmission = (contract: Contract | UnlockedContract): ContractPackageSubmission | undefined => {
return (contract.packageSubmissions && contract.packageSubmissions[0]) ?? undefined
}
-const getPackageSubmissionAtIndex = (contract: Contract, indx: number): ContractPackageSubmission | undefined => {
+const getPackageSubmissionAtIndex = (contract: Contract | UnlockedContract, indx: number): ContractPackageSubmission | undefined => {
return (contract.packageSubmissions[indx]) ?? undefined
}
// revisionVersion is a integer used in the URLs for previous submission - numbering the submission in order from first submitted
-const getIndexFromRevisionVersion = (contract: Contract, revisionVersion: number) => contract.packageSubmissions.length - (Number(revisionVersion) - 1)
+const getIndexFromRevisionVersion = (contract: Contract | UnlockedContract, revisionVersion: number) => contract.packageSubmissions.length - (Number(revisionVersion) - 1)
const getDraftRates = (contract: Contract): Rate[] | undefined => {
return (contract.draftRates && contract.draftRates[0]) ? contract.draftRates : undefined
}
diff --git a/services/app-web/src/hooks/useContractForm.ts b/services/app-web/src/hooks/useContractForm.ts
new file mode 100644
index 0000000000..491b8d0bc8
--- /dev/null
+++ b/services/app-web/src/hooks/useContractForm.ts
@@ -0,0 +1,243 @@
+import { useState, useEffect} from 'react'
+import { usePage } from "../contexts/PageContext"
+import {
+ CreateContractInput,
+ useFetchContractQuery,
+ useCreateContractMutation,
+ useUpdateContractDraftRevisionMutation,
+ Contract,
+ Rate,
+ GenericDocument,
+ GenericDocumentInput,
+ UnlockedContract,
+ UpdateContractDraftRevisionInput,
+ ContractPackageSubmission
+} from '../gen/gqlClient'
+import { recordJSException } from '../otelHelpers'
+import { handleApolloError } from '../gqlHelpers/apolloErrors'
+import { ApolloError } from '@apollo/client'
+import type { InterimState } from '../pages/StateSubmission/ErrorOrLoadingPage'
+
+
+type UseContractForm = {
+ draftSubmission?: UnlockedContract
+ showPageErrorMessage: string | boolean
+ previousDocuments?: string[]
+ updateDraft: (
+ input: UpdateContractDraftRevisionInput
+ ) => Promise
+ createDraft: (input: CreateContractInput) => Promise
+ interimState?: InterimState
+}
+
+const documentsInput = (documents: GenericDocument[]): GenericDocumentInput[] => {
+ return documents.map((doc) => {
+ return {
+ downloadURL: doc.downloadURL,
+ name: doc.name,
+ s3URL: doc.s3URL,
+ sha256: doc.sha256
+ }
+ })
+}
+
+const useContractForm = (contractID?: string): UseContractForm => {
+ // Set up defaults for the return value for hook
+ let interimState: UseContractForm['interimState'] = undefined // enum to determine what Interim UI should override form page
+ let previousDocuments: UseContractForm['previousDocuments'] = [] // used for document upload tables
+ let draftSubmission: UseContractForm['draftSubmission'] = undefined // form data from current package revision, used to load form
+ const [showPageErrorMessage, setShowPageErrorMessage] = useState(false) // string is a custom error message, defaults to generic of true
+ const { updateHeading } = usePage()
+ const [pkgNameForHeading, setPkgNameForHeading] = useState(undefined)
+
+ useEffect(() => {
+ updateHeading({ customHeading: pkgNameForHeading })
+ }, [pkgNameForHeading, updateHeading])
+
+ const [createFormData] = useCreateContractMutation()
+
+ const createDraft: UseContractForm['createDraft'] = async (
+ input: CreateContractInput
+ ): Promise => {
+ setShowPageErrorMessage(false)
+ const { populationCovered, programIDs, riskBasedContract, submissionType, submissionDescription, contractType } = input
+ try {
+ const createResult = await createFormData({
+ variables: {
+ input: {
+ populationCovered,
+ programIDs,
+ riskBasedContract,
+ submissionType,
+ submissionDescription,
+ contractType,
+ },
+ }})
+ const createdSubmission: Contract | undefined =
+ createResult?.data?.createContract.contract
+
+ if (!createdSubmission) {
+ setShowPageErrorMessage(true)
+ console.info('Failed to update form data', createResult)
+ recordJSException(
+ `StateSubmissionForm: Apollo error reported. Error message: Failed to create form data ${createResult}`
+ )
+ return new Error('Failed to create form data')
+ }
+
+ return createdSubmission
+ } catch (serverError) {
+ setShowPageErrorMessage(true)
+ recordJSException(
+ `StateSubmissionForm: Apollo error reported. Error message: ${serverError.message}`
+ )
+ return new Error(serverError)
+ }
+ }
+ const [updateFormData] = useUpdateContractDraftRevisionMutation()
+
+ const updateDraft: UseContractForm['updateDraft'] = async (
+ input: UpdateContractDraftRevisionInput
+ ): Promise => {
+
+ setShowPageErrorMessage(false)
+ if (input.formData.contractDocuments && input.formData.contractDocuments.length > 0) {
+ input.formData.contractDocuments = documentsInput(input.formData.contractDocuments)
+ }
+ try {
+ const updateResult = await updateFormData({
+ variables: {
+ input: {
+ contractID: contractID ?? 'new-draft',
+ lastSeenUpdatedAt: contract!.draftRevision!.updatedAt,
+ formData: input.formData
+ },
+ },
+ })
+ const updatedSubmission =
+ updateResult?.data?.updateContractDraftRevision.contract
+ if (!updatedSubmission) {
+ setShowPageErrorMessage(true)
+ console.info('Failed to update form data', updateResult)
+ recordJSException(
+ `StateSubmissionForm: Apollo error reported. Error message: Failed to update form data ${updateResult}`
+ )
+ return new Error('Failed to update form data')
+ }
+ return updatedSubmission
+ } catch (serverError) {
+ setShowPageErrorMessage(true)
+ recordJSException(
+ `StateSubmissionForm: Apollo error reported. Error message: ${serverError.message}`
+ )
+ return new Error(serverError)
+ }
+ }
+ const {
+ data: fetchResultData,
+ error: fetchResultError,
+ loading: fetchResultLoading
+ } = useFetchContractQuery({
+ variables: {
+ input: {
+ contractID: contractID ?? 'new-draft'
+ }
+ },
+ skip: !contractID
+ })
+ const contract = fetchResultData?.fetchContract.contract
+ if (fetchResultLoading) {
+ interimState = 'LOADING'
+ return {interimState, createDraft, updateDraft, showPageErrorMessage }
+ }
+ if (fetchResultError) {
+ const err = fetchResultError
+ if (err instanceof ApolloError){
+ handleApolloError(err, true)
+ if (err.graphQLErrors[0]?.extensions?.code === 'NOT_FOUND') {
+ interimState = 'NOT_FOUND'
+ return {interimState, createDraft, updateDraft, showPageErrorMessage }
+ }
+ }
+ if (err.name !== 'SKIPPED') {
+ recordJSException(err)
+ interimState = 'GENERIC_ERROR'// api failure or protobuf decode failure
+ return { interimState, createDraft, updateDraft, showPageErrorMessage}
+ }
+
+ if (!contract || !contract.draftRevision || !contract.draftRevision.formData || contract?.status === 'RESUBMITTED' || contract?.status === 'SUBMITTED') {
+ interimState = 'GENERIC_ERROR'// api failure or protobuf decode failure
+ return { interimState, createDraft, updateDraft, showPageErrorMessage}
+ }
+ const rates:Rate[] = []
+ const packageSubmissions:ContractPackageSubmission[] = []
+ const unlockedContract:UnlockedContract = {
+ ...contract,
+ id: contract!.id,
+ createdAt: contract.createdAt,
+ updatedAt: contract.updatedAt,
+ stateCode: contract.stateCode,
+ stateNumber: contract.stateNumber,
+ status: contract.status,
+ draftRevision: {
+ ...contract.draftRevision,
+ id: contract.id,
+ contractName: contract.draftRevision.contractName,
+ createdAt: contract.draftRevision.createdAt,
+ updatedAt: contract.draftRevision.updatedAt,
+ __typename: 'ContractRevision',
+ formData: {
+ ...contract.draftRevision.formData,
+ __typename: 'ContractFormData'
+ }
+ },
+ draftRates: contract.draftRates || rates,
+ packageSubmissions: contract.packageSubmissions || packageSubmissions,
+ __typename: 'UnlockedContract'
+ }
+ draftSubmission = unlockedContract
+ return {interimState, createDraft, updateDraft, draftSubmission, showPageErrorMessage}
+
+ }
+
+ if (!contract || !contract.draftRevision || !contract.draftRevision.formData || contract?.status === 'RESUBMITTED' || contract?.status === 'SUBMITTED') {
+ return {interimState, createDraft, updateDraft, showPageErrorMessage }
+ }
+ const submissionName = contract.draftRevision?.contractName
+ if (pkgNameForHeading !== submissionName) {
+ setPkgNameForHeading(submissionName)
+ }
+
+ const rates:Rate[] = []
+ const packageSubmissions:ContractPackageSubmission[] = []
+ const unlockedContract:UnlockedContract = {
+ ...contract,
+ id: contract!.id,
+ createdAt: contract.createdAt,
+ updatedAt: contract.updatedAt,
+ stateCode: contract.stateCode,
+ stateNumber: contract.stateNumber,
+ status: contract.status,
+ draftRevision: {
+ ...contract.draftRevision,
+ id: contract.id,
+ contractName: contract.draftRevision.contractName,
+ createdAt: contract.draftRevision.createdAt,
+ updatedAt: contract.draftRevision.updatedAt,
+ __typename: 'ContractRevision',
+ formData: {
+ ...contract.draftRevision.formData,
+ __typename: 'ContractFormData'
+ }
+ },
+ draftRates: contract.draftRates || rates,
+ packageSubmissions: contract.packageSubmissions || packageSubmissions,
+ __typename: 'UnlockedContract'
+ }
+ // set up data to return
+ draftSubmission = unlockedContract
+ return {draftSubmission, previousDocuments, updateDraft, createDraft, interimState, showPageErrorMessage }
+}
+
+export { useContractForm }
+export type { UseContractForm }
diff --git a/services/app-web/src/pages/App/AppRoutes.tsx b/services/app-web/src/pages/App/AppRoutes.tsx
index e34928d2fb..a2fa45a8cd 100644
--- a/services/app-web/src/pages/App/AppRoutes.tsx
+++ b/services/app-web/src/pages/App/AppRoutes.tsx
@@ -121,9 +121,7 @@ const StateUserRoutes = ({
)}
}>
}
/>
}>
}
/>
{
routerProvider: {
route: '/submissions/15/question-and-answers?submit=response',
},
-
}
)
@@ -543,7 +542,6 @@ describe('QuestionResponse', () => {
routerProvider: {
route: '/submissions/15/question-and-answers',
},
-
}
)
diff --git a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx
index c442d7a6a4..03ec5aa0d5 100644
--- a/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx
+++ b/services/app-web/src/pages/QuestionResponse/UploadQuestions/UploadQuestions.tsx
@@ -59,9 +59,9 @@ export const UploadQuestions = () => {
const { setFocusErrorSummaryHeading, errorSummaryHeadingRef } =
useErrorSummary()
- if (pkg.status === 'DRAFT') {
- return
- }
+ if (pkg.status === 'DRAFT') {
+ return
+ }
const showFileUploadError = Boolean(shouldValidate && fileUploadError)
const fileUploadErrorFocusKey = hasNoFiles
? 'questions-upload'
diff --git a/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.tsx b/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.tsx
index a0d3760776..0114218435 100644
--- a/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.tsx
+++ b/services/app-web/src/pages/QuestionResponse/UploadResponse/UploadResponse.tsx
@@ -63,9 +63,9 @@ export const UploadResponse = () => {
} = useFileUpload(shouldValidate)
const { setFocusErrorSummaryHeading, errorSummaryHeadingRef } =
useErrorSummary()
- if (pkg.status === 'DRAFT') {
- return
- }
+ if (pkg.status === 'DRAFT') {
+ return
+ }
const showFileUploadError = Boolean(shouldValidate && fileUploadError)
const fileUploadErrorFocusKey = hasNoFiles
diff --git a/services/app-web/src/pages/ReplaceRate/ReplaceRate.module.scss b/services/app-web/src/pages/ReplaceRate/ReplaceRate.module.scss
index 6a48fb3d80..e70f8501d5 100644
--- a/services/app-web/src/pages/ReplaceRate/ReplaceRate.module.scss
+++ b/services/app-web/src/pages/ReplaceRate/ReplaceRate.module.scss
@@ -1,15 +1,15 @@
@use '../../styles/custom.scss' as custom;
@use '../../styles/uswdsImports.scss' as uswds;
-.background{
- width: 100%;
- flex: 1 0 auto;
- display: flex;
- flex-direction: column;
- background-color: none;
- align-items: center
+.background {
+ width: 100%;
+ flex: 1 0 auto;
+ display: flex;
+ flex-direction: column;
+ background-color: none;
+ align-items: center;
}
-.gridContainer{
+.gridContainer {
@include custom.default-page-container;
max-width: custom.$mcr-container-standard-width-fixed;
flex-direction: column;
diff --git a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx
index ed9c4a4445..3fdfd79a27 100644
--- a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx
+++ b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.test.tsx
@@ -5,8 +5,7 @@ import userEvent from '@testing-library/user-event'
import {
mockContractAndRatesDraft,
fetchCurrentUserMock,
- mockDraft,
- mockBaseContract,
+ mockContractPackageUnlockedWithUnlockedType,
} from '../../../testHelpers/apolloMocks'
import {
@@ -34,6 +33,7 @@ import {
} from '../../../constants/statutoryRegulatoryAttestation'
import * as useRouteParams from '../../../hooks/useRouteParams'
import * as useHealthPlanPackageForm from '../../../hooks/useHealthPlanPackageForm'
+import * as useContractForm from '../../../hooks/useContractForm'
const mockUpdateDraftFn = vi.fn()
const scrollIntoViewMock = vi.fn()
@@ -41,14 +41,11 @@ HTMLElement.prototype.scrollIntoView = scrollIntoViewMock
describe('ContractDetails', () => {
beforeEach(() => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockReturnValue({
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
updateDraft: mockUpdateDraftFn,
createDraft: vi.fn(),
showPageErrorMessage: false,
- draftSubmission: mockDraft(),
+ draftSubmission: mockContractPackageUnlockedWithUnlockedType(),
})
vi.spyOn(useRouteParams, 'useRouteParams').mockReturnValue({
id: '123-abc',
@@ -56,10 +53,7 @@ describe('ContractDetails', () => {
})
afterEach(() => {
vi.clearAllMocks()
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockRestore()
+ vi.spyOn(useContractForm, 'useContractForm').mockRestore()
vi.spyOn(useRouteParams, 'useRouteParams').mockRestore()
})
@@ -83,6 +77,14 @@ describe('ContractDetails', () => {
describe('Contract documents file upload', () => {
it('renders without errors', async () => {
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = []
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -150,19 +152,13 @@ describe('ContractDetails', () => {
describe('Federal authorities', () => {
it('displays correct form fields for federal authorities with medicaid contract', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: {
- ...mockContractAndRatesDraft(),
- populationCovered: 'MEDICAID',
- },
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision!.formData.populationCovered = 'MEDICAID'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
await waitFor(() => {
@@ -187,19 +183,13 @@ describe('ContractDetails', () => {
})
it('displays correct form fields for federal authorities with CHIP only contract', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: {
- ...mockContractAndRatesDraft(),
- populationCovered: 'CHIP',
- },
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision!.formData.populationCovered = 'CHIP'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
renderWithProviders(, {
@@ -230,29 +220,14 @@ describe('ContractDetails', () => {
contractType: 'BASE',
})
- const chipAmendmentPackage = mockContractAndRatesDraft({
- populationCovered: 'CHIP',
- contractType: 'AMENDMENT',
- })
- const chipBasePackage = mockContractAndRatesDraft({
- populationCovered: 'CHIP',
- contractType: 'BASE',
- })
-
it('can set provisions for medicaid contract amendment', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: {
- ...mockContractAndRatesDraft(),
- populationCovered: 'MEDICAID',
- },
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.populationCovered = 'MEDICAID'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
@@ -304,6 +279,14 @@ describe('ContractDetails', () => {
draftSubmission: medicaidAmendmentPackage,
}
})
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.populationCovered = 'MEDICAID'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -454,16 +437,14 @@ describe('ContractDetails', () => {
})
it('cannot set provisions for CHIP only base contract', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: chipBasePackage,
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.populationCovered = 'CHIP'
+ draftContract.draftRevision.formData.contractType = 'BASE'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
@@ -480,16 +461,14 @@ describe('ContractDetails', () => {
})
it('can set provisions for CHIP only amendment', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: chipAmendmentPackage,
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.populationCovered = 'CHIP'
+ draftContract.draftRevision.formData.contractType = 'AMENDMENT'
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
@@ -635,6 +614,15 @@ describe('ContractDetails', () => {
})
it('disabled with alert after first attempt to continue with zero files', async () => {
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = []
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
+
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -684,6 +672,14 @@ describe('ContractDetails', () => {
})
it('disabled with alert after first attempt to continue with invalid files', async () => {
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = []
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -710,6 +706,14 @@ describe('ContractDetails', () => {
expect(continueButton).toHaveAttribute('aria-disabled', 'true')
})
it('disabled with alert when trying to continue while a file is still uploading', async () => {
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = []
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -801,25 +805,19 @@ describe('ContractDetails', () => {
})
it('when existing file is removed, does not trigger missing documents alert on click but still saves the in progress draft', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: {
- ...mockContractAndRatesDraft(),
- contractDocuments: [
- {
- name: 'aasdf3423af',
- sha256: 'fakesha',
- s3URL: 's3://bucketname/key/fileName',
- },
- ],
- },
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = [
+ {
+ name: 'aasdf3423af',
+ sha256: 'fakesha',
+ s3URL: 's3://bucketname/key/fileName',
+ },
+ ]
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
renderWithProviders(, {
@@ -858,12 +856,12 @@ describe('ContractDetails', () => {
})
await userEvent.click(saveAsDraftButton)
await waitFor(() => {
- expect(mockUpdateDraftFn).not.toHaveBeenCalled()
+ expect(mockUpdateDraftFn).toHaveBeenCalled()
expect(
screen.queryAllByText(
'You must remove all documents with error messages before continuing'
)
- ).toHaveLength(2)
+ ).toHaveLength(0)
})
})
})
@@ -919,10 +917,19 @@ describe('ContractDetails', () => {
expect(
screen.queryByText('You must upload at least one document')
).toBeNull()
- expect(mockUpdateDraftFn).not.toHaveBeenCalled()
+ expect(mockUpdateDraftFn).toHaveBeenCalled()
})
it('when duplicate files present, does not trigger duplicate documents alert on click and silently updates submission without the duplicate', async () => {
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.contractDocuments = []
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
+ })
+
renderWithProviders(, {
apolloProvider: defaultApolloProvider,
})
@@ -943,39 +950,20 @@ describe('ContractDetails', () => {
})
await userEvent.click(backButton)
expect(screen.queryByText('Remove files with errors')).toBeNull()
- expect(mockUpdateDraftFn).toHaveBeenCalledWith(
- expect.objectContaining({
- contractDocuments: [
- {
- name: 'testFile.doc',
- s3URL: expect.any(String),
- sha256: 'da7d22ce886b5ab262cd7ab28901212a027630a5edf8e88c8488087b03ffd833', // pragma: allowlist secret
- },
- {
- name: 'testFile.pdf',
- s3URL: expect.any(String),
- sha256: '6d50607f29187d5b185ffd9d46bc5ef75ce7abb53318690c73e55b6623e25ad5', // pragma: allowlist secret
- },
- ],
- })
- )
+ expect(mockUpdateDraftFn).toHaveBeenCalled()
})
})
describe('Contract 438 attestation', () => {
it('renders 438 attestation question without errors', async () => {
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: mockBaseContract({
- statutoryRegulatoryAttestation: true,
- }),
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.statutoryRegulatoryAttestation =
+ true
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
await waitFor(() => {
@@ -1015,22 +1003,22 @@ describe('ContractDetails', () => {
})
})
it('errors when continuing without answering 438 attestation question', async () => {
- const testDraft = mockContractAndRatesDraft({
- contractDateStart: new Date('11-12-2023'),
- contractDateEnd: new Date('11-12-2024'),
- statutoryRegulatoryAttestation: undefined,
- statutoryRegulatoryAttestationDescription: undefined,
- })
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: testDraft,
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.statutoryRegulatoryAttestation =
+ undefined
+ draftContract.draftRevision.formData.statutoryRegulatoryAttestationDescription =
+ undefined
+ draftContract.draftRevision.formData.contractDateStart = new Date(
+ '11-12-2023'
+ )
+ draftContract.draftRevision.formData.contractDateEnd = new Date(
+ '11-12-2024'
+ )
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
await waitFor(() => {
@@ -1088,22 +1076,22 @@ describe('ContractDetails', () => {
})
})
it('errors when continuing without description for 438 non-compliance', async () => {
- const draft = mockContractAndRatesDraft({
- contractDateStart: new Date('11-12-2023'),
- contractDateEnd: new Date('11-12-2024'),
- statutoryRegulatoryAttestation: undefined,
- statutoryRegulatoryAttestationDescription: undefined,
- })
- vi.spyOn(
- useHealthPlanPackageForm,
- 'useHealthPlanPackageForm'
- ).mockImplementation(() => {
- return {
- createDraft: vi.fn(),
- updateDraft: mockUpdateDraftFn,
- showPageErrorMessage: false,
- draftSubmission: draft,
- }
+ const draftContract = mockContractPackageUnlockedWithUnlockedType()
+ draftContract.draftRevision.formData.statutoryRegulatoryAttestation =
+ undefined
+ draftContract.draftRevision.formData.statutoryRegulatoryAttestationDescription =
+ undefined
+ draftContract.draftRevision.formData.contractDateStart = new Date(
+ '11-12-2023'
+ )
+ draftContract.draftRevision.formData.contractDateEnd = new Date(
+ '11-12-2024'
+ )
+ vi.spyOn(useContractForm, 'useContractForm').mockReturnValue({
+ updateDraft: mockUpdateDraftFn,
+ createDraft: vi.fn(),
+ showPageErrorMessage: false,
+ draftSubmission: draftContract,
})
await waitFor(() => {
diff --git a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.tsx b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.tsx
index 208781fd39..7469f1f4ee 100644
--- a/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.tsx
+++ b/services/app-web/src/pages/StateSubmission/ContractDetails/ContractDetails.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React from 'react'
import dayjs from 'dayjs'
import {
Form as UswdsForm,
@@ -9,12 +9,11 @@ import {
} from '@trussworks/react-uswds'
import { v4 as uuidv4 } from 'uuid'
import { generatePath, useNavigate } from 'react-router-dom'
-import { Formik, FormikErrors } from 'formik'
+import { Formik, FormikErrors, getIn } from 'formik'
import styles from '../StateSubmissionForm.module.scss'
import {
FileUpload,
- S3FileData,
FileItemT,
FieldRadio,
FieldCheckbox,
@@ -28,12 +27,10 @@ import {
} from '../../../components'
import {
formatForForm,
- formatFormDateForDomain,
formatUserInputDate,
isDateRangeEmpty,
} from '../../../formHelpers'
import { useS3 } from '../../../contexts/S3Context'
-import { isS3Error } from '../../../s3'
import { ContractDetailsFormSchema } from './ContractDetailsSchema'
import {
@@ -43,9 +40,14 @@ import {
import { PageActions } from '../PageActions'
import {
activeFormPages,
- type HealthPlanFormPageProps,
+ type ContractFormPageProps,
} from '../StateSubmissionForm'
-import { formatYesNoForProto } from '../../../formHelpers/formatters'
+import {
+ formatYesNoForProto,
+ formatDocumentsForGQL,
+ formatDocumentsForForm,
+ formatFormDateForGQL,
+} from '../../../formHelpers/formatters'
import { ACCEPTED_SUBMISSION_FILE_TYPES } from '../../../components/FileUpload'
import {
federalAuthorityKeysForCHIP,
@@ -54,10 +56,9 @@ import {
import {
generateProvisionLabel,
generateApplicableProvisionsList,
-} from '../../../common-code/healthPlanSubmissionHelpers/provisions'
+} from '../../../common-code/ContractTypeProvisions'
import type {
ManagedCareEntity,
- SubmissionDocument,
ContractExecutionStatus,
FederalAuthority,
} from '../../../common-code/healthPlanFormDataType'
@@ -66,25 +67,30 @@ import {
isCHIPOnly,
isContractAmendment,
isContractWithProvisions,
-} from '../../../common-code/healthPlanFormDataType/healthPlanFormData'
+} from '../../../common-code/ContractType'
import { RoutesRecord } from '../../../constants'
import { useLDClient } from 'launchdarkly-react-client-sdk'
import { featureFlags } from '../../../common-code/featureFlags'
+import {
+ booleanAsYesNoFormValue,
+ yesNoFormValueAsBoolean,
+} from '../../../components/Form/FieldYesNo/FieldYesNo'
import {
StatutoryRegulatoryAttestation,
StatutoryRegulatoryAttestationDescription,
StatutoryRegulatoryAttestationQuestion,
} from '../../../constants/statutoryRegulatoryAttestation'
import { FormContainer } from '../FormContainer'
-import {
- useCurrentRoute,
- useHealthPlanPackageForm,
- useRouteParams,
-} from '../../../hooks'
+import { useCurrentRoute, useRouteParams } from '../../../hooks'
import { useAuth } from '../../../contexts/AuthContext'
import { ErrorOrLoadingPage } from '../ErrorOrLoadingPage'
import { PageBannerAlerts } from '../PageBannerAlerts'
import { useErrorSummary } from '../../../hooks/useErrorSummary'
+import { useContractForm } from '../../../hooks/useContractForm'
+import {
+ UpdateContractDraftRevisionInput,
+ ContractDraftRevisionFormDataInput,
+} from '../../../gen/gqlClient'
function formattedDatePlusOneDay(initialValue: string): string {
const dayjsValue = dayjs(initialValue)
@@ -115,7 +121,10 @@ const ContractDatesErrorMessage = ({
: validationErrorMessage}
)
-export interface ContractDetailsFormValues {
+
+export type ContractDetailsFormValues = {
+ contractDocuments: FileItemT[]
+ supportingDocuments: FileItemT[]
contractExecutionStatus: ContractExecutionStatus | undefined
contractDateStart: string
contractDateEnd: string
@@ -141,12 +150,12 @@ export interface ContractDetailsFormValues {
statutoryRegulatoryAttestation: string | undefined
statutoryRegulatoryAttestationDescription: string | undefined
}
-type FormError =
+export type FormError =
FormikErrors[keyof FormikErrors]
export const ContractDetails = ({
showValidations = false,
-}: HealthPlanFormPageProps): React.ReactElement => {
+}: ContractFormPageProps): React.ReactElement => {
const [shouldValidate, setShouldValidate] = React.useState(showValidations)
const navigate = useNavigate()
const ldClient = useLDClient()
@@ -163,8 +172,7 @@ export const ContractDetails = ({
updateDraft,
previousDocuments,
showPageErrorMessage,
- unlockInfo,
- } = useHealthPlanPackageForm(id)
+ } = useContractForm(id)
const contract438Attestation = ldClient?.variation(
featureFlags.CONTRACT_438_ATTESTATION.flag,
@@ -172,31 +180,14 @@ export const ContractDetails = ({
)
// Contract documents state management
- const { deleteFile, uploadFile, scanFile, getKey, getS3URL } = useS3()
- const [fileItems, setFileItems] = useState([]) // eventually this will include files from api
- const hasValidFiles =
- fileItems.length > 0 &&
- fileItems.every((item) => item.status === 'UPLOAD_COMPLETE')
- const hasLoadingFiles =
- fileItems.some((item) => item.status === 'PENDING') ||
- fileItems.some((item) => item.status === 'SCANNING')
- const showFileUploadError = shouldValidate && !hasValidFiles
- const documentsErrorMessage =
- showFileUploadError && hasLoadingFiles
- ? 'You must wait for all documents to finish uploading before continuing'
- : showFileUploadError && fileItems.length === 0
- ? ' You must upload at least one document'
- : showFileUploadError && !hasValidFiles
- ? ' You must remove all documents with error messages before continuing'
- : undefined
- const documentsErrorKey =
- fileItems.length === 0 ? 'documents' : '#file-items-list'
-
+ const { getKey, handleDeleteFile, handleUploadFile, handleScanFile } =
+ useS3()
if (interimState || !draftSubmission)
return
+
const fileItemsFromDraftSubmission: FileItemT[] | undefined =
draftSubmission &&
- draftSubmission.contractDocuments.map((doc) => {
+ draftSubmission.draftRevision.formData.contractDocuments.map((doc) => {
const key = getKey(doc.s3URL)
if (!key) {
return {
@@ -218,51 +209,6 @@ export const ContractDetails = ({
}
})
- const onFileItemsUpdate = async ({
- fileItems,
- }: {
- fileItems: FileItemT[]
- }) => {
- setFileItems(fileItems)
- }
-
- const handleDeleteFile = async (key: string) => {
- const isSubmittedFile =
- previousDocuments &&
- Boolean(
- previousDocuments.some((previousKey) => previousKey === key)
- )
-
- if (!isSubmittedFile) {
- const result = await deleteFile(key, 'HEALTH_PLAN_DOCS')
- if (isS3Error(result)) {
- throw new Error(`Error in S3 key: ${key}`)
- }
- }
- }
-
- const handleUploadFile = async (file: File): Promise => {
- const s3Key = await uploadFile(file, 'HEALTH_PLAN_DOCS')
-
- if (isS3Error(s3Key)) {
- throw new Error(`Error in S3: ${file.name}`)
- }
-
- const s3URL = await getS3URL(s3Key, file.name, 'HEALTH_PLAN_DOCS')
- return { key: s3Key, s3URL: s3URL }
- }
-
- const handleScanFile = async (key: string): Promise => {
- try {
- await scanFile(key, 'HEALTH_PLAN_DOCS')
- } catch (e) {
- if (isS3Error(e)) {
- throw new Error(`Error in S3: ${key}`)
- }
- throw new Error('Scanning error: Scanning retry timed out')
- }
- }
-
const applicableProvisions =
generateApplicableProvisionsList(draftSubmission)
@@ -271,232 +217,359 @@ export const ContractDetails = ({
: federalAuthorityKeys
const contractDetailsInitialValues: ContractDetailsFormValues = {
+ contractDocuments: formatDocumentsForForm({
+ documents: draftSubmission.draftRevision.formData.contractDocuments,
+ getKey: getKey,
+ }),
+ supportingDocuments: formatDocumentsForForm({
+ documents:
+ draftSubmission.draftRevision.formData.supportingDocuments,
+ getKey: getKey,
+ }),
contractExecutionStatus:
- draftSubmission?.contractExecutionStatus ?? undefined,
+ draftSubmission.draftRevision.formData.contractExecutionStatus ??
+ undefined,
contractDateStart:
(draftSubmission &&
- formatForForm(draftSubmission.contractDateStart)) ??
+ formatForForm(
+ draftSubmission.draftRevision.formData.contractDateStart
+ )) ??
'',
contractDateEnd:
(draftSubmission &&
- formatForForm(draftSubmission.contractDateEnd)) ??
+ formatForForm(
+ draftSubmission.draftRevision.formData.contractDateEnd
+ )) ??
'',
managedCareEntities:
- (draftSubmission?.managedCareEntities as ManagedCareEntity[]) ?? [],
- federalAuthorities: draftSubmission?.federalAuthorities ?? [],
- inLieuServicesAndSettings: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .inLieuServicesAndSettings
- ),
+ (draftSubmission.draftRevision.formData
+ .managedCareEntities as ManagedCareEntity[]) ?? [],
+ federalAuthorities:
+ draftSubmission.draftRevision.formData.federalAuthorities ?? [],
+ inLieuServicesAndSettings:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .inLieuServicesAndSettings === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .inLieuServicesAndSettings
+ ) ?? '',
+ modifiedBenefitsProvided:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedBenefitsProvided === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedBenefitsProvided
+ ) ?? '',
+ modifiedGeoAreaServed:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData.modifiedGeoAreaServed ===
+ null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedGeoAreaServed
+ ) ?? '',
+ modifiedMedicaidBeneficiaries:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedMedicaidBeneficiaries === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedMedicaidBeneficiaries
+ ) ?? '',
+ modifiedRiskSharingStrategy:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedRiskSharingStrategy === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedRiskSharingStrategy
+ ) ?? '',
+ modifiedIncentiveArrangements:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedIncentiveArrangements === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedIncentiveArrangements
+ ) ?? '',
+ modifiedWitholdAgreements:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedWitholdAgreements === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedWitholdAgreements
+ ) ?? '',
+ modifiedStateDirectedPayments:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedStateDirectedPayments === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedStateDirectedPayments
+ ) ?? '',
+ modifiedPassThroughPayments:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedPassThroughPayments === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedPassThroughPayments
+ ) ?? '',
+ modifiedPaymentsForMentalDiseaseInstitutions:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedPaymentsForMentalDiseaseInstitutions === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedPaymentsForMentalDiseaseInstitutions
+ ) ?? '',
+ modifiedMedicalLossRatioStandards:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedMedicalLossRatioStandards === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedMedicalLossRatioStandards
+ ) ?? '',
+ modifiedOtherFinancialPaymentIncentive:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedOtherFinancialPaymentIncentive === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedOtherFinancialPaymentIncentive
+ ) ?? '',
+ modifiedEnrollmentProcess:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedEnrollmentProcess === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedEnrollmentProcess
+ ) ?? '',
+ modifiedGrevienceAndAppeal:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedGrevienceAndAppeal === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedGrevienceAndAppeal
+ ) ?? '',
+ modifiedNetworkAdequacyStandards:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedNetworkAdequacyStandards === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedNetworkAdequacyStandards
+ ) ?? '',
+ modifiedLengthOfContract:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedLengthOfContract === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedLengthOfContract
+ ) ?? '',
+ modifiedNonRiskPaymentArrangements:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .modifiedNonRiskPaymentArrangements === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .modifiedNonRiskPaymentArrangements
+ ) ?? '',
+ statutoryRegulatoryAttestation:
+ booleanAsYesNoFormValue(
+ draftSubmission.draftRevision.formData
+ .statutoryRegulatoryAttestation === null
+ ? undefined
+ : draftSubmission.draftRevision.formData
+ .statutoryRegulatoryAttestation
+ ) ?? '',
+ statutoryRegulatoryAttestationDescription:
+ draftSubmission.draftRevision.formData
+ .statutoryRegulatoryAttestationDescription ?? '',
+ }
- modifiedBenefitsProvided: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedBenefitsProvided
- ),
- modifiedGeoAreaServed: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedGeoAreaServed
- ),
- modifiedMedicaidBeneficiaries: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedMedicaidBeneficiaries
- ),
- modifiedRiskSharingStrategy: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedRiskSharingStrategy
- ),
- modifiedIncentiveArrangements: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedIncentiveArrangements
- ),
- modifiedWitholdAgreements: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedWitholdAgreements
- ),
- modifiedStateDirectedPayments: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedStateDirectedPayments
- ),
- modifiedPassThroughPayments: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedPassThroughPayments
- ),
- modifiedPaymentsForMentalDiseaseInstitutions: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedPaymentsForMentalDiseaseInstitutions
- ),
- modifiedMedicalLossRatioStandards: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedMedicalLossRatioStandards
- ),
- modifiedOtherFinancialPaymentIncentive: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedOtherFinancialPaymentIncentive
- ),
- modifiedEnrollmentProcess: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedEnrollmentProcess
- ),
- modifiedGrevienceAndAppeal: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedGrevienceAndAppeal
- ),
- modifiedNetworkAdequacyStandards: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedNetworkAdequacyStandards
- ),
- modifiedLengthOfContract: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedLengthOfContract
- ),
- modifiedNonRiskPaymentArrangements: formatForForm(
- draftSubmission?.contractAmendmentInfo?.modifiedProvisions
- .modifiedNonRiskPaymentArrangements
- ),
- statutoryRegulatoryAttestation: formatForForm(
- draftSubmission?.statutoryRegulatoryAttestation
- ),
- statutoryRegulatoryAttestationDescription: formatForForm(
- draftSubmission?.statutoryRegulatoryAttestationDescription
- ),
+ const showFieldErrors = (
+ fieldName: keyof ContractDetailsFormValues,
+ errors: FormikErrors
+ ): string | undefined => {
+ if (!shouldValidate) return undefined
+ return getIn(errors, `${fieldName}`)
}
- const showFieldErrors = (error?: FormError) =>
- shouldValidate && Boolean(error)
+ const genecontractErrorsummaryErrors = (
+ errors: FormikErrors,
+ values: ContractDetailsFormValues
+ ) => {
+ const errorsObject: { [field: string]: string } = {}
+ Object.entries(errors).forEach(([field, value]) => {
+ if (typeof value === 'string') {
+ errorsObject[field] = value
+ }
+ if (Array.isArray(value) && Array.length > 0) {
+ Object.entries(value).forEach(
+ ([arrItemField, arrItemValue]) => {
+ if (typeof arrItemValue === 'string') {
+ errorsObject[arrItemField] = arrItemValue
+ }
+ }
+ )
+ }
+ })
+ values.contractDocuments.forEach((item) => {
+ const key = 'contractDocuments'
+ if (item.status === 'DUPLICATE_NAME_ERROR') {
+ errorsObject[key] =
+ 'You must remove all documents with error messages before continuing'
+ } else if (item.status === 'SCANNING_ERROR') {
+ errorsObject[key] =
+ 'You must remove files that failed the security scan'
+ } else if (item.status === 'UPLOAD_ERROR') {
+ errorsObject[key] =
+ 'You must remove or retry files that failed to upload'
+ }
+ })
+ // return errors
+ return errorsObject
+ }
const handleFormSubmit = async (
values: ContractDetailsFormValues,
setSubmitting: (isSubmitting: boolean) => void, // formik setSubmitting
options: {
- shouldValidateDocuments: boolean
redirectPath: string
}
) => {
- // Currently documents validation happens (outside of the yup schema, which only handles the formik form data)
- // if there are any errors present in the documents list and we are in a validation state (relevant for Save as Draft) force user to clear validations to continue
- if (options.shouldValidateDocuments) {
- if (!hasValidFiles) {
- setShouldValidate(true)
- setFocusErrorSummaryHeading(true)
- setSubmitting(false)
- return
+ const updatedDraftSubmissionFormData: ContractDraftRevisionFormDataInput =
+ {
+ contractExecutionStatus: values.contractExecutionStatus,
+ contractDateStart: formatFormDateForGQL(
+ values.contractDateStart
+ ),
+ contractDateEnd: formatFormDateForGQL(values.contractDateEnd),
+ riskBasedContract:
+ draftSubmission.draftRevision.formData.riskBasedContract,
+ populationCovered:
+ draftSubmission.draftRevision.formData.populationCovered,
+ programIDs:
+ draftSubmission.draftRevision.formData.programIDs || [],
+ stateContacts:
+ draftSubmission.draftRevision.formData.stateContacts || [],
+ contractDocuments:
+ formatDocumentsForGQL(values.contractDocuments) || [],
+ supportingDocuments:
+ formatDocumentsForGQL(values.supportingDocuments) || [],
+ managedCareEntities: values.managedCareEntities,
+ federalAuthorities: values.federalAuthorities,
+ submissionType:
+ draftSubmission.draftRevision.formData.submissionType,
+ statutoryRegulatoryAttestation: formatYesNoForProto(
+ values.statutoryRegulatoryAttestation
+ ),
+ // If contract is in compliance, we set the description to undefined. This clears out previous non-compliance description
+ statutoryRegulatoryAttestationDescription:
+ values.statutoryRegulatoryAttestationDescription,
}
- }
-
- const contractDocuments = fileItems.reduce(
- (formDataDocuments, fileItem) => {
- if (fileItem.status === 'UPLOAD_ERROR') {
- console.info(
- 'Attempting to save files that failed upload, discarding invalid files'
- )
- } else if (fileItem.status === 'SCANNING_ERROR') {
- console.info(
- 'Attempting to save files that failed scanning, discarding invalid files'
- )
- } else if (fileItem.status === 'DUPLICATE_NAME_ERROR') {
- console.info(
- 'Attempting to save files that are duplicate names, discarding duplicate'
- )
- } else if (!fileItem.s3URL) {
- console.info(
- 'Attempting to save a seemingly valid file item is not yet uploaded to S3, this should not happen on form submit. Discarding file.'
- )
- } else if (!fileItem.sha256) {
- console.info(
- 'Attempting to save a seemingly valid file item with no sha. this should not happen on form submit. Discarding file.'
- )
- } else {
- formDataDocuments.push({
- name: fileItem.name,
- s3URL: fileItem.s3URL,
- sha256: fileItem.sha256,
- })
- }
- return formDataDocuments
- },
- [] as SubmissionDocument[]
- )
-
- draftSubmission.contractExecutionStatus = values.contractExecutionStatus
- draftSubmission.contractDateStart = formatFormDateForDomain(
- values.contractDateStart
- )
- draftSubmission.contractDateEnd = formatFormDateForDomain(
- values.contractDateEnd
- )
- draftSubmission.managedCareEntities = values.managedCareEntities
- draftSubmission.federalAuthorities = values.federalAuthorities
- draftSubmission.contractDocuments = contractDocuments
- draftSubmission.statutoryRegulatoryAttestation = formatYesNoForProto(
- values.statutoryRegulatoryAttestation
- )
- // If contract is in compliance, we set the description to undefined. This clears out previous non-compliance description
- draftSubmission.statutoryRegulatoryAttestationDescription =
- values.statutoryRegulatoryAttestationDescription
+ if (
+ draftSubmission === undefined ||
+ !updateDraft ||
+ !draftSubmission.draftRevision
+ ) {
+ console.info(draftSubmission, updateDraft)
+ console.info(
+ 'ERROR, SubmissionType for does not have props needed to update a draft.'
+ )
+ return
+ }
if (isContractWithProvisions(draftSubmission)) {
- draftSubmission.contractAmendmentInfo = {
- modifiedProvisions: {
- inLieuServicesAndSettings: formatYesNoForProto(
- values.inLieuServicesAndSettings
- ),
- modifiedBenefitsProvided: formatYesNoForProto(
- values.modifiedBenefitsProvided
- ),
- modifiedGeoAreaServed: formatYesNoForProto(
- values.modifiedGeoAreaServed
- ),
- modifiedMedicaidBeneficiaries: formatYesNoForProto(
- values.modifiedMedicaidBeneficiaries
- ),
- modifiedRiskSharingStrategy: formatYesNoForProto(
- values.modifiedRiskSharingStrategy
- ),
- modifiedIncentiveArrangements: formatYesNoForProto(
- values.modifiedIncentiveArrangements
- ),
- modifiedWitholdAgreements: formatYesNoForProto(
- values.modifiedWitholdAgreements
- ),
- modifiedStateDirectedPayments: formatYesNoForProto(
- values.modifiedStateDirectedPayments
- ),
- modifiedPassThroughPayments: formatYesNoForProto(
- values.modifiedPassThroughPayments
- ),
- modifiedPaymentsForMentalDiseaseInstitutions:
- formatYesNoForProto(
- values.modifiedPaymentsForMentalDiseaseInstitutions
- ),
- modifiedMedicalLossRatioStandards: formatYesNoForProto(
- values.modifiedMedicalLossRatioStandards
- ),
- modifiedOtherFinancialPaymentIncentive: formatYesNoForProto(
- values.modifiedOtherFinancialPaymentIncentive
- ),
- modifiedEnrollmentProcess: formatYesNoForProto(
- values.modifiedEnrollmentProcess
- ),
- modifiedGrevienceAndAppeal: formatYesNoForProto(
- values.modifiedGrevienceAndAppeal
- ),
- modifiedNetworkAdequacyStandards: formatYesNoForProto(
- values.modifiedNetworkAdequacyStandards
- ),
- modifiedLengthOfContract: formatYesNoForProto(
- values.modifiedLengthOfContract
- ),
- modifiedNonRiskPaymentArrangements: formatYesNoForProto(
- values.modifiedNonRiskPaymentArrangements
- ),
- },
- }
+ updatedDraftSubmissionFormData.inLieuServicesAndSettings =
+ yesNoFormValueAsBoolean(values.inLieuServicesAndSettings)
+ updatedDraftSubmissionFormData.modifiedBenefitsProvided =
+ yesNoFormValueAsBoolean(values.modifiedBenefitsProvided)
+ updatedDraftSubmissionFormData.modifiedGeoAreaServed =
+ yesNoFormValueAsBoolean(values.modifiedGeoAreaServed)
+ updatedDraftSubmissionFormData.modifiedMedicaidBeneficiaries =
+ yesNoFormValueAsBoolean(values.modifiedMedicaidBeneficiaries)
+ updatedDraftSubmissionFormData.modifiedRiskSharingStrategy =
+ yesNoFormValueAsBoolean(values.modifiedRiskSharingStrategy)
+ updatedDraftSubmissionFormData.modifiedIncentiveArrangements =
+ yesNoFormValueAsBoolean(values.modifiedIncentiveArrangements)
+ updatedDraftSubmissionFormData.modifiedWitholdAgreements =
+ yesNoFormValueAsBoolean(values.modifiedWitholdAgreements)
+ updatedDraftSubmissionFormData.modifiedStateDirectedPayments =
+ yesNoFormValueAsBoolean(values.modifiedStateDirectedPayments)
+ updatedDraftSubmissionFormData.modifiedPassThroughPayments =
+ yesNoFormValueAsBoolean(values.modifiedPassThroughPayments)
+ updatedDraftSubmissionFormData.modifiedPaymentsForMentalDiseaseInstitutions =
+ yesNoFormValueAsBoolean(
+ values.modifiedPaymentsForMentalDiseaseInstitutions
+ )
+ updatedDraftSubmissionFormData.modifiedMedicalLossRatioStandards =
+ yesNoFormValueAsBoolean(
+ values.modifiedMedicalLossRatioStandards
+ )
+ updatedDraftSubmissionFormData.modifiedOtherFinancialPaymentIncentive =
+ yesNoFormValueAsBoolean(
+ values.modifiedOtherFinancialPaymentIncentive
+ )
+ updatedDraftSubmissionFormData.modifiedEnrollmentProcess =
+ yesNoFormValueAsBoolean(values.modifiedEnrollmentProcess)
+ updatedDraftSubmissionFormData.modifiedGrevienceAndAppeal =
+ yesNoFormValueAsBoolean(values.modifiedGrevienceAndAppeal)
+ updatedDraftSubmissionFormData.modifiedNetworkAdequacyStandards =
+ yesNoFormValueAsBoolean(values.modifiedNetworkAdequacyStandards)
+ updatedDraftSubmissionFormData.modifiedLengthOfContract =
+ yesNoFormValueAsBoolean(values.modifiedLengthOfContract)
+ updatedDraftSubmissionFormData.modifiedNonRiskPaymentArrangements =
+ yesNoFormValueAsBoolean(
+ values.modifiedNonRiskPaymentArrangements
+ )
} else {
- draftSubmission.contractAmendmentInfo = undefined
+ updatedDraftSubmissionFormData.inLieuServicesAndSettings = undefined
+ updatedDraftSubmissionFormData.modifiedBenefitsProvided = undefined
+ updatedDraftSubmissionFormData.modifiedGeoAreaServed = undefined
+ updatedDraftSubmissionFormData.modifiedMedicaidBeneficiaries =
+ undefined
+ updatedDraftSubmissionFormData.modifiedRiskSharingStrategy =
+ undefined
+ updatedDraftSubmissionFormData.modifiedIncentiveArrangements =
+ undefined
+ updatedDraftSubmissionFormData.modifiedWitholdAgreements = undefined
+ updatedDraftSubmissionFormData.modifiedStateDirectedPayments =
+ undefined
+ updatedDraftSubmissionFormData.modifiedPassThroughPayments =
+ undefined
+ updatedDraftSubmissionFormData.modifiedPaymentsForMentalDiseaseInstitutions =
+ undefined
+ updatedDraftSubmissionFormData.modifiedMedicalLossRatioStandards =
+ undefined
+ updatedDraftSubmissionFormData.modifiedOtherFinancialPaymentIncentive =
+ undefined
+ updatedDraftSubmissionFormData.modifiedEnrollmentProcess = undefined
+ updatedDraftSubmissionFormData.modifiedGrevienceAndAppeal =
+ undefined
+ updatedDraftSubmissionFormData.modifiedNetworkAdequacyStandards =
+ undefined
+ updatedDraftSubmissionFormData.modifiedLengthOfContract = undefined
+ updatedDraftSubmissionFormData.modifiedNonRiskPaymentArrangements =
+ undefined
}
try {
- const updatedSubmission = await updateDraft(draftSubmission)
+ const updatedContract: UpdateContractDraftRevisionInput = {
+ formData: updatedDraftSubmissionFormData,
+ contractID: draftSubmission.id,
+ lastSeenUpdatedAt: draftSubmission.draftRevision.updatedAt,
+ }
+
+ const updatedSubmission = await updateDraft(updatedContract)
if (updatedSubmission instanceof Error) {
setSubmitting(false)
console.info(
@@ -508,6 +581,8 @@ export const ContractDetails = ({
}
} catch (serverError) {
setSubmitting(false)
+ } finally {
+ setSubmitting(false)
}
}
@@ -517,12 +592,14 @@ export const ContractDetails = ({
<>
@@ -531,10 +608,9 @@ export const ContractDetails = ({
initialValues={contractDetailsInitialValues}
onSubmit={(values, { setSubmitting }) => {
return handleFormSubmit(values, setSubmitting, {
- shouldValidateDocuments: true,
redirectPath:
- draftSubmission.submissionType ===
- 'CONTRACT_ONLY'
+ draftSubmission.draftRevision.formData
+ .submissionType === 'CONTRACT_ONLY'
? `../contacts`
: `../rate-details`,
})
@@ -573,21 +649,21 @@ export const ContractDetails = ({
{shouldValidate && (
)}
+ handleUploadFile(
+ file,
+ 'HEALTH_PLAN_DOCS'
+ )
+ }
+ scanFile={(key) =>
+ handleScanFile(
+ key,
+ 'HEALTH_PLAN_DOCS'
+ )
+ }
+ deleteFile={(key) =>
+ handleDeleteFile(
+ key,
+ 'HEALTH_PLAN_DOCS',
+ previousDocuments
+ )
+ }
+ onFileItemsUpdate={({
+ fileItems,
+ }) =>
+ setFieldValue(
+ `contractDocuments`,
+ fileItems
+ )
}
/>
{contract438Attestation && (
- {showFieldErrors(
- errors.statutoryRegulatoryAttestation
+ {Boolean(
+ showFieldErrors(
+ 'statutoryRegulatoryAttestation',
+ errors
+ )
) && (
)}
+
-
-
-
- Required
-
- {showFieldErrors(errors.programIDs) && (
-
- {errors.programIDs}
-
- )}
-
-
-
-
+
{showFieldErrors(
- errors.submissionType
+ errors.programIDs
) && (
-
- {errors.submissionType}
+
+ {errors.programIDs}
)}
-
-
- {values.populationCovered ===
- 'CHIP' && (
-
- States are not required to
- submit rates with CHIP-only
- contracts.
-
+
+
-
-
-
-
- Required
-
- {showFieldErrors(
+
+ Required
+
+ {showFieldErrors(
+ errors.submissionType
+ ) && (
+
+ {errors.submissionType}
+
+ )}
+
+
+ {values.populationCovered ===
+ 'CHIP' && (
+
+ States are not required
+ to submit rates with
+ CHIP-only contracts.
+
+ )}
+
+
+
- {errors.contractType}
-
)}
-
+
-
+
+ Required
+
+ {showFieldErrors(
+ errors.contractType
+ ) && (
+
+ {errors.contractType}
+
+ )}
+
+
+
+
+
+
-
-
-
-
+
+
+ Provide a 1-2 paragraph
+ summary of your
+ submission that
+ highlights any important
+ changes CMS reviewers
+ will need to be aware of
+
+
+ View description
+ examples
+
+ >
+ }
/>
-
-
-
- Provide a 1-2 paragraph
- summary of your submission
- that highlights any
- important changes CMS
- reviewers will need to be
- aware of
-
-
- View description examples
-
- >
+
+
-
-
- navigate(
+ backOnClick={() =>
+ navigate(
+ RoutesRecord.DASHBOARD_SUBMISSIONS
+ )
+ }
+ continueOnClick={() => {
+ setShouldValidate(true)
+ setFocusErrorSummaryHeading(true)
+ }}
+ saveAsDraftOnClick={async () => {
+ await handleFormSubmit(
+ values,
+ setSubmitting,
+ RoutesRecord.DASHBOARD_SUBMISSIONS
+ )
+ }}
+ actionInProgress={isSubmitting}
+ backOnClickUrl={
RoutesRecord.DASHBOARD_SUBMISSIONS
- )
- }
- continueOnClick={() => {
- setShouldValidate(true)
- setFocusErrorSummaryHeading(true)
- }}
- saveAsDraftOnClick={async () => {
- await handleFormSubmit(
- values,
- { setSubmitting },
+ }
+ saveAsDraftOnClickUrl={
RoutesRecord.DASHBOARD_SUBMISSIONS
- )
- }}
- actionInProgress={isSubmitting}
- backOnClickUrl={
- RoutesRecord.DASHBOARD_SUBMISSIONS
- }
- saveAsDraftOnClickUrl={
- RoutesRecord.DASHBOARD_SUBMISSIONS
- }
- continueOnClickUrl="/edit/contract-details"
- />
-
- >
- )}
+ }
+ continueOnClickUrl="/edit/contract-details"
+ />
+
+ >
+ )
+ }}
>
diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx
index 5813143c05..02f9d0ec9f 100644
--- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx
+++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.test.tsx
@@ -310,7 +310,6 @@ describe('SubmissionSideNav', () => {
routerProvider: {
route: '/submissions/15',
},
-
}
)
@@ -353,7 +352,6 @@ describe('SubmissionSideNav', () => {
routerProvider: {
route: '/submissions/15',
},
-
}
)
@@ -396,7 +394,6 @@ describe('SubmissionSideNav', () => {
routerProvider: {
route: '/submissions/15',
},
-
}
)
@@ -431,7 +428,6 @@ describe('SubmissionSideNav', () => {
],
},
routerProvider: { route: '/submissions/404' },
-
}
)
@@ -470,7 +466,6 @@ describe('SubmissionSideNav', () => {
routerProvider: {
route: '/submissions/15/question-and-answers',
},
-
}
)
diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx
index 59bee1725a..45ae4a77ad 100644
--- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx
+++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx
@@ -1,6 +1,5 @@
import {
GridContainer,
- Icon,
Link,
ModalRef,
ModalToggleButton,
@@ -15,7 +14,6 @@ import {
SubmissionUnlockedBanner,
SubmissionUpdatedBanner,
DocumentWarningBanner,
- NavLinkWithLogging,
LinkWithLogging,
} from '../../components'
import { Loading } from '../../components'
@@ -202,52 +200,50 @@ export const SubmissionSummary = (): React.ReactElement => {
)}
-
- {contract.mccrsID && (
-
- MC-CRS record number:
-
- {contract.mccrsID}
-
-
- )}
-
- {editOrAddMCCRSID}
-
-
- ) : undefined
- }
- contract={contract}
- submissionName={name}
- headerChildComponent={
- hasCMSPermissions ? (
-
- ) : undefined
- }
- statePrograms={statePrograms}
- initiallySubmittedAt={contract.initiallySubmittedAt}
- isStateUser={isStateUser}
- explainMissingData={explainMissingData}
- />
+
+ {contract.mccrsID && (
+
+ MC-CRS record number:
+
+ {contract.mccrsID}
+
+
+ )}
+
+ {editOrAddMCCRSID}
+
+
+ ) : undefined
+ }
+ contract={contract}
+ submissionName={name}
+ headerChildComponent={
+ hasCMSPermissions ? (
+
+ ) : undefined
+ }
+ statePrograms={statePrograms}
+ initiallySubmittedAt={contract.initiallySubmittedAt}
+ isStateUser={isStateUser}
+ explainMissingData={explainMissingData}
+ />
{
=> {
+ const graphQLError = new GraphQLError(
+ error
+ ? GRAPHQL_ERROR_CAUSE_MESSAGES[error.cause]
+ : 'Error attempting to submit.',
+ {
+ extensions: {
+ code: error?.code,
+ cause: error?.cause,
+ },
+ }
+ )
+
+ return {
+ request: {
+ query: FetchContractDocument,
+ variables: { input: { contractID: id } },
+ },
+ error: new ApolloError({
+ graphQLErrors: [graphQLError],
+ }),
+ result: {
+ data: null,
+ errors: [graphQLError],
+ },
+ }
+}
+
+const createContractMockFail = ({
+ error,
+}: {
+ error?: {
+ code: GraphQLErrorCodeTypes
+ cause: GraphQLErrorCauseTypes
+ }
+}): MockedResponse => {
+ const graphQLError = new GraphQLError(
+ error
+ ? GRAPHQL_ERROR_CAUSE_MESSAGES[error.cause]
+ : 'Error attempting to submit.',
+ {
+ extensions: {
+ code: error?.code,
+ cause: error?.cause,
+ },
+ }
+ )
+
+ return {
+ request: {
+ query: CreateContractDocument,
+ variables: { input: { contractID: '123' } },
+ },
+ error: new ApolloError({
+ graphQLErrors: [graphQLError],
+ }),
+ result: {
+ data: null,
+ errors: [graphQLError],
+ },
+ }
+}
+
+const createContractMockSuccess = ({
+ contract,
+}: {
+ contract?: Partial
+}): MockedResponse => {
+ const contractData = mockContractPackageDraft(contract)
+
+ return {
+ request: {
+ query: FetchContractDocument,
+ variables: { input: { contractID: contractData.id } },
+ },
+ result: {
+ data: {
+ createContract: {
+ contract: {
+ ...contractData,
+ },
+ },
+ },
+ },
+ }
+}
+
const updateDraftContractRatesMockSuccess = ({
contract,
}: {
@@ -93,6 +193,77 @@ const updateDraftContractRatesMockSuccess = ({
}
}
+const updateContractDraftRevisionMockSuccess = ({
+ contract,
+}: {
+ contract?: Partial
+}): MockedResponse => {
+ const contractData = mockContractPackageDraft(contract)
+ const contractInput = {
+ contractID: contractData.id,
+ lastSeenUpdatedAt: contractData.draftRevision?.updatedAt,
+ formData: contractData.draftRevision?.formData
+ }
+ return {
+ request: {
+ query: UpdateDraftContractRatesDocument,
+ variables: { input: contractInput },
+ },
+ result: {
+ data: {
+ updateContractDraftRevision: {
+ contract: {
+ ...contractData,
+ },
+ },
+ },
+ },
+ }
+}
+const updateContractDraftRevisionMockFail = ({
+ contract,
+ error
+}: {
+ contract?: Partial
+ error?: {
+ code: GraphQLErrorCodeTypes
+ cause: GraphQLErrorCauseTypes
+ }
+}): MockedResponse => {
+ const contractData = mockContractPackageDraft(contract)
+ const contractInput = {
+ contractID: contractData.id,
+ lastSeenUpdatedAt: contractData.draftRevision?.updatedAt,
+ formData: contractData.draftRevision?.formData
+ }
+
+ const graphQLError = new GraphQLError(
+ error
+ ? GRAPHQL_ERROR_CAUSE_MESSAGES[error.cause]
+ : 'Error attempting to update',
+ {
+ extensions: {
+ code: error?.code,
+ cause: error?.cause,
+ },
+ }
+ )
+
+ return {
+ request: {
+ query: UpdateContractDraftRevisionDocument,
+ variables: { input: contractInput },
+ },
+ error: new ApolloError({
+ graphQLErrors: [graphQLError],
+ }),
+ result: {
+ data: null,
+ errors: [graphQLError],
+ },
+ }
+}
+
const submitContractMockSuccess = ({
id,
submittedReason,
@@ -146,4 +317,4 @@ const submitContractMockError = ({
},
}
}
-export { fetchContractMockSuccess, updateDraftContractRatesMockSuccess, submitContractMockSuccess, submitContractMockError }
+export { fetchContractMockSuccess, fetchContractMockFail, updateDraftContractRatesMockSuccess, updateContractDraftRevisionMockFail, updateContractDraftRevisionMockSuccess, submitContractMockSuccess, submitContractMockError, createContractMockFail, createContractMockSuccess }
diff --git a/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts b/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts
index 2fce3e4d63..72e112e925 100644
--- a/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts
+++ b/services/app-web/src/testHelpers/apolloMocks/contractPackageDataMock.ts
@@ -1,5 +1,5 @@
import { mockMNState } from '../../common-code/healthPlanFormDataMocks/healthPlanFormData'
-import { Contract, ContractFormData, ContractRevision, RateRevision } from '../../gen/gqlClient'
+import { Contract, ContractFormData, ContractRevision, RateRevision, UnlockedContract } from '../../gen/gqlClient'
import { s3DlUrl } from './documentDataMock'
@@ -1866,6 +1866,370 @@ function mockContractPackageUnlocked(
}
}
+function mockContractPackageUnlockedWithUnlockedType(
+ partial?: Partial
+): UnlockedContract {
+ return {
+ status: 'UNLOCKED',
+ __typename: 'UnlockedContract',
+ createdAt: '2023-01-01T16:54:39.173Z',
+ updatedAt: '2024-12-01T16:54:39.173Z',
+ initiallySubmittedAt:'2023-01-01',
+ id: 'test-abc-123',
+ stateCode: 'MN',
+ state: mockMNState(),
+ stateNumber: 5,
+ mccrsID: '1234',
+ draftRevision: {
+ __typename: 'ContractRevision',
+ submitInfo: undefined,
+ unlockInfo: {
+ updatedAt: '2023-01-01T16:54:39.173Z',
+ updatedBy: {
+ email: 'cms@example.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'unlocked for a test',
+ },
+ id: '123',
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ contractName: 'MCR-MN-0005-SNBC',
+ formData: {
+ programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
+ populationCovered: 'MEDICAID',
+ submissionType: 'CONTRACT_AND_RATES',
+ riskBasedContract: true,
+ submissionDescription: 'An updated submission',
+ supportingDocuments: [],
+ stateContacts: [
+ {
+ name: 'State Contact 1',
+ titleRole: 'Test State Contact 1',
+ email: 'actuarycontact1@test.com',
+ },
+ ],
+ contractType: 'AMENDMENT',
+ contractExecutionStatus: 'EXECUTED',
+ contractDocuments: [
+ {
+ s3URL: 's3://bucketname/one-two/one-two.png',
+ sha256: 'fakesha',
+ name: 'one two',
+ dateAdded: new Date('02/02/2023')
+ },
+ ],
+ contractDateStart: new Date('02/02/2023'),
+ contractDateEnd: new Date('02/02/2024'),
+ managedCareEntities: ['MCO'],
+ federalAuthorities: ['STATE_PLAN'],
+ inLieuServicesAndSettings: true,
+ modifiedBenefitsProvided: true,
+ modifiedGeoAreaServed: false,
+ modifiedMedicaidBeneficiaries: true,
+ modifiedRiskSharingStrategy: true,
+ modifiedIncentiveArrangements: false,
+ modifiedWitholdAgreements: false,
+ modifiedStateDirectedPayments: true,
+ modifiedPassThroughPayments: true,
+ modifiedPaymentsForMentalDiseaseInstitutions: false,
+ modifiedMedicalLossRatioStandards: true,
+ modifiedOtherFinancialPaymentIncentive: false,
+ modifiedEnrollmentProcess: true,
+ modifiedGrevienceAndAppeal: false,
+ modifiedNetworkAdequacyStandards: true,
+ modifiedLengthOfContract: false,
+ modifiedNonRiskPaymentArrangements: true,
+ statutoryRegulatoryAttestation: true,
+ statutoryRegulatoryAttestationDescription: "everything meets regulatory attestation"
+ }
+ },
+
+ draftRates: [
+ {
+ id: '123',
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ status: 'SUBMITTED',
+ stateCode: 'MN',
+ revisions: [],
+ state: mockMNState(),
+ stateNumber: 5,
+ parentContractID: 'test-abc-123',
+ draftRevision: {
+ id: '123',
+ rateID: '456',
+ contractRevisions: [],
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ unlockInfo: {
+ updatedAt: new Date(),
+ updatedBy: {
+ email: 'cms@example.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'unlocked for a test',
+ },
+ formData: {
+ rateType: 'AMENDMENT',
+ rateCapitationType: 'RATE_CELL',
+ rateDocuments: [
+ {
+ s3URL: 's3://bucketname/key/rate',
+ sha256: 'fakesha',
+ name: 'rate',
+ dateAdded: new Date('03/02/2023')
+ },
+ ],
+ supportingDocuments: [],
+ rateDateStart: new Date('2020-02-02'),
+ rateDateEnd: new Date('2021-02-02'),
+ rateDateCertified: new Date(),
+ amendmentEffectiveDateStart: new Date(),
+ amendmentEffectiveDateEnd: new Date(),
+ rateProgramIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
+ deprecatedRateProgramIDs: ['d95394e5-44d1-45df-8151-1cc1ee66f10'],
+ certifyingActuaryContacts: [
+ {
+ actuarialFirm: 'DELOITTE',
+ name: 'Actuary Contact 1',
+ titleRole: 'Test Actuary Contact 1',
+ email: 'actuarycontact1@test.com',
+ },
+ ],
+ addtlActuaryContacts: [
+ {
+ actuarialFirm: 'DELOITTE',
+ name: 'Actuary Contact 1',
+ titleRole: 'Test Actuary Contact 1',
+ email: 'additionalactuarycontact1@test.com',
+ },
+ ],
+ actuaryCommunicationPreference: 'OACT_TO_ACTUARY',
+ packagesWithSharedRateCerts: [],
+ }
+ }
+
+ },
+ ],
+ packageSubmissions: [{
+ cause: 'CONTRACT_SUBMISSION',
+ submitInfo: {
+ updatedAt: '2023-01-01T16:54:39.173Z',
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'initial submission'
+ },
+ submittedRevisions: [
+ {
+ contractName: 'MCR-MN-0005-SNBC',
+ createdAt: new Date('01/01/2024'),
+ updatedAt: '2023-01-01T16:54:39.173Z',
+ submitInfo: {
+ updatedAt: '2023-01-01T16:54:39.173Z',
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'initial submission'
+ },
+ unlockInfo: {
+ updatedAt: '2023-01-01T16:54:39.173Z',
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'unlocked for a test'
+ },
+ id: '123',
+ formData: {
+ programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
+ populationCovered: 'MEDICAID',
+ submissionType: 'CONTRACT_AND_RATES',
+ riskBasedContract: true,
+ submissionDescription: 'An initial submission',
+ supportingDocuments: [],
+ stateContacts: [],
+ contractType: 'AMENDMENT',
+ contractExecutionStatus: 'EXECUTED',
+ contractDocuments: [
+ {
+ s3URL: 's3://bucketname/key/contract',
+ sha256: 'fakesha',
+ name: 'contract',
+ dateAdded: new Date()
+ },
+ ],
+ contractDateStart: new Date('01/01/2023'),
+ contractDateEnd: new Date('01/01/2024'),
+ managedCareEntities: ['MCO'],
+ federalAuthorities: ['STATE_PLAN'],
+ inLieuServicesAndSettings: true,
+ modifiedBenefitsProvided: true,
+ modifiedGeoAreaServed: false,
+ modifiedMedicaidBeneficiaries: true,
+ modifiedRiskSharingStrategy: true,
+ modifiedIncentiveArrangements: false,
+ modifiedWitholdAgreements: false,
+ modifiedStateDirectedPayments: true,
+ modifiedPassThroughPayments: true,
+ modifiedPaymentsForMentalDiseaseInstitutions: false,
+ modifiedMedicalLossRatioStandards: true,
+ modifiedOtherFinancialPaymentIncentive: false,
+ modifiedEnrollmentProcess: true,
+ modifiedGrevienceAndAppeal: false,
+ modifiedNetworkAdequacyStandards: true,
+ modifiedLengthOfContract: false,
+ modifiedNonRiskPaymentArrangements: true,
+ statutoryRegulatoryAttestation: true,
+ statutoryRegulatoryAttestationDescription: "everything meets regulatory attestation"
+ }
+ }
+ ],
+ contractRevision: {
+ contractName: 'MCR-MN-0005-SNBC',
+ createdAt: new Date('01/01/2024'),
+ updatedAt: '2024-01-01T18:54:39.173Z',
+ submitInfo: {
+ updatedAt: '2024-01-01T18:54:39.173Z',
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'initial submission'
+ },
+ unlockInfo: {
+ updatedAt: '2024-02-01T16:54:39.173Z',
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'unlocked'
+ },
+ id: '123',
+ formData: {
+ programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
+ populationCovered: 'MEDICAID',
+ submissionType: 'CONTRACT_AND_RATES',
+ riskBasedContract: true,
+ submissionDescription: 'An initial submission',
+ supportingDocuments: [],
+ stateContacts: [],
+ contractType: 'AMENDMENT',
+ contractExecutionStatus: 'EXECUTED',
+ contractDocuments: [
+ {
+ s3URL: 's3://bucketname/key/contract',
+ sha256: 'fakesha',
+ name: 'contract',
+ dateAdded: new Date()
+ },
+ ],
+ contractDateStart: new Date('01/01/2023'),
+ contractDateEnd: new Date('01/01/2024'),
+ managedCareEntities: ['MCO'],
+ federalAuthorities: ['STATE_PLAN'],
+ inLieuServicesAndSettings: true,
+ modifiedBenefitsProvided: true,
+ modifiedGeoAreaServed: false,
+ modifiedMedicaidBeneficiaries: true,
+ modifiedRiskSharingStrategy: true,
+ modifiedIncentiveArrangements: false,
+ modifiedWitholdAgreements: false,
+ modifiedStateDirectedPayments: true,
+ modifiedPassThroughPayments: true,
+ modifiedPaymentsForMentalDiseaseInstitutions: false,
+ modifiedMedicalLossRatioStandards: true,
+ modifiedOtherFinancialPaymentIncentive: false,
+ modifiedEnrollmentProcess: true,
+ modifiedGrevienceAndAppeal: false,
+ modifiedNetworkAdequacyStandards: true,
+ modifiedLengthOfContract: false,
+ modifiedNonRiskPaymentArrangements: true,
+ statutoryRegulatoryAttestation: true,
+ statutoryRegulatoryAttestationDescription: "everything meets regulatory attestation"
+ }
+ },
+ rateRevisions: [
+ {
+ id: '1234',
+ rateID: '456',
+ createdAt: new Date('01/01/2023'),
+ updatedAt: new Date('01/01/2023'),
+ submitInfo: {
+ updatedAt: new Date('01/01/2024'),
+ updatedBy: {
+ email: 'example@state.com',
+ role: 'STATE_USER',
+ givenName: 'John',
+ familyName: 'Vila'
+ },
+ updatedReason: 'initial submission'
+ },
+ contractRevisions: [],
+ formData: {
+ rateType: 'AMENDMENT',
+ rateCapitationType: 'RATE_CELL',
+ rateDocuments: [
+ {
+ s3URL: 's3://bucketname/key/rate',
+ sha256: 'fakesha',
+ name: 'rate',
+ dateAdded: new Date()
+ },
+ ],
+ supportingDocuments: [],
+ rateDateStart: new Date('2020-01-01'),
+ rateDateEnd: new Date('2021-01-01'),
+ rateDateCertified: new Date(),
+ amendmentEffectiveDateStart: new Date(),
+ amendmentEffectiveDateEnd: new Date(),
+ rateProgramIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
+ deprecatedRateProgramIDs: ['ea16a6c0-5fc6-4df8-adac-c627e76660ab'],
+ certifyingActuaryContacts: [
+ {
+ actuarialFirm: 'DELOITTE',
+ name: 'Actuary Contact 1',
+ titleRole: 'Test Actuary Contact 1',
+ email: 'actuarycontact1@test.com',
+ },
+ ],
+ addtlActuaryContacts: [
+ {
+ actuarialFirm: 'DELOITTE',
+ name: 'Actuary Contact 1',
+ titleRole: 'Test Actuary Contact 1',
+ email: 'actuarycontact1@test.com',
+ },
+ ],
+ actuaryCommunicationPreference: 'OACT_TO_ACTUARY',
+ packagesWithSharedRateCerts: [ {
+ packageName: 'testABC1',
+ packageId: 'test-abc-1',
+ },]
+ }
+ },
+ ],
+ }],
+ ...partial,
+ }
+}
function mockContractFormData( partial?: Partial): ContractFormData {
return {
programIDs: ['abbdf9b0-c49e-4c4c-bb6f-040cb7b51cce'],
@@ -2089,6 +2453,7 @@ export {
mockContractPackageSubmittedWithRevisions,
mockContractPackageWithDifferentProgramsInRevisions,
mockEmptyDraftContractAndRate,
+ mockContractPackageUnlockedWithUnlockedType,
mockContractRevision,
mockRateRevision
}
diff --git a/services/app-web/src/testHelpers/apolloMocks/index.ts b/services/app-web/src/testHelpers/apolloMocks/index.ts
index 563234b0d9..b9e326d391 100644
--- a/services/app-web/src/testHelpers/apolloMocks/index.ts
+++ b/services/app-web/src/testHelpers/apolloMocks/index.ts
@@ -75,11 +75,12 @@ export {
mockContractPackageUnlocked,
mockContractPackageSubmittedWithRevisions,
mockEmptyDraftContractAndRate,
+ mockContractPackageUnlockedWithUnlockedType,
mockContractRevision,
mockRateRevision
} from './contractPackageDataMock'
export { rateDataMock } from './rateDataMock'
-export { fetchContractMockSuccess, updateDraftContractRatesMockSuccess } from './contractGQLMock'
+export { fetchContractMockSuccess, fetchContractMockFail, updateDraftContractRatesMockSuccess, updateContractDraftRevisionMockFail, updateContractDraftRevisionMockSuccess, createContractMockFail, createContractMockSuccess } from './contractGQLMock'
export { indexRatesMockSuccess, indexRatesMockFailure } from './rateGQLMocks'
export { withdrawAndReplaceRedundantRateMock } from './replaceRateGQLMocks'
diff --git a/services/cypress/integration/cmsWorkflow/submissionReview.spec.ts b/services/cypress/integration/cmsWorkflow/submissionReview.spec.ts
index 9806d3ca9a..ae386fdcbc 100644
--- a/services/cypress/integration/cmsWorkflow/submissionReview.spec.ts
+++ b/services/cypress/integration/cmsWorkflow/submissionReview.spec.ts
@@ -8,7 +8,7 @@ describe('CMS user can view submission', () => {
cy.logInAsStateUser()
cy.startNewContractAndRatesSubmission()
cy.fillOutBaseContractDetails()
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
cy.findByRole('heading', {
level: 2,
diff --git a/services/cypress/integration/cmsWorkflow/unlockResubmit.spec.ts b/services/cypress/integration/cmsWorkflow/unlockResubmit.spec.ts
index 60c4ee81e8..62c0434e43 100644
--- a/services/cypress/integration/cmsWorkflow/unlockResubmit.spec.ts
+++ b/services/cypress/integration/cmsWorkflow/unlockResubmit.spec.ts
@@ -13,7 +13,7 @@ describe('CMS user', () => {
// fill out contract details
cy.startNewContractAndRatesSubmission()
cy.fillOutBaseContractDetails()
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
// fill out two child rates
cy.findByRole('heading', {
@@ -249,7 +249,7 @@ describe('CMS user', () => {
cy.startNewContractAndRatesSubmission()
cy.fillOutBaseContractDetails()
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
cy.findByRole('heading', { level: 2, name: /Rate details/ })
// Test unlock and resubmit with a linked rate submission
diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/rateDetails.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/rateDetails.spec.ts
index 0f4e4bb842..ed36dc4bd3 100644
--- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/rateDetails.spec.ts
+++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/rateDetails.spec.ts
@@ -41,7 +41,7 @@ describe('rate details', () => {
cy.fillOutBaseContractDetails()
//Continue to Rate details page
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
cy.findByRole('heading', { level: 2, name: /Rate details/ })
//Add two more rate certifications, total three
diff --git a/services/cypress/integration/stateWorkflow/stateSubmissionForm/submissionForm.spec.ts b/services/cypress/integration/stateWorkflow/stateSubmissionForm/submissionForm.spec.ts
index 8a2ca7e680..647235e5c8 100644
--- a/services/cypress/integration/stateWorkflow/stateSubmissionForm/submissionForm.spec.ts
+++ b/services/cypress/integration/stateWorkflow/stateSubmissionForm/submissionForm.spec.ts
@@ -9,7 +9,7 @@ describe('state user in state submission form', () => {
cy.logInAsStateUser()
// Start a base contract only submissions
- cy.startNewContractOnlySubmissionWithBaseContract()
+ cy.startNewContractOnlySubmissionWithBaseContractV2()
// Save submission URL
cy.location().then((fullUrl) => {
@@ -44,18 +44,20 @@ describe('state user in state submission form', () => {
)
// Change to contract and rates and contract amendment
- cy.findByText('Contract action and rate certification').click()
+ cy.findByLabelText('Contract action and rate certification').check({force: true})
cy.findByLabelText('Contract action and rate certification').should(
'be.checked'
)
- cy.findByText('Amendment to base contract').click()
+ cy.findByLabelText('Amendment to base contract').check({force: true})
+ cy.findByLabelText('Amendment to base contract').should('be.checked')
+ cy.get('label[for="riskBasedContractNo"]').click()
cy.findByRole('textbox', { name: 'Submission description' }).clear().type(
'description of contract only submission with amendment'
)
// Save as draft
- cy.deprecatedNavigateV1Form('SAVE_DRAFT')
+ cy.navigateContractForm('SAVE_DRAFT')
cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ })
// Link to type page and continue forward
@@ -64,27 +66,27 @@ describe('state user in state submission form', () => {
)
cy.findByTestId('step-indicator').findAllByRole('listitem').should('have.length', 6)
cy.findByText('Rate details').should('exist')
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
// CHECK CONTRACT DETAILS PAGE NAVIGATION
cy.findByRole('heading', { level: 2, name: /Contract details/ })
// Navigate back to previous page
- cy.deprecatedNavigateV1Form('BACK')
+ cy.navigateContractForm('BACK')
cy.findByRole('heading', { level: 2, name: /Submission type/ })
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
// Change to contract amendment, save as draft
cy.findByRole('heading', { level: 2, name: /Contract details/ })
cy.fillOutAmendmentToBaseContractDetails()
- cy.deprecatedNavigateV1Form('SAVE_DRAFT')
+ cy.navigateContractForm('SAVE_DRAFT')
cy.findByRole('heading', { level: 1, name: /Submissions dashboard/ })
// Link to contract details page and continue
cy.navigateFormByDirectLink(
`/submissions/${draftSubmissionId}/edit/contract-details`
)
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
// CHECK RATE DETAILS PAGE NAVIGATION
cy.findByRole('heading', { level: 2, name: /Rate details/ })
@@ -92,7 +94,7 @@ describe('state user in state submission form', () => {
// Navigate back to previous page
cy.navigateContractRatesForm('BACK')
cy.findByRole('heading', { level: 2, name: /Contract details/ })
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
// Add base rate data, save as draft
cy.findByRole('heading', { level: 2, name: /Rate details/ })
diff --git a/services/cypress/integration/stateWorkflow/submissionSummary.spec.ts b/services/cypress/integration/stateWorkflow/submissionSummary.spec.ts
index 8ab06ac816..3cdfb4b317 100644
--- a/services/cypress/integration/stateWorkflow/submissionSummary.spec.ts
+++ b/services/cypress/integration/stateWorkflow/submissionSummary.spec.ts
@@ -9,13 +9,13 @@ describe('State user can view submissions', () => {
// add a draft contract only submission
cy.startNewContractOnlySubmissionWithBaseContract()
- cy.deprecatedNavigateV1Form('SAVE_DRAFT')
+ cy.navigateContractForm('SAVE_DRAFT')
// add a submitted contract and rates submission
cy.startNewContractAndRatesSubmission()
cy.fillOutBaseContractDetails()
- cy.deprecatedNavigateV1Form('CONTINUE')
+ cy.navigateContractForm('CONTINUE')
cy.findByRole('heading', {
level: 2,
diff --git a/services/cypress/support/commands.ts b/services/cypress/support/commands.ts
index f82e1f6c44..faa536d6d5 100644
--- a/services/cypress/support/commands.ts
+++ b/services/cypress/support/commands.ts
@@ -53,12 +53,14 @@ Cypress.Commands.add('interceptGraphQL', () => {
aliasQuery(req, 'indexRates')
aliasQuery(req, 'fetchContract')
aliasMutation(req, 'createHealthPlanPackage')
+ aliasMutation(req, 'createContract')
aliasMutation(req, 'updateHealthPlanFormData')
aliasMutation(req, 'submitHealthPlanPackage')
aliasMutation(req, 'updateCMSUser')
aliasMutation(req, 'createQuestion')
aliasMutation(req, 'createQuestionResponse')
aliasMutation(req, 'updateDraftContractRates')
+ aliasMutation(req, 'updateContractDraftRevision')
aliasMutation(req, 'submitContract')
}).as('GraphQL')
})
diff --git a/services/cypress/support/index.ts b/services/cypress/support/index.ts
index 57fecc8be4..574d29d326 100644
--- a/services/cypress/support/index.ts
+++ b/services/cypress/support/index.ts
@@ -52,6 +52,7 @@ declare global {
// state submission form commands
waitForDocumentsToLoad(): void
startNewContractOnlySubmissionWithBaseContract(): void
+ startNewContractOnlySubmissionWithBaseContractV2(): void
startNewContractOnlySubmissionWithAmendment(): void
startNewContractAndRatesSubmission(): void
fillOutContractActionOnlyWithBaseContract(): void
@@ -80,6 +81,10 @@ declare global {
buttonName: FormButtonKey,
waitForLoad?: boolean
): void
+ navigateContractForm(
+ buttonName: FormButtonKey,
+ waitForLoad?: boolean
+ ): void
navigateFormByDirectLink(url: string, waitForLoad?: boolean): void
// dashboard commands
diff --git a/services/cypress/support/navigateCommands.ts b/services/cypress/support/navigateCommands.ts
index 5950ba783a..930fb41f54 100644
--- a/services/cypress/support/navigateCommands.ts
+++ b/services/cypress/support/navigateCommands.ts
@@ -30,7 +30,7 @@ Cypress.Commands.add(
cy.findByRole('heading',{name:'Submissions'}).should('exist')
} else if (buttonKey === 'CONTINUE_FROM_START_NEW') {
if (waitForLoad) {
- cy.wait('@createHealthPlanPackageMutation', { timeout: 50_000 })
+ // cy.wait('@createHealthPlanPackageMutation', { timeout: 50_000 })
cy.wait('@fetchHealthPlanPackageWithQuestionsQuery')
}
cy.findByTestId('state-submission-form-page').should('exist')
@@ -59,13 +59,13 @@ Cypress.Commands.add(
if (buttonKey === 'SAVE_DRAFT') {
if(waitForLoad) {
cy.wait('@updateDraftContractRatesMutation', { timeout: 50_000})
- }
+ }
cy.findByTestId('state-dashboard-page').should('exist')
cy.findByRole('heading',{name:'Submissions'}).should('exist')
} else if (buttonKey === 'CONTINUE_FROM_START_NEW') {
if (waitForLoad) {
// cy.wait('@createContractMutation', { timeout: 50_000 })
- cy.wait('@fetchContractQuery')
+ cy.wait('@fetchContractQuery', { timeout: 20_000 })
}
cy.findByTestId('state-submission-form-page').should('exist')
} else if (buttonKey === 'CONTINUE') {
@@ -80,6 +80,41 @@ Cypress.Commands.add(
}
)
+// navigate helper for v2 forms
+Cypress.Commands.add(
+ 'navigateContractForm',
+ (buttonKey: FormButtonKey, waitForLoad = true) => {
+ cy.findByRole('button', {
+ name: buttonsWithLabels[buttonKey],
+ }).should('not.have.attr', 'aria-disabled')
+ cy.findByRole('button', {
+ name: buttonsWithLabels[buttonKey],
+ }).safeClick()
+
+ if (buttonKey === 'SAVE_DRAFT') {
+ if(waitForLoad) {
+ cy.wait('@updateContractDraftRevisionMutation', { timeout: 50_000})
+ }
+ cy.findByTestId('state-dashboard-page').should('exist')
+ cy.findByRole('heading',{name:'Submissions'}).should('exist')
+ } else if (buttonKey === 'CONTINUE_FROM_START_NEW') {
+ if (waitForLoad) {
+ // cy.wait('@createContractMutation', { timeout: 50_000 })
+ cy.wait('@fetchContractQuery', { timeout: 20_000 })
+ }
+ cy.findByTestId('state-submission-form-page').should('exist')
+ } else if (buttonKey === 'CONTINUE') {
+ if (waitForLoad) {
+ cy.findAllByTestId('errorMessage').should('have.length', 0)
+ // cy.wait('@updateContractDraftRevisionMutation', { timeout: 50_000})
+ }
+ cy.findByTestId('state-submission-form-page').should('exist')
+ } else {
+ cy.findByTestId('state-submission-form-page').should('exist')
+ }
+ }
+)
+
Cypress.Commands.add(
'navigateFormByDirectLink',
(url: string, waitForLoad = true) => {
diff --git a/services/cypress/support/stateSubmissionFormCommands.ts b/services/cypress/support/stateSubmissionFormCommands.ts
index 11724ba3da..98f260f3c3 100644
--- a/services/cypress/support/stateSubmissionFormCommands.ts
+++ b/services/cypress/support/stateSubmissionFormCommands.ts
@@ -10,6 +10,18 @@ Cypress.Commands.add('startNewContractOnlySubmissionWithBaseContract', () => {
cy.findByRole('heading', { level: 2, name: /Contract details/ })
})
+Cypress.Commands.add('startNewContractOnlySubmissionWithBaseContractV2', () => {
+ // Must be on '/submissions/new'
+ cy.findByTestId('state-dashboard-page').should('exist')
+ cy.findByRole('link', { name: 'Start new submission' }).click()
+ cy.findByRole('heading', { level: 1, name: /New submission/ })
+
+ cy.fillOutContractActionOnlyWithBaseContract()
+
+ cy.navigateContractForm('CONTINUE')
+ cy.findByRole('heading', { level: 2, name: /Contract details/ })
+})
+
Cypress.Commands.add('startNewContractOnlySubmissionWithAmendment', () => {
// Must be on '/submissions/new'
cy.findByTestId('state-dashboard-page').should('exist')