From c4071204a02f029542ffe19e9de9986e02e6dfb8 Mon Sep 17 00:00:00 2001 From: Sarvesh <142646866+sarvesh-official@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:35:14 +0530 Subject: [PATCH 01/10] Implemented Next and Previous button for files preview (#9196) --- src/components/Common/FilePreviewDialog.tsx | 113 ++++++++++++++++++-- src/components/Files/FileUpload.tsx | 12 ++- src/components/Patient/FileUploadPage.tsx | 2 - src/hooks/useFileManager.tsx | 9 +- 4 files changed, 123 insertions(+), 13 deletions(-) diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index 4b7a07bd0cd..fa8a333515d 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -4,6 +4,7 @@ import { SetStateAction, Suspense, lazy, + useEffect, useState, } from "react"; import { useTranslation } from "react-i18next"; @@ -15,6 +16,8 @@ import CircularProgress from "@/components/Common/CircularProgress"; import DialogModal from "@/components/Common/Dialog"; import { StateInterface } from "@/components/Files/FileUpload"; +import { FileUploadModel } from "../Patient/models"; + const PDFViewer = lazy(() => import("@/components/Common/PDFViewer")); export const zoom_values = [ @@ -40,6 +43,9 @@ type FilePreviewProps = { className?: string; titleAction?: ReactNode; fixedWidth?: boolean; + uploadedFiles?: FileUploadModel[]; + loadFile?: (file: FileUploadModel, associating_id: string) => void; + currentIndex: number; }; const previewExtensions = [ @@ -56,12 +62,28 @@ const previewExtensions = [ ]; const FilePreviewDialog = (props: FilePreviewProps) => { - const { show, onClose, file_state, setFileState, downloadURL, fileUrl } = - props; + const { + show, + onClose, + file_state, + setFileState, + downloadURL, + fileUrl, + uploadedFiles, + loadFile, + currentIndex, + } = props; const { t } = useTranslation(); const [page, setPage] = useState(1); const [numPages, setNumPages] = useState(1); + const [index, setIndex] = useState(currentIndex); + + useEffect(() => { + if (uploadedFiles && show) { + setIndex(currentIndex); + } + }, [uploadedFiles, show, currentIndex]); const handleZoomIn = () => { const checkFull = file_state.zoom === zoom_values.length; @@ -79,9 +101,27 @@ const FilePreviewDialog = (props: FilePreviewProps) => { }); }; + const handleNext = (newIndex: number) => { + if ( + !uploadedFiles?.length || + !loadFile || + newIndex < 0 || + newIndex >= uploadedFiles.length + ) + return; + + const nextFile = uploadedFiles[newIndex]; + if (!nextFile?.id) return; + + const associating_id = nextFile.associating_id || ""; + loadFile(nextFile, associating_id); + setIndex(newIndex); + }; + const handleClose = () => { setPage(1); setNumPages(1); + setIndex(-1); onClose?.(); }; @@ -102,6 +142,21 @@ const FilePreviewDialog = (props: FilePreviewProps) => { : `rotate-${normalizedRotation}`; } + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (!show) return; + if (e.key === "ArrowLeft" && index > 0) { + handleNext(index - 1); + } + if (e.key === "ArrowRight" && index < (uploadedFiles?.length || 0) - 1) { + handleNext(index + 1); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [show, index, uploadedFiles]); + return ( { onClose={() => { handleClose(); }} - title={t("file_preview")} + title={{t("file_preview")}} show={show} > {fileUrl ? ( <> -
-

- {file_state.name}.{file_state.extension} -

-
+
+
+

+ {file_state.name}.{file_state.extension} +

+ {uploadedFiles && + uploadedFiles[index] && + uploadedFiles[index].created_date && ( +

+ Created on{" "} + {new Date( + uploadedFiles[index].created_date!, + ).toLocaleString("en-US", { + dateStyle: "long", + timeStyle: "short", + })} +

+ )} +
+
{downloadURL && downloadURL.length > 0 && ( {
+ {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index - 1)} + disabled={index <= 0} + aria-label="Previous file" + onKeyDown={(e) => + e.key === "ArrowLeft" && handleNext(index - 1) + } + > + + + )}
{file_state.isImage ? ( {
)}
+ + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index + 1)} + disabled={index >= uploadedFiles.length - 1} + aria-label="Next file" + onKeyDown={(e) => + e.key === "ArrowRight" && handleNext(index + 1) + } + > + + + )}
diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx index d98df131863..50ab3ffdb01 100644 --- a/src/components/Files/FileUpload.tsx +++ b/src/components/Files/FileUpload.tsx @@ -69,6 +69,8 @@ export interface StateInterface { isZoomInDisabled: boolean; isZoomOutDisabled: boolean; rotation: number; + id?: string; + associating_id?: string; } export const FileUpload = (props: FileUploadProps) => { @@ -208,8 +210,15 @@ export const FileUpload = (props: FileUploadProps) => { type, onArchive: refetchAll, onEdit: refetchAll, + uploadedFiles: + fileQuery?.data?.results + .slice() + .reverse() + .map((file) => ({ + ...file, + associating_id: associatedId, + })) || [], }); - const dischargeSummaryFileManager = useFileManager({ type: "DISCHARGE_SUMMARY", onArchive: refetchAll, @@ -244,7 +253,6 @@ export const FileUpload = (props: FileUploadProps) => { id: "record-audio", }, ]; - return (
{fileUpload.Dialogues} diff --git a/src/components/Patient/FileUploadPage.tsx b/src/components/Patient/FileUploadPage.tsx index 518644f54d7..d38b149ebeb 100644 --- a/src/components/Patient/FileUploadPage.tsx +++ b/src/components/Patient/FileUploadPage.tsx @@ -11,12 +11,10 @@ export default function FileUploadPage(props: { type: "CONSULTATION" | "PATIENT"; }) { const { facilityId, patientId, consultationId, type } = props; - const { data: patient } = useQuery(routes.getPatient, { pathParams: { id: patientId }, prefetch: !!patientId, }); - return ( void; onEdit?: () => void; + uploadedFiles?: FileUploadModel[]; } export interface FileManagerResult { viewFile: (file: FileUploadModel, associating_id: string) => void; @@ -48,7 +49,7 @@ export interface FileManagerResult { export default function useFileManager( options: FileManagerOptions, ): FileManagerResult { - const { type: fileType, onArchive, onEdit } = options; + const { type: fileType, onArchive, onEdit, uploadedFiles } = options; const [file_state, setFileState] = useState({ open: false, @@ -72,6 +73,7 @@ export default function useFileManager( const [editDialogueOpen, setEditDialogueOpen] = useState(null); const [editError, setEditError] = useState(""); + const [currentIndex, setCurrentIndex] = useState(-1); const getExtension = (url: string) => { const div1 = url.split("?")[0].split("."); @@ -80,6 +82,8 @@ export default function useFileManager( }; const viewFile = async (file: FileUploadModel, associating_id: string) => { + const index = uploadedFiles?.findIndex((f) => f.id === file.id) ?? -1; + setCurrentIndex(index); setFileUrl(""); setFileState({ ...file_state, open: true }); const { data } = await request(routes.retrieveUpload, { @@ -225,9 +229,12 @@ export default function useFileManager( file_state={file_state} setFileState={setFileState} downloadURL={downloadURL} + uploadedFiles={uploadedFiles} onClose={handleFilePreviewClose} fixedWidth={false} className="h-[80vh] w-full md:h-screen" + loadFile={viewFile} + currentIndex={currentIndex} /> Date: Fri, 29 Nov 2024 11:58:38 +0530 Subject: [PATCH 02/10] Fix: Date of Birth to Year of Birth on Patient Details Page when DOB is Missing (#9242) --- public/locale/en.json | 3 ++- .../Patient/PatientDetailsTab/Demography.tsx | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 56f84a1883a..cd921bef06a 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -562,7 +562,7 @@ "date_and_time": "Date and Time", "date_declared_positive": "Date of declaring positive", "date_of_admission": "Date of Admission", - "date_of_birth": "Date of birth", + "date_of_birth": "Date of Birth", "date_of_positive_covid_19_swab": "Date of Positive Covid 19 Swab", "date_of_result": "Covid confirmation date", "date_of_return": "Date of Return", @@ -1433,6 +1433,7 @@ "why_the_asset_is_not_working": "Why the asset is not working?", "width": "Width ({{unit}})", "working_status": "Working Status", + "year_of_birth": "Year of Birth", "years": "years", "years_of_experience": "Years of Experience", "years_of_experience_of_the_doctor": "Years of Experience of the Doctor", diff --git a/src/components/Patient/PatientDetailsTab/Demography.tsx b/src/components/Patient/PatientDetailsTab/Demography.tsx index 7ac854b8ee6..2a412f03f9a 100644 --- a/src/components/Patient/PatientDetailsTab/Demography.tsx +++ b/src/components/Patient/PatientDetailsTab/Demography.tsx @@ -188,12 +188,19 @@ export const Demography = (props: PatientProps) => { ), }, { - label: t("date_of_birth"), - value: ( + label: t( + patientData.date_of_birth ? "date_of_birth" : "year_of_birth", + ), + value: patientData.date_of_birth ? ( <> {dayjs(patientData.date_of_birth).format("DD MMM YYYY")} ( {formatPatientAge(patientData, true)}) + ) : ( + <> + {patientData.year_of_birth} ({formatPatientAge(patientData, true)} + ) + ), }, { From 2a89332514050c5f519ec650d6d5baf84c786f69 Mon Sep 17 00:00:00 2001 From: Bodhish Thomas Date: Fri, 29 Nov 2024 15:31:18 +0530 Subject: [PATCH 03/10] Update cursorrules --- .cursorrules | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/.cursorrules b/.cursorrules index 2eed3afe459..94ebea1bd57 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,23 +1,35 @@ -Care is a React Typescript Project, built with Vite and styled with TailwindCSS. +You are an expert in TypeScript, React, Shadcn UI, Tailwind. -Care uses a Plugin Architecture. Apps are installed in /apps. +Key Principles -Care uses a custom useQuery hook to fetch data from the API. APIs are defined in the api.tsx file +- Write concise, technical TypeScript code with accurate examples. +- Use functional and declarative programming patterns; avoid classes. +- Prefer iteration and modularization over code duplication. +- Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError). -Here's an example of how to use the useQuery hook to fetch data from the API: +Naming Conventions -``` -useQuery from "@/common/hooks/useQuery"; -const { data, loading, error } = useQuery(routes.getFacilityUsers, { - facility_id: "1", -}); +- Use lowercase with dashes for directories (e.g., components/auth-wizard). +- Favor named exports for components. -request from "@/common/utils/request"; -const { res } = await request(routes.partialUpdateAsset, { - pathParams: { external_id: assetId }, - body: data, -}); -``` +TypeScript Usage +- Use TypeScript for all code; prefer interfaces over types. +- Avoid enums; use maps instead. +- Use functional components with TypeScript interfaces. +Syntax and Formatting +- Use the "function" keyword for pure functions. +- Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements. +- Use declarative JSX. + +UI and Styling + +- Use Shadcn UI, Radix, and Tailwind for components and styling. +- Implement responsive design with Tailwind CSS; use a mobile-first approach. + +General Guidelines + +- Care uses a custom useQuery hook to fetch data from the API. (Docs @ /Utils/request/useQuery) +- APIs are defined in the api.tsx file. From c58de1777e8945a41b26690be3f73e055d19d143 Mon Sep 17 00:00:00 2001 From: Swanand Shekhar Bhuskute <103440604+SwanandBhuskute@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:27:32 +0530 Subject: [PATCH 04/10] fixing back button for camera asset (#9246) --- src/components/CameraFeed/ConfigureCamera.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/CameraFeed/ConfigureCamera.tsx b/src/components/CameraFeed/ConfigureCamera.tsx index f969ed2b0ca..eec453053d6 100644 --- a/src/components/CameraFeed/ConfigureCamera.tsx +++ b/src/components/CameraFeed/ConfigureCamera.tsx @@ -89,16 +89,15 @@ export default function ConfigureCamera(props: Props) { const firstBedId = linkedAssetBeds?.[0]?.bed_object.id ?? unlinkedBeds?.[0]?.id; - useEffect(() => { - if (!query.bed && firstBedId) { - setQuery({ bed: firstBedId }); - } - }, [query.bed, firstBedId]); + + const selectedBedId = query.bed || firstBedId; const selectedAssetBed = linkedAssetBeds?.find( - (a) => a.bed_object.id === query.bed, + (a) => a.bed_object.id === selectedBedId, + ); + const selectedUnlinkedBed = unlinkedBeds?.find( + (bed) => bed.id === selectedBedId, ); - const selectedUnlinkedBed = unlinkedBeds?.find((bed) => bed.id === query.bed); const cameraPresetsQuery = useQuery(FeedRoutes.listAssetBedPresets, { pathParams: { assetbed_id: selectedAssetBed?.id ?? "" }, From 59cc025ee2026213392a9337a2bcc89baec7e783 Mon Sep 17 00:00:00 2001 From: Anvesh Nalimela <151531961+AnveshNalimela@users.noreply.github.com> Date: Sat, 30 Nov 2024 05:57:40 +0530 Subject: [PATCH 05/10] Rewriting UsersCreation.cy.ts according to POM approach (#8930) Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- .../e2e/facility_spec/FacilityLocation.cy.ts | 5 +- cypress/e2e/users_spec/UsersCreation.cy.ts | 215 +++++++++--------- cypress/e2e/users_spec/UsersManage.cy.ts | 7 +- cypress/e2e/users_spec/UsersProfile.cy.ts | 15 +- cypress/pageobject/Users/UserCreation.ts | 79 +++---- cypress/pageobject/Users/UserProfilePage.ts | 42 ++-- cypress/pageobject/utils/constants.ts | 11 +- 7 files changed, 179 insertions(+), 195 deletions(-) diff --git a/cypress/e2e/facility_spec/FacilityLocation.cy.ts b/cypress/e2e/facility_spec/FacilityLocation.cy.ts index 9ac85e9ba4c..19c43d4dcf4 100644 --- a/cypress/e2e/facility_spec/FacilityLocation.cy.ts +++ b/cypress/e2e/facility_spec/FacilityLocation.cy.ts @@ -36,6 +36,7 @@ describe("Location Management Section", () => { const bedType = "ICU"; const bedStatus = "Vacant"; const bedModifiedName = "test modified bed"; + const duplicateBedName = "ICCU"; const bedModifiedDescrption = "test modified description"; const bedModifiedType = "Isolation"; const numberOfBeds = 10; @@ -96,12 +97,12 @@ describe("Location Management Section", () => { facilityHome.verifyAndCloseNotifyModal(); // edit the created bed facilityLocation.clickEditBedButton(); - facilityLocation.enterBedName(bedModifiedName); + facilityLocation.enterBedName(duplicateBedName); facilityLocation.enterBedDescription(bedModifiedDescrption); facilityLocation.selectBedType(bedModifiedType); assetPage.clickassetupdatebutton(); // verify the modification - facilityLocation.verifyBedNameBadge(bedModifiedName); + facilityLocation.verifyBedNameBadge(duplicateBedName); facilityLocation.verifyBedBadge(bedModifiedType); facilityLocation.verifyBedBadge(bedStatus); facilityLocation.closeNotification(); diff --git a/cypress/e2e/users_spec/UsersCreation.cy.ts b/cypress/e2e/users_spec/UsersCreation.cy.ts index f495a136d97..96b953a3e17 100644 --- a/cypress/e2e/users_spec/UsersCreation.cy.ts +++ b/cypress/e2e/users_spec/UsersCreation.cy.ts @@ -1,3 +1,6 @@ +import ManageUserPage from "pageobject/Users/ManageUserPage"; +import UserProfilePage from "pageobject/Users/UserProfilePage"; + import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import LoginPage from "../../pageobject/Login/LoginPage"; @@ -11,13 +14,15 @@ import { describe("User Creation", () => { const userPage = new UserPage(); const loginPage = new LoginPage(); + const userProfilePage = new UserProfilePage(); + const manageUserPage = new ManageUserPage(); const userCreationPage = new UserCreationPage(); const facilityPage = new FacilityPage(); const assetSearchPage = new AssetSearchPage(); - const phone_number = generatePhoneNumber(); - const emergency_phone_number = generateEmergencyPhoneNumber(); + const phoneNumber = generatePhoneNumber(); + const emergencyPhoneNumber = generateEmergencyPhoneNumber(); const fillFacilityName = "Dummy Facility 40"; - const makeid = (length: number) => { + const makeId = (length: number) => { let result = ""; const characters = "abcdefghijklmnopqrstuvwxyz0123456789"; const charactersLength = characters.length; @@ -26,8 +31,8 @@ describe("User Creation", () => { } return result; }; - const username = makeid(8); - const alreadylinkedusersviews = [ + const username = makeId(8); + const alreadyLinkedUsersViews = [ "devdoctor", "devstaff2", "devdistrictadmin", @@ -53,6 +58,25 @@ describe("User Creation", () => { "This field is required", "Please enter valid phone number", ]; + const userName = "devdistrictadmin"; + const firstName = "District Editted"; + const lastName = "Cypress"; + const gender = "Male"; + const email = "test@test.com"; + const password = "Test@123"; + const qualification = "MBBS"; + const experience = "2"; + const regNo = "123456789"; + const newUserFirstName = "cypress test"; + const newUserLastName = "staff user"; + const state = "Kerala"; + const district = "Ernakulam"; + const role = "Doctor"; + const homeFacility = "Dummy Shifting Center"; + const weeklyWorkingHrs = "14"; + const dob = "01011998"; + const formattedDob = "01/01/1998"; + const newUserDob = "25081999"; before(() => { loginPage.loginAsDistrictAdmin(); @@ -66,123 +90,92 @@ describe("User Creation", () => { }); it("Update the existing user profile and verify its reflection", () => { - userCreationPage.clickElementById("user-profile-name"); - userCreationPage.clickElementById("profile-button"); - userCreationPage.verifyElementContainsText( - "username-profile-details", - "devdistrictadmin", - ); - userCreationPage.clickElementById("edit-cancel-profile-button"); - userCreationPage.typeIntoElementByIdPostClear( - "firstName", - "District Editted", - ); - userCreationPage.typeIntoElementByIdPostClear("lastName", "Cypress"); - userCreationPage.selectDropdownOption("gender", "Male"); - userCreationPage.typeIntoElementByIdPostClear("phoneNumber", phone_number); - userCreationPage.typeIntoElementByIdPostClear( - "altPhoneNumber", - emergency_phone_number, - ); - userCreationPage.typeIntoElementByIdPostClear("email", "test@test.com"); - userCreationPage.typeIntoElementByIdPostClear("weekly_working_hours", "14"); - userCreationPage.typeIntoElementByIdPostClearDob( - "date_of_birth", - "01011998", - ); - 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( - "date_of_birth-profile-details", - "01/01/1998", - ); - userCreationPage.verifyElementContainsText( - "emailid-profile-details", - "test@test.com", - ); - userCreationPage.verifyElementContainsText( - "gender-profile-details", - "Male", - ); - userCreationPage.verifyElementContainsText( - "averageworkinghour-profile-details", - "14", - ); + manageUserPage.navigateToProfile(); + cy.verifyContentPresence("#username-profile-details", [userName]); + userProfilePage.clickEditProfileButton(); + userCreationPage.clearFirstName(); + userCreationPage.typeFirstName(firstName); + userCreationPage.clearLastName(); + userCreationPage.typeLastName(lastName); + userProfilePage.selectGender(gender); + userProfilePage.clearPhoneNumber(); + userProfilePage.typePhoneNumber(phoneNumber); + userProfilePage.clearAltPhoneNumber(); + userProfilePage.typeWhatsappNumber(emergencyPhoneNumber); + userProfilePage.clearEmail(); + userProfilePage.typeEmail(email); + userProfilePage.clearWorkingHours(); + userProfilePage.typeWorkingHours(weeklyWorkingHrs); + userProfilePage.typeDateOfBirth(dob); + cy.intercept("PATCH", "/api/v1/users/*").as("updateUser"); + userProfilePage.clickUpdateButton(); + cy.wait("@updateUser").its("response.statusCode").should("eq", 200); + cy.verifyContentPresence("#contactno-profile-details", [ + "+91" + phoneNumber, + ]); + cy.verifyContentPresence("#whatsapp-profile-details", [ + "+91" + emergencyPhoneNumber, + ]); + cy.verifyContentPresence("#firstname-profile-details", [firstName]); + cy.verifyContentPresence("#lastname-profile-details", [lastName]); + cy.verifyContentPresence("#date_of_birth-profile-details", [formattedDob]); + cy.verifyContentPresence("#emailid-profile-details", [email]); + cy.verifyContentPresence("#gender-profile-details", [gender]); + cy.verifyContentPresence("#averageworkinghour-profile-details", [ + weeklyWorkingHrs, + ]); }); it("Update the existing user profile Form Mandatory File Error", () => { - userCreationPage.clickElementById("user-profile-name"); - userCreationPage.clickElementById("profile-button"); - userCreationPage.clickElementById("edit-cancel-profile-button"); - userCreationPage.clearIntoElementById("firstName"); - userCreationPage.clearIntoElementById("lastName"); - userCreationPage.clearIntoElementById("phoneNumber"); - userCreationPage.clearIntoElementById("altPhoneNumber"); - userCreationPage.clearIntoElementById("weekly_working_hours"); - userCreationPage.clickElementById("submit"); + manageUserPage.navigateToProfile(); + userProfilePage.clickEditProfileButton(); + userCreationPage.clearFirstName(); + userCreationPage.clearLastName(); + userProfilePage.clearPhoneNumber(); + userProfilePage.clearAltPhoneNumber(); + userProfilePage.clearWorkingHours(); + userProfilePage.clickUpdateButton(); 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", phone_number); - userCreationPage.setInputDate("date_of_birth", "25081999"); - userCreationPage.selectDropdownOption("user_type", "Doctor"); - userCreationPage.typeIntoElementById("c_password", "Test@123"); - userCreationPage.typeIntoElementById("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"); + userCreationPage.clickAddUserButton(); + userCreationPage.selectFacility(homeFacility); + userCreationPage.typeUserName(username); + userCreationPage.typePassword(password); + userCreationPage.typeConfirmPassword(password); + userCreationPage.selectHomeFacility(homeFacility); + userPage.typeInPhoneNumber(phoneNumber); + userProfilePage.typeDateOfBirth(newUserDob); + userCreationPage.selectUserType(role); + userProfilePage.typeQualification(qualification); + userProfilePage.typeDoctorYoE(experience); + userProfilePage.typeMedicalCouncilRegistration(regNo); + userPage.typeInFirstName(newUserFirstName); + userPage.typeInLastName(newUserLastName); + userProfilePage.typeEmail(email); + userCreationPage.selectGender(gender); + userCreationPage.selectState(state); + userCreationPage.selectDistrict(district); + cy.intercept("POST", "/api/v1/users/add_user/").as("createUser"); + userCreationPage.clickSaveUserButton(); + cy.wait("@createUser").its("response.statusCode").should("eq", 201); + cy.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("qualification", "MBBS"); - userCreationPage.verifyElementContainsText("doctor-experience", "2"); - userCreationPage.verifyElementContainsText( - "medical-council-registration", - "123456789", - ); + cy.verifyContentPresence("#name", [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]); }); it("create new user form throwing mandatory field error", () => { - userCreationPage.clickElementById("addUserButton"); - userCreationPage.clickElementById("submit"); - cy.wait(2000); + userCreationPage.clickAddUserButton(); + userCreationPage.clickSaveUserButton(); + cy.get(".error-text", { timeout: 10000 }).should("be.visible"); userCreationPage.verifyErrorMessages(EXPECTED_ERROR_MESSAGES); }); @@ -194,7 +187,7 @@ describe("User Creation", () => { facilityPage.visitAlreadyCreatedFacility(); facilityPage.clickManageFacilityDropdown(); facilityPage.clickViewUsersOption(); - userPage.verifyMultipleBadgesWithSameId(alreadylinkedusersviews); + userPage.verifyMultipleBadgesWithSameId(alreadyLinkedUsersViews); }); afterEach(() => { diff --git a/cypress/e2e/users_spec/UsersManage.cy.ts b/cypress/e2e/users_spec/UsersManage.cy.ts index 40d436be0a6..74baee3deac 100644 --- a/cypress/e2e/users_spec/UsersManage.cy.ts +++ b/cypress/e2e/users_spec/UsersManage.cy.ts @@ -2,7 +2,6 @@ import { advanceFilters } from "pageobject/utils/advanceFilterHelpers"; import LoginPage from "../../pageobject/Login/LoginPage"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; -import { UserCreationPage } from "../../pageobject/Users/UserCreation"; import { UserPage } from "../../pageobject/Users/UserSearch"; describe("Manage User", () => { @@ -15,7 +14,6 @@ describe("Manage User", () => { const usernameToLinkSkill = "devdoctor"; const firstNameUserSkill = "Dev"; const lastNameUserSkill = "Doctor"; - const userCreationPage = new UserCreationPage(); const usernameforworkinghour = "devdistrictadmin"; const usernamerealname = "Dummy Doctor"; const facilitytolinkusername = "Dummy Shifting Center"; @@ -48,10 +46,9 @@ describe("Manage User", () => { manageUserPage.clickCloseSlideOver(); cy.wait(5000); manageUserPage.navigateToProfile(); - userCreationPage.verifyElementContainsText( - "username-profile-details", + cy.verifyContentPresence("#username-profile-details", [ usernameforworkinghour, - ); + ]); manageUserPage.assertSkillInAlreadyLinkedSkills(linkedskill); }); diff --git a/cypress/e2e/users_spec/UsersProfile.cy.ts b/cypress/e2e/users_spec/UsersProfile.cy.ts index 35771024180..32b812a188f 100644 --- a/cypress/e2e/users_spec/UsersProfile.cy.ts +++ b/cypress/e2e/users_spec/UsersProfile.cy.ts @@ -32,25 +32,22 @@ describe("Manage User Profile", () => { it("Set Dob, Gender, Email, Phone and Working Hours for a user and verify its reflection in user profile", () => { userProfilePage.clickEditProfileButton(); - userProfilePage.typedate_of_birth(date_of_birth); + userProfilePage.typeDateOfBirth(date_of_birth); userProfilePage.selectGender(gender); userProfilePage.typeEmail(email); - userProfilePage.typePhone(phone); - userProfilePage.typeWhatsApp(phone); + userProfilePage.typePhoneNumber(phone); + userProfilePage.typeWhatsappNumber(phone); userProfilePage.typeWorkingHours(workinghours); userProfilePage.typeQualification(qualification); userProfilePage.typeDoctorYoE(doctorYoE); userProfilePage.typeMedicalCouncilRegistration(medicalCouncilRegistration); - userProfilePage.clickUpdateButton(); - cy.verifyNotification("Details updated successfully"); - - userProfilePage.assertdate_of_birth("01/01/1999"); + userProfilePage.assertDateOfBirth("01/01/1999"); userProfilePage.assertGender(gender); userProfilePage.assertEmail(email); - userProfilePage.assertPhone(phone); - userProfilePage.assertWhatsApp(phone); + userProfilePage.assertPhoneNumber(phone); + userProfilePage.assertAltPhoneNumber(phone); userProfilePage.assertWorkingHours(workinghours); }); diff --git a/cypress/pageobject/Users/UserCreation.ts b/cypress/pageobject/Users/UserCreation.ts index 906c07e797a..72157861f2c 100644 --- a/cypress/pageobject/Users/UserCreation.ts +++ b/cypress/pageobject/Users/UserCreation.ts @@ -1,69 +1,52 @@ // UserCreation.ts export class UserCreationPage { - clickElementById(elementId: string) { - cy.get("#" + elementId).click(); + clickAddUserButton() { + cy.verifyAndClickElement("#addUserButton", "Add New User"); } - - typeIntoElementById(elementId: string, value: string) { - cy.get("#" + elementId) - .click() - .type(value); + typeUserName(username: string) { + cy.get("#username").click().type(username); } - - typeIntoElementByIdPostClear(elementId: string, value: string) { - cy.get("#" + elementId) - .click() - .clear() - .click() - .type(value); + typeFirstName(firstName: string) { + cy.get("#firstName").click().type(firstName); } - typeIntoElementByIdPostClearDob(elementId: string, value: string) { - cy.clickAndTypeDate("#" + elementId, value); + typeLastName(lastName: string) { + cy.get("#lastName").click().type(lastName); } - clearIntoElementById(elementId: string) { - cy.get("#" + elementId) - .click() - .clear(); + typePassword(password: string) { + cy.get("#password").click().type(password); } - - typeIntoInputByName(inputName: string, value: string) { - cy.get("input[name='" + inputName + "']") - .click() - .type(value); + typeConfirmPassword(password: string) { + cy.get("#c_password").click().type(password); } - - selectOptionContainingText(text: string) { - cy.get("[role='option']").contains(text).click(); + clearFirstName() { + cy.get("#firstName").click().clear(); } - - verifyNotification(message: string) { - cy.verifyNotification(message); + clearLastName() { + cy.get("#lastName").click().clear(); } - - selectFacility(name: string) { - this.typeIntoInputByName("facilities", name); - this.selectOptionContainingText(name); - cy.get("input[name='facilities'] + button") - .find("#dropdown-toggle") - .click(); + selectUserType(role: string) { + cy.clickAndSelectOption("#user_type", role); } - selectHomeFacility(name: string) { - this.clickElementById("home_facility"); - this.selectOptionContainingText(name); + cy.clickAndSelectOption("#home_facility", name); } - setInputDate(dateElementId: string, dateValue: string) { - cy.clickAndTypeDate("#" + dateElementId, dateValue); + selectGender(gender: string) { + cy.clickAndSelectOption("#gender", gender); + } + selectState(state: string) { + cy.clickAndSelectOption("#state", state); + } + selectDistrict(district: string) { + cy.clickAndSelectOption("#district", district); } - selectDropdownOption(dropdownId: string, optionText: string) { - this.clickElementById(dropdownId); - this.selectOptionContainingText(optionText); + selectFacility(name: string) { + cy.typeAndSelectOption("input[name='facilities']", name); } - verifyElementContainsText(elementId: string, expectedText: string) { - cy.get("#" + elementId).should("contain.text", expectedText); + clickSaveUserButton() { + cy.clickSubmitButton("Save User"); } verifyErrorMessages(errorMessages: string[]) { diff --git a/cypress/pageobject/Users/UserProfilePage.ts b/cypress/pageobject/Users/UserProfilePage.ts index 3744c5a5d82..882be0b7b9b 100644 --- a/cypress/pageobject/Users/UserProfilePage.ts +++ b/cypress/pageobject/Users/UserProfilePage.ts @@ -12,11 +12,24 @@ export default class UserProfilePage { } clickUpdateButton() { - cy.get("#submit").click(); + cy.clickSubmitButton("Update"); } - typedate_of_birth(date_of_birth: string) { - cy.clickAndTypeDate("#date_of_birth", date_of_birth); + typeDateOfBirth(dob: string) { + cy.clickAndTypeDate("#date_of_birth", dob); + } + + clearPhoneNumber() { + cy.get("#phoneNumber").click().clear(); + } + clearAltPhoneNumber() { + cy.get("#altPhoneNumber").click().clear(); + } + clearWorkingHours() { + cy.get("#weekly_working_hours").click().clear(); + } + clearEmail() { + cy.get("#email").click().clear(); } selectGender(gender: string) { @@ -28,16 +41,16 @@ export default class UserProfilePage { cy.get("#email").click().clear().type(email); } - typePhone(phone: string) { + typePhoneNumber(phone: string) { cy.get("#phoneNumber").click().clear().type(phone); } - typeWhatsApp(phone: string) { + typeWhatsappNumber(phone: string) { cy.get("#altPhoneNumber").click().clear().type(phone); } - typeWorkingHours(workinghours: string) { - cy.get("#weekly_working_hours").click().clear().type(workinghours); + typeWorkingHours(workingHours: string) { + cy.get("#weekly_working_hours").click().clear().type(workingHours); } typeQualification = (qualification: string) => { @@ -55,11 +68,8 @@ export default class UserProfilePage { .type(medicalCouncilRegistration); }; - assertdate_of_birth(date_of_birth: string) { - cy.get("#date_of_birth-profile-details").should( - "contain.text", - date_of_birth, - ); + assertDateOfBirth(dob: string) { + cy.get("#date_of_birth-profile-details").should("contain.text", dob); } assertGender(gender: string) { @@ -70,18 +80,18 @@ export default class UserProfilePage { cy.get("#emailid-profile-details").should("contain.text", email); } - assertPhone(phone: string) { + assertPhoneNumber(phone: string) { cy.get("#contactno-profile-details").should("contain.text", phone); } - assertWhatsApp(phone: string) { + assertAltPhoneNumber(phone: string) { cy.get("#whatsapp-profile-details").should("contain.text", phone); } - assertWorkingHours(workinghours: string) { + assertWorkingHours(workingHours: string) { cy.get("#averageworkinghour-profile-details").should( "contain.text", - workinghours, + workingHours, ); } } diff --git a/cypress/pageobject/utils/constants.ts b/cypress/pageobject/utils/constants.ts index 08411e84b6a..053d0561ce8 100644 --- a/cypress/pageobject/utils/constants.ts +++ b/cypress/pageobject/utils/constants.ts @@ -1,7 +1,10 @@ -export function generatePhoneNumber() { - return "9" + Math.floor(100000000 + Math.random() * 900000000).toString(); +export function generatePhoneNumber(): string { + const array = new Uint32Array(1); + window.crypto.getRandomValues(array); + const randomNum = (array[0] % 900000000) + 100000000; + return "9" + randomNum.toString(); } -export function generateEmergencyPhoneNumber() { - return "9" + Math.floor(100000000 + Math.random() * 900000000).toString(); +export function generateEmergencyPhoneNumber(): string { + return generatePhoneNumber(); } From 7d56fdc69265b21c8ebc60f7fffc97e4ad5612df Mon Sep 17 00:00:00 2001 From: Rajab Shoukath Kunnath <142793649+rayyjeb@users.noreply.github.com> Date: Sat, 30 Nov 2024 09:47:35 +0530 Subject: [PATCH 06/10] Fixed Breadcrumb to Display Detailed Name Instead of ID (#9200) --- src/components/Patient/SampleDetails.tsx | 1 + src/components/Patient/models.tsx | 2 +- src/components/Shifting/ShiftDetails.tsx | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Patient/SampleDetails.tsx b/src/components/Patient/SampleDetails.tsx index edc0b4d53a6..abf099a6e7d 100644 --- a/src/components/Patient/SampleDetails.tsx +++ b/src/components/Patient/SampleDetails.tsx @@ -300,6 +300,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { return ( From f2a96983f225789bd6758ef4b4ef8682091f71e1 Mon Sep 17 00:00:00 2001 From: Mahendar Chikolla <119734520+Mahendar0701@users.noreply.github.com> Date: Sat, 30 Nov 2024 10:06:09 +0530 Subject: [PATCH 07/10] Fix : Advanced Filters Autofilling on Revisit to Patient Tab (#9251) --- src/components/Patient/ManagePatients.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Patient/ManagePatients.tsx b/src/components/Patient/ManagePatients.tsx index 0664532b0a1..9507ce3d8d7 100644 --- a/src/components/Patient/ManagePatients.tsx +++ b/src/components/Patient/ManagePatients.tsx @@ -1143,7 +1143,10 @@ export const PatientManager = () => { />
- +
{managePatients}
From 66326dd053cffbb7020f4381ad397217b4de2698 Mon Sep 17 00:00:00 2001 From: Don Xavier <98073418+DonXavierdev@users.noreply.github.com> Date: Sat, 30 Nov 2024 10:09:06 +0530 Subject: [PATCH 08/10] Add edit button to immunisation section; replaced buttons with shadcn buttons across PatientsDetail section (#9245) --- .../Patient/PatientDetailsTab/Demography.tsx | 54 +++++++++---------- .../HealthProfileSummary.tsx | 12 +++-- .../PatientDetailsTab/ImmunisationRecords.tsx | 12 +++-- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/components/Patient/PatientDetailsTab/Demography.tsx b/src/components/Patient/PatientDetailsTab/Demography.tsx index 2a412f03f9a..b9d2b8fece4 100644 --- a/src/components/Patient/PatientDetailsTab/Demography.tsx +++ b/src/components/Patient/PatientDetailsTab/Demography.tsx @@ -5,8 +5,9 @@ import { useTranslation } from "react-i18next"; import Chip from "@/CAREUI/display/Chip"; import CareIcon from "@/CAREUI/icons/CareIcon"; +import AuthorizedChild from "@/CAREUI/misc/AuthorizedChild"; -import ButtonV2 from "@/components/Common/ButtonV2"; +import { Button } from "@/components/ui/button"; import useAuthUser from "@/hooks/useAuthUser"; @@ -312,10 +313,9 @@ export const Demography = (props: PatientProps) => { {t("no_data_found")}
)} - handleEditClick("insurance-details"), @@ -323,7 +323,7 @@ export const Demography = (props: PatientProps) => { > {t("add_insurance_details")} - +
, ], }, @@ -373,21 +373,24 @@ export const Demography = (props: PatientProps) => {
- - navigate( - `/facility/${patientData?.facility}/patient/${id}/update`, - ), + + {({ isAuthorized }) => ( + )} - > - - {t("edit_profile")} - +
{/*
@@ -415,22 +418,19 @@ export const Demography = (props: PatientProps) => { className="group mt-4 rounded-md bg-white pb-2 pl-5 pt-5 shadow" >
-
+

{t(`patient__${subtab.id}`)}

{subtab.allowEdit && ( - + )}
diff --git a/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx b/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx index 3fb6b60d330..374d5ede08b 100644 --- a/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx +++ b/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx @@ -3,6 +3,8 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; +import { Button } from "@/components/ui/button"; + import { UserModel } from "@/components/Users/models"; import useAuthUser from "@/hooks/useAuthUser"; @@ -56,12 +58,12 @@ export const HealthProfileSummary = (props: PatientProps) => {

-
+
{t("medical")}
- +
diff --git a/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx b/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx index eb298737f3c..7ee828868fc 100644 --- a/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx +++ b/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx @@ -3,6 +3,8 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; +import { Button } from "@/components/ui/button"; + import { UserModel } from "@/components/Users/models"; import useAuthUser from "@/hooks/useAuthUser"; @@ -40,10 +42,10 @@ export const ImmunisationRecords = (props: PatientProps) => {

-
+

{t("covid_details")}

- +
From 8118ccfa3c07d883336050b3fb694bffc735c14e Mon Sep 17 00:00:00 2001 From: HITMAN <112652819+ayushpatil2122@users.noreply.github.com> Date: Sat, 30 Nov 2024 16:39:33 +0530 Subject: [PATCH 09/10] phone size responsive issue shifting details page (#9256) --- package-lock.json | 8 ++++---- package.json | 2 +- src/components/Shifting/ShiftDetails.tsx | 13 +++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c703af8fe1..97073c88ecb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,7 +92,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", "glob": "^11.0.0", - "husky": "^9.1.6", + "husky": "^9.1.7", "jsdom": "^25.0.1", "lint-staged": "^15.2.10", "local-cypress": "^1.2.6", @@ -10449,9 +10449,9 @@ "license": "Unlicense" }, "node_modules/husky": { - "version": "9.1.6", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", - "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 020d73f5e65..798c910ea91 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", "glob": "^11.0.0", - "husky": "^9.1.6", + "husky": "^9.1.7", "jsdom": "^25.0.1", "lint-staged": "^15.2.10", "local-cypress": "^1.2.6", diff --git a/src/components/Shifting/ShiftDetails.tsx b/src/components/Shifting/ShiftDetails.tsx index 3eeb440a1b1..bc97b0b0f24 100644 --- a/src/components/Shifting/ShiftDetails.tsx +++ b/src/components/Shifting/ShiftDetails.tsx @@ -483,25 +483,22 @@ export default function ShiftDetails(props: { id: string }) { }} backUrl="/shifting/board" options={ -
+
- navigate(`/shifting/${data?.external_id}/update`) - } + disabled={data?.status === "COMPLETED" || data?.status === "CANCELLED"} + onClick={() => navigate(`/shifting/${data?.external_id}/update`)} > {t("update_status_details")} - setIsPrintMode(true)}> + setIsPrintMode(true)}> {" "} {t("referral_letter")} From f76c6250ec25c6d9216f58088ca4f9833e872743 Mon Sep 17 00:00:00 2001 From: Kamishetty Rishith <119791436+Rishith25@users.noreply.github.com> Date: Sun, 1 Dec 2024 12:21:46 +0530 Subject: [PATCH 10/10] =?UTF-8?q?Fix:=20Reflect=20Updated=20Service=20Note?= =?UTF-8?q?s=20Immediately=20in=20Asset=20Details=20without=C2=A0page?= =?UTF-8?q?=C2=A0reload=20(#9226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Assets/AssetManage.tsx | 11 +++++++++-- src/components/Shifting/ShiftDetails.tsx | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/Assets/AssetManage.tsx b/src/components/Assets/AssetManage.tsx index c78be71d627..b4377d2e1aa 100644 --- a/src/components/Assets/AssetManage.tsx +++ b/src/components/Assets/AssetManage.tsx @@ -69,7 +69,11 @@ const AssetManage = (props: AssetManageProps) => { >(); const [transactionFilter, setTransactionFilter] = useState({}); - const { data: asset, loading } = useQuery(routes.getAsset, { + const { + data: asset, + loading, + refetch, + } = useQuery(routes.getAsset, { pathParams: { external_id: assetId, }, @@ -594,7 +598,10 @@ const AssetManage = (props: AssetManageProps) => { handleClose={() => setServiceEditData({ ...serviceEditData, open: false }) } - handleUpdate={() => serviceRefetch()} + handleUpdate={() => { + serviceRefetch(); + refetch(); + }} show={serviceEditData.open} viewOnly={serviceEditData.viewOnly} /> diff --git a/src/components/Shifting/ShiftDetails.tsx b/src/components/Shifting/ShiftDetails.tsx index bc97b0b0f24..cb5140d6ed0 100644 --- a/src/components/Shifting/ShiftDetails.tsx +++ b/src/components/Shifting/ShiftDetails.tsx @@ -492,13 +492,20 @@ export default function ShiftDetails(props: { id: string }) { : "" } tooltipClassName="tooltip-top -translate-x-28 -translate-y-1 text-xs" - disabled={data?.status === "COMPLETED" || data?.status === "CANCELLED"} - onClick={() => navigate(`/shifting/${data?.external_id}/update`)} + disabled={ + data?.status === "COMPLETED" || data?.status === "CANCELLED" + } + onClick={() => + navigate(`/shifting/${data?.external_id}/update`) + } > {t("update_status_details")} - setIsPrintMode(true)}> + setIsPrintMode(true)} + > {" "} {t("referral_letter")}