diff --git a/.env b/.env
index fcabed6db34..38fd950b0d8 100644
--- a/.env
+++ b/.env
@@ -1,11 +1,10 @@
# Whitelabelling envs
-REACT_APP_TITLE="CARE"
-REACT_APP_META_DESCRIPTION="CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations."
+REACT_APP_TITLE=CARE
+REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations.
REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg
REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg
-REACT_APP_CONFIG=""
-REACT_PUBLIC_URL="https://care.coronasafe.in"
+REACT_PUBLIC_URL=https://care.coronasafe.in
# Dev envs
ESLINT_NO_DEV_ERRORS=true
diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml
index 4f23fbe4937..f04045d0c72 100644
--- a/.github/workflows/cypress.yaml
+++ b/.github/workflows/cypress.yaml
@@ -14,6 +14,10 @@ jobs:
permissions: write-all
if: github.repository == 'coronasafe/care_fe'
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ containers: [1,2,3,4,5,6,7,8]
steps:
- name: Checkout 📥
uses: actions/checkout@v3
@@ -24,13 +28,22 @@ jobs:
repository: coronasafe/care
path: care
- - name: Run docker compose up on care 🐳
- run: cd care && touch .env && make docker_config_file=docker-compose.pre-built.yaml up && cd .. && sleep 60s
- # Voluntarily kept 60 seconds delay to wait for migrations to complete.
+ - name: Start care docker containers 🐳
+ run: |
+ cd care
+ make docker_config_file=docker-compose.pre-built.yaml up
+ while docker compose exec backend bash -c "python manage.py showmigrations 2>/dev/null | cat | grep -q '\[ \]'"; do
+ >&2 echo "Migrations are not yet applied - sleeping"
+ sleep 5
+ done
+ echo "Migrations are applied"
+ cd ..
- - name: Run Django collectstatic and load dummy data on care 🐍
+ - name: Load dummy data into care backend 📂
run: |
- docker exec care python manage.py load_dummy_data
+ cd care
+ docker compose exec backend bash -c "python manage.py load_dummy_data"
+ cd ..
- name: Check care is up ♻
run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000
@@ -51,39 +64,14 @@ jobs:
wait-on-timeout: 300
browser: chrome
record: true
+ parallel: true
env:
CARE_API: http://localhost:9000
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_OPTIONS: --max_old_space_size=4096
-
- - name: Remove cypress passed label on failure 🏷️
- uses: actions-ecosystem/action-remove-labels@v1
- if: failure()
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- labels: cypress passed
-
- - name: Add cypress passed label on success 🏷️
- uses: actions-ecosystem/action-add-labels@v1
- if: success()
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- labels: cypress passed
-
- - name: Remove cypress failed label on success 🏷️
- uses: actions-ecosystem/action-remove-labels@v1
- if: success()
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- labels: cypress failed
-
- - name: Add cypress failed label on failure 🏷️
- uses: actions-ecosystem/action-add-labels@v1
- if: failure()
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- labels: cypress failed
+ COMMIT_INFO_MESSAGE: ${{github.event.pull_request.title}}
+ COMMIT_INFO_SHA: ${{github.event.pull_request.head.sha}}
- name: Upload cypress screenshots on failure 📸
uses: actions/upload-artifact@v3
@@ -92,10 +80,3 @@ jobs:
name: cypress-screenshots
path: cypress/screenshots
- # Test run video was always captured, so this action uses "always()" condition
- - name: Upload cypress videos 📹
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: cypress-videos
- path: cypress/videos
diff --git a/.gitignore b/.gitignore
index b97356ed20f..4ca589aab39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@ public/build-meta.json
# Using NPM
yarn.lock
pnpm-lock.yaml
+bun.lockb
# Cypress
cypress/downloads
diff --git a/cypress.config.ts b/cypress.config.ts
index 014eb1f4d07..66915f8a11b 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -19,7 +19,6 @@ export default defineConfig({
return null;
},
});
-
return config;
},
baseUrl: "http://localhost:4000",
diff --git a/cypress/e2e/assets_spec/asset_homepage.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts
new file mode 100644
index 00000000000..7b002a7c9bd
--- /dev/null
+++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts
@@ -0,0 +1,113 @@
+///
+
+import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress";
+import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
+import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan";
+import { AssetPagination } from "../../pageobject/Asset/AssetPagination";
+import { AssetFilters } from "../../pageobject/Asset/AssetFilters";
+import { AssetPage } from "../../pageobject/Asset/AssetCreation";
+import LoginPage from "../../pageobject/Login/LoginPage";
+import { v4 as uuidv4 } from "uuid";
+
+describe("Asset Tab", () => {
+ const assetSearchPage = new AssetSearchPage();
+ const assetQRScanPage = new AssetQRScanPage();
+ const assetPagination = new AssetPagination();
+ const assetFilters = new AssetFilters();
+ const assetPage = new AssetPage();
+ const loginPage = new LoginPage();
+ const assetName = "Dummy Camera 10";
+ const qrCode = uuidv4();
+ const serialNumber = Math.floor(Math.random() * 10 ** 10).toString();
+
+ before(() => {
+ loginPage.loginAsDisctrictAdmin();
+ cy.saveLocalStorage();
+ });
+
+ beforeEach(() => {
+ cy.restoreLocalStorage();
+ cy.awaitUrl("/assets");
+ });
+
+ // search for a element
+
+ it("Search Asset Name/QR_ID/Serial_number", () => {
+ assetSearchPage.typeSearchKeyword(assetName);
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyBadgeContent(assetName);
+ assetSearchPage.clickAssetByName(assetName);
+ assetSearchPage.clickUpdateButton();
+ assetSearchPage.clearAndTypeQRCode(qrCode);
+ assetSearchPage.clearAndTypeSerialNumber(serialNumber);
+ assetSearchPage.clickAssetSubmitButton();
+ assetSearchPage.visitAssetsPage();
+ assetSearchPage.typeSearchKeyword(qrCode);
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(qrCode);
+ assetSearchPage.typeSearchKeyword(serialNumber);
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(serialNumber);
+ });
+
+ // scan a asset qr code
+
+ it("Scan Asset QR", () => {
+ assetQRScanPage.scanAssetQR();
+ });
+
+ // filter the asset and verify the badges are there
+
+ it("Filter Asset", () => {
+ assetFilters.filterAssets(
+ "Dummy Facility 1",
+ "INTERNAL",
+ "ACTIVE",
+ "ONVIF Camera",
+ "Camera Loc"
+ );
+ assetFilters.clickadvancefilter();
+ assetFilters.clickslideoverbackbutton(); // to verify the back button doesn't clear applied filters
+ assetFilters.assertFacilityText("Dummy Facility 1");
+ assetFilters.assertAssetTypeText("INTERNAL");
+ assetFilters.assertAssetClassText("ONVIF");
+ assetFilters.assertStatusText("ACTIVE");
+ assetFilters.assertLocationText("Camera Locations");
+ assetFilters.clickadvancefilter();
+ assetFilters.clearFilters();
+ });
+
+ // Verify the pagination in the page
+
+ it("Next/Previous Page", () => {
+ assetPagination.navigateToNextPage();
+ assetPagination.navigateToPreviousPage();
+ });
+
+ it("Import new asset", () => {
+ assetPage.selectassetimportbutton();
+ assetPage.selectImportOption();
+ assetPage.selectImportFacility("Dummy Facility 1");
+ assetPage.importAssetFile();
+ assetPage.selectImportLocation("Camera Locations");
+ assetPage.clickImportAsset();
+ assetPage.verifySuccessNotification("Assets imported successfully");
+ });
+
+ it("verify imported asset", () => {
+ assetSearchPage.typeSearchKeyword("New Test Asset");
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetIsPresent("New Test Asset");
+ });
+
+ it("Export asset", () => {
+ assetPage.selectassetimportbutton();
+ assetPage.selectjsonexportbutton();
+ assetPage.selectcsvexportbutton();
+ });
+
+ afterEach(() => {
+ cy.saveLocalStorage();
+ });
+});
diff --git a/cypress/e2e/assets_spec/asset_tab.cy.ts b/cypress/e2e/assets_spec/asset_tab.cy.ts
deleted file mode 100644
index 0abf11fcaf0..00000000000
--- a/cypress/e2e/assets_spec/asset_tab.cy.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-///
-
-import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress";
-import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
-import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan";
-import { AssetPagination } from "../../pageobject/Asset/AssetPagination";
-import { AssetFilters } from "../../pageobject/Asset/AssetFilters";
-
-describe("Asset Tab", () => {
- const assetSearchPage = new AssetSearchPage();
- const assetQRScanPage = new AssetQRScanPage();
- const assetPagination = new AssetPagination();
- const assetFilters = new AssetFilters();
-
- before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
- cy.saveLocalStorage();
- });
-
- beforeEach(() => {
- cy.restoreLocalStorage();
- cy.awaitUrl("/assets");
- });
-
- // search for a element
-
- it("Search Asset Name", () => {
- const initialUrl = cy.url();
- assetSearchPage.typeSearchKeyword("dummy camera 30");
- assetSearchPage.pressEnter();
- assetSearchPage.verifyUrlChanged(initialUrl);
- });
-
- // scan a asset qr code
-
- it("Scan Asset QR", () => {
- assetQRScanPage.scanAssetQR();
- });
-
- // filter the asset and verify the badges are there
-
- it("Filter Asset", () => {
- assetFilters.filterAssets(
- "Dummy Facility 1",
- "INTERNAL",
- "ACTIVE",
- "ONVIF Camera"
- );
- });
-
- // Verify the pagination in the page
-
- it("Next/Previous Page", () => {
- assetPagination.navigateToNextPage();
- assetPagination.navigateToPreviousPage();
- });
-
- afterEach(() => {
- cy.saveLocalStorage();
- });
-});
diff --git a/cypress/e2e/assets_spec/assets_creation.cy.ts b/cypress/e2e/assets_spec/assets_creation.cy.ts
new file mode 100644
index 00000000000..3e7b24eeca6
--- /dev/null
+++ b/cypress/e2e/assets_spec/assets_creation.cy.ts
@@ -0,0 +1,178 @@
+///
+import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress";
+import { AssetPage } from "../../pageobject/Asset/AssetCreation";
+import { v4 as uuidv4 } from "uuid";
+import LoginPage from "../../pageobject/Login/LoginPage";
+import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
+
+describe("Asset", () => {
+ const assetPage = new AssetPage();
+ const assetSearchPage = new AssetSearchPage();
+ const loginPage = new LoginPage();
+ const phone_number = "9999999999";
+ const serialNumber = Math.floor(Math.random() * 10 ** 10).toString();
+
+ before(() => {
+ loginPage.loginAsDisctrictAdmin();
+ cy.saveLocalStorage();
+ });
+
+ beforeEach(() => {
+ cy.restoreLocalStorage();
+ cy.awaitUrl("/assets");
+ });
+
+ it("Verify asset creation fields throws error if empty", () => {
+ assetPage.createAsset();
+ assetPage.selectFacility("Dummy Facility 1");
+ assetPage.clickCreateAsset();
+
+ assetPage.verifyEmptyAssetNameError();
+ assetPage.verifyEmptyAssetTypeError();
+ assetPage.verifyEmptyLocationError();
+ assetPage.verifyEmptyStatusError();
+ assetPage.verifyEmptyPhoneError();
+ });
+
+ //Create an asset
+
+ it("Create an Asset", () => {
+ assetPage.createAsset();
+ assetPage.selectFacility("Dummy Facility 1");
+ assetPage.selectLocation("Camera Loc");
+ assetPage.selectAssetType("Internal");
+ assetPage.selectAssetClass("ONVIF Camera");
+
+ const qr_id_1 = uuidv4();
+
+ assetPage.enterAssetDetails(
+ "New Test Asset 1",
+ "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.clickCreateAddMore();
+ assetPage.verifySuccessNotification("Asset created successfully");
+
+ const qr_id_2 = uuidv4();
+
+ assetPage.selectLocation("Camera Loc");
+ assetPage.selectAssetType("Internal");
+ assetPage.selectAssetClass("ONVIF Camera");
+ assetPage.enterAssetDetails(
+ "New Test Asset 2",
+ "Test Description",
+ "Working",
+ qr_id_2,
+ "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.interceptAssetCreation();
+ assetPage.clickCreateAsset();
+ assetPage.verifyAssetCreation();
+ assetPage.verifySuccessNotification("Asset created successfully");
+
+ assetSearchPage.typeSearchKeyword("New Test Asset 2");
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetIsPresent("New Test Asset 2");
+ });
+
+ it("Edit an Asset", () => {
+ assetPage.openCreatedAsset();
+
+ const qr_id = uuidv4();
+
+ assetPage.editAssetDetails(
+ "New Test Asset Edited",
+ "Test Description Edited",
+ qr_id,
+ "Manufacturer's Name Edited",
+ "Customer Support's Name Edited",
+ "Vendor's Name Edited",
+ "Test note for asset creation edited!",
+ "25122021"
+ );
+
+ assetPage.clickUpdateAsset();
+
+ assetPage.verifySuccessNotification("Asset updated successfully");
+ });
+
+ it("Verify Editted Asset", () => {
+ assetSearchPage.typeSearchKeyword("New Test Asset Edited");
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetIsPresent("New Test Asset Edited");
+ });
+
+ it("Configure an asset", () => {
+ assetPage.openCreatedAsset();
+ assetPage.spyAssetConfigureApi();
+ assetPage.configureAsset(
+ "Host name",
+ "192.168.1.64",
+ "remote_user",
+ "2jCkrCRSeahzKEU",
+ "d5694af2-21e2-4a39-9bad-2fb98d9818bd"
+ );
+ assetPage.clickConfigureAsset();
+ assetPage.verifyAssetConfiguration(200);
+ });
+
+ it("Add an vital monitor asset and configure it", () => {
+ assetPage.createAsset();
+ assetPage.selectFacility("Dummy Facility 1");
+ assetPage.selectLocation("Camera Loc");
+ assetPage.selectAssetType("Internal");
+ assetPage.selectAssetClass("HL7 Vitals Monitor");
+
+ const qr_id_1 = uuidv4();
+
+ assetPage.enterAssetDetails(
+ "New Test Asset Vital",
+ "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.interceptAssetCreation();
+ assetPage.clickCreateAsset();
+ assetPage.verifyAssetCreation();
+
+ assetSearchPage.typeSearchKeyword("New Test Asset Vital");
+ assetSearchPage.pressEnter();
+
+ assetPage.openCreatedAsset();
+ assetPage.configureVitalAsset("Host name", "192.168.1.64");
+ assetPage.clickConfigureVital();
+ });
+
+ afterEach(() => {
+ cy.saveLocalStorage();
+ });
+});
diff --git a/cypress/e2e/assets_spec/assets_manage.cy.ts b/cypress/e2e/assets_spec/assets_manage.cy.ts
index af3efb917dd..f27fd302a0c 100644
--- a/cypress/e2e/assets_spec/assets_manage.cy.ts
+++ b/cypress/e2e/assets_spec/assets_manage.cy.ts
@@ -1,16 +1,10 @@
-///
import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress";
import { AssetPage } from "../../pageobject/Asset/AssetCreation";
-import { v4 as uuidv4 } from "uuid";
import LoginPage from "../../pageobject/Login/LoginPage";
-import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
describe("Asset", () => {
const assetPage = new AssetPage();
- const assetSearchPage = new AssetSearchPage();
const loginPage = new LoginPage();
- const phone_number = "9999999999";
- const serialNumber = Math.floor(Math.random() * 10 ** 10).toString();
before(() => {
loginPage.loginAsDisctrictAdmin();
@@ -22,150 +16,6 @@ describe("Asset", () => {
cy.awaitUrl("/assets");
});
- it("Verify asset creation fields throws error if empty", () => {
- assetPage.createAsset();
- assetPage.selectFacility("Dummy Facility 1");
- assetPage.clickCreateAsset();
-
- assetPage.verifyEmptyAssetNameError();
- assetPage.verifyEmptyAssetTypeError();
- assetPage.verifyEmptyLocationError();
- assetPage.verifyEmptyStatusError();
- assetPage.verifyEmptyPhoneError();
- });
-
- //Create an asset
-
- it("Create an Asset", () => {
- assetPage.createAsset();
- assetPage.selectFacility("Dummy Facility 1");
- assetPage.selectLocation("Camera Loc");
- assetPage.selectAssetType("Internal");
- assetPage.selectAssetClass("ONVIF Camera");
-
- const qr_id_1 = uuidv4();
-
- assetPage.enterAssetDetails(
- "New Test Asset 1",
- "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.clickCreateAddMore();
- assetPage.verifySuccessNotification("Asset created successfully");
-
- const qr_id_2 = uuidv4();
-
- assetPage.selectLocation("Camera Loc");
- assetPage.selectAssetType("Internal");
- assetPage.selectAssetClass("ONVIF Camera");
- assetPage.enterAssetDetails(
- "New Test Asset 2",
- "Test Description",
- "Working",
- qr_id_2,
- "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.interceptAssetCreation();
- assetPage.clickCreateAsset();
- assetPage.verifyAssetCreation();
- assetPage.verifySuccessNotification("Asset created successfully");
-
- assetSearchPage.typeSearchKeyword("New Test Asset 2");
- assetSearchPage.pressEnter();
- assetSearchPage.verifyAssetIsPresent("New Test Asset 2");
- });
-
- it("Edit an Asset", () => {
- assetPage.openCreatedAsset();
-
- const qr_id = uuidv4();
-
- assetPage.editAssetDetails(
- "New Test Asset Edited",
- "Test Description Edited",
- qr_id,
- "Manufacturer's Name Edited",
- "Customer Support's Name Edited",
- "Vendor's Name Edited",
- "Test note for asset creation edited!",
- "25122021"
- );
-
- assetPage.clickUpdateAsset();
-
- assetPage.verifySuccessNotification("Asset updated successfully");
- });
-
- it("Configure an asset", () => {
- assetPage.openCreatedAsset();
- assetPage.spyAssetConfigureApi();
- assetPage.configureAsset(
- "Host name",
- "192.168.1.64",
- "remote_user",
- "2jCkrCRSeahzKEU",
- "d5694af2-21e2-4a39-9bad-2fb98d9818bd"
- );
- assetPage.clickConfigureAsset();
- assetPage.verifyAssetConfiguration(200);
- });
-
- it("Add an vital monitor asset and configure it", () => {
- assetPage.createAsset();
- assetPage.selectFacility("Dummy Facility 1");
- assetPage.selectLocation("Camera Loc");
- assetPage.selectAssetType("Internal");
- assetPage.selectAssetClass("HL7 Vitals Monitor");
-
- const qr_id_1 = uuidv4();
-
- assetPage.enterAssetDetails(
- "New Test Asset Vital",
- "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.interceptAssetCreation();
- assetPage.clickCreateAsset();
- assetPage.verifyAssetCreation();
-
- assetSearchPage.typeSearchKeyword("New Test Asset Vital");
- assetSearchPage.pressEnter();
-
- assetPage.openCreatedAsset();
- assetPage.configureVitalAsset("Host name", "192.168.1.64");
- assetPage.clickConfigureVital();
- });
-
it("Delete an Asset", () => {
assetPage.openCreatedAsset();
assetPage.interceptDeleteAssetApi();
@@ -173,16 +23,6 @@ describe("Asset", () => {
assetPage.verifyDeleteStatus();
});
- it("Import new asset", () => {
- assetPage.selectImportOption();
- assetPage.selectImportFacility("Dummy Facility 1");
- assetPage.importAssetFile();
- assetPage.selectImportLocation("Camera Locations");
- assetPage.clickImportAsset();
-
- assetPage.verifySuccessNotification("Assets imported successfully");
- });
-
afterEach(() => {
cy.saveLocalStorage();
});
diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts
index f8fe04b4f30..f2ff847c9d9 100644
--- a/cypress/e2e/facility_spec/facility.cy.ts
+++ b/cypress/e2e/facility_spec/facility.cy.ts
@@ -7,7 +7,6 @@ describe("Facility Creation", () => {
let facilityUrl: string;
const facilityPage = new FacilityPage();
const loginPage = new LoginPage();
- const phone_number = "9999999999";
before(() => {
loginPage.loginAsDisctrictAdmin();
@@ -40,14 +39,9 @@ describe("Facility Creation", () => {
facilityPage.selectAreaOfSpecialization("General Medicine");
facilityPage.fillDoctorCount("5");
facilityPage.saveAndExitDoctorForm();
-
- cy.url().then((initialUrl) => {
- cy.get("button#save-and-exit").should("not.exist");
- cy.url()
- .should("not.equal", initialUrl)
- .then((newUrl) => {
- facilityUrl = newUrl;
- });
+ facilityPage.verifyfacilitynewurl();
+ cy.url().then((newUrl) => {
+ facilityUrl = newUrl;
});
});
@@ -75,24 +69,6 @@ describe("Facility Creation", () => {
facilityPage.verifySuccessNotification("Facility updated successfully");
});
- it("Create a resource request", () => {
- facilityPage.visitUpdateFacilityPage(facilityUrl);
- facilityPage.clickManageFacilityDropdown();
- facilityPage.clickResourceRequestOption();
- facilityPage.fillResourceRequestDetails(
- "Test User",
- phone_number,
- "cypress",
- "Test title",
- "10",
- "Test description"
- );
- facilityPage.clickSubmitRequestButton();
- facilityPage.verifySuccessNotification(
- "Resource request created successfully"
- );
- });
-
it("Delete a facility", () => {
facilityPage.visitUpdateFacilityPage(facilityUrl);
facilityPage.clickManageFacilityDropdown();
diff --git a/cypress/e2e/resource_spec/resources.cy.ts b/cypress/e2e/resource_spec/resources.cy.ts
index b3393d0942b..acf179db120 100644
--- a/cypress/e2e/resource_spec/resources.cy.ts
+++ b/cypress/e2e/resource_spec/resources.cy.ts
@@ -1,10 +1,14 @@
import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress";
import LoginPage from "../../pageobject/Login/LoginPage";
import ResourcePage from "../../pageobject/Resource/ResourcePage";
+import FacilityPage from "../../pageobject/Facility/FacilityCreation";
describe("Resource Page", () => {
+ let createdResource: string;
const loginPage = new LoginPage();
const resourcePage = new ResourcePage();
+ const facilityPage = new FacilityPage();
+ const phone_number = "9999999999";
before(() => {
loginPage.loginAsDisctrictAdmin();
@@ -34,8 +38,34 @@ describe("Resource Page", () => {
resourcePage.clickBoardViewButton();
});
+ it("Create a resource request", () => {
+ cy.visit("/facility");
+ cy.get("#search").click().type("dummy facility 1");
+ cy.intercept("GET", "**/api/v1/facility/**").as("loadFacilities");
+ cy.get("#facility-details").click();
+ cy.wait("@loadFacilities").its("response.statusCode").should("eq", 200);
+ facilityPage.clickManageFacilityDropdown();
+ facilityPage.clickResourceRequestOption();
+ facilityPage.fillResourceRequestDetails(
+ "Test User",
+ phone_number,
+ "Dummy",
+ "Test title",
+ "10",
+ "Test description"
+ );
+ facilityPage.clickSubmitRequestButton();
+ facilityPage.verifySuccessNotification(
+ "Resource request created successfully"
+ );
+ facilityPage.verifyresourcenewurl();
+ cy.url().then((url) => {
+ createdResource = url;
+ });
+ });
+
it("Update the status of resource", () => {
- resourcePage.openAlreadyCreatedResource();
+ cy.visit(createdResource);
resourcePage.clickUpdateStatus();
resourcePage.updateStatus("APPROVED");
resourcePage.clickSubmitButton();
@@ -45,7 +75,7 @@ describe("Resource Page", () => {
});
it("Post comment for a resource", () => {
- resourcePage.openAlreadyCreatedResource();
+ cy.visit(createdResource);
resourcePage.addCommentForResource("Test comment");
resourcePage.clickPostCommentButton();
resourcePage.verifySuccessNotification("Comment added successfully");
diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts
index fdaf802d4b7..b939c31405f 100644
--- a/cypress/pageobject/Asset/AssetCreation.ts
+++ b/cypress/pageobject/Asset/AssetCreation.ts
@@ -234,8 +234,29 @@ export class AssetPage {
});
}
- selectImportOption() {
+ selectassetimportbutton() {
cy.get("[data-testid=import-asset-button]").click();
+ }
+
+ selectjsonexportbutton() {
+ cy.intercept("GET", "**/api/v1/asset/?json=true**").as("getJsonexport");
+ cy.get("#export-json-option").click();
+ cy.wait("@getJsonexport").then(({ request, response }) => {
+ expect(response.statusCode).to.eq(200);
+ expect(request.url).to.include("json=true");
+ });
+ }
+
+ selectcsvexportbutton() {
+ cy.intercept("GET", "**/api/v1/asset/?csv=true**").as("getCsvexport");
+ cy.get("#export-csv-option").click();
+ cy.wait("@getCsvexport").then(({ request, response }) => {
+ expect(response.statusCode).to.eq(200);
+ expect(request.url).to.include("csv=true");
+ });
+ }
+
+ selectImportOption() {
cy.get(".import-assets-button").click();
}
diff --git a/cypress/pageobject/Asset/AssetFilters.ts b/cypress/pageobject/Asset/AssetFilters.ts
index 57cc893bdb7..a16b61f4fc5 100644
--- a/cypress/pageobject/Asset/AssetFilters.ts
+++ b/cypress/pageobject/Asset/AssetFilters.ts
@@ -3,7 +3,8 @@ export class AssetFilters {
facilityName: string,
assetType: string,
assetStatus: string,
- assetClass: string
+ assetClass: string,
+ assetLocation: string
) {
cy.contains("Advanced Filters").click();
cy.get("input[name='Facilities']")
@@ -27,6 +28,42 @@ export class AssetFilters {
.then(() => {
cy.get("[role='option']").contains(assetClass).click();
});
+ cy.get("#Facilities-location")
+ .click()
+ .type(assetLocation)
+ .then(() => {
+ cy.get("[role='option']").contains(assetLocation).click();
+ });
cy.contains("Apply").click();
}
+ clearFilters() {
+ cy.intercept("GET", "**/api/v1/asset/**").as("clearAssets");
+ cy.get("#clear-filter").click();
+ cy.wait("@clearAssets").its("response.statusCode").should("eq", 200);
+ cy.url().should("match", /\/assets$/);
+ }
+ clickadvancefilter() {
+ cy.intercept("GET", "**/api/v1/getallfacilities/**").as("advancefilter");
+ cy.get("#advanced-filter").click();
+ cy.wait("@advancefilter").its("response.statusCode").should("eq", 200);
+ }
+ clickslideoverbackbutton() {
+ cy.get("#close-slide-over").click();
+ }
+ // Assertions
+ assertFacilityText(text) {
+ cy.get("[data-testid=Facility]").should("contain", text);
+ }
+ assertAssetTypeText(text) {
+ cy.get("[data-testid='Asset Type']").should("contain", text);
+ }
+ assertAssetClassText(text) {
+ cy.get("[data-testid='Asset Class']").should("contain", text);
+ }
+ assertStatusText(text) {
+ cy.get("[data-testid=Status]").should("contain", text);
+ }
+ assertLocationText(text) {
+ cy.get("[data-testid=Location]").should("contain", text);
+ }
}
diff --git a/cypress/pageobject/Asset/AssetSearch.ts b/cypress/pageobject/Asset/AssetSearch.ts
index 315a414a62b..b1ccb2f71c9 100644
--- a/cypress/pageobject/Asset/AssetSearch.ts
+++ b/cypress/pageobject/Asset/AssetSearch.ts
@@ -1,16 +1,24 @@
export class AssetSearchPage {
typeSearchKeyword(keyword: string) {
- cy.get("[name='search']").type(keyword);
+ cy.get("#search").click().clear();
+ cy.get("#search").click().type(keyword);
}
pressEnter() {
cy.get("[name='search']").type("{enter}");
}
- verifyUrlChanged(initialUrl: string) {
- cy.url().should((currentUrl) => {
- expect(currentUrl).not.to.equal(initialUrl);
- });
+ clickAssetByName(assetName: string) {
+ cy.intercept("GET", "**/api/v1/asset/**").as("clearAssets");
+ cy.get("[data-testid='created-asset-list']").contains(assetName).click();
+ cy.wait("@clearAssets").its("response.statusCode").should("eq", 200);
+ }
+
+ verifyBadgeContent(expectedText: string) {
+ cy.get("[data-testid='Name/Serial No./QR ID']").should(
+ "contain",
+ expectedText
+ );
}
verifyAssetIsPresent(assetName: string) {
@@ -18,4 +26,35 @@ export class AssetSearchPage {
.first()
.should("contain", assetName);
}
+
+ clickUpdateButton() {
+ cy.get("[data-testid='asset-update-button']").contains("Update").click();
+ }
+
+ clearAndTypeQRCode(qrCode: string) {
+ cy.get("#qr_code_id").clear();
+ cy.get("#qr_code_id").click().type(qrCode);
+ }
+
+ clearAndTypeSerialNumber(serialNumber: string) {
+ cy.get("#serial-number").clear();
+ cy.get("#serial-number").click().type(serialNumber);
+ }
+
+ clickAssetSubmitButton() {
+ cy.intercept("GET", "**/api/v1/asset/**").as("getAssets");
+ cy.get("#submit").click();
+ cy.wait("@getAssets").its("response.statusCode").should("eq", 200);
+ }
+
+ visitAssetsPage() {
+ cy.visit("/assets");
+ }
+
+ verifyAssetListContains(dummyCameraText: string) {
+ cy.get("[data-testid='created-asset-list']").should(
+ "contain",
+ dummyCameraText
+ );
+ }
}
diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts
index aa46d4f1105..ef4e65781e0 100644
--- a/cypress/pageobject/Facility/FacilityCreation.ts
+++ b/cypress/pageobject/Facility/FacilityCreation.ts
@@ -100,7 +100,9 @@ class FacilityPage {
}
saveAndExitDoctorForm() {
+ cy.intercept("GET", "**/api/v1/facility/**").as("createFacilities");
cy.get("button#save-and-exit").click();
+ cy.wait("@createFacilities").its("response.statusCode").should("eq", 200);
}
clickManageFacilityDropdown() {
@@ -131,6 +133,14 @@ class FacilityPage {
cy.get("#delete-facility").contains("Delete Facility").click();
}
+ verifyfacilitynewurl() {
+ cy.url().should("match", /facility\/[a-z\d-]+/);
+ }
+
+ verifyresourcenewurl() {
+ cy.url().should("match", /resource\/[a-z\d-]+/);
+ }
+
confirmDeleteFacility() {
cy.intercept("DELETE", "**/api/v1/facility/**").as("deleteFacility");
cy.get("#submit").contains("Delete").click();
diff --git a/cypress/pageobject/Resource/ResourcePage.ts b/cypress/pageobject/Resource/ResourcePage.ts
index 1fecf86b9e1..f7feac925bf 100644
--- a/cypress/pageobject/Resource/ResourcePage.ts
+++ b/cypress/pageobject/Resource/ResourcePage.ts
@@ -46,10 +46,6 @@ class ResourcePage {
cy.contains("Board View").click();
}
- openAlreadyCreatedResource() {
- cy.get("[data-testid='resource-details']").first().click();
- }
-
clickUpdateStatus() {
cy.get("[data-testid='update-status']").click();
}
diff --git a/package-lock.json b/package-lock.json
index dda8ac9dd79..db11b8c5220 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11127,9 +11127,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.12.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
- "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+ "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
@@ -13742,8 +13742,6 @@
},
"node_modules/npm/node_modules/@colors/colors": {
"version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
- "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -13754,8 +13752,6 @@
},
"node_modules/npm/node_modules/@isaacs/cliui": {
"version": "8.0.2",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
- "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -13773,8 +13769,6 @@
},
"node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
- "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -13787,16 +13781,12 @@
},
"node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": {
"version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -13814,8 +13804,6 @@
},
"node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14068,8 +14056,6 @@
},
"node_modules/npm/node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14154,8 +14140,6 @@
},
"node_modules/npm/node_modules/agent-base": {
"version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14182,8 +14166,6 @@
},
"node_modules/npm/node_modules/aggregate-error": {
"version": "3.1.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
- "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14197,8 +14179,6 @@
},
"node_modules/npm/node_modules/ansi-regex": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14208,8 +14188,6 @@
},
"node_modules/npm/node_modules/ansi-styles": {
"version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14225,8 +14203,6 @@
},
"node_modules/npm/node_modules/aproba": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
- "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -14252,16 +14228,12 @@
},
"node_modules/npm/node_modules/balanced-match": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/base64-js": {
"version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
@@ -14306,10 +14278,7 @@
},
"node_modules/npm/node_modules/brace-expansion": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
- "inBundle": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
@@ -14385,8 +14354,6 @@
},
"node_modules/npm/node_modules/chownr": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
- "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -14423,8 +14390,6 @@
},
"node_modules/npm/node_modules/clean-stack": {
"version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14462,8 +14427,6 @@
},
"node_modules/npm/node_modules/clone": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
- "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14482,8 +14445,6 @@
},
"node_modules/npm/node_modules/color-convert": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14496,8 +14457,6 @@
},
"node_modules/npm/node_modules/color-name": {
"version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -14532,24 +14491,18 @@
},
"node_modules/npm/node_modules/concat-map": {
"version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/console-control-strings": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
},
"node_modules/npm/node_modules/cross-spawn": {
"version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14581,8 +14534,6 @@
},
"node_modules/npm/node_modules/cssesc": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14595,8 +14546,6 @@
},
"node_modules/npm/node_modules/debug": {
"version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14614,16 +14563,12 @@
},
"node_modules/npm/node_modules/debug/node_modules/ms": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/defaults": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
- "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14636,8 +14581,6 @@
},
"node_modules/npm/node_modules/delegates": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -14662,16 +14605,12 @@
},
"node_modules/npm/node_modules/eastasianwidth": {
"version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/emoji-regex": {
"version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -14712,8 +14651,6 @@
},
"node_modules/npm/node_modules/events": {
"version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14738,8 +14675,6 @@
},
"node_modules/npm/node_modules/foreground-child": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
- "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -14768,16 +14703,12 @@
},
"node_modules/npm/node_modules/fs.realpath": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"inBundle": true,
"license": "ISC"
},
"node_modules/npm/node_modules/function-bind": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -14831,8 +14762,6 @@
},
"node_modules/npm/node_modules/has": {
"version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14845,8 +14774,6 @@
},
"node_modules/npm/node_modules/has-unicode": {
"version": "2.0.1",
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -14885,8 +14812,6 @@
},
"node_modules/npm/node_modules/https-proxy-agent": {
"version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14954,8 +14879,6 @@
},
"node_modules/npm/node_modules/imurmurhash": {
"version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14965,8 +14888,6 @@
},
"node_modules/npm/node_modules/indent-string": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
- "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -14976,8 +14897,6 @@
},
"node_modules/npm/node_modules/inflight": {
"version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -14988,8 +14907,6 @@
},
"node_modules/npm/node_modules/inherits": {
"version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -15023,8 +14940,6 @@
},
"node_modules/npm/node_modules/ip": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
- "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -15064,8 +14979,6 @@
},
"node_modules/npm/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -15081,8 +14994,6 @@
},
"node_modules/npm/node_modules/isexe": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -15349,8 +15260,6 @@
},
"node_modules/npm/node_modules/minimatch": {
"version": "9.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
- "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15364,6 +15273,15 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/npm/node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/npm/node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
@@ -15389,8 +15307,6 @@
},
"node_modules/npm/node_modules/minipass-collect/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15432,8 +15348,6 @@
},
"node_modules/npm/node_modules/minipass-flush/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15456,8 +15370,6 @@
},
"node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15482,8 +15394,6 @@
},
"node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15508,8 +15418,6 @@
},
"node_modules/npm/node_modules/minipass-sized/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15522,8 +15430,6 @@
},
"node_modules/npm/node_modules/minizlib": {
"version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -15537,8 +15443,6 @@
},
"node_modules/npm/node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15551,8 +15455,6 @@
},
"node_modules/npm/node_modules/mkdirp": {
"version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -15633,8 +15535,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": {
"version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -15664,8 +15564,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/glob": {
"version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15686,8 +15584,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/minimatch": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15730,8 +15626,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/readable-stream": {
"version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -15746,8 +15640,6 @@
},
"node_modules/npm/node_modules/node-gyp/node_modules/signal-exit": {
"version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -15938,8 +15830,6 @@
},
"node_modules/npm/node_modules/once": {
"version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15949,8 +15839,6 @@
},
"node_modules/npm/node_modules/p-map": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
- "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16012,8 +15900,6 @@
},
"node_modules/npm/node_modules/path-is-absolute": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16023,8 +15909,6 @@
},
"node_modules/npm/node_modules/path-key": {
"version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16081,8 +15965,6 @@
},
"node_modules/npm/node_modules/process": {
"version": "0.11.10",
- "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
- "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16222,8 +16104,6 @@
},
"node_modules/npm/node_modules/rimraf": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16239,8 +16119,6 @@
},
"node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": {
"version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16251,8 +16129,6 @@
},
"node_modules/npm/node_modules/rimraf/node_modules/glob": {
"version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16273,8 +16149,6 @@
},
"node_modules/npm/node_modules/rimraf/node_modules/minimatch": {
"version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16287,8 +16161,6 @@
},
"node_modules/npm/node_modules/safe-buffer": {
"version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
@@ -16316,8 +16188,6 @@
},
"node_modules/npm/node_modules/semver": {
"version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16333,8 +16203,6 @@
},
"node_modules/npm/node_modules/semver/node_modules/lru-cache": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16347,16 +16215,12 @@
},
"node_modules/npm/node_modules/set-blocking": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true,
"inBundle": true,
"license": "ISC"
},
"node_modules/npm/node_modules/shebang-command": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16369,8 +16233,6 @@
},
"node_modules/npm/node_modules/shebang-regex": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16380,8 +16242,6 @@
},
"node_modules/npm/node_modules/signal-exit": {
"version": "4.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
- "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16449,8 +16309,6 @@
},
"node_modules/npm/node_modules/spdx-correct": {
"version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
"dev": true,
"inBundle": true,
"license": "Apache-2.0",
@@ -16461,16 +16319,12 @@
},
"node_modules/npm/node_modules/spdx-exceptions": {
"version": "2.3.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
- "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true,
"inBundle": true,
"license": "CC-BY-3.0"
},
"node_modules/npm/node_modules/spdx-expression-parse": {
"version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16481,8 +16335,6 @@
},
"node_modules/npm/node_modules/spdx-license-ids": {
"version": "3.0.13",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
- "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
"dev": true,
"inBundle": true,
"license": "CC0-1.0"
@@ -16501,8 +16353,6 @@
},
"node_modules/npm/node_modules/string_decoder": {
"version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16512,8 +16362,6 @@
},
"node_modules/npm/node_modules/string-width": {
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16529,8 +16377,6 @@
"node_modules/npm/node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16545,8 +16391,6 @@
},
"node_modules/npm/node_modules/strip-ansi": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16560,8 +16404,6 @@
"node_modules/npm/node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16605,8 +16447,6 @@
},
"node_modules/npm/node_modules/tar/node_modules/fs-minipass": {
"version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
- "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16619,8 +16459,6 @@
},
"node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16800,8 +16638,6 @@
},
"node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
- "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16814,8 +16650,6 @@
},
"node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16828,16 +16662,12 @@
},
"node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": {
"version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"inBundle": true,
"license": "MIT"
},
"node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": {
"version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16855,8 +16685,6 @@
},
"node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -19114,12 +18942,12 @@
"integrity": "sha512-PdjHqho8+kI7AIC3DlLgD99H8zzHphzpIhyv2skVtWaSJGK819+ZqWMC3mHEtSjlcFoYaLXliNt8sb6Taa2Mpg=="
},
"node_modules/resolve": {
- "version": "1.22.2",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
- "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
+ "version": "1.22.4",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz",
+ "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==",
"dev": true,
"dependencies": {
- "is-core-module": "^2.11.0",
+ "is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
diff --git a/src/CAREUI/interactive/SlideOver.tsx b/src/CAREUI/interactive/SlideOver.tsx
index 3eae13869cb..34e1c615f94 100644
--- a/src/CAREUI/interactive/SlideOver.tsx
+++ b/src/CAREUI/interactive/SlideOver.tsx
@@ -109,6 +109,7 @@ export default function SlideOver({
>
-
+
navigate(`/facility/${facilityId}/location`, {
diff --git a/src/Components/Facility/ConsultationCard.tsx b/src/Components/Facility/ConsultationCard.tsx
index 467729f0f73..f6b4484b477 100644
--- a/src/Components/Facility/ConsultationCard.tsx
+++ b/src/Components/Facility/ConsultationCard.tsx
@@ -5,6 +5,7 @@ import ButtonV2 from "../Common/components/ButtonV2";
import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
import RelativeDateUserMention from "../Common/RelativeDateUserMention";
import useConfig from "../../Common/hooks/useConfig";
+import Chip from "../../CAREUI/display/Chip";
interface ConsultationProps {
itemData: ConsultationModel;
@@ -70,6 +71,15 @@ export const ConsultationCard = (props: ConsultationProps) => {
{formatDateTime(itemData.admission_date)}
+ {itemData.is_readmission && (
+
+ )}
diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx
deleted file mode 100644
index 401f113a812..00000000000
--- a/src/Components/Facility/ConsultationDetails.tsx
+++ /dev/null
@@ -1,1324 +0,0 @@
-import { AssetBedModel, AssetClass, AssetData } from "../Assets/AssetTypes";
-import {
- CONSULTATION_TABS,
- DISCHARGE_REASONS,
- GENDER_TYPES,
- OptionsType,
- SYMPTOM_CHOICES,
-} from "../../Common/constants";
-import {
- BedModel,
- ConsultationModel,
- FacilityModel,
- ICD11DiagnosisModel,
-} from "./models";
-import {
- getConsultation,
- getPatient,
- getPermittedFacility,
- listAssetBeds,
-} from "../../Redux/actions";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-import { lazy, useCallback, useEffect, useState } from "react";
-
-import { ABGPlots } from "./Consultations/ABGPlots";
-import ButtonV2 from "../Common/components/ButtonV2";
-import CareIcon from "../../CAREUI/icons/CareIcon";
-import Chip from "../../CAREUI/display/Chip";
-import { DailyRoundsList } from "./Consultations/DailyRoundsList";
-import { DialysisPlots } from "./Consultations/DialysisPlots";
-import DischargeModal from "./DischargeModal";
-import DischargeSummaryModal from "./DischargeSummaryModal";
-import DoctorVideoSlideover from "./DoctorVideoSlideover";
-import { Feed } from "./Consultations/Feed";
-import { FileUpload } from "../Patient/FileUpload";
-import HL7PatientVitalsMonitor from "../VitalsMonitor/HL7PatientVitalsMonitor";
-import InvestigationTab from "./Investigations/investigationsTab";
-import { make as Link } from "../Common/components/Link.bs";
-import { NeurologicalTable } from "./Consultations/NeurologicalTables";
-import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
-import { NursingPlot } from "./Consultations/NursingPlot";
-import { NutritionPlots } from "./Consultations/NutritionPlots";
-import PatientInfoCard from "../Patient/PatientInfoCard";
-import { PatientModel } from "../Patient/models";
-import PrescriptionsTable from "../Medicine/PrescriptionsTable";
-import { PressureSoreDiagrams } from "./Consultations/PressureSoreDiagrams";
-import { PrimaryParametersPlot } from "./Consultations/PrimaryParametersPlot";
-import ReadMore from "../Common/components/Readmore";
-import VentilatorPatientVitalsMonitor from "../VitalsMonitor/VentilatorPatientVitalsMonitor";
-import { VentilatorPlot } from "./Consultations/VentilatorPlot";
-import { formatDate, formatDateTime, relativeTime } from "../../Utils/utils";
-
-import { navigate } from "raviger";
-import { useDispatch } from "react-redux";
-import { useQueryParams } from "raviger";
-import { useTranslation } from "react-i18next";
-import { triggerGoal } from "../Common/Plausible";
-import useVitalsAspectRatioConfig from "../VitalsMonitor/useVitalsAspectRatioConfig";
-import useAuthUser from "../../Common/hooks/useAuthUser";
-import PrescriptionAdministrationsTable from "../Medicine/PrescriptionAdministrationsTable";
-
-const Loading = lazy(() => import("../Common/Loading"));
-const PageTitle = lazy(() => import("../Common/PageTitle"));
-const symptomChoices = [...SYMPTOM_CHOICES];
-
-export const ConsultationDetails = (props: any) => {
- const { t } = useTranslation();
- const { facilityId, patientId, consultationId } = props;
- const tab = props.tab.toUpperCase();
- const dispatch: any = useDispatch();
- const [isLoading, setIsLoading] = useState(false);
- const [showDoctors, setShowDoctors] = useState(false);
- const [qParams, _] = useQueryParams();
-
- const [consultationData, setConsultationData] = useState(
- {} as ConsultationModel
- );
- const [patientData, setPatientData] = useState({});
- const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
- useState(false);
- const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
- const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
-
- const getPatientGender = (patientData: any) =>
- GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
-
- const getPatientAddress = (patientData: any) =>
- `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
-
- const getPatientComorbidities = (patientData: any) => {
- if (patientData?.medical_history?.length) {
- const medHis = patientData.medical_history;
- return medHis.map((item: any) => item.disease).join(", ");
- } else {
- return "None";
- }
- };
-
- const [hl7SocketUrl, setHL7SocketUrl] = useState();
- const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
- const [monitorBedData, setMonitorBedData] = useState();
- const [ventilatorBedData, setVentilatorBedData] = useState();
- const authUser = useAuthUser();
-
- useEffect(() => {
- if (
- !consultationData.facility ||
- !consultationData.current_bed?.bed_object.id
- )
- return;
-
- const fetchData = async () => {
- const [facilityRes, assetBedRes] = await Promise.all([
- dispatch(getPermittedFacility(consultationData.facility as any)),
- dispatch(
- listAssetBeds({
- facility: consultationData.facility as any,
- bed: consultationData.current_bed?.bed_object.id,
- })
- ),
- ]);
-
- const { middleware_address } = facilityRes.data as FacilityModel;
- const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
-
- const monitorBedData = assetBeds?.find(
- (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
- );
- setMonitorBedData(monitorBedData);
- const assetDataForMonitor = monitorBedData?.asset_object;
- const hl7Meta = assetDataForMonitor?.meta;
- const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
- if (hl7Middleware && hl7Meta?.local_ip_address) {
- setHL7SocketUrl(
- `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
- );
- }
-
- const consultationBedVentilator =
- consultationData?.current_bed?.assets_objects?.find(
- (i) => i.asset_class === AssetClass.VENTILATOR
- );
- let ventilatorBedData;
- if (consultationBedVentilator) {
- ventilatorBedData = {
- asset_object: consultationBedVentilator,
- bed_object: consultationData?.current_bed?.bed_object,
- } as AssetBedModel;
- } else {
- ventilatorBedData = assetBeds?.find(
- (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
- );
- }
- setVentilatorBedData(ventilatorBedData);
- const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
- const ventilatorMiddleware =
- ventilatorMeta?.middleware_hostname || middleware_address;
- if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
- setVentilatorSocketUrl(
- `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
- );
- }
-
- if (
- !(hl7Middleware && hl7Meta?.local_ip_address) &&
- !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
- ) {
- setHL7SocketUrl(undefined);
- setVentilatorSocketUrl(undefined);
- }
- };
-
- fetchData();
- }, [consultationData]);
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const res = await dispatch(getConsultation(consultationId));
- if (!status.aborted) {
- if (res?.data) {
- const data: ConsultationModel = {
- ...res.data,
- symptoms_text: "",
- };
- if (res.data.symptoms?.length) {
- const symptoms = res.data.symptoms
- .filter((symptom: number) => symptom !== 9)
- .map((symptom: number) => {
- const option = symptomChoices.find((i) => i.id === symptom);
- return option ? option.text.toLowerCase() : symptom;
- });
- data.symptoms_text = symptoms.join(", ");
- }
- setConsultationData(data);
- const id = res.data.patient;
- const patientRes = await dispatch(getPatient({ id }));
- if (patientRes?.data) {
- const patientGender = getPatientGender(patientRes.data);
- const patientAddress = getPatientAddress(patientRes.data);
- const patientComorbidities = getPatientComorbidities(
- patientRes.data
- );
- const data = {
- ...patientRes.data,
- gender: patientGender,
- address: patientAddress,
- comorbidities: patientComorbidities,
- is_declared_positive: patientRes.data.is_declared_positive
- ? "Yes"
- : "No",
- is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
- };
- setPatientData(data);
- }
- } else {
- navigate("/not-found");
- }
- setIsLoading(false);
- }
- },
- [consultationId, dispatch, patientData.is_vaccinated]
- );
-
- useAbortableEffect((status: statusType) => {
- fetchData(status);
- triggerGoal("Patient Consultation Viewed", {
- facilityId: facilityId,
- consultationId: consultationId,
- userId: authUser.id,
- });
- }, []);
-
- const vitals = useVitalsAspectRatioConfig({
- default: undefined,
- md: 8 / 11,
- lg: 15 / 11,
- xl: 13 / 11,
- "2xl": 19 / 11,
- "3xl": 23 / 11,
- });
-
- if (isLoading) {
- return ;
- }
-
- const tabButtonClasses = (selected: boolean) =>
- `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
- selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
- }`;
-
- const ShowDiagnosis = ({
- diagnoses = [],
- label = "Diagnosis",
- nshow = 2,
- }: {
- diagnoses: ICD11DiagnosisModel[] | undefined;
- label: string;
- nshow?: number;
- }) => {
- const [showMore, setShowMore] = useState(false);
-
- return diagnoses.length ? (
-
- ) : null;
- };
-
- return (
-
-
setOpenDischargeSummaryDialog(false)}
- />
-
- setOpenDischargeDialog(false)}
- consultationData={consultationData}
- />
-
-
-
-
-
-
-
-
- {consultationData.admitted_to && (
-
-
- Patient
- {consultationData.discharge_date
- ? " Discharged from"
- : " Admitted to"}
-
- {consultationData.admitted_to}
-
-
- {(consultationData.admission_date ??
- consultationData.discharge_date) && (
-
- {relativeTime(
- consultationData.discharge_date
- ? consultationData.discharge_date
- : consultationData.admission_date
- )}
-
- )}
-
- {consultationData.admission_date &&
- formatDateTime(consultationData.admission_date)}
- {consultationData.discharge_date &&
- ` - ${formatDateTime(consultationData.discharge_date)}`}
-
-
- )}
-
-
-
-
- {/*consultationData.other_symptoms && (
-
-
- Other Symptoms:{" "}
-
- {consultationData.other_symptoms}
-
- )*/}
-
- {consultationData.icd11_principal_diagnosis && (
-
- d.id === consultationData.icd11_principal_diagnosis
- )!,
- ]}
- />
- )}
-
-
-
-
-
- {(consultationData.verified_by_object ||
- consultationData.deprecated_verified_by) && (
-
-
- Verified By:{" "}
-
- {consultationData.verified_by_object
- ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
- : consultationData.deprecated_verified_by}
-
-
- )}
-
-
- setOpenDischargeSummaryDialog(true)}>
-
- {t("discharge_summary")}
-
-
- setOpenDischargeDialog(true)}
- disabled={!!consultationData.discharge_date}
- >
-
- {t("discharge_from_care")}
-
-
-
-
-
-
- Created:
- {consultationData.created_date
- ? formatDateTime(consultationData.created_date)
- : "--:--"}{" "}
- |
-
- {consultationData.created_by && (
-
- {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
- {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
-
- )}
-
-
-
- Last Modified:
- {consultationData.modified_date
- ? formatDateTime(consultationData.modified_date)
- : "--:--"}{" "}
- |
-
- {consultationData.last_edited_by && (
-
- {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
- {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
- {tab === "UPDATES" && (
-
- {!consultationData.discharge_date &&
- hl7SocketUrl &&
- ventilatorSocketUrl && (
-
- )}
-
-
-
-
- {!consultationData.discharge_date &&
- ((hl7SocketUrl && !ventilatorSocketUrl) ||
- (!hl7SocketUrl && ventilatorSocketUrl)) && (
-
- {(hl7SocketUrl || ventilatorSocketUrl) && (
-
- {hl7SocketUrl && (
-
-
-
- )}
- {ventilatorSocketUrl && (
-
-
-
- )}
-
- )}
-
- )}
- {consultationData.discharge_date && (
-
-
-
- Discharge Information
-
-
-
- Reason {" - "}
-
- {DISCHARGE_REASONS.find(
- (d) =>
- d.id === consultationData.discharge_reason
- )?.text ?? "--"}
-
-
- {consultationData.discharge_reason === "REF" && (
-
- Referred Facility {" - "}
-
- {consultationData.referred_to_external ||
- consultationData.referred_to_object?.name ||
- "--"}
-
-
- )}
- {consultationData.discharge_reason === "REC" && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Advice {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
-
-
-
- )}
- {consultationData.discharge_reason === "EXP" && (
-
-
- Date of Death {" - "}
-
- {consultationData.death_datetime
- ? formatDateTime(
- consultationData.death_datetime
- )
- : "--:--"}
-
-
-
- Cause of death {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- Confirmed By {" - "}
-
- {consultationData.death_confirmed_doctor ??
- "--"}
-
-
-
- )}
- {["REF", "LAMA"].includes(
- consultationData.discharge_reason ?? ""
- ) && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Notes {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- )}
-
-
-
- )}
- {consultationData.symptoms_text && (
-
-
-
- Symptoms
-
-
-
- Last Daily Update
-
- {consultationData.last_daily_round
- ?.additional_symptoms && (
- <>
-
- {consultationData.last_daily_round?.additional_symptoms.map(
- (symptom: any, index: number) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.last_daily_round
- ?.other_symptoms && (
-
-
- Other Symptoms:
-
- {
- consultationData.last_daily_round
- ?.other_symptoms
- }
-
- )}
-
- from{" "}
- {formatDate(
- consultationData.last_daily_round.created_at
- )}
-
- >
- )}
-
-
- Consultation Update
-
-
- {consultationData.symptoms?.map(
- (symptom, index) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.other_symptoms && (
-
-
- Other Symptoms:
-
- {consultationData.other_symptoms}
-
- )}
-
- from{" "}
- {consultationData.symptoms_onset_date
- ? formatDate(consultationData.symptoms_onset_date)
- : "--/--/----"}
-
-
-
-
- )}
-
- {consultationData.history_of_present_illness && (
-
-
-
- History of Present Illness
-
-
-
-
-
-
- )}
-
- {consultationData.examination_details && (
-
-
-
- Examination details and Clinical conditions:{" "}
-
-
-
-
-
-
- )}
- {consultationData.treatment_plan && (
-
-
-
- Treatment Summary
-
-
-
-
-
-
- )}
- {consultationData.consultation_notes && (
-
-
-
- General Instructions
-
-
-
-
-
-
- )}
-
- {(consultationData.operation ??
- consultationData.special_instruction) && (
-
-
-
- Notes
-
-
- {consultationData.operation && (
-
-
Operation
-
-
- )}
-
- {consultationData.special_instruction && (
-
-
Special Instruction
-
-
- )}
-
-
-
- )}
-
- {consultationData.procedure &&
- consultationData.procedure.length > 0 && (
-
-
-
-
-
-
- Procedure
- |
-
- Notes
- |
-
- Repetitive
- |
-
- Time / Frequency
- |
-
-
-
- {consultationData.procedure?.map(
- (procedure, index) => (
-
-
- {procedure.procedure}
- |
-
- {procedure.notes}
- |
-
- {procedure.repetitive ? "Yes" : "No"}
- |
-
- {procedure.repetitive
- ? procedure.frequency
- : formatDateTime(String(procedure.time))}
- |
-
- )
- )}
-
-
-
-
- )}
- {consultationData.intubation_start_date && (
-
-
-
- Date/Size/LL:{" "}
-
-
-
- Intubation Date{" - "}
-
- {formatDateTime(
- consultationData.intubation_start_date
- )}
-
-
-
- Extubation Date{" - "}
-
- {consultationData.intubation_end_date &&
- formatDateTime(
- consultationData.intubation_end_date
- )}
-
-
-
- ETT/TT (mmid){" - "}
-
- {consultationData.ett_tt}
-
-
-
- Cuff Pressure (mmhg){" - "}
-
- {consultationData.cuff_pressure}
-
-
-
-
-
- )}
-
- {consultationData.lines?.length > 0 && (
-
-
-
- Lines and Catheters
-
-
- {consultationData.lines?.map(
- (line: any, idx: number) => (
-
-
{line.type}
-
- Details:
-
- {line.other_type}
-
-
- Insertion Date:{" "}
-
- {formatDateTime(line.start_date)}
-
-
-
- Site/Level of Fixation:
-
- {line.site}
-
-
-
- )
- )}
-
-
-
- )}
-
-
-
-
- Body Details
-
-
-
- Gender {" - "}
-
- {patientData.gender ?? "-"}
-
-
-
- Age {" - "}
-
- {patientData.age ?? "-"}
-
-
-
- Weight {" - "}
-
- {consultationData.weight ?? "-"} Kg
-
-
-
- Height {" - "}
-
- {consultationData.height ?? "-"} cm
-
-
-
- Body Surface Area {" - "}
-
- {Math.sqrt(
- (Number(consultationData.weight) *
- Number(consultationData.height)) /
- 3600
- ).toFixed(2)}{" "}
- m2
-
-
-
- Blood Group {" - "}
-
- {patientData.blood_group ?? "-"}
-
-
-
-
-
-
-
-
-
-
- setShowAutomatedRounds((s) => !s)}
- />
-
-
-
-
-
-
-
- )}
- {tab === "FEED" && (
-
- )}
- {tab === "SUMMARY" && (
-
- )}
- {tab === "MEDICINES" && (
-
- )}
- {tab === "FILES" && (
-
-
-
- )}
-
- {tab === "ABG" && (
-
- )}
- {tab === "NURSING" && (
-
- )}
- {tab === "NEUROLOGICAL_MONITORING" && (
-
- )}
- {tab === "VENTILATOR" && (
-
- )}
- {tab === "NUTRITION" && (
-
- )}
- {tab === "PRESSURE_SORE" && (
-
- )}
- {tab === "DIALYSIS" && (
-
- )}
- {tab === "INVESTIGATIONS" && (
-
-
-
-
-
- navigate(
- `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/investigation/`
- )
- }
- >
-
- {t("log_lab_results")}
-
-
-
-
-
- )}
-
-
-
-
- );
-};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
new file mode 100644
index 00000000000..cf0ce7bd6af
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { ABGPlots } from "../Consultations/ABGPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationABGTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
new file mode 100644
index 00000000000..c3515a80953
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { DialysisPlots } from "../Consultations/DialysisPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationDialysisTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
new file mode 100644
index 00000000000..f4fbc08331c
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { Feed } from "../Consultations/Feed";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationFeedTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
new file mode 100644
index 00000000000..754d4bf7b86
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
@@ -0,0 +1,18 @@
+import { ConsultationTabProps } from "./index";
+import { FileUpload } from "../../Patient/FileUpload";
+
+export const ConsultationFilesTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
new file mode 100644
index 00000000000..69b11a30ed5
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
@@ -0,0 +1,39 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NonReadOnlyUsers } from "../../../Utils/AuthorizeFor";
+import ButtonV2 from "../../Common/components/ButtonV2";
+import { navigate } from "raviger";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import InvestigationTab from "../Investigations/investigationsTab";
+import { t } from "i18next";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+export const ConsultationInvestigationsTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+
+
+ navigate(
+ `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}/investigation/`
+ )
+ }
+ >
+
+ {t("log_lab_results")}
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
new file mode 100644
index 00000000000..19810102833
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
@@ -0,0 +1,19 @@
+import { ConsultationTabProps } from "./index";
+import PrescriptionAdministrationsTable from "../../Medicine/PrescriptionAdministrationsTable";
+
+export const ConsultationMedicinesTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
new file mode 100644
index 00000000000..85f34f0ec63
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
@@ -0,0 +1,24 @@
+import { lazy } from "react";
+import { NeurologicalTable } from "../Consultations/NeurologicalTables";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeurologicalMonitoringTab = (
+ props: ConsultationTabProps
+) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
new file mode 100644
index 00000000000..69f130aca0d
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NutritionPlots } from "../Consultations/NutritionPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeutritionTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
new file mode 100644
index 00000000000..721ee18d13e
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NursingPlot } from "../Consultations/NursingPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNursingTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
new file mode 100644
index 00000000000..05327ca6870
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PressureSoreDiagrams } from "../Consultations/PressureSoreDiagrams";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationPressureSoreTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
new file mode 100644
index 00000000000..69d20dd64c9
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PrimaryParametersPlot } from "../Consultations/PrimaryParametersPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationSummaryTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
new file mode 100644
index 00000000000..705b3d9479a
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
@@ -0,0 +1,690 @@
+import { lazy, useEffect, useState } from "react";
+import { ConsultationTabProps } from "./index";
+import { AssetBedModel, AssetClass, AssetData } from "../../Assets/AssetTypes";
+import { useDispatch } from "react-redux";
+import { getPermittedFacility, listAssetBeds } from "../../../Redux/actions";
+import { BedModel, FacilityModel } from "../models";
+import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor";
+import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor";
+import useVitalsAspectRatioConfig from "../../VitalsMonitor/useVitalsAspectRatioConfig";
+import { DISCHARGE_REASONS, SYMPTOM_CHOICES } from "../../../Common/constants";
+import PrescriptionsTable from "../../Medicine/PrescriptionsTable";
+import Chip from "../../../CAREUI/display/Chip";
+import { formatDate, formatDateTime } from "../../../Utils/utils";
+import ReadMore from "../../Common/components/Readmore";
+import { DailyRoundsList } from "../Consultations/DailyRoundsList";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
+ const dispatch: any = useDispatch();
+ const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
+ const [hl7SocketUrl, setHL7SocketUrl] = useState();
+ const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
+ const [monitorBedData, setMonitorBedData] = useState();
+ const [ventilatorBedData, setVentilatorBedData] = useState();
+
+ const vitals = useVitalsAspectRatioConfig({
+ default: undefined,
+ md: 8 / 11,
+ lg: 15 / 11,
+ xl: 13 / 11,
+ "2xl": 19 / 11,
+ "3xl": 23 / 11,
+ });
+
+ useEffect(() => {
+ if (
+ !props.consultationData.facility ||
+ !props.consultationData.current_bed?.bed_object.id
+ )
+ return;
+
+ const fetchData = async () => {
+ const [facilityRes, assetBedRes] = await Promise.all([
+ dispatch(getPermittedFacility(props.consultationData.facility as any)),
+ dispatch(
+ listAssetBeds({
+ facility: props.consultationData.facility as any,
+ bed: props.consultationData.current_bed?.bed_object.id,
+ })
+ ),
+ ]);
+
+ const { middleware_address } = facilityRes.data as FacilityModel;
+ const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
+
+ const monitorBedData = assetBeds?.find(
+ (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
+ );
+ setMonitorBedData(monitorBedData);
+ const assetDataForMonitor = monitorBedData?.asset_object;
+ const hl7Meta = assetDataForMonitor?.meta;
+ const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
+ if (hl7Middleware && hl7Meta?.local_ip_address) {
+ setHL7SocketUrl(
+ `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
+ );
+ }
+
+ const consultationBedVentilator =
+ props.consultationData?.current_bed?.assets_objects?.find(
+ (i) => i.asset_class === AssetClass.VENTILATOR
+ );
+ let ventilatorBedData;
+ if (consultationBedVentilator) {
+ ventilatorBedData = {
+ asset_object: consultationBedVentilator,
+ bed_object: props.consultationData?.current_bed?.bed_object,
+ } as AssetBedModel;
+ } else {
+ ventilatorBedData = assetBeds?.find(
+ (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
+ );
+ }
+ setVentilatorBedData(ventilatorBedData);
+ const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
+ const ventilatorMiddleware =
+ ventilatorMeta?.middleware_hostname || middleware_address;
+ if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
+ setVentilatorSocketUrl(
+ `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
+ );
+ }
+
+ if (
+ !(hl7Middleware && hl7Meta?.local_ip_address) &&
+ !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
+ ) {
+ setHL7SocketUrl(undefined);
+ setVentilatorSocketUrl(undefined);
+ }
+ };
+
+ fetchData();
+ }, [props.consultationData]);
+
+ return (
+
+ {!props.consultationData.discharge_date &&
+ hl7SocketUrl &&
+ ventilatorSocketUrl && (
+
+ )}
+
+
+
+
+ {!props.consultationData.discharge_date &&
+ ((hl7SocketUrl && !ventilatorSocketUrl) ||
+ (!hl7SocketUrl && ventilatorSocketUrl)) && (
+
+ {(hl7SocketUrl || ventilatorSocketUrl) && (
+
+ {hl7SocketUrl && (
+
+
+
+ )}
+ {ventilatorSocketUrl && (
+
+
+
+ )}
+
+ )}
+
+ )}
+ {props.consultationData.discharge_date && (
+
+
+
+ Discharge Information
+
+
+
+ Reason {" - "}
+
+ {DISCHARGE_REASONS.find(
+ (d) =>
+ d.id === props.consultationData.discharge_reason
+ )?.text ?? "--"}
+
+
+ {props.consultationData.discharge_reason === "REF" && (
+
+ Referred Facility {" - "}
+
+ {props.consultationData.referred_to_external ||
+ props.consultationData.referred_to_object?.name ||
+ "--"}
+
+
+ )}
+ {props.consultationData.discharge_reason === "REC" && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Advice {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+
+
+
+ )}
+ {props.consultationData.discharge_reason === "EXP" && (
+
+
+ Date of Death {" - "}
+
+ {props.consultationData.death_datetime
+ ? formatDateTime(
+ props.consultationData.death_datetime
+ )
+ : "--:--"}
+
+
+
+ Cause of death {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ Confirmed By {" - "}
+
+ {props.consultationData.death_confirmed_doctor ??
+ "--"}
+
+
+
+ )}
+ {["REF", "LAMA"].includes(
+ props.consultationData.discharge_reason ?? ""
+ ) && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Notes {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ )}
+
+
+
+ )}
+ {props.consultationData.symptoms_text && (
+
+
+
+ Symptoms
+
+
+
+ Last Daily Update
+
+ {props.consultationData.last_daily_round
+ ?.additional_symptoms && (
+ <>
+
+ {props.consultationData.last_daily_round?.additional_symptoms.map(
+ (symptom: any, index: number) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.last_daily_round
+ ?.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {
+ props.consultationData.last_daily_round
+ ?.other_symptoms
+ }
+
+ )}
+
+ from{" "}
+ {formatDate(
+ props.consultationData.last_daily_round.created_at
+ )}
+
+ >
+ )}
+
+
+ Consultation Update
+
+
+ {props.consultationData.symptoms?.map(
+ (symptom, index) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {props.consultationData.other_symptoms}
+
+ )}
+
+ from{" "}
+ {props.consultationData.symptoms_onset_date
+ ? formatDate(props.consultationData.symptoms_onset_date)
+ : "--/--/----"}
+
+
+
+
+ )}
+
+ {props.consultationData.history_of_present_illness && (
+
+
+
+ History of Present Illness
+
+
+
+
+
+
+ )}
+
+ {props.consultationData.examination_details && (
+
+
+
+ Examination details and Clinical conditions:{" "}
+
+
+
+
+
+
+ )}
+ {props.consultationData.treatment_plan && (
+
+
+
+ Treatment Summary
+
+
+
+
+
+
+ )}
+ {props.consultationData.consultation_notes && (
+
+
+
+ General Instructions
+
+
+
+
+
+
+ )}
+
+ {(props.consultationData.operation ??
+ props.consultationData.special_instruction) && (
+
+
+
+ Notes
+
+
+ {props.consultationData.operation && (
+
+
Operation
+
+
+ )}
+
+ {props.consultationData.special_instruction && (
+
+
Special Instruction
+
+
+ )}
+
+
+
+ )}
+
+ {props.consultationData.procedure &&
+ props.consultationData.procedure.length > 0 && (
+
+
+
+
+
+
+ Procedure
+ |
+
+ Notes
+ |
+
+ Repetitive
+ |
+
+ Time / Frequency
+ |
+
+
+
+ {props.consultationData.procedure?.map(
+ (procedure, index) => (
+
+
+ {procedure.procedure}
+ |
+
+ {procedure.notes}
+ |
+
+ {procedure.repetitive ? "Yes" : "No"}
+ |
+
+ {procedure.repetitive
+ ? procedure.frequency
+ : formatDateTime(String(procedure.time))}
+ |
+
+ )
+ )}
+
+
+
+
+ )}
+ {props.consultationData.intubation_start_date && (
+
+
+
+ Date/Size/LL:{" "}
+
+
+
+ Intubation Date{" - "}
+
+ {formatDateTime(
+ props.consultationData.intubation_start_date
+ )}
+
+
+
+ Extubation Date{" - "}
+
+ {props.consultationData.intubation_end_date &&
+ formatDateTime(
+ props.consultationData.intubation_end_date
+ )}
+
+
+
+ ETT/TT (mmid){" - "}
+
+ {props.consultationData.ett_tt}
+
+
+
+ Cuff Pressure (mmhg){" - "}
+
+ {props.consultationData.cuff_pressure}
+
+
+
+
+
+ )}
+
+ {props.consultationData.lines?.length > 0 && (
+
+
+
+ Lines and Catheters
+
+
+ {props.consultationData.lines?.map(
+ (line: any, idx: number) => (
+
+
{line.type}
+
+ Details:
+
+ {line.other_type}
+
+
+ Insertion Date:{" "}
+
+ {formatDateTime(line.start_date)}
+
+
+
+ Site/Level of Fixation:
+
+ {line.site}
+
+
+
+ )
+ )}
+
+
+
+ )}
+
+
+
+
+ Body Details
+
+
+
+ Gender {" - "}
+
+ {props.patientData.gender ?? "-"}
+
+
+
+ Age {" - "}
+
+ {props.patientData.age ?? "-"}
+
+
+
+ Weight {" - "}
+
+ {props.consultationData.weight ?? "-"} Kg
+
+
+
+ Height {" - "}
+
+ {props.consultationData.height ?? "-"} cm
+
+
+
+ Body Surface Area {" - "}
+
+ {Math.sqrt(
+ (Number(props.consultationData.weight) *
+ Number(props.consultationData.height)) /
+ 3600
+ ).toFixed(2)}{" "}
+ m2
+
+
+
+ Blood Group {" - "}
+
+ {props.patientData.blood_group ?? "-"}
+
+
+
+
+
+
+
+
+
+
+ setShowAutomatedRounds((s) => !s)}
+ />
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
new file mode 100644
index 00000000000..d14b54cc096
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { VentilatorPlot } from "../Consultations/VentilatorPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationVentilatorTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx
new file mode 100644
index 00000000000..affdb2756cd
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/index.tsx
@@ -0,0 +1,501 @@
+import {
+ CONSULTATION_TABS,
+ GENDER_TYPES,
+ OptionsType,
+ SYMPTOM_CHOICES,
+} from "../../../Common/constants";
+import { ConsultationModel, ICD11DiagnosisModel } from "../models";
+import { getConsultation, getPatient } from "../../../Redux/actions";
+import { statusType, useAbortableEffect } from "../../../Common/utils";
+import { lazy, useCallback, useState } from "react";
+
+import ButtonV2 from "../../Common/components/ButtonV2";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import DischargeModal from "../DischargeModal";
+import DischargeSummaryModal from "../DischargeSummaryModal";
+import DoctorVideoSlideover from "../DoctorVideoSlideover";
+import { make as Link } from "../../Common/components/Link.bs";
+import PatientInfoCard from "../../Patient/PatientInfoCard";
+import { PatientModel } from "../../Patient/models";
+import { formatDateTime, relativeTime } from "../../../Utils/utils";
+
+import { navigate } from "raviger";
+import { useDispatch } from "react-redux";
+import { useQueryParams } from "raviger";
+import { useTranslation } from "react-i18next";
+import { triggerGoal } from "../../Common/Plausible";
+import useAuthUser from "../../../Common/hooks/useAuthUser";
+import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab";
+import { ConsultationABGTab } from "./ConsultationABGTab";
+import { ConsultationNursingTab } from "./ConsultationNursingTab";
+import { ConsultationFeedTab } from "./ConsultationFeedTab";
+import { ConsultationSummaryTab } from "./ConsultationSummaryTab";
+import { ConsultationFilesTab } from "./ConsultationFilesTab";
+import { ConsultationMedicinesTab } from "./ConsultationMedicinesTab";
+import { ConsultationInvestigationsTab } from "./ConsultationInvestigationsTab";
+import { ConsultationVentilatorTab } from "./ConsultationVentilatorTab";
+import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab";
+import { ConsultationDialysisTab } from "./ConsultationDialysisTab";
+import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab";
+
+const Loading = lazy(() => import("../../Common/Loading"));
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+const symptomChoices = [...SYMPTOM_CHOICES];
+
+export interface ConsultationTabProps {
+ consultationId: string;
+ facilityId: string;
+ patientId: string;
+ consultationData: ConsultationModel;
+ patientData: PatientModel;
+}
+
+export const ConsultationDetails = (props: any) => {
+ const { t } = useTranslation();
+ const { facilityId, patientId, consultationId } = props;
+ const tab = props.tab.toUpperCase();
+ const dispatch: any = useDispatch();
+ const [isLoading, setIsLoading] = useState(false);
+ const [showDoctors, setShowDoctors] = useState(false);
+ const [qParams, _] = useQueryParams();
+
+ const [consultationData, setConsultationData] = useState(
+ {} as ConsultationModel
+ );
+ const [patientData, setPatientData] = useState({});
+ const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
+ useState(false);
+ const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
+
+ const getPatientGender = (patientData: any) =>
+ GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
+
+ const getPatientAddress = (patientData: any) =>
+ `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
+
+ const getPatientComorbidities = (patientData: any) => {
+ if (patientData?.medical_history?.length) {
+ const medHis = patientData.medical_history;
+ return medHis.map((item: any) => item.disease).join(", ");
+ } else {
+ return "None";
+ }
+ };
+
+ const authUser = useAuthUser();
+
+ const fetchData = useCallback(
+ async (status: statusType) => {
+ setIsLoading(true);
+ const res = await dispatch(getConsultation(consultationId));
+ if (!status.aborted) {
+ if (res?.data) {
+ const data: ConsultationModel = {
+ ...res.data,
+ symptoms_text: "",
+ };
+ if (res.data.symptoms?.length) {
+ const symptoms = res.data.symptoms
+ .filter((symptom: number) => symptom !== 9)
+ .map((symptom: number) => {
+ const option = symptomChoices.find((i) => i.id === symptom);
+ return option ? option.text.toLowerCase() : symptom;
+ });
+ data.symptoms_text = symptoms.join(", ");
+ }
+ setConsultationData(data);
+ const id = res.data.patient;
+ const patientRes = await dispatch(getPatient({ id }));
+ if (patientRes?.data) {
+ const patientGender = getPatientGender(patientRes.data);
+ const patientAddress = getPatientAddress(patientRes.data);
+ const patientComorbidities = getPatientComorbidities(
+ patientRes.data
+ );
+ const data = {
+ ...patientRes.data,
+ gender: patientGender,
+ address: patientAddress,
+ comorbidities: patientComorbidities,
+ is_declared_positive: patientRes.data.is_declared_positive
+ ? "Yes"
+ : "No",
+ is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
+ };
+ setPatientData(data);
+ }
+ } else {
+ navigate("/not-found");
+ }
+ setIsLoading(false);
+ }
+ },
+ [consultationId, dispatch, patientData.is_vaccinated]
+ );
+
+ useAbortableEffect((status: statusType) => {
+ fetchData(status);
+ triggerGoal("Patient Consultation Viewed", {
+ facilityId: facilityId,
+ consultationId: consultationId,
+ userId: authUser.id,
+ });
+ }, []);
+
+ const TABS = {
+ UPDATES: ConsultationUpdatesTab,
+ FEED: ConsultationFeedTab,
+ SUMMARY: ConsultationSummaryTab,
+ MEDICINES: ConsultationMedicinesTab,
+ FILES: ConsultationFilesTab,
+ INVESTIGATIONS: ConsultationInvestigationsTab,
+ ABG: ConsultationABGTab,
+ NURSING: ConsultationNursingTab,
+ NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab,
+ VENTILATOR: ConsultationVentilatorTab,
+ NUTRITION: ConsultationNursingTab,
+ PRESSURE_SORE: ConsultationPressureSoreTab,
+ DIALYSIS: ConsultationDialysisTab,
+ };
+
+ const consultationTabProps: ConsultationTabProps = {
+ consultationId,
+ facilityId,
+ patientId,
+ consultationData,
+ patientData,
+ };
+
+ const SelectedTab = TABS[tab];
+
+ if (isLoading) {
+ return ;
+ }
+
+ const tabButtonClasses = (selected: boolean) =>
+ `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
+ selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
+ }`;
+
+ const ShowDiagnosis = ({
+ diagnoses = [],
+ label = "Diagnosis",
+ nshow = 2,
+ }: {
+ diagnoses: ICD11DiagnosisModel[] | undefined;
+ label: string;
+ nshow?: number;
+ }) => {
+ const [showMore, setShowMore] = useState(false);
+
+ return diagnoses.length ? (
+
+ ) : null;
+ };
+
+ return (
+
+
setOpenDischargeSummaryDialog(false)}
+ />
+
+ setOpenDischargeDialog(false)}
+ consultationData={consultationData}
+ />
+
+
+
+
+
+
+
+
+ {consultationData.admitted_to && (
+
+
+ Patient
+ {consultationData.discharge_date
+ ? " Discharged from"
+ : " Admitted to"}
+
+ {consultationData.admitted_to}
+
+
+ {(consultationData.admission_date ??
+ consultationData.discharge_date) && (
+
+ {relativeTime(
+ consultationData.discharge_date
+ ? consultationData.discharge_date
+ : consultationData.admission_date
+ )}
+
+ )}
+
+ {consultationData.admission_date &&
+ formatDateTime(consultationData.admission_date)}
+ {consultationData.discharge_date &&
+ ` - ${formatDateTime(consultationData.discharge_date)}`}
+
+
+ )}
+
+
+
+
+ {/*consultationData.other_symptoms && (
+
+
+ Other Symptoms:{" "}
+
+ {consultationData.other_symptoms}
+
+ )*/}
+
+ {consultationData.icd11_principal_diagnosis && (
+
+ d.id === consultationData.icd11_principal_diagnosis
+ )!,
+ ]}
+ />
+ )}
+
+
+
+
+
+ {(consultationData.verified_by_object ||
+ consultationData.deprecated_verified_by) && (
+
+
+ Verified By:{" "}
+
+ {consultationData.verified_by_object
+ ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
+ : consultationData.deprecated_verified_by}
+
+
+ )}
+
+
+ setOpenDischargeSummaryDialog(true)}>
+
+ {t("discharge_summary")}
+
+
+ setOpenDischargeDialog(true)}
+ disabled={!!consultationData.discharge_date}
+ >
+
+ {t("discharge_from_care")}
+
+
+
+
+
+
+ Created:
+ {consultationData.created_date
+ ? formatDateTime(consultationData.created_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.created_by && (
+
+ {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
+ {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
+
+ )}
+
+
+
+ Last Modified:
+ {consultationData.modified_date
+ ? formatDateTime(consultationData.modified_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.last_edited_by && (
+
+ {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
+ {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx
index 07fd6f31739..84b3e62ef9e 100644
--- a/src/Components/Facility/Consultations/Feed.tsx
+++ b/src/Components/Facility/Consultations/Feed.tsx
@@ -56,6 +56,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
const [precision, setPrecision] = useState(1);
const [cameraState, setCameraState] = useState(null);
const [isFullscreen, setFullscreen] = useFullscreen();
+ const [videoStartTime, setVideoStartTime] = useState(null);
const authUser = useAuthUser();
useEffect(() => {
@@ -197,6 +198,16 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
dispatch,
});
+ const calculateVideoLiveDelay = () => {
+ const video = liveFeedPlayerRef.current as HTMLVideoElement;
+ if (!video || !videoStartTime) return 0;
+
+ const timeDifference =
+ (new Date().getTime() - videoStartTime.getTime()) / 1000;
+
+ return timeDifference - video.currentTime;
+ };
+
const getBedPresets = async (asset: any) => {
if (asset.id && bed) {
const bedAssets = await dispatch(listAssetBeds({ asset: asset.id, bed }));
@@ -240,7 +251,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
}, []);
useEffect(() => {
- if (streamStatus === StreamStatus.Playing) {
+ if (!currentPreset && streamStatus === StreamStatus.Playing) {
setLoading(CAMERA_STATES.MOVING.GENERIC);
const preset =
bedPresets?.find(
@@ -296,6 +307,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
},
reset: () => {
setStreamStatus(StreamStatus.Loading);
+ setVideoStartTime(null);
startStream({
onSuccess: () => setStreamStatus(StreamStatus.Playing),
onError: () => setStreamStatus(StreamStatus.Offline),
@@ -437,10 +449,16 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
playsinline={true}
playing={true}
muted={true}
+ onPlay={() => {
+ setVideoStartTime(() => new Date());
+ }}
width="100%"
height="100%"
onBuffer={() => {
- setStreamStatus(StreamStatus.Loading);
+ const delay = calculateVideoLiveDelay();
+ if (delay > 5) {
+ setStreamStatus(StreamStatus.Loading);
+ }
}}
onError={(e: any, _: any, hlsInstance: any) => {
if (e === "hlsError") {
@@ -459,6 +477,15 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
muted
playsInline
className="max-h-full max-w-full"
+ onPlay={() => {
+ setVideoStartTime(() => new Date());
+ }}
+ onWaiting={() => {
+ const delay = calculateVideoLiveDelay();
+ if (delay > 5) {
+ setStreamStatus(StreamStatus.Loading);
+ }
+ }}
ref={liveFeedPlayerRef as any}
/>
)}
@@ -506,7 +533,6 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
{["fullScreen", "reset", "updatePreset", "zoomIn", "zoomOut"].map(
(button, index) => {
- if (isIOS && button === "reset") return null;
const option = cameraPTZ.find(
(option) => option.action === button
);
@@ -531,6 +557,13 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
clickAction={() => cameraPTZ[4].callback()}
/>
+ {streamStatus === StreamStatus.Playing &&
+ calculateVideoLiveDelay() > 3 && (
+
+
+ Slow Network Detected
+
+ )}
{[
false,
diff --git a/src/Components/Facility/LocationManagement.tsx b/src/Components/Facility/LocationManagement.tsx
index 01e0c246ecb..38dcfc1f389 100644
--- a/src/Components/Facility/LocationManagement.tsx
+++ b/src/Components/Facility/LocationManagement.tsx
@@ -1,176 +1,99 @@
-import { useCallback, useState, ReactElement, lazy } from "react";
-
-import { useDispatch } from "react-redux";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-import { listFacilityAssetLocation, getAnyFacility } from "../../Redux/actions";
-import Pagination from "../Common/Pagination";
-import { LocationModel } from "./models";
+import { lazy } from "react";
import ButtonV2 from "../Common/components/ButtonV2";
import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
import CareIcon from "../../CAREUI/icons/CareIcon";
import Page from "../Common/components/Page";
-const Loading = lazy(() => import("../Common/Loading"));
+import routes from "../../Redux/api";
+import PaginatedList from "../../CAREUI/misc/PaginatedList";
+import { LocationModel } from "./models";
-interface LocationManagementProps {
- facilityId: string;
-}
+const Loading = lazy(() => import("../Common/Loading"));
-interface LocationRowProps {
- id: string;
+interface Props {
facilityId: string;
- name: string;
- description: string;
}
-const LocationRow = (props: LocationRowProps) => {
- const { id, facilityId, name, description } = props;
-
+export default function LocationManagement({ facilityId }: Props) {
return (
-
-
-
-
{name}
-
{description}
-
-
-
-
-
- Edit
-
-
(
+
+
+ Add New Location
+
+ }
>
-
- Manage Beds
-
-
-
- );
-};
-
-export const LocationManagement = (props: LocationManagementProps) => {
- const { facilityId } = props;
- const dispatchAction: any = useDispatch();
- const [isLoading, setIsLoading] = useState(false);
- let location: ReactElement | null = null;
- let locationsList: ReactElement[] | ReactElement = [];
- const [locations, setLocations] = useState
([]);
- const [offset, setOffset] = useState(0);
- const [currentPage, setCurrentPage] = useState(1);
- const [totalCount, setTotalCount] = useState(0);
- const [facilityName, setFacilityName] = useState("");
- const limit = 14;
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const facility = await dispatchAction(getAnyFacility(facilityId));
-
- setFacilityName(facility?.data?.name || "");
-
- const res = await dispatchAction(
- listFacilityAssetLocation(
- { limit, offset },
- { facility_external_id: facilityId }
- )
- );
- if (!status.aborted) {
- if (res?.data) {
- setLocations(res.data.results);
- setTotalCount(res.data.count);
- }
- setIsLoading(false);
- }
- },
- [dispatchAction, offset, facilityId]
- );
-
- useAbortableEffect(
- (status: statusType) => {
- fetchData(status);
- },
- [fetchData]
- );
+
+
+
+ Add New Location
+
+
+
+ No locations available
+
- const handlePagination = (page: number, limit: number) => {
- const offset = (page - 1) * limit;
- setCurrentPage(page);
- setOffset(offset);
- };
+
+
+
- if (locations?.length) {
- locationsList = locations.map((locationItem: LocationModel) => (
-
- ));
- } else if (locations && locations.length === 0) {
- locationsList = (
-
- No locations available
-
- );
- }
+ className="my-8 flex grow flex-col gap-3 lg:mx-8">
+ {(item) => }
+
- if (locations) {
- location = (
- <>
-
- {locationsList}
-
- {totalCount > limit && (
-
-
+
- )}
- >
- );
- }
-
- if (isLoading || !locations) {
- return
;
- }
+
+ )}
+
+ );
+}
- return (
-
-
-
-
-
- Add New Location
-
-
- {location}
+const Location = ({ name, description, id }: LocationModel) => (
+
+
+
+
+
+
+ Edit
+
+
+
+ Manage Beds
+
+
+
+);
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx
index 012ca1d68b2..cc34140e7a9 100644
--- a/src/Components/Facility/models.tsx
+++ b/src/Components/Facility/models.tsx
@@ -142,6 +142,7 @@ export interface ConsultationModel {
cause_of_death?: string;
death_datetime?: string;
death_confirmed_doctor?: string;
+ is_readmission?: boolean;
}
export interface PatientStatsModel {
id?: number;
diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx
index b29c112c5a1..54d22b5f54c 100644
--- a/src/Components/Patient/ManagePatients.tsx
+++ b/src/Components/Patient/ManagePatients.tsx
@@ -603,6 +603,15 @@ export const PatientManager = () => {
text="Review Missed"
/>
)}
+ {patient.last_consultation?.is_readmission && (
+
+ )}
{patient.disease_status === "POSITIVE" && (
{
);
};
+ const isPatientEligibleForNewConsultation = (patientData: PatientModel) => {
+ return !patientData.last_consultation ||
+ patientData.last_consultation?.discharge_date
+ ? true
+ : false;
+ };
+
return (
{
- patientData.is_active &&
- (!patientData?.last_consultation ||
- patientData?.last_consultation?.discharge_date) &&
+ isPatientEligibleForNewConsultation(patientData) &&
navigate(
`/facility/${patientData?.facility}/patient/${id}/consultation`
)
}
>
-
-
+
+
diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx
index 9df4c93051a..dbb4aac34ce 100644
--- a/src/Components/Patient/PatientInfoCard.tsx
+++ b/src/Components/Patient/PatientInfoCard.tsx
@@ -143,7 +143,7 @@ export default function PatientInfoCard(props: {
)}
-
+
•
-
- Domiciliary Care{" "}
-
+
+
+ Domiciliary Care
+
+ >
+ )}
+ {consultation?.is_readmission && (
+ <>
+ •
+
+
+ Readmitted
>
)}
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx
index d2e0ca415e3..8625d442f52 100644
--- a/src/Components/Users/ManageUsers.tsx
+++ b/src/Components/Users/ManageUsers.tsx
@@ -73,6 +73,8 @@ export default function ManageUsers() {
name: string;
}>({ show: false, username: "", name: "" });
+ const [weeklyHoursError, setWeeklyHoursError] = useState("");
+
const extremeSmallScreenBreakpoint = 320;
const isExtremeSmallScreen =
width <= extremeSmallScreenBreakpoint ? true : false;
@@ -144,7 +146,10 @@ export default function ManageUsers() {
const handleWorkingHourSubmit = async () => {
const username = selectedUser;
- if (!username || weeklyHours < 0 || weeklyHours > 168) return;
+ if (!username || !weeklyHours || weeklyHours < 0 || weeklyHours > 168) {
+ setWeeklyHoursError("Value should be between 0 and 168");
+ return;
+ }
const res = await dispatch(
partialUpdateUser(username, {
weekly_working_hours: weeklyHours,
@@ -163,6 +168,7 @@ export default function ManageUsers() {
});
}
setWeeklyHours(0);
+ setWeeklyHoursError("");
fetchData({ aborted: false });
};
@@ -493,13 +499,14 @@ export default function ManageUsers() {
{
+ setExpandWorkingHours(state);
+ setWeeklyHours(0);
+ setWeeklyHoursError("");
+ }}
slideFrom="right"
title="Average weekly working hours"
dialogClass="md:w-[400px]"
- onCloseClick={() => {
- setWeeklyHours(0);
- }}
>
@@ -512,11 +519,7 @@ export default function ManageUsers() {
onChange={(e) => {
setWeeklyHours(e.value);
}}
- error={
- weeklyHours < 0 || weeklyHours > 168
- ? "Average weekly working hours should be between 0 and 168"
- : ""
- }
+ error={weeklyHoursError}
required
label=""
type="number"
diff --git a/src/Components/VitalsMonitor/HL7DeviceClient.ts b/src/Components/VitalsMonitor/HL7DeviceClient.ts
index 72f6406193a..7e13622b31f 100644
--- a/src/Components/VitalsMonitor/HL7DeviceClient.ts
+++ b/src/Components/VitalsMonitor/HL7DeviceClient.ts
@@ -1,11 +1,31 @@
import { EventEmitter } from "events";
import { VitalsDataBase, VitalsValueBase, VitalsWaveformBase } from "./types";
-const WAVEFORM_KEY_MAP: Record = {
- II: "ecg-waveform",
- Pleth: "pleth-waveform",
- Respiration: "spo2-waveform",
-};
+const ECG_WAVENAME_KEYS = [
+ "I",
+ "II",
+ "III",
+ "aVR",
+ "aVL",
+ "aVF",
+ "V1",
+ "V2",
+ "V3",
+ "V4",
+ "V5",
+ "V6",
+] as const;
+
+const WAVEFORM_KEY_MAP: Record =
+ {
+ Pleth: "pleth-waveform",
+ Respiration: "spo2-waveform",
+
+ // Maps each ECG wave name to the event "ecg-waveform"
+ ...(Object.fromEntries(
+ ECG_WAVENAME_KEYS.map((key) => [key, "ecg-waveform"])
+ ) as Record),
+ };
/**
* Provides the API for connecting to the Vitals Monitor WebSocket and emitting
@@ -74,8 +94,10 @@ export interface HL7VitalsValueData extends VitalsDataBase, VitalsValueBase {
| "body-temperature2";
}
+type EcgWaveName = (typeof ECG_WAVENAME_KEYS)[number];
+
export interface HL7VitalsWaveformData extends VitalsWaveformBase {
- "wave-name": "II" | "Pleth" | "Respiration";
+ "wave-name": EcgWaveName | "Pleth" | "Respiration";
}
export interface HL7VitalsBloodPressureData extends VitalsDataBase {
diff --git a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx
index a340c21e28b..c0b089df8b0 100644
--- a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx
+++ b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx
@@ -88,7 +88,7 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
{/* Pulse Rate */}
❤️
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 393b9372ca5..b98a099f439 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -1,16 +1,17 @@
-interface Route {
- path: string;
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
- noAuth?: boolean;
+import { LocationModel } from "../Components/Facility/models";
+import { PaginatedResponse } from "../Utils/request/types";
+
+/**
+ * A fake function that returns an empty object casted to type T
+ * @returns Empty object as type T
+ */
+function Res(): T {
+ return {} as T;
}
-interface Routes {
- [name: string]: Route;
-}
-
-const routes: Routes = {
+const routes = {
config: {
- path: import.meta.env.REACT_APP_CONFIG || "/config.json",
+ path: import.meta.env.REACT_APP_CONFIG ?? "/config.json",
method: "GET",
noAuth: true,
},
@@ -187,6 +188,7 @@ const routes: Routes = {
listFacilityAssetLocation: {
path: "/api/v1/facility/{facility_external_id}/asset_location/",
method: "GET",
+ TRes: Res>(),
},
createFacilityAssetLocation: {
path: "/api/v1/facility/{facility_external_id}/asset_location/",
@@ -1065,6 +1067,6 @@ const routes: Routes = {
path: "/api/v1/hcx/make_claim/",
method: "POST",
},
-};
+} as const;
export default routes;
diff --git a/src/Router/AppRouter.tsx b/src/Router/AppRouter.tsx
index 24b24cfdf09..f1449f13bc2 100644
--- a/src/Router/AppRouter.tsx
+++ b/src/Router/AppRouter.tsx
@@ -49,7 +49,7 @@ import ShowPushNotification from "../Components/Notifications/ShowPushNotificati
import { NoticeBoard } from "../Components/Notifications/NoticeBoard";
import { AddLocationForm } from "../Components/Facility/AddLocationForm";
import { AddBedForm } from "../Components/Facility/AddBedForm";
-import { LocationManagement } from "../Components/Facility/LocationManagement";
+import LocationManagement from "../Components/Facility/LocationManagement";
import { BedManagement } from "../Components/Facility/BedManagement";
import AssetsList from "../Components/Assets/AssetsList";
import AssetManage from "../Components/Assets/AssetManage";
diff --git a/src/Utils/request/request.ts b/src/Utils/request/request.ts
new file mode 100644
index 00000000000..2dc938fa6f1
--- /dev/null
+++ b/src/Utils/request/request.ts
@@ -0,0 +1,27 @@
+import { RequestOptions, Route } from "./types";
+import { makeHeaders, makeUrl } from "./utils";
+
+interface Options extends RequestOptions {
+ controller?: AbortController;
+}
+
+export default async function request(
+ { path, method, noAuth }: Route,
+ { query, body, pathParams, controller }: Options = {}
+) {
+ const signal = controller?.signal;
+
+ const headers = makeHeaders(noAuth ?? false);
+ const url = makeUrl(path, query, pathParams);
+
+ const options: RequestInit = { headers, method, signal };
+
+ if (body) {
+ options.body = JSON.stringify(body);
+ }
+
+ const res = await fetch(url, options);
+ const data: TData = await res.json();
+
+ return { res, data };
+}
diff --git a/src/Utils/request/types.ts b/src/Utils/request/types.ts
new file mode 100644
index 00000000000..e7f0f9544a3
--- /dev/null
+++ b/src/Utils/request/types.ts
@@ -0,0 +1,32 @@
+type QueryParamValue = string | number | boolean | null | undefined;
+
+export type QueryParams = Record;
+
+interface RouteBase {
+ path: string;
+ TRes: TData;
+ noAuth?: boolean;
+}
+
+export interface QueryRoute extends RouteBase {
+ method?: "GET";
+}
+
+export interface MutationRoute extends RouteBase {
+ method: "POST" | "PUT" | "PATCH" | "DELETE";
+}
+
+export type Route = QueryRoute | MutationRoute;
+
+export interface RequestOptions {
+ query?: QueryParams;
+ body?: object;
+ pathParams?: Record;
+}
+
+export interface PaginatedResponse {
+ count: number;
+ next: string | null;
+ previous: string | null;
+ results: TItem[];
+}
diff --git a/src/Utils/request/useQuery.ts b/src/Utils/request/useQuery.ts
new file mode 100644
index 00000000000..e459a579e3e
--- /dev/null
+++ b/src/Utils/request/useQuery.ts
@@ -0,0 +1,70 @@
+import { useCallback, useEffect, useRef, useState } from "react";
+import { QueryRoute, RequestOptions } from "./types";
+import request from "./request";
+import { mergeRequestOptions } from "./utils";
+
+export interface QueryOptions extends RequestOptions {
+ prefetch?: boolean;
+ refetchOnWindowFocus?: boolean;
+}
+
+export default function useQuery(
+ route: QueryRoute,
+ options?: QueryOptions
+) {
+ const [res, setRes] = useState();
+ const [data, setData] = useState();
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+
+ const controllerRef = useRef();
+
+ const runQuery = useCallback(
+ async (overrides?: QueryOptions) => {
+ controllerRef.current?.abort();
+
+ const controller = new AbortController();
+ controllerRef.current = controller;
+
+ const resolvedOptions =
+ options && overrides
+ ? mergeRequestOptions(options, overrides)
+ : options;
+
+ setLoading(true);
+
+ try {
+ const { res, data } = await request(route, resolvedOptions);
+
+ setRes(res);
+ setData(res.ok ? data : undefined);
+ setError(res.ok ? undefined : data);
+ } catch (error) {
+ console.error(error);
+ setData(undefined);
+ setError(error);
+ } finally {
+ setLoading(false);
+ }
+ },
+ [route, JSON.stringify(options)]
+ );
+
+ useEffect(() => {
+ if (options?.prefetch ?? true) {
+ runQuery();
+ }
+ }, [runQuery, options?.prefetch]);
+
+ useEffect(() => {
+ if (options?.refetchOnWindowFocus) {
+ const onFocus = () => runQuery();
+
+ window.addEventListener("focus", onFocus);
+
+ return () => window.removeEventListener("focus", onFocus);
+ }
+ }, [runQuery, options?.refetchOnWindowFocus]);
+
+ return { res, data, error, loading, refetch: runQuery };
+}
diff --git a/src/Utils/request/utils.ts b/src/Utils/request/utils.ts
new file mode 100644
index 00000000000..21236a8145f
--- /dev/null
+++ b/src/Utils/request/utils.ts
@@ -0,0 +1,81 @@
+import { LocalStorageKeys } from "../../Common/constants";
+import { QueryParams, RequestOptions } from "./types";
+
+export function makeUrl(
+ path: string,
+ query?: QueryParams,
+ pathParams?: Record
+) {
+ if (pathParams) {
+ path = Object.entries(pathParams).reduce(
+ (acc, [key, value]) => acc.replace(`{${key}}`, value),
+ path
+ );
+ }
+
+ ensurePathNotMissingReplacements(path);
+
+ if (query) {
+ path += `?${makeQueryParams(query)}`;
+ }
+
+ return path;
+}
+
+const makeQueryParams = (query: QueryParams) => {
+ const qParams = new URLSearchParams();
+
+ Object.entries(query).forEach(([key, value]) => {
+ if (value !== undefined) {
+ qParams.set(key, `${value}`);
+ }
+ });
+
+ return qParams.toString();
+};
+
+const ensurePathNotMissingReplacements = (path: string) => {
+ const missingParams = path.match(/\{.*\}/g);
+
+ if (missingParams) {
+ throw new Error(`Missing path params: ${missingParams.join(", ")}`);
+ }
+};
+
+export function makeHeaders(noAuth: boolean) {
+ const headers = new Headers({
+ "Content-Type": "application/json",
+ Accept: "application/json",
+ });
+
+ if (!noAuth) {
+ const token = getAuthorizationHeader();
+
+ if (token) {
+ headers.append("Authorization", token);
+ }
+ }
+
+ return headers;
+}
+
+export function getAuthorizationHeader() {
+ const bearerToken = localStorage.getItem(LocalStorageKeys.accessToken);
+
+ if (bearerToken) {
+ return `Bearer ${bearerToken}`;
+ }
+
+ return null;
+}
+
+export function mergeRequestOptions(
+ options: RequestOptions,
+ overrides: RequestOptions
+): RequestOptions {
+ return {
+ query: { ...options.query, ...overrides.query },
+ body: { ...options.body, ...overrides.body },
+ pathParams: { ...options.pathParams, ...overrides.pathParams },
+ };
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 529c2689aed..c899d453626 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -5,7 +5,7 @@ interface ImportMetaEnv {
readonly REACT_APP_META_DESCRIPTION: string;
readonly REACT_APP_COVER_IMAGE: string;
readonly REACT_APP_COVER_IMAGE_ALT: string;
- readonly REACT_APP_CONFIG: string;
+ readonly REACT_APP_CONFIG: string | undefined;
readonly REACT_PUBLIC_URL: string;
readonly REACT_APP_SITE_URL: string;
readonly REACT_APP_ANALYTICS_SERVER_URL: string;