diff --git a/cypress/e2e/users_spec/user_profile.cy.ts b/cypress/e2e/users_spec/user_profile.cy.ts new file mode 100644 index 00000000000..b6fe64b2455 --- /dev/null +++ b/cypress/e2e/users_spec/user_profile.cy.ts @@ -0,0 +1,85 @@ +import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress"; +import LoginPage from "../../pageobject/Login/LoginPage"; +import UserProfilePage from "../../pageobject/Users/UserProfilePage"; +import ManageUserPage from "../../pageobject/Users/ManageUserPage"; + +describe("Manage User Profile", () => { + const loginPage = new LoginPage(); + const userProfilePage = new UserProfilePage(); + const manageUserPage = new ManageUserPage(); + + const age = "30"; + const gender = "Male"; + const email = "test@example.com"; + const phone = "+918899887788"; + const workinghours = "8"; + const doctorQualification = "MBBS"; + const doctorYoE = "10"; + const medicalCouncilRegistration = "1234567890"; + + const facilitySearch = "Dummy Facility 1"; + + before(() => { + loginPage.loginAsDevDoctor(); + cy.saveLocalStorage(); + }); + + beforeEach(() => { + cy.restoreLocalStorage(); + console.log(localStorage); + cy.clearLocalStorage(/filters--.+/); + console.log(localStorage); + cy.awaitUrl("/user/profile"); + }); + + it("Set Age, Gender, Email, Phone and Working Hours for a user and verify its reflection in user profile", () => { + userProfilePage.clickEditProfileButton(); + + userProfilePage.typeAge(age); + userProfilePage.selectGender(gender); + userProfilePage.typeEmail(email); + userProfilePage.typePhone(phone); + userProfilePage.typeWhatsApp(phone); + userProfilePage.typeWorkingHours(workinghours); + userProfilePage.typeDoctorQualification(doctorQualification); + userProfilePage.typeDoctorYoE(doctorYoE); + userProfilePage.typeMedicalCouncilRegistration(medicalCouncilRegistration); + + userProfilePage.clickUpdateButton(); + + cy.verifyNotification("Details updated successfully"); + + userProfilePage.assertAge(age); + userProfilePage.assertGender(gender); + userProfilePage.assertEmail(email); + userProfilePage.assertPhone(phone); + userProfilePage.assertWhatsApp(phone); + userProfilePage.assertWorkingHours(workinghours); + }); + + it("Adding video connect link for a user and verify its reflection in user profile and doctor connect", () => { + // verify the user doesn't have any video connect link + userProfilePage.assertVideoConnectLink("-"); + // Link a new video connect link and ensure it is under video connect link + userProfilePage.clickEditProfileButton(); + userProfilePage.typeVideoConnectLink("https://www.example.com"); + userProfilePage.clickUpdateButton(); + userProfilePage.assertVideoConnectLink("https://www.example.com"); + // Edit the video connect link and ensure it is updated + userProfilePage.clickEditProfileButton(); + userProfilePage.typeVideoConnectLink("https://www.test.com"); + userProfilePage.clickUpdateButton(); + userProfilePage.assertVideoConnectLink("https://www.test.com"); + // Go to particular facility doctor connect and verify the video connect link is present + manageUserPage.navigateToFacility(); + manageUserPage.typeFacilitySearch(facilitySearch); + manageUserPage.assertFacilityInCard(facilitySearch); + manageUserPage.clickFacilityPatients(); + manageUserPage.clickDoctorConnectButton(); + manageUserPage.assertVideoConnectLink("Dev Doctor", "https://www.test.com"); + }); + + afterEach(() => { + cy.saveLocalStorage(); + }); +}); diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts index f691d5f9e15..f4f188f11d6 100644 --- a/cypress/pageobject/Login/LoginPage.ts +++ b/cypress/pageobject/Login/LoginPage.ts @@ -6,6 +6,10 @@ class LoginPage { cy.loginByApi("devdistrictadmin", "Coronasafe@123"); } + loginAsDevDoctor(): void { + cy.loginByApi("devdoctor", "Coronasafe@123"); + } + login(username: string, password: string): void { cy.loginByApi(username, password); } diff --git a/cypress/pageobject/Users/ManageUserPage.ts b/cypress/pageobject/Users/ManageUserPage.ts index 2d1ebbc14f0..622229745f7 100644 --- a/cypress/pageobject/Users/ManageUserPage.ts +++ b/cypress/pageobject/Users/ManageUserPage.ts @@ -149,6 +149,18 @@ export class ManageUserPage { cy.get("#doctor-connect-home-doctor").should("contain.text", realName); cy.get("#doctor-connect-remote-doctor").should("contain.text", realName); } + + assertVideoConnectLink(docName: string, link: string) { + cy.get("ul#options") + .find("li") + .contains(docName) + .within(() => { + cy.get("a").should(($a) => { + const hrefs = $a.map((i, el) => Cypress.$(el).attr("href")).get(); + expect(hrefs).to.include(link); + }); + }); + } } export default ManageUserPage; diff --git a/cypress/pageobject/Users/UserProfilePage.ts b/cypress/pageobject/Users/UserProfilePage.ts new file mode 100644 index 00000000000..3f71a29181b --- /dev/null +++ b/cypress/pageobject/Users/UserProfilePage.ts @@ -0,0 +1,84 @@ +export default class UserProfilePage { + assertVideoConnectLink(link: string) { + cy.get("#videoconnectlink-profile-details").should("contain.text", link); + } + + clickEditProfileButton() { + cy.get("#edit-cancel-profile-button").click(); + } + + typeVideoConnectLink(link: string) { + cy.get("#video_connect_link").click().clear().type(link); + } + + clickUpdateButton() { + cy.get("#submit").click(); + } + + typeAge(age: string) { + cy.get("#age").click().clear().type(age); + } + + selectGender(gender: string) { + cy.get("#gender").click(); + cy.get("#gender-option-" + gender).click(); + } + + typeEmail(email: string) { + cy.get("#email").click().clear().type(email); + } + + typePhone(phone: string) { + cy.get("#phoneNumber").click().clear().type(phone); + } + + typeWhatsApp(phone: string) { + cy.get("#altPhoneNumber").click().clear().type(phone); + } + + typeWorkingHours(workinghours: string) { + cy.get("#weekly_working_hours").click().clear().type(workinghours); + } + + typeDoctorQualification = (doctorQualification: string) => { + cy.get("#doctor_qualification").click().clear().type(doctorQualification); + }; + + typeDoctorYoE = (doctorYoE: string) => { + cy.get("#doctor_experience_commenced_on").click().clear().type(doctorYoE); + }; + + typeMedicalCouncilRegistration = (medicalCouncilRegistration: string) => { + cy.get("#doctor_medical_council_registration") + .click() + .clear() + .type(medicalCouncilRegistration); + }; + + assertAge(age: string) { + cy.get("#age-profile-details").should("contain.text", age); + } + + assertGender(gender: string) { + cy.get("#gender-profile-details").should("contain.text", gender); + } + + assertEmail(email: string) { + cy.get("#emailid-profile-details").should("contain.text", email); + } + + assertPhone(phone: string) { + cy.get("#contactno-profile-details").should("contain.text", phone); + } + + assertWhatsApp(phone: string) { + cy.get("#whatsapp-profile-details").should("contain.text", phone); + } + + assertWorkingHours(workinghours: string) { + cy.get("#averageworkinghour-profile-details").should( + "contain.text", + workinghours + ); + } +} diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index 5302f7f9d53..f16b7ba1811 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -6,6 +6,8 @@ import { UserAssignedModel } from "../Users/models"; import { SkillObjectModel } from "../Users/models"; import CareIcon from "../../CAREUI/icons/CareIcon"; import { relativeTime } from "../../Utils/utils"; +import useAuthUser from "../../Common/hooks/useAuthUser"; +import { triggerGoal } from "../../Integrations/Plausible"; export default function DoctorVideoSlideover(props: { show: boolean; @@ -22,10 +24,12 @@ export default function DoctorVideoSlideover(props: { const res = await dispatchAction( getFacilityUsers(facilityId, { limit: 50 }) ); - if (res && res.data) { + if (res?.data) { setDoctors( res.data.results - .filter((user: any) => user.alt_phone_number) + .filter( + (user: any) => user.alt_phone_number || user.video_connect_link + ) .sort((a: any, b: any) => { return Number(a.last_login) - Number(b.last_login); }) @@ -106,6 +110,7 @@ function UserListItem(props: { user: UserAssignedModel }) { const user = props.user; const icon = user.user_type === "Doctor" ? "fa-user-doctor " : " fa-user-nurse"; + const authUser = useAuthUser(); return (
  • @@ -152,6 +157,27 @@ function UserListItem(props: { user: UserAssignedModel }) { {user.first_name} {user.last_name}
    + {user.video_connect_link && ( + { + triggerGoal("Doctor Connect Click", { + medium: "Video Call", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} + target="_blank" + rel="noopener noreferrer" + > +
    + + Connect on a Video Call + + +
    +
    + )} { + triggerGoal("Doctor Connect Click", { + medium: "WhatsApp", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} target="_blank" rel="noopener noreferrer" > @@ -176,6 +209,13 @@ function UserListItem(props: { user: UserAssignedModel }) { href={ user.alt_phone_number ? `tel:${user.alt_phone_number}` : "#" } + onClick={() => { + triggerGoal("Doctor Connect Click", { + medium: "Phone Call", + userId: authUser?.id, + targetUserType: user.user_type, + }); + }} >
    diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 2dc75d33613..1fcc8fb385e 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -5,7 +5,7 @@ import * as Notification from "../../Utils/Notifications.js"; import LanguageSelector from "../../Components/Common/LanguageSelector"; import TextFormField from "../Form/FormFields/TextFormField"; import ButtonV2, { Submit } from "../Common/components/ButtonV2"; -import { classNames, parsePhoneNumber } from "../../Utils/utils"; +import { classNames, isValidUrl, parsePhoneNumber } from "../../Utils/utils"; import CareIcon from "../../CAREUI/icons/CareIcon"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; @@ -27,6 +27,7 @@ type EditForm = { age: string; gender: GenderType; email: string; + video_connect_link: string | undefined; phoneNumber: string; altPhoneNumber: string; user_type: string | undefined; @@ -41,6 +42,7 @@ type ErrorForm = { age: string; gender: string; email: string; + video_connect_link: string | undefined; phoneNumber: string; altPhoneNumber: string; user_type: string | undefined; @@ -62,6 +64,7 @@ const initForm: EditForm = { lastName: "", age: "", gender: "Male", + video_connect_link: "", email: "", phoneNumber: "", altPhoneNumber: "", @@ -145,6 +148,7 @@ export default function UserProfile() { age: result.data.age?.toString() || "", gender: result.data.gender || "Male", email: result.data.email, + video_connect_link: result.data.video_connect_link, phoneNumber: result.data.phone_number?.toString() || "", altPhoneNumber: result.data.alt_phone_number?.toString() || "", user_type: result.data.user_type, @@ -262,6 +266,14 @@ export default function UserProfile() { invalidForm = true; } return; + case "video_connect_link": + if (states.form[field]) { + if (isValidUrl(states.form[field]) === false) { + errors[field] = "Please enter a valid url"; + invalidForm = true; + } + } + return; } }); dispatch({ type: "set_error", errors }); @@ -294,6 +306,7 @@ export default function UserProfile() { first_name: states.form.firstName, last_name: states.form.lastName, email: states.form.email, + video_connect_link: states.form.video_connect_link, phone_number: parsePhoneNumber(states.form.phoneNumber) ?? "", alt_phone_number: parsePhoneNumber(states.form.altPhoneNumber) ?? "", gender: states.form.gender, @@ -578,6 +591,28 @@ export default function UserProfile() { {userData?.weekly_working_hours || "-"}
    +
    )} @@ -679,6 +714,12 @@ export default function UserProfile() { min={0} max={168} /> +
    diff --git a/src/Components/Users/models.tsx b/src/Components/Users/models.tsx index 6fa0bfb3e42..a6cd0d7c8ff 100644 --- a/src/Components/Users/models.tsx +++ b/src/Components/Users/models.tsx @@ -28,6 +28,7 @@ export type UserModel = UserBareMinimum & { local_body?: number; district?: number; state?: number; + video_connect_link: string; phone_number?: string; alt_phone_number?: string; gender?: GenderType; @@ -62,6 +63,7 @@ export interface UserAssignedModel extends UserBareMinimum { state?: number; phone_number?: string; alt_phone_number?: string; + video_connect_link: string; gender?: number; age?: number; is_superuser?: boolean; diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index df6bd46d131..c518bcffe7d 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -462,3 +462,12 @@ export const compareBy = (key: keyof T) => { return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0; }; }; + +export const isValidUrl = (url?: string) => { + try { + new URL(url ?? ""); + return true; + } catch { + return false; + } +};