From ccdefe48feb7ec3a6a9712537cfcdd936c08fefb Mon Sep 17 00:00:00 2001 From: Nicholas Summers Date: Fri, 21 Jul 2023 14:06:05 -0400 Subject: [PATCH 01/36] Update Overlay page to use submit function --- .../src/components/overlays/EntityDetailsOverlay.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/services/ui-src/src/components/overlays/EntityDetailsOverlay.tsx b/services/ui-src/src/components/overlays/EntityDetailsOverlay.tsx index 6b7dc7c19..64b3f08f1 100644 --- a/services/ui-src/src/components/overlays/EntityDetailsOverlay.tsx +++ b/services/ui-src/src/components/overlays/EntityDetailsOverlay.tsx @@ -140,15 +140,11 @@ export const EntityDetailsOverlay = ({ autosave={true} disabled={!userIsEndUser} validateOnRender={validateOnRender || false} - dontReset={false} + dontReset={true} /> - + + + MLR report for: +
    + {programInfo.map((field, index) => ( +
  • {field}
  • + ))} +
+
+ + + + {disabled ? ( + - - + ) : ( + + )} +
); }; interface Props { + closeEntityDetailsOverlay: Function; entityType: EntityType; + entities: any; form: FormJson; - verbiage: AnyObject; + onSubmit: Function; selectedEntity: EntityShape; - closeEntityDetailsOverlay: Function; - setSidebarHidden: Function; + disabled: boolean; + submitting?: boolean; validateOnRender?: boolean; } @@ -200,6 +127,8 @@ const sx = { width: "100%", }, backButton: { + padding: 0, + fontWeight: "normal", color: "palette.primary", display: "flex", position: "relative", @@ -211,8 +140,6 @@ const sx = { color: "palette.primary", height: "1rem", marginRight: "0.5rem", - position: "relative", - top: "0.25rem", }, footerBox: { marginTop: "2rem", diff --git a/services/ui-src/src/components/reports/ModalOverlayReportPage.test.tsx b/services/ui-src/src/components/reports/ModalOverlayReportPage.test.tsx index 063590ed9..d8c7ab457 100644 --- a/services/ui-src/src/components/reports/ModalOverlayReportPage.test.tsx +++ b/services/ui-src/src/components/reports/ModalOverlayReportPage.test.tsx @@ -1,280 +1,375 @@ -import { act, render, screen, waitFor } from "@testing-library/react"; -import { axe } from "jest-axe"; +import { fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { axe } from "jest-axe"; // components -import { ReportContext, ModalOverlayReportPage } from "components"; +import { + EntityProvider, + ModalOverlayReportPage, + ReportContext, +} from "components"; // utils import { - mockModalOverlayReportPageJson, + RouterWrappedComponent, + mockMLRNewReportContext, mockModalOverlayReportPageWithOverlayJson, + mockMLREntityStartedReportContext, mockStateUser, - RouterWrappedComponent, - mockMlrReportContext, + mockAdminUser, } from "utils/testing/setupJest"; -import { useBreakpoint, makeMediaQueryClasses, useUser } from "utils"; - -const mockUseNavigate = jest.fn(); -jest.mock("react-router-dom", () => ({ - useNavigate: () => mockUseNavigate, - useLocation: jest.fn(() => ({ - pathname: "/mock-route", - })), -})); - -jest.mock("utils/other/useBreakpoint"); -const mockUseBreakpoint = useBreakpoint as jest.MockedFunction< - typeof useBreakpoint ->; -const mockMakeMediaQueryClasses = makeMediaQueryClasses as jest.MockedFunction< - typeof makeMediaQueryClasses ->; +import { useUser } from "utils"; +// verbiage +import accordionVerbiage from "../../verbiage/pages/accordion"; jest.mock("utils/auth/useUser"); const mockedUseUser = useUser as jest.MockedFunction; -const { addEntityButtonText, deleteModalConfirmButtonText } = - mockModalOverlayReportPageJson.verbiage; - -const mockReportContextWithoutEntities = { - ...mockMlrReportContext, - report: undefined, -}; - -const mockReportWithCompletedEntityContext = { - ...mockMlrReportContext, - report: { - ...mockMlrReportContext.report, - fieldData: { - ...mockMlrReportContext.report.fieldData, - program: [ - { - id: "123", - name: "example-program1", - report_eligibilityGroup: [ - { - key: "option1", - value: "mock-option", - }, - ], - "report_eligibilityGroup-otherText": "mock-text", - }, - ], - }, - }, -}; - -const modalOverlayReportPageComponent = ( +const mockSetSidebarHidden = jest.fn(); + +const modalOverlayReportPageInitialComponent = ( - + ); -const modalOverlayReportPageComponentWithEntities = ( +const modalOverlayReportPageEntityAddedComponent = ( - - + + + + ); -describe("Test ModalOverlayReportPage (empty state, desktop)", () => { +describe("Test ModalOverlayReportPage (empty state)", () => { beforeEach(() => { - mockUseBreakpoint.mockReturnValue({ - isMobile: false, - isTablet: false, - }); - mockMakeMediaQueryClasses.mockReturnValue("desktop"); + jest.clearAllMocks(); + }); + + const verbiage = mockModalOverlayReportPageWithOverlayJson.verbiage; + + it("should render the initial view for a State user", () => { + // Set as State User mockedUseUser.mockReturnValue(mockStateUser); - render(modalOverlayReportPageComponent); + render(modalOverlayReportPageInitialComponent); + + // Check if header is visible on load - H1 + expect(screen.getByText(verbiage.intro.section)).toBeVisible(); + // Check if header is visible on load - H2 + expect(screen.getByText(verbiage.intro.subsection)).toBeVisible(); + + // Check if accordion is showing + const accordionHeader = accordionVerbiage.MLR.formIntro.buttonLabel; + expect(screen.getByText(accordionHeader)).toBeVisible(); + + // Check if dashboard title is showing 0 entities + const dashboardTitle = `${verbiage.dashboardTitle} 0`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); + + // Check if emptyDashboardText is displaying + const emptyDashboardText = verbiage.emptyDashboardText; + expect(screen.getByText(emptyDashboardText)).toBeVisible(); + + // Check if addEntity button is displaying + const addInformationButton = verbiage.addEntityButtonText; + expect(screen.getByText(addInformationButton)).toBeVisible(); + + // Check if Footer is display with a next button and no previous butto + expect(screen.getByText("Continue")).toBeVisible(); + expect(screen.getByText("Previous")).toBeVisible(); }); - it("should render the view", () => { - expect( - screen.getByText( - mockModalOverlayReportPageWithOverlayJson.verbiage.intro.section - ) - ).toBeVisible(); + it("should open the add/edit modal for a State User", async () => { + // Set as State User + mockedUseUser.mockReturnValue(mockStateUser); + render(modalOverlayReportPageInitialComponent); + + const user = userEvent.setup(); + + // Get the Add button and click it + const addEntityButton = screen.getByText(verbiage.addEntityButtonText); + await user.click(addEntityButton); + expect(screen.getByRole("dialog")).toBeVisible(); + + // Close out of the modal it created + const closeButton = screen.getByText("Close"); + await user.click(closeButton); }); + + it("should render the initial view for a Admin user", () => { + // Set as State User + mockedUseUser.mockReturnValue(mockAdminUser); + render(modalOverlayReportPageInitialComponent); + + // Check if header is visible on load - H1 + expect(screen.getByText(verbiage.intro.section)).toBeVisible(); + // Check if header is visible on load - H2 + expect(screen.getByText(verbiage.intro.subsection)).toBeVisible(); + + // Check if accordion is showing + const accordionHeader = accordionVerbiage.MLR.formIntro.buttonLabel; + expect(screen.getByText(accordionHeader)).toBeVisible(); + + // Check if dashboard title is showing 0 entities + const dashboardTitle = `${verbiage.dashboardTitle} 0`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); + + // Check if emptyDashboardText is displaying + const emptyDashboardText = verbiage.emptyDashboardText; + expect(screen.getByText(emptyDashboardText)).toBeVisible(); + + // Check if addEntity button is displaying but disabled + const addInformationButton = verbiage.addEntityButtonText; + expect(screen.getByText(addInformationButton)).toBeVisible(); + expect(screen.getByText(addInformationButton)).toBeDisabled(); + + // Check if Footer is display with a next button and no previous butto + expect(screen.getByText("Continue")).toBeVisible(); + expect(screen.getByText("Previous")).toBeVisible(); + }); + /** + * @todo Write a test to make sure admins can't click/change details? + */ }); -describe("Test ModalOverlayReportPage (empty state, mobile & tablet)", () => { +describe("Test ModalOverlayReportPage (Entities Added State)", () => { beforeEach(() => { - mockUseBreakpoint.mockReturnValue({ - isMobile: true, - isTablet: true, - }); - mockMakeMediaQueryClasses.mockReturnValue("mobile"); + jest.clearAllMocks(); + }); + + const verbiage = mockModalOverlayReportPageWithOverlayJson.verbiage; + + it("should render the initial view for a State user", () => { + // Set as State User mockedUseUser.mockReturnValue(mockStateUser); - render(modalOverlayReportPageComponent); + render(modalOverlayReportPageEntityAddedComponent); + + // Check if header is visible on load - H1 + expect(screen.getByText(verbiage.intro.section)).toBeVisible(); + // Check if header is visible on load - H2 + expect(screen.getByText(verbiage.intro.subsection)).toBeVisible(); + + // Check if accordion is showing + const accordionHeader = accordionVerbiage.MLR.formIntro.buttonLabel; + expect(screen.getByText(accordionHeader)).toBeVisible(); + + // Check if dashboard title is showing 1 entities + const dashboardTitle = `${verbiage.dashboardTitle} 1`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); + + // Check if emptyDashboardText is NOT displaying + const emptyDashboardText = verbiage.emptyDashboardText; + expect(screen.queryByText(emptyDashboardText)).toBeNull(); + + // Check if action buttons are visible + const editEntityButton = screen.getByText(verbiage.editEntityButtonText); + const deleteEntityButton = screen.getByTestId("delete-entity"); + expect(editEntityButton).toBeVisible(); + expect(deleteEntityButton).toBeVisible(); + + // Check if addEntity button is displaying + const addInformationButton = verbiage.addEntityButtonText; + expect(screen.getByText(addInformationButton)).toBeVisible(); + + // Check if Footer is display with a next button and no previous butto + expect(screen.getByText("Continue")).toBeVisible(); + expect(screen.getByText("Previous")).toBeVisible(); }); - afterAll(() => { - jest.clearAllMocks(); + it("should open the edit modal", async () => { + // Setup as a State User + mockedUseUser.mockReturnValue(mockStateUser); + render(modalOverlayReportPageEntityAddedComponent); + + const user = userEvent.setup(); + + // Get the Edit button and click it + const editEntityButton = screen.getByText(verbiage.editEntityButtonText); + await user.click(editEntityButton); + expect(screen.getByRole("dialog")).toBeVisible(); + + // Close out of the modal it created + const closeButton = screen.getByText("Close"); + await user.click(closeButton); + + // And make sure they can still add entities + const addEntityButton = screen.getByText(verbiage.addEntityButtonText); + expect(addEntityButton).toBeVisible(); }); - it("should render the view", () => { + it("should open and close the delete modal as a State user", async () => { + //Setup as a state user + mockedUseUser.mockReturnValue(mockStateUser); + render(modalOverlayReportPageEntityAddedComponent); + + const user = userEvent.setup(); + + // Verify the entity exists + expect(screen.getByRole("table")).not.toBeNull; expect( screen.getByText( - mockModalOverlayReportPageWithOverlayJson.verbiage.intro.section + mockMLREntityStartedReportContext.report.fieldData.program[0] + .report_planName ) ).toBeVisible(); - }); -}); -describe("Test ModalOverlayReportPage (desktop, adding new program reporting information)", () => { - beforeEach(() => { - mockUseBreakpoint.mockReturnValue({ - isMobile: false, - isTablet: false, - }); - mockMakeMediaQueryClasses.mockReturnValue("desktop"); - mockedUseUser.mockReturnValue(mockStateUser); - act(() => { - render(modalOverlayReportPageComponentWithEntities); - }); - }); + // Get the Delete button and click it + const deleteEntityButton = screen.getByTestId("delete-entity"); + await userEvent.click(deleteEntityButton); + expect(screen.getByRole("dialog")).toBeVisible(); - it("State user should be able to enter an existing program", async () => { - const enterButton = screen.getByText("Mock enter report text"); - await userEvent.click(enterButton); - await waitFor( - () => { - expect(screen.getByText("Return to MLR Reporting")); - }, - { - timeout: 1000, - } + // Click delete in modal + const deleteButton = screen.getByText( + verbiage.deleteModalConfirmButtonText ); + await user.click(deleteButton); + + // Close out of the modal it created + const closeButton = screen.getByText("Close"); + await user.click(closeButton); + + // Verify that the entity is removed + expect(screen.getByRole("table")).toBeNull; + + // And make sure they can still add entities + const addEntityButton = screen.getByText(verbiage.addEntityButtonText); + expect(addEntityButton).toBeVisible(); }); - it("State user should be able to open and close the Add Program Reporting Information modal", async () => { - // open the modal - const addEntityButton = screen.getByText(addEntityButtonText); - await userEvent.click(addEntityButton); - expect(screen.getByRole("dialog")).toBeVisible(); + it("should be unable to click the delete button as an Admin", async () => { + //Setup as a state user + mockedUseUser.mockReturnValue(mockAdminUser); + render(modalOverlayReportPageEntityAddedComponent); - // close the modal - const closeButton = screen.getByText("Close"); - await userEvent.click(closeButton); + // Verify the entity exists + expect(screen.getByRole("table")).not.toBeNull; expect( screen.getByText( - mockModalOverlayReportPageWithOverlayJson.verbiage.intro.section + mockMLREntityStartedReportContext.report.fieldData.program[0] + .report_planName ) ).toBeVisible(); + + // Get the Delete button and click it + const deleteEntityButton = screen.getByTestId("delete-entity"); + expect(deleteEntityButton).toBeDisabled(); }); - it("State user should be able to delete existing entities", async () => { - // verify program table exists - expect(screen.getByRole("table")).not.toBeNull; + it("should open and close the overlay page as a State user", async () => { + //Setup as a state user + mockedUseUser.mockReturnValue(mockStateUser); + render(modalOverlayReportPageEntityAddedComponent); - // delete program entity - const closeButton = screen.getByRole("button", { name: "delete icon" }); - await userEvent.click(closeButton); - expect(screen.getByRole("dialog")).toBeVisible(); + const user = userEvent.setup(); - // click delete in modal - const deleteButton = screen.getByRole("button", { - name: deleteModalConfirmButtonText, - }); - await userEvent.click(deleteButton); + // Check if dashboard title is showing 1 entities + const dashboardTitle = `${verbiage.dashboardTitle} 1`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); - // verify that the program is removed - expect(screen.getByRole("table")).toBeNull; + // Get the Enter button and click it + const enterEntityButton = screen.getByText(verbiage.enterReportText); + await user.click(enterEntityButton); + + expect(mockSetSidebarHidden).toBeCalledWith(true); + + // Close out of the Overlay it opened + const closeButton = screen.getByText("Return to MLR Reporting"); + await user.click(closeButton); + + // And make sure we're back on the first page! + expect(screen.getByText(dashboardTitle)).toBeVisible(); }); -}); -describe("Test ModalOverlayReportPage (mobile + tablet, adding new program reporting information)", () => { - beforeEach(() => { - mockUseBreakpoint.mockReturnValue({ - isMobile: true, - isTablet: true, - }); - mockMakeMediaQueryClasses.mockReturnValue("mobile"); + it("should submit the form when a State user opens an entity and adds information", async () => { + window.HTMLElement.prototype.scrollIntoView = function () {}; + + //Setup as a state user mockedUseUser.mockReturnValue(mockStateUser); - render(modalOverlayReportPageComponentWithEntities); - }); + render(modalOverlayReportPageEntityAddedComponent); - it("State user should be able to enter and leave an existing program", async () => { - const enterButton = screen.getByText("Mock enter report text"); - await userEvent.click(enterButton); - const closeButton = await screen.findByText("Return to MLR Reporting"); - await waitFor( - () => { - expect(closeButton).toBeVisible; - }, - { - timeout: 1000, - } - ); - await userEvent.click(closeButton); - await waitFor( - () => { - expect(enterButton).toBeVisible; - }, - { - timeout: 1000, - } - ); - }); + const user = userEvent.setup(); - it("State user should be able to open and close the Add Program Reporting Information modal", async () => { - // open the modal - const addEntityButton = screen.getByText(addEntityButtonText); - await userEvent.click(addEntityButton); - expect(screen.getByRole("dialog")).toBeVisible(); + // Check if dashboard title is showing 1 entities + const dashboardTitle = `${verbiage.dashboardTitle} 1`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); - // close the modal - const closeButton = screen.getByText("Close"); - await userEvent.click(closeButton); + // Get the Enter button and click it + const enterEntityButton = screen.getByText(verbiage.enterReportText); + await user.click(enterEntityButton); + + expect(mockSetSidebarHidden).toBeCalledWith(true); + + // Test text fields + const textField = screen.getByLabelText("mock text field"); + expect(textField).toBeVisible(); + await userEvent.type(textField, "test"); + + // Test number fields + const numberField = screen.getByLabelText("mock number field"); + expect(numberField).toBeVisible(); + await userEvent.type(numberField, "123"); + + const saveAndCloseButton = screen.getByText("Save & return"); + await userEvent.click(saveAndCloseButton); + + // Will be 3 times! Twice for autosave and once for clicking the button expect( - screen.getByText( - mockModalOverlayReportPageWithOverlayJson.verbiage.intro.section - ) - ).toBeVisible(); + mockMLREntityStartedReportContext.updateReport + ).toHaveBeenCalledTimes(3); + + // And make sure we're back on the first page! + expect(mockSetSidebarHidden).toBeCalledWith(false); + expect(screen.getByText(dashboardTitle)).toBeVisible(); }); - it("State user should be able to delete existing entities", async () => { - // verify program table exists - expect(screen.getByRole("table")).not.toBeNull; + it("should be able to open an entity by not submit as an admin", async () => { + //Setup as a state user + mockedUseUser.mockReturnValue(mockAdminUser); + render(modalOverlayReportPageEntityAddedComponent); - // delete program entity - const closeButton = screen.getByRole("button", { name: "delete icon" }); - await userEvent.click(closeButton); - expect(screen.getByRole("dialog")).toBeVisible(); + const user = userEvent.setup(); - // click delete in modal - const deleteButton = screen.getByRole("button", { - name: deleteModalConfirmButtonText, - }); - await userEvent.click(deleteButton); + // Check if dashboard title is showing 1 entities + const dashboardTitle = `${verbiage.dashboardTitle} 1`; + expect(screen.getByText(dashboardTitle)).toBeVisible(); - // verify that the program is removed - expect(screen.getByRole("table")).toBeNull; + // Get the Enter button and click it + const enterEntityButton = screen.getByText(verbiage.enterReportText); + await user.click(enterEntityButton); + expect(mockSetSidebarHidden).toBeCalledWith(true); + + const saveAndCloseButton = screen.getByText("Return"); + await userEvent.click(saveAndCloseButton); + + expect( + mockMLREntityStartedReportContext.updateReport + ).toHaveBeenCalledTimes(0); + + // And make sure we're back on the first page! + expect(mockSetSidebarHidden).toBeCalledWith(false); + expect(screen.getByText(dashboardTitle)).toBeVisible(); }); }); describe("Test ModalOverlayReportPage accessibility", () => { - beforeEach(() => { + it("Should not have basic accessibility issues", async () => { mockedUseUser.mockReturnValue(mockStateUser); - render(modalOverlayReportPageComponent); - }); - - it("Should not have basic accessibility issues (desktop)", async () => { - const { container } = render(modalOverlayReportPageComponent); + const { container } = render(modalOverlayReportPageInitialComponent); const results = await axe(container); expect(results).toHaveNoViolations(); }); - it("Should not have basic accessibility issues (mobile)", async () => { - const { container } = render(modalOverlayReportPageComponent); + it("Should not have basic accessibility issues", async () => { + mockedUseUser.mockReturnValue(mockStateUser); + const { container } = render(modalOverlayReportPageEntityAddedComponent); const results = await axe(container); expect(results).toHaveNoViolations(); }); diff --git a/services/ui-src/src/components/reports/ModalOverlayReportPage.tsx b/services/ui-src/src/components/reports/ModalOverlayReportPage.tsx index 7e38bf1aa..cd54977c3 100644 --- a/services/ui-src/src/components/reports/ModalOverlayReportPage.tsx +++ b/services/ui-src/src/components/reports/ModalOverlayReportPage.tsx @@ -5,54 +5,65 @@ import { AddEditEntityModal, DeleteEntityModal, EntityDetailsOverlay, + EntityProvider, EntityRow, - MobileEntityRow, ReportContext, ReportPageFooter, ReportPageIntro, Table, } from "components"; // types -import { EntityShape, EntityType, ModalOverlayReportPageShape } from "types"; - +import { + AnyObject, + EntityShape, + EntityType, + isFieldElement, + ModalOverlayReportPageShape, + ReportStatus, +} from "types"; // utils -import { useBreakpoint, useUser } from "utils"; - +import { + entityWasUpdated, + filterFormData, + getEntriesToClear, + setClearedEntriesToDefaultValue, + useUser, +} from "utils"; // verbiage import accordionVerbiage from "../../verbiage/pages/accordion"; -import { EntityProvider } from "./EntityProvider"; export const ModalOverlayReportPage = ({ route, setSidebarHidden, validateOnRender, }: Props) => { - const { isTablet, isMobile } = useBreakpoint(); - + // Route Information const { entityType, verbiage, modalForm, overlayForm } = route; - const [selectedEntity, setSelectedEntity] = useState( + + // Context Information + // const { isTablet, isMobile } = useBreakpoint(); + const { report, updateReport } = useContext(ReportContext); + const [isEntityDetailsOpen, setIsEntityDetailsOpen] = useState(); + const [currentEntity, setCurrentEntity] = useState( undefined ); - const [isEntityDetailsOpen, setIsEntityDetailsOpen] = useState(); - const { report } = useContext(ReportContext); - const reportType = report?.reportType; - const reportFieldDataEntities = report?.fieldData[entityType] || []; - const { userIsAdmin, userIsReadOnly } = useUser().user ?? {}; - const isAdminUserType = userIsAdmin || userIsReadOnly; - const formIsDisabled = isAdminUserType && route.modalForm?.adminDisabled; - // is MLR report in a LOCKED state - const isLocked = report?.locked || formIsDisabled; + const [submitting, setSubmitting] = useState(false); + const { userIsAdmin, userIsReadOnly, userIsEndUser, full_name, state } = + useUser().user ?? {}; - const dashTitle = `${verbiage.dashboardTitle}${ - verbiage.countEntitiesInTitle ? ` ${reportFieldDataEntities.length}` : "" - }`; + // Determine whether form is locked or unlocked based on user and route + const isAdminUserType = userIsAdmin || userIsReadOnly; + const isLocked = report?.locked || isAdminUserType; + // Display Variables + const reportFieldDataEntities = report?.fieldData[entityType] || []; + const dashTitle = `${verbiage.dashboardTitle} ${reportFieldDataEntities.length}`; const tableHeaders = () => { - if (isTablet || isMobile) return { headRow: ["", ""] }; + // if (isTablet || isMobile) return { headRow: ["", ""] }; return { headRow: ["", verbiage.tableHeader, ""] }; }; - // add/edit entity modal disclosure and methods + // Add/edit entity modal disclosure and methods const { isOpen: addEditEntityModalIsOpen, onOpen: addEditEntityModalOnOpenHandler, @@ -60,16 +71,16 @@ export const ModalOverlayReportPage = ({ } = useDisclosure(); const openAddEditEntityModal = (entity?: EntityShape) => { - if (entity) setSelectedEntity(entity); + if (entity) setCurrentEntity(entity); addEditEntityModalOnOpenHandler(); }; const closeAddEditEntityModal = () => { - setSelectedEntity(undefined); + setCurrentEntity(undefined); addEditEntityModalOnCloseHandler(); }; - // delete modal disclosure and methods + // Delete entity modal disclosure and methods const { isOpen: deleteEntityModalIsOpen, onOpen: deleteEntityModalOnOpenHandler, @@ -77,90 +88,127 @@ export const ModalOverlayReportPage = ({ } = useDisclosure(); const openDeleteEntityModal = (entity: EntityShape) => { - setSelectedEntity(entity); + setCurrentEntity(entity); deleteEntityModalOnOpenHandler(); }; const closeDeleteEntityModal = () => { - setSelectedEntity(undefined); + setCurrentEntity(undefined); deleteEntityModalOnCloseHandler(); }; - // entity overlay disclosure and methods - + // Open/Close overlay action methods const openEntityDetailsOverlay = (entity: EntityShape) => { - setSelectedEntity(entity); + setCurrentEntity(entity); setIsEntityDetailsOpen(true); setSidebarHidden(true); }; const closeEntityDetailsOverlay = () => { - setSelectedEntity(undefined); + setCurrentEntity(undefined); setIsEntityDetailsOpen(false); setSidebarHidden(false); }; + // Form submit methods + const onSubmit = async (enteredData: AnyObject) => { + if (userIsEndUser) { + setSubmitting(true); + const reportKeys = { + reportType: report?.reportType, + state: state, + id: report?.id, + }; + const currentEntities = [...(report?.fieldData[entityType] || [])]; + const selectedEntityIndex = report?.fieldData[entityType].findIndex( + (entity: EntityShape) => entity.id === currentEntity?.id + ); + const filteredFormData = filterFormData( + enteredData, + overlayForm!.fields.filter(isFieldElement) + ); + const entriesToClear = getEntriesToClear( + enteredData, + overlayForm!.fields.filter(isFieldElement) + ); + const newEntity = { + ...currentEntity, + ...filteredFormData, + }; + let newEntities = currentEntities; + newEntities[selectedEntityIndex] = newEntity; + newEntities[selectedEntityIndex] = setClearedEntriesToDefaultValue( + newEntities[selectedEntityIndex], + entriesToClear + ); + const shouldSave = entityWasUpdated( + reportFieldDataEntities[selectedEntityIndex], + newEntity + ); + if (shouldSave) { + const dataToWrite = { + metadata: { + status: ReportStatus.IN_PROGRESS, + lastAlteredBy: full_name, + }, + fieldData: { + [entityType]: newEntities, + }, + }; + await updateReport(reportKeys, dataToWrite); + } + setSubmitting(false); + } + closeEntityDetailsOverlay(); + setSidebarHidden(false); + }; + return ( - {overlayForm && selectedEntity && isEntityDetailsOpen ? ( + {overlayForm && isEntityDetailsOpen && currentEntity ? ( ) : ( - {verbiage.intro && ( - - )} + + {dashTitle} {reportFieldDataEntities.length === 0 ? ( <> -
+ {verbiage.emptyDashboardText} ) : ( - - {reportFieldDataEntities.map( - (entity: EntityShape, entityIndex: number) => - isMobile || isTablet ? ( - - ) : ( - - ) - )} +
+ {reportFieldDataEntities.map((entity: EntityShape) => ( + + ))}
)} - {report && ( - <> - - - - )}
- + + + + + +
)}
@@ -225,7 +272,16 @@ const sx = { paddingBottom: "0", }, }, - header: { + emptyDashboard: { + paddingTop: "1rem", + }, + tableSeparator: { + borderTop: "1px solid", + borderColor: "palette.gray_light", + paddingBottom: "1rem", + marginTop: "1.25rem", + }, + table: { tableLayout: "fixed", br: { marginBottom: "0.25rem", @@ -238,19 +294,16 @@ const sx = { ".tablet &, .mobile &": { border: "none", }, - "&:nth-child(1)": { + "&:nth-of-type(1)": { width: "2.5rem", }, - "&:nth-child(3)": { + "&:nth-of-type(3)": { width: "260px", }, }, }, - emptyDashboard: { - paddingTop: "2rem", - }, addEntityButton: { - marginTop: "1.5rem", + marginTop: "2rem", marginBottom: "2rem", ".tablet &, .mobile &": { wordBreak: "break-word", diff --git a/services/ui-src/src/components/tables/EntityRow.tsx b/services/ui-src/src/components/tables/EntityRow.tsx index a2a6b4971..24e07682e 100644 --- a/services/ui-src/src/components/tables/EntityRow.tsx +++ b/services/ui-src/src/components/tables/EntityRow.tsx @@ -71,6 +71,7 @@ export const EntityRow = ({