diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index b90adb20138..6e760362cd6 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -188,9 +188,6 @@ jobs: needs: build-staging name: Deploy to staging GCP cluster runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Staging-GCP url: https://care-staging.ohc.network/ @@ -202,15 +199,19 @@ jobs: token: ${{ secrets.GIT_ACCESS_TOKEN }} path: kube ref: main + + - uses: actions/setup-node@v3 + with: + node-version: '20' # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -233,9 +234,6 @@ jobs: needs: build-production name: Deploy to GKE Manipur runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production-Manipur url: https://care.mn.gov.in @@ -248,14 +246,18 @@ jobs: path: kube ref: main + - uses: actions/setup-node@v3 + with: + node-version: '20' + # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -278,9 +280,6 @@ jobs: needs: build-production name: Deploy to GKE Karnataka runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production-Karnataka url: https://karnataka.care @@ -293,14 +292,18 @@ jobs: path: kube ref: main + - uses: actions/setup-node@v3 + with: + node-version: '20' + # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -323,9 +326,6 @@ jobs: needs: build-production name: Deploy to GKE Sikkim runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production-Sikkim url: https://care.sikkim.gov.in @@ -338,14 +338,18 @@ jobs: path: kube ref: main + - uses: actions/setup-node@v3 + with: + node-version: '20' + # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -368,9 +372,6 @@ jobs: needs: build-production name: Deploy to GKE Assam runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production-Assam url: https://care.assam.gov.in @@ -383,14 +384,18 @@ jobs: path: kube ref: main + - uses: actions/setup-node@v3 + with: + node-version: '20' + # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -413,9 +418,6 @@ jobs: needs: build-production name: Deploy to GKE Nagaland runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production - Nagaland url: https://care.nagaland.gov.in @@ -427,15 +429,19 @@ jobs: token: ${{ secrets.GIT_ACCESS_TOKEN }} path: kube ref: main + + - uses: actions/setup-node@v3 + with: + node-version: '20' # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials, so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} @@ -458,9 +464,6 @@ jobs: needs: build-production name: Deploy to GKE Meghalaya runs-on: ubuntu-latest - - uses: actions/setup-node@v3 - with: - node-version: '20' environment: name: Production-Meghalaya url: https://care.meghealth.gov.in @@ -472,15 +475,19 @@ jobs: token: ${{ secrets.GIT_ACCESS_TOKEN }} path: kube ref: main + + - uses: actions/setup-node@v3 + with: + node-version: '20' # Setup gcloud CLI - - uses: google-github-actions/setup-gcloud@v2 + - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7 with: service_account_key: ${{ secrets.GKE_SA_KEY }} project_id: ${{ secrets.GKE_PROJECT }} # Get the GKE credentials, so we can deploy to the cluster - - uses: google-github-actions/get-gke-credentials@v2 + - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e with: cluster_name: ${{ secrets.GKE_CLUSTER }} location: ${{ secrets.GKE_ZONE }} diff --git a/cypress/e2e/facility_spec/facility_homepage.cy.ts b/cypress/e2e/facility_spec/facility_homepage.cy.ts index 3d916b4ba47..7269c3a2581 100644 --- a/cypress/e2e/facility_spec/facility_homepage.cy.ts +++ b/cypress/e2e/facility_spec/facility_homepage.cy.ts @@ -5,6 +5,7 @@ import FacilityHome from "../../pageobject/Facility/FacilityHome"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import { UserPage } from "../../pageobject/Users/UserSearch"; +import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; describe("Facility Homepage Function", () => { const loginPage = new LoginPage(); @@ -12,6 +13,7 @@ describe("Facility Homepage Function", () => { const facilityPage = new FacilityPage(); const manageUserPage = new ManageUserPage(); const userPage = new UserPage(); + const assetPagination = new AssetPagination(); const facilitiesAlias = "downloadFacilitiesCSV"; const capacitiesAlias = "downloadCapacitiesCSV"; const doctorsAlias = "downloadDoctorsCSV"; @@ -19,6 +21,7 @@ describe("Facility Homepage Function", () => { const facilityname = "Dummy Facility 1"; const statename = "Kerala"; const district = "Ernakulam"; + const localbody = "Aikaranad"; const facilitytype = "Private Hospital"; before(() => { @@ -31,24 +34,52 @@ describe("Facility Homepage Function", () => { cy.awaitUrl("/facility"); }); + it("Verify the Facility card button redirection", () => { + // view cns button + facilityHome.clickViewCnsButton(); + facilityHome.verifyCnsUrl(); + facilityHome.navigateBack(); + // view notify button + facilityHome.clickFacilityNotifyButton(); + facilityHome.verifyAndCloseNotifyModal(); + // view facility button + facilityHome.clickViewFacilityDetails(); + facilityPage.getFacilityName().should("be.visible"); + facilityHome.verifyFacilityDetailsUrl(); + facilityHome.navigateBack(); + // view patient button + manageUserPage.clickFacilityPatients(); + facilityHome.verifyPatientListVisibility(); + facilityHome.verifyPatientListUrl(); + facilityHome.navigateBack(); + // occupancy badge + facilityHome.verifyOccupancyBadgeVisibility(); + }); + it("Verify the functionality of advance filter", () => { userPage.clickAdvancedFilters(); facilityPage.selectState(statename); facilityPage.selectDistrict(district); - // facilityPage.selectLocalBody("Anthikad Grama"); current dummy data have issue in local body + facilityPage.selectLocalBody(localbody); facilityPage.clickUpdateFacilityType(facilitytype); userPage.applyFilter(); facilityPage.verifyStateBadgeContent(statename); facilityPage.verifyDistrictBadgeContent(district); + facilityPage.verifyLocalBodyBadgeContent(localbody); facilityPage.verifyFacilityTypeBadgeContent(facilitytype); manageUserPage.assertFacilityInCard(facilityname); userPage.clearFilters(); userPage.verifyDataTestIdNotVisible("State"); userPage.verifyDataTestIdNotVisible("District"); userPage.verifyDataTestIdNotVisible("Facility type"); + userPage.verifyDataTestIdNotVisible("Local Body"); }); - it("Search a facility in homepage", () => { + it("Search a facility in homepage and pagination", () => { + // pagination of the facility page + assetPagination.navigateToNextPage(); + assetPagination.navigateToPreviousPage(); + // search for a facility manageUserPage.typeFacilitySearch(facilityname); facilityPage.verifyFacilityBadgeContent(facilityname); manageUserPage.assertFacilityInCard(facilityname); diff --git a/cypress/e2e/facility_spec/locations.cy.ts b/cypress/e2e/facility_spec/locations.cy.ts index 26364048ca2..d6377ba97c3 100644 --- a/cypress/e2e/facility_spec/locations.cy.ts +++ b/cypress/e2e/facility_spec/locations.cy.ts @@ -3,14 +3,18 @@ import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import { UserCreationPage } from "../../pageobject/Users/UserCreation"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import FacilityLocation from "../../pageobject/Facility/FacilityLocation"; -// import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; +import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; +import FacilityHome from "../../pageobject/Facility/FacilityHome"; + describe("Location Management Section", () => { const assetPage = new AssetPage(); const userCreationPage = new UserCreationPage(); const facilityPage = new FacilityPage(); const facilityLocation = new FacilityLocation(); - // const assetPagination = new AssetPagination(); + const assetPagination = new AssetPagination(); + const facilityHome = new FacilityHome(); + const EXPECTED_LOCATION_ERROR_MESSAGES = [ "Name is required", "Location Type is required", @@ -27,7 +31,7 @@ describe("Location Management Section", () => { const locationModifiedDescription = "Test Modified Description"; const locationModifiedType = "ICU"; const locationModifiedMiddleware = "dev-middleware.coronasafe.live"; - const bedName = "Test Bed"; + const bedName = "Test-Bed"; const bedDescrption = "test description"; const bedType = "ICU"; const bedStatus = "Vacant"; @@ -35,7 +39,7 @@ describe("Location Management Section", () => { const bedModifiedDescrption = "test modified description"; const bedModifiedType = "Isolation"; const numberOfBeds = 10; - // const numberOfModifiedBeds = 25; + const numberOfModifiedBeds = 25; before(() => { cy.loginByApi("devdistrictadmin", "Coronasafe@123"); @@ -56,6 +60,42 @@ describe("Location Management Section", () => { cy.get("[id=location-management]").click(); }); + it("Add a Bed to facility location along with duplication and deleting a bed", () => { + // mandatory field verification in bed creation + facilityLocation.clickManageBedButton(); + facilityLocation.clickAddBedButton(); + assetPage.clickassetupdatebutton(); + userCreationPage.verifyErrorMessages(EXPECTED_BED_ERROR_MESSAGES); + // create a new single bed and verify + facilityLocation.enterBedName(bedName); + facilityLocation.enterBedDescription(bedDescrption); + facilityLocation.selectBedType(bedType); + assetPage.clickassetupdatebutton(); + // Verify the bed creation + facilityLocation.verifyBedNameBadge(bedName); + facilityLocation.verifyBedBadge(bedType); + facilityLocation.verifyBedBadge(bedStatus); + // Try to create duplication bed and verify the error + facilityLocation.clickAddBedButton(); + facilityLocation.enterBedName(bedName); + facilityLocation.selectBedType(bedType); + assetPage.clickassetupdatebutton(); + facilityLocation.verifyNotification( + "Name - Bed with same name already exists in location" + ); + facilityHome.verifyAndCloseNotifyModal(); + // edit the created bed + facilityLocation.clickEditBedButton(); + facilityLocation.enterBedName(bedModifiedName); + facilityLocation.enterBedDescription(bedModifiedDescrption); + facilityLocation.selectBedType(bedModifiedType); + assetPage.clickassetupdatebutton(); + // verify the modification + facilityLocation.verifyBedNameBadge(bedModifiedName); + facilityLocation.verifyBedBadge(bedModifiedType); + facilityLocation.verifyBedBadge(bedStatus); + }); + it("Adds Location to a facility and modify it", () => { // add a new location form mandatory error facilityLocation.clickAddNewLocationButton(); @@ -86,7 +126,7 @@ describe("Location Management Section", () => { facilityLocation.verifyLocationMiddleware(locationModifiedMiddleware); }); - it("Add Multiple Bed to a facility location and delete a bed", () => { + it("Multiple Bed to a facility location and delete a bed", () => { // create multiple bed and verify facilityLocation.clickManageBedButton(); facilityLocation.clickAddBedButton(); @@ -106,45 +146,18 @@ describe("Location Management Section", () => { facilityLocation.deleteBedRequest(); }); - // it("Add Multiple Bed to a facility location and verify pagination", () => { - // // bed creation - // facilityLocation.clickManageBedButton(); - // facilityLocation.clickAddBedButton(); - // facilityLocation.enterBedName(bedModifiedName); - // facilityLocation.enterBedDescription(bedModifiedDescrption); - // facilityLocation.selectBedType(bedModifiedType); - // facilityLocation.setMultipleBeds(numberOfModifiedBeds); - // assetPage.clickassetupdatebutton(); - // // pagination - // assetPagination.navigateToNextPage(); - // assetPagination.navigateToPreviousPage(); - // }); need to be unblocked upon issue #6906 is solved - - it("Add Single Bed to a facility location and modify it", () => { - // mandatory field verification in bed creation + it("Add Multiple Bed to a facility location and verify pagination", () => { + // bed creation facilityLocation.clickManageBedButton(); facilityLocation.clickAddBedButton(); - assetPage.clickassetupdatebutton(); - userCreationPage.verifyErrorMessages(EXPECTED_BED_ERROR_MESSAGES); - // create a new single bed and verify - facilityLocation.enterBedName(bedName); - facilityLocation.enterBedDescription(bedDescrption); - facilityLocation.selectBedType(bedType); - assetPage.clickassetupdatebutton(); - // Verify the bed creation - facilityLocation.verifyBedNameBadge(bedName); - facilityLocation.verifyBedBadge(bedType); - facilityLocation.verifyBedBadge(bedStatus); - // edit the created bed - facilityLocation.clickEditBedButton(); facilityLocation.enterBedName(bedModifiedName); facilityLocation.enterBedDescription(bedModifiedDescrption); facilityLocation.selectBedType(bedModifiedType); + facilityLocation.setMultipleBeds(numberOfModifiedBeds); assetPage.clickassetupdatebutton(); - // verify the modification - facilityLocation.verifyBedNameBadge(bedModifiedName); - facilityLocation.verifyBedBadge(bedModifiedType); - facilityLocation.verifyBedBadge(bedStatus); + // pagination + assetPagination.navigateToNextPage(); + assetPagination.navigateToPreviousPage(); }); afterEach(() => { diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index 608dd9c357b..2d6aa9ff375 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -299,6 +299,7 @@ class FacilityPage { cy.intercept("https://maps.googleapis.com/maps/api/mapsjs/*").as("mapApi"); cy.wait("@mapApi").its("response.statusCode").should("eq", 200); cy.get("input#pac-input").type(location).type("{enter}"); + cy.wait(2000); cy.get("div#map-close").click(); } @@ -337,6 +338,10 @@ class FacilityPage { cy.get("[data-testid='District']").should("contain", expectedText); } + verifyLocalBodyBadgeContent(expectedText: string) { + cy.get("[data-testid='Local Body']").should("contain", expectedText); + } + verifyFacilityTypeBadgeContent(expectedText: string) { cy.get("[data-testid='Facility type']").should("contain", expectedText); } @@ -414,6 +419,7 @@ class FacilityPage { selectStateOnPincode(stateName) { this.getStateElement() .scrollIntoView() + .wait(2000) .should("be.visible") .then(($element) => { const text = $element.text(); @@ -427,6 +433,7 @@ class FacilityPage { selectDistrictOnPincode(districtName) { this.getDistrictElement() .scrollIntoView() + .wait(2000) .should("be.visible") .then(($element) => { const text = $element.text(); diff --git a/cypress/pageobject/Facility/FacilityHome.ts b/cypress/pageobject/Facility/FacilityHome.ts index 04dfe94d002..03f2be9b19b 100644 --- a/cypress/pageobject/Facility/FacilityHome.ts +++ b/cypress/pageobject/Facility/FacilityHome.ts @@ -23,6 +23,49 @@ class FacilityHome { cy.intercept("GET", `**/api/v1/facility/?csv${queryParam}`).as(alias); } + clickViewCnsButton() { + cy.get("#view-cns-button").first().click(); + } + + verifyCnsUrl() { + cy.url().should("include", "/cns"); + } + + clickFacilityNotifyButton() { + cy.get("#facility-notify").first().click(); + } + + verifyFacilityDetailsUrl() { + cy.url().should("match", /\/facility\/[\w-]+/); + } + + verifyPatientListVisibility() { + cy.get("#patient-name-list").scrollIntoView(); + cy.get("#patient-name-list").should("be.visible"); + } + + verifyPatientListUrl() { + cy.url().should("match", /\/patients\?facility=.+/); + } + + verifyOccupancyBadgeVisibility() { + cy.get("#occupany-badge").should("be.visible"); + } + + verifyAndCloseNotifyModal() { + cy.get("#cancel").should("be.visible"); + cy.get("#cancel").click(); + } + + navigateBack() { + cy.go(-1); + } + + clickViewFacilityDetails() { + cy.get("#facility-details").should("be.visible"); + cy.get("#facility-details").first().click(); + } + verifyDownload(alias: string) { cy.wait(`@${alias}`).its("response.statusCode").should("eq", 200); } diff --git a/cypress/pageobject/Facility/FacilityLocation.ts b/cypress/pageobject/Facility/FacilityLocation.ts index d3eebdaf4ff..8924119510f 100644 --- a/cypress/pageobject/Facility/FacilityLocation.ts +++ b/cypress/pageobject/Facility/FacilityLocation.ts @@ -32,6 +32,10 @@ class FacilityLocation { cy.get("#location-type").contains(type); } + verifyNotification(message: string) { + cy.get(".pnotify-container").should("contain", message).and("be.visible"); + } + verifyLocationDescription(description: string) { cy.get("#view-location-description").contains(description); } @@ -49,7 +53,7 @@ class FacilityLocation { } enterBedName(name: string) { - cy.get("#bed-name").clear().click().type(name); + cy.get("#bed-name").click().clear().click().type(name); } enterBedDescription(description: string) { diff --git a/cypress/pageobject/Users/ManageUserPage.ts b/cypress/pageobject/Users/ManageUserPage.ts index d3f7e17e7d5..2d1ebbc14f0 100644 --- a/cypress/pageobject/Users/ManageUserPage.ts +++ b/cypress/pageobject/Users/ManageUserPage.ts @@ -103,6 +103,7 @@ export class ManageUserPage { } clickFacilityPatients() { + cy.get("#facility-patients").should("be.visible"); cy.get("#facility-patients").click(); } diff --git a/src/CAREUI/display/RecordMeta.tsx b/src/CAREUI/display/RecordMeta.tsx index 818553d9207..5e1e117f9d6 100644 --- a/src/CAREUI/display/RecordMeta.tsx +++ b/src/CAREUI/display/RecordMeta.tsx @@ -1,7 +1,8 @@ import CareIcon from "../icons/CareIcon"; import { - formatDateTime, + formatDate, formatName, + formatTime, isUserOnline, relativeTime, } from "../../Utils/utils"; @@ -38,7 +39,8 @@ const RecordMeta = ({
{relativeTime(time)} - {formatDateTime(time)} + {formatTime(time)}
+ {formatDate(time)} {user && !inlineUser && ( by diff --git a/src/CAREUI/display/Timeline.tsx b/src/CAREUI/display/Timeline.tsx index 7549fbfd69f..276c437056c 100644 --- a/src/CAREUI/display/Timeline.tsx +++ b/src/CAREUI/display/Timeline.tsx @@ -76,23 +76,29 @@ export const TimelineNode = (props: TimelineNodeProps) => { > {props.title || ( -

- {props.event.by && ( - - {formatName(props.event.by)}{" "} - - )} - {props.titleSuffix - ? props.titleSuffix - : `${props.event.type} the ${props.name || name}.`} -

- {props.actions && ( - {props.actions} - )} - +
+

+ {props.event.by && ( + + {formatName(props.event.by)}{" "} + {props.event.by.user_type && + `(${props.event.by.user_type}) `} + + )} + {props.titleSuffix + ? props.titleSuffix + : `${props.event.type} the ${props.name || name}.`} +

+
+ {props.actions && ( + {props.actions} + )} + +
+
)}
diff --git a/src/CAREUI/misc/PaginatedList.tsx b/src/CAREUI/misc/PaginatedList.tsx index 02ee0e3d90e..1487d69e4fa 100644 --- a/src/CAREUI/misc/PaginatedList.tsx +++ b/src/CAREUI/misc/PaginatedList.tsx @@ -7,6 +7,7 @@ import ButtonV2, { import CareIcon from "../icons/CareIcon"; import { classNames } from "../../Utils/utils"; import Pagination from "../../Components/Common/Pagination"; +import Timeline from "../display/Timeline"; const DEFAULT_PER_PAGE_LIMIT = 14; @@ -129,20 +130,26 @@ interface ItemsProps { const Items = (props: ItemsProps) => { const { loading, items } = useContextualized(); + if (loading || items.length === 0) { + return null; + } + return ( -
    - {loading && props.shimmer - ? Array.from({ length: props.shimmerCount ?? 8 }).map((_, i) => ( -
  • - {props.shimmer} -
  • - )) - : items.map((item, index, items) => ( -
  • - {props.children(item, items)} -
  • - ))} -
+ +
    + {loading && props.shimmer + ? Array.from({ length: props.shimmerCount ?? 8 }).map((_, i) => ( +
  • + {props.shimmer} +
  • + )) + : items.map((item, index, items) => ( +
  • + {props.children(item, items)} +
  • + ))} +
+
); }; @@ -153,8 +160,16 @@ interface PaginatorProps { hideIfSinglePage?: boolean; } -const Paginator = ({ className, hideIfSinglePage }: PaginatorProps) => { +const Paginator = ({ + className, + hideIfSinglePage, +}: PaginatorProps) => { const { data, perPage, currentPage, setPage } = useContextualized(); + const { loading } = useContextualized(); + + if (loading) { + return null; + } if (hideIfSinglePage && (data?.count ?? 0) <= perPage) { return null; diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 23c02e389f3..647c77ecf57 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -300,6 +300,19 @@ export const DISCHARGE_REASONS = [ { id: "LAMA", text: "LAMA" }, ]; +export const CONSCIOUSNESS_LEVEL = [ + { id: "UNRESPONSIVE", text: "Unresponsive" }, + { id: "RESPONDS_TO_PAIN", text: "Responds to Pain" }, + { id: "RESPONDS_TO_VOICE", text: "Responds to Voice" }, + { id: "ALERT", text: "Alert" }, + { id: "AGITATED_OR_CONFUSED", text: "Agitated or Confused" }, + { + id: "ONSET_OF_AGITATION_AND_CONFUSION", + text: "Onset of Agitation and Confusion", + }, + { id: "UNKNOWN", text: "Unknown" }, +]; + export const LINES_CATHETER_CHOICES: Array = [ { id: 1, text: "CVP catheter " }, { id: 2, text: "Arterial Line" }, diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx index 02fe1d90265..adab83c744c 100644 --- a/src/Components/Assets/AssetImportModal.tsx +++ b/src/Components/Assets/AssetImportModal.tsx @@ -22,9 +22,10 @@ interface Props { open: boolean; onClose: (() => void) | undefined; facility: FacilityModel; + onUpdate?: (() => void) | undefined; } -const AssetImportModal = ({ open, onClose, facility }: Props) => { +const AssetImportModal = ({ open, onClose, facility, onUpdate }: Props) => { const [isImporting, setIsImporting] = useState(false); const [selectedFile, setSelectedFile] = useState(); const [preview, setPreview] = @@ -170,7 +171,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => { Notification.Success({ msg: "Assets imported successfully" }); await sleep(1000); setIsImporting(false); - window.location.reload(); + onUpdate?.(); } else { Notification.Error({ msg: "Error importing some assets" }); await sleep(1000); diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index b4fefbc90b2..86b9565e536 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -1,5 +1,5 @@ import { SyntheticEvent, useEffect, useState } from "react"; -import { AssetData } from "../AssetTypes"; +import { AssetData, ResolvedMiddleware } from "../AssetTypes"; import * as Notification from "../../../Utils/Notifications.js"; import MonitorConfigure from "../configure/MonitorConfigure"; import Loading from "../../Common/Loading"; @@ -13,7 +13,6 @@ import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatien import useAuthUser from "../../../Common/hooks/useAuthUser"; import request from "../../../Utils/request/request"; import routes from "../../../Redux/api"; -import useQuery from "../../../Utils/request/useQuery"; interface HL7MonitorProps { assetId: string; @@ -22,27 +21,20 @@ interface HL7MonitorProps { } const HL7Monitor = (props: HL7MonitorProps) => { - const { assetId, asset, facilityId } = props; + const { assetId, asset } = props; const [assetType, setAssetType] = useState(""); const [middlewareHostname, setMiddlewareHostname] = useState(""); - const [facilityMiddlewareHostname, setFacilityMiddlewareHostname] = - useState(""); + const [resolvedMiddleware, setResolvedMiddleware] = + useState(); const [isLoading, setIsLoading] = useState(true); const [localipAddress, setLocalIPAddress] = useState(""); const [ipadrdress_error, setIpAddress_error] = useState(""); const authUser = useAuthUser(); - const { data: facility, loading } = useQuery(routes.getPermittedFacility, { - pathParams: { id: facilityId }, - onResponse: ({ res, data }) => { - if (res?.status === 200 && data && data.middleware_address) { - setFacilityMiddlewareHostname(data.middleware_address); - } - }, - }); useEffect(() => { setAssetType(asset?.asset_class); setMiddlewareHostname(asset?.meta?.middleware_hostname); + setResolvedMiddleware(asset?.resolved_middleware); setLocalIPAddress(asset?.meta?.local_ip_address); setIsLoading(false); }, [asset]); @@ -76,10 +68,7 @@ const HL7Monitor = (props: HL7MonitorProps) => { } }; - const fallbackMiddleware = - asset?.location_object?.middleware_address || facilityMiddlewareHostname; - - if (isLoading || loading || !facility) return ; + if (isLoading) return ; return (
@@ -94,23 +83,21 @@ const HL7Monitor = (props: HL7MonitorProps) => { label={

Middleware Hostname

- {!middlewareHostname && ( + {resolvedMiddleware?.source != "asset" && (
- Middleware hostname sourced from{" "} - {asset?.location_object?.middleware_address - ? "asset location" - : "asset facility"} + Middleware hostname sourced from asset{" "} + {resolvedMiddleware?.source}
)}
} - placeholder={fallbackMiddleware} + placeholder={resolvedMiddleware?.hostname} value={middlewareHostname} onChange={(e) => setMiddlewareHostname(e.value)} errorClassName="hidden" @@ -140,16 +127,12 @@ const HL7Monitor = (props: HL7MonitorProps) => { {assetType === "HL7MONITOR" && ( )} {assetType === "VENTILATOR" && ( )}
diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index 44d4d372d73..86b7199cdac 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { AssetData } from "../AssetTypes"; +import { AssetData, ResolvedMiddleware } from "../AssetTypes"; import * as Notification from "../../../Utils/Notifications.js"; import { BedModel } from "../../Facility/models"; import axios from "axios"; @@ -29,8 +29,8 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const [isLoading, setIsLoading] = useState(true); const [assetType, setAssetType] = useState(""); const [middlewareHostname, setMiddlewareHostname] = useState(""); - const [facilityMiddlewareHostname, setFacilityMiddlewareHostname] = - useState(""); + const [resolvedMiddleware, setResolvedMiddleware] = + useState(); const [cameraAddress, setCameraAddress] = useState(""); const [ipadrdress_error, setIpAddress_error] = useState(""); const [username, setUsername] = useState(""); @@ -47,20 +47,11 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { pathParams: { id: facilityId }, }); const authUser = useAuthUser(); - useEffect(() => { - if (facility?.middleware_address) { - setFacilityMiddlewareHostname(facility.middleware_address); - } - }, [facility, facilityId]); - - const fallbackMiddleware = - asset?.location_object?.middleware_address || facilityMiddlewareHostname; - - const currentMiddleware = middlewareHostname || fallbackMiddleware; useEffect(() => { if (asset) { setAssetType(asset?.asset_class); + setResolvedMiddleware(asset?.resolved_middleware); const cameraConfig = getCameraConfig(asset); setMiddlewareHostname(cameraConfig.middleware_hostname); setCameraAddress(cameraConfig.hostname); @@ -79,7 +70,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const data = { meta: { asset_type: "CAMERA", - middleware_hostname: middlewareHostname, // TODO: remove this infavour of facility.middleware_address + middleware_hostname: middlewareHostname, local_ip_address: cameraAddress, camera_access_key: `${username}:${password}:${streamUuid}`, }, @@ -110,7 +101,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { try { setLoadingAddPreset(true); const presetData = await axios.get( - `https://${currentMiddleware}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}` + `https://${resolvedMiddleware?.hostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}` ); const { res } = await request(routes.createAssetBed, { @@ -151,23 +142,21 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { label={

Middleware Hostname

- {!middlewareHostname && ( + {resolvedMiddleware?.source != "asset" && (
- Middleware hostname sourced from{" "} - {asset?.location_object?.middleware_address - ? "asset location" - : "asset facility"} + Middleware hostname sourced from asset{" "} + {resolvedMiddleware?.source}
)}
} - placeholder={fallbackMiddleware} + placeholder={resolvedMiddleware?.hostname} value={middlewareHostname} onChange={({ value }) => setMiddlewareHostname(value)} /> @@ -225,7 +214,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { addPreset={addPreset} isLoading={loadingAddPreset} refreshPresetsHash={refreshPresetsHash} - facilityMiddlewareHostname={currentMiddleware} + facilityMiddlewareHostname={resolvedMiddleware?.hostname || ""} /> ) : null}
diff --git a/src/Components/Assets/AssetTypes.tsx b/src/Components/Assets/AssetTypes.tsx index 97334f6af49..436c9370dd8 100644 --- a/src/Components/Assets/AssetTypes.tsx +++ b/src/Components/Assets/AssetTypes.tsx @@ -72,6 +72,11 @@ export interface AssetService { note: string; } +export interface ResolvedMiddleware { + hostname: string; + source: "asset" | "location" | "facility"; +} + export interface AssetData { id: string; name: string; @@ -93,6 +98,7 @@ export interface AssetData { qr_code_id: string; manufacturer: string; warranty_amc_end_of_validity: string; + resolved_middleware?: ResolvedMiddleware; last_service: AssetService; meta?: { [key: string]: any; diff --git a/src/Components/Assets/AssetsList.tsx b/src/Components/Assets/AssetsList.tsx index 447bff1c1d0..3fc9c9f9f14 100644 --- a/src/Components/Assets/AssetsList.tsx +++ b/src/Components/Assets/AssetsList.tsx @@ -69,7 +69,7 @@ const AssetsList = () => { qParams.warranty_amc_end_of_validity_after || "", }; - const { loading } = useQuery(routes.listAssets, { + const { refetch: assetsFetch, loading } = useQuery(routes.listAssets, { query: params, onResponse: ({ res, data }) => { if (res?.status === 200 && data) { @@ -436,6 +436,7 @@ const AssetsList = () => { return f; }); }} + onUpdate={assetsFetch} facility={facility} /> )} diff --git a/src/Components/CameraFeed/CameraFeed.tsx b/src/Components/CameraFeed/CameraFeed.tsx index 4ec039e4e70..6e2d8647c83 100644 --- a/src/Components/CameraFeed/CameraFeed.tsx +++ b/src/Components/CameraFeed/CameraFeed.tsx @@ -14,7 +14,6 @@ import Fullscreen from "../../CAREUI/misc/Fullscreen"; interface Props { children?: React.ReactNode; asset: AssetData; - fallbackMiddleware: string; // TODO: remove this in favour of `asset.resolved_middleware.hostname` once https://github.com/coronasafe/care/pull/1741 is merged preset?: PTZPayload; silent?: boolean; className?: string; @@ -29,7 +28,7 @@ interface Props { export default function CameraFeed(props: Props) { const playerRef = useRef(null); - const streamUrl = getStreamUrl(props.asset, props.fallbackMiddleware); + const streamUrl = getStreamUrl(props.asset); const player = usePlayer(streamUrl, playerRef); const operate = useOperateCamera(props.asset.id, props.silent); diff --git a/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx b/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx index b52071a8597..386b93325b0 100644 --- a/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx +++ b/src/Components/CameraFeed/CameraFeedWithBedPresets.tsx @@ -5,7 +5,6 @@ import AssetBedSelect from "./AssetBedSelect"; interface Props { asset: AssetData; - fallbackMiddleware?: string; } export default function LocationFeedTile(props: Props) { @@ -14,7 +13,6 @@ export default function LocationFeedTile(props: Props) { return ( {data.results.map((asset) => (
- +
))} diff --git a/src/Components/CameraFeed/utils.ts b/src/Components/CameraFeed/utils.ts index b5b8920fd5a..e2793d76b41 100644 --- a/src/Components/CameraFeed/utils.ts +++ b/src/Components/CameraFeed/utils.ts @@ -17,9 +17,9 @@ export const calculateVideoDelay = ( return playedDuration - video.currentTime; }; -export const getStreamUrl = (asset: AssetData, fallbackMiddleware?: string) => { +export const getStreamUrl = (asset: AssetData) => { const config = getCameraConfig(asset); - const host = config.middleware_hostname || fallbackMiddleware; + const host = asset.resolved_middleware?.hostname; const uuid = config.accessKey; return isIOS diff --git a/src/Components/Common/BloodPressureFormField.tsx b/src/Components/Common/BloodPressureFormField.tsx index ed0557ae8e3..3ff2774b900 100644 --- a/src/Components/Common/BloodPressureFormField.tsx +++ b/src/Components/Common/BloodPressureFormField.tsx @@ -21,7 +21,7 @@ export default function BloodPressureFormField(props: Props) { name: field.name, value: { ...field.value, - [event.name]: event.value, + [event.name]: event.value ?? -1, }, }); }; @@ -35,9 +35,10 @@ export default function BloodPressureFormField(props: Props) { MAP: {map.toFixed(1)} - ) : undefined, + labelSuffix: + map && map !== -1 ? ( + MAP: {map.toFixed(1)} + ) : undefined, }} >
diff --git a/src/Components/Common/FilePreviewDialog.tsx b/src/Components/Common/FilePreviewDialog.tsx index 90dde50a249..cb16337b238 100644 --- a/src/Components/Common/FilePreviewDialog.tsx +++ b/src/Components/Common/FilePreviewDialog.tsx @@ -192,7 +192,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { /> ) : (