Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMDCT-4086: Add / Edit Report Modal (NAAAR) #11920

Merged
merged 6 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions services/app-api/forms/naaar.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"type": "p",
"content": "According to the Paperwork Reduction Act of 1995, no persons are required to respond to a collection of information unless it displays a valid OMB control number. The valid OMB control number for this information collection is 0938-0920 (Expires: June 30, 2024). The time required to complete this information collection is estimated to average 6 hours per response, including the time to review instructions, search existing data resources, gather the data needed, and complete and review the information collection. If you have comments concerning the accuracy of the time estimate(s) or suggestions for improving this form, please write to: CMS, 7500 Security Boulevard, Attn: PRA Reports Clearance Officer, Mail Stop C4-26-05, Baltimore, Maryland 21244-1850"
}

]
},
"form": {
Expand All @@ -62,7 +61,7 @@
}
},
{
"id": "stateOrTerritory",
"id": "stateName",
"type": "text",
"validation": "textOptional",
"props": {
Expand Down
4 changes: 3 additions & 1 deletion services/app-api/handlers/reports/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
MCPARReportMetadata,
MLRReportMetadata,
UserRoles,
NAAARReportMetadata,
} from "../../utils/types";

export const submitReport = handler(async (event, _context) => {
Expand Down Expand Up @@ -65,7 +66,8 @@ export const submitReport = handler(async (event, _context) => {

const reportMetadata = response.Item as
| MLRReportMetadata
| MCPARReportMetadata;
| MCPARReportMetadata
| NAAARReportMetadata;
const { status, isComplete, fieldDataId, formTemplateId } = reportMetadata;

if (status === "Submitted") {
Expand Down
2 changes: 2 additions & 0 deletions services/app-api/utils/types/reportContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface ReportMetadataShape extends ReportKeys {
copyFieldDataSourceId?: string;
programIsPCCM?: Choice[];
previousRevisions: string[];
planTypeIncludedInProgram?: Choice[];
"planTypeIncludedInProgram-otherText"?: string;
novMcparRelease?: boolean;
}

Expand Down
10 changes: 10 additions & 0 deletions services/app-api/utils/types/reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ export interface MCPARReportMetadata extends ReportMetadata {
novMcparRelease: boolean;
}

export interface NAAARReportMetadata extends ReportMetadata {
karla-vm marked this conversation as resolved.
Show resolved Hide resolved
programName: string;
reportType: "NAAAR";
reportingPeriodStartDate: number;
reportingPeriodEndDate: number;
dueDate: number;
planTypeIncludedInProgram: Choice[];
"planTypeIncludedInProgram-otherText"?: string;
}

// HELPER FUNCTIONS

/**
Expand Down
4 changes: 3 additions & 1 deletion services/app-api/utils/validation/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as yup from "yup";
import { radioOptional } from "./completionSchemas";
import { radioOptional, textOptional } from "./completionSchemas";

export const metadataValidationSchema = yup.object().shape({
programName: yup.string(),
Expand All @@ -19,4 +19,6 @@ export const metadataValidationSchema = yup.object().shape({
submissionName: yup.string(),
completionStatus: yup.mixed(),
copyFieldDataSourceId: yup.string(),
planTypeIncludedInProgram: radioOptional(),
"planTypeIncludedInProgram-otherText": textOptional(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
mockMlrReport,
mockMlrReportContext,
mockMlrReportStore,
mockNaaarReport,
mockNaaarReportContext,
mockNaaarReportStore,
mockStateUserStore,
Expand Down Expand Up @@ -81,6 +82,22 @@ const mockSelectedMcparReport = {
},
};

// a similar assignment is performed in DashboardPage and is needed here to make sure the modal form hydrates
const mockSelectedNaaarReport = {
...mockNaaarReport,
fieldData: {
programName: mockNaaarReport.programName,
reportingPeriodEndDate: convertDateUtcToEt(
mockNaaarReport.reportingPeriodEndDate
),
reportingPeriodStartDate: convertDateUtcToEt(
mockNaaarReport.reportingPeriodStartDate
),
combinedData: mockNaaarReport.combinedData,
planTypeIncludedInProgram: mockNaaarReport.planTypeIncludedInProgram,
},
};

const modalComponentWithSelectedReport = (
<RouterWrappedComponent>
<ReportContext.Provider value={mockedMcparReportContext}>
Expand Down Expand Up @@ -145,6 +162,22 @@ const naaarModalComponent = (
</RouterWrappedComponent>
);

const naaarModalComponentWithSelectedReport = (
<RouterWrappedComponent>
<ReportContext.Provider value={mockedNaaarReportContext}>
<AddEditReportModal
activeState="AB"
selectedReport={mockSelectedNaaarReport}
reportType={"NAAAR"}
modalDisclosure={{
isOpen: true,
onClose: mockCloseHandler,
}}
/>
</ReportContext.Provider>
</RouterWrappedComponent>
);

describe("Test AddEditProgramModal", () => {
beforeEach(async () => {
mockedUseStore.mockReturnValue({
Expand Down Expand Up @@ -324,8 +357,16 @@ describe("Test AddEditReportModal functionality for NAAAR", () => {
});

const fillForm = async (form: any) => {
const contactNameField = form.querySelector("[name='contactName']")!;
await userEvent.type(contactNameField, "fake contact name");
const programNameField = form.querySelector("[name='programName']")!;
await userEvent.type(programNameField, "fake program name");
const startDateField = form.querySelector(
"[name='reportingPeriodStartDate']"
)!;
await userEvent.type(startDateField, "1/1/2022");
const endDateField = form.querySelector("[name='reportingPeriodEndDate']")!;
await userEvent.type(endDateField, "12/31/2022");
const planTypeField = screen.getByLabelText("MCO") as HTMLInputElement;
await userEvent.click(planTypeField);
const submitButton = screen.getByRole("button", { name: "Save" });
await userEvent.click(submitButton);
};
Expand All @@ -340,6 +381,50 @@ describe("Test AddEditReportModal functionality for NAAAR", () => {
expect(mockCloseHandler).toHaveBeenCalledTimes(1);
});
});

test("Edit modal hydrates with report info and disables fields", async () => {
const result = render(naaarModalComponentWithSelectedReport);
const form = result.getByTestId("add-edit-report-form");
const copyFieldDataSourceId = form.querySelector(
"[name='copyFieldDataSourceId']"
)!;

// yoy copy field is disabled
expect(copyFieldDataSourceId).toHaveProperty("disabled", true);

// hydrated values are in the modal
const programNameField = form.querySelector("[name='programName']")!;
const startDateField = form.querySelector(
"[name='reportingPeriodStartDate']"
)!;
const endDateField = form.querySelector("[name='reportingPeriodEndDate']")!;

expect(programNameField).toHaveProperty(
"value",
mockNaaarReport.programName
);
expect(startDateField).toHaveProperty(
"value",
convertDateUtcToEt(mockNaaarReport.reportingPeriodStartDate)
);
expect(endDateField).toHaveProperty(
"value",
convertDateUtcToEt(mockNaaarReport.reportingPeriodEndDate)
);

await userEvent.click(screen.getByText("Cancel"));
});

test("Editing an existing report", async () => {
const result = render(naaarModalComponentWithSelectedReport);
const form = result.getByTestId("add-edit-report-form");
await fillForm(form);
await waitFor(() => {
expect(mockUpdateReport).toHaveBeenCalledTimes(1);
expect(mockFetchReportsByState).toHaveBeenCalledTimes(1);
expect(mockCloseHandler).toHaveBeenCalledTimes(1);
});
});
});

describe("Test AddEditReportModal accessibility", () => {
Expand Down
23 changes: 20 additions & 3 deletions services/ui-src/src/components/modals/AddEditReportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,35 @@ export const AddEditReportModal = ({

// NAAAR report payload
const prepareNaaarPayload = (formData: any) => {
const contactName = formData["contactName"];
const programName = formData["programName"];
const copyFieldDataSourceId = formData["copyFieldDataSourceId"];
const dueDate = calculateDueDate(formData["reportingPeriodEndDate"]);
const reportingPeriodStartDate = convertDateEtToUtc(
formData["reportingPeriodStartDate"]
);
const reportingPeriodEndDate = convertDateEtToUtc(
formData["reportingPeriodEndDate"]
);
const planTypeIncludedInProgram = formData["planTypeIncludedInProgram"];

return {
metadata: {
contactName,
programName,
reportingPeriodStartDate,
reportingPeriodEndDate,
dueDate,
lastAlteredBy: full_name,
copyFieldDataSourceId: copyFieldDataSourceId?.value,
planTypeIncludedInProgram,
"planTypeIncludedInProgram-otherText":
formData["planTypeIncludedInProgram-otherText"],
locked: false,
submissionCount: 0,
previousRevisions: [],
naaarReport,
},
fieldData: {
contactName,
programName,
},
};
};
Expand Down Expand Up @@ -227,6 +243,7 @@ export const AddEditReportModal = ({
content={{
heading: selectedReport?.id ? form.heading?.edit : form.heading?.add,
subheading: selectedReport?.id ? "" : form.heading?.subheading,
intro: selectedReport?.id ? "" : form.heading?.intro,
actionButtonText: submitting ? <Spinner size="md" /> : "Save",
closeButtonText: "Cancel",
}}
Expand Down
11 changes: 9 additions & 2 deletions services/ui-src/src/components/modals/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ export const Modal = ({
</Heading>
</ModalHeader>
{content.subheading && (
<Box sx={sx.modalSubheader}>{content.subheading}</Box>
<Box sx={sx.modalSubheading}>{content.subheading}</Box>
)}
{content.intro && <Box sx={sx.intro}>{content.intro}</Box>}
<Flex sx={sx.modalCloseContainer}>
<Button
sx={sx.modalClose}
Expand Down Expand Up @@ -95,6 +96,7 @@ interface Props {
content: {
heading: string;
subheading?: string;
intro?: string;
actionButtonText: string | ReactNode;
closeButtonText?: string;
};
Expand All @@ -121,7 +123,12 @@ const sx = {
fontSize: "2xl",
fontWeight: "bold",
},
modalSubheader: {
modalSubheading: {
marginTop: "0.5rem",
fontSize: "xl",
fontWeight: "bold",
},
intro: {
margin: "0.5rem auto -1rem auto",
},
modalCloseContainer: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ export const DashboardPage = ({ reportType }: Props) => {
combinedData: report.combinedData,
copyFieldDataSourceId,
programIsPCCM: report?.programIsPCCM,
planTypeIncludedInProgram: report?.planTypeIncludedInProgram,
"planTypeIncludedInProgram-otherText":
report?.["planTypeIncludedInProgram-otherText"],
},
state: report.state,
id: report.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
},
"heading": {
"add": "Add / Copy a MCPAR",
"subheading": "Complete the form below to generate a MCPAR for your managed care program. You may choose to copy an existing MCPAR report, which will retain many key responses (e.g., plan names, quality measures, etc.). Copied MCPARs will still have several blank fields and all responses can be edited. To create a blank MCPAR form, leave the “copy a report” field as “Select an option”.",
"intro": "Complete the form below to generate a MCPAR for your managed care program. You may choose to copy an existing MCPAR report, which will retain many key responses (e.g., plan names, quality measures, etc.). Copied MCPARs will still have several blank fields and all responses can be edited. To create a blank MCPAR form, leave the “copy a report” field as “Select an option”.",
"edit": "Edit Program"
},
"fields": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,91 @@
"mode": "onChange"
},
"heading": {
"add": "Add new NAAAR submission",
"edit": "Edit NAAAR submission"
"add": "Add / Copy a Program",
"subheading": "State information and reporting scenario",
"intro": "Add a new program to start a blank report or copy an existing report. Copying a program’s NAAAR will retain the structure of your program, including plans and access measures, but allow you to enter updated responses for the new reporting period, saving you time. You can still edit these details within.",
"edit": "Edit Program"
},
"fields": [
{
"id": "contactName",
"id": "programName",
"type": "text",
"validation": "text",
"props": {
"label": "Placeholder",
"hint": "Placeholder"
"label": "Program name"
}
},
{
"id": "copyFieldDataSourceId",
"type": "dropdown",
"props": {
"label": "If you want to copy an existing report, select one (optional)",
"options": "copyEligibleReports"
}
},
{
"id": "reportingPeriodStartDate",
"type": "date",
"validation": "date",
"props": {
"label": "Reporting period (i.e. contract period) start date",
"hint": "Enter start date of the reporting period represented in the report.",
"timetype": "startDate"
karla-vm marked this conversation as resolved.
Show resolved Hide resolved
}
},
{
"id": "reportingPeriodEndDate",
"type": "date",
"validation": {
"type": "endDate",
"dependentFieldName": "reportingPeriodStartDate"
},
"props": {
"label": "Reporting period (i.e. contract period) end date",
"hint": "Enter end date of the reporting period represented in the report.",
"timetype": "endDate"
}
},
{
"id": "planTypeIncludedInProgram",
"type": "radio",
"validation": "radio",
"props": {
"label": "Plan type included in program",
"hint": "Indicate the managed care plan type (MCO, PIHP, PAHP, or MMP) that contracts with the state in each program.",
"choices": [
{
"id": "NHAbx1VBdvZkHgG2HTfexemq",
"label": "MCO"
},
{
"id": "MiyW1eKfcetIG8k2eyT5dbhw",
"label": "PIHP"
},
{
"id": "QASeuhF5cDBrRpWbmYBndH2v",
"label": "PAHP"
},
{
"id": "U4dg782RHft2Fs53fOpcbocr",
"label": "MMP"
},
{
"id": "ZRH5GgCnJSlnCdieekgh67sv",
"label": "Other, specify",
"children": [
{
"id": "planTypeIncludedInProgram-otherText",
"type": "textarea",
"validation": {
"type": "text",
"nested": true,
"parentFieldName": "planTypeIncludedInProgram"
}
}
]
}
]
}
}
]
Expand Down
Loading