diff --git a/cypress/e2e/users_spec/UsersCreation.cy.ts b/cypress/e2e/users_spec/UsersCreation.cy.ts
index 947d5b6ce87..d93707617ff 100644
--- a/cypress/e2e/users_spec/UsersCreation.cy.ts
+++ b/cypress/e2e/users_spec/UsersCreation.cy.ts
@@ -43,7 +43,7 @@ describe("User Creation", () => {
"Please enter valid phone number",
"Please enter the username",
"Please enter date in DD/MM/YYYY format",
- "Please enter the password",
+ "Password is required",
"Confirm password is required",
"First Name is required",
"Last Name is required",
@@ -164,13 +164,10 @@ describe("User Creation", () => {
cy.verifyNotification("User added successfully");
userPage.typeInSearchInput(username);
userPage.checkUsernameText(username);
- cy.verifyContentPresence("#name", [newUserFirstName]);
+ cy.verifyContentPresence(`#name-${username}`, [newUserFirstName]);
cy.verifyContentPresence("#role", [role]);
cy.verifyContentPresence("#district", [district]);
- cy.verifyContentPresence("#home_facility", [homeFacility]);
- cy.verifyContentPresence("#qualification", [qualification]);
- cy.verifyContentPresence("#doctor-experience", [experience]);
- cy.verifyContentPresence("#medical-council-registration", [regNo]);
+ cy.verifyContentPresence("#home-facility", [homeFacility]);
});
it("create new user form throwing mandatory field error", () => {
diff --git a/cypress/e2e/users_spec/UsersHomepage.cy.ts b/cypress/e2e/users_spec/UsersHomepage.cy.ts
index 8d86482645b..acc1639e003 100644
--- a/cypress/e2e/users_spec/UsersHomepage.cy.ts
+++ b/cypress/e2e/users_spec/UsersHomepage.cy.ts
@@ -89,6 +89,18 @@ describe("User Homepage", () => {
pageNavigation.verifyCurrentPageNumber(1);
});
+ it("Switch to list view, search by username and verify the results", () => {
+ userPage.switchToListView();
+ userPage.verifyListView();
+ userPage.checkSearchInputVisibility();
+ userPage.typeInSearchInput(doctorUserName);
+ userPage.checkUrlForUsername(doctorUserName);
+ userPage.checkUsernameText(doctorUserName);
+ userPage.checkUsernameBadgeVisibility(true);
+ userPage.clearSearchInput();
+ userPage.verifyListView();
+ });
+
afterEach(() => {
cy.saveLocalStorage();
});
diff --git a/cypress/e2e/users_spec/UsersManage.cy.ts b/cypress/e2e/users_spec/UsersManage.cy.ts
index 41557e87756..120eb551f57 100644
--- a/cypress/e2e/users_spec/UsersManage.cy.ts
+++ b/cypress/e2e/users_spec/UsersManage.cy.ts
@@ -1,3 +1,4 @@
+import * as dayjs from "dayjs";
import FacilityHome from "pageobject/Facility/FacilityHome";
import { advanceFilters } from "pageobject/utils/advanceFilterHelpers";
@@ -17,11 +18,14 @@ describe("Manage User", () => {
const firstNameUserSkill = "Dev";
const lastNameUserSkill = "Doctor";
const usernameforworkinghour = "devdistrictadmin";
+ const nurseUsername = "dummynurse1";
+ const doctorUsername = "devdoctor";
+ const doctorToDelete = "dummydoctor12";
const usernamerealname = "Dummy Doctor";
const facilitytolinkusername = "Dummy Shifting Center";
const facilitytolinkskill = "Dummy Facility 40";
const workinghour = "23";
- const linkedskill = "General Medicine";
+ const linkedskill = "Immunologist";
before(() => {
loginPage.loginByRole("districtAdmin");
@@ -34,24 +38,221 @@ describe("Manage User", () => {
cy.awaitUrl("/users");
});
+ // To Do: Add avatar upload
+ /* it("District Admin can change their own avatar", () => {
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.verifyChangeAvatarButtonVisible();
+ manageUserPage.clickChangeAvatarButton();
+ }); */
+
+ it("edit a nurse user's basic information and verify its reflection", () => {
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickBasicInfoViewButton();
+ manageUserPage.clickBasicInfoEditButton();
+ manageUserPage.clearUserBasicInfo();
+ manageUserPage.clickSubmit();
+ manageUserPage.verifyErrorText("First Name is required");
+ manageUserPage.verifyErrorText("Last Name is required");
+ manageUserPage.editUserBasicInfo("Devo", "Districto", "11081999", "Female");
+ manageUserPage.clickSubmit();
+ manageUserPage.clickBasicInfoViewButton();
+ manageUserPage.verifyEditUserDetails(
+ "Devo",
+ "Districto",
+ "8/11/1999",
+ "Female",
+ );
+ });
+
+ it("edit a nurse user's contact information and verify its reflection", () => {
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickContactInfoViewButton();
+ manageUserPage.clickContactInfoEditButton();
+ manageUserPage.clearUserContactInfo();
+ manageUserPage.clickSubmit();
+ manageUserPage.verifyErrorText("Please enter a valid email address");
+ manageUserPage.verifyErrorText("Please enter valid phone number");
+ manageUserPage.editUserContactInfo("dev@gmail.com", "6234343435");
+ manageUserPage.clickSubmit();
+ manageUserPage.clickContactInfoViewButton();
+ manageUserPage.verifyEditUserContactInfo("dev@gmail.com", "6234343435");
+ });
+
+ it("edit a nurse user's professional information and verify its reflection", () => {
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickProfessionalInfoViewButton();
+ // Should have qualification field
+ // Should not have years of experience and medical council registration fields
+ manageUserPage.verifyQualificationExist();
+ manageUserPage.verifyYoeAndCouncilRegistrationDoesntExist();
+ manageUserPage.clickProfessionalInfoEditButton();
+ manageUserPage.clearDoctorOrNurseProfessionalInfo(false);
+ manageUserPage.clickSubmit();
+ manageUserPage.verifyErrorText("Qualification is required");
+ manageUserPage.editUserProfessionalInfo("Msc");
+ manageUserPage.clickSubmit();
+ manageUserPage.clickProfessionalInfoViewButton();
+ manageUserPage.verifyEditUserProfessionalInfo("Msc");
+ });
+
+ it("edit a doctor user's professional information and verify its reflection", () => {
+ // Should have qualification, years of experience and medical council registration
+ userPage.typeInSearchInput(usernameToLinkFacilitydoc1);
+ userPage.checkUsernameText(usernameToLinkFacilitydoc1);
+ manageUserPage.clickMoreDetailsButton(usernameToLinkFacilitydoc1);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickProfessionalInfoViewButton();
+ manageUserPage.verifyQualificationExist();
+ manageUserPage.verifyYoeAndCouncilRegistrationExist();
+ manageUserPage.clickProfessionalInfoEditButton();
+ manageUserPage.clearDoctorOrNurseProfessionalInfo(true);
+ manageUserPage.clickSubmit();
+ manageUserPage.verifyErrorText("Qualification is required");
+ manageUserPage.verifyErrorText("Years of experience is required");
+ manageUserPage.verifyErrorText("Medical Council Registration is required");
+ manageUserPage.editUserProfessionalInfo("Msc", "120", "1234567890");
+ manageUserPage.clickSubmit();
+ manageUserPage.verifyErrorText(
+ "Please enter a valid number between 0 and 100.",
+ );
+ manageUserPage.clearDoctorOrNurseProfessionalInfo(true);
+ manageUserPage.editUserProfessionalInfo("Msc", "10", "1234567890");
+ manageUserPage.clickSubmit();
+ manageUserPage.clickProfessionalInfoViewButton();
+ const experienceCommencedOn = dayjs().subtract(10, "year");
+ const formattedDate = dayjs(experienceCommencedOn).format("YYYY-MM-DD");
+ manageUserPage.verifyEditUserProfessionalInfo(
+ "Msc",
+ formattedDate,
+ "1234567890",
+ );
+ });
+
+ it("Nurse user doesn't have edit options or password change option (for other users)", () => {
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsNurse();
+ loginPage.ensureLoggedIn();
+ cy.visit("/users");
+ userPage.typeInSearchInput(doctorUsername);
+ userPage.checkUsernameText(doctorUsername);
+ manageUserPage.clickMoreDetailsButton(doctorUsername);
+ manageUserPage.verifyMoreDetailsPage(false);
+ manageUserPage.verifyUsername(doctorUsername);
+ manageUserPage.verifyBasicInfoEditButtonNotExist();
+ manageUserPage.verifyContactInfoEditButtonNotExist();
+ manageUserPage.verifyProfessionalInfoEditButtonNotExist();
+ manageUserPage.verifyPasswordEditButtonNotExist();
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsDistrictAdmin();
+ loginPage.ensureLoggedIn();
+ });
+
+ it("Nurse user doesn't have delete option for other users", () => {
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsNurse();
+ loginPage.ensureLoggedIn();
+ cy.visit("/users");
+ userPage.typeInSearchInput(doctorUsername);
+ userPage.checkUsernameText(doctorUsername);
+ manageUserPage.clickMoreDetailsButton(doctorUsername);
+ manageUserPage.verifyMoreDetailsPage(false);
+ manageUserPage.verifyDeleteButtonNotExist();
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsDistrictAdmin();
+ loginPage.ensureLoggedIn();
+ });
+
+ it("Nurse user can change their own password", () => {
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsNurse();
+ loginPage.ensureLoggedIn();
+ cy.visit("/users");
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickPasswordEditButton();
+ manageUserPage.changePassword("Coronasafe@123", "Coronasafe@1233");
+ manageUserPage.clickSubmit();
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsNurse("Coronasafe@1233");
+ loginPage.ensureLoggedIn();
+ cy.visit("/users");
+ userPage.typeInSearchInput(nurseUsername);
+ userPage.checkUsernameText(nurseUsername);
+ manageUserPage.clickMoreDetailsButton(nurseUsername);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickPasswordEditButton();
+ manageUserPage.changePassword("Coronasafe@1233", "Coronasafe@123");
+ manageUserPage.clickSubmit();
+ loginPage.ensureLoggedIn();
+ loginPage.clickSignOutBtn();
+ loginPage.loginManuallyAsDistrictAdmin();
+ loginPage.ensureLoggedIn();
+ });
+
+ it("District Admin can delete a user", () => {
+ userPage.typeInSearchInput(doctorToDelete);
+ userPage.checkUsernameText(doctorToDelete);
+ manageUserPage.clickMoreDetailsButton(doctorToDelete);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.verifyDeleteButtonVisible();
+ manageUserPage.clickDeleteButton();
+ manageUserPage.clickSubmit();
+ cy.verifyNotification("User Deleted Successfully");
+ cy.closeNotification();
+ userPage.typeInSearchInput(doctorToDelete);
+ userPage.checkUsernameTextDoesNotExist(doctorToDelete);
+ });
+
it("linking skills for users and verify its reflection in profile", () => {
// select the district user and select one skill link and verify its profile reflection
userPage.typeInSearchInput(usernameforworkinghour);
userPage.checkUsernameText(usernameforworkinghour);
- manageUserPage.clicklinkedskillbutton();
+ manageUserPage.clickMoreDetailsButton(usernameforworkinghour);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedSkillTab();
+ cy.wait(500);
+ manageUserPage.verifyLinkedSkillsTabPage();
manageUserPage.selectSkillFromDropdown(linkedskill);
manageUserPage.clickAddSkillButton();
- manageUserPage.clickCloseSlideOver();
- cy.wait(5000);
- manageUserPage.clicklinkedskillbutton();
+ cy.wait(500);
manageUserPage.assertSkillInAddedUserSkills(linkedskill);
- manageUserPage.clickCloseSlideOver();
- cy.wait(5000);
+ cy.wait(500);
manageUserPage.navigateToProfile();
cy.verifyContentPresence("#username-profile-details", [
usernameforworkinghour,
]);
manageUserPage.assertSkillInAlreadyLinkedSkills(linkedskill);
+ // unlink the skill
+ manageUserPage.navigateToManageUser();
+ userPage.typeInSearchInput(usernameforworkinghour);
+ userPage.checkUsernameText(usernameforworkinghour);
+ manageUserPage.clickMoreDetailsButton(usernameforworkinghour);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedSkillTab();
+ manageUserPage.assertSkillInAddedUserSkills(linkedskill);
+ manageUserPage.clickUnlinkSkill();
+ manageUserPage.verifyUnlinkSkillModal();
+ manageUserPage.clickConfirmUnlinkSkill();
});
it("linking skills for a doctor users and verify its reflection in doctor connect", () => {
@@ -62,13 +263,15 @@ describe("Manage User", () => {
userPage.selectHomeFacility(facilitytolinkskill);
advanceFilters.applySelectedFilter();
userPage.checkUsernameText(usernameToLinkSkill);
- manageUserPage.clicklinkedskillbutton();
+ manageUserPage.clickMoreDetailsButton(usernameToLinkSkill);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedSkillTab();
+ manageUserPage.verifyLinkedSkillsTabPage();
manageUserPage.selectSkillFromDropdown(linkedskill);
manageUserPage.clickAddSkillButton();
cy.verifyNotification("Skill added successfully");
cy.closeNotification();
manageUserPage.assertSkillInAddedUserSkills(linkedskill);
- manageUserPage.clickCloseSlideOver();
// verifying the doctor connect
facilityHome.navigateToFacilityHomepage();
facilityHome.typeFacilitySearch(facilitytolinkskill);
@@ -79,18 +282,28 @@ describe("Manage User", () => {
});
it("add working hour for a user and verify its reflection in card and user profile", () => {
- // verify mandatory field error and select working hour for a user
+ // verify qualification and yoe and council registration fields are not present
+ // verify field error and add working hour
userPage.typeInSearchInput(usernameforworkinghour);
userPage.checkUsernameText(usernameforworkinghour);
- manageUserPage.clicksetaveragehourbutton();
- manageUserPage.clearweeklyhourfield();
+ manageUserPage.clickMoreDetailsButton(usernameforworkinghour);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.verifyProfileTabPage();
+ manageUserPage.clickProfessionalInfoViewButton();
+ manageUserPage.verifyQualificationDoesntExist();
+ manageUserPage.verifyYoeAndCouncilRegistrationDoesntExist();
+ manageUserPage.clickProfessionalInfoEditButton();
+ manageUserPage.clearProfessionalInfo();
+ manageUserPage.typeInWeeklyWorkingHours("200");
manageUserPage.clickSubmit();
- manageUserPage.verifyErrorText("Value should be between 0 and 168");
- // verify the data is reflected in user card and profile page
+ manageUserPage.verifyErrorText(
+ "Average weekly working hours must be a number between 0 and 168",
+ );
+ manageUserPage.clearProfessionalInfo();
manageUserPage.typeInWeeklyWorkingHours(workinghour);
manageUserPage.clickSubmit();
+ // verify the data is reflected in the page
manageUserPage.verifyWorkingHours(workinghour);
- manageUserPage.navigateToProfile();
manageUserPage.verifyProfileWorkingHours(workinghour);
});
@@ -98,42 +311,51 @@ describe("Manage User", () => {
// verify the user doesn't have any home facility
userPage.typeInSearchInput(usernameToLinkFacilitydoc1);
userPage.checkUsernameText(usernameToLinkFacilitydoc1);
- manageUserPage.assertHomeFacility("No Home Facility");
+ manageUserPage.assertHomeFacility("No home facility");
+ manageUserPage.clickMoreDetailsButton(usernameToLinkFacilitydoc1);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedFacilitiesTab();
+ manageUserPage.verifyLinkedFacilitiesTabPage();
// Link a new facility and ensure it is under linked facility - doctor username (1)
- manageUserPage.clickFacilitiesTab();
manageUserPage.selectFacilityFromDropdown(facilitytolinkusername);
manageUserPage.clickLinkFacility();
manageUserPage.assertLinkedFacility(facilitytolinkusername);
// Verify in the already linked facility are not present in droplist
manageUserPage.assertFacilityNotInDropdown(facilitytolinkusername);
- manageUserPage.clickCloseSlideOver();
+ // Go back to manage user page
+ manageUserPage.navigateToManageUser();
// Link a new facility and ensure it is under home facility - doctor username (2)
- userPage.clearSearchInput();
userPage.typeInSearchInput(usernameToLinkFacilitydoc2);
userPage.checkUsernameText(usernameToLinkFacilitydoc2);
- manageUserPage.clickFacilitiesTab();
+ manageUserPage.clickMoreDetailsButton(usernameToLinkFacilitydoc2);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedFacilitiesTab();
+ manageUserPage.verifyLinkedFacilitiesTabPage();
manageUserPage.selectFacilityFromDropdown(facilitytolinkusername);
manageUserPage.clickLinkFacility();
- manageUserPage.clickHomeFacilityIcon();
+ manageUserPage.clickLinkedFacilitySettings();
+ manageUserPage.clickSetHomeFacility();
manageUserPage.assertnotLinkedFacility(facilitytolinkusername);
manageUserPage.assertHomeFacilitylink(facilitytolinkusername);
- manageUserPage.clickCloseSlideOver();
// verify the home facility doctor id have reflection in user card
- userPage.clearSearchInput();
+ manageUserPage.navigateToManageUser();
userPage.typeInSearchInput(usernameToLinkFacilitydoc2);
userPage.checkUsernameText(usernameToLinkFacilitydoc2);
manageUserPage.assertHomeFacility(facilitytolinkusername);
// Link a new facility and unlink the facility from the doctor username (3)
- userPage.clearSearchInput();
+ manageUserPage.navigateToManageUser();
userPage.typeInSearchInput(usernameToLinkFacilitydoc3);
userPage.checkUsernameText(usernameToLinkFacilitydoc3);
- manageUserPage.clickFacilitiesTab();
+ manageUserPage.clickMoreDetailsButton(usernameToLinkFacilitydoc3);
+ manageUserPage.verifyMoreDetailsPage();
+ manageUserPage.clickLinkedFacilitiesTab();
+ manageUserPage.verifyLinkedFacilitiesTabPage();
manageUserPage.selectFacilityFromDropdown(facilitytolinkusername);
manageUserPage.clickLinkFacility();
+ manageUserPage.clickLinkedFacilitySettings();
manageUserPage.clickUnlinkFacilityButton();
manageUserPage.clickSubmit();
manageUserPage.linkedfacilitylistnotvisible();
- manageUserPage.clickCloseSlideOver();
// Go to particular facility doctor connect and all user-id are reflected based on there access
// Path will be facility page to patient page then doctor connect button
facilityHome.navigateToFacilityHomepage();
diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts
index 481c6ec3045..64e9cd5f25e 100644
--- a/cypress/pageobject/Login/LoginPage.ts
+++ b/cypress/pageobject/Login/LoginPage.ts
@@ -19,9 +19,11 @@ class LoginPage {
cy.clickSubmitButton("Login");
}
- loginManuallyAsNurse(): void {
+ loginManuallyAsNurse(password?: string): void {
cy.get("input[id='username']").click().type("dummynurse1");
- cy.get("input[id='password']").click().type("Coronasafe@123");
+ cy.get("input[id='password']")
+ .click()
+ .type(password || "Coronasafe@123");
cy.clickSubmitButton("Login");
}
diff --git a/cypress/pageobject/Users/ManageUserPage.ts b/cypress/pageobject/Users/ManageUserPage.ts
index 01871acd97d..f5bbce7e95b 100644
--- a/cypress/pageobject/Users/ManageUserPage.ts
+++ b/cypress/pageobject/Users/ManageUserPage.ts
@@ -1,10 +1,6 @@
export class ManageUserPage {
assertHomeFacility(expectedText: string) {
- cy.get("#home_facility").should("contain.text", expectedText);
- }
-
- clickFacilitiesTab() {
- cy.get("#facilities").click();
+ cy.get("#home-facility").should("contain.text", expectedText);
}
selectFacilityFromDropdown(facilityName: string) {
@@ -15,10 +11,6 @@ export class ManageUserPage {
cy.typeAndSelectOption("input[name='skill']", skill);
}
- clickLinkFacility() {
- cy.get("#link-facility").click();
- }
-
assertLinkedFacility(facilityName: string) {
cy.get("#linked-facility-list").should("contain.text", facilityName);
}
@@ -40,36 +32,155 @@ export class ManageUserPage {
cy.get("[role='option']").should("not.exist");
}
- clickCloseSlideOver() {
- cy.get("#close-slide-over").click({ force: true });
+ clickLinkedFacilitySettings() {
+ cy.get("#linked-facility-settings").click();
}
- clickHomeFacilityIcon() {
- cy.get("#home-facility-icon").click();
+ clickSetHomeFacility() {
+ cy.get("#set-home-facility").click();
}
clickUnlinkFacilityButton() {
- cy.get("#unlink-facility-button").click();
+ cy.get("#unlink-facility").click();
+ }
+
+ clickConfirmUnlinkSkill() {
+ cy.get("button[name='confirm-unlink-skill']").click();
+ }
+
+ clickLinkFacility() {
+ cy.get("#link-facility").click();
}
clickSubmit() {
cy.get("#submit").click();
}
- clicksetaveragehourbutton() {
- cy.get("#avg-workinghour").click();
+ verifyErrorText(expectedError: string) {
+ cy.get(".error-text").first().scrollIntoView();
+ cy.get(".error-text")
+ .should("be.visible")
+ .then(($elements) => {
+ const errorTextArray = Array.from($elements).map(
+ (el) => el.textContent,
+ );
+ expect(errorTextArray).to.include(expectedError);
+ });
}
- clearweeklyhourfield() {
- cy.get("#weekly_working_hours").click().clear();
+ clearUserBasicInfo() {
+ cy.get("input[name='first_name']").click().clear();
+ cy.get("input[name='last_name']").click().clear();
}
- verifyErrorText(expectedError: string) {
- cy.get(".error-text").should("contain", expectedError).and("be.visible");
+ editUserBasicInfo(
+ fName: string,
+ lName: string,
+ dateOfBirth: string,
+ gender: string,
+ ) {
+ cy.get("input[name='first_name']").click().type(fName);
+ cy.get("input[name='last_name']").click().type(lName);
+ cy.clickAndTypeDate("#date_of_birth", dateOfBirth);
+ cy.get("#gender").click();
+ cy.get("[role='option']").contains(gender).click();
+ }
+
+ verifyEditUserDetails(
+ fName: string,
+ lName: string,
+ dateOfBirth: string,
+ gender: string,
+ ) {
+ cy.get("#view-first_name").should("contain.text", fName);
+ cy.get("#view-last_name").should("contain.text", lName);
+ cy.get("#view-date_of_birth").should("contain.text", dateOfBirth);
+ cy.get("#view-gender").should("contain.text", gender);
+ }
+
+ clearUserContactInfo() {
+ cy.get("input[name='email']").click().clear();
+ cy.get("input[name='phone_number']").click().clear();
+ cy.get("input[name='phone_number_is_whatsapp']").should("be.checked");
+ }
+
+ editUserContactInfo(email: string, phoneNumber: string) {
+ cy.get("input[name='email']").click().type(email);
+ cy.get("input[name='phone_number']").click().type(phoneNumber);
+ cy.get("input[name='phone_number_is_whatsapp']").should("be.checked");
+ }
+
+ verifyEditUserContactInfo(email: string, phoneNumber: string) {
+ cy.get("#view-email").should("contain.text", email);
+ cy.get("#view-phone_number").should("contain.text", phoneNumber);
+ cy.get("#view-whatsapp_number").should("contain.text", phoneNumber);
+ }
+
+ clearDoctorOrNurseProfessionalInfo(yoeAndCouncilRegistration: boolean) {
+ cy.get("input[name='qualification']").click().clear();
+ if (yoeAndCouncilRegistration) {
+ cy.get("input[name='doctor_experience_commenced_on']").click().clear();
+ cy.get("input[name='doctor_medical_council_registration']")
+ .click()
+ .clear();
+ }
+ }
+
+ clearProfessionalInfo() {
+ cy.get("input[name='weekly_working_hours']").click().clear();
+ cy.get("input[name='video_connect_link']").click().clear();
+ }
+
+ editUserProfessionalInfo(
+ qualification: string,
+ yearsOfExperience?: string,
+ medicalCouncilRegistration?: string,
+ ) {
+ cy.get("input[name='qualification']").click().type(qualification);
+ if (yearsOfExperience) {
+ cy.get("input[name='doctor_experience_commenced_on']")
+ .click()
+ .type(yearsOfExperience);
+ }
+ if (medicalCouncilRegistration) {
+ cy.get("input[name='doctor_medical_council_registration']")
+ .click()
+ .type(medicalCouncilRegistration);
+ }
+ }
+
+ verifyEditUserProfessionalInfo(
+ qualification: string,
+ yearsOfExperience?: string,
+ medicalCouncilRegistration?: string,
+ ) {
+ cy.get("#view-qualification").should("contain.text", qualification);
+ if (yearsOfExperience) {
+ cy.get("#view-years_of_experience").should(
+ "contain.text",
+ yearsOfExperience,
+ );
+ }
+ if (medicalCouncilRegistration) {
+ cy.get("#view-doctor_medical_council_registration").should(
+ "contain.text",
+ medicalCouncilRegistration,
+ );
+ }
+ }
+
+ verifyPasswordEditButtonNotExist() {
+ cy.get("#change-edit-password-button").should("not.exist");
+ }
+
+ changePassword(oldPassword: string, newPassword: string) {
+ cy.get("input[name='old_password']").click().type(oldPassword);
+ cy.get("input[name='new_password_1']").click().type(newPassword);
+ cy.get("input[name='new_password_2']").click().type(newPassword);
}
typeInWeeklyWorkingHours(hours: string) {
- cy.get("#weekly_working_hours").click().type(hours);
+ cy.get("input[name='weekly_working_hours']").click().type(hours);
}
navigateToProfile() {
@@ -80,25 +191,174 @@ export class ManageUserPage {
}
verifyWorkingHours(expectedHours: string) {
- cy.get("#working-hours").should("contain", `${expectedHours} hours`);
+ cy.get("input[name='weekly_working_hours']").should("be.visible");
+ cy.get("input[name='weekly_working_hours']").should(
+ "have.value",
+ expectedHours,
+ );
}
verifyProfileWorkingHours(expectedHours: string) {
- cy.get("#averageworkinghour-profile-details").should(
- "contain",
+ cy.get("#view-average_weekly_working_hours").should(
+ "contain.text",
expectedHours,
);
}
+ navigateToManageUser() {
+ cy.visit("/users");
+ }
+
clickFacilityPatients() {
cy.get("#facility-patients").should("be.visible");
cy.get("#facility-patients").click();
}
- clicklinkedskillbutton() {
+ clickLinkedSkillTab() {
cy.get("#skills").click();
}
+ clickLinkedFacilitiesTab() {
+ cy.get("#facilities").click();
+ }
+
+ clickMoreDetailsButton(username: string) {
+ cy.intercept("GET", "**/api/v1/users/**").as("getUserDetails");
+ cy.get(`#more-details-${username}`).click();
+ cy.wait("@getUserDetails");
+ }
+
+ verifyMoreDetailsPage(hasPermissions = true) {
+ cy.get("#username").should("be.visible");
+ cy.get("#role").should("be.visible");
+ cy.get("#usermanagement_tab_nav").should("be.visible");
+ cy.get("#profile").should("be.visible");
+ if (hasPermissions) {
+ cy.get("#facilities").should("be.visible");
+ cy.get("#skills").should("be.visible");
+ }
+ cy.get("#view-username").scrollIntoView();
+ cy.get("#view-username").should("be.visible");
+ }
+
+ verifyChangeAvatarButtonVisible() {
+ cy.get("#change-avatar").should("be.visible");
+ }
+
+ clickChangeAvatarButton() {
+ cy.get("#change-avatar").click();
+ }
+
+ clickBasicInfoViewButton() {
+ cy.get("#basic-info-view-button").scrollIntoView();
+ cy.get("#basic-info-view-button").should("be.visible");
+ cy.get("#basic-info-view-button").click();
+ }
+
+ clickBasicInfoEditButton() {
+ cy.get("#basic-info-edit-button").scrollIntoView();
+ cy.get("#basic-info-edit-button").should("be.visible");
+ cy.get("#basic-info-edit-button").click();
+ }
+
+ clickContactInfoViewButton() {
+ cy.get("#contact-info-view-button").scrollIntoView();
+ cy.get("#contact-info-view-button").should("be.visible");
+ cy.get("#contact-info-view-button").click();
+ }
+
+ clickContactInfoEditButton() {
+ cy.get("#contact-info-edit-button").scrollIntoView();
+ cy.get("#contact-info-edit-button").should("be.visible");
+ cy.get("#contact-info-edit-button").click();
+ }
+
+ clickProfessionalInfoViewButton() {
+ cy.get("#professional-info-view-button").scrollIntoView();
+ cy.get("#professional-info-view-button").should("be.visible");
+ cy.get("#professional-info-view-button").click();
+ }
+
+ clickProfessionalInfoEditButton() {
+ cy.get("#professional-info-edit-button").scrollIntoView();
+ cy.get("#professional-info-edit-button").should("be.visible");
+ cy.get("#professional-info-edit-button").click();
+ }
+
+ clickPasswordEditButton() {
+ cy.get("#change-edit-password-button").scrollIntoView();
+ cy.get("#change-edit-password-button").should("be.visible");
+ cy.get("#change-edit-password-button").click();
+ }
+
+ verifyQualificationDoesntExist() {
+ cy.get("input[name='qualification']").should("not.exist");
+ }
+
+ verifyQualificationExist() {
+ cy.get("#view-qualification").should("be.visible");
+ }
+
+ verifyYoeAndCouncilRegistrationDoesntExist() {
+ cy.get("#view-years_of_experience").should("not.exist");
+ cy.get("#view-doctor_medical_council_registration").should("not.exist");
+ }
+
+ verifyYoeAndCouncilRegistrationExist() {
+ cy.get("#view-years_of_experience").should("be.visible");
+ cy.get("#view-doctor_medical_council_registration").should("be.visible");
+ }
+
+ verifyUsername(username: string) {
+ cy.get("#view-username").should("contain", username);
+ }
+
+ verifyBasicInfoEditButtonNotExist() {
+ cy.get("#basic-info-edit-button").should("not.exist");
+ }
+
+ verifyContactInfoEditButtonNotExist() {
+ cy.get("#contact-info-edit-button").should("not.exist");
+ }
+
+ verifyProfessionalInfoEditButtonNotExist() {
+ cy.get("#professional-info-edit-button").should("not.exist");
+ }
+
+ verifyProfileTabPage() {
+ cy.get("#user-edit-form").should("be.visible");
+ }
+
+ verifyDoctorQualification() {
+ cy.get("#view-qualification").should("be.visible");
+ }
+
+ verifyDoctorQualificationDoesNotExist() {
+ cy.get("#view-qualification").should("not.exist");
+ }
+
+ verifyLinkedSkillsTabPage() {
+ cy.get("#select-skill").scrollIntoView();
+ cy.get("#select-skill").should("be.visible");
+ }
+
+ verifyLinkedFacilitiesTabPage() {
+ cy.get("#select-facility").should("be.visible");
+ }
+
+ verifyDeleteButtonNotExist() {
+ cy.get("[data-testid='user-delete-button']").should("not.exist");
+ }
+
+ verifyDeleteButtonVisible() {
+ cy.get("[data-testid='user-delete-button']").scrollIntoView();
+ cy.get("[data-testid='user-delete-button']").should("be.visible");
+ }
+
+ clickDeleteButton() {
+ cy.get("[data-testid='user-delete-button']").click();
+ }
+
clickAddSkillButton() {
cy.intercept("GET", "**/api/v1/skill/**").as("getSkills");
cy.get("#add-skill-button").click();
@@ -125,10 +385,19 @@ export class ManageUserPage {
cy.get("#unlink-skill").click();
}
+ verifyUnlinkSkillModal() {
+ cy.get("#unlink-skill-modal-description").should("be.visible");
+ cy.get("button[name='confirm-unlink-skill']").should("be.visible");
+ }
+
assertSkillInAddedUserSkills(skillName: string) {
cy.get("#added-user-skills").should("contain", skillName);
}
+ assertSkillNotInAddedUserSkills(skillName: string) {
+ cy.get("#added-user-skills").should("not.contain", skillName);
+ }
+
assertDoctorConnectVisibility(realName: string) {
cy.get('*[id="doctor-connect-home-doctor"]').should(
"contain.text",
diff --git a/cypress/pageobject/Users/UserCreation.ts b/cypress/pageobject/Users/UserCreation.ts
index d8d386402b7..26eaa088e23 100644
--- a/cypress/pageobject/Users/UserCreation.ts
+++ b/cypress/pageobject/Users/UserCreation.ts
@@ -46,6 +46,6 @@ export class UserCreationPage {
}
clickSaveUserButton() {
- cy.clickSubmitButton("Save User");
+ cy.clickSubmitButton("Submit");
}
}
diff --git a/cypress/pageobject/Users/UserSearch.ts b/cypress/pageobject/Users/UserSearch.ts
index 9d996abd83f..882a4dc8ea6 100644
--- a/cypress/pageobject/Users/UserSearch.ts
+++ b/cypress/pageobject/Users/UserSearch.ts
@@ -23,7 +23,11 @@ export class UserPage {
}
checkUsernameText(username: string) {
- cy.get(this.usernameText).should("contain.text", username);
+ cy.get(`${this.usernameText}-${username}`).should("contain.text", username);
+ }
+
+ checkUsernameTextDoesNotExist(username: string) {
+ cy.get(`${this.usernameText}-${username}`).should("not.exist");
}
checkUsernameBadgeVisibility(shouldBeVisible: boolean) {
@@ -69,24 +73,16 @@ export class UserPage {
}
verifyMultipleBadgesWithSameId(alreadylinkedusersviews: string[]) {
- cy.get("#user-view-name").then(($elements) => {
- const userViews = $elements
- .map((_, el) => Cypress.$(el).text().trim())
- .get();
- let foundMatches = 0;
-
- alreadylinkedusersviews.forEach((expectedContent) => {
- const index = userViews.findIndex((actualContent) =>
- actualContent.includes(expectedContent),
- );
- if (index !== -1) {
- userViews.splice(index, 1); // Remove the matched element
- foundMatches++;
- }
- if (foundMatches === alreadylinkedusersviews.length) {
- return false; // Break the loop if all matches are found
- }
- });
+ cy.wrap(alreadylinkedusersviews).each((username) => {
+ cy.get(`#name-${username}`).scrollIntoView().should("be.visible");
});
}
+
+ switchToListView() {
+ cy.get("#user-list-view").click();
+ }
+
+ verifyListView() {
+ cy.get("#user-list-view").should("have.class", "text-white");
+ }
}
diff --git a/public/locale/en.json b/public/locale/en.json
index d73268df952..9f0bc5ecf12 100644
--- a/public/locale/en.json
+++ b/public/locale/en.json
@@ -223,6 +223,9 @@
"URINATION_FREQUENCY__DECREASED": "Decreased",
"URINATION_FREQUENCY__INCREASED": "Increased",
"URINATION_FREQUENCY__NORMAL": "Normal",
+ "USERMANAGEMENT_TAB__FACILITIES": "Linked Facilities",
+ "USERMANAGEMENT_TAB__PROFILE": "User Information",
+ "USERMANAGEMENT_TAB__SKILLS": "Linked Skills",
"VENTILATOR": "Detailed Update",
"VENTILATOR_MODE__CMV": "Control Mechanical Ventilation (CMV)",
"VENTILATOR_MODE__CMV_short": "CMV",
@@ -293,6 +296,7 @@
"add_consultation": "Add consultation",
"add_consultation_update": "Add Consultation Update",
"add_details_of_patient": "Add Details of Patient",
+ "add_facility": "Add Facility",
"add_insurance_details": "Add Insurance Details",
"add_location": "Add Location",
"add_new_beds": "Add New Bed(s)",
@@ -304,6 +308,7 @@
"add_preset": "Add preset",
"add_prn_prescription": "Add PRN Prescription",
"add_remarks": "Add remarks",
+ "add_skill": "Add Skill",
"add_spoke": "Add Spoke Facility",
"address": "Address",
"administer": "Administer",
@@ -377,6 +382,7 @@
"autofilled_fields": "Autofilled Fields",
"available_features": "Available Features",
"available_in": "Available in",
+ "avatar_updated_success": "Avatar updated successfully",
"average_weekly_working_hours": "Average weekly working hours",
"awaiting_destination_approval": "AWAITING DESTINATION APPROVAL",
"back": "Back",
@@ -384,6 +390,7 @@
"back_to_consultation": "Go back to Consultation",
"back_to_login": "Back to login",
"base_dosage": "Dosage",
+ "basic_info": "Basic Information",
"bed_capacity": "Bed Capacity",
"bed_created_notification_one": "{{count}} Bed created successfully",
"bed_created_notification_other": "{{count}} Beds created successfully",
@@ -418,6 +425,8 @@
"category": "Category",
"caution": "Caution",
"central_nursing_station": "Central Nursing Station",
+ "change_avatar": "Change Avatar",
+ "change_avatar_note": "JPG, GIF or PNG. 1MB max.",
"change_file": "Change File",
"change_password": "Change Password",
"chat_on_whatsapp": "Chat on Whatsapp",
@@ -431,8 +440,11 @@
"checking_for_update": "Checking for update",
"checking_policy_eligibility": "Checking Policy Eligibility",
"choose_date_time": "Choose Date and Time",
+ "choose_district": "Choose District",
"choose_file": "Upload From Device",
+ "choose_localbody": "Choose Local Body",
"choose_location": "Choose Location",
+ "choose_state": "Choose State",
"claim__add_item": "Add Item",
"claim__create_claim": "Create Claim",
"claim__create_preauthorization": "Create Pre Authorization",
@@ -468,7 +480,10 @@
"clear": "Clear",
"clear_all_filters": "Clear All Filters",
"clear_home_facility": "Clear Home Facility",
+ "clear_home_facility_confirm": "Are you sure you want to clear the home facility",
+ "clear_home_facility_error": "Error while clearing home facility. Try again later.",
"clear_selection": "Clear selection",
+ "clear_skill": "Clear Skill",
"close": "Close",
"close_scanner": "Close Scanner",
"collapse_sidebar": "Collapse Sidebar",
@@ -483,6 +498,7 @@
"confirm_delete": "Confirm Delete",
"confirm_discontinue": "Confirm Discontinue",
"confirm_password": "Confirm Password",
+ "confirm_password_required": "Confirm password is required",
"confirm_transfer_complete": "Confirm Transfer Complete!",
"confirmed": "Confirmed",
"consent__hi_range": "Health Information Range",
@@ -523,7 +539,10 @@
"consultation_not_filed_description": "Please file a consultation for this patient to continue.",
"consultation_notes": "General Instructions (Advice)",
"consultation_updates": "Consultation updates",
- "contact_info": "Contact Info",
+ "contact_info": "Contact Information",
+ "contact_info_note": "View or update user's contact information",
+ "contact_info_note_self": "View or update your contact information",
+ "contact_info_note_view": "View user's contact information",
"contact_number": "Contact Number",
"contact_person": "Name of Contact Person at Facility",
"contact_person_at_the_facility": "Contact person at the current facility",
@@ -531,6 +550,7 @@
"contact_phone": "Contact Person Number",
"contact_with_confirmed_carrier": "Contact with confirmed carrier",
"contact_with_suspected_carrier": "Contact with suspected carrier",
+ "contact_your_admin_to_add_facilities": "Contact your admin to add facilities",
"contact_your_admin_to_add_skills": "Contact your admin to add skills",
"continue": "Continue",
"continue_watching": "Continue watching",
@@ -578,6 +598,9 @@
"days": "Days",
"death_report": "Death Report",
"delete": "Delete",
+ "delete_account": "Delete account",
+ "delete_account_btn": "Yes, delete this account",
+ "delete_account_note": "Deleting this account will remove all associated data and cannot be undone.",
"delete_facility": "Delete Facility",
"delete_item": "Delete {{name}}",
"delete_record": "Delete Record",
@@ -621,6 +644,9 @@
"disease_status": "Disease status",
"district": "District",
"district_program_management_supporting_unit": "District Program Management Supporting Unit",
+ "dob_format": "Please enter date in DD/MM/YYYY format",
+ "doctor_experience_error": "Please enter a valid number between 0 and 100.",
+ "doctor_experience_required": "Years of experience is required",
"doctor_s_medical_council_registration": "Doctor's Medical Council Registration",
"doctors_name": "Doctor's Name",
"domestic_healthcare_support": "Domestic healthcare support",
@@ -640,6 +666,9 @@
"duplicate_patient_record_rejection": "I confirm that the suspect / patient I want to create is not on the list.",
"edit": "Edit",
"edit_avatar": "Edit Avatar",
+ "edit_avatar_note": "Change the avatar of the user",
+ "edit_avatar_note_self": "Change your avatar",
+ "edit_avatar_permission_error": "You do not have permissions to edit the avatar of this user",
"edit_caution_note": "A new prescription will be added to the consultation with the edited details and the current prescription will be discontinued.",
"edit_cover_photo": "Edit Cover Photo",
"edit_history": "Edit History",
@@ -692,6 +721,8 @@
"enter_mobile_otp": "Enter OTP sent to the given mobile number",
"enter_otp": "Enter OTP sent to the registered mobile with the respective ID",
"enter_valid_age": "Please Enter Valid Age",
+ "enter_valid_dob": "Enter a valid date of birth",
+ "enter_valid_dob_age": "Please enter an age greater than 15 years",
"entered-in-error": "Entered in error",
"error_404": "Error 404",
"error_deleting_shifting": "Error while deleting Shifting record",
@@ -709,6 +740,7 @@
"facility": "Facility",
"facility_consent_requests_page_title": "Patient Consent List",
"facility_district_name": "Facility/District Name",
+ "facility_linked_success": "Facility linked successfully",
"facility_name": "Facility Name",
"facility_preference": "Facility preference",
"facility_search_placeholder": "Search by Facility / District Name",
@@ -742,10 +774,12 @@
"filter_by_category": "Filter by category",
"filters": "Filters",
"first_name": "First Name",
+ "first_name_required": "First Name is required",
"footer_body": "Open Healthcare Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. Open Healthcare Network CARE is a Digital Public Good recognised by the United Nations.",
"forget_password": "Forgot password?",
"forget_password_instruction": "Enter your username, and if it exists, we will send you a link to reset your password.",
"frequency": "Frequency",
+ "from_user": "from User",
"full_name": "Full Name",
"full_screen": "Full Screen",
"gender": "Gender",
@@ -794,6 +828,9 @@
"hide": "Hide",
"high": "High",
"home_facility": "Home Facility",
+ "home_facility_cleared_success": "Home Facility cleared successfully",
+ "home_facility_updated_error": "Error while updating Home Facility",
+ "home_facility_updated_success": "Home Facility updated successfully",
"hubs": "Hub Facilities",
"i_declare": "I hereby declare that:",
"icd11_as_recommended": "As per ICD-11 recommended by WHO",
@@ -812,7 +849,7 @@
"insurer_name_required": "Insurer Name is required",
"international_mobile": "International Mobile",
"invalid_asset_id_msg": "Oops! The asset ID you entered does not appear to be valid.",
- "invalid_email": "Please Enter a Valid Email Address",
+ "invalid_email": "Please enter a valid email address",
"invalid_ip_address": "Invalid IP Address",
"invalid_link_msg": "It appears that the password reset link you have used is either invalid or expired. Please request a new password reset link.",
"invalid_password": "Password doesn't meet the requirements",
@@ -822,7 +859,11 @@
"invalid_phone_number": "Invalid Phone Number",
"invalid_pincode": "Invalid Pincode",
"invalid_reset": "Invalid Reset",
+ "invalid_url": "Please enter a valid url",
+ "invalid_url_http_https": "URL should start with http:// or https://",
+ "invalid_url_javascript": "URL should not include javascript, please enter a valid URL.",
"invalid_username": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
+ "invalid_username_format": "Please enter a 4-16 characters long username with lowercase letters, digits and . _ - only and it should not start or end with . _ -",
"inventory_management": "Inventory Management",
"investigation_report": "Investigation Report",
"investigation_report_for_{{name}}": "Investigation Report for {{name}}",
@@ -845,6 +886,7 @@
"is_emergency": "Is emergency",
"is_emergency_case": "Is emergency case",
"is_it_upshift": "is it upshift",
+ "is_phone_a_whatsapp_number": "Is the phone number a WhatsApp number?",
"is_pregnant": "Is pregnant",
"is_this_an_emergency": "Is this an emergency?",
"is_this_an_upshift": "Is this an upshift?",
@@ -861,6 +903,7 @@
"last_edited": "Last Edited",
"last_modified": "Last Modified",
"last_name": "Last Name",
+ "last_name_required": "Last Name is required",
"last_online": "Last Online",
"last_serviced_on": "Last Serviced On",
"last_updated_by": "Last updated by",
@@ -872,8 +915,11 @@
"link_abha_profile": "Link ABHA Profile",
"link_camera_and_bed": "Link bed to Camera",
"link_existing_abha_profile": "Already have an ABHA number",
+ "link_facility_error": "Error while linking facility. Try again later.",
"linked_facilities": "Linked Facilities",
+ "linked_facilities_note": "Add or remove facilities and set or change the Home Facility",
"linked_skills": "Linked Skills",
+ "linked_skills_note": "Search and select skills to add to the skill set",
"liquid_oxygen_capacity": "Liquid Oxygen Capacity",
"list_view": "List View",
"litres": "Litres",
@@ -901,6 +947,7 @@
"manage_bed_presets": "Manage Presets of Bed",
"manage_prescriptions": "Manage Prescriptions",
"manage_preset": "Manage preset {{ name }}",
+ "manage_user": "Manage User",
"manufacturer": "Manufacturer",
"map_acronym": "M.A.P.",
"mark_all_as_read": "Mark all as Read",
@@ -915,6 +962,7 @@
"measured_before": "Measured before",
"medical": "Medical",
"medical_council_registration": "Medical Council Registration",
+ "medical_council_registration_required": "Medical Council Registration is required",
"medical_worker": "Medical Worker",
"medicine": "Medicine",
"medicine_administration_history": "Medicine Administration History",
@@ -941,6 +989,7 @@
"modified_date": "Modified Date",
"modified_on": "Modified On",
"monitor": "Monitor",
+ "more_details": "More details",
"more_info": "More Info",
"move_to_onvif_preset": "Move to an ONVIF Preset",
"moving_camera": "Moving Camera",
@@ -951,6 +1000,8 @@
"never": "never",
"new_password": "New Password",
"new_password_confirmation": "Confirm New Password",
+ "new_password_same_as_old": "New password is same as old password, please enter a different new password.",
+ "new_password_validation": "New password is not valid.",
"next_sessions": "Next Sessions",
"no": "No",
"no_attachments_found": "This communication has no attachments.",
@@ -966,7 +1017,7 @@
"no_duplicate_facility": "You should not create duplicate facilities",
"no_facilities": "No Facilities found",
"no_files_found": "No {{type}} files found",
- "no_home_facility": "No home facility assigned",
+ "no_home_facility": "No home facility",
"no_image_found": "No image found",
"no_investigation": "No investigation Reports found",
"no_investigation_suggestions": "No Investigation Suggestions",
@@ -977,6 +1028,7 @@
"no_notices_for_you": "No notices for you.",
"no_patients_found": "No Patients Found",
"no_patients_to_show": "No patients to show.",
+ "no_permission_to_view_page": "You do not have permissions to view this page",
"no_policy_added": "No Insurance Policy Added",
"no_policy_found": "No Insurance Policy Found for this Patient",
"no_presets": "No Presets",
@@ -1032,10 +1084,17 @@
"pain_chart_description": "Mark region and intensity of pain",
"passport_number": "Passport Number",
"password": "Password",
- "password_mismatch": "Password and confirm password must be same.",
+ "password_length_validation": "Password must be at least 8 characters long",
+ "password_lowercase_validation": "Password must contain at least one lowercase letter (a-z)",
+ "password_mismatch": "New password and confirm password must be the same.",
+ "password_number_validation": "Password must contain at least one number (0-9)",
+ "password_required": "Password is required",
"password_reset_failure": "Password Reset Failed",
"password_reset_success": "Password Reset successfully",
"password_sent": "Password Reset Email Sent",
+ "password_update_error": "Error while updating password. Try again later.",
+ "password_uppercase_validation": "Password must contain at least one uppercase letter (A-Z)",
+ "password_validation": "Password must contain at least: 8 characters, 1 uppercase letter (A-Z), 1 lowercase letter (a-z), and 1 number (0-9)",
"patient": "Patient",
"patient-notes": "Notes",
"patient__general-info": "General Info",
@@ -1082,19 +1141,31 @@
"permanent_address": "Permanent Address",
"permission_denied": "You do not have permission to perform this action",
"personal_information": "Personal Information",
+ "personal_information_note": "View or update user's personal information",
+ "personal_information_note_self": "View or update your personal information",
+ "personal_information_note_view": "View user's personal information",
"phone": "Phone",
"phone_no": "Phone no.",
"phone_number": "Phone Number",
"phone_number_at_current_facility": "Phone Number of Contact person at current Facility",
"pincode": "Pincode",
"please_assign_bed_to_patient": "Please assign a bed to this patient",
+ "please_confirm_password": "Please confirm your new password.",
"please_enter_a_reason_for_the_shift": "Please enter a reason for the shift.",
+ "please_enter_current_password": "Please enter your current password.",
+ "please_enter_new_password": "Please enter your new password.",
+ "please_enter_username": "Please enter the username",
"please_select_a_facility": "Please select a facility",
"please_select_breathlessness_level": "Please select Breathlessness Level",
+ "please_select_district": "Please select the district",
"please_select_facility_type": "Please select Facility Type",
+ "please_select_gender": "Please select the Gender",
+ "please_select_localbody": "Please select the local body",
"please_select_patient_category": "Please select Patient Category",
"please_select_preferred_vehicle_type": "Please select Preferred Vehicle Type",
+ "please_select_state": "Please select the state",
"please_select_status": "Please select Status",
+ "please_select_user_type": "Please select the User Type",
"please_upload_a_csv_file": "Please Upload A CSV file",
"policy": "Policy",
"policy__insurer": "Insurer",
@@ -1138,9 +1209,14 @@
"procedure_suggestions": "Procedure Suggestions",
"procedures_select_placeholder": "Select procedures to add details",
"process_transcript": "Process Again",
+ "professional_info": "Professional Information",
+ "professional_info_note": "View or update user's professional information",
+ "professional_info_note_self": "View or update your professional information",
+ "professional_info_note_view": "View user's professional information",
"profile": "Profile",
"provisional": "Provisional",
"qualification": "Qualification",
+ "qualification_required": "Qualification is required",
"raise_consent_request": "Raise a consent request to fetch patient records over ABDM",
"ration_card__APL": "APL",
"ration_card__BPL": "BPL",
@@ -1169,6 +1245,9 @@
"reload": "Reload",
"remove": "Remove",
"rename": "Rename",
+ "replace_home_facility": "Replace Home Facility",
+ "replace_home_facility_confirm": "Are you sure you want to replace",
+ "replace_home_facility_confirm_as": "as home facility for user",
"reply": "Reply",
"report": "Report",
"req_atleast_one_digit": "Require at least one digit",
@@ -1187,6 +1266,7 @@
"resend_otp": "Resend OTP",
"reset": "Reset",
"reset_password": "Reset Password",
+ "reset_password_note_self": "Enter your current password, then create and confirm your new password",
"resource": "Resource",
"resource_approving_facility": "Resource approving facility",
"resource_origin_facility": "Origin Facility",
@@ -1207,6 +1287,7 @@
"review_missed": "Review Missed",
"revoked_on": "Revoked On",
"right": "Right",
+ "role": "Role",
"route": "Route",
"routine": "Routine",
"sample_collection_date": "Sample Collection Date",
@@ -1263,6 +1344,7 @@
"session_expired": "Session Expired",
"session_expired_msg": "It appears that your session has expired. This could be due to inactivity. Please login again to continue.",
"set_average_weekly_working_hours_for": "Set Average weekly working hours for",
+ "set_home_facility": "Set as home facility",
"set_your_local_language": "Set your local language",
"settings_and_filters": "Settings and Filters",
"severity_of_breathlessness": "Severity of Breathlessness",
@@ -1284,6 +1366,8 @@
"show_patient_presets": "Show Patient Presets",
"show_unread_notifications": "Show Unread",
"sign_out": "Sign Out",
+ "skill_add_error": "Error while adding skill",
+ "skill_added_successfully": "Skill added successfully",
"skills": "Skills",
"social_profile": "Social Profile",
"socioeconomic_status": "Socioeconomic status",
@@ -1363,6 +1447,19 @@
"unlink_asset_bed_and_presets": "Delete linked presets and unlink bed",
"unlink_asset_bed_caution": "This action will also delete all presets that are associated to this camera and bed.",
"unlink_camera_and_bed": "Unlink this bed from this camera",
+ "unlink_facility": "Unlink Facility",
+ "unlink_facility_access": "The user will lose access to the facility",
+ "unlink_facility_confirm": "Are you sure you want to unlink the facility",
+ "unlink_facility_error": "Error while unlinking facility. Try again later.",
+ "unlink_facility_success": "Facility unlinked successfully",
+ "unlink_home_facility_error": "Error while unlinking home facility. Try again later.",
+ "unlink_home_facility_success": "Home Facility cleared successfully",
+ "unlink_skill": "Unlink Skill",
+ "unlink_skill_access": "The user will not have the skill associated anymore.",
+ "unlink_skill_confirm": "Are you sure you want to unlink the skill",
+ "unlink_skill_error": "Error while unlinking skill. Try again later.",
+ "unlink_skill_success": "Skill unlinked successfully",
+ "unlink_this_facility": "Unlink Facility",
"unsubscribe": "Unsubscribe",
"unsubscribe_failed": "Unsubscribe failed.",
"unsubscribed_successfully": "Unsubscribed Successfully.",
@@ -1397,9 +1494,22 @@
"upload_report": "Upload Report",
"uploading": "Uploading",
"use_existing_abha_address": "Use Existing ABHA Address",
- "user_deleted_successfuly": "User Deleted Successfuly",
+ "user_add_error": "Error while adding User",
+ "user_added_successfully": "User added successfully",
+ "user_delete_error": "Error while deleting User",
+ "user_deleted_successfully": "User Deleted Successfully",
+ "user_details": "User Details",
+ "user_details_update_error": "Error while updating user details",
+ "user_details_update_success": "User details updated successfully",
"user_management": "User Management",
+ "user_qualifications": "Qualifications",
+ "user_qualifications_note": "Enter appropriate qualifications for this user",
+ "user_type": "User Type",
"username": "Username",
+ "username_already_exists": "This username already exists",
+ "username_available": "Username is available",
+ "username_not_available": "Username is not available",
+ "username_userdetails_not_found": "Unable to fetch details as username or user details not found",
"users": "Users",
"vacant": "Vacant",
"vaccinated": "Vaccinated",
@@ -1433,6 +1543,8 @@
"view_patients": "View Patients",
"view_update_patient_files": "View/Update patient files",
"view_updates": "View Updates",
+ "view_user": "View User",
+ "view_user_profile": "View Profile",
"view_users": "View Users",
"village": "Village",
"virtual_nursing_assistant": "Virtual Nursing Assistant",
@@ -1445,10 +1557,12 @@
"volunteer_unassigned": "Volunteer unassigned successfuly",
"ward": "Ward",
"warranty_amc_expiry": "Warranty / AMC Expiry",
+ "weekly_working_hours_error": "Average weekly working hours must be a number between 0 and 168",
"what_facility_assign_the_patient_to": "What facility would you like to assign the patient to",
"whatsapp_number": "Whatsapp Number",
"why_the_asset_is_not_working": "Why the asset is not working?",
"width": "Width ({{unit}})",
+ "with": "with",
"working_status": "Working Status",
"year_of_birth": "Year of Birth",
"years": "years",
diff --git a/src/Routers/routes/UserRoutes.tsx b/src/Routers/routes/UserRoutes.tsx
index ff7212a02e2..cc668e2fee6 100644
--- a/src/Routers/routes/UserRoutes.tsx
+++ b/src/Routers/routes/UserRoutes.tsx
@@ -1,5 +1,6 @@
import ManageUsers from "@/components/Users/ManageUsers";
-import { UserAdd } from "@/components/Users/UserAdd";
+import UserAdd from "@/components/Users/UserAdd";
+import UserHome from "@/components/Users/UserHome";
import UserProfile from "@/components/Users/UserProfile";
import { AppRoutes } from "@/Routers/AppRouter";
@@ -7,6 +8,12 @@ import { AppRoutes } from "@/Routers/AppRouter";
const UserRoutes: AppRoutes = {
"/users": () => ,
"/users/add": () => ,
+ "/users/:username": ({ username }) => (
+
+ ),
+ "/users/:username/:tab": ({ username, tab }) => (
+
+ ),
"/user/profile": () => ,
};
diff --git a/src/Utils/permissions.ts b/src/Utils/permissions.ts
index 35f12715eff..41587d2dcfd 100644
--- a/src/Utils/permissions.ts
+++ b/src/Utils/permissions.ts
@@ -2,29 +2,58 @@ import { UserModel } from "@/components/Users/models";
import { USER_TYPES, UserRole } from "@/common/constants";
+const checkIfStateOrDistrictAdminInSameLocation = (
+ authUser: UserModel,
+ targetUser: UserModel,
+) => {
+ const hasLocation = Boolean(
+ targetUser.state_object || targetUser.district_object,
+ );
+
+ const isStateAdminOfSameState =
+ authUser.user_type === "StateAdmin" &&
+ targetUser.state_object?.id === authUser.state;
+
+ const isDistrictAdminOfSameDistrict =
+ authUser.user_type === "DistrictAdmin" &&
+ targetUser.district_object?.id === authUser.district;
+
+ return (
+ hasLocation && (isStateAdminOfSameState || isDistrictAdminOfSameDistrict)
+ );
+};
+
export const showUserDelete = (authUser: UserModel, targetUser: UserModel) => {
// Auth user should be higher in hierarchy than target user
+ // User can't delete their own account
if (
USER_TYPES.indexOf(authUser.user_type) <=
- USER_TYPES.indexOf(targetUser.user_type)
+ USER_TYPES.indexOf(targetUser.user_type) ||
+ authUser.username === targetUser.username
)
return false;
- if (
- authUser.user_type === "StateAdmin" &&
- targetUser.state_object?.id === authUser.state
- )
- return true;
-
- if (
- authUser.user_type === "DistrictAdmin" &&
- targetUser.district_object?.id === authUser.district
- )
- return true;
+ return checkIfStateOrDistrictAdminInSameLocation(authUser, targetUser);
+};
- return false;
+export const showUserPasswordReset = (
+ authUser: UserModel,
+ targetUser: UserModel,
+) => {
+ return authUser.username === targetUser.username;
};
+export const showAvatarEdit = (authUser: UserModel, targetUser: UserModel) => {
+ return authUser.username === targetUser.username || authUser.is_superuser;
+};
+export const editUserPermissions = (
+ authUser: UserModel,
+ targetUser: UserModel,
+) => {
+ if (authUser.username === targetUser.username || authUser.is_superuser)
+ return true;
+ return checkIfStateOrDistrictAdminInSameLocation(authUser, targetUser);
+};
export const CameraFeedPermittedUserTypes: UserRole[] = [
"DistrictAdmin",
"StateAdmin",
diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts
index a7e6a0a78ec..5588e048bd2 100644
--- a/src/Utils/utils.ts
+++ b/src/Utils/utils.ts
@@ -217,7 +217,9 @@ export const formatCurrency = (price: number) =>
});
export const isUserOnline = (user: { last_login: DateLike }) => {
- return dayjs().subtract(5, "minutes").isBefore(user.last_login);
+ return user.last_login
+ ? dayjs().subtract(5, "minutes").isBefore(user.last_login)
+ : false;
};
export interface CountryData {
diff --git a/src/common/validation.tsx b/src/common/validation.tsx
index 99b4342fbf6..f091303a452 100644
--- a/src/common/validation.tsx
+++ b/src/common/validation.tsx
@@ -57,6 +57,11 @@ export const validatePincode = (pincode: string) => {
return pattern.test(pincode);
};
+export const validateNumber = (number: string) => {
+ const pattern = /^[0-9]+$/;
+ return pattern.test(number);
+};
+
export const checkIfValidIP = (str: string) => {
// Regular expression to check if string is a IP address
const regexExp =
diff --git a/src/components/Auth/ResetPassword.tsx b/src/components/Auth/ResetPassword.tsx
index 11b541e276c..f993b621062 100644
--- a/src/components/Auth/ResetPassword.tsx
+++ b/src/components/Auth/ResetPassword.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { Cancel, Submit } from "@/components/Common/ButtonV2";
import TextFormField from "@/components/Form/FormFields/TextFormField";
-import { validateRule } from "@/components/Users/UserAdd";
+import { validateRule } from "@/components/Users/UserAddEditForm";
import { LocalStorageKeys } from "@/common/constants";
import { validatePassword } from "@/common/validation";
@@ -134,22 +134,22 @@ const ResetPassword = (props: ResetPasswordProps) => {
{validateRule(
form.password?.length >= 8,
- "Password should be atleast 8 characters long",
+ t("password_length_validation"),
!form.password,
)}
{validateRule(
form.password !== form.password.toUpperCase(),
- "Password should contain at least 1 lowercase letter",
+ t("password_lowercase_validation"),
!form.password,
)}
{validateRule(
form.password !== form.password.toLowerCase(),
- "Password should contain at least 1 uppercase letter",
+ t("password_uppercase_validation"),
!form.password,
)}
{validateRule(
/\d/.test(form.password),
- "Password should contain at least 1 number",
+ t("password_number_validation"),
!form.password,
)}
@@ -168,7 +168,7 @@ const ResetPassword = (props: ResetPasswordProps) => {
form.password.length > 0 &&
validateRule(
form.confirm === form.password,
- "Confirm password should match the entered password",
+ t("password_mismatch"),
!form.password && form.password.length > 0,
)}
diff --git a/src/components/Common/ConfirmDialog.tsx b/src/components/Common/ConfirmDialog.tsx
index 0183fc1d651..5fff5d9f8fb 100644
--- a/src/components/Common/ConfirmDialog.tsx
+++ b/src/components/Common/ConfirmDialog.tsx
@@ -13,6 +13,7 @@ type ConfirmDialogProps = {
onConfirm: () => void;
children?: React.ReactNode;
cancelLabel?: string;
+ name?: string;
};
const ConfirmDialog = ({
@@ -22,6 +23,7 @@ const ConfirmDialog = ({
onConfirm,
cancelLabel,
children,
+ name,
...props
}: ConfirmDialogProps) => {
return (
@@ -29,7 +31,12 @@ const ConfirmDialog = ({
{children}
-
+
{action}
diff --git a/src/components/Common/FacilitySelect.tsx b/src/components/Common/FacilitySelect.tsx
index e4397cbcb9c..4c0a6629bec 100644
--- a/src/components/Common/FacilitySelect.tsx
+++ b/src/components/Common/FacilitySelect.tsx
@@ -27,6 +27,7 @@ interface FacilitySelectProps {
allowNone?: boolean;
placeholder?: string;
filter?: (facilities: FacilityModel) => boolean;
+ id?: string;
}
export const FacilitySelect = (props: FacilitySelectProps) => {
@@ -50,6 +51,7 @@ export const FacilitySelect = (props: FacilitySelectProps) => {
errors = "",
placeholder,
filter,
+ id,
} = props;
const facilitySearch = useCallback(
@@ -88,6 +90,7 @@ export const FacilitySelect = (props: FacilitySelectProps) => {
return (
{
);
return (
-
+
);
};
diff --git a/src/components/Common/Page.tsx b/src/components/Common/Page.tsx
index 558d1432182..874bfce4ad6 100644
--- a/src/components/Common/Page.tsx
+++ b/src/components/Common/Page.tsx
@@ -17,6 +17,7 @@ interface PageProps extends PageTitleProps {
* @default false
**/
collapseSidebar?: boolean;
+ hideTitleOnPage?: boolean;
}
export default function Page(props: PageProps) {
@@ -51,6 +52,7 @@ export default function Page(props: PageProps) {
focusOnLoad={props.focusOnLoad}
onBackClick={props.onBackClick}
isInsidePage={true}
+ hideTitleOnPage={props.hideTitleOnPage}
/>
{props.options}
diff --git a/src/components/Common/PageTitle.tsx b/src/components/Common/PageTitle.tsx
index ecdadec2d62..b7af8f0eaa8 100644
--- a/src/components/Common/PageTitle.tsx
+++ b/src/components/Common/PageTitle.tsx
@@ -19,6 +19,7 @@ export interface PageTitleProps {
// New props for Breadcrumbs
hideBack?: boolean;
backUrl?: string;
+ hideTitleOnPage?: boolean;
onBackClick?: () => boolean | void;
}
@@ -35,6 +36,7 @@ export default function PageTitle({
hideBack = false,
backUrl,
onBackClick,
+ hideTitleOnPage,
}: PageTitleProps) {
const divRef = useRef();
@@ -70,7 +72,9 @@ export default function PageTitle({
)}
>
-
{title}
+ {!hideTitleOnPage && (
+ {title}
+ )}
{componentRight}
diff --git a/src/components/Common/SkillSelect.tsx b/src/components/Common/SkillSelect.tsx
index 74ddbba5d83..eff16d1380e 100644
--- a/src/components/Common/SkillSelect.tsx
+++ b/src/components/Common/SkillSelect.tsx
@@ -1,7 +1,7 @@
import { useCallback } from "react";
import AutoCompleteAsync from "@/components/Form/AutoCompleteAsync";
-import { SkillModel, SkillObjectModel } from "@/components/Users/models";
+import { SkillModel } from "@/components/Users/models";
import routes from "@/Utils/request/api";
import request from "@/Utils/request/request";
@@ -15,8 +15,8 @@ interface SkillSelectProps {
multiple?: boolean;
showNOptions?: number;
disabled?: boolean;
- selected: SkillObjectModel | SkillObjectModel[] | null;
- setSelected: (selected: SkillObjectModel) => void;
+ selected: SkillModel | null;
+ setSelected: (selected: SkillModel | null) => void;
userSkills?: SkillModel[];
}
diff --git a/src/components/Common/Tabs.tsx b/src/components/Common/Tabs.tsx
index 9a549cfabcf..9c375db618e 100644
--- a/src/components/Common/Tabs.tsx
+++ b/src/components/Common/Tabs.tsx
@@ -8,7 +8,7 @@ export default function Tabs(props: {
className?: string;
currentTab: string | number;
onTabChange: (value: string | number) => void;
- tabs: { text: ReactNode; value: string | number }[];
+ tabs: { text: ReactNode; value: string | number; id?: string }[];
}) {
const { className, currentTab, onTabChange, tabs } = props;
const ref = useRef(null);
@@ -60,6 +60,7 @@ export default function Tabs(props: {
{tabs.map((tab, i) => (