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

ILOS (I Lose ollmy Sanity) #11710

Merged
merged 13 commits into from
May 22, 2024
5 changes: 4 additions & 1 deletion services/app-api/handlers/reports/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ export const createReport = handler(async (event, _context) => {
const isProgramPCCM =
unvalidatedMetadata?.programIsPCCM?.[0]?.value === "Yes";

const ilosAvailable = unvalidatedMetadata?.ilosAvailable;

try {
({ formTemplate, formTemplateVersion } = await getOrCreateFormTemplate(
reportBucket,
reportType,
isProgramPCCM
isProgramPCCM,
ilosAvailable
));
} catch (err) {
logger.error(err, "Error getting or creating template");
Expand Down
12 changes: 8 additions & 4 deletions services/app-api/utils/formTemplates/formTemplates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ describe("Test getOrCreateFormTemplate MCPAR", () => {
const result = await getOrCreateFormTemplate(
"local-mcpar-reports",
ReportType.MCPAR,
programIsNotPCCM
programIsNotPCCM,
true
);
expect(dynamoPutSpy).toHaveBeenCalled();
expect(s3PutSpy).toHaveBeenCalled();
Expand Down Expand Up @@ -95,7 +96,8 @@ describe("Test getOrCreateFormTemplate MCPAR", () => {
const result = await getOrCreateFormTemplate(
"local-mcpar-reports",
ReportType.MCPAR,
programIsPCCM
programIsPCCM,
true
);
expect(dynamoPutSpy).toHaveBeenCalled();
expect(s3PutSpy).toHaveBeenCalled();
Expand Down Expand Up @@ -126,7 +128,8 @@ describe("Test getOrCreateFormTemplate MCPAR", () => {
const result = await getOrCreateFormTemplate(
"local-mcpar-reports",
ReportType.MCPAR,
programIsNotPCCM
programIsNotPCCM,
true
);
expect(dynamoPutSpy).not.toHaveBeenCalled();
expect(s3PutSpy).not.toHaveBeenCalled();
Expand Down Expand Up @@ -165,7 +168,8 @@ describe("Test getOrCreateFormTemplate MCPAR", () => {
const result = await getOrCreateFormTemplate(
"local-mcpar-reports",
ReportType.MCPAR,
programIsNotPCCM
programIsNotPCCM,
true
);
expect(dynamoPutSpy).toHaveBeenCalled();
expect(s3PutSpy).toHaveBeenCalled();
Expand Down
24 changes: 22 additions & 2 deletions services/app-api/utils/formTemplates/formTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,18 @@ export const formTemplateForReportType = (reportType: ReportType) => {
export async function getOrCreateFormTemplate(
reportBucket: string,
reportType: ReportType,
isProgramPCCM: boolean
isProgramPCCM: boolean,
ilosAvailable?: boolean
) {
let currentFormTemplate = formTemplateForReportType(reportType);
// if ILOS is not enabled, remove the fields from form template
if (!ilosAvailable) {
currentFormTemplate = generateTemplateWithoutIlos(currentFormTemplate);
}
if (isProgramPCCM) {
currentFormTemplate = generatePCCMTemplate(currentFormTemplate);
}
const stringifiedTemplate = JSON.stringify(currentFormTemplate);

const currentTemplateHash = createHash("md5")
.update(stringifiedTemplate)
.digest("hex");
Expand Down Expand Up @@ -323,3 +327,19 @@ const makePCCMTemplateModifications = (reportTemplate: ReportJson) => {
}
programTypeQuestion.props!.disabled = true;
};

const generateTemplateWithoutIlos = (originalReportTemplate: any) => {
const reportTemplate = structuredClone(originalReportTemplate);
// remove ILOS sections from template
for (let route of reportTemplate.routes) {
if (
route.path === "/mcpar/program-information" ||
route.path === "/mcpar/plan-level-indicators"
) {
// These sections' last subsection is ILOS-specific; remove it.
route.children = route.children?.slice(0, -1);
}
}

return reportTemplate;
};
1 change: 1 addition & 0 deletions services/app-api/utils/types/reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export interface MCPARReportMetadata extends ReportMetadata {
dueDate: number;
combinedData: boolean;
programIsPCCM: Choice[];
ilosAvailable?: boolean;
}

export enum ReportType {
Expand Down
9 changes: 2 additions & 7 deletions services/ui-src/src/components/menus/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import { useFlags } from "launchdarkly-react-client-sdk";
// components
import {
Box,
Expand Down Expand Up @@ -90,19 +89,15 @@ const NavSection = ({ section, level }: NavSectionProps) => {
const { pathname } = useLocation();
const [isOpen, setIsOpen] = useState(false);

// LaunchDarkly
const ilos = useFlags()?.ilos;

useEffect(() => {
if (pathname.includes(section.path)) {
setIsOpen(true);
}
}, [pathname]);

const { name, path, children } = section;
const ilosRoute =
path.endsWith("add-in-lieu-of-services") || path.endsWith("ilos");
return ilosRoute && !ilos ? null : (

return (
<React.Fragment key={path}>
{children ? (
<Box
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export const AddEditReportModal = ({
const { copyEligibleReportsByState } = useStore();

const [submitting, setSubmitting] = useState<boolean>(false);

// LaunchDarkly
const yoyCopyFlag = useFlags()?.yoyCopy;
const ilosAvailable = useFlags()?.ilos;

// get correct form
const modalFormJsonMap: any = {
Expand Down Expand Up @@ -101,6 +104,7 @@ export const AddEditReportModal = ({
lastAlteredBy: full_name,
copyFieldDataSourceId: copyFieldDataSourceId?.value,
programIsPCCM,
ilosAvailable,
locked: false,
submissionCount: 0,
previousRevisions: [],
Expand Down
17 changes: 3 additions & 14 deletions services/ui-src/src/components/reports/DrawerReportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
entityWasUpdated,
filterFormData,
generateIlosFields,
isIlosCompleted,
getEntriesToClear,
parseCustomHtml,
setClearedEntriesToDefaultValue,
Expand Down Expand Up @@ -127,25 +128,13 @@ export const DrawerReportPage = ({ route, validateOnRender }: Props) => {
.every((field: FormField) => field.id in entity);
};

// if reporting on ILOS, verify that there its questions are completed
let isIlosCompleted = false;
if (
(reportingOnIlos && entity["plan_ilosOfferedByPlan"]) ||
entity["plan_ilosUtilizationByPlan"]
) {
isIlosCompleted = entity["plan_ilosOfferedByPlan"][0].value.startsWith(
"Yes"
)
? entity["plan_ilosUtilizationByPlan"].length > 0
: entity["plan_ilosOfferedByPlan"][0];
}

/*
* If the entity has the same fields from drawerForms fields, it was completed
* at somepoint.
*/
const isEntityCompleted = reportingOnIlos
? calculateEntityCompletion() && isIlosCompleted
? calculateEntityCompletion() &&
isIlosCompleted(reportingOnIlos, entity)
: calculateEntityCompletion();

return (
Expand Down
12 changes: 2 additions & 10 deletions services/ui-src/src/components/reports/ReportPageWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { useFlags } from "launchdarkly-react-client-sdk";
// components
import { Flex, Spinner } from "@chakra-ui/react";
import {
Expand Down Expand Up @@ -31,9 +30,6 @@ export const ReportPageWrapper = () => {
const { pathname } = useLocation();
const locationState = useLocation().state as AnyObject;

// LaunchDarkly
const ilos = useFlags()?.ilos;

// get state and id from context or storage
const reportId = report?.id || localStorage.getItem("selectedReport");
const reportState = state || localStorage.getItem("selectedState");
Expand All @@ -53,19 +49,15 @@ export const ReportPageWrapper = () => {
};

const renderPageSection = (route: ReportRoute) => {
const ilosRoute =
route.path.endsWith("add-in-lieu-of-services") ||
route.path.endsWith("ilos");
switch (route.pageType) {
case PageTypes.DRAWER:
showSidebar();
return ilosRoute && !ilos ? null : (
return (
<DrawerReportPage
route={route as DrawerReportPageShape}
validateOnRender={locationState?.validateOnRender}
/>
);

case PageTypes.MODAL_DRAWER:
showSidebar();
return (
Expand All @@ -87,7 +79,7 @@ export const ReportPageWrapper = () => {
return <ReviewSubmitPage />;
default:
showSidebar();
return ilosRoute && !ilos ? null : (
return (
<StandardReportPage
route={route as StandardReportPageShape}
validateOnRender={locationState?.validateOnRender}
Expand Down
1 change: 1 addition & 0 deletions services/ui-src/src/types/reportContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface ReportMetadataShape extends ReportKeys {
fieldDataId: string;
copyFieldDataSourceId?: string;
programIsPCCM?: Choice[];
ilosAvailable?: boolean;
previousRevisions: string[];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { generateIlosFields } from "utils";
import { FormJson, AnyObject } from "types";
import { generateIlosFields, isIlosCompleted } from "utils";
import { FormJson, AnyObject, EntityShape } from "types";

const mockForm: FormJson = {
id: "mock-id",
Expand Down Expand Up @@ -50,9 +50,28 @@ const mockIlos: AnyObject[] = [
},
];

const result = generateIlosFields(mockForm, mockIlos);
const mockCompleteIlosEntity: EntityShape = {
id: "mock-complete-ilos",
plan_ilosOfferedByPlan: [
{
key: "mock-key",
value: "Yes",
},
],
plan_ilosUtilizationByPlan: [
{
id: "mock-ilos",
name: "ilos",
},
],
};

const mockIncompleteIlosEntity: EntityShape = {
id: "mock-incomplete-ilos",
};

describe("generateIlosFields", () => {
const result = generateIlosFields(mockForm, mockIlos);
it("should generate checkboxes per each available ILOS", () => {
expect(result.fields[0].props.choices.length).toBe(2);
});
Expand All @@ -66,3 +85,14 @@ describe("generateIlosFields", () => {
);
});
});

describe("isIlosCompleted", () => {
it("should return TRUE if entity is complete", () => {
const result = isIlosCompleted(true, mockCompleteIlosEntity);
expect(result).toBe(true);
});
it("should return FALSE if entity is incomplete", () => {
const result = isIlosCompleted(true, mockIncompleteIlosEntity);
expect(result).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AnyObject, FormJson } from "types";
import { AnyObject, EntityShape, FormJson } from "types";

export const generateIlosFields = (form: FormJson, ilos: AnyObject[]) => {
const fields = form.fields[0];
Expand Down Expand Up @@ -73,3 +73,22 @@ const updatedIlosChoiceList = (
});
return updatedChoiceList;
};

// if reporting on ILOS, verify that its questions are completed
export const isIlosCompleted = (
reportingOnIlos: boolean,
entity: EntityShape
) => {
let isIlosCompleted = false;
if (
(reportingOnIlos && entity["plan_ilosOfferedByPlan"]) ||
entity["plan_ilosUtilizationByPlan"]
) {
isIlosCompleted = entity["plan_ilosOfferedByPlan"][0].value.startsWith(
"Yes"
)
? entity["plan_ilosUtilizationByPlan"].length > 0
: entity["plan_ilosOfferedByPlan"][0];
}
return isIlosCompleted;
};
2 changes: 1 addition & 1 deletion services/ui-src/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export * from "./auth/authLifecycle";
export * from "./autosave/autosave";
// forms
export * from "./forms/forms";
export * from "./forms/generateIlosFields";
export * from "./forms/ilosFields";
// reports
export * from "./reports/entities";
export * from "./reports/reports";
Expand Down
8 changes: 1 addition & 7 deletions tests/cypress/e2e/mcpar/form.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,7 @@ function fillOutPartialMCPAR() {
const traverseRoutes = (routes) => {
//iterate over each route
routes.forEach((route) => {
// skip over the ILOS routes as they are behind an LD flag
if (
!route.path.includes("add-in-lieu-of-services") ||
!route.path.includes("ilos")
) {
traverseRoute(route);
}
traverseRoute(route);
});
};

Expand Down