diff --git a/services/app-web/src/formHelpers/formatters.ts b/services/app-web/src/formHelpers/formatters.ts
index 10c1a49a7c..cdf6c2274e 100644
--- a/services/app-web/src/formHelpers/formatters.ts
+++ b/services/app-web/src/formHelpers/formatters.ts
@@ -145,7 +145,7 @@ const formatDocumentsForForm = ({
name: doc.name,
key: key,
s3URL: doc.s3URL,
- sha256: doc.sha256,
+ sha256: doc.sha256 ?? undefined,
status: 'UPLOAD_COMPLETE',
}
}) || []
diff --git a/services/app-web/src/pages/App/AppRoutes.tsx b/services/app-web/src/pages/App/AppRoutes.tsx
index 2bf67b4544..d7d70df89b 100644
--- a/services/app-web/src/pages/App/AppRoutes.tsx
+++ b/services/app-web/src/pages/App/AppRoutes.tsx
@@ -37,8 +37,9 @@ import {
UploadQuestions,
} from '../QuestionResponse'
import { GraphQLExplorer } from '../GraphQLExplorer/GraphQLExplorer'
-import { RateSummary } from '../SubmissionSummary/RateSummary'
-import { RateEdit } from '../RateEdit/RateEdit'
+import { RateSummary } from '../RateSummary'
+import { RateDetailsV2 } from '../StateSubmission/RateDetails/RateDetailsV2'
+import { RateEdit } from '../RateSubmission/RateEdit/RateEdit'
function componentForAuthMode(
authMode: AuthModeType
@@ -108,10 +109,16 @@ const StateUserRoutes = ({
element={}
/>
{showRatePages && (
+ <>
}
/>
+ }
+ />
+ >
)}
}>
{showQuestionResponse && (
diff --git a/services/app-web/src/pages/RateEdit/RateEdit.test.tsx b/services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.test.tsx
similarity index 84%
rename from services/app-web/src/pages/RateEdit/RateEdit.test.tsx
rename to services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.test.tsx
index 420d69c305..e62c45f946 100644
--- a/services/app-web/src/pages/RateEdit/RateEdit.test.tsx
+++ b/services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.test.tsx
@@ -1,8 +1,8 @@
import { screen, waitFor } from '@testing-library/react'
-import { renderWithProviders } from "../../testHelpers"
+import { renderWithProviders } from "../../../testHelpers"
import { RateEdit } from "./RateEdit"
-import { fetchCurrentUserMock, fetchRateMockSuccess, mockValidStateUser } from "../../testHelpers/apolloMocks"
-import { RoutesRecord } from '../../constants'
+import { fetchCurrentUserMock, fetchRateMockSuccess, mockValidStateUser } from "../../../testHelpers/apolloMocks"
+import { RoutesRecord } from '../../../constants'
import { Route, Routes } from 'react-router-dom'
// Wrap test component in some top level routes to allow getParams to be tested
@@ -33,8 +33,8 @@ describe('RateEdit', () => {
routerProvider: {
route: '/rates/1337/edit'
},
- featureFlags: {
- 'rate-edit-unlock': true
+ featureFlags: {
+ 'rate-edit-unlock': true
}
})
diff --git a/services/app-web/src/pages/RateEdit/RateEdit.tsx b/services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.tsx
similarity index 79%
rename from services/app-web/src/pages/RateEdit/RateEdit.tsx
rename to services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.tsx
index 552c094d96..2866d1baa6 100644
--- a/services/app-web/src/pages/RateEdit/RateEdit.tsx
+++ b/services/app-web/src/pages/RateSubmission/RateEdit/RateEdit.tsx
@@ -1,10 +1,10 @@
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
-import { useFetchRateQuery } from "../../gen/gqlClient";
+import { useFetchRateQuery } from "../../../gen/gqlClient";
import { GridContainer } from "@trussworks/react-uswds";
-import { Loading } from "../../components";
-import { GenericErrorPage } from "../Errors/GenericErrorPage";
-import { RateDetailsV2 } from "../StateSubmission/RateDetails/RateDetailsV2";
+import { Loading } from "../../../components";
+import { GenericErrorPage } from "../../Errors/GenericErrorPage";
+import { RateDetailsV2 } from "../../StateSubmission/RateDetails/RateDetailsV2";
type RouteParams = {
id: string
diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx b/services/app-web/src/pages/RateSummary/RateSummary.test.tsx
similarity index 92%
rename from services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx
rename to services/app-web/src/pages/RateSummary/RateSummary.test.tsx
index 490f69e143..931131939c 100644
--- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.test.tsx
+++ b/services/app-web/src/pages/RateSummary/RateSummary.test.tsx
@@ -1,15 +1,15 @@
import { screen, waitFor } from '@testing-library/react'
-import { renderWithProviders, testS3Client } from '../../../testHelpers'
+import { renderWithProviders, testS3Client } from '../../testHelpers'
import {
fetchCurrentUserMock,
fetchRateMockSuccess,
mockValidCMSUser,
mockValidStateUser,
-} from '../../../testHelpers/apolloMocks'
+} from '../../testHelpers/apolloMocks'
import { RateSummary } from './RateSummary'
-import { RoutesRecord } from '../../../constants'
+import { RoutesRecord } from '../../constants'
import { Route, Routes } from 'react-router-dom'
-import { RateEdit } from '../../RateEdit/RateEdit'
+import { RateEdit } from '../RateSubmission/RateEdit/RateEdit'
// Wrap test component in some top level routes to allow getParams to be tested
const wrapInRoutes = (children: React.ReactNode) => {
@@ -146,15 +146,15 @@ describe('RateSummary', () => {
it('redirects to RateEdit component from RateSummary without errors for unlocked rate', async () => {
renderWithProviders(
- }
+ }
/>
- }
+ }
/>
- ,
+ ,
{
apolloProvider: {
mocks: [
@@ -168,8 +168,8 @@ describe('RateSummary', () => {
routerProvider: {
route: '/rates/1337'
},
- featureFlags: {
- 'rate-edit-unlock': true
+ featureFlags: {
+ 'rate-edit-unlock': true
}
})
diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx b/services/app-web/src/pages/RateSummary/RateSummary.tsx
similarity index 86%
rename from services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx
rename to services/app-web/src/pages/RateSummary/RateSummary.tsx
index bffa5e076f..d4aa0cc2a0 100644
--- a/services/app-web/src/pages/SubmissionSummary/RateSummary/RateSummary.tsx
+++ b/services/app-web/src/pages/RateSummary/RateSummary.tsx
@@ -2,14 +2,14 @@ import { GridContainer, Icon, Link } from '@trussworks/react-uswds'
import React, { useEffect, useState } from 'react'
import { NavLink, useNavigate, useParams } from 'react-router-dom'
-import { Loading } from '../../../components'
-import { usePage } from '../../../contexts/PageContext'
-import { useFetchRateQuery } from '../../../gen/gqlClient'
+import { Loading } from '../../components'
+import { usePage } from '../../contexts/PageContext'
+import { useFetchRateQuery } from '../../gen/gqlClient'
import styles from '../SubmissionSummary.module.scss'
-import { GenericErrorPage } from '../../Errors/GenericErrorPage'
-import { RoutesRecord } from '../../../constants'
-import { SingleRateSummarySection } from '../../../components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection'
-import { useAuth } from '../../../contexts/AuthContext'
+import { GenericErrorPage } from '../Errors/GenericErrorPage'
+import { RoutesRecord } from '../../constants'
+import { SingleRateSummarySection } from '../../components/SubmissionSummarySection/RateDetailsSummarySection/SingleRateSummarySection'
+import { useAuth } from '../../contexts/AuthContext'
type RouteParams = {
id: string
diff --git a/services/app-web/src/pages/SubmissionSummary/RateSummary/index.ts b/services/app-web/src/pages/RateSummary/index.ts
similarity index 100%
rename from services/app-web/src/pages/SubmissionSummary/RateSummary/index.ts
rename to services/app-web/src/pages/RateSummary/index.ts
diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsSchema.ts b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsSchema.ts
index 3ec15222c7..63dafdc998 100644
--- a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsSchema.ts
+++ b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsSchema.ts
@@ -14,7 +14,7 @@ const SingleRateCertSchema = (_activeFeatureFlags: FeatureFlagSettings) =>
rateDocuments: validateFileItemsListSingleUpload({ required: true }),
supportingDocuments: validateFileItemsList({ required: false }),
hasSharedRateCert: Yup.string().defined('You must select yes or no'),
- packagesWithSharedRateCerts: Yup.array()
+ packagesWithSharedRateCerts: _activeFeatureFlags['rate-edit-unlock']? Yup.array()
.when('hasSharedRateCert', {
is: 'YES',
then: Yup.array().min(
@@ -22,7 +22,7 @@ const SingleRateCertSchema = (_activeFeatureFlags: FeatureFlagSettings) =>
'You must select at least one submission'
),
})
- .required(),
+ .required(): Yup.array().optional(),
rateProgramIDs: Yup.array().min(1, 'You must select a program'),
rateType: Yup.string().defined(
'You must choose a rate certification type'
diff --git a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsV2.tsx b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsV2.tsx
index f5ee893fbc..b5eaf1af5b 100644
--- a/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsV2.tsx
+++ b/services/app-web/src/pages/StateSubmission/RateDetails/RateDetailsV2.tsx
@@ -1,13 +1,11 @@
import React, { useEffect } from 'react'
import { Form as UswdsForm } from '@trussworks/react-uswds'
-import { FieldArray, FieldArrayRenderProps, Formik, FormikErrors } from 'formik'
+import { Formik, FormikErrors } from 'formik'
import { useNavigate, useParams } from 'react-router-dom'
import { v4 as uuidv4 } from 'uuid'
import styles from '../StateSubmissionForm.module.scss'
-import { RateInfoType } from '../../../common-code/healthPlanFormDataType'
-
import { ErrorSummary, GenericApiErrorBanner, Loading } from '../../../components'
import { formatFormDateForDomain } from '../../../formHelpers'
import { RateDetailsFormSchema } from './RateDetailsSchema'
@@ -20,24 +18,23 @@ import {
formatDocumentsForForm,
formatForForm,
} from '../../../formHelpers/formatters'
-import {
- RateCertFormType,
- SingleRateCert,
-} from './SingleRateCert/SingleRateCert'
import { useS3 } from '../../../contexts/S3Context'
import { S3ClientT } from '../../../s3'
import { isLoadingOrHasFileErrors } from '../../../components/FileUpload'
import { RoutesRecord } from '../../../constants'
-import { SectionCard } from '../../../components/SectionCard'
import { Rate, RateRevision, useFetchRateQuery, useSubmitRateMutation } from '../../../gen/gqlClient'
import { Error404 } from '../../Errors/Error404Page'
import { GenericErrorPage } from '../../Errors/GenericErrorPage'
+import { SingleRateCertV2 } from './SingleRateCert/SingleRateCertV2'
+
+
+export type RateCertForm = {key: string} & RateRevision['formData']
// This function is used to get initial form values as well return empty form values when we add a new rate or delete a rate
// We need to include the getKey function in params because there are no guarantees currently file is in s3 even if when we load data from API
const generateRateCertFormValues = (
getKey: S3ClientT['getKey'],
- rateRev?: RateRevision): RateCertFormType => {
+ rateRev?: RateRevision): RateCertForm => {
const rateInfo = rateRev?.formData
const newRateID = uuidv4();
@@ -49,12 +46,12 @@ const generateRateCertFormValues = (
rateDateStart: formatForForm(rateInfo?.rateDateStart),
rateDateEnd: formatForForm(rateInfo?.rateDateEnd),
rateDateCertified: formatForForm(rateInfo?.rateDateCertified),
- effectiveDateStart: formatForForm(
- rateInfo?.amendmentEffectiveDateStart
- ),
- effectiveDateEnd: formatForForm(
- rateInfo?.amendmentEffectiveDateEnd
- ),
+ effectiveDateStart: formatForForm(
+ rateInfo?.amendmentEffectiveDateStart
+ ),
+ effectiveDateEnd: formatForForm(
+ rateInfo?.amendmentEffectiveDateEnd
+ ),
rateProgramIDs: rateInfo?.rateProgramIDs ?? [],
rateDocuments: formatDocumentsForForm({
documents: rateInfo?.rateDocuments,
@@ -69,15 +66,9 @@ const generateRateCertFormValues = (
),
actuaryCommunicationPreference:
rateInfo?.actuaryCommunicationPreference ?? undefined,
- packagesWithSharedRateCerts:
- rateInfo?.packagesWithSharedRateCerts ?? [],
}
}
-interface RateInfoArrayType {
- rateInfos: RateCertFormType[]
-}
-
export const rateErrorHandling = (
error: string | FormikErrors | undefined
): FormikErrors | undefined => {
@@ -104,34 +95,10 @@ export const RateDetailsV2 = ({
}
const { getKey } = useS3()
- // API handling
- const [submitRate, { loading: submitRateLoading }] = useSubmitRateMutation()
- const { data: fetchRateQuery, loading: fetchRateLoading, error: fetchRateError } = useFetchRateQuery({
- variables: {
- input: {
- rateID: id
- }
- }
- })
-
- if (fetchRateLoading){
- return
- }
-
- if( fetchRateError) {
- return
- } else if (!fetchRateQuery) {
- return
- }
- const rate = fetchRateQuery?.fetchRate.rate
- const previousDocuments: string[] = []
// Form validation
const [shouldValidate, setShouldValidate] = React.useState(showValidations)
- const rateDetailsFormSchema = RateDetailsFormSchema()
- const rateInfosInitialValues: RateInfoArrayType = {
- rateInfos: [generateRateCertFormValues(getKey, rate.draftRevision ?? undefined)],
- }
+ const rateDetailsFormSchema = RateDetailsFormSchema({'rate-edit-unlock': true})
// UI focus state management
const [focusNewRate, setFocusNewRate] = React.useState(false)
@@ -158,21 +125,43 @@ export const RateDetailsV2 = ({
}, [focusErrorSummaryHeading])
+ // API handling
+ const [submitRate, { loading: submitRateLoading }] = useSubmitRateMutation()
+ const { data: fetchRateQuery, loading: fetchRateLoading, error: fetchRateError } = useFetchRateQuery({
+ variables: {
+ input: {
+ rateID: id
+ }
+ }
+ })
+
+ if (fetchRateLoading){
+ return
+ }
+
+ if( fetchRateError) {
+ return
+ } else if (!fetchRateQuery) {
+ return
+ }
+ const rate = fetchRateQuery?.fetchRate.rate
+ const previousDocuments: string[] = []
+
+ // Formik setup
+ const initialValues: RateCertForm = generateRateCertFormValues(getKey, rate.draftRevision ?? undefined)
+
const handleFormSubmit = async (
- form: RateInfoArrayType,
+ form: RateCertForm ,
setSubmitting: (isSubmitting: boolean) => void, // formik setSubmitting
options: {
shouldValidateDocuments: boolean
redirectPath: string
}
) => {
- const { rateInfos } = form
if (options.shouldValidateDocuments) {
- const fileErrorsNeedAttention = rateInfos.some((rateInfo) =>
- isLoadingOrHasFileErrors(
- rateInfo.supportingDocuments.concat(rateInfo.rateDocuments)
+ const fileErrorsNeedAttention = isLoadingOrHasFileErrors(
+ initialValues.supportingDocuments.concat(initialValues.rateDocuments)
)
- )
if (fileErrorsNeedAttention) {
// make inline field errors visible so user can correct documents, direct user focus to errors, and manually exit formik submit
setShouldValidate(true)
@@ -182,49 +171,41 @@ export const RateDetailsV2 = ({
}
}
- const cleanedRateInfos = rateInfos.map((rateInfo) => {
- return {
- id: rateInfo.id,
- rateType: rateInfo.rateType,
- rateCapitationType: rateInfo.rateCapitationType,
- rateDocuments: formatDocumentsForDomain(rateInfo.rateDocuments),
+ const cleanedrateCertForms = {
+ id: form.id,
+ rateType: form.rateType,
+ rateCapitationType: form.rateCapitationType,
+ rateDocuments: formatDocumentsForDomain(form.rateDocuments),
supportingDocuments: formatDocumentsForDomain(
- rateInfo.supportingDocuments
+ form.supportingDocuments
),
- rateDateStart: formatFormDateForDomain(rateInfo.rateDateStart),
- rateDateEnd: formatFormDateForDomain(rateInfo.rateDateEnd),
+ rateDateStart: formatFormDateForDomain(form.rateDateStart),
+ rateDateEnd: formatFormDateForDomain(form.rateDateEnd),
rateDateCertified: formatFormDateForDomain(
- rateInfo.rateDateCertified
+ form.rateDateCertified
),
rateAmendmentInfo:
- rateInfo.rateType === 'AMENDMENT'
+ form.rateType === 'AMENDMENT'
? {
effectiveDateStart: formatFormDateForDomain(
- rateInfo.effectiveDateStart
+ form.effectiveDateStart
),
effectiveDateEnd: formatFormDateForDomain(
- rateInfo.effectiveDateEnd
+ form.effectiveDateEnd
),
}
: undefined,
- rateProgramIDs: rateInfo.rateProgramIDs,
- actuaryContacts: rateInfo.actuaryContacts,
+ rateProgramIDs: form.rateProgramIDs,
+ actuaryContacts: form.actuaryContacts,
actuaryCommunicationPreference:
- rateInfo.actuaryCommunicationPreference,
- packagesWithSharedRateCerts:
- rateInfo.hasSharedRateCert === 'YES'
- ? rateInfo.packagesWithSharedRateCerts
- : [],
- }
- })
-
- const initialRateRevision = cleanedRateInfos[0] // only submit the first rate revision right now from RateDetailsV2, adapt later for mulit-rate workflow
+ form.actuaryCommunicationPreference,
+ }
try {
const updatedSubmission = await submitRate({
variables: {
input: {
- rateID: initialRateRevision.id,
+ rateID: rate.id,
// formData: initialRateRevision
}
},
@@ -247,9 +228,9 @@ export const RateDetailsV2 = ({
// Error summary object keys will be used as DOM focus point from error-summary. Must be valid html selector
// Error summary object values will be used as messages displays in error summary
const generateErrorSummaryErrors = (
- errors: FormikErrors
+ errors: FormikErrors
) => {
- const rateErrors = errors.rateInfos
+ const rateErrors = errors
const errorObject: { [field: string]: string } = {}
if (rateErrors && Array.isArray(rateErrors)) {
@@ -258,12 +239,11 @@ export const RateDetailsV2 = ({
Object.entries(rateError).forEach(([field, value]) => {
if (typeof value === 'string') {
+
//rateProgramIDs error message needs a # proceeding the key name because this is the only way to be able to link to the Select component element see comments in ErrorSummaryMessage component.
const errorKey =
- field === 'rateProgramIDs' ||
- field === 'packagesWithSharedRateCerts'
- ? `#rateInfos.${index}.${field}`
- : `rateInfos.${index}.${field}`
+ field === 'rateProgramIDs' ? `#rateCertForms.${index}.${field}`
+ : `rateCertForms.${index}.${field}`
errorObject[errorKey] = value
}
// If the field is actuaryContacts then the value should be an array with at least one object of errors
@@ -277,7 +257,7 @@ export const RateDetailsV2 = ({
Object.entries(actuaryContact).forEach(
([contactField, contactValue]) => {
if (typeof contactValue === 'string') {
- const errorKey = `rateInfos.${index}.actuaryContacts.0.${contactField}`
+ const errorKey = `rateCertForms.${index}.actuaryContacts.0.${contactField}`
errorObject[errorKey] = contactValue
}
}
@@ -292,9 +272,9 @@ export const RateDetailsV2 = ({
return (
{
- return handleFormSubmit({ rateInfos }, setSubmitting, {
+ initialValues={initialValues}
+ onSubmit={({ rateCertForm }, { setSubmitting }) => {
+ return handleFormSubmit({ rateCertForm }, setSubmitting, {
shouldValidateDocuments: true,
redirectPath: `../contacts`,
})
@@ -302,7 +282,7 @@ export const RateDetailsV2 = ({
validationSchema={rateDetailsFormSchema}
>
{({
- values: { rateInfos },
+ values: { rateCertForm },
errors,
dirty,
handleSubmit,
@@ -313,7 +293,7 @@ export const RateDetailsV2 = ({
<>
{
@@ -333,69 +313,25 @@ export const RateDetailsV2 = ({
headingRef={errorSummaryHeadingRef}
/>
)}
-
- {({
- remove,
- push,
- }: FieldArrayRenderProps) => (
- <>
- {rateInfos.map(
- (rateInfo, index) => (
- {
- remove(index)
- setNewRateButtonFocus()
- },
- reassignNewRateRef:
- (el) =>
- (newRateNameRef.current =
- el),
- }}
- />
- )
- )}
-
-
- Additional rate
- certification
-
-
-
- >
- )}
-
+
{
const redirectPath = `../contract-details`
if (dirty) {
await handleFormSubmit(
- { rateInfos },
+ { rateCertForms },
setSubmitting,
{
shouldValidateDocuments: false,
@@ -408,7 +344,7 @@ export const RateDetailsV2 = ({
}}
saveAsDraftOnClick={async () => {
await handleFormSubmit(
- { rateInfos },
+ { rateCertForms },
setSubmitting,
{
shouldValidateDocuments: true,
diff --git a/services/app-web/src/pages/SubmissionSummary/index.ts b/services/app-web/src/pages/SubmissionSummary/index.ts
index 9694162a2b..055197426d 100644
--- a/services/app-web/src/pages/SubmissionSummary/index.ts
+++ b/services/app-web/src/pages/SubmissionSummary/index.ts
@@ -1,2 +1,2 @@
export { SubmissionSummary } from './SubmissionSummary'
-export { RateSummary } from './RateSummary'
+export { RateSummary } from '../RateSummary'