From a946b9e003ffc7fafbd82eefd0840f2fe97e9e50 Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:55:29 +0530 Subject: [PATCH] Add support to delete location (#6996) * Add support to delete location * fix button widths * add modal for delete confirmation * add cypress for delete location functionality * add confirmation dialog before deleting * update cypress tests * refactor * fix cypress tests * show modal with links to linked assets and beds * use pom in tests and cover all cases * update cypress tests to close notifications * use id for cypress tests * re-order asset creation commands * add wait for creating asset in tests * refactor * verify location creation success * update location name in cypress * change facility name * update to dummy shifting facility * load specific facility for test * fix typo * fix tests for new assets page --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Co-authored-by: Rithvik Nishad --- cypress/e2e/facility_spec/locations.cy.ts | 111 ++++++++++- .../pageobject/Facility/FacilityLocation.ts | 43 +++++ .../Facility/LocationManagement.tsx | 182 ++++++++++++++++-- src/Redux/api.tsx | 5 + 4 files changed, 323 insertions(+), 18 deletions(-) diff --git a/cypress/e2e/facility_spec/locations.cy.ts b/cypress/e2e/facility_spec/locations.cy.ts index 97727e05ca0..bad772ac410 100644 --- a/cypress/e2e/facility_spec/locations.cy.ts +++ b/cypress/e2e/facility_spec/locations.cy.ts @@ -5,6 +5,7 @@ import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import FacilityLocation from "../../pageobject/Facility/FacilityLocation"; import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; +import { v4 as uuidv4 } from "uuid"; describe("Location Management Section", () => { const assetPage = new AssetPage(); @@ -40,6 +41,9 @@ describe("Location Management Section", () => { const bedModifiedType = "Isolation"; const numberOfBeds = 10; const numberOfModifiedBeds = 25; + const qr_id_1 = uuidv4(); + const phone_number = "9999999999"; + const serialNumber = Math.floor(Math.random() * 10 ** 10).toString(); before(() => { cy.loginByApi("devdistrictadmin", "Coronasafe@123"); @@ -51,9 +55,7 @@ describe("Location Management Section", () => { cy.restoreLocalStorage(); cy.clearLocalStorage(/filters--.+/); cy.awaitUrl("/"); - facilityPage.visitAlreadyCreatedFacility(); - facilityPage.clickManageFacilityDropdown(); - facilityLocation.clickFacilityLocationManagement(); + facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); }); it("Add a Bed to facility location along with duplication and deleting a bed", () => { @@ -103,6 +105,7 @@ describe("Location Management Section", () => { facilityLocation.verifyBedNameBadge(bedModifiedName); facilityLocation.verifyBedBadge(bedModifiedType); facilityLocation.verifyBedBadge(bedStatus); + facilityLocation.closeNotification(); }); it("Adds Location to a facility and modify it", () => { @@ -134,6 +137,7 @@ describe("Location Management Section", () => { facilityLocation.verifyLocationType(locationModifiedType); facilityLocation.verifyLocationDescription(locationModifiedDescription); facilityLocation.verifyLocationMiddleware(locationModifiedMiddleware); + facilityLocation.closeNotification(); }); it("Multiple Bed to a facility location and delete a bed", () => { @@ -155,6 +159,7 @@ describe("Location Management Section", () => { facilityLocation.deleteBedRequest(); assetPage.clickassetupdatebutton(); facilityLocation.deleteBedRequest(); + facilityLocation.closeNotification(); }); it("Add Multiple Bed to a facility location and verify pagination", () => { @@ -169,6 +174,106 @@ describe("Location Management Section", () => { // pagination assetPagination.navigateToNextPage(); assetPagination.navigateToPreviousPage(); + facilityLocation.closeNotification(); + }); + + it("Delete location", () => { + facilityLocation.clickAddNewLocationButton(); + facilityLocation.enterLocationName("Test Location"); + facilityLocation.selectLocationType("OTHER"); + assetPage.clickassetupdatebutton(); + facilityLocation.deleteLocation("Test Location"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Location Test Location deleted successfully" + ); + facilityLocation.closeNotification(); + }); + + it("Delete location with linked beds", () => { + facilityLocation.clickAddNewLocationButton(); + facilityLocation.enterLocationName("Test Location with Beds"); + facilityLocation.selectLocationType("OTHER"); + assetPage.clickassetupdatebutton(); + facilityLocation.clickManageBedButton(); + facilityLocation.clickAddBedButton(); + facilityLocation.enterBedName("Bed 1"); + facilityLocation.selectBedType("Regular"); + assetPage.clickassetupdatebutton(); + facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); + facilityLocation.deleteLocation("Test Location with Beds"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Cannot delete a Location with associated Beds" + ); + facilityLocation.closeNotification(); + + // delete bed + facilityLocation.clickManageBeds(); + facilityLocation.deleteFirstBed(); + assetPage.clickassetupdatebutton(); + facilityLocation.closeNotification(); + + // delete location + facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); + facilityLocation.deleteLocation("Test Location with Beds"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Location Test Location with Beds deleted successfully" + ); + facilityLocation.closeNotification(); + }); + + it("Delete location with linked assets", () => { + facilityLocation.clickAddNewLocationButton(); + facilityLocation.enterLocationName("Test Location with linked Assets"); + facilityLocation.selectLocationType("OTHER"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification("Location created successfully"); + facilityLocation.closeNotification(); + // create asset and link it to location + cy.awaitUrl("/assets"); + assetPage.createAsset(); + assetPage.selectFacility("Dummy Shifting Center"); + assetPage.selectLocation("Test Location with linked Assets"); + assetPage.enterAssetDetails( + "Test Asset linked to Facility", + "Test Description", + "Working", + qr_id_1, + "Manufacturer's Name", + "2025-12-25", + "Customer Support's Name", + phone_number, + "email@support.com", + "Vendor's Name", + serialNumber, + "25122021", + "Test note for asset creation!" + ); + assetPage.clickassetupdatebutton(); + facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); + facilityLocation.deleteLocation("Test Location with linked Assets"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Cannot delete a Location with associated Assets" + ); + facilityLocation.closeNotification(); + + // delete asset + facilityLocation.clickManageAssets(); + assetPage.openCreatedAsset(); + assetPage.deleteAsset(); + facilityLocation.closeNotification(); + + // delete location + facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); + facilityLocation.deleteLocation("Test Location with linked Assets"); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Location Test Location with linked Assets deleted successfully" + ); + facilityLocation.closeNotification(); }); afterEach(() => { diff --git a/cypress/pageobject/Facility/FacilityLocation.ts b/cypress/pageobject/Facility/FacilityLocation.ts index b332d06ebf0..be14e9a8c81 100644 --- a/cypress/pageobject/Facility/FacilityLocation.ts +++ b/cypress/pageobject/Facility/FacilityLocation.ts @@ -1,4 +1,23 @@ class FacilityLocation { + loadLocationManagementPage(name: string) { + cy.awaitUrl("/"); + cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities"); + cy.get("[id='facility-name-card']").contains(name).click(); + cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); + cy.get("h1.text-3xl.font-bold", { timeout: 10000 }).should("be.visible"); + cy.get("#manage-facility-dropdown button").should("be.visible"); + cy.get("[id='manage-facility-dropdown']").scrollIntoView().click(); + cy.get("[id=location-management]").click(); + } + + closeNotification() { + cy.get(".pnotify") + .should("exist") + .each(($div) => { + cy.wrap($div).click(); + }); + } + clickAddNewLocationButton() { cy.get("#add-new-location").click(); } @@ -19,6 +38,14 @@ class FacilityLocation { cy.get("#description").clear().click().type(description); } + clickText(name: string) { + cy.get("div").contains(name).click(); + } + + enterLocationName(name: string) { + cy.get("input[id=name]").type(name); + } + selectLocationType(type: string) { cy.get("#location-type").click(); cy.get("li[role=option]").contains(type).click(); @@ -101,6 +128,22 @@ class FacilityLocation { } } + clickManageBeds() { + cy.get("#manage-beds").click(); + } + + clickManageAssets() { + cy.get("#manage-assets").click(); + } + + deleteLocation(name: string) { + cy.contains("div", name) + .should("exist") + .then(($div) => { + $div.parents("div").eq(2).find("button#delete-location-button").click(); + }); + } + deleteFirstBed() { cy.get("#delete-bed-button").first().click(); } diff --git a/src/Components/Facility/LocationManagement.tsx b/src/Components/Facility/LocationManagement.tsx index 707a954ed0d..985d6a029fe 100644 --- a/src/Components/Facility/LocationManagement.tsx +++ b/src/Components/Facility/LocationManagement.tsx @@ -1,5 +1,5 @@ -import { lazy } from "react"; -import ButtonV2 from "../Common/components/ButtonV2"; +import { lazy, useState } from "react"; +import ButtonV2, { Cancel } from "../Common/components/ButtonV2"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import CareIcon from "../../CAREUI/icons/CareIcon"; import Page from "../Common/components/Page"; @@ -7,6 +7,10 @@ import routes from "../../Redux/api"; import PaginatedList from "../../CAREUI/misc/PaginatedList"; import { LocationModel } from "./models"; import RecordMeta from "../../CAREUI/display/RecordMeta"; +import request from "../../Utils/request/request"; +import * as Notification from "../../Utils/Notifications.js"; +import ConfirmDialog from "../Common/ConfirmDialog"; +import DialogModal from "../Common/Dialog"; const Loading = lazy(() => import("../Common/Loading")); @@ -14,13 +18,62 @@ interface Props { facilityId: string; } +interface LocationProps extends LocationModel { + setShowDeletePopup: (e: { open: boolean; name: string; id: string }) => void; +} + export default function LocationManagement({ facilityId }: Props) { + const [showDeleteFailModal, setShowDeleteFailModal] = useState({ + open: false, + id: "", + reason: "", + }); + const [showDeletePopup, setShowDeletePopup] = useState({ + open: false, + name: "", + id: "", + }); + + const closeDeleteFailModal = () => { + setShowDeleteFailModal({ ...showDeleteFailModal, open: false }); + }; + + const deleteAssetLocation = async () => { + const { res, error } = await request(routes.deleteFacilityAssetLocation, { + pathParams: { + facility_external_id: facilityId, + external_id: showDeletePopup.id, + }, + }); + if (res?.ok) { + Notification.Success({ + msg: `Location ${showDeletePopup.name} deleted successfully`, + }); + } else if (res?.status === 400 && error?.length) { + const errorMessage: string = (error as unknown as string[])[0]; + if (errorMessage.includes("Asset")) { + setShowDeleteFailModal({ + open: true, + id: showDeletePopup.id, + reason: "assets", + }); + } else { + setShowDeleteFailModal({ + open: true, + id: showDeletePopup.id, + reason: "beds", + }); + } + } + setShowDeletePopup({ ...showDeletePopup, open: false }); + }; + return ( - {() => ( + {({ refetch }) => ( className="my-8 grid gap-3 @4xl:grid-cols-2 @6xl:grid-cols-3 @[100rem]:grid-cols-4 lg:mx-8"> - {(item) => } + {(item) => ( + + )}
+ + + setShowDeletePopup({ ...showDeletePopup, open: false }) + } + onConfirm={async () => { + await deleteAssetLocation(); + refetch(); + }} + /> + + + + Delete Location + + } + show={showDeleteFailModal.open} + onClose={() => + setShowDeleteFailModal({ ...showDeleteFailModal, open: false }) + } + > +
+ {showDeleteFailModal.reason === "beds" ? ( +
+
+ There are Beds associated with this location that need to be + deleted first before the location can be removed +
+
+ { + closeDeleteFailModal(); + }} + /> + + Manage Beds + +
+
+ ) : ( +
+
+ There are Assets associated with this location that need to + be deleted first before the location can be removed +
+
+ { + closeDeleteFailModal(); + }} + /> + + Manage Assets + +
+
+ )} +
+
)}
@@ -76,7 +207,8 @@ const Location = ({ created_date, modified_date, id, -}: LocationModel) => ( + setShowDeletePopup, +}: LocationProps) => (
@@ -93,16 +225,6 @@ const Location = ({

- - - Edit -

Manage Beds +

+
+ + + Edit + +
+
+ + setShowDeletePopup({ open: true, name: name ?? "", id: id ?? "" }) + } + authorizeFor={NonReadOnlyUsers} + > + + Delete + +
+
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 524d2311557..35a878adabc 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -381,6 +381,11 @@ const routes = { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/", method: "PATCH", }, + deleteFacilityAssetLocation: { + path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/", + method: "DELETE", + TRes: Type>(), + }, getFacilityAssetLocationAvailability: { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/availability/", method: "GET",