diff --git a/services/app-web/src/components/FileUpload/FileUpload.stories.tsx b/services/app-web/src/components/FileUpload/FileUpload.stories.tsx index 8b6b9434ff..80a2029de3 100644 --- a/services/app-web/src/components/FileUpload/FileUpload.stories.tsx +++ b/services/app-web/src/components/FileUpload/FileUpload.stories.tsx @@ -26,7 +26,7 @@ export const DemoListUploadSuccess = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -50,7 +50,7 @@ export const DemoTableUploadSuccess = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -74,7 +74,7 @@ export const DemoListUploadFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -98,7 +98,7 @@ export const DemoTableUploadFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -122,7 +122,7 @@ export const DemoListScanFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) @@ -146,7 +146,7 @@ export const DemoTableScanFailure = (): React.ReactElement => { await fakeRequest(true, resolveData) return }} - onFileItemsUpdate={() => console.log('Async load complete')} + onFileItemsUpdate={() => console.info('Async load complete')} isContractOnly={false} /> ) diff --git a/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx b/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx index ed5c30386b..3c4c55d1f8 100644 --- a/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx +++ b/services/app-web/src/components/Form/FieldCheckbox/FieldCheckbox.stories.tsx @@ -10,7 +10,7 @@ export default { const Template: Story = (args) => ( console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx b/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx index b9479cc2ba..b239477964 100644 --- a/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx +++ b/services/app-web/src/components/Form/FieldDropdown/FieldDropdown.stories.tsx @@ -16,7 +16,7 @@ const Template: Story = (args) => ( initialValues={{ program: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx b/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx index efa171c147..3b0023cfa9 100644 --- a/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx +++ b/services/app-web/src/components/Form/FieldRadio/FieldRadio.stories.tsx @@ -10,7 +10,7 @@ export default { const Template: Story = (args) => ( console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx b/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx index 2c5d4d0716..7f3cef6d73 100644 --- a/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx +++ b/services/app-web/src/components/Form/FieldTextInput/FieldTextInput.stories.tsx @@ -18,7 +18,7 @@ const Template: Story = (args) => ( initialValues={{ submissionDescription: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx index 5ce3417bcb..f73ed76a73 100644 --- a/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx +++ b/services/app-web/src/components/Form/FieldTextarea/FieldTextarea.stories.tsx @@ -19,7 +19,7 @@ const Template: Story = (args) => ( initialValues={{ submissionDescription: '' }} validationSchema={schema} validateOnMount={true} - onSubmit={(e) => console.log('submitted')} + onSubmit={(e) => console.info('submitted')} > diff --git a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx index 34899bc55f..7a7a67bbf3 100644 --- a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.test.tsx @@ -31,6 +31,7 @@ describe('ContractDetailsSummarySection', () => { renderWithProviders( { it('can render state submission on summary page without errors (submission summary behavior)', () => { renderWithProviders( { it('can render all contract details fields', () => { renderWithProviders( { it('displays correct effective dates text for base contract', () => { renderWithProviders( @@ -137,6 +141,7 @@ describe('ContractDetailsSummarySection', () => { it('displays correct effective dates text for contract amendment', () => { renderWithProviders( @@ -179,6 +184,7 @@ describe('ContractDetailsSummarySection', () => { } renderWithProviders( @@ -220,6 +226,7 @@ describe('ContractDetailsSummarySection', () => { it('does not render supporting contract documents table when no documents exist', () => { renderWithProviders( @@ -235,6 +242,7 @@ describe('ContractDetailsSummarySection', () => { it('does not render download all button when on previous submission', () => { renderWithProviders( @@ -249,6 +257,7 @@ describe('ContractDetailsSummarySection', () => { it('renders federal authorities for a medicaid contract', async () => { renderWithProviders( { it('renders federal authorities for a CHIP contract as expected, removing invalid authorities', async () => { renderWithProviders( { it('renders provisions and MLR references for a medicaid amendment', () => { renderWithProviders( @@ -354,9 +367,7 @@ describe('ContractDetailsSummarySection', () => { ).toBeInTheDocument() expect( - within(modifiedProvisions).getByText( - /Risk-sharing strategy/ - ) + within(modifiedProvisions).getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( within(modifiedProvisions).getByText( @@ -425,6 +436,9 @@ describe('ContractDetailsSummarySection', () => { it('renders provisions and MLR references for a medicaid base contract', () => { renderWithProviders( { ).toBeInTheDocument() expect( - within(modifiedProvisions).getByText( - /Risk-sharing strategy/ - ) + within(modifiedProvisions).getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( within(modifiedProvisions).getByText( @@ -475,6 +487,9 @@ describe('ContractDetailsSummarySection', () => { it('renders provisions with correct MLR references for CHIP amendment', () => { renderWithProviders( { } renderWithProviders( { } renderWithProviders( { } renderWithProviders( , @@ -701,6 +725,9 @@ describe('ContractDetailsSummarySection', () => { } renderWithProviders( { } renderWithProviders( , diff --git a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx index 2fb4b41ca6..451c242124 100644 --- a/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/ContractDetailsSummarySection/ContractDetailsSummarySection.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react' import { DataDetail } from '../../../components/DataDetail' import { SectionHeader } from '../../../components/SectionHeader' import { UploadedDocumentsTable } from '../../../components/SubmissionSummarySection' -import { DocumentDateLookupTable } from '../../../pages/SubmissionSummary/SubmissionSummary' import { ContractExecutionStatusRecord, FederalAuthorityRecord, @@ -30,11 +29,12 @@ import { HealthPlanFormDataType, federalAuthorityKeysForCHIP, } from '../../../common-code/healthPlanFormDataType' +import { DocumentDateLookupTableType } from '../../../documentHelpers/makeDocumentDateLookupTable' export type ContractDetailsSummarySectionProps = { submission: HealthPlanFormDataType navigateTo?: string - documentDateLookupTable?: DocumentDateLookupTable + documentDateLookupTable: DocumentDateLookupTableType isCMSUser?: boolean submissionName: string } @@ -168,7 +168,11 @@ export const ContractDetailsSummarySection = ({ )} @@ -184,7 +188,11 @@ export const ContractDetailsSummarySection = ({ { it('can render draft submission without errors', () => { renderWithProviders( { it('can render state submission without errors', () => { renderWithProviders( { it('can render all rate details fields for amendment to prior rate certification submission', () => { renderWithProviders( { const statePrograms = mockMNState().programs renderWithProviders( { renderWithProviders( { 'MCR-MN-0005-SNBC-RATE-20221014-20221014-CERTIFICATION-20221014' renderWithProviders( { } renderWithProviders( { it('does not render supporting rate documents when they do not exist', () => { renderWithProviders( { it('does not render download all button when on previous submission', () => { renderWithProviders( { it('renders rate cell capitation type', () => { renderWithProviders( { draftSubmission.rateInfos[0].rateCapitationType = 'RATE_RANGE' renderWithProviders( { ] renderWithProviders( { ] renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { draftSubmission.rateInfos = mockRateInfos renderWithProviders( { ] renderWithProviders( { ] renderWithProviders( { } renderWithProviders( { it('renders submitted package without errors', () => { renderWithProviders( ) : ( 'LOADING...' @@ -357,7 +357,6 @@ export const RateDetailsSummarySection = ({ documentDateLookupTable={ documentDateLookupTable } - isCMSUser={isCMSUser} caption="Rate supporting documents" isSupportingDocuments documentCategory="Rate-supporting" @@ -378,7 +377,6 @@ export const RateDetailsSummarySection = ({ { + const emptyDocumentsTable = () => { + return { previousSubmissionDate: '01/01/01' } + } it('renders documents without errors', async () => { const testDocuments = [ { @@ -17,6 +22,7 @@ describe('UploadedDocumentsTable', () => { ] renderWithProviders( { renderWithProviders( { ) renderWithProviders( { ).toBeNull() }) }) - it('renders date added when supplied with a date lookup table', async () => { - const testDocuments = [ + it('renders date added when supplied with a date lookup table and is CMS user viewing submission', async () => { + const testDocuments: SubmissionDocument[] = [ { s3URL: 's3://foo/bar/test-1', name: 'supporting docs test 1', @@ -167,7 +175,67 @@ describe('UploadedDocumentsTable', () => { ], }, ] - const dateLookupTable = { + const dateLookupTable: DocumentDateLookupTableType = { + 's3://foo/bar/test-1': + 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', + 's3://foo/bar/test-2': + 'Sat Mar 26 2022 16:13:20 GMT-0500 (Central Daylight Time)', + 's3://foo/bar/test-3': + 'Sun Mar 27 2022 16:13:20 GMT-0500 (Central Daylight Time)', + previousSubmissionDate: + 'Sun Mar 26 2022 16:13:20 GMT-0500 (Central Daylight Time)', + } + renderWithProviders( + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + ], + }, + } + ) + await waitFor(() => { + const rows = screen.getAllByRole('row') + expect(rows).toHaveLength(4) + expect(rows[0]).toHaveTextContent('Date added') + expect(rows[1]).toHaveTextContent('3/25/22') + expect(rows[2]).toHaveTextContent('3/26/22') + expect(rows[3]).toHaveTextContent('3/27/22') + }) + }) + + it('renders date added when supplied with a date lookup table and is State user', async () => { + const testDocuments: SubmissionDocument[] = [ + { + s3URL: 's3://foo/bar/test-1', + name: 'supporting docs test 1', + documentCategories: ['CONTRACT_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-2', + name: 'supporting docs test 2', + documentCategories: ['RATES_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-3', + name: 'supporting docs test 3', + documentCategories: [ + 'CONTRACT_RELATED' as const, + 'RATES_RELATED' as const, + ], + }, + ] + const dateLookupTable: DocumentDateLookupTableType = { 's3://foo/bar/test-1': 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', 's3://foo/bar/test-2': @@ -184,7 +252,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={true} />, { apolloProvider: { @@ -206,8 +273,69 @@ describe('UploadedDocumentsTable', () => { expect(rows[3]).toHaveTextContent('3/27/22') }) }) + + it('does not render a date added when supplied with empty date lookup table (this happens with new draft submissions)', async () => { + const testDocuments: SubmissionDocument[] = [ + { + s3URL: 's3://foo/bar/test-1', + name: 'supporting docs test 1', + documentCategories: ['CONTRACT_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-2', + name: 'supporting docs test 2', + documentCategories: ['RATES_RELATED' as const], + }, + { + s3URL: 's3://foo/bar/test-3', + name: 'supporting docs test 3', + documentCategories: [ + 'CONTRACT_RELATED' as const, + 'RATES_RELATED' as const, + ], + }, + ] + const dateLookupTable: DocumentDateLookupTableType = { + previousSubmissionDate: null, + } + renderWithProviders( + , + { + apolloProvider: { + mocks: [ + fetchCurrentUserMock({ + user: mockValidCMSUser(), + statusCode: 200, + }), + ], + }, + } + ) + await waitFor(() => { + expect(screen.getByRole('table')).toBeInTheDocument() + // we have a table with rows for each document + const rows = screen.getAllByRole('row') + rows.shift() // get ride of column header row + expect(rows).toHaveLength(testDocuments.length) + + // There is a screenreader only "N/A" for each row + rows.forEach((row) => { + expect(within(row).getByText('N/A')).toBeInTheDocument() + expect(within(row).getByText('N/A')).toHaveAttribute( + 'class', + 'srOnly' + ) + }) + }) + }) it('shows the NEW tag when a document is submitted after the last submission', async () => { - const testDocuments = [ + const testDocuments: SubmissionDocument[] = [ { s3URL: 's3://foo/bar/test-1', name: 'supporting docs test 1', @@ -227,7 +355,7 @@ describe('UploadedDocumentsTable', () => { ], }, ] - const dateLookupTable = { + const dateLookupTable: DocumentDateLookupTableType = { 's3://foo/bar/test-1': 'Fri Mar 25 2022 16:13:20 GMT-0500 (Central Daylight Time)', 's3://foo/bar/test-2': @@ -244,7 +372,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={true} />, { apolloProvider: { @@ -299,7 +426,6 @@ describe('UploadedDocumentsTable', () => { documentCategory="Contract-supporting" isSupportingDocuments documentDateLookupTable={dateLookupTable} - isCMSUser={false} />, { apolloProvider: { diff --git a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx index 9dfa9ab29a..4ab2f3de31 100644 --- a/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx +++ b/services/app-web/src/components/SubmissionSummarySection/UploadedDocumentsTable/UploadedDocumentsTable.tsx @@ -3,96 +3,72 @@ import { Link } from '@trussworks/react-uswds' import { NavLink } from 'react-router-dom' import dayjs from 'dayjs' import { SubmissionDocument } from '../../../common-code/healthPlanFormDataType' -import { DocumentDateLookupTable } from '../../../pages/SubmissionSummary/SubmissionSummary' import styles from './UploadedDocumentsTable.module.scss' import { usePreviousSubmission } from '../../../hooks' import { SharedRateCertDisplay } from '../../../common-code/healthPlanFormDataType/UnlockedHealthPlanFormDataType' import { DocumentTag } from './DocumentTag' import { useDocument } from '../../../hooks/useDocument' -import { getDocumentKey } from '../../../documentHelpers/getDocumentKey' +import { DocumentDateLookupTableType } from '../../../documentHelpers/makeDocumentDateLookupTable' +import { getDocumentKey } from '../../../documentHelpers' +import { useAuth } from '../../../contexts/AuthContext' + export type UploadedDocumentsTableProps = { documents: SubmissionDocument[] caption: string | null - documentDateLookupTable?: DocumentDateLookupTable packagesWithSharedRateCerts?: SharedRateCertDisplay[] + documentDateLookupTable: DocumentDateLookupTableType isSupportingDocuments?: boolean documentCategory?: string // if this prop is not included, do not show category column - isEditing?: boolean - isCMSUser?: boolean -} - -type DocumentWithLink = { url: string | null } & SubmissionDocument - -const isBothContractAndRateSupporting = (doc: SubmissionDocument) => - doc.documentCategories.includes('CONTRACT_RELATED') && - doc.documentCategories.includes('RATES_RELATED') - -type LinkedPackagesListProps = { - unlinkDrafts: boolean - packages: SharedRateCertDisplay[] -} - -const linkedPackagesList = ({ - unlinkDrafts, - packages, -}: LinkedPackagesListProps): React.ReactElement[] => { - return packages.map((item, index) => { - const maybeComma = index > 0 ? ', ' : '' - const linkedPackageIsDraft = - item.packageName && item.packageName.includes('(Draft)') - - if (linkedPackageIsDraft && unlinkDrafts) { - return ( - - {maybeComma} - {item.packageName} - - ) - } else { - return ( - - {maybeComma} - - {item.packageName} - - - ) - } - }) + isEditing?: boolean // by default assume we are on summary page, if true, assume review and submit page } export const UploadedDocumentsTable = ({ documents, caption, documentCategory, - documentDateLookupTable, packagesWithSharedRateCerts, isSupportingDocuments = false, + documentDateLookupTable, isEditing = false, - isCMSUser, }: UploadedDocumentsTableProps): React.ReactElement => { const initialDocState = documents.map((doc) => ({ ...doc, url: null, + s3Key: null, })) - const { getDocumentsUrl } = useDocument() + const { loggedInUser } = useAuth() + const isCMSUser = loggedInUser?.__typename === 'CMSUser' + const { getDocumentsWithS3KeyAndUrl } = useDocument() const [refreshedDocs, setRefreshedDocs] = - useState(initialDocState) + useState(initialDocState) const shouldShowEditButton = isEditing && isSupportingDocuments const shouldShowAsteriskExplainer = refreshedDocs.some((doc) => isBothContractAndRateSupporting(doc) ) - const shouldHaveNewTag = (doc: SubmissionDocument) => { - const documentKey = getDocumentKey(doc) - return ( - isCMSUser && - documentDateLookupTable && - documentDateLookupTable[documentKey] > - documentDateLookupTable.previousSubmissionDate - ) + // canDisplayDateAddedForDocument - guards against passing in null or undefined to dayjs + // dates will be undefined in lookup table we are dealing with a new initial submission + const canDisplayDateAddedForDocument = (doc: DocumentWithS3Data) => { + const documentLookupKey = getDocumentKey(doc) + return documentLookupKey && documentDateLookupTable[documentLookupKey] + } + + const shouldHaveNewTag = (doc: DocumentWithS3Data) => { + const documentLookupKey = getDocumentKey(doc) + if (!isCMSUser) { + return false // design requirement, don't show new tag to state users on review submit + } + + if (!documentDateLookupTable || !doc || !doc.s3Key) { + return false // this is a document with bad s3 data + } + const documentDate = documentDateLookupTable?.[documentLookupKey] + const previousSubmissionDate = + documentDateLookupTable.previousSubmissionDate + + if (!documentDate || !previousSubmissionDate) { + return false // this document is on an initial submission or not submitted yet + } + return documentDate > previousSubmissionDate } const hasSharedRateCert = @@ -124,17 +100,18 @@ export const UploadedDocumentsTable = ({ useEffect(() => { const refreshDocuments = async () => { - const newDocuments = (await getDocumentsUrl( + const newDocuments = (await getDocumentsWithS3KeyAndUrl( documents, 'HEALTH_PLAN_DOCS' - )) as DocumentWithLink[] + )) as DocumentWithS3Data[] if (newDocuments.length) { setRefreshedDocs(newDocuments) } } void refreshDocuments() - }, [documents, getDocumentsUrl]) + }, [documents, getDocumentsWithS3KeyAndUrl]) + // Empty State if (refreshedDocs.length === 0) { return ( @@ -175,7 +152,7 @@ export const UploadedDocumentsTable = ({ {refreshedDocs.map((doc) => ( - {doc.url ? ( + {doc.url && doc.s3Key ? ( )} - {documentDateLookupTable - ? dayjs( - documentDateLookupTable[ - getDocumentKey(doc) - ] - ).format('M/D/YY') - : ''} + {canDisplayDateAddedForDocument(doc) ? ( + dayjs( + documentDateLookupTable[ + getDocumentKey(doc) + ] + ).format('M/D/YY') + ) : ( + N/A + )} {documentCategory && {documentCategory}} {showSharedInfo @@ -234,3 +213,50 @@ export const UploadedDocumentsTable = ({ ) } + +// TODO - get the api to return documents in this state rather than frontend generating on demand +type DocumentWithS3Data = { + url: string | null + s3Key: string | null +} & SubmissionDocument + +const isBothContractAndRateSupporting = (doc: SubmissionDocument) => + doc.documentCategories.includes('CONTRACT_RELATED') && + doc.documentCategories.includes('RATES_RELATED') + +type LinkedPackagesListProps = { + unlinkDrafts: boolean + packages: SharedRateCertDisplay[] +} + +const linkedPackagesList = ({ + unlinkDrafts, + packages, +}: LinkedPackagesListProps): React.ReactElement[] => { + return packages.map((item, index) => { + const maybeComma = index > 0 ? ', ' : '' + const linkedPackageIsDraft = + item.packageName && item.packageName.includes('(Draft)') + + if (linkedPackageIsDraft && unlinkDrafts) { + return ( + + {maybeComma} + {item.packageName} + + ) + } else { + return ( + + {maybeComma} + + {item.packageName} + + + ) + } + }) +} diff --git a/services/app-web/src/documentHelpers/getAllDocuments.ts b/services/app-web/src/documentHelpers/getAllDocuments.ts new file mode 100644 index 0000000000..cb7caefc43 --- /dev/null +++ b/services/app-web/src/documentHelpers/getAllDocuments.ts @@ -0,0 +1,14 @@ +import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' + +const getAllDocuments = (pkg: HealthPlanFormDataType) => { + let allDocuments = [...pkg.contractDocuments, ...pkg.documents] + if (pkg.rateInfos.length > 0) { + pkg.rateInfos.forEach((rateInfo) => { + allDocuments = allDocuments.concat(rateInfo.rateDocuments) + allDocuments = allDocuments.concat(rateInfo.supportingDocuments) + }) + } + return allDocuments +} + +export { getAllDocuments } diff --git a/services/app-web/src/documentHelpers/getDocumentKey.ts b/services/app-web/src/documentHelpers/getDocumentKey.ts index 04a4836505..5284ed803b 100644 --- a/services/app-web/src/documentHelpers/getDocumentKey.ts +++ b/services/app-web/src/documentHelpers/getDocumentKey.ts @@ -1,5 +1,9 @@ import { SubmissionDocument } from '../common-code/healthPlanFormDataType' -export const getDocumentKey = (doc: SubmissionDocument): string => { +// getDocumentKey - Returns a unique identifier for the document. This should be the document sha. +// Not to be confused with "s3Key" which is a different field / use case +const getDocumentKey = (doc: SubmissionDocument): string => { return doc.sha256 ? doc.sha256 : doc.s3URL } + +export { getDocumentKey } diff --git a/services/app-web/src/documentHelpers/index.ts b/services/app-web/src/documentHelpers/index.ts new file mode 100644 index 0000000000..cf7dde5e8a --- /dev/null +++ b/services/app-web/src/documentHelpers/index.ts @@ -0,0 +1,3 @@ +export { makeDocumentDateTable } from './makeDocumentDateLookupTable' +export { makeDocumentS3KeyLookup } from './makeDocumentKeyLookupList' +export { getDocumentKey } from './getDocumentKey' diff --git a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts index 7d771376cf..396b30b040 100644 --- a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts +++ b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.test.ts @@ -1,56 +1,75 @@ -import { makeDateTable } from './makeDocumentDateLookupTable' -import { mockSubmittedHealthPlanPackageWithRevision } from '../testHelpers/apolloMocks' +import { makeDocumentDateTable } from './makeDocumentDateLookupTable' +import { + mockDraftHealthPlanPackage, + mockSubmittedHealthPlanPackageWithRevision, +} from '../testHelpers/apolloMocks' import { UnlockedHealthPlanFormDataType } from '../common-code/healthPlanFormDataType' import { TextEncoder, TextDecoder } from 'util' +import { buildRevisionsLookup } from '../gqlHelpers/fetchHealthPlanPackageWrapper' Object.assign(global, { TextDecoder, TextEncoder }) -describe('makeDateTable', () => { +describe('makeDocumentDateTable', () => { it('should make a proper lookup table', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ - currentSubmissionData: { + const submission = mockSubmittedHealthPlanPackageWithRevision({ + currentSubmitInfo: { updatedAt: new Date('2022-03-28T17:56:32.953Z'), }, - previousSubmissionData: { + previousSubmitInfo: { updatedAt: new Date('2022-03-25T21:14:43.057Z'), }, - initialSubmissionData: { + initialSubmitInfo: { updatedAt: new Date('2022-03-25T21:13:20.420Z'), }, }) - const lookupTable = makeDateTable(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) + expect(lookupTable).toEqual({ + 's3://bucketname/1648242632157-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648490162641-lifeofgalileo.pdf/lifeofgalileo.pdf': + new Date('2022-03-28T17:56:32.953Z'), + 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf': + new Date('2022-03-25T21:13:20.420Z'), + 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf': + new Date('2022-03-25T21:13:20.420Z'), + previousSubmissionDate: new Date('2022-03-25T21:14:43.057Z'), + }) + }) + + it('should return no document dates for submission still in initial draft', () => { + const submission = mockDraftHealthPlanPackage() + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) - expect(JSON.stringify(lookupTable)).toEqual( - JSON.stringify({ - 's3://bucketname/1648242632157-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648490162641-lifeofgalileo.pdf/lifeofgalileo.pdf': - '2022-03-28T17:56:32.953Z', - 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf': - '2022-03-25T21:13:20.420Z', - 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf': - '2022-03-25T21:13:20.420Z', - previousSubmissionDate: '2022-03-25T21:14:43.057Z', - }) - ) + expect(lookupTable).toEqual({ + previousSubmissionDate: null, + }) }) - it('should use earliest document added date', () => { + + it('should use earliest document added date based that revisions submit date', () => { const docs: Partial = { documents: [ { - s3URL: 's3://bucketname/testDateDoc/testDateDoc.png', + s3URL: 's3://bucketname/testDateDoc/testDateDoc.pdf', name: 'Test Date Doc', documentCategories: ['CONTRACT_RELATED'], }, ], contractDocuments: [ { - s3URL: 's3://bucketname/key/foo.png', - name: 'contract doc', + s3URL: 's3://bucketname/key/replaced-contract.pdf', + name: 'replaced contract', documentCategories: ['CONTRACT'], }, ], @@ -63,28 +82,50 @@ describe('makeDateTable', () => { }, ], } - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: { ...docs, + }, + currentSubmitInfo: { updatedAt: new Date('2022-03-10T00:00:00.000Z'), }, previousSubmissionData: { ...docs, + }, + previousSubmitInfo: { updatedAt: new Date('2022-02-10T00:00:00.000Z'), }, initialSubmissionData: { ...docs, + contractDocuments: [ + { + s3URL: 's3://bucketname/key/original-contract.pdf', + name: 'original contract', + documentCategories: ['CONTRACT'], + }, + ], + }, + initialSubmitInfo: { updatedAt: new Date('2022-01-10T00:00:00.000Z'), }, }) - const lookupTable = makeDateTable(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentDateTable(revisionsLookup) expect(lookupTable).toEqual({ - 's3://bucketname/key/foo.png': new Date('2022-01-10T00:00:00.000Z'), - 's3://bucketname/testDateDoc/testDateDoc.png': new Date( + 's3://bucketname/key/original-contract.pdf': new Date( '2022-01-10T00:00:00.000Z' ), + 's3://bucketname/key/replaced-contract.pdf': new Date( + '2022-02-10T00:00:00.000Z' + ), + 's3://bucketname/testDateDoc/testDateDoc.pdf': new Date( + '2022-01-10T00:00:00.00' + ), previousSubmissionDate: new Date('2022-02-10T00:00:00.000Z'), }) }) diff --git a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts index 8109739861..49409e30ab 100644 --- a/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts +++ b/services/app-web/src/documentHelpers/makeDocumentDateLookupTable.ts @@ -1,62 +1,53 @@ -import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' -import { HealthPlanPackage } from '../gen/gqlClient' -import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' -import { DocumentDateLookupTable } from '../pages/SubmissionSummary/SubmissionSummary' -import { recordJSException } from '../otelHelpers/tracingHelper' +import { + ExpandedRevisionsType, + RevisionsLookupType, +} from '../gqlHelpers/fetchHealthPlanPackageWrapper' +import { getAllDocuments } from './getAllDocuments' import { getDocumentKey } from './getDocumentKey' -const DocBuckets = ['contractDocuments', 'rateDocuments', 'documents'] as const - -export function makeDateTableFromFormData( - formDatas: HealthPlanFormDataType[] -): DocumentDateLookupTable { - const lookupTable: DocumentDateLookupTable = {} +// DocumentDateLookupTableType - { document lookup key string : date string for "date added" } +type DocumentDateLookupTableType = { + previousSubmissionDate: string | null + [key: string]: string | null +} - formDatas.forEach((revisionData, index) => { - if (index === 1) { - // second most recent revision - lookupTable['previousSubmissionDate'] = revisionData.updatedAt - } - DocBuckets.forEach((bucket) => { - if (bucket === 'rateDocuments') { - revisionData.rateInfos.forEach((rateInfo) => { - rateInfo.rateDocuments.forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) - rateInfo.supportingDocuments.forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) - }) - } else { - revisionData[bucket].forEach((doc) => { - const documentKey = getDocumentKey(doc) - lookupTable[documentKey] = revisionData.updatedAt - }) +// getDateAdded - picks out the submit info updatedAt date for a revision +// value is undefined if document not yet submitted +const getDateAdded = ( + revisionData: ExpandedRevisionsType +): string | undefined => { + return revisionData.submitInfo?.updatedAt +} +// makeDateTable - generates unique document keys and their "date added" +// used for date added column on UploadedDocumentsTable displayed in SubmissionSummary and ReviewSubmit +// documents without a submitted date are excluded from list +// logic for unique document keys comes from getDocumentKey - This can be simplified once we have doc.sha everywhere +function makeDocumentDateTable( + revisionsLookup: RevisionsLookupType +): DocumentDateLookupTableType { + const lookupTable: DocumentDateLookupTableType = { + previousSubmissionDate: null, + } + Object.keys(revisionsLookup).forEach( + (revisionId: string, index: number) => { + const revision = revisionsLookup[revisionId] + if (index === 1) { + // second most recent revision + const previousSubmission = getDateAdded(revision) // used in UploadedDocumentsTable to determine if we should show NEW tag + if (previousSubmission) + lookupTable['previousSubmissionDate'] = previousSubmission } - }) - }) + const allDocuments = getAllDocuments(revision.formData) + allDocuments.forEach((doc) => { + const documentKey = getDocumentKey(doc) + const dateAdded = getDateAdded(revision) + if (dateAdded) lookupTable[documentKey] = dateAdded + }) + } + ) return lookupTable } -export const makeDateTable = ( - submissions: HealthPlanPackage -): DocumentDateLookupTable | undefined => { - const formDatas: HealthPlanFormDataType[] = [] - for (const revision of submissions.revisions) { - const revisionData = base64ToDomain(revision.node.formDataProto) - - if (revisionData instanceof Error) { - recordJSException( - `makeDocumentLookupTable: failed to read submission data; unable to display document dates. ID: ${submissions.id} Error message: ${revisionData.message}` - ) - // We are just ignoring this error in this code path. - } else { - formDatas.push(revisionData) - } - } - - return makeDateTableFromFormData(formDatas) -} +export { makeDocumentDateTable } +export type { DocumentDateLookupTableType } diff --git a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts index fc56e398ca..8f56015146 100644 --- a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts +++ b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.test.ts @@ -1,8 +1,9 @@ -import { makeDocumentList } from './makeDocumentKeyLookupList' +import { makeDocumentS3KeyLookup } from './makeDocumentKeyLookupList' import { mockSubmittedHealthPlanPackageWithRevision } from '../testHelpers/apolloMocks' import { UnlockedHealthPlanFormDataType } from '../common-code/healthPlanFormDataType' +import { buildRevisionsLookup } from '../gqlHelpers/fetchHealthPlanPackageWrapper' -describe('makeDocumentList', () => { +describe('makeDocumentS3KeyLookup', () => { const noSubmissionDocuments: Partial = { contractDocuments: [], rateInfos: [ @@ -17,47 +18,45 @@ describe('makeDocumentList', () => { } it('should make two lists with document s3 keys', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({}) - const lookupTable = makeDocumentList(submissions) - - expect(lookupTable).toEqual({ - currentDocuments: [ + const submission = mockSubmittedHealthPlanPackageWithRevision({}) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) + expect(lookupTable.currentDocuments.sort()).toEqual( + [ '1648242632157-Amerigroup Texas, Inc.pdf', '1648490162641-lifeofgalileo.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - ], - previousDocuments: [ - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', + ].sort() + ) + + expect(lookupTable.previousDocuments.sort()).toEqual( + [ '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', '1648242632157-Amerigroup Texas, Inc.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - ], - }) + ].sort() + ) }) + it('should return empty arrays for no documents in submission', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: noSubmissionDocuments, previousSubmissionData: noSubmissionDocuments, initialSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) expect(lookupTable).toEqual({ currentDocuments: [], @@ -65,66 +64,50 @@ describe('makeDocumentList', () => { }) }) - it('should return empty array for currentDocuments', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + it('should return empty array for currentDocuments when none exist', () => { + const submission = mockSubmittedHealthPlanPackageWithRevision({ currentSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) - expect(lookupTable).toEqual({ - currentDocuments: [], - previousDocuments: [ - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - '1648242632157-Amerigroup Texas, Inc.pdf', - '1648242665634-Amerigroup Texas, Inc.pdf', - '1648242711421-Amerigroup Texas Inc copy.pdf', - '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', + expect(lookupTable.currentDocuments).toEqual([]) + expect(lookupTable.previousDocuments.sort()).toEqual( + [ '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', '1648242632157-Amerigroup Texas, Inc.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', - ], - }) + ].sort() + ) }) - it('should return empty array for previousDocuments', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({ + it('should return empty array for previousDocuments when none exist', () => { + const submission = mockSubmittedHealthPlanPackageWithRevision({ previousSubmissionData: noSubmissionDocuments, initialSubmissionData: noSubmissionDocuments, }) - const lookupTable = makeDocumentList(submissions) - expect(lookupTable).toEqual({ - currentDocuments: [ + const revisionsLookup = buildRevisionsLookup(submission) + if (revisionsLookup instanceof Error) { + throw revisionsLookup + } + const lookupTable = makeDocumentS3KeyLookup(revisionsLookup) + + expect(lookupTable.previousDocuments).toEqual([]) + expect(lookupTable.currentDocuments.sort()).toEqual( + [ '1648242632157-Amerigroup Texas, Inc.pdf', '1648490162641-lifeofgalileo.pdf', '1648242665634-Amerigroup Texas, Inc.pdf', '1648242711421-Amerigroup Texas Inc copy.pdf', '1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf', '1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - ], - previousDocuments: [], - }) - }) - - it('should return error if any revisions does not decode', () => { - const submissions = mockSubmittedHealthPlanPackageWithRevision({}) - submissions.revisions[1].node.formDataProto = 'Should return an error' - const lookupTable = makeDocumentList(submissions) - - expect(lookupTable).toEqual( - new Error( - 'Failed to read submission data; unable to display documents' - ) + ].sort() ) }) }) diff --git a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts index 953efd16a9..1cdb36e10e 100644 --- a/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts +++ b/services/app-web/src/documentHelpers/makeDocumentKeyLookupList.ts @@ -1,9 +1,10 @@ -import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' -import { HealthPlanPackage } from '../gen/gqlClient' import { parseKey } from '../common-code/s3URLEncoding' -import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' +import { RevisionsLookupType } from '../gqlHelpers/fetchHealthPlanPackageWrapper' +import { getAllDocuments } from './getAllDocuments' -export type LookupListType = { +// CurrentPreviousDocsLookup - array of document keys for currentDocuments and previousDocuments + +type DocumentKeyLookupType = { currentDocuments: string[] previousDocuments: string[] } @@ -13,64 +14,51 @@ const getKey = (s3URL: string) => { return key instanceof Error ? null : key } -export function makeDocumentListFromFormDatas( - formDatas: HealthPlanFormDataType[] -): LookupListType { - const docBuckets = [ - 'contractDocuments', - 'rateDocuments', - 'documents', - ] as const - const lookupList: LookupListType = { - currentDocuments: [], - previousDocuments: [], - } +// makeDocumentS3KeyLookup - generates list of currentDocuments and previousDocuments by s3Key +// this is used to to determine if can delete a document from s3 after it removed from the FileUpload UI on the documents pages - ContractDetails, RateDetails, Documents + +function makeDocumentS3KeyLookup( + revisionsLookup: RevisionsLookupType +): DocumentKeyLookupType { + const currentDocumentsSet = new Set() + const previousDocumentsSet = new Set() - for (let index = 0; index < formDatas.length; index++) { - const revisionData = formDatas[index] - docBuckets.forEach((bucket) => { - if (bucket === 'rateDocuments') { - revisionData.rateInfos.forEach((rateInfo) => { - rateInfo[bucket].forEach((doc) => { - const key = getKey(doc.s3URL) - if (key && index === 0) { - lookupList.currentDocuments.push(key) - } else if (key && index > 0) { - lookupList.previousDocuments.push(key) - } - }) + Object.keys(revisionsLookup).forEach( + (revisionId: string, index: number) => { + const revisionData = revisionsLookup[revisionId].formData + const allDocuments = getAllDocuments(revisionData) + if (index === 0) { + allDocuments.forEach((doc) => { + const s3Key = getKey(doc.s3URL) + if (!s3Key) { + console.error( + `makeDocumentS3KeyLookup- Failed to read S3 key for $${doc.name} - ${doc.s3URL}` + ) + // fail silently, just return good documents + } else { + currentDocumentsSet.add(s3Key) + } }) } else { - revisionData[bucket].forEach((doc) => { - const key = getKey(doc.s3URL) - if (key && index === 0) { - lookupList.currentDocuments.push(key) - } else if (key && index > 0) { - lookupList.previousDocuments.push(key) + allDocuments.forEach((doc) => { + const s3Key = getKey(doc.s3URL) + if (!s3Key) { + console.error( + `makeDocumentS3KeyLookup - Failed to read S3 key for $${doc.name} - ${doc.s3URL}` + ) + // fail silently, just return good documents + } else { + previousDocumentsSet.add(s3Key) } }) } - }) - } - - return lookupList -} - -export const makeDocumentList = ( - submissions: HealthPlanPackage -): LookupListType | Error => { - const revisions = submissions.revisions - - const formDatas: HealthPlanFormDataType[] = [] - for (let index = 0; index < revisions.length; index++) { - const revisionData = base64ToDomain(revisions[index].node.formDataProto) - if (revisionData instanceof Error) { - return new Error( - 'Failed to read submission data; unable to display documents' - ) } - formDatas.push(revisionData) + ) + return { + currentDocuments: [...currentDocumentsSet], + previousDocuments: [...previousDocumentsSet], } - - return makeDocumentListFromFormDatas(formDatas) } + +export { makeDocumentS3KeyLookup } +export type {DocumentKeyLookupType} diff --git a/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts b/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts index 008b390424..1c46cc14f1 100644 --- a/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts +++ b/services/app-web/src/gqlHelpers/fetchHealthPlanPackageWrapper.ts @@ -2,6 +2,8 @@ import { useFetchHealthPlanPackageQuery, FetchHealthPlanPackageQuery, useFetchHealthPlanPackageWithQuestionsQuery, + HealthPlanRevision, + HealthPlanPackage, } from '../gen/gqlClient' import { HealthPlanFormDataType } from '../common-code/healthPlanFormDataType' import { base64ToDomain } from '../common-code/proto/healthPlanFormDataProto' @@ -11,20 +13,24 @@ import { QuerySuccessType, WrappedApolloResultType, } from './apolloQueryWrapper' -import { DocumentDateLookupTable } from '../pages/SubmissionSummary/SubmissionSummary' -import { makeDateTableFromFormData } from '../documentHelpers/makeDocumentDateLookupTable' -import { - LookupListType, - makeDocumentListFromFormDatas, -} from '../documentHelpers/makeDocumentKeyLookupList' import { QueryFunctionOptions } from '@apollo/client' +import { recordJSException } from '../otelHelpers' +import { + DocumentDateLookupTableType, + makeDocumentDateTable, +} from '../documentHelpers/makeDocumentDateLookupTable' + +// ExpandedRevisionsType - HPP revision plus an additional formData field containing values of formDataProto decoded into typescript +type ExpandedRevisionsType = HealthPlanRevision & { + formData: HealthPlanFormDataType +} -// We return a slightly modified version of the wrapped result adding formDatas +type RevisionsLookupType = { [revisionID: string]: ExpandedRevisionsType } +// We return a slightly modified version of the wrapped result adding RevisionsLookup, documentDateLookup, etc // all of these fields will be added to the SUCCESS type type AdditionalParsedDataType = { - formDatas: { [revisionID: string]: HealthPlanFormDataType } - documentDates: DocumentDateLookupTable - documentLists: LookupListType + revisionsLookup: RevisionsLookupType + documentDates: DocumentDateLookupTableType } type ParsedFetchResultType = ApolloResultType< @@ -42,6 +48,33 @@ type WrappedFetchResultWithQuestionsType = WrappedApolloResultType< AdditionalParsedDataType > +// Take health plan package directly from API, decodes all fields and return expanded revisions list. +const buildRevisionsLookup = ( + pkg: HealthPlanPackage +): RevisionsLookupType | Error => { + const expandedRevisions: RevisionsLookupType = {} + for (const revision of pkg.revisions) { + const revisionDecodedFormData = base64ToDomain( + revision.node.formDataProto + ) + + if (revisionDecodedFormData instanceof Error) { + const err = + new Error(`buildRevisionsLookup: proto decoding error. pkg ID: ${pkg.id} revision ID: + ${revision.node.id}. Error message: ${revisionDecodedFormData}`) + recordJSException(err) + return err + } else { + expandedRevisions[revision.node.id] = { + ...revision.node, + formData: revisionDecodedFormData, + } + } + } + + return expandedRevisions +} + function parseProtos( result: QuerySuccessType ): ParsedFetchResultType { @@ -50,12 +83,8 @@ function parseProtos( if (!pkg) { return { ...result, - formDatas: {}, - documentDates: {}, - documentLists: { - currentDocuments: [], - previousDocuments: [], - }, + revisionsLookup: {}, + documentDates: { previousSubmissionDate: null }, } } @@ -70,36 +99,20 @@ function parseProtos( } } - const formDatas: { [revisionID: string]: HealthPlanFormDataType } = {} - for (const revisionEdge of pkg.revisions) { - const revision = revisionEdge.node - const formDataResult = base64ToDomain(revision.formDataProto) - - if (formDataResult instanceof Error) { - const err = - new Error(`useFetchHealthPlanPackageWrapper: proto decoding error. ID: - ${pkg.id}. Error message: ${formDataResult}`) - console.error('Error decoding revision', revision, err) - return { - status: 'ERROR', - error: formDataResult, - } + const revisionsLookup = buildRevisionsLookup(pkg) + if (revisionsLookup instanceof Error) { + return { + status: 'ERROR', + error: revisionsLookup, } + } else { + const documentDates = makeDocumentDateTable(revisionsLookup) - formDatas[revision.id] = formDataResult - } - - const formDatasInOrder = pkg.revisions.map((rEdge) => { - return formDatas[rEdge.node.id] - }) - const documentDates = makeDateTableFromFormData(formDatasInOrder) - const documentLists = makeDocumentListFromFormDatas(formDatasInOrder) - - return { - ...result, - formDatas, - documentDates, - documentLists, + return { + ...result, + revisionsLookup, + documentDates, + } } } @@ -167,4 +180,7 @@ function useFetchHealthPlanPackageWithQuestionsWrapper( export { useFetchHealthPlanPackageWrapper, useFetchHealthPlanPackageWithQuestionsWrapper, + buildRevisionsLookup, } + +export type { ExpandedRevisionsType, RevisionsLookupType } diff --git a/services/app-web/src/hooks/useDocument.tsx b/services/app-web/src/hooks/useDocument.tsx index 8c18242edc..52ef044f72 100644 --- a/services/app-web/src/hooks/useDocument.tsx +++ b/services/app-web/src/hooks/useDocument.tsx @@ -11,24 +11,26 @@ type ParsedDocumentWithLinkType = const useDocument = () => { const { getKey, getURL } = useS3() - - const getDocumentsUrl = useCallback( + const getDocumentsWithS3KeyAndUrl= useCallback( async ,>( documents: Array, bucket: BucketShortName ): Promise> => { + return await Promise.all( documents.map(async (doc) => { const key = getKey(doc.s3URL) if (!key) return { ...doc, + s3Key: null, url: null, } const documentLink = await getURL(key, bucket) return { ...doc, + s3Key: key, url: documentLink, } }) @@ -40,7 +42,8 @@ const useDocument = () => { [getURL, getKey] ) - return { getDocumentsUrl } + + return { getDocumentsWithS3KeyAndUrl } } export { useDocument } diff --git a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx index cdae871ad7..261b50ace4 100644 --- a/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx +++ b/services/app-web/src/pages/QuestionResponse/QATable/QATable.tsx @@ -45,7 +45,7 @@ export const QATable = ({ round: number user: User }) => { - const { getDocumentsUrl } = useDocument() + const { getDocumentsWithS3KeyAndUrl } = useDocument() const tableDocuments = [ ...question.documents.map((doc) => ({ ...doc, @@ -84,7 +84,7 @@ export const QATable = ({ useDeepCompareEffect(() => { const refreshDocuments = async () => { - const newDocuments = await getDocumentsUrl( + const newDocuments = await getDocumentsWithS3KeyAndUrl( tableDocuments, 'QUESTION_ANSWER_DOCS' ) @@ -94,7 +94,7 @@ export const QATable = ({ } void refreshDocuments() - }, [tableDocuments, getDocumentsUrl, setRefreshedDocs]) + }, [tableDocuments, getDocumentsWithS3KeyAndUrl, setRefreshedDocs]) return ( <> diff --git a/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx b/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx index 2bee86db31..10e1be427c 100644 --- a/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx +++ b/services/app-web/src/pages/StateSubmission/ReviewSubmit/ReviewSubmit.test.tsx @@ -10,6 +10,7 @@ describe('ReviewSubmit', () => { it('renders without errors', async () => { renderWithProviders( { it('displays edit buttons for every section', async () => { renderWithProviders( { it('does not display zip download buttons', async () => { renderWithProviders( { it('renders info from a DraftSubmission', async () => { renderWithProviders( { it('displays back and save as draft buttons', async () => { renderWithProviders( { it('displays submit button', async () => { renderWithProviders( { diff --git a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx index 478f1c4330..5eb8b859da 100644 --- a/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx +++ b/services/app-web/src/pages/StateSubmission/StateSubmissionForm.tsx @@ -44,6 +44,10 @@ import { recordJSException } from '../../otelHelpers/tracingHelper' import { useStatePrograms } from '../../hooks/useStatePrograms' import { ApolloError } from '@apollo/client' import { handleApolloError } from '../../gqlHelpers/apolloErrors' +import { + makeDocumentS3KeyLookup, + makeDocumentDateTable, +} from '../../documentHelpers' const getRelativePathFromNestedRoute = (formRouteType: RouteT): string => getRelativePath({ @@ -98,9 +102,9 @@ const activeFormPages = ( ) } -/* +/* Prep work for refactor of form pages. This should be pulled out into a HealthPlanFormPageContext or HOC. - We have several instances of shared state across pages. + We have several instances of shared state across pages. */ export type HealthPlanFormPageProps = { @@ -196,7 +200,7 @@ export const StateSubmissionForm = (): React.ReactElement => { return // api failure or protobuf decode failure } - const { data, formDatas, documentDates, documentLists } = fetchResult + const { data, revisionsLookup } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // fetchHPP returns null if no package is found with the given ID @@ -204,9 +208,12 @@ export const StateSubmissionForm = (): React.ReactElement => { return } - // pull out the latest revision for editing + // pull out the latest revision and document lookups const latestRevision = pkg.revisions[0].node - const formDataFromLatestRevision = formDatas[latestRevision.id] + const formDataFromLatestRevision = + revisionsLookup[latestRevision.id].formData + const documentDates = makeDocumentDateTable(revisionsLookup) + const documentLists = makeDocumentS3KeyLookup(revisionsLookup) // if we've gotten back a submitted revision, it can't be edited if (formDataFromLatestRevision.status !== 'DRAFT') { @@ -311,9 +318,9 @@ export const StateSubmissionForm = (): React.ReactElement => { element={ } /> diff --git a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx index 056fa36ce7..cf1cb7607c 100644 --- a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.test.tsx @@ -84,7 +84,12 @@ describe('SubmissionRevisionSummary', () => { expect(rows).toHaveLength(2) expect(within(rows[0]).getByText('Date added')).toBeInTheDocument() expect( - within(rows[1]).getByText(dayjs(new Date()).format('M/D/YY')) + within(rows[1]).getByText( + dayjs( + mockSubmittedHealthPlanPackageWithRevisions() + .revisions[2]?.node?.submitInfo?.updatedAt + ).format('M/D/YY') + ) ).toBeInTheDocument() }) }) diff --git a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx index 3c59024a96..691e7a1645 100644 --- a/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx +++ b/services/app-web/src/pages/SubmissionRevisionSummary/SubmissionRevisionSummary.tsx @@ -60,7 +60,7 @@ export const SubmissionRevisionSummary = (): React.ReactElement => { return // api failure or protobuf decode failure } - const { data, formDatas, documentDates } = fetchResult + const { data, revisionsLookup, documentDates } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // fetchHPP returns null if no package is found with the given ID @@ -77,7 +77,7 @@ export const SubmissionRevisionSummary = (): React.ReactElement => { console.info('no revision found at index', revisionIndex) return } - const packageData = formDatas[revision.id] + const packageData = revisionsLookup[revision.id].formData const statePrograms = pkg.state.programs const name = packageName(packageData, statePrograms) diff --git a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx index 0cad8319bb..cbcd0eae30 100644 --- a/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx +++ b/services/app-web/src/pages/SubmissionSideNav/SubmissionSideNav.tsx @@ -28,14 +28,17 @@ import { } from '../../common-code/healthPlanFormDataType' import { useLDClient } from 'launchdarkly-react-client-sdk' import { featureFlags } from '../../common-code/featureFlags' -import { DocumentDateLookupTable } from '../SubmissionSummary/SubmissionSummary' +import { + DocumentDateLookupTableType, + makeDocumentDateTable, +} from '../../documentHelpers/makeDocumentDateLookupTable' export type SideNavOutletContextType = { pkg: HealthPlanPackage packageName: string currentRevision: HealthPlanRevision packageData: HealthPlanFormDataType - documentDates: DocumentDateLookupTable + documentDates: DocumentDateLookupTableType user: User } @@ -86,7 +89,7 @@ export const SubmissionSideNav = () => { ) } - const { data, formDatas, documentDates } = fetchResult + const { data, revisionsLookup } = fetchResult const pkg = data.fetchHealthPlanPackage.pkg // Display generic error page if getting logged in user returns undefined. @@ -98,7 +101,6 @@ export const SubmissionSideNav = () => { if (!pkg) { return } - const submissionStatus = pkg.status const isCMSUser = loggedInUser?.role === 'CMS_USER' @@ -130,9 +132,9 @@ export const SubmissionSideNav = () => { return } const currentRevision = edge.node - const packageData = formDatas[currentRevision.id] + const packageData = revisionsLookup[currentRevision.id].formData const pkgName = packageName(packageData, pkg.state.programs) - + const documentDates = makeDocumentDateTable(revisionsLookup) const outletContext: SideNavOutletContextType = { pkg, packageName: pkgName, diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx index 8b4cdfa5fb..a12b16471e 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.test.tsx @@ -367,13 +367,13 @@ describe('SubmissionSummary', () => { it('extracts the correct dates from the submission and displays them in tables', async () => { const submission = mockSubmittedHealthPlanPackageWithRevision({ - currentSubmissionData: { + currentSubmitInfo: { updatedAt: new Date('2022-05-12T21:13:20.420Z'), }, - previousSubmissionData: { + previousSubmitInfo: { updatedAt: new Date('2022-04-12T21:13:20.420Z'), }, - initialSubmissionData: { + initialSubmitInfo: { updatedAt: new Date('2022-03-12T21:13:20.420Z'), }, }) @@ -408,7 +408,7 @@ describe('SubmissionSummary', () => { ) await waitFor(() => { const rows = screen.getAllByRole('row') - expect(rows).toHaveLength(10) + expect(rows).toHaveLength(8) expect( within(rows[0]).getByText('Date added') ).toBeInTheDocument() @@ -523,7 +523,7 @@ describe('SubmissionSummary', () => { 'src/common-code/proto/healthPlanFormDataProto/testData/' ) .filter((f) => f.endsWith('.proto')) - /* as much as we'd like to loop over all the proto files here, looping and async tests, + /* as much as we'd like to loop over all the proto files here, looping and async tests, which this one is (document loading) produces inconsistent results. We have to copy/paste the test for each proto file. */ it('loads outdated health plan packages with old protos as expected - 0', async () => { @@ -582,10 +582,10 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('8/19/22') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract-supporting') expect(rows[3]).toHaveTextContent('rates cert 1') - expect(rows[3]).toHaveTextContent('8/19/22') + expect(rows[3]).toHaveTextContent('11/2/21') expect(rows[3]).toHaveTextContent('Rate certification') expect(rows[4]).toHaveTextContent('rates cert 2') expect(document.body).toHaveTextContent(/Submission type/) @@ -714,9 +714,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -742,9 +740,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -955,10 +951,10 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(document.body).toHaveTextContent(/Submission type/) @@ -1086,9 +1082,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1114,9 +1108,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -1322,17 +1314,17 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[2]).toHaveTextContent('Document') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(rows[5]).toHaveTextContent('rates cert 1') - expect(rows[5]).toHaveTextContent('5/13/21') + expect(rows[5]).toHaveTextContent('1/2/21') expect(rows[5]).toHaveTextContent('Rate certification') - expect(rows[6]).toHaveTextContent('5/13/21') + expect(rows[6]).toHaveTextContent('1/2/21') expect(document.body).toHaveTextContent(/Submission type/) expect( screen.getByRole('heading', { @@ -1458,9 +1450,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1486,9 +1476,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { @@ -1693,17 +1681,17 @@ describe('SubmissionSummary', () => { expect(rows[0]).toHaveTextContent('Document name') expect(rows[0]).toHaveTextContent('Document category') expect(rows[1]).toHaveTextContent('contract doc') - expect(rows[1]).toHaveTextContent('5/13/21') + expect(rows[1]).toHaveTextContent('1/2/21') expect(rows[1]).toHaveTextContent('Contract') expect(rows[2]).toHaveTextContent('Document') expect(rows[3]).toHaveTextContent('contract doc') - expect(rows[3]).toHaveTextContent('5/13/21') + expect(rows[3]).toHaveTextContent('1/2/21') expect(rows[3]).toHaveTextContent('Contract-supporting') expect(rows[4]).toHaveTextContent('Document') expect(rows[5]).toHaveTextContent('rates cert 1') - expect(rows[5]).toHaveTextContent('5/13/21') + expect(rows[5]).toHaveTextContent('1/2/21') expect(rows[5]).toHaveTextContent('Rate certification') - expect(rows[6]).toHaveTextContent('5/13/21') + expect(rows[6]).toHaveTextContent('1/2/21') expect(document.body).toHaveTextContent(/Submission type/) expect( screen.getByRole('heading', { @@ -1829,9 +1817,7 @@ describe('SubmissionSummary', () => { ) ).toBeInTheDocument() expect( - screen.getByText( - /Risk-sharing strategy/ - ) + screen.getByText(/Risk-sharing strategy/) ).toBeInTheDocument() expect( screen.getByText( @@ -1857,9 +1843,7 @@ describe('SubmissionSummary', () => { screen.getByText('Network adequacy standards') ).toBeInTheDocument() expect( - screen.getByText( - /Non-risk payment arrangements/ - ) + screen.getByText(/Non-risk payment arrangements/) ).toBeInTheDocument() expect( screen.getByRole('definition', { diff --git a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx index b84ad2ad4f..5069614102 100644 --- a/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx +++ b/services/app-web/src/pages/SubmissionSummary/SubmissionSummary.tsx @@ -28,10 +28,6 @@ import { useLDClient } from 'launchdarkly-react-client-sdk' import { featureFlags } from '../../common-code/featureFlags' import { SideNavOutletContextType } from '../SubmissionSideNav/SubmissionSideNav' -export type DocumentDateLookupTable = { - [key: string]: string -} - function UnlockModalButton({ disabled, modalRef, @@ -164,17 +160,17 @@ export const SubmissionSummary = (): React.ReactElement => { initiallySubmittedAt={pkg.initiallySubmittedAt} /> {isContractActionAndRateCertification && ( diff --git a/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts b/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts index 77910acc2d..60f2ec88b8 100644 --- a/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts +++ b/services/app-web/src/testHelpers/apolloMocks/healthPlanPackageGQLMock.ts @@ -18,6 +18,7 @@ import { UpdateHealthPlanFormDataMutation, CreateHealthPlanPackageDocument, CreateHealthPlanPackageMutation, + HealthPlanRevision, } from '../../gen/gqlClient' import { mockContractAndRatesDraft, @@ -125,12 +126,18 @@ const fetchStateHealthPlanPackageMockSuccess = ({ const mockSubmittedHealthPlanPackageWithRevision = ({ currentSubmissionData, + currentSubmitInfo, previousSubmissionData, + previousSubmitInfo, initialSubmissionData, + initialSubmitInfo, }: { currentSubmissionData?: Partial + currentSubmitInfo?: Partial previousSubmissionData?: Partial + previousSubmitInfo?: Partial initialSubmissionData?: Partial + initialSubmitInfo?: Partial }): HealthPlanPackage => { const currentFiles: Partial = { contractDocuments: [ @@ -151,15 +158,21 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ { s3URL: 's3://bucketname/1648242665634-Amerigroup Texas, Inc.pdf/Amerigroup Texas, Inc.pdf', name: 'Amerigroup Texas, Inc.pdf', - documentCategories: ['RATES_RELATED'], + documentCategories: ['RATES'], }, { s3URL: 's3://bucketname/1648242711421-Amerigroup Texas Inc copy.pdf/Amerigroup Texas Inc copy.pdf', name: 'Amerigroup Texas Inc copy.pdf', + documentCategories: ['RATES'], + }, + ], + supportingDocuments: [ + { + s3URL: 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', + name: 'covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', documentCategories: ['RATES_RELATED'], }, ], - supportingDocuments: [], actuaryContacts: [], packagesWithSharedRateCerts: [], }, @@ -170,11 +183,6 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', documentCategories: ['CONTRACT_RELATED'], }, - { - s3URL: 's3://bucketname/1648242873229-covid-ifc-2-flu-rsv-codes 5-5-2021.pdf/covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - name: 'covid-ifc-2-flu-rsv-codes 5-5-2021.pdf', - documentCategories: ['RATES_RELATED'], - }, ], } const previousFiles: Partial = { @@ -214,7 +222,13 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ documentCategories: ['RATES'], }, ], - supportingDocuments: [], + supportingDocuments: [ + { + s3URL: 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf', + name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', + documentCategories: ['RATES_RELATED'], + }, + ], actuaryContacts: [], packagesWithSharedRateCerts: [], }, @@ -230,11 +244,6 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ name: 'Amerigroup Texas Inc copy.pdf', documentCategories: ['CONTRACT_RELATED'], }, - { - s3URL: 's3://bucketname/1648242711421-529-10-0020-00003_Superior_Health Plan, Inc.pdf/529-10-0020-00003_Superior_Health Plan, Inc.pdf', - name: '529-10-0020-00003_Superior_Health Plan, Inc.pdf', - documentCategories: ['RATES_RELATED'], - }, ], } @@ -243,16 +252,36 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ ...currentFiles, ...currentSubmissionData, }) + const currentSubmit = { + updatedAt: '2022-03-28T17:56:32.952Z', + updatedBy: 'aang@example.com', + updatedReason: 'Placeholder resubmission reason', + ...currentSubmitInfo, + } const previousProto = domainToBase64({ ...unlockedWithALittleBitOfEverything(), ...previousFiles, ...previousSubmissionData, }) + + const previousSubmit = { + updatedAt: '2022-03-25T21:14:43.057Z', + updatedBy: 'aang@example.com', + updatedReason: 'Placeholder resubmission reason', + ...previousSubmitInfo, + } const initialProto = domainToBase64({ ...mockContractAndRatesDraft(), ...previousFiles, ...initialSubmissionData, }) + + const initialSubmit = { + updatedAt: '2022-03-25T21:13:20.419Z', + updatedBy: 'aang@example.com', + updatedReason: 'Initial submission', + ...initialSubmitInfo, + } return { __typename: 'HealthPlanPackage', id: '07f9efbf-d4d1-44ae-8674-56d9d6b75ce6', @@ -278,9 +307,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ }, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-28T17:56:32.952Z', - updatedBy: 'aang@example.com', - updatedReason: 'Placeholder resubmission reason', + ...currentSubmit, }, createdAt: '2022-03-28T17:54:39.175Z', formDataProto: currentProto, @@ -299,9 +326,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ }, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-25T21:14:43.057Z', - updatedBy: 'aang@example.com', - updatedReason: 'Placeholder resubmission reason', + ...previousSubmit, }, createdAt: '2022-03-25T21:13:56.176Z', formDataProto: previousProto, @@ -315,9 +340,7 @@ const mockSubmittedHealthPlanPackageWithRevision = ({ unlockInfo: null, submitInfo: { __typename: 'UpdateInformation', - updatedAt: '2022-03-25T21:13:20.419Z', - updatedBy: 'aang@example.com', - updatedReason: 'Initial submission', + ...initialSubmit, }, createdAt: '2022-03-25T03:28:56.244Z', formDataProto: initialProto,