From 9005adf2d0b81958761306750de9d417deae24e8 Mon Sep 17 00:00:00 2001
From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com>
Date: Mon, 30 Oct 2023 15:01:19 +0530
Subject: [PATCH 01/11] Existing Cypress POM Conversion | Create New User &
Verify its reflection along with validation | User Tab (#6514)
* cypress new test for advance filter
* convert navigation to POM
* facility redirection
* minor change
* user creation
* user creation
* revert package-lock
---
cypress/e2e/users_spec/user_creation.cy.ts | 154 ++++++++++++++++
cypress/e2e/users_spec/user_crud.cy.ts | 199 ---------------------
cypress/pageobject/Users/UserCreation.ts | 65 +++++++
src/Components/Users/ManageUsers.tsx | 6 +-
4 files changed, 224 insertions(+), 200 deletions(-)
delete mode 100644 cypress/e2e/users_spec/user_crud.cy.ts
create mode 100644 cypress/pageobject/Users/UserCreation.ts
diff --git a/cypress/e2e/users_spec/user_creation.cy.ts b/cypress/e2e/users_spec/user_creation.cy.ts
index 498697566c5..ae416d8d990 100644
--- a/cypress/e2e/users_spec/user_creation.cy.ts
+++ b/cypress/e2e/users_spec/user_creation.cy.ts
@@ -3,18 +3,45 @@ import LoginPage from "../../pageobject/Login/LoginPage";
import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
import FacilityPage from "../../pageobject/Facility/FacilityCreation";
import { UserPage } from "../../pageobject/Users/UserSearch";
+import { UserCreationPage } from "../../pageobject/Users/UserCreation";
describe("User Creation", () => {
const userPage = new UserPage();
const loginPage = new LoginPage();
+ const userCreationPage = new UserCreationPage();
const facilityPage = new FacilityPage();
const assetSearchPage = new AssetSearchPage();
const fillFacilityName = "Dummy Facility 1";
+ const makeid = (length: number) => {
+ let result = "";
+ const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
+ const charactersLength = characters.length;
+ for (let i = 0; i < length; i++) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+ };
+ const username = makeid(25);
const alreadylinkedusersviews = [
"devdoctor",
"devstaff2",
"devdistrictadmin",
];
+ const EXPECTED_ERROR_MESSAGES = [
+ "Please select the User Type",
+ "Please enter valid phone number",
+ "Please enter the username",
+ "Please enter date in YYYY/MM/DD format",
+ "Please enter the password",
+ "Confirm password is required",
+ "First Name is required",
+ "Last Name is required",
+ "Please enter a valid email address",
+ "Please select the Gender",
+ "Please select the state",
+ "Please select the district",
+ "Please select the local body",
+ ];
before(() => {
loginPage.loginAsDisctrictAdmin();
@@ -26,6 +53,53 @@ describe("User Creation", () => {
cy.awaitUrl("/users");
});
+ it("create new user and verify reflection", () => {
+ userCreationPage.clickElementById("addUserButton");
+ userCreationPage.selectFacility("Dummy Shifting Center");
+ userCreationPage.typeIntoElementById("username", username);
+ userCreationPage.typeIntoElementById("password", "Test@123");
+ userCreationPage.selectHomeFacility("Dummy Shifting Center");
+ userCreationPage.typeIntoElementById("phone_number", "9999999999");
+ userCreationPage.setInputDate("date_of_birth", "date-input", "25081999");
+ userCreationPage.selectDropdownOption("user_type", "Doctor");
+ userCreationPage.typeIntoElementById("c_password", "Test@123");
+ userCreationPage.typeIntoElementById("doctor_qualification", "MBBS");
+ userCreationPage.typeIntoElementById("doctor_experience_commenced_on", "2");
+ userCreationPage.typeIntoElementById(
+ "doctor_medical_council_registration",
+ "123456789"
+ );
+ userCreationPage.typeIntoElementById("first_name", "cypress test");
+ userCreationPage.typeIntoElementById("last_name", "staff user");
+ userCreationPage.typeIntoElementById("email", "test@test.com");
+ userCreationPage.selectDropdownOption("gender", "Male");
+ userCreationPage.selectDropdownOption("state", "Kerala");
+ userCreationPage.selectDropdownOption("district", "Ernakulam");
+ userCreationPage.clickElementById("submit");
+ userCreationPage.verifyNotification("User added successfully");
+ userPage.typeInSearchInput(username);
+ userPage.checkUsernameText(username);
+ userCreationPage.verifyElementContainsText("name", "cypress test");
+ userCreationPage.verifyElementContainsText("role", "Doctor");
+ userCreationPage.verifyElementContainsText("district", "Ernakulam");
+ userCreationPage.verifyElementContainsText(
+ "home_facility",
+ "Dummy Shifting Center"
+ );
+ userCreationPage.verifyElementContainsText("doctor-qualification", "MBBS");
+ userCreationPage.verifyElementContainsText("doctor-experience", "2");
+ userCreationPage.verifyElementContainsText(
+ "medical-council-registration",
+ "123456789"
+ );
+ });
+
+ it("create new user form throwing mandatory field error", () => {
+ userCreationPage.clickElementById("addUserButton");
+ userCreationPage.clickElementById("submit");
+ userCreationPage.verifyErrorMessages(EXPECTED_ERROR_MESSAGES);
+ });
+
it("view user redirection from facility page", () => {
cy.visit("/facility");
assetSearchPage.typeSearchKeyword(fillFacilityName);
@@ -37,6 +111,86 @@ describe("User Creation", () => {
userPage.verifyMultipleBadgesWithSameId(alreadylinkedusersviews);
});
+ // the below commented out codes, will be used in the upcoming refactoring of this module, since it is a inter-dependent of partially converted code, currently taking it down
+
+ // it("link facility for user", () => {
+ // cy.contains("Linked Facilities").click();
+ // cy.intercept(/\/api\/v1\/facility/).as("getFacilities");
+ // cy.get("[name='facility']")
+ // .click()
+ // .type("Dummy Facility 1")
+ // .wait("@getFacilities");
+ // cy.get("li[role='option']").first().click();
+ // cy.intercept(/\/api\/v1\/users\/\w+\/add_facility\//).as("addFacility");
+ // cy.get("button[id='link-facility']").click();
+ // cy.wait("@addFacility")
+ // // .its("response.statusCode")
+ // // .should("eq", 201)
+ // .get("span")
+ // .contains("Facility - User Already has permission to this facility");
+ // });
+
+ // describe("Edit User Profile & Error Validation", () => {
+ // before(() => {
+ // cy.loginByApi(username, "#@Cypress_test123");
+ // cy.saveLocalStorage();
+ // });
+
+ // beforeEach(() => {
+ // cy.restoreLocalStorage();
+ // cy.awaitUrl("/user/profile");
+ // cy.contains("button", "Edit User Profile").click();
+ // });
+
+ // it("First name Field Updation " + username, () => {
+ // cy.get("input[name=firstName]").clear();
+ // cy.contains("button[type='submit']", "Update").click();
+ // cy.get("span.error-text").should("contain", "Field is required");
+ // cy.get("input[name=firstName]").type("firstName updated");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
+ // it("Last name Field Updation " + username, () => {
+ // cy.get("input[name=lastName]").clear();
+ // cy.contains("button[type='submit']", "Update").click();
+ // cy.get("span.error-text").should("contain", "Field is required");
+ // cy.get("input[name=lastName]").type("lastName updated");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
+ // it("Age Field Updation " + username, () => {
+ // cy.get("input[name=age]").clear();
+ // cy.contains("button[type='submit']", "Update").click();
+ // cy.get("span.error-text").should("contain", "This field is required");
+ // cy.get("input[name=age]").type("11");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
+ // it("Phone number Field Updation " + username, () => {
+ // cy.get("input[name=phoneNumber]").clear();
+ // cy.contains("button[type='submit']", "Update").click();
+ // cy.get("span.error-text").should(
+ // "contain",
+ // "Please enter valid phone number"
+ // );
+ // cy.get("input[name=phoneNumber]").type("+919999999999");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
+ // it("Whatsapp number Field Updation " + username, () => {
+ // cy.get("input[name=altPhoneNumber]").clear();
+ // cy.get("input[name=altPhoneNumber]").type("+919999999999");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
+ // it("Email Field Updation " + username, () => {
+ // cy.get("input[name=email]").clear();
+ // cy.contains("button[type='submit']", "Update").click();
+ // cy.get("span.error-text").should("contain", "This field is required");
+ // cy.get("input[name=email]").type("test@test.com");
+ // cy.contains("button[type='submit']", "Update").click();
+ // });
+
afterEach(() => {
cy.saveLocalStorage();
});
diff --git a/cypress/e2e/users_spec/user_crud.cy.ts b/cypress/e2e/users_spec/user_crud.cy.ts
deleted file mode 100644
index 91104af518f..00000000000
--- a/cypress/e2e/users_spec/user_crud.cy.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress";
-
-const makeid = (length: number) => {
- let result = "";
- const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
- const charactersLength = characters.length;
- for (let i = 0; i < length; i++) {
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
- }
- return result;
-};
-
-const username = makeid(25);
-const phone_number = "9999999999";
-const alt_phone_number = "9999999999";
-
-describe("User management", () => {
- before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
- cy.saveLocalStorage();
- });
-
- beforeEach(() => {
- cy.restoreLocalStorage();
- cy.awaitUrl("/user");
- });
-
- it("create user", () => {
- cy.contains("Add New User").click();
- cy.get("[id='user_type'] > div > button").click();
- cy.get("div").contains("Ward Admin").click();
- cy.get("[id='state'] > div > button").click();
- cy.get("div").contains("Kerala").click();
- cy.get("[id='district'] > div > button").click();
- cy.get("div").contains("Ernakulam").click();
- cy.get("[id='local_body'] > div > button").click();
- cy.get("div").contains("Aikaranad").click();
- cy.intercept(/\/api\/v1\/facility/).as("facility");
- cy.get("[name='facilities']")
- .click()
- .type("Dummy Facility 1")
- .wait("@facility");
- cy.get("li[role='option']").first().click();
- cy.get("input[type='checkbox']").click();
- cy.get("[name='phone_number']").type(phone_number);
- cy.get("[name='alt_phone_number']").type(alt_phone_number);
- cy.intercept(/users/).as("check_availability");
- cy.get("#date_of_birth").should("be.visible").click();
- cy.get("#date-input").click().type("25081999");
- cy.get("[name='username']").type(username);
- cy.wait("@check_availability").its("response.statusCode").should("eq", 200);
- cy.get("[name='password']").type("#@Cypress_test123");
- cy.get("[name='c_password']").type("#@Cypress_test123");
- cy.get("[name='first_name']").type("Cypress Test");
- cy.get("[name='last_name']").type("Tester");
- cy.get("[name='email']").type("cypress@tester.com");
- cy.get("[id='gender'] > div > button").click();
- cy.get("div").contains("Male").click();
- cy.get("button[id='submit']").contains("Save User").click();
- cy.verifyNotification("User added successfully");
- });
-
- it("view user and verify details", () => {
- cy.contains("Advanced Filters").click();
- cy.get("[name='first_name']").type("Cypress Test");
- cy.get("[name='last_name']").type("Tester");
- cy.get("#role button").click();
- cy.contains("#role li", "Ward Admin").click();
- cy.get("input[name='district']").click();
- cy.get("input[name='district']").type("Ernakulam");
- cy.get("li[id^='headlessui-combobox-option']")
- .contains("Ernakulam")
- .click();
- cy.get("[placeholder='Phone Number']").click();
- cy.get("[placeholder='Phone Number']").type(phone_number);
- cy.get("[placeholder='WhatsApp Phone Number']").type(alt_phone_number);
- cy.contains("Apply").click();
- cy.intercept(/\/api\/v1\/users/).as("getUsers");
- cy.wait(1000);
- cy.get("[name='username']").type(username);
- cy.wait("@getUsers");
- cy.get("dd[id='count']").contains(/^1$/).click();
- cy.get("div[id='usr_0']").within(() => {
- cy.intercept(`/api/v1/users/${username}/get_facilities/`).as(
- "userFacility"
- );
- cy.get("div[id='role']").contains(/^WardAdmin$/);
- cy.get("div[id='name']").contains("Cypress Test Tester");
- cy.get("div[id='district']").contains(/^Ernakulam$/);
- cy.get("div[id='local_body']").contains("Aikaranad");
- cy.get("div[id='created_by']").contains(/^devdistrictadmin$/);
- cy.get("div[id='home_facility']").contains("No Home Facility");
- cy.get("button[id='facilities']").click();
- cy.wait("@userFacility")
- .getAttached("div[id=facility_0] > div > span")
- .contains("Dummy Facility 1");
- });
- });
-
- it("link facility for user", () => {
- cy.contains("Linked Facilities").click();
- cy.intercept(/\/api\/v1\/facility/).as("getFacilities");
- cy.get("[name='facility']")
- .click()
- .type("Dummy Facility 1")
- .wait("@getFacilities");
- cy.get("li[role='option']").first().click();
- cy.intercept(/\/api\/v1\/users\/\w+\/add_facility\//).as("addFacility");
- cy.get("button[id='link-facility']").click();
- cy.wait("@addFacility")
- // .its("response.statusCode")
- // .should("eq", 201)
- .get("span")
- .contains("Facility - User Already has permission to this facility");
- });
-
- afterEach(() => {
- cy.saveLocalStorage();
- });
-});
-
-describe("Edit User Profile & Error Validation", () => {
- before(() => {
- cy.loginByApi(username, "#@Cypress_test123");
- cy.saveLocalStorage();
- });
-
- beforeEach(() => {
- cy.restoreLocalStorage();
- cy.awaitUrl("/user/profile");
- cy.contains("button", "Edit User Profile").click();
- });
-
- it("First name Field Updation " + username, () => {
- cy.get("input[name=firstName]").clear();
- cy.contains("button[type='submit']", "Update").click();
- cy.get("span.error-text").should("contain", "Field is required");
- cy.get("input[name=firstName]").type("firstName updated");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- it("Last name Field Updation " + username, () => {
- cy.get("input[name=lastName]").clear();
- cy.contains("button[type='submit']", "Update").click();
- cy.get("span.error-text").should("contain", "Field is required");
- cy.get("input[name=lastName]").type("lastName updated");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- it("Age Field Updation " + username, () => {
- cy.get("input[name=age]").clear();
- cy.contains("button[type='submit']", "Update").click();
- cy.get("span.error-text").should("contain", "This field is required");
- cy.get("input[name=age]").type("11");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- it("Phone number Field Updation " + username, () => {
- cy.get("input[name=phoneNumber]").clear();
- cy.contains("button[type='submit']", "Update").click();
- cy.get("span.error-text").should(
- "contain",
- "Please enter valid phone number"
- );
- cy.get("input[name=phoneNumber]").type("+919999999999");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- it("Whatsapp number Field Updation " + username, () => {
- cy.get("input[name=altPhoneNumber]").clear();
- cy.get("input[name=altPhoneNumber]").type("+919999999999");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- it("Email Field Updation " + username, () => {
- cy.get("input[name=email]").clear();
- cy.contains("button[type='submit']", "Update").click();
- cy.get("span.error-text").should("contain", "This field is required");
- cy.get("input[name=email]").type("test@test.com");
- cy.contains("button[type='submit']", "Update").click();
- });
-
- afterEach(() => {
- cy.saveLocalStorage();
- });
-});
-
-// describe("Delete User", () => { district admin wont be able to delete user
-// it("deletes user", () => {
-// cy.loginByApi("devdistrictadmin", "Coronasafe@123");
-// cy.awaitUrl("/user");
-// cy.get("[name='username']").type(username);
-// cy.get("button")
-// .should("contain", "Delete")
-// .contains("Delete")
-// .click();
-// cy.get("button.font-medium.btn.btn-danger").click();
-// });
-// });
diff --git a/cypress/pageobject/Users/UserCreation.ts b/cypress/pageobject/Users/UserCreation.ts
new file mode 100644
index 00000000000..6bef3584ec8
--- /dev/null
+++ b/cypress/pageobject/Users/UserCreation.ts
@@ -0,0 +1,65 @@
+// UserCreation.ts
+export class UserCreationPage {
+ clickElementById(elementId: string) {
+ cy.get("#" + elementId).click();
+ }
+
+ typeIntoElementById(elementId: string, value: string) {
+ cy.get("#" + elementId)
+ .click()
+ .type(value);
+ }
+
+ typeIntoInputByName(inputName: string, value: string) {
+ cy.get("input[name='" + inputName + "']")
+ .click()
+ .type(value);
+ }
+
+ selectOptionContainingText(text: string) {
+ cy.get("[role='option']").contains(text).click();
+ }
+
+ verifyNotification(message: string) {
+ cy.verifyNotification(message);
+ }
+
+ selectFacility(name: string) {
+ this.typeIntoInputByName("facilities", name);
+ this.selectOptionContainingText(name);
+ }
+
+ selectHomeFacility(name: string) {
+ this.clickElementById("home_facility");
+ this.selectOptionContainingText(name);
+ }
+
+ setInputDate(
+ dateElementId: string,
+ inputElementId: string,
+ dateValue: string
+ ) {
+ this.clickElementById(dateElementId);
+ this.typeIntoElementById(inputElementId, dateValue);
+ }
+
+ selectDropdownOption(dropdownId: string, optionText: string) {
+ this.clickElementById(dropdownId);
+ this.selectOptionContainingText(optionText);
+ }
+
+ verifyElementContainsText(elementId: string, expectedText: string) {
+ cy.get("#" + elementId).should("contain.text", expectedText);
+ }
+
+ verifyErrorMessages(errorMessages: string[]) {
+ cy.get(".error-text").then(($errors) => {
+ const displayedErrorMessages = $errors
+ .map((_, el) => Cypress.$(el).text())
+ .get();
+ errorMessages.forEach((errorMessage) => {
+ expect(displayedErrorMessages).to.include(errorMessage);
+ });
+ });
+ }
+}
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx
index 150c102aff1..3b55c40fc70 100644
--- a/src/Components/Users/ManageUsers.tsx
+++ b/src/Components/Users/ManageUsers.tsx
@@ -134,7 +134,11 @@ export default function ManageUsers() {
);
const addUser = (
- navigate("/users/add")}>
+ navigate("/users/add")}
+ >
Add New User
From 7703f1f6056207cd388e74fc742445a89b42aa69 Mon Sep 17 00:00:00 2001
From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com>
Date: Tue, 31 Oct 2023 03:04:10 +0530
Subject: [PATCH 02/11] existing user profile updation (#6527)
---
cypress/e2e/users_spec/user_creation.cy.ts | 145 ++++++++++--------
cypress/pageobject/Users/UserCreation.ts | 14 ++
.../Common/Sidebar/SidebarUserCard.tsx | 1 +
src/Components/Users/UserProfile.tsx | 48 ++++--
4 files changed, 136 insertions(+), 72 deletions(-)
diff --git a/cypress/e2e/users_spec/user_creation.cy.ts b/cypress/e2e/users_spec/user_creation.cy.ts
index ae416d8d990..5a1e5d874de 100644
--- a/cypress/e2e/users_spec/user_creation.cy.ts
+++ b/cypress/e2e/users_spec/user_creation.cy.ts
@@ -4,6 +4,10 @@ import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
import FacilityPage from "../../pageobject/Facility/FacilityCreation";
import { UserPage } from "../../pageobject/Users/UserSearch";
import { UserCreationPage } from "../../pageobject/Users/UserCreation";
+import {
+ emergency_phone_number,
+ phone_number,
+} from "../../pageobject/constants";
describe("User Creation", () => {
const userPage = new UserPage();
@@ -43,6 +47,15 @@ describe("User Creation", () => {
"Please select the local body",
];
+ const EXPECTED_PROFILE_ERROR_MESSAGES = [
+ "Field is required",
+ "Field is required",
+ "This field is required",
+ "Please enter valid phone number",
+ "This field is required",
+ "This field is required",
+ ];
+
before(() => {
loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
@@ -53,13 +66,82 @@ describe("User Creation", () => {
cy.awaitUrl("/users");
});
+ it("Update the existing user profile and verify its reflection", () => {
+ userCreationPage.clickElementById("profilenamelink");
+ userCreationPage.verifyElementContainsText(
+ "username-profile-details",
+ "devdistrictadmin"
+ );
+ userCreationPage.clickElementById("edit-cancel-profile-button");
+ userCreationPage.typeIntoElementByIdPostClear(
+ "firstName",
+ "District Editted"
+ );
+ userCreationPage.typeIntoElementByIdPostClear("lastName", "Cypress");
+ userCreationPage.typeIntoElementByIdPostClear("age", "22");
+ userCreationPage.selectDropdownOption("gender", "Male");
+ userCreationPage.typeIntoElementByIdPostClear(
+ "phoneNumber",
+ "+91" + phone_number
+ );
+ userCreationPage.typeIntoElementByIdPostClear(
+ "altPhoneNumber",
+ "+91" + emergency_phone_number
+ );
+ userCreationPage.typeIntoElementByIdPostClear("email", "test@test.com");
+ userCreationPage.typeIntoElementByIdPostClear("weekly_working_hours", "14");
+ userCreationPage.clickElementById("submit");
+ userCreationPage.verifyElementContainsText(
+ "contactno-profile-details",
+ "+91" + phone_number
+ );
+ userCreationPage.verifyElementContainsText(
+ "whatsapp-profile-details",
+ "+91" + emergency_phone_number
+ );
+ userCreationPage.verifyElementContainsText(
+ "firstname-profile-details",
+ "District Editted"
+ );
+ userCreationPage.verifyElementContainsText(
+ "lastname-profile-details",
+ "Cypress"
+ );
+ userCreationPage.verifyElementContainsText("age-profile-details", "22");
+ userCreationPage.verifyElementContainsText(
+ "emailid-profile-details",
+ "test@test.com"
+ );
+ userCreationPage.verifyElementContainsText(
+ "gender-profile-details",
+ "Male"
+ );
+ userCreationPage.verifyElementContainsText(
+ "averageworkinghour-profile-details",
+ "14"
+ );
+ });
+
+ it("Update the existing user profile Form Mandatory File Error", () => {
+ userCreationPage.clickElementById("profilenamelink");
+ userCreationPage.clickElementById("edit-cancel-profile-button");
+ userCreationPage.clearIntoElementById("firstName");
+ userCreationPage.clearIntoElementById("lastName");
+ userCreationPage.clearIntoElementById("age");
+ userCreationPage.clearIntoElementById("phoneNumber");
+ userCreationPage.clearIntoElementById("altPhoneNumber");
+ userCreationPage.clearIntoElementById("weekly_working_hours");
+ userCreationPage.clickElementById("submit");
+ userCreationPage.verifyErrorMessages(EXPECTED_PROFILE_ERROR_MESSAGES);
+ });
+
it("create new user and verify reflection", () => {
userCreationPage.clickElementById("addUserButton");
userCreationPage.selectFacility("Dummy Shifting Center");
userCreationPage.typeIntoElementById("username", username);
userCreationPage.typeIntoElementById("password", "Test@123");
userCreationPage.selectHomeFacility("Dummy Shifting Center");
- userCreationPage.typeIntoElementById("phone_number", "9999999999");
+ userCreationPage.typeIntoElementById("phone_number", phone_number);
userCreationPage.setInputDate("date_of_birth", "date-input", "25081999");
userCreationPage.selectDropdownOption("user_type", "Doctor");
userCreationPage.typeIntoElementById("c_password", "Test@123");
@@ -130,67 +212,6 @@ describe("User Creation", () => {
// .contains("Facility - User Already has permission to this facility");
// });
- // describe("Edit User Profile & Error Validation", () => {
- // before(() => {
- // cy.loginByApi(username, "#@Cypress_test123");
- // cy.saveLocalStorage();
- // });
-
- // beforeEach(() => {
- // cy.restoreLocalStorage();
- // cy.awaitUrl("/user/profile");
- // cy.contains("button", "Edit User Profile").click();
- // });
-
- // it("First name Field Updation " + username, () => {
- // cy.get("input[name=firstName]").clear();
- // cy.contains("button[type='submit']", "Update").click();
- // cy.get("span.error-text").should("contain", "Field is required");
- // cy.get("input[name=firstName]").type("firstName updated");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
- // it("Last name Field Updation " + username, () => {
- // cy.get("input[name=lastName]").clear();
- // cy.contains("button[type='submit']", "Update").click();
- // cy.get("span.error-text").should("contain", "Field is required");
- // cy.get("input[name=lastName]").type("lastName updated");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
- // it("Age Field Updation " + username, () => {
- // cy.get("input[name=age]").clear();
- // cy.contains("button[type='submit']", "Update").click();
- // cy.get("span.error-text").should("contain", "This field is required");
- // cy.get("input[name=age]").type("11");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
- // it("Phone number Field Updation " + username, () => {
- // cy.get("input[name=phoneNumber]").clear();
- // cy.contains("button[type='submit']", "Update").click();
- // cy.get("span.error-text").should(
- // "contain",
- // "Please enter valid phone number"
- // );
- // cy.get("input[name=phoneNumber]").type("+919999999999");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
- // it("Whatsapp number Field Updation " + username, () => {
- // cy.get("input[name=altPhoneNumber]").clear();
- // cy.get("input[name=altPhoneNumber]").type("+919999999999");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
- // it("Email Field Updation " + username, () => {
- // cy.get("input[name=email]").clear();
- // cy.contains("button[type='submit']", "Update").click();
- // cy.get("span.error-text").should("contain", "This field is required");
- // cy.get("input[name=email]").type("test@test.com");
- // cy.contains("button[type='submit']", "Update").click();
- // });
-
afterEach(() => {
cy.saveLocalStorage();
});
diff --git a/cypress/pageobject/Users/UserCreation.ts b/cypress/pageobject/Users/UserCreation.ts
index 6bef3584ec8..32127ffcb90 100644
--- a/cypress/pageobject/Users/UserCreation.ts
+++ b/cypress/pageobject/Users/UserCreation.ts
@@ -10,6 +10,20 @@ export class UserCreationPage {
.type(value);
}
+ typeIntoElementByIdPostClear(elementId: string, value: string) {
+ cy.get("#" + elementId)
+ .click()
+ .clear()
+ .click()
+ .type(value);
+ }
+
+ clearIntoElementById(elementId: string) {
+ cy.get("#" + elementId)
+ .click()
+ .clear();
+ }
+
typeIntoInputByName(inputName: string, value: string) {
cy.get("input[name='" + inputName + "']")
.click()
diff --git a/src/Components/Common/Sidebar/SidebarUserCard.tsx b/src/Components/Common/Sidebar/SidebarUserCard.tsx
index 6f243d430d0..59970e8a73c 100644
--- a/src/Components/Common/Sidebar/SidebarUserCard.tsx
+++ b/src/Components/Common/Sidebar/SidebarUserCard.tsx
@@ -37,6 +37,7 @@ const SidebarUserCard = ({ shrinked }: { shrinked: boolean }) => {
{profileName}
diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx
index 5c5ad46b9fa..0653ea452a2 100644
--- a/src/Components/Users/UserProfile.tsx
+++ b/src/Components/Users/UserProfile.tsx
@@ -417,7 +417,11 @@ export default function UserProfile() {
Local Body, District and State are Non Editable Settings.
-
setShowEdit(!showEdit)} type="button">
+ setShowEdit(!showEdit)}
+ type="button"
+ id="edit-cancel-profile-button"
+ >
{showEdit ? "Cancel" : "Edit User Profile"}
handleSignOut(true)}>
@@ -431,7 +435,10 @@ export default function UserProfile() {
{!showEdit && (
-
+
-
Username
@@ -439,7 +446,10 @@ export default function UserProfile() {
{details.username || "-"}
-
+
-
Contact No
@@ -448,7 +458,10 @@ export default function UserProfile() {
-
+
-
Whatsapp No
@@ -456,7 +469,10 @@ export default function UserProfile() {
{details.alt_phone_number || "-"}
-
+
-
Email address
@@ -464,7 +480,10 @@ export default function UserProfile() {
{details.email || "-"}
-
+
-
First Name
@@ -472,7 +491,10 @@ export default function UserProfile() {
{details.first_name || "-"}
-
+
-
Last Name
@@ -480,7 +502,7 @@ export default function UserProfile() {
{details.last_name || "-"}
-
+
-
Age
@@ -497,7 +519,10 @@ export default function UserProfile() {
{details.user_type || "-"}
-
+
-
Gender
@@ -547,7 +572,10 @@ export default function UserProfile() {
-
+
-
Average weekly working hours
From bcd97300f0b56975d2130345a628c8c74e6d2217 Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 31 Oct 2023 12:50:41 +0530
Subject: [PATCH 03/11] Track patient card button clicks (#6521)
* Track patient card button clicks
* Add null check
---
src/Components/Patient/PatientInfoCard.tsx | 27 ++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx
index 1392b7af6eb..fd6213d8e08 100644
--- a/src/Components/Patient/PatientInfoCard.tsx
+++ b/src/Components/Patient/PatientInfoCard.tsx
@@ -8,7 +8,7 @@ import {
TELEMEDICINE_ACTIONS,
} from "../../Common/constants";
import { ConsultationModel, PatientCategory } from "../Facility/models";
-import { Switch } from "@headlessui/react";
+import { Switch, Menu } from "@headlessui/react";
import { Link } from "raviger";
import { useState } from "react";
@@ -26,8 +26,9 @@ import Beds from "../Facility/Consultations/Beds";
import { PatientModel } from "./models";
import request from "../../Utils/request/request.js";
import routes from "../../Redux/api.js";
-import { Menu } from "@headlessui/react";
import DropdownMenu from "../Common/components/Menu.js";
+import { triggerGoal } from "../../Integrations/Plausible.js";
+import useAuthUser from "../../Common/hooks/useAuthUser.js";
export default function PatientInfoCard(props: {
patient: PatientModel;
@@ -36,6 +37,8 @@ export default function PatientInfoCard(props: {
consultationId: string;
showAbhaProfile?: boolean;
}) {
+ const authUser = useAuthUser();
+
const [open, setOpen] = useState(false);
const [showLinkABHANumber, setShowLinkABHANumber] = useState(false);
const [showABHAProfile, setShowABHAProfile] = useState(
@@ -459,6 +462,11 @@ export default function PatientInfoCard(props: {
});
setOpen(true);
}
+ triggerGoal("Patient Card Button Clicked", {
+ buttonName: action[1],
+ consultationId: consultation?.id,
+ userId: authUser?.id,
+ });
}}
>
{
close();
setShowABHAProfile(true);
+ triggerGoal("Patient Card Button Clicked", {
+ buttonName: "Show ABHA Profile",
+ consultationId: consultation?.id,
+ userId: authUser?.id,
+ });
}}
>
@@ -497,6 +510,11 @@ export default function PatientInfoCard(props: {
{
+ triggerGoal("Patient Card Button Clicked", {
+ buttonName: "Link Care Context",
+ consultationId: consultation?.id,
+ userId: authUser?.id,
+ });
close();
setShowLinkCareContext(true);
}}
@@ -532,6 +550,11 @@ export default function PatientInfoCard(props: {
{
+ triggerGoal("Patient Card Button Clicked", {
+ buttonName: "Medico Legal Case",
+ consultationId: consultation?.id,
+ userId: authUser?.id,
+ });
setMedicoLegalCase(checked);
switchMedicoLegalCase(checked);
}}
From 67463597abeaf8bb859088c4e38200e0809fd188 Mon Sep 17 00:00:00 2001
From: konavivekramakrishna
<101407963+konavivekramakrishna@users.noreply.github.com>
Date: Tue, 31 Oct 2023 16:46:31 +0530
Subject: [PATCH 04/11] Replaced useDispatch w. useQuery/request: Resource
(src/Components/Resource/**) (#6461)
* replaced useDispatch in src/resource with useQuery and request
* removed unused actions
* fix loading
* removed interface import
* rm limit and offset
* fixed useQuery Hooks
* checking cypress failure
* changed function name
* removed external_id from models
* fix naming
* fix listfilter
* fixed the condition in badgeList
---
src/Components/Resource/BadgesList.tsx | 88 ++++------
src/Components/Resource/CommentSection.tsx | 152 ++++++++----------
src/Components/Resource/ListFilter.tsx | 87 ++++------
src/Components/Resource/ListView.tsx | 68 ++------
src/Components/Resource/ResourceBoard.tsx | 76 ++++-----
src/Components/Resource/ResourceCreate.tsx | 41 ++---
src/Components/Resource/ResourceDetails.tsx | 50 ++----
.../Resource/ResourceDetailsUpdate.tsx | 96 ++++-------
src/Components/Resource/models.ts | 41 +++++
src/Redux/actions.tsx | 21 ---
src/Redux/api.tsx | 25 ++-
11 files changed, 305 insertions(+), 440 deletions(-)
create mode 100644 src/Components/Resource/models.ts
diff --git a/src/Components/Resource/BadgesList.tsx b/src/Components/Resource/BadgesList.tsx
index 31b8f1c17ae..6977861d596 100644
--- a/src/Components/Resource/BadgesList.tsx
+++ b/src/Components/Resource/BadgesList.tsx
@@ -1,63 +1,25 @@
-import { useState, useEffect } from "react";
-import { getAnyFacility } from "../../Redux/actions";
-import { useDispatch } from "react-redux";
import { SHIFTING_FILTER_ORDER } from "../../Common/constants";
+import routes from "../../Redux/api";
+import useQuery from "../../Utils/request/useQuery";
+
+function useFacilityQuery(facilityId: string | undefined) {
+ return useQuery(routes.getAnyFacility, {
+ pathParams: { id: String(facilityId) },
+ prefetch: facilityId !== undefined,
+ });
+}
export default function BadgesList(props: any) {
const { appliedFilters, FilterBadges } = props;
- const [orginFacilityName, setOrginFacilityName] = useState("");
- const [approvingFacilityName, setApprovingFacilityName] = useState("");
- const [assignedFacilityName, setAssignedFacilityName] = useState("");
- const dispatch: any = useDispatch();
-
- useEffect(() => {
- async function fetchData() {
- if (!appliedFilters.origin_facility) return setOrginFacilityName("");
- const res = await dispatch(
- getAnyFacility(appliedFilters.origin_facility, "origin_facility_name")
- );
- setOrginFacilityName(res?.data?.name);
- }
- fetchData();
- }, [dispatch, appliedFilters.origin_facility]);
-
- useEffect(() => {
- async function fetchData() {
- if (!appliedFilters.approving_facility)
- return setApprovingFacilityName("");
- const res = await dispatch(
- getAnyFacility(
- appliedFilters.approving_facility,
- "approving_facility_name"
- )
- );
- setApprovingFacilityName(res?.data?.name);
- }
- fetchData();
- }, [dispatch, appliedFilters.approving_facility]);
-
- useEffect(() => {
- async function fetchData() {
- if (!appliedFilters.assigned_facility) return setAssignedFacilityName("");
- const res = await dispatch(
- getAnyFacility(
- appliedFilters.assigned_facility,
- "assigned_facility_name"
- )
- );
- setAssignedFacilityName(res?.data?.name);
- }
- fetchData();
- }, [dispatch, appliedFilters.assigned_facility]);
+ const originFacility = useFacilityQuery(appliedFilters.origin_facility);
+ const approvingFacility = useFacilityQuery(appliedFilters.approving_facility);
+ const assignedFacility = useFacilityQuery(appliedFilters.assigned_facility);
const getDescShiftingFilterOrder = (ordering: any) => {
- let desc = "";
- SHIFTING_FILTER_ORDER.map((item: any) => {
- if (item.text === ordering) {
- desc = item.desc;
- }
- });
- return desc;
+ const foundItem = SHIFTING_FILTER_ORDER.find(
+ (item) => item.text === ordering
+ );
+ return foundItem ? foundItem.desc : "";
};
return (
@@ -75,13 +37,25 @@ export default function BadgesList(props: any) {
}),
...dateRange("Modified", "modified_date"),
...dateRange("Created", "created_date"),
- value("Origin facility", "origin_facility", orginFacilityName),
+ value(
+ "Origin facility",
+ "origin_facility",
+ appliedFilters.origin_facility ? originFacility?.data?.name || "" : ""
+ ),
value(
"Approving facility",
"approving_facility",
- approvingFacilityName
+ appliedFilters.approving_facility
+ ? approvingFacility?.data?.name || ""
+ : ""
+ ),
+ value(
+ "Assigned facility",
+ "assigned_facility",
+ appliedFilters.assigned_facility
+ ? assignedFacility?.data?.name || ""
+ : ""
),
- value("Assigned facility", "assigned_facility", assignedFacilityName),
]}
/>
);
diff --git a/src/Components/Resource/CommentSection.tsx b/src/Components/Resource/CommentSection.tsx
index 01bd67454f5..25d8142dae7 100644
--- a/src/Components/Resource/CommentSection.tsx
+++ b/src/Components/Resource/CommentSection.tsx
@@ -1,60 +1,26 @@
-import { useCallback, useState } from "react";
-import { useDispatch } from "react-redux";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-import { getResourceComments, addResourceComments } from "../../Redux/actions";
+import { useState } from "react";
import * as Notification from "../../Utils/Notifications.js";
-import Pagination from "../Common/Pagination";
import { formatDateTime } from "../../Utils/utils";
import CircularProgress from "../Common/components/CircularProgress";
import ButtonV2 from "../Common/components/ButtonV2";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
+import useQuery from "../../Utils/request/useQuery";
+import routes from "../../Redux/api";
+import PaginatedList from "../../CAREUI/misc/PaginatedList";
+import { IComment } from "./models";
+import request from "../../Utils/request/request";
-interface CommentSectionProps {
- id: string;
-}
-const CommentSection = (props: CommentSectionProps) => {
- const dispatch: any = useDispatch();
- const initialData: any = [];
- const [comments, setComments] = useState(initialData);
+const CommentSection = (props: { id: string }) => {
const [commentBox, setCommentBox] = useState("");
- const [isLoading, setIsLoading] = useState(true);
-
- const [currentPage, setCurrentPage] = useState(1);
- const [totalCount, setTotalCount] = useState(0);
- const [offset, setOffset] = useState(0);
- const limit = 8;
-
- const handlePagination = (page: number, limit: number) => {
- const offset = (page - 1) * limit;
- setCurrentPage(page);
- setOffset(offset);
- };
-
- const fetchData = useCallback(
- async (status: statusType = { aborted: false }) => {
- setIsLoading(true);
- const res = await dispatch(
- getResourceComments(props.id, { limit, offset })
- );
- if (!status.aborted) {
- if (res && res.data) {
- setComments(res.data?.results);
- setTotalCount(res.data.count);
- }
- setIsLoading(false);
- }
- },
- [props.id, dispatch, offset]
- );
-
- useAbortableEffect(
- (status: statusType) => {
- fetchData(status);
- },
- [fetchData]
+ const { loading, refetch: resourceRefetch } = useQuery(
+ routes.getResourceComments,
+ {
+ pathParams: { id: props.id },
+ query: { limit: 8, offset: 0 },
+ }
);
- const onSubmitComment = () => {
+ const onSubmitComment = async () => {
const payload = {
comment: commentBox,
};
@@ -64,13 +30,16 @@ const CommentSection = (props: CommentSectionProps) => {
});
return;
}
- dispatch(addResourceComments(props.id, payload)).then((_: any) => {
- Notification.Success({ msg: "Comment added successfully" });
- fetchData();
+ const { res } = await request(routes.addResourceComments, {
+ pathParams: { id: props.id },
+ body: payload,
});
+ if (res?.ok) {
+ Notification.Success({ msg: "Comment added successfully" });
+ resourceRefetch();
+ }
setCommentBox("");
};
-
return (
{
Post Your Comment
- {isLoading ? (
+ {loading ? (
) : (
- comments.map((comment: any) => (
-
-
-
-
- {formatDateTime(comment.modified_date) || "-"}
-
-
-
-
- {comment.created_by_object?.first_name?.charAt(0) || "U"}
+
+ {() => (
+
+
+ No comments available
+
+
+
+
+
>
+ {(item) => }
+
+
-
- {comment.created_by_object?.first_name || "Unknown"}{" "}
- {comment.created_by_object?.last_name}
-
-
- ))
+ )}
+
)}
- {totalCount > limit && (
-
- )}
);
};
export default CommentSection;
+
+export const Comment = ({
+ comment,
+ created_by_object,
+ modified_date,
+}: IComment) => (
+
+
+
+
+ {formatDateTime(modified_date) || "-"}
+
+
+
+
+ {created_by_object?.first_name?.charAt(0) || "U"}
+
+
+ {created_by_object?.first_name || "Unknown"}{" "}
+ {created_by_object?.last_name}
+
+
+
+);
diff --git a/src/Components/Resource/ListFilter.tsx b/src/Components/Resource/ListFilter.tsx
index 7a47732db64..afe48eedfd0 100644
--- a/src/Components/Resource/ListFilter.tsx
+++ b/src/Components/Resource/ListFilter.tsx
@@ -1,8 +1,5 @@
-import { useEffect, useState } from "react";
import { FacilitySelect } from "../Common/FacilitySelect";
import { RESOURCE_FILTER_ORDER } from "../../Common/constants";
-import { getAnyFacility } from "../../Redux/actions";
-import { useDispatch } from "react-redux";
import { RESOURCE_CHOICES } from "../../Common/constants";
import useMergeState from "../../Common/hooks/useMergeState";
import { navigate } from "raviger";
@@ -15,6 +12,8 @@ import { DateRange } from "../Common/DateRangeInputV2";
import DateRangeFormField from "../Form/FormFields/DateRangeFormField";
import dayjs from "dayjs";
import { dateQueryString } from "../../Utils/utils";
+import useQuery from "../../Utils/request/useQuery";
+import routes from "../../Redux/api";
const clearFilterState = {
origin_facility: "",
@@ -37,9 +36,6 @@ const getDate = (value: any) =>
export default function ListFilter(props: any) {
const { filter, onChange, closeFilter } = props;
- const [isOriginLoading, setOriginLoading] = useState(false);
- const [isResourceLoading, setResourceLoading] = useState(false);
- const [isAssignedLoading, setAssignedLoading] = useState(false);
const [filterState, setFilterState] = useMergeState({
origin_facility: filter.origin_facility || "",
origin_facility_ref: null,
@@ -55,55 +51,42 @@ export default function ListFilter(props: any) {
ordering: filter.ordering || null,
status: filter.status || null,
});
- const dispatch: any = useDispatch();
- useEffect(() => {
- async function fetchData() {
- if (filter.origin_facility) {
- setOriginLoading(true);
- const res = await dispatch(
- getAnyFacility(filter.origin_facility, "origin_facility")
- );
- if (res && res.data) {
- setFilterState({ origin_facility_ref: res.data });
- }
- setOriginLoading(false);
+ const { loading: orginFacilityLoading } = useQuery(routes.getAnyFacility, {
+ prefetch: filter.origin_facility !== undefined,
+ pathParams: { id: filter.origin_facility },
+ onResponse: ({ res, data }) => {
+ if (res && data) {
+ setFilterState({
+ origin_facility_ref: filter.origin_facility === "" ? "" : data,
+ });
}
- }
- fetchData();
- }, [dispatch]);
+ },
+ });
- useEffect(() => {
- async function fetchData() {
- if (filter.approving_facility) {
- setResourceLoading(true);
- const res = await dispatch(
- getAnyFacility(filter.approving_facility, "approving_facility")
- );
- if (res && res.data) {
- setFilterState({ approving_facility_ref: res.data });
- }
- setResourceLoading(false);
+ const { loading: resourceFacilityLoading } = useQuery(routes.getAnyFacility, {
+ prefetch: filter.approving_facility !== undefined,
+ pathParams: { id: filter.approving_facility },
+ onResponse: ({ res, data }) => {
+ if (res && data) {
+ setFilterState({
+ approving_facility_ref: filter.approving_facility === "" ? "" : data,
+ });
}
- }
- fetchData();
- }, [dispatch]);
+ },
+ });
- useEffect(() => {
- async function fetchData() {
- if (filter.assigned_facility) {
- setAssignedLoading(true);
- const res = await dispatch(
- getAnyFacility(filter.assigned_facility, "assigned_facility")
- );
- if (res && res.data) {
- setFilterState({ assigned_facility_ref: res.data });
- }
- setAssignedLoading(false);
+ const { loading: assignedFacilityLoading } = useQuery(routes.getAnyFacility, {
+ pathParams: { id: filter.assigned_facility },
+ prefetch: filter.assigned_facility !== undefined,
+ onResponse: ({ res, data }) => {
+ if (res && data) {
+ setFilterState({
+ assigned_facility_ref: filter.assigned_facility === "" ? "" : data,
+ });
}
- }
- fetchData();
- }, [dispatch]);
+ },
+ });
const setFacility = (selected: any, name: string) => {
setFilterState({
@@ -178,7 +161,7 @@ export default function ListFilter(props: any) {
Origin facility
- {isOriginLoading ? (
+ {orginFacilityLoading && filter.origin_facility ? (
) : (
Resource approving facility
- {isResourceLoading ? (
+ {filter.approving_facility && resourceFacilityLoading ? (
) : (
Assigned facility
- {isAssignedLoading ? (
+ {filter.approving_facility && assignedFacilityLoading ? (
) : (
import("../Common/Loading"));
const PageTitle = lazy(() => import("../Common/PageTitle"));
export default function ListView() {
- const dispatch: any = useDispatch();
const { qParams, Pagination, FilterBadges, advancedFilter, resultsPerPage } =
useFilters({});
- const [data, setData] = useState([]);
- const [totalCount, setTotalCount] = useState(0);
- const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation();
const onBoardViewBtnClick = () =>
navigate("/resource/board", { query: qParams });
const appliedFilters = formatFilter(qParams);
- const refreshList = () => {
- fetchData();
- };
-
- const fetchData = () => {
- setIsLoading(true);
- dispatch(
- listResourceRequests(
- formatFilter({
- ...qParams,
- offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage,
- }),
- "resource-list-call"
- )
- ).then((res: any) => {
- if (res && res.data) {
- setData(res.data.results);
- setTotalCount(res.data.count);
- }
- setIsLoading(false);
- });
- };
-
- useEffect(() => {
- fetchData();
- }, [
- qParams.status,
- qParams.facility,
- qParams.origin_facility,
- qParams.approving_facility,
- qParams.assigned_facility,
- qParams.emergency,
- qParams.created_date_before,
- qParams.created_date_after,
- qParams.modified_date_before,
- qParams.modified_date_after,
- qParams.ordering,
- qParams.page,
- ]);
+ const { loading, data, refetch } = useQuery(routes.listResourceRequests, {
+ query: formatFilter({
+ ...qParams,
+ offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage,
+ }),
+ });
const showResourceCardList = (data: any) => {
if (data && !data.length) {
@@ -218,14 +178,14 @@ export default function ListView() {
- {isLoading ? (
+ {loading ? (
) : (
- {showResourceCardList(data)}
+ {data?.results && showResourceCardList(data?.results)}
-
+
)}
diff --git a/src/Components/Resource/ResourceBoard.tsx b/src/Components/Resource/ResourceBoard.tsx
index 66bf559be0d..217f2941e10 100644
--- a/src/Components/Resource/ResourceBoard.tsx
+++ b/src/Components/Resource/ResourceBoard.tsx
@@ -1,15 +1,13 @@
import { useState, useEffect } from "react";
-import { useDispatch } from "react-redux";
-import {
- listResourceRequests,
- downloadResourceRequests,
-} from "../../Redux/actions";
+import { downloadResourceRequests } from "../../Redux/actions";
import { navigate } from "raviger";
import { classNames } from "../../Utils/utils";
import { useDrag, useDrop } from "react-dnd";
import { formatDateTime } from "../../Utils/utils";
import { ExportButton } from "../Common/Export";
import dayjs from "../../Utils/dayjs";
+import useQuery from "../../Utils/request/useQuery";
+import routes from "../../Redux/api";
const limit = 14;
@@ -118,7 +116,6 @@ const ResourceCard = ({ resource }: any) => {
-
{resource.assigned_to_object && (
- {
)}
-
@@ -273,13 +258,14 @@ export default function ResourceBoard({
- ) : data?.length > 0 ? (
+ ) : data && data?.results.length > 0 ? (
boardFilter(board)
) : (
No requests to show.
)}
{!isLoading.board &&
- data?.length < (totalCount || 0) &&
+ data &&
+ data?.results.length < (data?.count || 0) &&
(isLoading.more ? (
Loading
diff --git a/src/Components/Resource/ResourceCreate.tsx b/src/Components/Resource/ResourceCreate.tsx
index cf9e5f6f22a..d0a6c36272b 100644
--- a/src/Components/Resource/ResourceCreate.tsx
+++ b/src/Components/Resource/ResourceCreate.tsx
@@ -1,8 +1,7 @@
-import { useReducer, useState, useEffect, lazy } from "react";
+import { useReducer, useState, lazy } from "react";
import { FacilitySelect } from "../Common/FacilitySelect";
import * as Notification from "../../Utils/Notifications.js";
-import { useDispatch } from "react-redux";
import { navigate } from "raviger";
import {
OptionsType,
@@ -11,8 +10,6 @@ import {
} from "../../Common/constants";
import { parsePhoneNumber } from "../../Utils/utils";
import { phonePreg } from "../../Common/validation";
-
-import { createResource, getAnyFacility } from "../../Redux/actions";
import { Cancel, Submit } from "../Common/components/ButtonV2";
import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField";
import { FieldChangeEvent } from "../Form/FormFields/Utils";
@@ -26,6 +23,9 @@ import { FieldLabel } from "../Form/FormFields/FormField";
import Card from "../../CAREUI/display/Card";
import Page from "../Common/components/Page";
import { PhoneNumberValidator } from "../Form/FieldValidators";
+import useQuery from "../../Utils/request/useQuery";
+import routes from "../../Redux/api";
+import request from "../../Utils/request/request";
const Loading = lazy(() => import("../Common/Loading"));
@@ -87,10 +87,7 @@ export default function ResourceCreate(props: resourceProps) {
const { goBack } = useAppHistory();
const { facilityId } = props;
const { t } = useTranslation();
-
- const dispatchAction: any = useDispatch();
const [isLoading, setIsLoading] = useState(false);
- const [facilityName, setFacilityName] = useState("");
const resourceFormReducer = (state = initialState, action: any) => {
switch (action.type) {
@@ -113,18 +110,10 @@ export default function ResourceCreate(props: resourceProps) {
const [state, dispatch] = useReducer(resourceFormReducer, initialState);
- useEffect(() => {
- async function fetchFacilityName() {
- if (facilityId) {
- const res = await dispatchAction(getAnyFacility(facilityId));
-
- setFacilityName(res?.data?.name || "");
- } else {
- setFacilityName("");
- }
- }
- fetchFacilityName();
- }, [dispatchAction, facilityId]);
+ const { data: facilityData } = useQuery(routes.getAnyFacility, {
+ prefetch: facilityId !== undefined,
+ pathParams: { id: String(facilityId) },
+ });
const validateForm = () => {
const errors = { ...initError };
@@ -184,11 +173,11 @@ export default function ResourceCreate(props: resourceProps) {
if (validForm) {
setIsLoading(true);
- const data = {
+ const resourceData = {
status: "PENDING",
category: state.form.category,
sub_category: state.form.sub_category,
- origin_facility: props.facilityId,
+ origin_facility: String(props.facilityId),
approving_facility: (state.form.approving_facility || {}).id,
assigned_facility: (state.form.assigned_facility || {}).id,
emergency: state.form.emergency === "true",
@@ -202,16 +191,18 @@ export default function ResourceCreate(props: resourceProps) {
requested_quantity: state.form.requested_quantity || 0,
};
- const res = await dispatchAction(createResource(data));
+ const { res, data } = await request(routes.createResource, {
+ body: resourceData,
+ });
setIsLoading(false);
- if (res && res.data && (res.status == 201 || res.status == 200)) {
+ if (res?.ok && data) {
await dispatch({ type: "set_form", form: initForm });
Notification.Success({
msg: "Resource request created successfully",
});
- navigate(`/resource/${res.data.id}`);
+ navigate(`/resource/${data.id}`);
}
}
};
@@ -224,7 +215,7 @@ export default function ResourceCreate(props: resourceProps) {
import("../Common/Loading"));
export default function ResourceDetails(props: { id: string }) {
- const dispatch: any = useDispatch();
- const initialData: any = {};
- const [data, setData] = useState(initialData);
- const [isLoading, setIsLoading] = useState(true);
const [isPrintMode, setIsPrintMode] = useState(false);
-
const [openDeleteResourceDialog, setOpenDeleteResourceDialog] =
useState(false);
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const res = await dispatch(getResourceDetails({ id: props.id }));
- if (!status.aborted) {
- if (res && res.data) {
- setData(res.data);
- } else {
- navigate("/not-found");
- }
- setIsLoading(false);
+ const { data, loading } = useQuery(routes.getResourceDetails, {
+ pathParams: { id: props.id },
+ onResponse: ({ res, data }) => {
+ if (!res && !data) {
+ navigate("/not-found");
}
},
- [props.id, dispatch]
- );
-
- useAbortableEffect(
- (status: statusType) => {
- fetchData(status);
- },
- [fetchData]
- );
+ });
const handleResourceDelete = async () => {
setOpenDeleteResourceDialog(true);
-
- const res = await dispatch(deleteResourceRecord(props.id));
+ const { res, data } = await request(routes.deleteResourceRecord, {
+ pathParams: { id: props.id },
+ });
if (res?.status === 204) {
Notification.Success({
msg: "Resource record has been deleted successfully.",
});
} else {
Notification.Error({
- msg: "Error while deleting Resource: " + (res?.data?.detail || ""),
+ msg: "Error while deleting Resource: " + (data?.detail || ""),
});
}
@@ -223,7 +203,7 @@ export default function ResourceDetails(props: { id: string }) {
);
};
- if (isLoading) {
+ if (loading || !data) {
return ;
}
diff --git a/src/Components/Resource/ResourceDetailsUpdate.tsx b/src/Components/Resource/ResourceDetailsUpdate.tsx
index ce0751e7b2e..c692618a774 100644
--- a/src/Components/Resource/ResourceDetailsUpdate.tsx
+++ b/src/Components/Resource/ResourceDetailsUpdate.tsx
@@ -1,15 +1,7 @@
import * as Notification from "../../Utils/Notifications.js";
-
import { Cancel, Submit } from "../Common/components/ButtonV2";
-import { lazy, useCallback, useEffect, useReducer, useState } from "react";
-import {
- getResourceDetails,
- getUserList,
- updateResource,
-} from "../../Redux/actions";
+import { lazy, useReducer, useState } from "react";
import { navigate, useQueryParams } from "raviger";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-
import Card from "../../CAREUI/display/Card";
import CircularProgress from "../Common/components/CircularProgress";
import { FacilitySelect } from "../Common/FacilitySelect";
@@ -22,9 +14,11 @@ import { SelectFormField } from "../Form/FormFields/SelectFormField";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
import TextFormField from "../Form/FormFields/TextFormField";
import UserAutocompleteFormField from "../Common/UserAutocompleteFormField";
-
import useAppHistory from "../../Common/hooks/useAppHistory";
-import { useDispatch } from "react-redux";
+import useQuery from "../../Utils/request/useQuery.js";
+import routes from "../../Redux/api.js";
+import { UserModel } from "../Users/models.js";
+import request from "../../Utils/request/request.js";
const Loading = lazy(() => import("../Common/Loading"));
@@ -67,13 +61,9 @@ const initialState = {
export const ResourceDetailsUpdate = (props: resourceProps) => {
const { goBack } = useAppHistory();
- const dispatchAction: any = useDispatch();
const [qParams, _] = useQueryParams();
const [isLoading, setIsLoading] = useState(true);
- const [assignedQuantity, setAssignedQuantity] = useState(0);
- const [requestTitle, setRequestTitle] = useState("");
- const [assignedUser, SetAssignedUser] = useState(null);
- const [assignedUserLoading, setAssignedUserLoading] = useState(false);
+ const [assignedUser, SetAssignedUser] = useState();
const resourceFormReducer = (state = initialState, action: any) => {
switch (action.type) {
case "set_form": {
@@ -95,23 +85,13 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
const [state, dispatch] = useReducer(resourceFormReducer, initialState);
- useEffect(() => {
- async function fetchData() {
- if (state.form.assigned_to) {
- setAssignedUserLoading(true);
-
- const res = await dispatchAction(
- getUserList({ id: state.form.assigned_to })
- );
-
- if (res && res.data && res.data.count)
- SetAssignedUser(res.data.results[0]);
-
- setAssignedUserLoading(false);
+ const { loading: assignedUserLoading } = useQuery(routes.userList, {
+ onResponse: ({ res, data }) => {
+ if (res?.ok && data && data.count) {
+ SetAssignedUser(data.results[0]);
}
- }
- fetchData();
- }, [dispatchAction, state.form.assigned_to]);
+ },
+ });
const validateForm = () => {
const errors = { ...initError };
@@ -147,13 +127,25 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
dispatch({ type: "set_form", form });
};
+ const { data: resourceDetails } = useQuery(routes.getResourceDetails, {
+ pathParams: { id: props.id },
+ onResponse: ({ res, data }) => {
+ if (res && data) {
+ const d = data;
+ d["status"] = qParams.status || data.status;
+ dispatch({ type: "set_form", form: d });
+ }
+ setIsLoading(false);
+ },
+ });
+
const handleSubmit = async () => {
const validForm = validateForm();
if (validForm) {
setIsLoading(true);
- const data = {
+ const resourceData = {
category: "OXYGEN",
status: state.form.status,
origin_facility: state.form.origin_facility_object?.id,
@@ -167,14 +159,17 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
assigned_quantity:
state.form.status === "PENDING"
? state.form.assigned_quantity
- : assignedQuantity,
+ : resourceDetails?.assigned_quantity || 0,
};
- const res = await dispatchAction(updateResource(props.id, data));
+ const { res, data } = await request(routes.updateResource, {
+ pathParams: { id: props.id },
+ body: resourceData,
+ });
setIsLoading(false);
- if (res && res.status == 200 && res.data) {
- dispatch({ type: "set_form", form: res.data });
+ if (res && res.status == 200 && data) {
+ dispatch({ type: "set_form", form: data });
Notification.Success({
msg: "Resource request updated successfully",
});
@@ -186,31 +181,6 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
}
};
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const res = await dispatchAction(getResourceDetails({ id: props.id }));
- if (!status.aborted) {
- if (res && res.data) {
- setRequestTitle(res.data.title);
- setAssignedQuantity(res.data.assigned_quantity);
- const d = res.data;
- d["status"] = qParams.status || res.data.status;
- dispatch({ type: "set_form", form: d });
- }
- setIsLoading(false);
- }
- },
- [props.id, dispatchAction, qParams.status]
- );
-
- useAbortableEffect(
- (status: statusType) => {
- fetchData(status);
- },
- [fetchData]
- );
-
if (isLoading) {
return ;
}
@@ -219,7 +189,7 @@ export const ResourceDetailsUpdate = (props: resourceProps) => {
diff --git a/src/Components/Resource/models.ts b/src/Components/Resource/models.ts
new file mode 100644
index 00000000000..f10ac988552
--- /dev/null
+++ b/src/Components/Resource/models.ts
@@ -0,0 +1,41 @@
+import { PerformedByModel } from "../HCX/misc";
+
+export interface IComment {
+ id: string;
+ created_by_object: PerformedByModel;
+ created_date: string;
+ modified_date: string;
+ comment: string;
+ created_by: number;
+}
+
+export interface IResource {
+ id: string;
+ title: string;
+ emergency: boolean;
+ status?: string;
+ origin_facility_object: {
+ name: string;
+ };
+ approving_facility_object: {
+ name: string;
+ };
+ assigned_facility_object: {
+ name: string;
+ };
+ assigned_quantity: number;
+ modified_date: string;
+ category: any;
+ sub_category: number;
+ origin_facility: string;
+ approving_facility: string;
+ assigned_facility: string;
+ reason: string;
+ refering_facility_contact_name: string;
+ refering_facility_contact_number: string;
+ requested_quantity: number;
+ assigned_to_object: PerformedByModel;
+ created_by_object: PerformedByModel;
+ created_date: string;
+ last_edited_by_object: PerformedByModel;
+}
diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx
index 1d2d6f4f7b6..15934d0957d 100644
--- a/src/Redux/actions.tsx
+++ b/src/Redux/actions.tsx
@@ -803,30 +803,9 @@ export const listMedibaseMedicines = (
};
// Resource
-export const createResource = (params: object) => {
- return fireRequest("createResource", [], params);
-};
-export const updateResource = (id: string, params: object) => {
- return fireRequest("updateResource", [id], params);
-};
-export const deleteResourceRecord = (id: string) => {
- return fireRequest("deleteResourceRecord", [id], {});
-};
-export const listResourceRequests = (params: object, key: string) => {
- return fireRequest("listResourceRequests", [], params, null, key);
-};
-export const getResourceDetails = (pathParam: object) => {
- return fireRequest("getResourceDetails", [], {}, pathParam);
-};
export const downloadResourceRequests = (params: object) => {
return fireRequest("downloadResourceRequests", [], params);
};
-export const getResourceComments = (id: string, params: object) => {
- return fireRequest("getResourceComments", [], params, { id });
-};
-export const addResourceComments = (id: string, params: object) => {
- return fireRequest("addResourceComments", [], params, { id });
-};
export const listAssets = (params: object) =>
fireRequest("listAssets", [], params);
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 104fa0c0c75..01c356f6603 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -28,6 +28,7 @@ import {
AssetUpdate,
} from "../Components/Assets/AssetTypes";
import {
+ ConsultationModel,
FacilityModel,
LocationModel,
WardModel,
@@ -40,10 +41,13 @@ import {
ILocalBodyByDistrict,
IPartialUpdateExternalResult,
} from "../Components/ExternalResult/models";
+
import { Prescription } from "../Components/Medicine/models";
-import { PatientModel } from "../Components/Patient/models";
+
import { UserModel } from "../Components/Users/models";
import { PaginatedResponse } from "../Utils/request/types";
+import { PatientModel } from "../Components/Patient/models";
+import { IComment, IResource } from "../Components/Resource/models";
/**
* A fake function that returns an empty object casted to type T
@@ -128,6 +132,8 @@ const routes = {
userList: {
path: "/api/v1/users/",
+ method: "GET",
+ TRes: Type>(),
},
userListSkill: {
@@ -843,21 +849,31 @@ const routes = {
createResource: {
path: "/api/v1/resource/",
method: "POST",
+ TRes: Type(),
+ TBody: Type>(),
},
updateResource: {
- path: "/api/v1/resource",
+ path: "/api/v1/resource/{id}",
method: "PUT",
+ TRes: Type(),
+ TBody: Type>(),
},
deleteResourceRecord: {
- path: "/api/v1/resource",
+ path: "/api/v1/resource/{id}",
method: "DELETE",
+ TRes: Type<{
+ detail?: string;
+ }>(),
},
listResourceRequests: {
path: "/api/v1/resource/",
method: "GET",
+ TRes: Type>(),
},
getResourceDetails: {
path: "/api/v1/resource/{id}/",
+ method: "GET",
+ TRes: Type(),
},
downloadResourceRequests: {
path: "/api/v1/resource/",
@@ -866,10 +882,13 @@ const routes = {
getResourceComments: {
path: "/api/v1/resource/{id}/comment/",
method: "GET",
+ TRes: Type>(),
},
addResourceComments: {
path: "/api/v1/resource/{id}/comment/",
method: "POST",
+ TRes: Type(),
+ TBody: Type>(),
},
// Assets endpoints
From 343ff7eeb01623e4c521fdb9ba2bc1c4fa895483 Mon Sep 17 00:00:00 2001
From: Ashraf Mohammed <98876115+AshrafMd-1@users.noreply.github.com>
Date: Tue, 31 Oct 2023 04:17:38 -0700
Subject: [PATCH 05/11] Fixed bed number coming up two time in patients page.
(#6526)
* stop displaying bed name two times
* resolve tooltip
---
src/Components/Patient/ManagePatients.tsx | 26 ++++++++++-------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx
index 61b18faa367..7db0bcceb39 100644
--- a/src/Components/Patient/ManagePatients.tsx
+++ b/src/Components/Patient/ManagePatients.tsx
@@ -519,27 +519,23 @@ export const PatientManager = () => {
{patient?.last_consultation?.current_bed &&
patient?.last_consultation?.discharge_date === null ? (
-
-
+
+
{
patient?.last_consultation?.current_bed?.bed_object
?.location_object?.name
}
-
- {
- patient?.last_consultation?.current_bed?.bed_object
- ?.location_object?.name
- }
-
- {patient?.last_consultation?.current_bed?.bed_object.name}
-
- {
- patient?.last_consultation?.current_bed?.bed_object
- ?.name
- }
-
+ {patient?.last_consultation?.current_bed?.bed_object?.name}
+
+
+ {
+ patient?.last_consultation?.current_bed?.bed_object
+ ?.location_object?.name
+ }
+
+ {patient?.last_consultation?.current_bed?.bed_object?.name}
) : patient.last_consultation?.suggestion === "DC" ? (
From b43f8d577e12b5b144ffe6772807eace8968629a Mon Sep 17 00:00:00 2001
From: Onkar Jadhav <56870381+Omkar76@users.noreply.github.com>
Date: Tue, 31 Oct 2023 16:51:06 +0530
Subject: [PATCH 06/11] Make header in patient dashboard more responsive. Fixes
#6488 (#6515)
---
.../Facility/ConsultationDetails/index.tsx | 40 +++++++++----------
1 file changed, 19 insertions(+), 21 deletions(-)
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx
index cedb1a843d3..202c9200b0a 100644
--- a/src/Components/Facility/ConsultationDetails/index.tsx
+++ b/src/Components/Facility/ConsultationDetails/index.tsx
@@ -271,7 +271,7 @@ export const ConsultationDetails = (props: any) => {
/>
-
{value.length !== 0 && (
diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx
index 7db0bcceb39..7a78d94582e 100644
--- a/src/Components/Patient/ManagePatients.tsx
+++ b/src/Components/Patient/ManagePatients.tsx
@@ -16,9 +16,14 @@ import {
getAllPatient,
getAnyFacility,
getDistrict,
+ getFacilityAssetLocation,
getLocalBody,
} from "../../Redux/actions";
-import { statusType, useAbortableEffect } from "../../Common/utils";
+import {
+ statusType,
+ useAbortableEffect,
+ parseOptionId,
+} from "../../Common/utils";
import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover";
import ButtonV2 from "../Common/components/ButtonV2";
@@ -36,7 +41,6 @@ import RecordMeta from "../../CAREUI/display/RecordMeta";
import SearchInput from "../Form/SearchInput";
import SortDropdownMenu from "../Common/SortDropdown";
import SwitchTabs from "../Common/components/SwitchTabs";
-import { parseOptionId } from "../../Common/utils";
import { formatAge, parsePhoneNumber } from "../../Utils/utils.js";
import { useDispatch } from "react-redux";
import useFilters from "../../Common/hooks/useFilters";
@@ -104,6 +108,7 @@ export const PatientManager = () => {
const [districtName, setDistrictName] = useState("");
const [localbodyName, setLocalbodyName] = useState("");
const [facilityBadgeName, setFacilityBadge] = useState("");
+ const [locationBadgeName, setLocationBadge] = useState("");
const [phone_number, setPhoneNumber] = useState("");
const [phoneNumberError, setPhoneNumberError] = useState("");
const [emergency_phone_number, setEmergencyPhoneNumber] = useState("");
@@ -199,6 +204,8 @@ export const PatientManager = () => {
qParams.last_consultation_admitted_bed_type_list || undefined,
last_consultation_discharge_reason:
qParams.last_consultation_discharge_reason || undefined,
+ last_consultation_current_bed__location:
+ qParams.last_consultation_current_bed__location || undefined,
srf_id: qParams.srf_id || undefined,
number_of_doses: qParams.number_of_doses || undefined,
covin_id: qParams.covin_id || undefined,
@@ -344,6 +351,7 @@ export const PatientManager = () => {
qParams.age_min,
qParams.last_consultation_admitted_bed_type_list,
qParams.last_consultation_discharge_reason,
+ qParams.last_consultation_current_bed__location,
qParams.facility,
qParams.facility_type,
qParams.district,
@@ -443,12 +451,32 @@ export const PatientManager = () => {
[dispatch, qParams.facility]
);
+ const fetchLocationBadgeName = useCallback(
+ async (status: statusType) => {
+ const res =
+ qParams.last_consultation_current_bed__location &&
+ (await dispatch(
+ getFacilityAssetLocation(
+ qParams.facility,
+ qParams.last_consultation_current_bed__location
+ )
+ ));
+
+ if (!status.aborted) {
+ setLocationBadge(res?.data?.name);
+ }
+ },
+ [dispatch, qParams.last_consultation_current_bed__location]
+ );
+
useAbortableEffect(
(status: statusType) => {
fetchFacilityBadgeName(status);
+ fetchLocationBadgeName(status);
},
- [fetchFacilityBadgeName]
+ [fetchFacilityBadgeName, fetchLocationBadgeName]
);
+
const LastAdmittedToTypeBadges = () => {
const badge = (key: string, value: any, id: string) => {
return (
@@ -936,6 +964,11 @@ export const PatientManager = () => {
"last_consultation_medico_legal_case"
),
value("Facility", "facility", facilityBadgeName),
+ value(
+ "Location",
+ "last_consultation_current_bed__location",
+ locationBadgeName
+ ),
badge("Facility Type", "facility_type"),
value("District", "district", districtName),
ordering(),
diff --git a/src/Components/Patient/PatientFilter.tsx b/src/Components/Patient/PatientFilter.tsx
index 97f5a6c6b9c..7bc7f7b861f 100644
--- a/src/Components/Patient/PatientFilter.tsx
+++ b/src/Components/Patient/PatientFilter.tsx
@@ -34,6 +34,7 @@ import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover";
import AccordionV2 from "../Common/components/AccordionV2";
import { dateQueryString } from "../../Utils/utils";
import dayjs from "dayjs";
+import { LocationSelect } from "../Common/LocationSelect";
const getDate = (value: any) =>
value && dayjs(value).isValid() && dayjs(value).toDate();
@@ -79,6 +80,8 @@ export default function PatientFilter(props: any) {
filter.last_consultation_admitted_bed_type_list
? filter.last_consultation_admitted_bed_type_list.split(",")
: [],
+ last_consultation_current_bed__location:
+ filter.last_consultation_current_bed__location || "",
last_consultation_discharge_reason:
filter.last_consultation_discharge_reason || null,
srf_id: filter.srf_id || null,
@@ -128,6 +131,7 @@ export default function PatientFilter(props: any) {
last_consultation_discharge_date_before: "",
last_consultation_discharge_date_after: "",
last_consultation_admitted_to_list: [],
+ last_consultation_current_bed__location: "",
srf_id: "",
number_of_doses: null,
covin_id: "",
@@ -239,6 +243,7 @@ export default function PatientFilter(props: any) {
last_consultation_discharge_date_after,
last_consultation_admitted_bed_type_list,
last_consultation_discharge_reason,
+ last_consultation_current_bed__location,
number_of_doses,
covin_id,
srf_id,
@@ -256,6 +261,8 @@ export default function PatientFilter(props: any) {
district: district || "",
lsgBody: lsgBody || "",
facility: facility || "",
+ last_consultation_current_bed__location:
+ last_consultation_current_bed__location || "",
facility_type: facility_type || "",
date_declared_positive_before: dateQueryString(
date_declared_positive_before
@@ -588,13 +595,29 @@ export default function PatientFilter(props: any) {
setFacility(obj, "facility")}
/>
-
+ Location
+
+ setFilterState({
+ ...filterState,
+ last_consultation_current_bed__location: selected,
+ })
+ }
+ />
+
+
Facility type
Date: Tue, 31 Oct 2023 16:52:38 +0530
Subject: [PATCH 08/11] Show only those facilties that aren't linked to user
(#6253)
* only show unlinked facilties
* rename param
* fix cypress for updated backend
* fix typo
---
cypress/e2e/users_spec/user_creation.cy.ts | 9 +--------
src/Components/Common/FacilitySelect.tsx | 3 +++
src/Components/Users/ManageUsers.tsx | 1 +
3 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/cypress/e2e/users_spec/user_creation.cy.ts b/cypress/e2e/users_spec/user_creation.cy.ts
index 5a1e5d874de..bd5002d386f 100644
--- a/cypress/e2e/users_spec/user_creation.cy.ts
+++ b/cypress/e2e/users_spec/user_creation.cy.ts
@@ -202,14 +202,7 @@ describe("User Creation", () => {
// .click()
// .type("Dummy Facility 1")
// .wait("@getFacilities");
- // cy.get("li[role='option']").first().click();
- // cy.intercept(/\/api\/v1\/users\/\w+\/add_facility\//).as("addFacility");
- // cy.get("button[id='link-facility']").click();
- // cy.wait("@addFacility")
- // // .its("response.statusCode")
- // // .should("eq", 201)
- // .get("span")
- // .contains("Facility - User Already has permission to this facility");
+ // cy.get("li[role='option']").should("not.exist");
// });
afterEach(() => {
diff --git a/src/Components/Common/FacilitySelect.tsx b/src/Components/Common/FacilitySelect.tsx
index 400ce115bc5..19494081aa3 100644
--- a/src/Components/Common/FacilitySelect.tsx
+++ b/src/Components/Common/FacilitySelect.tsx
@@ -6,6 +6,7 @@ import { FacilityModel } from "../Facility/models";
interface FacilitySelectProps {
name: string;
+ exclude_user: string;
errors?: string | undefined;
className?: string;
searchAll?: boolean;
@@ -22,6 +23,7 @@ interface FacilitySelectProps {
export const FacilitySelect = (props: FacilitySelectProps) => {
const {
name,
+ exclude_user,
multiple,
selected,
setSelected,
@@ -45,6 +47,7 @@ export const FacilitySelect = (props: FacilitySelectProps) => {
search_text: text,
all: searchAll,
facility_type: facilityType,
+ exclude_user: exclude_user,
district,
};
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx
index 3b55c40fc70..1771a6ca22f 100644
--- a/src/Components/Users/ManageUsers.tsx
+++ b/src/Components/Users/ManageUsers.tsx
@@ -706,6 +706,7 @@ function UserFacilities(props: { user: any }) {
Date: Fri, 3 Nov 2023 17:50:46 +0530
Subject: [PATCH 09/11] Refactor middleware hostname in Feed component (#6538)
---
src/Common/hooks/useMSEplayer.ts | 2 ++
.../Assets/AssetType/ONVIFCamera.tsx | 12 +++++----
.../Facility/Consultations/Feed.tsx | 27 ++++++++++++++-----
3 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/src/Common/hooks/useMSEplayer.ts b/src/Common/hooks/useMSEplayer.ts
index fcbf216ed6a..4d1bb36b9ac 100644
--- a/src/Common/hooks/useMSEplayer.ts
+++ b/src/Common/hooks/useMSEplayer.ts
@@ -20,6 +20,8 @@ interface UseMSEMediaPlayerOption {
export interface ICameraAssetState {
id: string;
accessKey: string;
+ middleware_address: string;
+ location_middleware: string;
}
export enum StreamStatus {
diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx
index 4a3e475419e..44d4d372d73 100644
--- a/src/Components/Assets/AssetType/ONVIFCamera.tsx
+++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx
@@ -53,6 +53,11 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => {
}
}, [facility, facilityId]);
+ const fallbackMiddleware =
+ asset?.location_object?.middleware_address || facilityMiddlewareHostname;
+
+ const currentMiddleware = middlewareHostname || fallbackMiddleware;
+
useEffect(() => {
if (asset) {
setAssetType(asset?.asset_class);
@@ -105,7 +110,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => {
try {
setLoadingAddPreset(true);
const presetData = await axios.get(
- `https://${facilityMiddlewareHostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}`
+ `https://${currentMiddleware}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}`
);
const { res } = await request(routes.createAssetBed, {
@@ -136,9 +141,6 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => {
};
if (isLoading || loading || !facility) return ;
- const fallbackMiddleware =
- asset?.location_object?.middleware_address || facilityMiddlewareHostname;
-
return (
{["DistrictAdmin", "StateAdmin"].includes(authUser.user_type) && (
@@ -223,7 +225,7 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => {
addPreset={addPreset}
isLoading={loadingAddPreset}
refreshPresetsHash={refreshPresetsHash}
- facilityMiddlewareHostname={facilityMiddlewareHostname}
+ facilityMiddlewareHostname={currentMiddleware}
/>
) : null}
diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx
index 31691c736f4..3448d85a6a2 100644
--- a/src/Components/Facility/Consultations/Feed.tsx
+++ b/src/Components/Facility/Consultations/Feed.tsx
@@ -48,8 +48,11 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
const [cameraAsset, setCameraAsset] = useState({
id: "",
accessKey: "",
+ middleware_address: "",
+ location_middleware: "",
});
- const [cameraMiddlewareHostname, setCameraMiddlewareHostname] = useState("");
+ const [facilityMiddlewareHostname, setFacilityMiddlewareHostname] =
+ useState("");
const [cameraConfig, setCameraConfig] = useState({});
const [isLoading, setIsLoading] = useState(true);
const [bedPresets, setBedPresets] = useState([]);
@@ -66,13 +69,19 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
const res = await dispatch(getPermittedFacility(facilityId));
if (res.status === 200 && res.data) {
- setCameraMiddlewareHostname(res.data.middleware_address);
+ setFacilityMiddlewareHostname(res.data.middleware_address);
}
};
if (facilityId) fetchFacility();
}, [dispatch, facilityId]);
+ const fallbackMiddleware =
+ cameraAsset.location_middleware || facilityMiddlewareHostname;
+
+ const currentMiddleware =
+ cameraAsset.middleware_address || fallbackMiddleware;
+
useEffect(() => {
if (cameraState) {
setCameraState({
@@ -130,6 +139,12 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
setCameraAsset({
id: bedAssets.data.results[0].asset_object.id,
accessKey: config[2] || "",
+ middleware_address:
+ bedAssets.data.results[0].asset_object?.meta
+ ?.middleware_hostname,
+ location_middleware:
+ bedAssets.data.results[0].asset_object.location_object
+ ?.middleware_address,
});
setCameraConfig(bedAssets.data.results[0].meta);
setCameraState({
@@ -170,8 +185,8 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
);
const url = !isIOS
- ? `wss://${cameraMiddlewareHostname}/stream/${cameraAsset?.accessKey}/channel/0/mse?uuid=${cameraAsset?.accessKey}&channel=0`
- : `https://${cameraMiddlewareHostname}/stream/${cameraAsset?.accessKey}/channel/0/hls/live/index.m3u8?uuid=${cameraAsset?.accessKey}&channel=0`;
+ ? `wss://${currentMiddleware}/stream/${cameraAsset?.accessKey}/channel/0/mse?uuid=${cameraAsset?.accessKey}&channel=0`
+ : `https://${currentMiddleware}/stream/${cameraAsset?.accessKey}/channel/0/hls/live/index.m3u8?uuid=${cameraAsset?.accessKey}&channel=0`;
const {
startStream,
@@ -182,7 +197,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
: // eslint-disable-next-line react-hooks/rules-of-hooks
useMSEMediaPlayer({
config: {
- middlewareHostname: cameraMiddlewareHostname,
+ middlewareHostname: currentMiddleware,
...cameraAsset,
},
url,
@@ -229,7 +244,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
});
getBedPresets(cameraAsset);
}
- }, [cameraAsset, cameraMiddlewareHostname]);
+ }, [cameraAsset, currentMiddleware]);
useEffect(() => {
let tId: any;
From ac405ce4e9c51293d3d0cfb1fc4a6d8322417769 Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 3 Nov 2023 20:33:34 +0530
Subject: [PATCH 10/11] Show camera feed button only for specific roles
(#6540)
---
.../Facility/ConsultationDetails/index.tsx | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx
index 202c9200b0a..6fda874e3b3 100644
--- a/src/Components/Facility/ConsultationDetails/index.tsx
+++ b/src/Components/Facility/ConsultationDetails/index.tsx
@@ -335,14 +335,17 @@ export const ConsultationDetails = (props: any) => {
>
Doctor Connect
- {patientData.last_consultation?.id && (
-
- Camera Feed
-
- )}
+ {patientData.last_consultation?.id &&
+ ["DistrictAdmin", "StateAdmin", "Doctor"].includes(
+ authUser.user_type
+ ) && (
+
+ Camera Feed
+
+ )}
>
)}
Date: Fri, 3 Nov 2023 21:43:50 +0530
Subject: [PATCH 11/11] add auto deployment for staging gcp deployment (#6519)
---
.github/workflows/deploy.yaml | 42 +++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 06b1cf8eff0..e5b3c768c49 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -180,6 +180,48 @@ jobs:
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
+ deploy-staging-gcp:
+ needs: build-production
+ name: Deploy to staging GCP cluster
+ runs-on: ubuntu-latest
+ environment:
+ name: Staging-GCP
+ url: https://care-staging.ohc.network/
+ steps:
+ - name: Checkout Kube Config
+ uses: actions/checkout@v3
+ with:
+ repository: coronasafe/care-staging-gcp
+ token: ${{ secrets.GIT_ACCESS_TOKEN }}
+ path: kube
+ ref: main
+
+ # Setup gcloud CLI
+ - uses: google-github-actions/setup-gcloud@94337306dda8180d967a56932ceb4ddcf01edae7
+ with:
+ service_account_key: ${{ secrets.GKE_SA_KEY }}
+ project_id: ${{ secrets.GKE_PROJECT }}
+
+ # Get the GKE credentials so we can deploy to the cluster
+ - uses: google-github-actions/get-gke-credentials@fb08709ba27618c31c09e014e1d8364b02e5042e
+ with:
+ cluster_name: ${{ secrets.GKE_CLUSTER }}
+ location: ${{ secrets.GKE_ZONE }}
+ credentials: ${{ secrets.GKE_SA_KEY }}
+
+ - name: install kubectl
+ uses: azure/setup-kubectl@v3.0
+ with:
+ version: "v1.23.6"
+ id: install
+
+ - name: Deploy Care Fe Production
+ run: |
+ mkdir -p $HOME/.kube/
+ cd kube/deployments/
+ sed -i -e "s/_BUILD_NUMBER_/${GITHUB_RUN_NUMBER}/g" care-fe.yaml
+ kubectl apply -f care-fe.yaml
+
deploy-production-manipur:
needs: build-production
name: Deploy to GKE Manipur