From 8e5744a6903d8e5bb9c06dbe5de576a7e2ed37a7 Mon Sep 17 00:00:00 2001 From: Angela Cooney Date: Fri, 6 Dec 2024 16:16:53 -0500 Subject: [PATCH 1/2] CMDCT-4137: adding optional measures page with new optional measures (#71) Co-authored-by: benmartin-coforma <126210497+benmartin-coforma@users.noreply.github.com> --- services/app-api/forms/qm.ts | 96 +++++++++++++++++-- services/app-api/types/reports.ts | 8 ++ .../src/components/report/MeasureTable.tsx | 2 +- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/services/app-api/forms/qm.ts b/services/app-api/forms/qm.ts index cb10bccf..f6d7b594 100644 --- a/services/app-api/forms/qm.ts +++ b/services/app-api/forms/qm.ts @@ -16,7 +16,7 @@ export const qmReportTemplate: ReportTemplate = { childPageIds: [ "general-info", "req-measure-result", - "strat-measure-result", + "optional-measure-result", "review-submit", ], }, @@ -106,14 +106,14 @@ export const qmReportTemplate: ReportTemplate = { ], }, { - id: "strat-measure-result", - title: "Stratified Measure Results", + id: "optional-measure-result", + title: "Optional Measure Results", type: PageType.Standard, sidebar: true, elements: [ { type: ElementType.Header, - text: "Stratified Measure Results", + text: "Optional Measure Results", }, { type: ElementType.Accordion, @@ -122,7 +122,7 @@ export const qmReportTemplate: ReportTemplate = { }, { type: ElementType.MeasureTable, - measureDisplay: "stratified", + measureDisplay: "optional", }, ], }, @@ -155,8 +155,9 @@ export const qmReportTemplate: ReportTemplate = { }, ], measureLookup: { - // TODO: wtf is default and are there any other kinds of measures? + // TODO: what is a default measure and are there any other kinds of measures? defaultMeasures: [ + // required measures { cmit: 960, required: true, @@ -187,6 +188,43 @@ export const qmReportTemplate: ReportTemplate = { stratified: false, measureTemplate: MeasureTemplateName["LTSS-8"], }, + // optional measures + { + cmit: 969, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["FASI-1"], + }, + { + cmit: 970, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["FASI-2"], + }, + { + cmit: 111, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["HCBS-10"], + }, + { + cmit: 963, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["LTSS-3"], + }, + { + cmit: 962, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["LTSS-4"], + }, + { + cmit: 1255, + required: false, + stratified: false, + measureTemplate: MeasureTemplateName["LTSS-5"], + }, ], }, measureTemplates: { @@ -309,5 +347,51 @@ export const qmReportTemplate: ReportTemplate = { sidebar: false, elements: [], }, + //optional + [MeasureTemplateName["FASI-1"]]: { + id: "FASI-1", + title: "FASI-1: Identification of Person-Centered Priorities", + type: PageType.Measure, + sidebar: false, + elements: [], + }, + [MeasureTemplateName["FASI-2"]]: { + id: "FASI-2", + title: "FASI-2: Documentation of a Person-Centered Service Plan", + type: PageType.Measure, + sidebar: false, + elements: [], + }, + [MeasureTemplateName["HCBS-10"]]: { + id: "HCBS-10", + title: + "HCBS-10: Self-direction of Services and Supports Among Medicaid Beneficiaries Receiving LTSS through Managed Care Organizations", + type: PageType.Measure, + sidebar: false, + elements: [], + }, + [MeasureTemplateName["LTSS-3"]]: { + id: "LTSS-3", + title: "LTSS-3: Shared Person-Centered Plan with Primary Care Provider", + type: PageType.Measure, + sidebar: false, + elements: [], + }, + [MeasureTemplateName["LTSS-4"]]: { + id: "LTSS-4", + title: + "LTSS-4: Reassessment and Person-Centered Plan Update after Inpatient Discharge", + type: PageType.Measure, + sidebar: false, + elements: [], + }, + [MeasureTemplateName["LTSS-5"]]: { + id: "LTSS-5", + title: + "LTSS-5: Screening, Risk Assessment, and Plan of Care to Prevent Future Falls", + type: PageType.Measure, + sidebar: false, + elements: [], + }, } as Record, }; diff --git a/services/app-api/types/reports.ts b/services/app-api/types/reports.ts index 28a4cee9..0b5d01a5 100644 --- a/services/app-api/types/reports.ts +++ b/services/app-api/types/reports.ts @@ -36,11 +36,19 @@ export interface MeasureOptions { } export enum MeasureTemplateName { + // required measures "LTSS-1", "LTSS-2", "LTSS-6", "LTSS-7", "LTSS-8", + //optional measures + "FASI-1", + "FASI-2", + "HCBS-10", + "LTSS-3", + "LTSS-4", + "LTSS-5", } export enum ReportStatus { diff --git a/services/ui-src/src/components/report/MeasureTable.tsx b/services/ui-src/src/components/report/MeasureTable.tsx index c45082f6..2a52106a 100644 --- a/services/ui-src/src/components/report/MeasureTable.tsx +++ b/services/ui-src/src/components/report/MeasureTable.tsx @@ -34,7 +34,7 @@ export const MeasureTableElement = (props: PageElementProps) => { const selectedMeasures = measures.filter( (page) => - (table.measureDisplay == "optional" && page.optional) || + (table.measureDisplay == "optional" && !page.required) || (table.measureDisplay == "required" && page.required) || (table.measureDisplay == "stratified" && page.stratified) ); From 888ec4bf9aa81ab1107449228d57e4d5b8670639 Mon Sep 17 00:00:00 2001 From: ajaitasaini Date: Mon, 9 Dec 2024 15:59:15 -0500 Subject: [PATCH 2/2] 3/x Create Report by Name - Add AddEditReportModal to create report (#67) --- services/ui-src/src/components/index.ts | 1 + .../modals/AddEditReportModal.test.tsx | 63 +++++++++++++++ .../components/modals/AddEditReportModal.tsx | 78 +++++++++++++++++++ .../ui-src/src/components/modals/Modal.tsx | 1 + .../pages/Dashboard/DashboardPage.tsx | 36 ++++++++- .../pages/Dashboard/DashboardTable.tsx | 4 +- services/ui-src/src/types/report.ts | 1 + .../ui-src/src/verbiage/pages/dashboard.ts | 1 - 8 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 services/ui-src/src/components/modals/AddEditReportModal.test.tsx create mode 100644 services/ui-src/src/components/modals/AddEditReportModal.tsx diff --git a/services/ui-src/src/components/index.ts b/services/ui-src/src/components/index.ts index 5a947975..595e521b 100644 --- a/services/ui-src/src/components/index.ts +++ b/services/ui-src/src/components/index.ts @@ -51,6 +51,7 @@ export { Menu } from "./menus/Menu"; export { MenuOption } from "./menus/MenuOption"; // modals export { Modal } from "./modals/Modal"; +export { AddEditReportModal } from "./modals/AddEditReportModal"; // Redirects export { PostLogoutRedirect } from "./PostLogoutRedirect/index"; // tables diff --git a/services/ui-src/src/components/modals/AddEditReportModal.test.tsx b/services/ui-src/src/components/modals/AddEditReportModal.test.tsx new file mode 100644 index 00000000..c8cb8dba --- /dev/null +++ b/services/ui-src/src/components/modals/AddEditReportModal.test.tsx @@ -0,0 +1,63 @@ +import { render, screen } from "@testing-library/react"; +import { axe } from "jest-axe"; +import { AddEditReportModal } from "components"; +import { + mockStateUserStore, + RouterWrappedComponent, +} from "utils/testing/setupJest"; +import { useStore } from "utils"; +import userEvent from "@testing-library/user-event"; + +const mockCloseHandler = jest.fn(); +const mockReportHandler = jest.fn(); + +jest.mock("utils/state/useStore"); +const mockedUseStore = useStore as jest.MockedFunction; + +const modalComponent = ( + + + +); + +describe("Test AddEditProgramModal", () => { + beforeEach(() => { + mockedUseStore.mockReturnValue(mockStateUserStore); + render(modalComponent); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test("AddEditReportModal shows the contents", () => { + expect(screen.getByText("QMS report name")).toBeInTheDocument(); + expect(screen.getByText("Start new")).toBeInTheDocument(); + }); + + test("AddEditReportModal top close button can be clicked", async () => { + await userEvent.click(screen.getByText("Close")); + expect(mockCloseHandler).toHaveBeenCalledTimes(1); + }); + + test("AddEditReportModal bottom cancel button can be clicked", async () => { + await userEvent.click(screen.getByText("Cancel")); + expect(mockCloseHandler).toHaveBeenCalledTimes(1); + }); +}); + +describe("Test AddEditReportModal accessibility", () => { + it("Should not have basic accessibility issues", async () => { + const { container } = render(modalComponent); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/services/ui-src/src/components/modals/AddEditReportModal.tsx b/services/ui-src/src/components/modals/AddEditReportModal.tsx new file mode 100644 index 00000000..ce926f5c --- /dev/null +++ b/services/ui-src/src/components/modals/AddEditReportModal.tsx @@ -0,0 +1,78 @@ +import { useState } from "react"; +import { Modal, TextField } from "components"; +import { Spinner, Flex } from "@chakra-ui/react"; +import { AnyObject, ElementType } from "types"; +import { createReport } from "utils/api/requestMethods/report"; +import { FormProvider, useForm } from "react-hook-form"; +import { ReportOptions } from "types/report"; + +export const AddEditReportModal = ({ + activeState, + reportType, + modalDisclosure, + reportHandler, +}: Props) => { + const [submitting, setSubmitting] = useState(false); + + // add validation to formJson + const form = useForm(); + + const onSubmit = async (formData: any) => { + setSubmitting(true); + + const reportOptions: ReportOptions = { + name: "", + }; + + if (formData.reportTitle) { + reportOptions.name = formData.reportTitle.answer; + } + + await createReport(reportType, activeState, reportOptions); + + await reportHandler(reportType, activeState); + setSubmitting(false); + modalDisclosure.onClose(); + }; + + return ( + : "Start new", + closeButtonText: "Cancel", + }} + > + +
+ + + +
+
+
+ ); +}; + +interface Props { + activeState: string; + reportType: string; + selectedReport?: AnyObject; + modalDisclosure: { + isOpen: boolean; + onClose: any; + }; + reportHandler: (reportType: string, activeState: string) => void; +} diff --git a/services/ui-src/src/components/modals/Modal.tsx b/services/ui-src/src/components/modals/Modal.tsx index f90f7c55..91e91926 100644 --- a/services/ui-src/src/components/modals/Modal.tsx +++ b/services/ui-src/src/components/modals/Modal.tsx @@ -113,6 +113,7 @@ const sx = { }, modalHeader: { padding: "0", + margin: "0 0 2rem 0", }, modalHeaderText: { padding: "0 4rem 0 0", diff --git a/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx b/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx index 98b449c9..f9497167 100644 --- a/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx +++ b/services/ui-src/src/components/pages/Dashboard/DashboardPage.tsx @@ -1,11 +1,12 @@ import { useEffect, useState } from "react"; -import { Link as RouterLink, useNavigate, useParams } from "react-router-dom"; +import { Link as RouterLink, useParams } from "react-router-dom"; import { StateNames } from "../../../constants"; import { isReportType, isStateAbbr, Report } from "types"; import { PageTemplate, InstructionsAccordion, DashboardTable, + AddEditReportModal, } from "components"; import { Box, @@ -15,6 +16,7 @@ import { Link, Text, Flex, + useDisclosure, } from "@chakra-ui/react"; import { parseCustomHtml, useStore } from "utils"; import dashboardVerbiage from "verbiage/pages/dashboard"; @@ -23,7 +25,6 @@ import arrowLeftIcon from "assets/icons/arrows/icon_arrow_left_blue.png"; import { getReportsForState } from "utils/api/requestMethods/report"; export const DashboardPage = () => { - const navigate = useNavigate(); const { userIsAdmin } = useStore().user ?? {}; const { reportType, state } = useParams(); const [isLoading, setIsLoading] = useState(true); @@ -36,13 +37,31 @@ export const DashboardPage = () => { return; } + reloadReports(reportType, state); + }, [reportType, state]); + + const reloadReports = (reportType: string, state: string) => { (async () => { setIsLoading(true); const result = await getReportsForState(reportType, state); setReports(result); setIsLoading(false); })(); - }, [reportType, state]); + }; + + const openAddEditReportModal = () => { + // TO-DO: setSelectedReport with formData + + // use disclosure to open modal + addEditReportModalOnOpenHandler(); + }; + + // add/edit program modal disclosure + const { + isOpen: addEditReportModalIsOpen, + onOpen: addEditReportModalOnOpenHandler, + onClose: addEditReportModalOnCloseHandler, + } = useDisclosure(); return ( @@ -69,11 +88,20 @@ export const DashboardPage = () => { {!isLoading && } {!reports?.length && {body.empty}} - + ); }; diff --git a/services/ui-src/src/components/pages/Dashboard/DashboardTable.tsx b/services/ui-src/src/components/pages/Dashboard/DashboardTable.tsx index 3f18cf75..e0b5ba6d 100644 --- a/services/ui-src/src/components/pages/Dashboard/DashboardTable.tsx +++ b/services/ui-src/src/components/pages/Dashboard/DashboardTable.tsx @@ -24,7 +24,9 @@ export const DashboardTable = ({ reports }: DashboardTableProps) => { {reports.map((report) => ( - + diff --git a/services/ui-src/src/types/report.ts b/services/ui-src/src/types/report.ts index 4b383aa3..f65112f6 100644 --- a/services/ui-src/src/types/report.ts +++ b/services/ui-src/src/types/report.ts @@ -36,6 +36,7 @@ export interface Report extends ReportTemplate { lastEdited?: number; lastEditedBy?: string; status: ReportStatus; + name?: string; } export type PageTemplate = diff --git a/services/ui-src/src/verbiage/pages/dashboard.ts b/services/ui-src/src/verbiage/pages/dashboard.ts index 2f2b75ce..aa1d3bb7 100644 --- a/services/ui-src/src/verbiage/pages/dashboard.ts +++ b/services/ui-src/src/verbiage/pages/dashboard.ts @@ -8,7 +8,6 @@ export default { "Keep track of your Quality Measures Reports, once you start a report you can access it here.", link: { callToActionText: "Start Quality Measures Report", - route: "/report/QM", }, }, };
{"{Name of form}"} + {report.name ? report.name : "{Name of form}"} + {!!report.lastEdited && formatMonthDayYear(report.lastEdited)}